1 /*----------------------------------------------------------------------
2  * 06 Feb 2008: top-level routines to read GIFTI surfaces        [zss]
3  *
4  * These main routines should match those in gifti_choice.c.
5  *
6  * Do not call anything that is in suma_datasets.c or libSUMA.a
7  *----------------------------------------------------------------------
8  */
9 
10 #include "mrilib.h"
11 #include "gifti_io.h"
12 #include "suma_afni_surface.h"
13 
14 #define GIFTI_NO_META "Unknown"
15 
16 /*--- read/write gifti routines ---*/
17 static NI_group * gifti_surf_to_afni_surf(gifti_image * gim);
18 static gifti_image * afni_surf_to_gifti_surf(NI_group * aSO);
19 static byte is_gifti_surface(char * fname);
20 static byte gifti_surf_meta_to_afni_surf_meta(
21                gifti_image * gim,
22                NI_group *aSO);
23 static byte afni_surf_meta_to_gifti_surf_meta(
24                NI_group *aSO,
25                gifti_image * gim);
26 static int flip_float_triples(float * fdata, int ntrip);
27 
28 
29 
30 
31 /* ------------------------------- AFNI ------------------------------- */
32 
afni_open_gifti_surf(char * fname,int read_data)33 NI_group * afni_open_gifti_surf(char * fname, int read_data)
34 {
35    NI_group * aSO=NULL;
36    gifti_image * gim=NULL;
37 
38    ENTRY("afni_open_gifti_surf");
39 
40    /* read gim */
41    gifti_globs_from_env();     /* for thd_gifti */
42    if( !fname ) {
43      if( GP->verb > 0 ) fprintf( stderr,
44                                  "** afni_open_gifti_surf: null filename\n");
45      RETURN(NULL);
46    }
47 
48    if( GP->verb > 2 ) fprintf(stderr,"-- NI_read_gifti from '%s'\n", fname );
49    gifti_set_verb(GP->gverb);
50 
51    if (!is_gifti_surface(fname)) {
52       if( GP->verb > 0 )
53          fprintf( stderr,
54                   "** afni_open_gifti_surf: %s is not a surface\n", fname);
55       RETURN(NULL);
56    }
57    gim = gifti_read_image(fname, read_data);
58    if( !gim ) {
59      if( GP->verb > 1 )
60          fprintf(stderr,"-- NI_read_gifti: failed to read '%s'\n", fname);
61      RETURN(NULL);
62    }
63 
64    /* turn image to surface object */
65    aSO = gifti_surf_to_afni_surf (gim);
66 
67    /* free gim */
68    gifti_free_image(gim); gim = NULL;
69 
70    RETURN(aSO);
71 }
72 
afni_write_gifti_surf(NI_group * aSO,char * fname,int write_data,int encoding)73 int afni_write_gifti_surf( NI_group *aSO, char * fname,
74                            int write_data, int encoding)
75 {
76    gifti_image * gim=NULL;
77    giiDataArray  * dac=NULL; /* coords */
78    giiDataArray  * dat=NULL; /* triangles */
79 
80    ENTRY("afni_write_gifti_surf");
81 
82    if (!(gim = afni_surf_to_gifti_surf(aSO))) {
83       fprintf( stderr,
84                "** Failed to gimate\n");
85       RETURN(0);
86    }
87    if (encoding > GIFTI_ENCODING_UNDEF && encoding < GIFTI_ENCODING_MAX) {
88       /* enforce this encoding */
89        if( !(dac = gifti_find_DA(gim, NIFTI_INTENT_POINTSET, 0)) ||
90           !(dat = gifti_find_DA(gim, NIFTI_INTENT_TRIANGLE, 0))    ) {
91          RETURN(0);
92       }
93       dac->encoding = encoding;
94       dat->encoding = encoding;
95    }
96    if (gifti_write_image(gim, fname, write_data)) {
97       fprintf( stderr,
98                "** Failed to write_image\n");
99       gifti_free_image(gim); gim = NULL;
100       RETURN(0);
101    }
102 
103    gifti_free_image(gim); gim = NULL;
104 
105    RETURN(1);
106 }
107 
is_gifti_surface(char * fname)108 static byte is_gifti_surface(char * fname)
109 {
110    gifti_image * gim=NULL;
111    giiDataArray  * dac=NULL; /* coords */
112    giiDataArray  * dat=NULL; /* triangles */
113 
114    ENTRY("is_gifti_surface");
115 
116    if( !fname ) RETURN(0);
117 
118    gim = gifti_read_image(fname, 0);
119 
120    if( !gim ) {
121       gifti_free_image(gim); gim = NULL;
122       RETURN(0);
123    }
124 
125    if( !(dac = gifti_find_DA(gim, NIFTI_INTENT_POINTSET, 0)) ||
126        !(dat = gifti_find_DA(gim, NIFTI_INTENT_TRIANGLE, 0))    ) {
127       gifti_free_image(gim); gim = NULL;
128       RETURN(0);
129    }
130 
131    gifti_free_image(gim); gim = NULL;
132 
133    RETURN(1);
134 }
135 
136 /* convert GIFTI surface to afni surface */
gifti_surf_to_afni_surf(gifti_image * gim)137 static NI_group *gifti_surf_to_afni_surf(gifti_image * gim)
138 {
139    NI_group * aSO=NULL;
140    giiDataArray  * dac=NULL; /* coords */
141    giiDataArray  * dat=NULL; /* triangles */
142    giiDataArray  * dan=NULL; /* normals */
143    long long  rows=0, cols=0;
144    NI_element *nelxyz=NULL, *nelnormals=NULL, *nelxform=NULL, *nelijk=NULL;
145 
146    int rai_convert = 0;
147 
148    ENTRY("gifti_surf_to_afni_surf");
149 
150    rai_convert = !AFNI_yesenv("AFNI_GIFTI_IN_RAI");
151 
152    gifti_globs_from_env();     /* for thd_gifti */
153    if( (dac = gifti_find_DA(gim, NIFTI_INTENT_POINTSET, 0)) &&
154        (dat = gifti_find_DA(gim, NIFTI_INTENT_TRIANGLE, 0))    ) {
155       if( GP->verb > 1 )
156          fprintf( stderr,
157                   "++ gifti_surf_to_afni_surf: found pointset and triangles\n");
158    } else {
159         fprintf(stderr,"** failed to find coordinate/triangle structs\n");
160         RETURN(aSO);
161    }
162    if (dac->datatype != NIFTI_TYPE_FLOAT32) {
163       fprintf(stderr,"** NIFTI_INTENT_POINTSET not FLOAT32\n");
164       RETURN(aSO);
165    }
166    if (dat->datatype != NIFTI_TYPE_INT32) {
167       fprintf(stderr,"** NIFTI_INTENT_TRIANGLE not INT32\n");
168       RETURN(aSO);
169    }
170 
171    dan = gifti_find_DA(gim, NIFTI_INTENT_VECTOR, 0);
172    if (dan && dan->datatype != NIFTI_TYPE_FLOAT32) {
173       fprintf(stderr,"** NIFTI_INTENT_VECTOR not FLOAT32\n");
174       RETURN(aSO);
175    }
176    if (dan && dan->nvals != dac->nvals) {
177       fprintf(stderr,"** NIFTI_INTENT_VECTOR number of values differs"
178                      "   from that of NIFTI_INTENT_POINTSET\n");
179       RETURN(aSO);
180    }
181 
182    aSO = SUMA_NewAfniSurfaceObject();
183    gifti_DA_rows_cols(  dac,
184                         &(rows),
185                         &(cols) );
186    nelxyz = SUMA_FindNgrNamedElement(aSO, "Node_XYZ");
187    NI_SETA_INT(nelxyz,"N_Node",(int)rows);
188    NI_SETA_INT(nelxyz,"NodeDim",(int)cols);
189    NI_alter_veclen(nelxyz, (int)(rows*cols));
190    NI_add_column(nelxyz, NI_FLOAT, (void *)dac->data);
191    NI_SETA_INT(nelxyz,"EmbedDim",3);
192 
193    /* maybe convert LPI to RAI   31 Jul 2013 [rickr/zaid] */
194    if( rai_convert ) {
195       if( cols != 3 ) {
196          fprintf(stderr, "** GIFTI 2 AFNI: cols != 3, no RAI conversion\n");
197          rai_convert = 0;
198       } else flip_float_triples((float *)nelxyz->vec[0], rows);
199    }
200 
201    gifti_DA_rows_cols(  dat,
202                         &(rows),
203                         &(cols) );
204    nelijk = SUMA_FindNgrNamedElement(aSO, "Mesh_IJK");
205    NI_SETA_INT(nelijk, "N_FaceSet", (int)rows);
206    NI_SETA_INT(nelijk, "FaceSetDim", (int)cols);
207    NI_alter_veclen(nelijk, (int)(rows*cols));
208    NI_add_column(nelijk, NI_INT, dat->data);
209 
210    if (dan) {
211       nelnormals = SUMA_FindNgrNamedElement(aSO, "Node_Normals");
212       gifti_DA_rows_cols(  dan,
213                         &(rows),
214                         &(cols) );
215       NI_alter_veclen(nelnormals, (int)(rows*cols));
216       NI_add_column(nelnormals, NI_FLOAT, dan->data);
217       /* maybe convert LPI to RAI   31 Jul 2013 [rickr/zaid] */
218       if( rai_convert ) flip_float_triples((float *)nelnormals->vec[0], rows);
219    }
220 
221    if (!gifti_surf_meta_to_afni_surf_meta(gim, aSO)) {
222       fprintf(stderr,"** failed to include metadata!\n");
223       RETURN(aSO);
224    }
225 
226    RETURN(aSO);
227 }
228 
flip_float_triples(float * fdata,int ntrip)229 static int flip_float_triples(float * fdata, int ntrip)
230 {
231    float * fp = fdata;
232    int ind;
233    if( !fp || ntrip < 0 ) return 1;
234 
235    for( ind = 0; ind < ntrip; ind++ ) {
236       *fp = -*fp;  fp++;
237       *fp = -*fp;  fp++;
238       fp++;
239    }
240    return 0;
241 }
242 
243 /* convert afni surface to GIFTI surface */
afni_surf_to_gifti_surf(NI_group * aSO)244 static  gifti_image *afni_surf_to_gifti_surf(NI_group *aSO)
245 {
246    gifti_image * gim=NULL;
247    giiDataArray  * da=NULL;
248    NI_element *nelxyz=NULL, *nelijk=NULL, *nelnormals=NULL, *nelxform=NULL;
249    int           rai_convert=0;
250 
251    ENTRY("afni_surf_to_gifti_surf");
252 
253    /* maybe convert RAI back to LPI   1 Aug 2013 [rickr/zaid] */
254    rai_convert = !AFNI_yesenv("AFNI_GIFTI_IN_RAI");
255 
256    gifti_globs_from_env();     /* for thd_gifti */
257 
258    if (!aSO) {
259       fprintf( stderr,
260                "++ afni_surf_to_gifti_surf: NULL input\n");
261       RETURN(gim);
262    }
263 
264    /* form image */
265    if( gifti_get_verb() > 1 ) {
266       fprintf(stderr,"++ creating gifti_image \n" );
267    }
268 
269    /* basic step - create empty image (with a version string)
270       from gifti_create_image */
271    gim = (gifti_image *)calloc(1, sizeof(gifti_image));
272    if(!gim){ fprintf(stderr,"** failed to alloc gifti_image\n"); RETURN(NULL); }
273 
274    gifti_clear_gifti_image(gim);
275    gim->version = gifti_strdup(GIFTI_XML_VERSION);
276 
277    /* now add the pointset */
278    if( gifti_add_empty_darray(gim, 1) ) {
279       fprintf( stderr,
280                "++ afni_surf_to_gifti_surf: Failed to add DA\n");
281       gifti_free_image(gim); gim = NULL;
282       RETURN(gim);
283    }
284    da = gim->darray[gim->numDA-1];
285 
286    gifti_set_DA_defaults(da);
287    nelxyz = SUMA_FindNgrNamedElement(aSO, "Node_XYZ");
288    da->intent   = NIFTI_INTENT_POINTSET;
289    da->datatype = NIFTI_TYPE_FLOAT32;
290    da->num_dim  = 2;
291    da->dims[0]  = (long long)SUMA_NI_get_int(nelxyz,"N_Node");
292    da->dims[1]  = (long long)SUMA_NI_get_int(nelxyz,"NodeDim");
293    da->nvals = da->dims[0] * da->dims[1];
294    if( GP->write_mode == NI_TEXT_MODE ) da->encoding = GIFTI_ENCODING_ASCII;
295    if (!(da->data = malloc(sizeof(float)*da->dims[0]*da->dims[1]))) {
296       fprintf( stderr,
297             "++ afni_surf_to_gifti_surf: Failed to malloc\n");
298       gifti_free_image(gim); gim = NULL;
299       RETURN(gim);
300    }
301    memcpy( da->data, (void *)nelxyz->vec[0],
302             sizeof(float)*da->dims[0]*da->dims[1]);
303 
304    /* convert AFNI's RAI back to GIFTI's LPI */
305    if( rai_convert ) flip_float_triples((float *)da->data, da->dims[0]);
306 
307    /* coordsys is added in the afni_surf_meta_to_gifti_surf_meta function */
308 
309    /* and the triangles */
310    if( gifti_add_empty_darray(gim, 1) ) {
311       fprintf( stderr,
312                "++ afni_surf_to_gifti_surf: Failed to add DA\n");
313       gifti_free_image(gim); gim = NULL;
314       RETURN(gim);
315    }
316    da = gim->darray[gim->numDA-1];
317 
318    gifti_set_DA_defaults(da);
319    nelijk = SUMA_FindNgrNamedElement(aSO, "Mesh_IJK");
320    da->intent   = NIFTI_INTENT_TRIANGLE;
321    da->datatype = NIFTI_TYPE_INT32;
322    da->num_dim  = 2;
323    da->dims[0]  = (long long)SUMA_NI_get_int(nelijk,"N_FaceSet");
324    da->dims[1]  = (long long)SUMA_NI_get_int(nelijk,"FaceSetDim");
325    da->nvals = da->dims[0] * da->dims[1];
326    if( GP->write_mode == NI_TEXT_MODE ) da->encoding = GIFTI_ENCODING_ASCII;
327 
328    if (!(da->data = malloc(sizeof(int)*da->dims[0]*da->dims[1]))) {
329       fprintf( stderr,
330             "++ afni_surf_to_gifti_surf: Failed to malloc\n");
331       gifti_free_image(gim); gim = NULL;
332       RETURN(gim);
333    }
334    memcpy( da->data, (void *)nelijk->vec[0],
335             sizeof(int)*da->dims[0]*da->dims[1]);
336 
337 #if 0   /* GIFTI standard now says no normals with nodes  5 Feb 2010 */
338 
339    /* and the normals */
340    if( gifti_add_empty_darray(gim, 1) ) {
341       fprintf( stderr,
342                "++ afni_surf_to_gifti_surf: Failed to add DA\n");
343       gifti_free_image(gim); gim = NULL;
344       RETURN(gim);
345    }
346    da = gim->darray[gim->numDA-1];
347 
348    gifti_set_DA_defaults(da);
349    nelnormals = SUMA_FindNgrNamedElement(aSO, "Node_Normals");
350    da->intent   = NIFTI_INTENT_VECTOR;
351    da->datatype = NIFTI_TYPE_FLOAT32;
352    da->num_dim  = 2;
353    da->dims[0]  = (long long)SUMA_NI_get_int(nelxyz,"N_Node");
354    da->dims[1]  = (long long)SUMA_NI_get_int(nelxyz,"NodeDim");
355    da->nvals = da->dims[0] * da->dims[1];
356    if( GP->write_mode == NI_TEXT_MODE ) da->encoding = GIFTI_ENCODING_ASCII;
357 
358    if (!(da->data = malloc(sizeof(float)*da->dims[0]*da->dims[1]))) {
359       fprintf( stderr,
360             "++ afni_surf_to_gifti_surf: Failed to malloc\n");
361       gifti_free_image(gim); gim = NULL;
362       RETURN(gim);
363    }
364    memcpy( da->data, nelnormals->vec[0],
365             sizeof(float)*da->dims[0]*da->dims[1]);
366    /* convert AFNI's RAI back to GIFTI's LPI */
367    if( rai_convert ) flip_float_triples((float *)da->data, da->dims[0]);
368 
369 #endif  /* end normals */
370 
371    /* and now for the grits */
372    if (!afni_surf_meta_to_gifti_surf_meta(aSO, gim)) {
373       fprintf(stderr,"** failed to include metadata!\n");
374       gifti_free_image(gim); gim = NULL;
375       RETURN(gim);
376    }
377 
378    if( gifti_get_verb() > 1 ) {
379       gifti_disp_gifti_image("afni_surf_to_gifti_surf :",
380                              gim, gifti_get_verb() > 3);
381    }
382 
383    RETURN(gim);
384 }
385 
gifti_surf_meta_to_afni_surf_meta(gifti_image * gim,NI_group * aSO)386 static byte gifti_surf_meta_to_afni_surf_meta(
387                gifti_image * gim,
388                NI_group *aSO)
389 {
390    char *cp=NULL;
391    int i=0, j=0, k=0, rai_convert=0;
392    giiDataArray  * dac=NULL; /* coords */
393    giiDataArray  * dat=NULL; /* triangles */
394    NI_element *nelxyz=NULL, *nelijk=NULL, *nelnormals=NULL, *nelxform=NULL;
395    double M[4][4];
396    double *dv = NULL;
397 
398    ENTRY("gifti_surf_meta_to_afni_surf_meta");
399 
400    rai_convert = !AFNI_yesenv("AFNI_GIFTI_IN_RAI");
401 
402    /* Begin with nodelist */
403    dac = gifti_find_DA(gim, NIFTI_INTENT_POINTSET, 0);
404    nelxyz = SUMA_FindNgrNamedElement(aSO, "Node_XYZ");
405    if (!(cp = gifti_get_meta_value( &dac->meta,
406                                     "AnatomicalStructurePrimary"))) {
407       cp = GIFTI_NO_META;
408    }
409    NI_set_attribute( nelxyz,
410                      "AnatomicalStructurePrimary",
411                      cp);
412 
413    if (!(cp = gifti_get_meta_value( &dac->meta,
414                                     "AnatomicalStructureSecondary"))) {
415       cp = GIFTI_NO_META;
416    }
417    NI_set_attribute( nelxyz,
418                      "AnatomicalStructureSecondary",
419                      cp);
420 
421    if (!(cp = gifti_get_meta_value( &dac->meta,
422                                     "GeometricType"))) {
423       cp = GIFTI_NO_META;
424    }
425    NI_set_attribute( nelxyz,
426                      "GeometricType",
427                      cp);
428 
429    if (!(cp = gifti_get_meta_value( &dac->meta,
430                                     "UniqueID"))) {
431       cp = GIFTI_NO_META;
432    }
433    NI_set_attribute( nelxyz,
434                      "idcode_str",
435                      cp);
436 
437    if (!(cp = gifti_get_meta_value( &dac->meta,
438                                     "date"))) {
439       cp = GIFTI_NO_META;
440    }
441    NI_set_attribute( nelxyz,
442                      "date",
443                      cp);
444    NI_set_attribute(nelxyz,"inxformspace","no");
445 
446    nelxform = SUMA_FindNgrNamedElement(aSO, "Coord_System");
447    dv = (double *)nelxform->vec[0];
448    if (dac->numCS > 0 && dac->coordsys && dac->coordsys[0]) {
449       if (!(cp = dac->coordsys[0]->dataspace)) {
450          cp = GIFTI_NO_META;
451       }
452       NI_set_attribute( nelxform,
453                         "dataspace",
454                         cp);
455 
456       if (!(cp = dac->coordsys[0]->xformspace)) {
457          cp = GIFTI_NO_META;
458       }
459       NI_set_attribute( nelxform,
460                         "xformspace",
461                         cp);
462 
463       /* XR = F XL F-1, (F-1 = F) 31 Jul 2013 [rickr/zaid] */
464       if (rai_convert) {
465          AFF44_LPI_RAI_FLIP ( M , dac->coordsys[0]->xform );
466       } else {
467          AFF44_COPY( M , dac->coordsys[0]->xform );
468       }
469 
470       k = 0;
471       for (i=0; i<4;++i) {
472          for (j=0; j<4; ++j) {
473             dv[k] = M[i][j];
474             ++k;
475          }
476       }
477    } else {
478       NI_set_attribute( nelxform,"dataspace","NIFTI_XFORM_UNKNOWN");
479       NI_set_attribute( nelxform,"xformspace","NIFTI_XFORM_UNKNOWN");
480       k=0;
481       for (i=0; i<4; ++i)
482          for (j=0; j< 4; ++j) {
483             if (i==j)   dv[k] = 1.0;
484             else        dv[k] = 0.0;
485             ++k;
486          }
487    }
488    dac = NULL; /* make sure it is not used below */
489 
490    /* Now do the FaceSetList */
491    dat = gifti_find_DA(gim, NIFTI_INTENT_TRIANGLE, 0);
492    nelijk = SUMA_FindNgrNamedElement(aSO,"Mesh_IJK");
493    if (!(cp = gifti_get_meta_value( &dat->meta,
494                                     "TopologicalType"))) {
495       cp = GIFTI_NO_META;
496    }
497    NI_set_attribute( nelijk,
498                      "TopologicalType",
499                      cp);
500 
501    if (!(cp = gifti_get_meta_value( &dat->meta,
502                                     "UniqueID"))) {
503       cp = GIFTI_NO_META;
504    }
505    NI_set_attribute( nelijk,
506                      "idcode_str",
507                      cp);
508 
509    if (!(cp = gifti_get_meta_value( &dat->meta,
510                                     "date"))) {
511       cp = GIFTI_NO_META;
512    }
513    NI_set_attribute( nelijk,
514                      "date",
515                      cp);
516 
517 
518    RETURN(1);
519 }
520 
afni_surf_meta_to_gifti_surf_meta(NI_group * aSO,gifti_image * gim)521 static byte afni_surf_meta_to_gifti_surf_meta(
522                NI_group *aSO,
523                gifti_image * gim )
524 {
525    int i=0, j=0;
526    giiDataArray  * dac=NULL; /* coords */
527    giiDataArray  * dat=NULL; /* triangles */
528    NI_element *nelxyz=NULL, *nelijk=NULL, *nelnormals=NULL, *nelxform=NULL;
529    double *dv=NULL, M[4][4];
530    int k=0, rai_convert=0;
531 
532    ENTRY("afni_surf_meta_to_gifti_surf_meta");
533 
534    rai_convert = !AFNI_yesenv("AFNI_GIFTI_IN_RAI");
535 
536    /* Begin with nodelist */
537    dac = gifti_find_DA(gim, NIFTI_INTENT_POINTSET, 0);
538    nelxyz = SUMA_FindNgrNamedElement(aSO,"Node_XYZ");
539    if( gifti_add_to_meta(&dac->meta,
540                          "AnatomicalStructurePrimary",
541                          NI_get_attribute(nelxyz,"AnatomicalStructurePrimary"),
542                          1) ) {
543       fprintf(stderr,"** failed to add meta AnatomicalStructurePrimary.\n");
544       RETURN(1);
545    }
546    if( gifti_add_to_meta(&dac->meta,
547                       "AnatomicalStructureSecondary",
548                       NI_get_attribute(nelxyz,"AnatomicalStructureSecondary"),
549                       1) ) {
550       fprintf(stderr,"** failed to add meta AnatomicalStructureSecondary.\n");
551       RETURN(1);
552    }
553    if( gifti_add_to_meta(&dac->meta,
554                          "GeometricType",
555                          NI_get_attribute(nelxyz,"GeometricType"),
556                          1) ) {
557       fprintf(stderr,"** failed to add meta GeometricType.\n");
558       RETURN(1);
559    }
560    if( gifti_add_to_meta(&dac->meta,
561                          "UniqueID",
562                          NI_get_attribute(nelxyz,"idcode_str"),
563                          1) ) {
564       fprintf(stderr,"** failed to add meta UniqueID.\n");
565       RETURN(1);
566    }
567    if( gifti_add_to_meta(&dac->meta,
568                          "date",
569                          NI_get_attribute(nelxyz,"date"),
570                          1) ) {
571       fprintf(stderr,"** failed to add meta date.\n");
572       RETURN(1);
573    }
574 
575    /* free any old CoordSystems and add a single new one      */
576    /* - coordsys now an array of pointers, 8 May 2008 [rickr] */
577    gifti_free_CS_list(dac);
578    if( gifti_add_empty_CS(dac) ) {
579       fprintf(stderr,"** failed to add empty CoordSystem\n");
580       RETURN(1);
581    }
582 
583    nelxform = SUMA_FindNgrNamedElement(aSO,"Coord_System");
584    dac->coordsys[0]->dataspace =
585          gifti_strdup(NI_get_attribute(nelxform,"dataspace"));
586    dac->coordsys[0]->xformspace =
587          gifti_strdup(NI_get_attribute(nelxform,"xformspace"));
588    dv = (double *)nelxform->vec[0];
589    k = 0;
590    for (i=0; i<4; ++i)
591          for (j=0; j< 4; ++j) {
592             dac->coordsys[0]->xform[i][j] = dv[k];
593             ++k;
594          }
595 
596    /* XR = F XL F-1, (F-1 = F) 31 Jul 2013 [rickr/zaid] */
597    if (rai_convert) {
598       AFF44_COPY( M , dac->coordsys[0]->xform );
599       AFF44_LPI_RAI_FLIP ( dac->coordsys[0]->xform , M );
600    }
601 
602    dac = NULL; /* make sure it is not used below */
603 
604    /* Now do the FaceSetList */
605    dat = gifti_find_DA(gim, NIFTI_INTENT_TRIANGLE, 0);
606    nelijk = SUMA_FindNgrNamedElement(aSO,"Mesh_IJK");
607    if( gifti_add_to_meta(&dat->meta,
608                          "TopologicalType",
609                          NI_get_attribute(nelijk,"TopologicalType"),
610                          1) ) {
611       fprintf(stderr,"** failed to add meta TopologicalType.\n");
612       RETURN(1);
613    }
614 
615    if( gifti_add_to_meta(&dat->meta,
616                          "UniqueID",
617                          NI_get_attribute(nelijk,"idcode_str"),
618                          1) ) {
619       fprintf(stderr,"** failed to add meta UniqueID.\n");
620       RETURN(1);
621    }
622 
623    if( gifti_add_to_meta(&dat->meta,
624                          "date",
625                          NI_get_attribute(nelijk,"date"),
626                          1) ) {
627       fprintf(stderr,"** failed to add meta date.\n");
628       RETURN(1);
629    }
630 
631    RETURN(1);
632 }
633