Skip to content

AFNI/NIfTI Server

Sections
Personal tools
You are here: Home » AFNI » Documentation

Doxygen Source Code Documentation


Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Search  

SUMA_SurfMeasures.c

Go to the documentation of this file.
00001 
00002 #define VERSION "version 1.11 (October 6, 2004)"
00003 
00004 /*----------------------------------------------------------------------
00005  * SurfMeasures - compute measures from the surface dataset(s)
00006  *
00007  * This program takes as input one or two surfaces (as part of a
00008  * spec file), and outputs a 1D format surface dataset, containing
00009  * requested measures from the input surfaces.
00010  *
00011  * Valid measures (to be supplied via the -func option) are:
00012  *
00013  *     ang_norms        : angle between the 2 surface normals
00014  *     ang_ns_A         : angle between the segment and first normal
00015  *     ang_ns_B         : angle between the segment and second normal
00016  *     coord_A          : first xyz coordinate
00017  *     coord_B          : second xyz coordinate
00018  *     n_area_A         : area for each node on the first surface
00019  *     n_area_B         : area for each node on the second surface
00020  *     n_avearea_A      : average area of trianges for each node on surface
00021  *     n_avearea_B      : average area of trianges for each node on surface
00022  *     n_ntri           : number of included trianges for each node
00023  *     nodes            : node index
00024  *     node_vol         : between surface volume per node
00025  *     norm_A           : vector of normal at node on first surface
00026  *     norm_B           : vector of normal at node on second surface
00027  *     thick            : thickness - length of node segment
00028  *
00029  * Final info options (accessed via '-info_XXXX'):
00030  *
00031  *     all              : combines all '-info_XXXX' options
00032  *     area             : display total area of each surface
00033  *     norms            : display norm averages
00034  *     thick            : display min/max thickness
00035  *     vol              : display total volume and areas
00036  *
00037  * See "SurfMeasures -help" for more information.
00038  *
00039  * Author: R. Reynolds
00040  *----------------------------------------------------------------------
00041 */
00042 
00043 /* use history as a global string to print out with '-hist' option */
00044 
00045 static char g_history[] =
00046     "----------------------------------------------------------------------\n"
00047     "history :\n"
00048     "\n"
00049     "1.0 December 01, 2003  [rickr]\n"
00050     "  - initial release\n"
00051     "\n"
00052     "1.1 December 02, 2003  [rickr]\n"
00053     "  - fixed stupid macro error, grrrr...\n"
00054     "\n"
00055     "1.2 December 03, 2003  [rickr]\n"
00056     "  - added '-cmask' and '-nodes_1D' options\n"
00057     "\n"
00058     "1.3 December 11, 2003  [rickr]\n"
00059     "  - added required program argument(s): '-surf_A' (and 'B' for 2 surfs)\n"
00060     "      o  see '-help' for a description and examples\n"
00061     "  - added SUMA_spec_select_surfs() and SUMA_spec_set_map_refs() so that:\n"
00062     "      o  only requested surfaces are loaded\n"
00063     "      o  spec files no longer need 'SAME' as MappingRef\n"
00064     "  - fixed loss of default node indices (from nodes_1D change)\n"
00065     "  - added '-hist' option\n"
00066     "  - display angle averages only if at least 1 total is computed\n"
00067     "\n"
00068     "1.4 January 22, 2004  [rickr]\n"
00069     "  - fixed error with '-nodes_1D' indexing (fp0 in write_output())\n"
00070     "    (for output of node coordinates)\n"
00071     "  - added '-sv' option to examples\n"
00072     "  - reversed history list (most recent last) for '-hist' option\n"
00073     "\n"
00074     "1.5 January 23, 2004  [rickr]\n"
00075     "  - SUMA_isINHmappable() is deprecated, check with AnatCorrect field\n"
00076     "\n"
00077     "1.6 February 11, 2004  [rickr]\n"
00078     "  - add a little debug help for !AnatCorrect case\n"
00079     "\n"
00080     "1.7 February 23, 2004  [rickr]\n"
00081     "  - added functions 'n_avearea_A', 'n_avearea_B', 'n_ntri'\n"
00082     "\n"
00083     "1.8 March 26, 2004  [ziad]\n"
00084     "  - DsetList added to SUMA_LoadSpec_eng() and SUMA_SurfaceMetrics_eng()\n"
00085     "\n"
00086     "1.9 July 29, 2004  [rickr]\n"
00087     "  - Remove check for anat correct.\n"
00088     "\n"
00089     "1.10 August 11, 2004  [rickr]\n"
00090     "  - Add comment about volume being too large.\n"
00091     "\n"
00092     "1.11 October 12, 2004  [rickr]\n"
00093     "  - More volume comments: suggest 'SurfPatch -vol'\n"
00094     "----------------------------------------------------------------------\n";
00095 
00096 /*----------------------------------------------------------------------
00097  * todo:
00098  *----------------------------------------------------------------------
00099 */
00100 
00101 #include "mrilib.h"
00102 #include "SUMA_suma.h"
00103 #include "SUMA_SurfMeasures.h"
00104 
00105 extern void machdep( void );
00106 
00107 
00108 /* globals */
00109 SUMA_SurfaceViewer * SUMAg_SVv = NULL;  /* array of Surf View structs   */
00110 int                  SUMAg_N_SVv = 0;   /* length of SVv array          */
00111 SUMA_DO            * SUMAg_DOv = NULL;  /* array of Displayable Objects */
00112 int                  SUMAg_N_DOv = 0;   /* length of DOv array          */
00113 SUMA_CommonFields  * SUMAg_CF = NULL;   /* info common to all viewers   */
00114 
00115 /* these must match smeasure_codes_e enum */
00116 char * g_sm_names[] = { "none", "ang_norms", "ang_ns_A", "ang_ns_B",
00117                         "coord_A", "coord_B", "n_area_A", "n_area_B",
00118                         "n_avearea_A", "n_avearea_B", "n_ntri",
00119                         "node_vol", "nodes", "norm_A", "norm_B", "thick" };
00120 
00121 char * g_sm_desc[] = { "invalid function",
00122                        "angular difference between normals",
00123                        "angular diff between segment and first norm",
00124                        "angular diff between segment and second norm",
00125                        "xyz coordinates of node on first surface",
00126                        "xyz coordinates of node on second surface",
00127                        "associated node area on first surface",
00128                        "associated node area on second surface",
00129                        "for each node, average area of triangles (surf A)",
00130                        "for each node, average area of triangles (surf B)",
00131                        "for each node, number of associated triangles",
00132                        "associated node volume between surfaces",
00133                        "node number",
00134                        "vector of normal at node on first surface",
00135                        "vector of normal at node on second surface",
00136                        "distance between surfaces along segment" };
00137 
00138 /*----------------------------------------------------------------------*/
00139 
00140 int main ( int argc, char * argv[] )
00141 {
00142     param_t p;
00143     opts_t  opts;
00144     int     rv;
00145 
00146     /* note initial time */
00147     (void) COX_clock_time();
00148 
00149     mainENTRY("SurfMeasures main");
00150     machdep();
00151     AFNI_logger(PROG_NAME,argc,argv);
00152 
00153     if ( (rv = init_options(&opts, argc, argv)) != 0 )
00154         return rv;
00155 
00156     if ( opts.debug > 1 )
00157         fprintf(stderr,"-- timing: init opts         : time = %.3f\n",
00158                 COX_clock_time());
00159 
00160     if ( (rv = validate_options(&opts, &p)) != 0 )
00161         return rv;
00162 
00163     if ( opts.debug > 1 )
00164         fprintf(stderr,"-- timing: validate opts     : time = %.3f\n",
00165                 COX_clock_time());
00166 
00167     if ( (rv = get_surf_data(&opts, &p)) != 0 )
00168         return rv;
00169 
00170     if ( opts.debug > 1 )
00171         fprintf(stderr,"-- timing: get surf data     : time = %.3f\n",
00172                 COX_clock_time());
00173 
00174     if ( (rv = write_output(&opts, &p)) != 0 )
00175         return rv;
00176 
00177     if ( opts.debug > 1 )
00178         fprintf(stderr,"-- timing: write outfile     : time = %.3f\n",
00179                 COX_clock_time());
00180 
00181     final_cleanup(&opts, &p);
00182 
00183     if ( opts.debug > 1 )
00184         fprintf(stderr,"-- timing: final clean up    : time = %.3f\n",
00185                 COX_clock_time());
00186 
00187     return 0;
00188 }
00189 
00190 
00191 /*----------------------------------------------------------------------
00192  * write_output
00193  *
00194  *----------------------------------------------------------------------
00195 */
00196 int write_output( opts_t * opts, param_t * p )
00197 {
00198     SUMA_SurfaceObject * so0, * so1;
00199 
00200     THD_fvec3   p0, p1;
00201     double      tarea0, tarea1, tvolume;
00202     double      dist, min_dist, max_dist, tdist;
00203     double      atn = 0.0, atna = 0.0, atnb = 0.0;  /* angle totals */
00204     float       fvn, fva, fvb;
00205     float     * fp0, * fp1;
00206     float     * norms0, * norms1;
00207     float       ave_dist, fval;
00208     int         c, fnum, node, nindex;
00209     int       * fcodes;
00210     int         skipped = 0;
00211 
00212 ENTRY("write_output");
00213 
00214     so0 = p->S.slist[0];
00215     so1 = p->S.slist[1];
00216 
00217     /* just a simple case for now */
00218 
00219     print_column_headers(opts, p);
00220 
00221     /* initialize some pointers */
00222     fp0    = so0->NodeList;
00223     norms0 = so0->NodeNormList;
00224 
00225     /* if we have only one surface, just init fp1 to that */
00226     if ( p->S.nsurf > 1 )
00227     {
00228         fp1    = so1->NodeList;
00229         norms1 = so1->NodeNormList;
00230     }
00231     else
00232     {
00233         fp1    = fp0;
00234         norms1 = norms0;
00235     }
00236 
00237     fcodes = p->F->codes;       /* for convenience */
00238 
00239     tdist   = 0.0;              /* for total distance over nodes */
00240     tarea0  = 0.0;              /* for total area computation   */
00241     tarea1  = 0.0;              /* for total area computation  */
00242     tvolume = 0.0;              /* for total volume           */
00243 
00244     min_dist = 9999.0;
00245     max_dist = 0.0;
00246     for (nindex = 0; nindex < p->nnodes; nindex++)
00247     {
00248         if ( p->cmask && !p->cmask[nindex] )
00249         {
00250             skipped++;
00251             continue;
00252         }
00253 
00254         node = p->nodes[nindex];
00255 
00256         memcpy(p0.xyz, fp0+3*node, 3*sizeof(float));
00257         memcpy(p1.xyz, fp1+3*node, 3*sizeof(float));
00258 
00259         dist = dist_fn(3, p0.xyz, p1.xyz);
00260         if ( dist < min_dist ) min_dist = dist;
00261         if ( dist > max_dist ) max_dist = dist;
00262         tdist += dist;
00263 
00264         /* keep track of the total areas and volume */
00265         if ( p->S.narea[0] )
00266             tarea0  += p->S.narea[0][node];
00267 
00268         if ( p->S.narea[1] )
00269             tarea1  += p->S.narea[1][node];
00270 
00271         if ( p->S.nvol )
00272             tvolume += p->S.nvol[node];
00273 
00274         fputc(' ', p->outfp);
00275 
00276         for (fnum = 0; fnum < p->F->nused; fnum++)
00277         {
00278             switch(fcodes[fnum])
00279             {
00280                 default:
00281                     fprintf(stderr,"** bad output Info code %d\n",fcodes[fnum]);
00282                     break;
00283 
00284                 case E_SM_ANG_NORMS:
00285                     fvn = vector_angle(norms0 + 3*node, norms1 + 3*node);
00286                     fprintf(p->outfp,"  %10s", MV_format_fval(fvn));
00287                     atn += fvn;
00288                     break;
00289 
00290                 case E_SM_ANG_NS_A:
00291                     fva = norm2seg_angle(&p0, &p1, norms0 + 3*node);
00292                     fprintf(p->outfp,"  %10s", MV_format_fval(fva));
00293                     atna += fva;
00294                     break;
00295 
00296                 case E_SM_ANG_NS_B:
00297                     fvb = norm2seg_angle(&p0, &p1, norms1 + 3*node);
00298                     fprintf(p->outfp,"  %10s", MV_format_fval(fvb));
00299                     atnb += fvb;
00300                     break;
00301 
00302                 case E_SM_COORD_A:
00303                     for (c = 0; c < 3; c++)
00304                         fprintf(p->outfp,"  %10s",
00305                                 MV_format_fval(fp0[3*node+c]));
00306                     break;
00307 
00308                 case E_SM_COORD_B:
00309                     for (c = 0; c < 3; c++)
00310                         fprintf(p->outfp,"  %10s",
00311                                 MV_format_fval(fp1[3*node+c]));
00312                     break;
00313 
00314                 case E_SM_N_AREA_A:
00315                     fprintf(p->outfp,"  %10s",
00316                             MV_format_fval(p->S.narea[0][node]));
00317                     break;
00318 
00319                 case E_SM_N_AREA_B:
00320                     fprintf(p->outfp,"  %10s",
00321                             MV_format_fval(p->S.narea[1][node]));
00322                     break;
00323 
00324                 case E_SM_N_AVEAREA_A:
00325                     fval = 3 * p->S.narea[0][node] / so0->MF->N_Memb[node];
00326                     fprintf(p->outfp,"  %10s", MV_format_fval(fval));
00327                     break;
00328 
00329                 case E_SM_N_AVEAREA_B:
00330                     fval = 3 * p->S.narea[1][node] / so1->MF->N_Memb[node];
00331                     fprintf(p->outfp,"  %10s", MV_format_fval(fval));
00332                     break;
00333 
00334                 case E_SM_NTRI:
00335                     fprintf(p->outfp,"  %10d", so0->MF->N_Memb[node]);
00336                     break;
00337 
00338                 case E_SM_NODES:
00339                     fprintf(p->outfp,"  %10d", node);
00340                     break;
00341 
00342                 case E_SM_NODE_VOL:
00343                     fprintf(p->outfp,"  %10s", MV_format_fval(p->S.nvol[node]));
00344                     break;
00345 
00346                 case E_SM_NORM_A:
00347                     if ( norms0 )
00348                         for (c = 0; c < 3; c++)
00349                             fprintf(p->outfp,"  %10s",
00350                                     MV_format_fval(norms0[3*node+c]));
00351                     break;
00352 
00353                 case E_SM_NORM_B:
00354                     if ( norms1 )
00355                         for (c = 0; c < 3; c++)
00356                             fprintf(p->outfp,"  %10s",
00357                                     MV_format_fval(norms1[3*node+c]));
00358                     break;
00359 
00360                 case E_SM_THICK:
00361                     fprintf(p->outfp,"  %10s", MV_format_fval(dist));
00362                     break;
00363             }
00364         }
00365 
00366         fputc('\n', p->outfp);
00367 
00368         if ( node == opts->dnode && opts->debug > 0 )
00369         {
00370             fprintf(stderr,"-- dnode %d:\n", node);
00371             disp_f3_point("-- p0    = ", fp0+3*node);
00372             disp_f3_point("-- p1    = ", fp1+3*node);
00373 
00374             if ( atn > 0.0 || atna > 0.0 || atnb > 0.0 )
00375             {
00376                 disp_f3_point("-- normA = ", norms0 + 3*node);
00377                 disp_f3_point("-- normB = ", norms1 + 3*node);
00378             }
00379         }
00380     }
00381 
00382     ave_dist = tdist/p->nnodes;
00383 
00384     if ( opts->info )
00385         printf("----------------------------------------------------------\n");
00386 
00387     if ( opts->info & ST_INFO_AREA )
00388     {
00389         if ( p->S.narea[0] )
00390             printf("-- total area 0 = %.1f\n", tarea0);
00391 
00392         if ( p->S.nsurf > 1 )
00393             printf("-- total area 1 = %.1f\n", tarea1);
00394     }
00395 
00396     if ( (opts->info & ST_INFO_NORMS) &&
00397          (atn > 0.0 || atna > 0.0 || atnb > 0.0) )
00398     {
00399         printf( "-- ave. angles to normals: (nA_nB, sA, sB) = "
00400                 "(%.4f, %.4f, %.4f)\n",
00401                 atn/p->nnodes, atna/p->nnodes, atnb/p->nnodes);
00402     }
00403 
00404     if ( (opts->info & ST_INFO_THICK) && (p->S.nsurf > 1) )
00405         printf( "-- thickness: (min, ave, max) = (%.5f, %.5f, %.5f)\n",
00406                 min_dist, ave_dist, max_dist);
00407 
00408     if ( (opts->info & ST_INFO_VOL) && p->S.nvol )
00409     {
00410         printf("-- total volume = %.1f\n", tvolume);
00411         if ( opts->debug > 1 )
00412             printf("-- ave dist * (area0, ave, area1) = (%.1f, %.1f, %.1f)\n",
00413                 ave_dist*tarea0, ave_dist*(tarea0+tarea1)/2, ave_dist*tarea1);
00414     }
00415 
00416     if ( p->cmask )
00417         printf("-- from cmask, nodes skipped = %d\n", skipped);
00418 
00419     RETURN(0);
00420 }
00421 
00422 
00423 /*----------------------------------------------------------------------
00424  * norm2seg_angle               - return the angle between segment and norm
00425  *----------------------------------------------------------------------
00426 */
00427 float norm2seg_angle( THD_fvec3 * p0, THD_fvec3 * p1, float * norm )
00428 {
00429     float seg[3];
00430     int   c;
00431 
00432 ENTRY("norm2seg_angle");
00433 
00434     for ( c = 0; c < 3; c++ )
00435         seg[c] = p1->xyz[c] - p0->xyz[c];
00436 
00437     RETURN(vector_angle(seg, norm));
00438 }
00439 
00440 
00441 /*----------------------------------------------------------------------
00442  * fvec_magnitude                       - compute magnitude of float vector
00443  *----------------------------------------------------------------------
00444 */
00445 float fvec_magnitude( float * v, int length )
00446 {
00447     double sums;
00448     int    c;
00449 
00450 ENTRY("fvec_magnitude");
00451 
00452     sums = 0.0;
00453     for ( c = 0; c < length; c++ )
00454         sums += v[c] * v[c];
00455 
00456     RETURN(sqrt(sums));
00457 }
00458 
00459 
00460 /*----------------------------------------------------------------------
00461  * vector_angle         - return the angle between the vectors
00462  *
00463  * if either magnitude is 0, return 0
00464  * else, use dot product definition
00465  *----------------------------------------------------------------------
00466 */
00467 float vector_angle( float * v0, float * v1 )
00468 {
00469     double mag0, mag1, dot, ratio, angle;
00470 
00471 ENTRY("vector_angle");
00472 
00473     mag0 = fvec_magnitude(v0, 3);
00474     mag1 = fvec_magnitude(v1, 3);
00475     dot  = v0[0]*v1[0] + v0[1]*v1[1] + v0[2]*v1[2];
00476 
00477     if ( mag0 == 0.0 || mag1 == 0.0 )
00478         RETURN(0.0);
00479 
00480     ratio = dot / (mag0 * mag1);
00481     RANGE(-1.0, ratio, 1.0);
00482 
00483     angle = acos(ratio);
00484 
00485     /* keep angle in [0,PI/2] */
00486     if ( 2 * angle > ST_PI )
00487         angle = PI - angle;
00488 
00489     RETURN(angle);
00490 }
00491 
00492 
00493 /*----------------------------------------------------------------------
00494  * print_column_headers
00495  *----------------------------------------------------------------------
00496 */
00497 int print_column_headers( opts_t * opts, param_t * p )
00498 {
00499     int c, c2, num2print;
00500 
00501 ENTRY("print_column_headers");
00502 
00503     fputc('#', p->outfp);
00504     for (c = 0; c < p->F->nused; c++ )
00505     {
00506         if ( ( p->F->codes[c] == E_SM_COORD_A ) ||
00507              ( p->F->codes[c] == E_SM_COORD_B ) ||
00508              ( p->F->codes[c] == E_SM_NORM_A  ) ||
00509              ( p->F->codes[c] == E_SM_NORM_B  ) )
00510             num2print = 3;
00511         else
00512             num2print = 1;
00513 
00514         for (c2 = 0; c2 < num2print; c2++)
00515             if ( num2print > 1 )
00516                 fprintf(p->outfp, " %8s[%d]", p->F->names[c],c2);
00517             else
00518                 fprintf(p->outfp, "  %10s", p->F->names[c]);
00519     }
00520     fputc('\n', p->outfp);
00521 
00522     fputc('#', p->outfp);
00523     for (c = 0; c < p->F->nused; c++ )
00524     {
00525         if ( ( p->F->codes[c] == E_SM_COORD_A ) ||
00526              ( p->F->codes[c] == E_SM_COORD_B ) ||
00527              ( p->F->codes[c] == E_SM_NORM_A  ) ||
00528              ( p->F->codes[c] == E_SM_NORM_B  ) )
00529             num2print = 3;
00530         else
00531             num2print = 1;
00532 
00533         for (c2 = 0; c2 < num2print; c2++)
00534             fprintf(p->outfp, "  ----------");
00535     }
00536     fputc('\n', p->outfp);
00537 
00538     RETURN(0);
00539 }
00540 
00541 
00542 /*----------------------------------------------------------------------
00543  * verify_surf_t
00544  *
00545  *----------------------------------------------------------------------
00546 */
00547 int verify_surf_t( opts_t * opts, param_t * p )
00548 {
00549     int c;
00550 
00551 ENTRY("verify_surf_t");
00552 
00553     if ( p->S.nsurf < 1 )
00554     {
00555         fprintf(stderr,"** no surfaces found\n");
00556         if ( opts->debug > 0 )
00557             disp_surf_t("-- empty? ", &p->S);
00558         RETURN(-1);
00559     }
00560 
00561     if ( p->S.nsurf > 1 )
00562     {
00563         for ( c = 1; c < p->S.nsurf; c++ )
00564             if ( p->S.slist[c]->N_Node != p->S.nnodes )
00565             {
00566                 fprintf(stderr,"** surface %d has %d nodes (should be %d)\n",
00567                         c, p->S.slist[c]->N_Node, p->S.nnodes);
00568                 RETURN(-1);
00569             }
00570     }
00571 
00572     /* verify nodes and cmask */
00573     if ( ! p->nodes )                   /* if empty, create the trivial list */
00574     {
00575         p->nnodes = p->S.nnodes;
00576         p->nodes  = (int *)malloc(p->nnodes * sizeof(int));
00577         ALLOC_CHECK(p->nodes, "int", p->S.nnodes);
00578 
00579         /* now fill the list with trivial indices */
00580         for ( c = 0; c < p->nnodes; c++ )
00581             p->nodes[c] = c;
00582     }
00583     else                                /* verify that the indices are valid */
00584     {
00585         for ( c = 0; c < p->nnodes; c++ )
00586             if ( (p->nodes[c] < 0) || (p->nodes[c] >= p->S.nnodes) )
00587             {
00588                 fprintf(stderr,"** error: node list index %d (value = %d):"
00589                                "          outside valid range [0,%d]\n",
00590                         c, p->nodes[c], p->S.nnodes-1);
00591                 RETURN(-1);
00592             }
00593     }
00594 
00595     /* in any case, if there is a mask, the length should match nnodes */
00596     if ( p->cmask && (p->ncmask != p->nnodes) )
00597     {
00598         fprintf(stderr,"** cmask and node list lengths differ (%d, %d)\n",
00599                 p->ncmask, p->nnodes);
00600         RETURN(-1);
00601     }
00602 
00603     if ( opts->debug > 1 )
00604     {
00605         disp_param_t("-- surf params verified: ", p);
00606         disp_surf_t ("-- surf params verified: ", &p->S);
00607     }
00608 
00609     if ( validate_option_lists(opts, p) != 0 )
00610         RETURN(-1);
00611 
00612     RETURN(0);
00613 }
00614 
00615 
00616 /*----------------------------------------------------------------------
00617  * get_surf_data
00618  *
00619  *----------------------------------------------------------------------
00620 */
00621 int get_surf_data( opts_t * opts, param_t * p )
00622 {
00623     int rv;
00624 
00625 ENTRY("get_surf_data");
00626 
00627     rv = spec2SUMA(&p->S.spec, opts);
00628     if ( rv != 0 )
00629         RETURN(rv);
00630 
00631     if ( opts->debug > 1 )
00632         fprintf(stderr,"-- timing: Surf (spec2SUMA)  : time = %.3f\n",
00633                 COX_clock_time());
00634 
00635     if ( (rv = all_mappable_surfs(opts, p)) != 0 )
00636         RETURN(rv);
00637 
00638     if ( opts->debug > 1 )
00639         fprintf(stderr,"-- timing: Surf (all_map)    : time = %.3f\n",
00640                 COX_clock_time());
00641 
00642     if ( (rv = verify_surf_t(opts, p)) != 0)
00643         RETURN(rv);
00644 
00645     if ( opts->debug > 1 )
00646         fprintf(stderr,"-- timing: Surf (verify surf): time = %.3f\n",
00647                 COX_clock_time());
00648 
00649     if ( (rv = get_surf_measures(opts, p)) != 0)
00650         RETURN(rv);
00651 
00652     if ( opts->debug > 1 )
00653         fprintf(stderr,"-- timing: Surf (get measure): time = %.3f\n",
00654                 COX_clock_time());
00655 
00656     RETURN(0);
00657 }
00658 
00659 
00660 /*----------------------------------------------------------------------
00661  * get_surf_measures
00662  *----------------------------------------------------------------------
00663 */
00664 int get_surf_measures( opts_t * opts, param_t * p )
00665 {
00666     int * fcodes;
00667     int   c, debug;
00668     int   geta, getb;
00669 
00670 ENTRY("get_surf_measures");
00671 
00672     geta = getb = 0;
00673     debug = opts->debug > 2;
00674 
00675     if ( opts->info & ST_INFO_VOL )
00676     {
00677         geta = getb = 1;
00678     }
00679     else                /* maybe there are functions that need them */
00680     {
00681         fcodes = p->F->codes;
00682         for ( c = 0; c < p->F->nused; c++ )
00683         {
00684             if ( fcodes[c] == E_SM_N_AREA_A )
00685                 geta = 1;
00686             else if ( fcodes[c] == E_SM_N_AREA_B )
00687                 getb = 1;
00688             else if ( fcodes[c] == E_SM_N_AVEAREA_A )
00689                 geta = 1;
00690             else if ( fcodes[c] == E_SM_N_AVEAREA_B )
00691                 getb = 1;
00692             else if ( fcodes[c] == E_SM_NODE_VOL )
00693             {
00694                 geta = getb = 1;
00695             }
00696         }
00697     }
00698 
00699     if ( geta ) 
00700     {
00701         if ( !SUMA_SurfaceMetrics_eng(p->S.slist[0], "PolyArea", NULL, debug,
00702                                       SUMAg_CF->DsetList) )
00703         {
00704             fprintf(stderr,"** gsf: surface metrics A failure\n");
00705             RETURN(-1);
00706         }
00707 
00708         if ( compute_node_areas(opts, p, 0) != 0 )      /* index 0 */
00709             RETURN(-1);
00710     }
00711 
00712     if ( getb )
00713     {
00714         if ( p->S.nsurf < 2 )
00715         {
00716             fprintf(stderr,"** gsf: functions requre 2 surfaces, failing...\n");
00717             RETURN(-1);
00718         }
00719 
00720         if ( !SUMA_SurfaceMetrics_eng(p->S.slist[1], "PolyArea", NULL, debug,
00721                                       SUMAg_CF->DsetList) )
00722         {
00723             fprintf(stderr,"** gsf: surface metrics B failure\n");
00724             RETURN(-1);
00725         }
00726 
00727         if ( compute_node_areas(opts, p, 1) != 0 )  /* index 1 */
00728             RETURN(-1);
00729     }
00730 
00731     if( geta && getb )
00732     {
00733         if ( surf_triangle_match(opts, p) != 0 )
00734             RETURN(-1);
00735         if ( compute_face_vols(opts, p) != 0 )
00736             RETURN(-1);
00737         if ( compute_node_vols(opts, p) != 0 )
00738             RETURN(-1);
00739     }
00740 
00741     RETURN(0);
00742 }
00743 
00744 
00745 /*----------------------------------------------------------------------
00746  * surf_triangles_match - check that the trangle lists match 
00747  *----------------------------------------------------------------------
00748 */
00749 int surf_triangle_match( opts_t * opts, param_t * p )
00750 {
00751     int   face, maxf;
00752     int * f0, * f1;
00753 
00754 ENTRY("surf_triangle_match");
00755 
00756     f0 = p->S.slist[0]->FaceSetList;
00757     f1 = p->S.slist[1]->FaceSetList;
00758 
00759     maxf = p->S.slist[0]->N_FaceSet * 3;
00760 
00761     if ( !f0 || !f1 || maxf <= 0 )
00762     {
00763         fprintf(stderr,"** stm: bad face data (%p, %p, %d)\n", f0, f1, maxf);
00764         RETURN(-1);
00765     }
00766 
00767     for ( face = 0; face < maxf; face++ )
00768     {
00769         if (*f0 != *f1 )
00770         {
00771             fprintf(stderr,"** face diff @ f3 = %d, *f0,*f1 = (%d,%d)\n",
00772                     face, *f0, *f1);
00773             RETURN(-1);
00774         }
00775         f0++;  f1++;
00776     }
00777 
00778     if ( opts->debug > 1 )
00779         fprintf(stderr,"-- faces okay, woohoo!\n");
00780 
00781     RETURN(0);
00782 }
00783 
00784 
00785 #if 0           /* test_planar_sides */
00786 
00787 /*----------------------------------------------------------------------
00788  * test_planar_sides    - check that all pentahedra have planar sides
00789  *----------------------------------------------------------------------
00790 */
00791 int test_planar_sides( opts_t * opts, param_t * p )
00792 {
00793     SUMA_SurfaceObject    * so;
00794     float                 * nodes0, * nodes1;
00795     float                   p1, p2, p3, max, mold;
00796     int                     a, b, c, mcount;
00797     int                     face, nfaces;
00798     int                   * flist;
00799 
00800 ENTRY("test_planar_sides");
00801 
00802     flist  = p->S.slist[0]->FaceSetList;
00803     nfaces = p->S.slist[1]->N_FaceSet;
00804 
00805     nodes0 = p->S.slist[0]->NodeList;
00806     nodes1 = p->S.slist[1]->NodeList;
00807 
00808     if ( !flist || !nodes0 || !nodes1 || nfaces <= 0 )
00809     {
00810         fprintf(stderr,"** tps: bad face or node data (%p, %p, %p, %d)\n",
00811                 flist, nodes0, nodes1, nfaces);
00812         RETURN(-1);
00813     }
00814 
00815     max    = 0.0;
00816     mold   = 1.0;               /* basic cutoff  */
00817     mcount = 0;                 /* count updates */
00818     for ( face = 0; face < nfaces; face++ )
00819     {
00820         a = flist[0] * 3;               /* get the indices into NodeList */
00821         b = flist[1] * 3;
00822         c = flist[2] * 3;
00823 
00824         p1 = eval_planar_points(nodes0+a, nodes0+b, nodes1+a, nodes1+b);
00825         p2 = eval_planar_points(nodes0+b, nodes0+c, nodes1+b, nodes1+c);
00826         p3 = eval_planar_points(nodes0+c, nodes0+a, nodes1+c, nodes1+a);
00827         if (p1 > max) max = p1;
00828         if (p2 > max) max = p2;
00829         if (p3 > max) max = p3;
00830 
00831         if ( max > mold )
00832         {
00833             mcount++;
00834             if ( opts->debug > 2 )
00835             {
00836                 fprintf(stderr,"** new max (%d) for face %d, m = %f, m0 = %f\n",
00837                         mcount, face, max, mold );
00838                 disp_f3_point("  a0 = ", nodes0+a);
00839                 disp_f3_point("  b0 = ", nodes0+b);
00840                 disp_f3_point("  c0 = ", nodes0+c);
00841                 disp_f3_point("  a1 = ", nodes1+a);
00842                 disp_f3_point("  b1 = ", nodes1+b);
00843                 disp_f3_point("  c1 = ", nodes1+c);
00844             }
00845             mold = max*1.5;
00846         }
00847 
00848         flist += 3;
00849     }
00850 
00851     if ( max > 1.0 )    /* rcr - what test to use? */
00852         RETURN(1);
00853 
00854     RETURN(0);
00855 }
00856 
00857 
00858 /*----------------------------------------------------------------------
00859  * planar_points                - check whether points are co-planer
00860  *
00861  * return 0 if false
00862  *----------------------------------------------------------------------
00863 */
00864 float eval_planar_points( float * a, float * b, float * c, float * d )
00865 {
00866 ENTRY("planar_points");
00867 
00868     double d0[3], d1[3], d2[3];
00869     double xp[3];
00870     double result;
00871 
00872     d0[0] = c[0] - a[0];
00873     d0[1] = c[1] - a[1];
00874     d0[2] = c[2] - a[2];
00875 
00876     d1[0] = b[0] - a[0];
00877     d1[1] = b[1] - a[1];
00878     d1[2] = b[2] - a[2];
00879 
00880     d2[0] = d[0] - c[0];
00881     d2[1] = d[1] - c[1];
00882     d2[2] = d[2] - c[2];
00883 
00884     cross_product(xp, d1, d2);
00885 
00886     result = d0[0]*xp[0] + d0[1]*xp[1] + d0[2]*xp[2];
00887 
00888     RETURN(fabs(result));
00889 }
00890 
00891 #endif          /* test_planar_sides */
00892 
00893 
00894 /*----------------------------------------------------------------------
00895  * cross_product                - return the cross product of u and v
00896  *----------------------------------------------------------------------
00897 */
00898 int cross_product( double * res, double * u, double * v )
00899 {
00900     res[0] = u[1]*v[2] - u[2]*v[1];
00901     res[1] = u[2]*v[0] - u[0]*v[2];
00902     res[2] = u[0]*v[1] - u[1]*v[0];
00903 
00904     return 0;
00905 }
00906 
00907 
00908 /*----------------------------------------------------------------------
00909  * dot_product          - return the dot product of u and v
00910  *----------------------------------------------------------------------
00911 */
00912 double dot_product( double * u, double * v )
00913 {
00914     double res;
00915 
00916     res  = u[0]*v[0];
00917     res += u[1]*v[1];
00918     res += u[2]*v[2];
00919 
00920     return res;
00921 }
00922 
00923 
00924 /*----------------------------------------------------------------------
00925  * compute_node_areas                   - surface area for each node
00926  *
00927  * The area of a node is defined as one third of the area of the
00928  * associated triangles.
00929  *----------------------------------------------------------------------
00930 */
00931 int compute_node_areas( opts_t * opts, param_t * p, int sindex )
00932 {
00933     SUMA_SurfaceObject    * so;
00934     double                  sum;
00935     float                 * alist;
00936     int                   * flist;
00937     int                     node, c;
00938 
00939 ENTRY("compute_node_areas");
00940 
00941     if ( sindex < 0 || sindex >= p->S.nsurf || sindex >= 2 )
00942     {
00943         fprintf(stderr,"** cna: surf index <%d> is out of range\n",sindex);
00944         RETURN(-1);
00945     }
00946 
00947     so = p->S.slist[sindex];            /* just for ease of typing */
00948 
00949     if ( ! so->PolyArea )
00950     {
00951         fprintf(stderr,"** cna: no PolyArea to compute from\n");
00952         RETURN(-1);
00953     }
00954 
00955     alist = (float *)malloc(p->S.nnodes*sizeof(float));
00956     ALLOC_CHECK(alist, "float", p->S.nnodes);
00957     p->S.narea[sindex] = alist;
00958 
00959     for ( node = 0; node < p->S.nnodes; node++ )
00960     {
00961         flist = so->MF->NodeMemberOfFaceSet[node];
00962         sum = 0.0;
00963         for (c = 0; c < so->MF->N_Memb[node]; c++)
00964         {
00965             if ( flist[c] < 0 || flist[c] >= so->N_FaceSet )
00966             {
00967                 fprintf(stderr,"** cna: FaceSet mis-match flist,max = %d,%d\n",
00968                         flist[c], so->N_FaceSet);
00969                 free(p->S.narea[sindex]);
00970                 RETURN(-1);
00971             }
00972 
00973             sum += so->PolyArea[flist[c]];
00974         }
00975 
00976         alist[node] = sum/3.0;
00977 
00978         if ( node == opts->dnode )
00979             fprintf(stderr, "-- dnode %d: area = %s (%d faces, surf %s)\n",
00980                     node, MV_format_fval(alist[node]),
00981                     so->MF->N_Memb[node], CHECK_NULL_STR(so->Label));
00982     }
00983 
00984     RETURN(0);
00985 }
00986 
00987 
00988 /*----------------------------------------------------------------------
00989  * compute_node_vols                    - get volume at each node
00990  *
00991  * Set each node's volume to one third the sum of the included
00992  * triangle face volumes.
00993  *----------------------------------------------------------------------
00994 */
00995 int compute_node_vols( opts_t * opts, param_t * p )
00996 {
00997     SUMA_SurfaceObject    * so;
00998     double                  sum;
00999     float                 * nvols;
01000     int                   * flist;
01001     int                     node, c;
01002 
01003 ENTRY("compute_node_vols");
01004 
01005     so = p->S.slist[0];                 /* just for ease of typing */
01006 
01007     nvols = (float *)malloc(p->S.nnodes*sizeof(float));
01008     ALLOC_CHECK(nvols, "float", p->S.nnodes);
01009     p->S.nvol = nvols;
01010 
01011     for ( node = 0; node < p->S.nnodes; node++ )
01012     {
01013         if ( node == opts->dnode && opts->debug > 1 )
01014             fprintf(stderr,"-- dnode %d, facelist (%d) :\n        ",
01015                     node, so->MF->N_Memb[node]);
01016         flist = so->MF->NodeMemberOfFaceSet[node];
01017         sum = 0.0;
01018         for (c = 0; c < so->MF->N_Memb[node]; c++)
01019         {
01020             if ( flist[c] < 0 || flist[c] >= so->N_FaceSet )
01021             {
01022                 fprintf(stderr,"** cnv: FaceSet mis-match flist,max = %d,%d\n",
01023                         flist[c], so->N_FaceSet);
01024                 free(p->S.nvol);
01025                 RETURN(-1);
01026             }
01027             if ( node == opts->dnode && opts->debug > 1 )
01028                 fprintf(stderr,"  %d", flist[c]);
01029 
01030             sum += p->S.fvol[flist[c]];
01031         }
01032 
01033         nvols[node] = sum/3.0;
01034 
01035         if ( node == opts->dnode && opts->debug > 0 )
01036             fprintf(stderr,"\n-- volume = %f\n", sum);
01037     }
01038 
01039     RETURN(0);
01040 }
01041 
01042 
01043 /*----------------------------------------------------------------------
01044  * compute_face_vols                    - volume for each triangle face
01045  *
01046  * The volume corresponding to a triange face is the approximate volume
01047  * of the pentahedron formed by the pair of corresponding triangular faces.
01048  * It is computed (approximated) as the sum of three quadrahedrons, which
01049  * will be close to correct, and will give a very accurate total volume
01050  * (since the interior face volumes will perfectly fill the space).
01051  *----------------------------------------------------------------------
01052 */
01053 int compute_face_vols( opts_t * opts, param_t * p )
01054 {
01055     SUMA_SurfaceObject    * so0, *so1;
01056     double                  totalv;
01057     double                  v0, v1, v2;
01058     float                 * fvlist, * xyzn0, * xyzn1;
01059     float                   vmin, vmax;
01060     int                   * fp;
01061     int                     i0, i1, i2;
01062     int                     face, nnodes, test;
01063 
01064 ENTRY("compute_face_vols");
01065 
01066     so0 = p->S.slist[0];
01067     so1 = p->S.slist[1];
01068 
01069     if ( !so0 || !so1 )
01070     {
01071         fprintf(stderr,"** cfv: missing SurfaceObject (%p, %p)\n", so0, so1 );
01072         RETURN(-1);
01073     }
01074 
01075     fp    = so0->FaceSetList;  /* we need only 1, as they are the same */
01076     xyzn0 = so0->NodeList;
01077     xyzn1 = so1->NodeList;
01078     
01079     if ( !fp || !xyzn0 || !xyzn1 )
01080     {
01081         fprintf(stderr,"** cfv: missing face set list data (%p,%p,%p)\n",
01082                 fp, xyzn0, xyzn1);
01083         RETURN(-1);
01084     }
01085 
01086     fvlist = (float *)malloc(p->S.nfaces*sizeof(float));
01087     ALLOC_CHECK(fvlist, "float", p->S.nfaces);
01088     p->S.fvol = fvlist;
01089     
01090     vmin = 10000.0;  vmax = -1.0;
01091     nnodes = p->S.nnodes;
01092     totalv = 0.0;
01093     for ( face = 0; face < p->S.nfaces; face++ )
01094     {
01095         i0 = fp[0];  i1 = fp[1];  i2 = fp[2];
01096 
01097         if ( i0 < 0 || i1 < 0 || i2 < 0 ||
01098              i0 >= nnodes || i1 >= nnodes || i2 >= nnodes )
01099         {
01100             fprintf(stderr,"** cfv: face %d, index out of range [0,%d]\n"
01101                            "        indices are (%d,%d,%d)\n",
01102                            face, nnodes-1, i0, i1, i2);
01103             RETURN(-1);
01104         }
01105 
01106         test = (i0 == opts->dnode || i1 == opts->dnode || i2 == opts->dnode);
01107 
01108         i0 *= 3;  i1 *= 3;  i2 *= 3;    /* node index -> (float*) index */
01109 
01110         v0 = tetra_volume( xyzn0+i0, xyzn0+i1, xyzn0+i2, xyzn1+i0 );
01111         v1 = tetra_volume( xyzn0+i1, xyzn0+i2, xyzn1+i0, xyzn1+i1 );
01112         v2 = tetra_volume( xyzn0+i2, xyzn1+i0, xyzn1+i1, xyzn1+i2 );
01113 
01114         if ( v0 < 0.0 || v1 < 0.0 || v2 < 0.0 ||
01115              (opts->debug > 2 && test) )
01116         {
01117             fprintf(stderr,"** cfv: check volume: %f = %f + %f + %f, face %d\n",
01118                            v0+v1+v2, v0, v1, v2, face );
01119             fprintf(stderr,"        nodes %d, %d, %d\n", i0/3, i1/3, i2/3);
01120 
01121             if ( v0 < 0.0 || v1 < 0.0 || v2 < 0.0 )
01122                 RETURN(-1);
01123         }
01124 
01125         fvlist[face] = v0 + v1 + v2;
01126         totalv += fvlist[face];
01127         if ( fvlist[face] < vmin )  vmin = fvlist[face];
01128         if ( fvlist[face] > vmax )  vmax = fvlist[face];
01129 
01130         fp += 3;
01131     }
01132 
01133     if ( opts->debug > 0 )
01134     {
01135         fprintf(stderr,"++ total face volume = %f\n", totalv);
01136         if ( opts->debug > 1 )
01137         {
01138             fprintf(stderr,"++ volumes: faces 0, 1 (of %d), vols = %f, %f\n",
01139                     p->S.nfaces, fvlist[0], fvlist[1]);
01140             fprintf(stderr,"-- faces: vmin, vmax = %f, %f\n", vmin, vmax);
01141         }
01142     }
01143 
01144     RETURN(0);
01145 }
01146 
01147 
01148 /*----------------------------------------------------------------------
01149  * all_mappable_surfs           - fill surf_t struct, S
01150  *
01151  *----------------------------------------------------------------------
01152 */
01153 int all_mappable_surfs( opts_t * opts, param_t * p )
01154 {
01155     SUMA_SurfaceObject * so;
01156     int                  count;
01157 
01158 ENTRY("all_mappable_surfs");
01159 
01160     p->S.slist = (SUMA_SurfaceObject **)malloc(SUMAg_N_DOv *
01161                                                sizeof(SUMA_SurfaceObject *));
01162     ALLOC_CHECK(p->S.slist,"SUMA_SurfaceObject *",SUMAg_N_DOv);
01163 
01164     p->S.salloc = SUMAg_N_DOv;          /* number allocated for              */
01165     p->S.nsurf  = 0;                    /* number of mappable surfaces found */
01166 
01167     for ( count = 0; count < SUMAg_N_DOv; count++ )
01168     {
01169         if ( ! SUMA_isSO(SUMAg_DOv[count]) )
01170             continue;
01171 
01172         so = (SUMA_SurfaceObject *)SUMAg_DOv[count].OP;
01173 
01174 /*      if ( ! SUMA_isINHmappable(so) )       -  deprecated  [v1.5] */
01175 
01176 #if 0   /* do not require this [v1.9] */
01177 
01178         if ( ! so->AnatCorrect )
01179         {
01180             if ( opts->debug )
01181                 fprintf(stderr,"** warning: surface '%s' is not mappable, "
01182                         "skipping...\n", so->Label ? so->Label : "<unnamed>");
01183             if ( opts->debug > 1 )
01184                 fprintf(stderr,"** consider adding the following to the "
01185                                "surface definition in the spec file:\n"
01186                                "       Anatomical = Y\n");
01187             continue;
01188         }
01189 #endif
01190 
01191         if ( opts->debug > 1 )
01192         {
01193             fprintf(stderr,"-------- surface #%d (%s) --------\n",
01194                     p->S.nsurf, so->Label ? so->Label : "<unnamed>");
01195             if ( opts->debug > 3 )
01196                 SUMA_Print_Surface_Object(so, stderr);
01197         }
01198 
01199         p->S.slist[p->S.nsurf] = so;
01200         p->S.nsurf++;
01201     }
01202 
01203     if ( p->S.nsurf > 0 )
01204     {
01205         p->S.nnodes = p->S.slist[0]->N_Node;
01206         p->S.nfaces = p->S.slist[0]->N_FaceSet;
01207     }
01208 
01209     if ( opts->debug )
01210         fprintf(stderr, "++ found %d mappable surfaces\n", p->S.nsurf);
01211 
01212     RETURN(0);
01213 }
01214 
01215 
01216 /*----------------------------------------------------------------------
01217  * spec2SUMA            - call the SUMA functions for reading surfaces
01218  *
01219  *----------------------------------------------------------------------
01220 */
01221 int spec2SUMA( SUMA_SurfSpecFile * spec, opts_t * opts )
01222 {
01223     int rv;
01224 
01225 ENTRY("spec2SUMA");
01226 
01227     /* initialize common fields struct */
01228     SUMAg_CF = SUMA_Create_CommonFields();
01229 
01230     if ( SUMAg_CF == NULL )
01231     {
01232         fprintf( stderr, "** failed SUMA_Create_CommonFields(), exiting...\n" );
01233         RETURN(-1);
01234     }
01235 
01236     /* for SUMA type notifications */
01237     if ( opts->debug > 3 )
01238     {
01239         SUMAg_CF->MemTrace = 1;
01240 
01241         if ( opts->debug > 4 )
01242             SUMAg_CF->InOut_Notify = 1;
01243     }
01244 
01245     SUMAg_DOv = SUMA_Alloc_DisplayObject_Struct(SUMA_MAX_DISPLAYABLE_OBJECTS);
01246 
01247     if ( SUMA_Read_SpecFile( opts->spec_file, spec) == 0 )
01248     {
01249         fprintf( stderr, "** failed SUMA_Read_SpecFile(), exiting...\n" );
01250         RETURN(-1);
01251     }
01252 
01253     if ( opts->debug > 2 )
01254         SUMA_ShowSpecStruct(spec, stderr, 3);
01255 
01256     rv = SUMA_spec_select_surfs(spec,opts->surf_names,ST_MAX_SURFS,opts->debug);
01257     if ( rv < 1 )
01258     {
01259         if ( rv == 0 )
01260             fprintf(stderr,"** no given surfaces found in spec file\n");
01261         RETURN(-1);
01262     }
01263 
01264     if ( opts->debug > 1 )
01265         SUMA_ShowSpecStruct(spec, stderr, opts->debug > 2 ? 3 : 1);
01266 
01267     if ( SUMA_spec_set_map_refs(spec, opts->debug) != 0 )
01268         RETURN(-1);
01269 
01270     /* make sure only group was read from spec file */
01271     if ( spec->N_Groups != 1 )
01272     {
01273         fprintf( stderr,"** error: N_Groups <%d> must be 1 in spec file <%s>\n",
01274                  spec->N_Groups, opts->spec_file );
01275         RETURN(-1);
01276     }
01277 
01278     /* actually load the surface(s) from the spec file */
01279     if (SUMA_LoadSpec_eng(spec, SUMAg_DOv, &SUMAg_N_DOv, opts->sv_file,
01280                           opts->debug>3, SUMAg_CF->DsetList) == 0)
01281     {
01282         fprintf( stderr, "** error: failed SUMA_LoadSpec(), exiting...\n" );
01283         RETURN(-1);
01284     }
01285 
01286     if ( opts->debug > 0 )
01287         fputs( "++ surfaces loaded.\n", stderr );
01288 
01289     RETURN(0);
01290 }
01291 
01292 
01293 /*----------------------------------------------------------------------
01294  * add_to_flist                 - add function name and code to list
01295  *----------------------------------------------------------------------
01296 */
01297 int add_to_flist( func_t * F, char * fname )
01298 {
01299 ENTRY("add_to_flist");
01300 
01301     if ( F->nused >= F->nalloc )
01302     {
01303         F->nalloc += ST_DEFAULT_FALLOC;
01304         F->names   = (char **)realloc(F->names, F->nalloc*sizeof(char *));
01305         F->codes   = (int   *)realloc(F->codes, F->nalloc*sizeof(int   ));
01306 
01307         if ( !F->names || !F->codes )
01308         {
01309             fprintf(stderr,"** failed to allocate for %d functs (%p,%p)\n",
01310                     F->nalloc, F->names, F->codes);
01311             RETURN(-1);
01312         }
01313     }
01314 
01315     F->names[F->nused] = fname;
01316     F->codes[F->nused] = check_func_name(fname);
01317 
01318     if ( F->codes[F->nused] == E_SM_INVALID )
01319     {
01320         fprintf(stderr,"** function '%s' is not valid\n",
01321                 CHECK_NULL_STR(fname));
01322         RETURN(-1);
01323     }
01324 
01325     F->nused++;                 /* we've got another good one */
01326 
01327     RETURN(0);
01328 }
01329 
01330 
01331 /*----------------------------------------------------------------------
01332  * init_opts_t                  - initialize the struct
01333  *----------------------------------------------------------------------
01334 */
01335 int init_opts_t( opts_t * opts )
01336 {
01337     int c;
01338 
01339 ENTRY("init_opts_t");
01340 
01341     memset(opts, 0, sizeof(opts_t));
01342 
01343     /* init the function list func_t struct */
01344     opts->F.names  = NULL;
01345     opts->F.codes  = NULL;
01346     opts->F.nalloc = 0;
01347     opts->F.nused  = 0;
01348 
01349     /* just to try this out, init the info list */
01350     if ( add_to_flist(&opts->F, g_sm_names[E_SM_NODES]) != 0 )
01351     {
01352         fprintf(stderr,"** failed to init func_t list with '%s'\n",
01353                 g_sm_names[E_SM_NODES]);
01354         RETURN(-1);
01355     }
01356 
01357     opts->spec_file     = NULL;
01358     opts->sv_file       = NULL;
01359     opts->out_1D_file   = NULL;
01360     opts->cmask_cmd     = NULL;
01361     opts->nodes_1D_file = NULL;
01362 
01363     for ( c = 0; c < ST_MAX_SURFS; c++ )
01364         opts->surf_names[c] = NULL;
01365 
01366     opts->dnode         = -1;           /* init to something invalid */
01367 
01368     RETURN(0);
01369 }
01370 
01371 
01372 /*----------------------------------------------------------------------*/
01373 /* this macro is specifically for init_options(), below                 */
01374 
01375 #define CHECK_ARG_COUNT(ac,str)     \
01376         do {                        \
01377             if ((ac+1) >= argc) {   \
01378                 fputs(str,stderr);  \
01379                 RETURN(-1);         \
01380             }                       \
01381         } while (0)
01382 
01383 /*----------------------------------------------------------------------
01384  * init_options
01385  *----------------------------------------------------------------------
01386 */
01387 int init_options( opts_t * opts, int argc, char * argv[] )
01388 {
01389     int ac, ind;
01390 
01391 ENTRY("init_options");
01392 
01393     if ( argc < 2 )
01394         RETURN( usage(PROG_NAME, ST_USE_LONG) );
01395 
01396     /* init the structure to empty */
01397     if ( init_opts_t(opts) != 0 )
01398         RETURN(-1);
01399 
01400     for ( ac = 1; ac < argc; ac++ )
01401     {
01402         if ( ! strncmp(argv[ac], "-debug", 6) )
01403         {
01404             CHECK_ARG_COUNT(ac,"option usage: -debug LEVEL\n");
01405 
01406             opts->debug = atoi(argv[++ac]);
01407             if ( opts->debug < 0 || opts->debug > ST_DEBUG_MAX_LEVEL )
01408             {
01409                 fprintf(stderr,"** bad debug level %d, should be in [0,%d]\n",
01410                         opts->debug, ST_DEBUG_MAX_LEVEL);
01411                 RETURN(-1);
01412             }
01413         }
01414         else if ( ! strncmp(argv[ac], "-cmask", 6) )
01415         {
01416             CHECK_ARG_COUNT(ac,"option usage: -cmask COMMAND\n");
01417             opts->cmask_cmd = argv[++ac];
01418         }
01419         else if ( ! strncmp(argv[ac], "-dnode", 6) )
01420         {
01421             CHECK_ARG_COUNT(ac,"option usage: -dnode NODE_NUM\n");
01422             opts->dnode = atoi(argv[++ac]);
01423         }
01424         else if ( ! strncmp(argv[ac], "-func", 5) )
01425         {
01426             CHECK_ARG_COUNT(ac,"option usage: -func FUNCTION\n");
01427             ++ac;
01428             if ( add_to_flist(&opts->F, argv[ac]) != 0 )
01429                 RETURN(-1);
01430         }
01431         else if ( ! strncmp(argv[ac], "-help", 5) )
01432             RETURN(usage(PROG_NAME, ST_USE_LONG));
01433         else if ( ! strncmp(argv[ac], "-hist", 5) )
01434             RETURN(usage(PROG_NAME, ST_USE_HIST));
01435         else if ( ! strncmp(argv[ac], "-info_all",9) )
01436         {
01437             opts->info |= ST_INFO_ALL;
01438         }
01439         else if ( ! strncmp(argv[ac], "-info_area",10) )
01440         {
01441             opts->info |= ST_INFO_AREA;
01442         }
01443         else if ( ! strncmp(argv[ac], "-info_norms",10) )
01444         {
01445             opts->info |= ST_INFO_NORMS;
01446         }
01447         else if ( ! strncmp(argv[ac], "-info_thick",11) )
01448         {
01449             opts->info |= ST_INFO_THICK;
01450         }
01451         else if ( ! strncmp(argv[ac], "-info_vol",9) )
01452         {
01453             opts->info |= ST_INFO_VOL;
01454         }
01455         else if ( ! strncmp(argv[ac], "-nodes_1D", 9) )
01456         {
01457             CHECK_ARG_COUNT(ac,"option usage: -nodes_1D NODE_LIST_FILE\n");
01458             opts->nodes_1D_file = argv[++ac];
01459         }
01460         else if ( ! strncmp(argv[ac], "-out_1D", 7) )
01461         {
01462             CHECK_ARG_COUNT(ac,"option usage: -out_1D OUTPUT_FILE\n");
01463             opts->out_1D_file = argv[++ac];
01464         }
01465         else if ( ! strncmp(argv[ac], "-spec", 5) )
01466         {
01467             CHECK_ARG_COUNT(ac,"option usage: -spec SPEC_FILE\n");
01468             opts->spec_file = argv[++ac];
01469         }
01470         else if ( ! strncmp(argv[ac], "-surf_", 6) )
01471         {
01472             CHECK_ARG_COUNT(ac,"option usage: -surf_X SURF_NAME\n");
01473             ind = argv[ac][6] - 'A';
01474             if ( (ind < 0) || (ind >= ST_MAX_SURFS) )
01475             {
01476                 fprintf(stderr,"** -surf_X option '%s' out of range,\n"
01477                         "   use one of '-surf_A' through '-surf_%c'\n",
01478                         argv[ac], 'A'+ST_MAX_SURFS-1);
01479                 RETURN(-1);
01480             }
01481             opts->surf_names[ind] = argv[++ac];
01482         }
01483         else if ( ! strncmp(argv[ac], "-sv", 3) )
01484         {
01485             CHECK_ARG_COUNT(ac,"option usage: -sv SURF_VOLUME\n");
01486             opts->sv_file = argv[++ac];
01487         }
01488         else if ( ! strncmp(argv[ac], "-ver",4) )
01489         {
01490             RETURN( usage(PROG_NAME, ST_USE_VERSION) );
01491         }
01492         else
01493         {
01494             fprintf(stderr,"invalid option <%s>\n",argv[ac]);
01495             RETURN( usage(PROG_NAME, ST_USE_SHORT) );
01496         }
01497     }
01498 
01499     RETURN(0);
01500 }
01501 
01502 
01503 /*----------------------------------------------------------------------
01504  * validate_options
01505  *----------------------------------------------------------------------
01506 */
01507 int validate_options( opts_t * opts, param_t * p )
01508 {
01509     int errs = 0;
01510 
01511 ENTRY("validate_options");    
01512 
01513     if ( !opts || !p )
01514     {
01515         fprintf(stderr,"** vo: bad params (%p,%p)\n", opts, p);
01516         RETURN(-1);
01517     }
01518 
01519     if ( opts->F.nused <= 0 )
01520     {
01521         fprintf(stderr,"** must specify at least one '-func' option\n");
01522         errs++;
01523     }
01524 
01525     if ( ! opts->spec_file )
01526     {
01527         fprintf(stderr,"** missing argument: -spec\n");
01528         errs++;
01529     }
01530 
01531     if ( ! opts->surf_names[0] )
01532     {
01533         fprintf(stderr,"** missing argument -surf_A\n");
01534         errs++;
01535     }
01536 
01537     /* we don't necessarily need an sv_file ... do not check */
01538 
01539     /* verify output file, and open for writing */
01540     if ( ! opts->out_1D_file )
01541     {
01542         fprintf(stderr,"** missing argument: -out_1D\n");
01543         errs++;
01544     }
01545     else if ( THD_is_file(opts->out_1D_file) )
01546     {
01547         fprintf(stderr,"** output file already exists: %s\n",opts->out_1D_file);
01548         errs++;
01549     }
01550 
01551     if ( errs > 0 )
01552         RETURN(-1);
01553 
01554     if ( opts->debug > 1 )
01555     {
01556         disp_opts_t( "-- opts okay: ", opts );
01557         disp_func_t( "-- opts okay: ", &opts->F );
01558     }
01559 
01560 
01561     /* options look good, now fill the param_t struct */
01562     memset(p, 0, sizeof(param_t));      /* clear params    */
01563 
01564     p->S.slist    = NULL;                       /* to be safe...   */
01565     p->S.narea[0] = NULL;
01566     p->S.narea[1] = NULL;
01567     p->S.nvol     = NULL;
01568     p->S.fvol     = NULL;
01569 
01570     p->F          = &opts->F;                   /* point to struct */
01571 
01572     if ( (p->outfp = fopen(opts->out_1D_file, "w")) == NULL )
01573     {
01574         fprintf(stderr,"** cannot open output file '%s'\n",opts->out_1D_file);
01575         RETURN(-1);
01576     }
01577 
01578     /* init before filling */
01579     p->nodes  = NULL;
01580     p->nnodes = 0;
01581     p->cmask  = NULL;
01582     p->ncmask = 0;
01583 
01584     if ( opts->nodes_1D_file )
01585     {
01586         if ( read_nodes_file(opts, p) != 0 )
01587             RETURN(-1);
01588     }
01589 
01590     if ( opts->cmask_cmd )
01591     {
01592         if ( get_cmask(opts, p) != 0 )
01593             RETURN(-1);
01594     }
01595 
01596     RETURN(0);
01597 }
01598 
01599 
01600 /*----------------------------------------------------------------------
01601  * get_cmask                            - get cmask from command
01602  *----------------------------------------------------------------------
01603 */
01604 int get_cmask( opts_t * opts, param_t * p )
01605 {
01606     char * cmd;
01607     int    clen;
01608 
01609 ENTRY("get_cmask");
01610 
01611     if ( ! opts->cmask_cmd )
01612         RETURN(0);
01613 
01614     clen = strlen(opts->cmask_cmd);
01615     cmd  = (char *)malloc((clen + 1)*sizeof(char));
01616     ALLOC_CHECK(cmd, "char", clen);
01617 
01618     strcpy(cmd, opts->cmask_cmd);
01619 
01620     p->cmask = EDT_calcmask(cmd, &p->ncmask);
01621 
01622     free(cmd);          /* we are done with the now corrupted command */
01623 
01624     if ( ! p->cmask || p->ncmask < 1 )
01625     {
01626         fprintf(stderr,"** failure: cannot compute cmask from option:\n"
01627                 "   -cmask '%s'\n", opts->cmask_cmd);
01628         RETURN(-1);
01629     }
01630 
01631     p->ccount = THD_countmask( p->ncmask, p->cmask );
01632 
01633     if ( p->ccount < 1 )                /* do not quit */
01634         fprintf(stderr,"** warning: cmask is empty from option\n"
01635                 "   -cmask '%s'\n", opts->cmask_cmd);
01636 
01637     if ( opts->debug > 0 )
01638         fprintf(stderr,"++ have cmask with %d of %d set entries\n",
01639                 p->ccount, p->ncmask);
01640 
01641     RETURN(0);
01642 }
01643 
01644 
01645 /*----------------------------------------------------------------------
01646  * read_nodes_file                      - read 1D file with node list
01647  *----------------------------------------------------------------------
01648 */
01649 int read_nodes_file( opts_t * opts, param_t * p )
01650 {
01651     MRI_IMAGE * im;
01652     float     * fim;
01653     char      * nfile;
01654     int         index;
01655 
01656 ENTRY("read_nodes_file");
01657 
01658     nfile = opts->nodes_1D_file;                /* for ease of typing */
01659 
01660     if ( !nfile )
01661         RETURN(0);
01662 
01663     if ( (im = mri_read_1D(nfile)) == NULL )
01664     {
01665         fprintf(stderr,"** failed to read 1D nodes file '%s'\n", nfile);
01666         RETURN(-1);
01667     }
01668     
01669     if ( im->nx < 1 )
01670     {
01671         fprintf(stderr,"** node list file '%s' appears empty\n", nfile);
01672         RETURN(-1);
01673     }
01674 
01675     if ( im->ny > 1 )
01676     {
01677         fprintf(stderr,"** please specify only one column of node file '%s'\n"
01678                        "   e.g.  -nodes_1D 'surf_data.1D[0]'\n", nfile);
01679         RETURN(-1);
01680     }
01681 
01682     p->nnodes = im->nx;
01683     p->nodes  = (int *)malloc(im->nx * sizeof(int));
01684     ALLOC_CHECK(p->nodes, "int", im->nx);
01685 
01686     /* now get values */
01687     fim = MRI_FLOAT_PTR(im);
01688     for ( index = 0; index < p->nnodes; index++ )
01689         p->nodes[index] = (int)fim[index];
01690 
01691     if ( opts->debug > 0 )
01692         fprintf(stderr,"++ read %d node indices from '%s'\n", p->nnodes, nfile);
01693 
01694     mri_free(im);               /* we're done with image */
01695 
01696     RETURN(0);
01697 }
01698 
01699 
01700 /* so I don't have to repeat this all over (and can do it per function)... */
01701 
01702 #define SM_2SURF_TEST(code)                                                 \
01703             do{ if (p->S.nsurf<2) {                                         \
01704                     fprintf(stderr,"** function %s requires 2 surfaces\n",  \
01705                             g_sm_names[fcodes[code]]);                      \
01706                     errs++;                                                 \
01707                 }                                                           \
01708             } while (0);
01709 
01710 /*----------------------------------------------------------------------
01711  * validate_option_lists                - check function and info entries
01712  *----------------------------------------------------------------------
01713 */
01714 int validate_option_lists( opts_t * opts, param_t * p )
01715 {
01716     int * fcodes, c, errs;
01717 
01718 ENTRY("validate_option_lists");
01719 
01720     errs = 0;
01721 
01722     if ( (opts->info & ST_INFO_THICK) && p->S.nsurf < 2 )
01723     {
01724         fprintf(stderr,"** -info_thick option requiers 2 surfaces\n");
01725         errs++;
01726     }
01727 
01728     if ( (opts->info & ST_INFO_VOL) && p->S.nsurf < 2 )
01729     {
01730         fprintf(stderr,"** -info_vol option requiers 2 surfaces\n");
01731         errs++;
01732     }
01733 
01734     /* verify all the functions in our list */
01735     fcodes = p->F->codes;       /* just for less typing */
01736     for ( c = 0; c < p->F->nused; c++ )
01737     {
01738         switch (fcodes[c])
01739         {
01740             default:
01741                 fprintf(stderr, "** vol: invalid function code %d\n",fcodes[c]);
01742                 errs++;
01743                 break;
01744 
01745             case E_SM_COORD_A:
01746             case E_SM_N_AREA_A:
01747             case E_SM_N_AVEAREA_A:
01748             case E_SM_NODES:
01749             case E_SM_NORM_A:
01750                 break;
01751 
01752             case E_SM_ANG_NS_A:
01753                 if ( !p->S.slist[0]->NodeNormList )
01754                 {
01755                     fprintf(stderr,"** missing node normals for func '%s'\n",
01756                             g_sm_names[fcodes[c]]);
01757                     errs++;
01758                 }
01759                 break;
01760 
01761             case E_SM_ANG_NORMS:
01762             case E_SM_ANG_NS_B:
01763             case E_SM_NORM_B:
01764                 if ( !p->S.slist[0]->NodeNormList ||
01765                      !p->S.slist[1]->NodeNormList )
01766                 {
01767                     fprintf(stderr,"** missing node normals for func '%s'\n",
01768                             g_sm_names[fcodes[c]]);
01769                     errs++;
01770                 }
01771 
01772                 SM_2SURF_TEST(c);
01773                 break;
01774                 
01775             case E_SM_COORD_B:
01776             case E_SM_N_AREA_B:
01777             case E_SM_N_AVEAREA_B:
01778             case E_SM_NODE_VOL:
01779             case E_SM_THICK:
01780 
01781                 SM_2SURF_TEST(c);
01782                 break;
01783 
01784             case E_SM_NTRI:
01785                 if ( ! p->S.slist[0]->MF->N_Memb )
01786                 {
01787                     fprintf(stderr,"** missing Face Member list, func '%s'\n",
01788                             g_sm_names[fcodes[c]]);
01789                     errs++;
01790                 }
01791 
01792                 break;
01793         }
01794 
01795         if ( opts->debug > 1 && errs == 0 )
01796             fprintf(stderr,"++ have valid function name '%s'\n",
01797                     g_sm_names[fcodes[c]]);
01798     }
01799 
01800     if ( opts->debug > 0 && errs > 0 )
01801         fprintf(stderr,"** validate_option_lists: %d errors found\n", errs);
01802 
01803     if ( errs > 0 )
01804         RETURN(-1);
01805 
01806     if ( opts->debug > 0 )
01807         fprintf(stderr,"-- found %d output functions\n", p->F->nused);
01808 
01809     RETURN(0);
01810 }
01811 
01812 
01813 
01814 /*----------------------------------------------------------------------
01815  * usage              - provide info, depending on the type
01816  *
01817  * ST_USE_SHORT
01818  * ST_USE_LONG
01819  * ST_USE_VERSION
01820  *
01821  * return -1 to signal this as a terminal function
01822  *----------------------------------------------------------------------
01823 */
01824 int usage( char * prog, int use_type )
01825 {
01826     int c;
01827 
01828 ENTRY("usage");
01829 
01830     if ( use_type == ST_USE_SHORT )
01831     {
01832         fprintf(stderr,"usage: %s [options] -spec SPEC_FILE -func FUNC_NAME\\\n"
01833                        "                    -out_1D OUTFILE\n", prog);
01834     }
01835     else if ( use_type == ST_USE_LONG )
01836     {
01837         printf(
01838             "\n"
01839             "%s - compute measures from the surface dataset(s)\n"
01840             "\n"
01841             "  usage: %s [options] -spec SPEC_FILE -out_1D OUTFILE.1D\n"
01842             "\n"
01843             "    This program is meant to read in a surface or surface pair,\n"
01844             "    and to output and user-requested measures over the surfaces.\n"
01845             "    The surfaces must be specified in the SPEC_FILE.\n"
01846             "\n"
01847             " ** Use the 'inspec' command for getting information about the\n"
01848             "    surfaces in a spec file.\n"
01849             "\n"
01850             "    The output will be a 1D format text file, with one column\n"
01851             "    (or possibly 3) per user-specified measure function.  Some\n"
01852             "    functions require only 1 surface, some require 2.\n"
01853             "\n"
01854             "    Current functions (applied with '-func') include:\n"
01855             "\n",
01856             prog, prog );
01857 
01858         /* display current list of measures */
01859         for ( c = E_SM_INVALID + 1; c < E_SM_FINAL; c++ )
01860             printf( "        %-12s : %s\n", g_sm_names[c], g_sm_desc[c]);
01861 
01862         printf(
01863             "\n"
01864             "------------------------------------------------------------\n"
01865             "\n"
01866             "  examples:\n"
01867             "\n"
01868             "    1. For each node on the surface smoothwm in the spec file,\n"
01869             "       fred.spec, output the node number (the default action),\n"
01870             "       the xyz coordinates, and the area associated with the\n"
01871             "       node (1/3 of the total area of triangles having that node\n"
01872             "       as a vertex).\n"
01873             "\n"
01874             "        %s                                   \\\n"
01875             "            -spec       fred1.spec                     \\\n"
01876             "            -sv         fred_anat+orig                 \\\n"
01877             "            -surf_A     smoothwm                       \\\n"
01878             "            -func       coord_A                        \\\n"
01879             "            -func       n_area_A                       \\\n"
01880             "            -out_1D     fred1_areas.1D                   \n"
01881             "\n"
01882             "    2. For each node of the surface pair smoothwm and pial,\n"
01883             "       display the:\n"
01884             "         o  node index\n"
01885             "         o  node's area from the first surface\n"
01886             "         o  node's area from the second surface\n"
01887             "         o  node's (approximate) resulting volume\n"
01888             "         o  thickness at that node (segment distance)\n"
01889             "         o  coordinates of the first segment node\n"
01890             "         o  coordinates of the second segment node\n"
01891             "\n"
01892             "         Additionally, display total surface areas, minimum and\n"
01893             "         maximum thicknesses, and the total volume for the\n"
01894             "         cortical ribbon (the sum of node volumes).\n"
01895             "\n"
01896             "        %s                                   \\\n"
01897             "            -spec       fred2.spec                     \\\n"
01898             "            -sv         fred_anat+orig                 \\\n"
01899             "            -surf_A     smoothwm                       \\\n"
01900             "            -surf_B     pial                           \\\n"
01901             "            -func       n_area_A                       \\\n"
01902             "            -func       n_area_B                       \\\n"
01903             "            -func       node_vol                       \\\n"
01904             "            -func       thick                          \\\n"
01905             "            -func       coord_A                        \\\n"
01906             "            -func       coord_B                        \\\n"
01907             "            -info_area                                 \\\n"
01908             "            -info_thick                                \\\n"
01909             "            -info_vol                                  \\\n"
01910             "            -out_1D     fred2_vol.1D                     \n"
01911             "\n"
01912             "    3. For each node of the surface pair, display the:\n"
01913             "         o  node index\n"
01914             "         o  angular diff between the first and second norms\n"
01915             "         o  angular diff between the segment and first norm\n"
01916             "         o  angular diff between the segment and second norm\n"
01917             "         o  the normal vectors for the first surface nodes\n"
01918             "         o  the normal vectors for the second surface nodes\n"
01919             "         o  angular diff between the segment and second norm\n"
01920             "\n"
01921             "        %s                                   \\\n"
01922             "            -spec       fred2.spec                     \\\n"
01923             "            -surf_A     smoothwm                       \\\n"
01924             "            -surf_B     pial                           \\\n"
01925             "            -func       ang_norms                      \\\n"
01926             "            -func       ang_ns_A                       \\\n"
01927             "            -func       ang_ns_B                       \\\n"
01928             "            -func       norm_A                         \\\n"
01929             "            -func       norm_B                         \\\n"
01930             "            -out_1D     fred2_norm_angles.1D             \n"
01931             "\n"
01932             "    4. Similar to #3, but output extra debug info, and in\n"
01933             "       particular, info regarding node 5000.\n"
01934             "\n"
01935             "        %s                                   \\\n"
01936             "            -spec       fred2.spec                     \\\n"
01937             "            -sv         fred_anat+orig                 \\\n"
01938             "            -surf_A     smoothwm                       \\\n"
01939             "            -surf_B     pial                           \\\n"
01940             "            -func       ang_norms                      \\\n"
01941             "            -func       ang_ns_A                       \\\n"
01942             "            -func       ang_ns_B                       \\\n"
01943             "            -debug      2                              \\\n"
01944             "            -dnode      5000                           \\\n"
01945             "            -out_1D     fred2_norm_angles.1D             \n"
01946             "\n"
01947             "    5. For each node, output the volume, thickness and areas,\n"
01948             "       but restrict the nodes to the list contained in column 0\n"
01949             "       of file sdata.1D.  Furthermore, restrict those nodes to\n"
01950             "       the mask inferred by the given '-cmask' option.\n"
01951             "\n"
01952             "        %s                                                   \\\n"
01953             "            -spec       fred2.spec                           \\\n"
01954             "            -sv         fred_anat+orig                       \\\n"
01955             "            -surf_A     smoothwm                             \\\n"
01956             "            -surf_B     pial                                 \\\n"
01957             "            -func       node_vol                             \\\n"
01958             "            -func       thick                                \\\n"
01959             "            -func       n_area_A                             \\\n"
01960             "            -func       n_area_B                             \\\n"
01961             "            -nodes_1D   'sdata.1D[0]'                        \\\n"
01962             "            -cmask      '-a sdata.1D[2] -expr step(a-1000)'  \\\n"
01963             "            -out_1D     fred2_masked.1D                  \n"
01964             "\n"
01965             "------------------------------------------------------------\n"
01966             "\n"
01967             "  REQUIRED COMMAND ARGUMENTS:\n"
01968             "\n"
01969             "    -spec SPEC_FILE       : SUMA spec file\n"
01970             "\n"
01971             "        e.g. -spec fred2.spec\n"
01972             "\n"
01973             "        The surface specification file contains a list of\n"
01974             "        related surfaces.  In order for a surface to be\n"
01975             "        processed by this program, it must exist in the spec\n"
01976             "        file.\n"
01977             "\n"
01978             "    -surf_A SURF_NAME     : surface name (in spec file)\n"
01979             "    -surf_B SURF_NAME     : surface name (in spec file)\n"
01980             "\n"
01981             "        e.g. -surf_A smoothwm\n"
01982             "        e.g. -surf_A lh.smoothwm\n"
01983             "        e.g. -surf_B lh.pial\n"
01984             "\n"
01985             "        This is used to specify which surface(s) will be used\n"
01986             "        by the program.  The 'A' and 'B' correspond to other\n"
01987             "        program options (e.g. the 'A' in n_area_A).\n"
01988             "\n"
01989             "        The '-surf_B' parameter is required only when the user\n"
01990             "        wishes to input two surfaces.\n"
01991             "\n"
01992             "        Any surface name provided must be unique in the spec\n"
01993             "        file, and must match the name of the surface data file\n"
01994             "        (e.g. lh.smoothwm.asc).\n"
01995             "\n"
01996             "    -out_1D OUT_FILE.1D   : 1D output filename\n"
01997             "\n"
01998             "        e.g. -out_1D pickle_norm_info.1D\n"
01999             "\n"
02000             "        This option is used to specify the name of the output\n"
02001             "        file.  The output file will be in the 1D ascii format,\n"
02002             "        with 2 rows of comments for column headers, and 1 row\n"
02003             "        for each node index.\n"
02004             "\n"
02005             "        There will be 1 or 3 columns per '-func' option, with\n"
02006             "        a default of 1 for \"nodes\".\n"
02007             "\n"
02008             "------------------------------------------------------------\n"
02009             "\n"
02010             "  ALPHABETICAL LISTING OF OPTIONS:\n"
02011             "\n"
02012             "    -cmask COMMAND        : restrict nodes with a mask\n"
02013             "\n"
02014             "        e.g.     -cmask '-a sdata.1D[2] -expr step(a-1000)'\n"
02015             "\n"
02016             "        This option will produce a mask to be applied to the\n"
02017             "        list of surface nodes.  The total mask size, including\n"
02018             "        zero entries, must match the number of nodes.  If a\n"
02019             "        specific node list is provided via the '-nodes_1D'\n"
02020             "        option, then the mask size should match the length of\n"
02021             "        the provided node list.\n"
02022             "        \n"
02023             "        Consider the provided example using the file sdata.1D.\n"
02024             "        If a surface has 100000 nodes (and no '-nodes_1D' option\n"
02025             "        is used), then there must be 100000 values in column 2\n"
02026             "        of the file sdata.1D.\n"
02027             "\n"
02028             "        Alternately, if the '-nodes_1D' option is used, giving\n"
02029             "        a list of 42 nodes, then the mask length should also be\n"
02030             "        42 (regardless of 0 entries).\n"
02031             "\n"
02032             "        See '-nodes_1D' for more information.\n"
02033             "\n"
02034             "    -debug LEVEL          : display extra run-time info\n"
02035             "\n"
02036             "        e.g.     -debug 2\n"
02037             "        default: -debug 0\n"
02038             "\n"
02039             "        Valid debug levels are from 0 to 5.\n"
02040             "\n"
02041             "    -dnode NODE           : display extra info for node NODE\n"
02042             "\n"
02043             "        e.g. -dnode 5000\n"
02044             "\n"
02045             "        This option can be used to display extra information\n"
02046             "        about node NODE during surface evaluation.\n"
02047             "\n"
02048             "    -func FUNCTION        : request output for FUNCTION\n"
02049             "\n"
02050             "        e.g. -func thick\n"
02051             "\n"
02052             "        This option is used to request output for the given\n"
02053             "        FUNCTION (measure).  Some measures produce one column\n"
02054             "        of output (e.g. thick or ang_norms), and some produce\n"
02055             "        three (e.g. coord_A).  These options, in the order they\n"
02056             "        are given, determine the structure of the output file.\n"
02057             "\n"
02058             "        Current functions include:\n"
02059             "\n",
02060             prog, prog, prog, prog, prog );
02061 
02062         /* display current list of measures */
02063         for ( c = E_SM_INVALID + 1; c < E_SM_FINAL; c++ )
02064             printf( "            %-12s : %s\n", g_sm_names[c], g_sm_desc[c]);
02065 
02066         printf(
02067             "\n"
02068             "          Note that the node volumes are approximations.  Places\n"
02069             "          where either normal points in the 'wrong' direction\n"
02070             "          will be incorrect, as will be the parts of the surface\n"
02071             "          that 'encompass' this region.  Maybe we could refer\n"
02072             "          to this as a mushroom effect...\n"
02073             "\n"
02074             "          Basically, expect the total volume to be around 10%%\n"
02075             "          too large.\n"
02076             "\n"
02077             "          ** for more accuracy, try 'SurfPatch -vol' **\n"
02078             "\n"
02079             "    -help                 : show this help menu\n"
02080             "\n"
02081             "    -hist                 : display program revision history\n"
02082             "\n"
02083             "        This option is used to provide a history of changes\n"
02084             "        to the program, along with version numbers.\n"
02085             "\n"
02086             "  NOTE: the following '-info_XXXX' options are used to display\n"
02087             "        pieces of 'aggregate' information about the surface(s).\n"
02088             "\n"
02089             "    -info_all             : display all final info\n"
02090             "\n"
02091             "        This is a short-cut to get all '-info_XXXX' options.\n"
02092             "\n"
02093             "    -info_area            : display info on surface area(s)\n"
02094             "\n"
02095             "        Display the total area of each triangulated surface.\n"
02096             "\n"
02097             "    -info_norms           : display info about the normals\n"
02098             "\n"
02099             "        For 1 or 2 surfaces, this will give (if possible) the\n"
02100             "        average angular difference between:\n"
02101             "\n"
02102             "            o the normals of the surfaces\n"
02103             "            o the connecting segment and the first normal\n"
02104             "            o the connecting segment and the second normal\n"
02105             "\n"
02106             "    -info_thick           : display min and max thickness\n"
02107             "\n"
02108             "        For 2 surfaces, this is used to display the minimum and\n"
02109             "        maximum distances between the surfaces, along each of\n"
02110             "        the connecting segments.\n"
02111             "\n"
02112             "    -info_vol             : display info about the volume\n"
02113             "\n"
02114             "        For 2 surfaces, display the total computed volume.\n"
02115             "        Note that this node-wise volume computation is an\n"
02116             "        approximation, and tends to run ~10 %% high.\n"
02117             "\n"
02118             "        ** for more accuracy, try 'SurfPatch -vol' **\n"
02119             "\n"
02120             "    -nodes_1D NODELIST.1D : request output for only these nodes\n"
02121             "\n"
02122             "        e.g.  -nodes_1D node_index_list.1D\n"
02123             "        e.g.  -nodes_1D sdata.1D'[0]'\n"
02124             "\n"
02125             "        The NODELIST file should contain a list of node indices.\n"
02126             "        Output from the program would then be restricted to the\n"
02127             "        nodes in the list.\n"
02128             "        \n"
02129             "        For instance, suppose that the file BA_04.1D contains\n"
02130             "        a list of surface nodes that are located in Broadman's\n"
02131             "        Area 4.  To get output from the nodes in that area, use:\n"
02132             "        \n"
02133             "            -nodes_1D BA_04.1D\n"
02134             "        \n"
02135             "        For another example, suppose that the file sdata.1D has\n"
02136             "        node indices in column 0, and Broadman's Area indices in\n"
02137             "        column 3.  To restrict output to the nodes in Broadman's\n"
02138             "        area 4, use the pair of options:\n"
02139             "        \n"
02140             "            -nodes_1D 'sdata.1D[0]'                     \\\n"
02141             "            -cmask '-a sdata.1D[3] -expr (1-bool(a-4))' \n"
02142             "\n"
02143             "    -sv SURF_VOLUME       : specify an associated AFNI volume\n"
02144             "\n"
02145             "        e.g. -sv fred_anat+orig\n"
02146             "\n"
02147             "        If there is any need to know the orientation of the\n"
02148             "        surface, a surface volume dataset may be provided.\n"
02149             "\n"
02150             "    -ver                  : show version information\n"
02151             "\n"
02152             "        Show version and compile date.\n"
02153             "\n"
02154             "------------------------------------------------------------\n"
02155             "\n"
02156             "  Author: R. Reynolds  - %s\n"
02157             "\n",
02158             VERSION );
02159     }
02160     else if ( use_type == ST_USE_HIST )
02161     {
02162         fputs (g_history, stdout);
02163     }
02164     else if ( use_type == ST_USE_VERSION )
02165     {
02166         printf("%s: %s, compile date: %s\n", prog, VERSION, __DATE__);
02167     }
02168     else
02169         fprintf(stderr,"** error: usage - invalid use_type %d\n", use_type); 
02170 
02171     RETURN(-1);
02172 }
02173 
02174 
02175 /*----------------------------------------------------------------------
02176  * check_func_name                      - return function code for name
02177  *----------------------------------------------------------------------
02178 */
02179 int check_func_name( char * func )
02180 {
02181     int fnum;
02182 
02183 ENTRY("check_func_name");
02184 
02185     if ( !func )
02186         RETURN(E_SM_INVALID);
02187 
02188     /* just to be safe, let's verify the names and enum */
02189 
02190     if ( sizeof(g_sm_names)/sizeof(char *) != (int)E_SM_FINAL )
02191     {
02192         fprintf(stderr,"** error: g_sm_names mis-match\n");
02193         RETURN(E_SM_INVALID);
02194     }
02195 
02196     if ( sizeof(g_sm_desc)/sizeof(char *) != (int)E_SM_FINAL )
02197     {
02198         fprintf(stderr,"** error: g_sm_desc mis-match\n");
02199         RETURN(E_SM_INVALID);
02200     }
02201 
02202     for ( fnum = E_SM_INVALID; fnum < E_SM_FINAL; fnum++ )
02203         if ( !strcmp(func, g_sm_names[fnum]) )
02204             RETURN(fnum);
02205 
02206     RETURN(E_SM_INVALID);
02207 }
02208 
02209 
02210 /*----------------------------------------------------------------------
02211  * final_cleanup                        - free memory
02212  *----------------------------------------------------------------------
02213 */
02214 int final_cleanup( opts_t * opts, param_t * p )
02215 {
02216 
02217 ENTRY("final_cleanup");
02218 
02219     /* first, close the output file, the rest are in order */
02220     if ( p->outfp != stdout )
02221         fclose(p->outfp);
02222 
02223     if ( p->S.narea[0] )  free(p->S.narea[0]);
02224     if ( p->S.narea[1] )  free(p->S.narea[1]);
02225     if ( p->S.slist    )  free(p->S.slist);
02226     if ( p->S.nvol     )  free(p->S.nvol);
02227     if ( p->S.fvol     )  free(p->S.fvol);
02228 
02229     if ( p->F->nalloc > 0 )
02230     {
02231         free(p->F->names);
02232         free(p->F->codes);
02233     }
02234 
02235     if ( p->nodes )
02236         free(p->nodes);
02237 
02238     if ( p->cmask )
02239         free(p->cmask);
02240 
02241     if ( opts->debug > 2 )
02242         fprintf(stderr,"-- freeing SUMA data...\n");
02243 
02244     if ( ( SUMAg_DOv != NULL ) &&
02245          ( SUMA_Free_Displayable_Object_Vect(SUMAg_DOv, SUMAg_N_DOv) == 0 ) )
02246         fprintf(stderr, "** failed SUMA_Free_Displayable_Object_Vect()\n" );
02247 
02248     if ( ( SUMAg_SVv != NULL ) &&
02249          ( SUMA_Free_SurfaceViewer_Struct_Vect(SUMAg_SVv, SUMAg_N_SVv) == 0 ) )
02250         fprintf( stderr, "** failed SUMA_Free_SurfaceViewer_Struct_Vect()\n" );
02251 
02252     if ( ( SUMAg_CF != NULL ) && ( SUMA_Free_CommonFields(SUMAg_CF) == 0 ) )
02253         fprintf( stderr, "** failed SUMA_Free_CommonFields()\n" );
02254 
02255     RETURN(0);
02256 }
02257 
02258 
02259 /*----------------------------------------------------------------------
02260  * tetra_volume                 - retrun the volume of the tetrahedron
02261  *
02262  *      V = 1/6 * |a o (b x c)|               -- mathworld.wolfram.com
02263  *----------------------------------------------------------------------
02264 */
02265 double tetra_volume( float * p0, float * p1, float * p2, float * p3 )
02266 {
02267     double v0[3], v1[3], v2[3], xp[3];
02268 
02269     v0[0] = p1[0] - p0[0];
02270     v0[1] = p1[1] - p0[1];
02271     v0[2] = p1[2] - p0[2];
02272 
02273     v1[0] = p2[0] - p0[0];
02274     v1[1] = p2[1] - p0[1];
02275     v1[2] = p2[2] - p0[2];
02276 
02277     v2[0] = p3[0] - p0[0];
02278     v2[1] = p3[1] - p0[1];
02279     v2[2] = p3[2] - p0[2];
02280 
02281     cross_product(xp, v1, v2);
02282 
02283     return(fabs(dot_product(v0,xp))/6.0);
02284 }
02285 
02286 
02287 /*----------------------------------------------------------------------
02288  * dist_fn            - return Euclidean distance between the points
02289  *----------------------------------------------------------------------
02290 */
02291 double dist_fn( int len, float * p1, float * p2 )
02292 {
02293     double diff, sum;
02294     int    c;
02295 
02296     if ( len < 1 || p1 == NULL || p2 == NULL )
02297     {
02298         fprintf(stderr, "** dist_fn: invalid params (%d,%p,%p)\n",
02299                 len, p1, p2);
02300         return 0.0;
02301     }
02302 
02303     sum = 0.0;
02304     for ( c = 0; c < len; c++ )
02305     {
02306         diff = p1[c] - p2[c];
02307         sum += diff * diff;
02308     }
02309 
02310     return sqrt(sum);
02311 }
02312 
02313 
02314 /*----------------------------------------------------------------------
02315  * disp_surf_t
02316  *----------------------------------------------------------------------
02317 */
02318 int disp_surf_t( char * info, surf_t * d )
02319 {
02320     if ( info )
02321         fputs( info, stderr );
02322 
02323     if ( ! d )
02324     {
02325         fprintf(stderr,"** disp_surf_t: d == NULL\n");
02326         return -1;
02327     }
02328 
02329     fprintf(stderr,
02330             "surf_t struct at %p:\n"
02331             "    spec N_Surfs   = %d\n"
02332             "    spec N_Groups  = %d\n"
02333             "    slist          = %p\n"
02334             "    narea[0,1]     = %p, %p\n"
02335             "    nvol, fvol     = %p, %p\n"
02336             "    nsurf, salloc  = %d, %d\n"
02337             "    nnodes, nfaces = %d, %d\n",
02338             d,
02339             d->spec.N_Surfs, d->spec.N_Groups, d->slist,
02340             d->narea[0], d->narea[1], d->nvol, d->fvol,
02341             d->nsurf, d->salloc, d->nnodes, d->nfaces);
02342 
02343     return 0;
02344 }
02345 
02346 
02347 /*----------------------------------------------------------------------
02348  * disp_func_t
02349  *----------------------------------------------------------------------
02350 */
02351 int disp_func_t( char * info, func_t * d )
02352 {
02353     if ( info )
02354         fputs( info, stderr );
02355 
02356     if ( ! d )
02357     {
02358         fprintf(stderr,"** disp_func_t: d == NULL\n");
02359         return -1;
02360     }
02361 
02362     fprintf(stderr,
02363             "func_t struct at %p:\n"
02364             "    names, codes   = %p, %p\n"
02365             "    nalloc, nused  = %d, %d\n",
02366             d,
02367             d->names, d->codes, d->nalloc, d->nused);
02368 
02369     return 0;
02370 }
02371 
02372 
02373 /*----------------------------------------------------------------------
02374  * disp_opts_t
02375  *----------------------------------------------------------------------
02376 */
02377 int disp_opts_t( char * info, opts_t * d )
02378 {
02379     if ( info )
02380         fputs( info, stderr );
02381 
02382     if ( ! d )
02383     {
02384         fprintf(stderr,"** disp_opts_t: d == NULL\n");
02385         return -1;
02386     }
02387 
02388     fprintf(stderr,
02389             "opts_t struct at %p:\n"
02390             "    spec_file       = %s\n"
02391             "    sv_file         = %s\n"
02392             "    out_1D_file     = %s\n"
02393             "    cmask_cmd       = %s\n"
02394             "    nodes_1D_file   = %s\n"
02395             "    surf_names[0,1] = %s, %s\n"
02396             "    info            = %0x\n"
02397             "    debug, dnode    = %d, %d\n",
02398             d,
02399             CHECK_NULL_STR(d->spec_file),
02400             CHECK_NULL_STR(d->sv_file),
02401             CHECK_NULL_STR(d->out_1D_file),
02402             CHECK_NULL_STR(d->cmask_cmd),
02403             CHECK_NULL_STR(d->nodes_1D_file),
02404             CHECK_NULL_STR(d->surf_names[0]),
02405             CHECK_NULL_STR(d->surf_names[1]),
02406             d->info, d->debug, d->dnode);
02407 
02408     return 0;
02409 }
02410 
02411 
02412 /*----------------------------------------------------------------------
02413  * disp_param_t
02414  *----------------------------------------------------------------------
02415 */
02416 int disp_param_t( char * info, param_t * d )
02417 {
02418     if ( info )
02419         fputs( info, stderr );
02420 
02421     if ( ! d )
02422     {
02423         fprintf(stderr,"** disp_param_t: d == NULL\n");
02424         return -1;
02425     }
02426 
02427     fprintf(stderr,
02428             "param_t struct at %p:\n"
02429             "    F, outfp       = %p, %p\n"
02430             "    nodes, nnodes  = %p, %d\n"
02431             "    cmask          = %p\n"
02432             "    ncmask, ccount = %d, %d\n",
02433             d,
02434             d->F, d->outfp, d->nodes, d->nnodes,
02435             d->cmask, d->ncmask, d->ccount );
02436 
02437     return 0;
02438 }
02439 
02440 
02441 /*----------------------------------------------------------------------
02442  * disp_f3_point
02443  *----------------------------------------------------------------------
02444 */
02445 int disp_f3_point( char * info, float * d )
02446 {
02447     if ( info )
02448         fputs( info, stderr );
02449 
02450     if ( ! d )
02451     {
02452         fprintf(stderr,"** disp_f3_point: d == NULL\n");
02453         return -1;
02454     }
02455 
02456     fprintf(stderr,"(%6s, ", MV_format_fval(d[0]));
02457     fprintf(stderr,"%6s, ",  MV_format_fval(d[1]));
02458     fprintf(stderr,"%6s)\n", MV_format_fval(d[2]));
02459 
02460     return 0;
02461 }
02462 
02463 
02464 /*----------------------------------------------------------------------
02465  * lazy_det                     - compute determinant
02466  *                              - assume small, don't search for efficiency
02467  *----------------------------------------------------------------------
02468 */
02469 double lazy_det( int width, double * data )
02470 {
02471     static int   total_size = 0;
02472     double     * newm, * rowp, * cur, rv;
02473     int          c, row, col, w2, sign;
02474 
02475     if ( width < 2 )
02476         return *data;
02477 
02478     if ( width == 2 )
02479         return( data[0]*data[3] - data[1]*data[2] );
02480 
02481     /* otherwise, we need more space */
02482     w2 = width - 1;
02483     total_size += w2*w2;
02484     newm = malloc(w2*w2 * sizeof(double));
02485     if ( !newm )
02486     {
02487         fprintf(stderr,"** failed to allocate %d doubles for mat (%d ttl)\n",
02488                 w2*w2, total_size);
02489         return 0.0;
02490     }
02491 
02492     rv   = 0.0;
02493     sign = -1;
02494     for ( c = 0; c < width; c++ )
02495     {
02496         sign = -sign;                           /* catch the continue */
02497 
02498         if ( data[c] == 0.0 )                   /* skip any zeros */
02499             continue;
02500 
02501         /* start by copying new data */
02502         for ( row = 1; row < width; row++ )     /* always skip row 0 */
02503         {
02504             rowp = data + row     * width;      /* point to source column */
02505             cur  = newm + (row-1) * w2;         /* init dest pointer */
02506 
02507             for ( col = 0; col < width; col++ )
02508             {
02509                 if ( col == c )
02510                     continue;
02511 
02512                 *cur++ = rowp[col];
02513             }
02514         }
02515 
02516         rv += sign * data[c] * lazy_det( w2, newm );
02517     }
02518 
02519     free(newm);
02520 
02521     return rv;
02522 }
02523 
02524 
 

Powered by Plone

This site conforms to the following standards: