Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

amiraplugin.C

Go to the documentation of this file.
00001 /*
00002  *  Amira File Format (.am)
00003  *
00004  *  Supports scalar and vector fields, and a variety of triangle meshes.
00005  *  
00006  *  File format information and parsing approaches for reference and 
00007  *  comparison with our own:
00008  *    EMBL "ahds" parsers for Amira:
00009  *    https://ahds.readthedocs.io/en/v0.2.0.dev0/overview.html#overview
00010  *    https://github.com/emdb-empiar/ahds
00011  *    Others:
00012  *    http://docs.drabba.net/usersguide/amira/5.3.3/amiramesh/HxFileFormat_AmiraMesh.html
00013  *    https://people.mpi-inf.mpg.de/~weinkauf/notes/amiramesh.html
00014  *    http://neuronland.org/NLMorphologyConverter/MorphologyFormats/AmiraMesh/Spec.html
00015  *    https://www.mathworks.com/matlabcentral/fileexchange/34909-loadamiramesh?requestedDomain=www.mathworks.com
00016  *    https://www.mathworks.com/matlabcentral/fileexchange/34909-loadamiramesh/content/loadAmiraMesh.m
00017  *    https://github.com/dune-project/dune-grid/blob/master/dune/grid/io/file/amiramesh/amirameshreader.cc
00018  *    https://parcomp-git.iwr.uni-heidelberg.de/marian/dune-common/commit/c184290ea19b14358c053441e3a55f6639d4f6c4.patch
00019  *    https://gitlab.dune-project.org/dominic/dune-grid/blob/6edbd89b8be2778d3940d1d293ce0c53b814cf0b/dune/grid/io/file/amiramesh/amirameshreader.cc
00020  *    https://github.com/openmicroscopy/bioformats/blob/v5.3.0-m3/components/formats-gpl/src/loci/formats/in/AmiraReader.java
00021  */
00022 
00023 #include <stdlib.h>
00024 #include <stdio.h>
00025 #include <ctype.h>
00026 #include <math.h>
00027 #include <string.h>
00028 
00029 #if defined(_AIX)
00030 #include <strings.h>
00031 #endif
00032 
00033 #if defined(WIN32) || defined(WIN64)
00034 #define strcasecmp stricmp
00035 #endif
00036 
00037 #include "molfile_plugin.h"
00038 
00039 // internal buffer size
00040 #define BUFSZ 1024
00041 
00042 
00043 typedef struct {
00044   FILE *f;
00045   char *headerbuf;
00046   int isbinary;
00047   int islittleendian;
00048   long dataoffset;
00049 
00050   // triangle mesh data
00051   int hastrimesh;
00052   int nvertices;
00053   int ntriangles;
00054 
00055   // volumetric  data
00056   int nsets;
00057   molfile_volumetric_t *vol;
00058 } am_t;
00059 
00060 
00061 static char *get_next_noncomment_line(char *buf, int bufsz, FILE *f) {
00062   while (1) {
00063     char *res = fgets(buf, bufsz, f);
00064     if (!res || (res[0] != '#' && res[0] != '\n' && res[0] != '\r'))
00065       return res;
00066   }
00067 }
00068 
00069 static long find_data_offset(FILE *f) {
00070   char buf[BUFSZ];
00071   while (1) {
00072     char *res = fgets(buf, BUFSZ, f);
00073     if (!res || (res[0] != '#' && res[0] != '\n' && res[0] != '\r')) {
00074       if (!res) {
00075         return -1;
00076       } else if (strstr(res, "# Data section follows")) {
00077         return ftell(f);
00078         // XXX fseek()/ftell() are incompatible with 64-bit LFS I/O 
00079         // implementations, hope we don't read any files >= 2GB...
00080       }
00081     }
00082   }
00083 }
00084 
00085 
00086 static int amira_readvar_int(am_t *am, char *varname, int *val) {
00087   char *pos = strstr(am->headerbuf, varname);
00088   if (pos != NULL) {
00089     if (sscanf(am->headerbuf, "%*s %d", val) == 2) {
00090       return 0;
00091     }
00092     printf("amiraplugin) failed to find variable '%s' in header\n", varname);
00093     return -1;
00094   }
00095 
00096   return -1;
00097 }
00098 
00099 
00100 static int amira_readvar_float(am_t *am, char *varname, float *val) {
00101   char *pos = strstr(am->headerbuf, varname);
00102   if (pos != NULL) {
00103     if (sscanf(am->headerbuf, "%*s %f", val) == 2) {
00104       return 0;
00105     }
00106     printf("amiraplugin) failed to find variable '%s' in header\n", varname);
00107     return -1;
00108   }
00109 
00110   return -1;
00111 }
00112 
00113 
00114 static int amira_check_trimesh(am_t *am) {
00115   // see if this file contains a triangle mesh or not
00116   if ((strstr(am->headerbuf, "Vertices { float[3] Vertices }") != NULL) &&
00117       (strstr(am->headerbuf, "TriangleData { int[7] Triangles }") != NULL)) {
00118     int rc;
00119     rc=amira_readvar_int(am, "nVertices", &am->nvertices);
00120     if (!rc) rc=amira_readvar_int(am, "nVertices", &am->ntriangles);
00121     if (!rc) {
00122       am->hastrimesh = 1;
00123       printf("amiraplugin) Found RGBA triangle mesh: %d verts, %d tris\n",
00124              am->nvertices, am->ntriangles);
00125       return 1;
00126     } else {
00127       printf("amiraplugin) Failed to find vertex/triangle counts for mesh!\n");
00128     }
00129   }
00130   return 0;
00131 }
00132 
00133 
00134 static void *open_file_read(const char *filepath, const char *filetype,
00135                             int *natoms) {
00136   FILE *f=NULL;
00137   am_t *am=NULL;
00138   *natoms = 0;
00139   
00140   f = fopen(filepath, "rb");
00141   if (!f) {
00142     printf("amiraplugin) Error opening file.\n");
00143     return NULL;
00144   }
00145 
00146   // read file header 
00147   char linebuf[BUFSZ];
00148   fgets(linebuf, BUFSZ, f);
00149   if (strncmp(linebuf, "# AmiraMesh", strlen("# AmiraMesh")) != 0) {
00150     printf("amiraplugin) Bad file header: '%s'\n", linebuf);
00151     return NULL;
00152   }
00153 
00154   char endianbuf[BUFSZ];
00155   char versionbuf[BUFSZ];
00156   char commentbuf[BUFSZ];
00157   int parsed=sscanf("# AmiraMesh %s %s %s", endianbuf, versionbuf, commentbuf);
00158   if (parsed < 2) {
00159     printf("amiraplugin) Bad file header: '%s'\n", linebuf);
00160     return NULL;
00161   }
00162  
00163   am = new am_t;
00164   memset(am, 0, sizeof(am_t));
00165   am->f = f;
00166   am->hastrimesh = 0;
00167   am->nsets = 0;
00168   am->vol = NULL;
00169 
00170   if (strstr(linebuf, "BINARY-LITTLE-ENDIAN") != NULL) {
00171     am->isbinary=1;
00172     am->islittleendian=1;
00173   } else if (strstr(linebuf, "BINARY-BIG-ENDIAN") != NULL) {
00174     am->isbinary=1;
00175     am->islittleendian=1;
00176   } else if (strstr(linebuf, "ASCII") != NULL) {
00177     am->isbinary=0;
00178     am->islittleendian=0;
00179   } else {
00180     printf("amiraplugin) Failed to parse header data format: '%s'\n",
00181            endianbuf);
00182     delete am;
00183     return NULL;
00184   }
00185 
00186   // A cheap strategy for avoiding difficult parsing for the few file variants
00187   // we really care about is to read all of the header into a giant string,
00188   // and then search for the variables and other bits we want using strstr()
00189   // or the like...
00190   am->dataoffset = find_data_offset(am->f);
00191   if (am->dataoffset < 0) {
00192     printf("amiraplugin) Failed to find data offset!\n");
00193     delete am;
00194     return NULL;
00195   }
00196   if (am->dataoffset > 1000000) {
00197     printf("amiraplugin) Header appears to be overly large, aborting read!\n");
00198     delete am;
00199     return NULL;
00200   }
00201 
00202   am->headerbuf = (char*) calloc(1, am->dataoffset); 
00203   rewind(am->f);
00204   fread(am->headerbuf, am->dataoffset, 1, am->f); 
00205 
00206   // check to see if the file contains a triangle mesh format
00207   // that we currently recognize.
00208   amira_check_trimesh(am);
00209 
00210   return am;
00211 }
00212 
00213 
00214 static void close_file_read(void *v) {
00215   am_t *am = (am_t *)v;
00216 
00217   fclose(am->f);
00218   if (am->vol != NULL)
00219     delete [] am->vol;
00220   delete am;
00221 }
00222 
00223 
00224 static int read_volumetric_metadata(void *v, int *nsets,
00225                                     molfile_volumetric_t **metadata) {
00226   am_t *am = (am_t *)v;
00227   *nsets = am->nsets;
00228   *metadata = am->vol;
00229 
00230   return MOLFILE_SUCCESS;
00231 }
00232 
00233 
00234 static int read_volumetric_data(void *v, int set, float *datablock,
00235                          float *colorblock) {
00236   return MOLFILE_ERROR;
00237 //  return MOLFILE_SUCCESS;
00238 }
00239 
00240 
00241 static int read_rawgraphics(void *v, int *nelem, 
00242                             const molfile_graphics_t **data) {
00243 #if 0
00244   int i, k, n;
00245   int nVert, nFaces, nEdges;
00246   float *vertices = NULL, *vertColors = NULL;
00247   char *vertHasColor = NULL;
00248   molfile_graphics_t *graphics = NULL;
00249   int j=0;
00250 
00251   char buff[BUFFLEN+1];
00252   FILE *infile = (FILE *)v;
00253 
00254   // First line is the header: "OFF"
00255   nextNoncommentLine(buff, BUFFLEN, infile);
00256   if (buff[0] != 'O' || buff[1] != 'F' || buff[2] != 'F') {
00257     fprintf(stderr, "offplugin) error: expected \"OFF\" header.\n");
00258     goto error;
00259   }
00260 
00261   // Second line: numVertices numFaces numEdges
00262   nextNoncommentLine(buff, BUFFLEN, infile);
00263   if (sscanf (buff, " %d %d %d", &nVert, &nFaces, &nEdges) < 2 || 
00264       nVert <= 0 || nFaces <= 0) {
00265     fprintf(stderr, "offplugin) error: wrong number of elements.\n");
00266     goto error;
00267   }
00268 
00269   // Read vertices
00270   vertices = (float *) calloc (3 * nVert, sizeof(float));
00271   vertHasColor = (char *) calloc (nVert, sizeof(char));
00272   vertColors = (float *) calloc (3 * nVert, sizeof(float));
00273   for (i = 0; i < nVert; i++) {
00274     nextNoncommentLine(buff, BUFFLEN, infile);
00275     int n = sscanf (buff, " %g %g %g %g %g %g", 
00276                     &vertices[3*i], &vertices[3*i+1], &vertices[3*i+2],
00277                     &vertColors[3*i], &vertColors[3*i+1], &vertColors[3*i+2]);
00278     if (n != 3 && n != 6) {
00279       fprintf(stderr, "offplugin) error: not enough data.\n");
00280       goto error;
00281     }
00282     vertHasColor[i] = (n == 6);
00283   }
00284 
00285   // Read faces
00286   // We alloc 6 times the memory because:
00287   //   -- a quadrangle will be transformed into two triangles.
00288   //   -- each triangle may have color, and then also its norm will be specified
00289   graphics = (molfile_graphics_t *) calloc(6*nFaces, sizeof(molfile_graphics_t));
00290   n = 0;
00291   for (i = 0; i < nFaces; i++) {
00292     int idx[4];
00293     float c[3];
00294     nextNoncommentLine(buff, BUFFLEN, infile);
00295 
00296     if (sscanf (buff, "%d", &k) != 1 || k < 3) {
00297       fprintf(stderr, "offplugin) error: not enough data.\n");
00298       goto error;
00299     }
00300 
00301     if (k > 4) {
00302       // TODO -- handle polygon decomposition into triangles
00303       // Follow the algorithm there:
00304       // http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
00305       fprintf(stderr, "offplugin) error: TODO -- handling polygons with more than 4 vertices.\n");
00306       goto error;
00307     }
00308 
00309     if (k == 3) {
00310       j = sscanf (buff, "%d %d %d %d %g %g %g", &k, &idx[0], &idx[1], &idx[2], &c[0], &c[1], &c[2]);
00311       bool hasColor = ((j == 7) || (vertHasColor[idx[0]] && vertHasColor[idx[1]] && vertHasColor[idx[2]]));
00312 
00313       graphics[n].type = (hasColor ? MOLFILE_TRICOLOR : MOLFILE_TRIANGLE);
00314       graphics[n].data[0] = vertices[3*idx[0]  ];
00315       graphics[n].data[1] = vertices[3*idx[0]+1];
00316       graphics[n].data[2] = vertices[3*idx[0]+2];
00317       graphics[n].data[3] = vertices[3*idx[1]  ];
00318       graphics[n].data[4] = vertices[3*idx[1]+1];
00319       graphics[n].data[5] = vertices[3*idx[1]+2];
00320       graphics[n].data[6] = vertices[3*idx[2]  ];
00321       graphics[n].data[7] = vertices[3*idx[2]+1];
00322       graphics[n].data[8] = vertices[3*idx[2]+2];
00323       n++;
00324 
00325       if (j == 7) {
00326         // The facet has a specific color, use it.
00327         graphics[n].type = MOLFILE_NORMS;
00328         calcNormals (graphics[n-1].data, graphics[n].data);
00329         n++;
00330 
00331         graphics[n].type = MOLFILE_COLOR;
00332         graphics[n].data[0] = graphics[n].data[3] = graphics[n].data[6] = c[0];
00333         graphics[n].data[1] = graphics[n].data[4] = graphics[n].data[7] = c[1];
00334         graphics[n].data[2] = graphics[n].data[5] = graphics[n].data[8] = c[2];
00335         n++;
00336       } else if (hasColor) {
00337         // All three vertices have a color attribute
00338         graphics[n].type = MOLFILE_NORMS;
00339         calcNormals (graphics[n-1].data, graphics[n].data);
00340         n++;
00341 
00342         graphics[n].type = MOLFILE_COLOR;
00343         graphics[n].data[0] = vertColors[3*idx[0]  ];
00344         graphics[n].data[1] = vertColors[3*idx[0]+1];
00345         graphics[n].data[2] = vertColors[3*idx[0]+2];
00346         graphics[n].data[3] = vertColors[3*idx[1]  ];
00347         graphics[n].data[4] = vertColors[3*idx[1]+1];
00348         graphics[n].data[5] = vertColors[3*idx[1]+2];
00349         graphics[n].data[6] = vertColors[3*idx[2]  ];
00350         graphics[n].data[7] = vertColors[3*idx[2]+1];
00351         graphics[n].data[8] = vertColors[3*idx[2]+2];
00352         n++;
00353       }
00354     } else if (k == 4) {
00355       j = sscanf (buff, "%d %d %d %d %d %g %g %g", &k, &idx[0], &idx[1], &idx[2], &idx[3], &c[0], &c[1], &c[2]);
00356       bool hasColor = ((j == 8) || (vertHasColor[idx[0]] && vertHasColor[idx[1]] && vertHasColor[idx[2]] && vertHasColor[idx[3]]));
00357 
00358       // Split a quadrangle into two triangles
00359       graphics[n].type = (hasColor ? MOLFILE_TRICOLOR : MOLFILE_TRIANGLE);
00360       graphics[n].data[0] = vertices[3*idx[0]  ];
00361       graphics[n].data[1] = vertices[3*idx[0]+1];
00362       graphics[n].data[2] = vertices[3*idx[0]+2];
00363       graphics[n].data[3] = vertices[3*idx[1]  ];
00364       graphics[n].data[4] = vertices[3*idx[1]+1];
00365       graphics[n].data[5] = vertices[3*idx[1]+2];
00366       graphics[n].data[6] = vertices[3*idx[2]  ];
00367       graphics[n].data[7] = vertices[3*idx[2]+1];
00368       graphics[n].data[8] = vertices[3*idx[2]+2];
00369       n++;
00370 
00371       if (j == 8) {
00372         graphics[n].type = MOLFILE_NORMS;
00373         calcNormals (graphics[n-1].data, graphics[n].data);
00374         n++;
00375       
00376         graphics[n].type = MOLFILE_COLOR;
00377         graphics[n].data[0] = graphics[n].data[3] = graphics[n].data[6] = c[0];
00378         graphics[n].data[1] = graphics[n].data[4] = graphics[n].data[7] = c[1];
00379         graphics[n].data[2] = graphics[n].data[5] = graphics[n].data[8] = c[2];
00380         n++;
00381       } else if (hasColor) {
00382         graphics[n].type = MOLFILE_NORMS;
00383         calcNormals (graphics[n-1].data, graphics[n].data);
00384         n++;
00385 
00386         graphics[n].type = MOLFILE_COLOR;
00387         graphics[n].data[0] = vertColors[3*idx[0]];
00388         graphics[n].data[1] = vertColors[3*idx[0]+1];
00389         graphics[n].data[2] = vertColors[3*idx[0]+2];
00390         graphics[n].data[3] = vertColors[3*idx[1]];
00391         graphics[n].data[4] = vertColors[3*idx[1]+1];
00392         graphics[n].data[5] = vertColors[3*idx[1]+2];
00393         graphics[n].data[6] = vertColors[3*idx[2]];
00394         graphics[n].data[7] = vertColors[3*idx[2]+1];
00395         graphics[n].data[8] = vertColors[3*idx[2]+2];
00396         n++;
00397       }
00398 
00399       graphics[n].type = (hasColor ? MOLFILE_TRICOLOR : MOLFILE_TRIANGLE);
00400       graphics[n].data[0] = vertices[3*idx[2]];
00401       graphics[n].data[1] = vertices[3*idx[2]+1];
00402       graphics[n].data[2] = vertices[3*idx[2]+2];
00403       graphics[n].data[3] = vertices[3*idx[3]];
00404       graphics[n].data[4] = vertices[3*idx[3]+1];
00405       graphics[n].data[5] = vertices[3*idx[3]+2];
00406       graphics[n].data[6] = vertices[3*idx[0]];
00407       graphics[n].data[7] = vertices[3*idx[0]+1];
00408       graphics[n].data[8] = vertices[3*idx[0]+2];
00409       n++;
00410 
00411       if (j == 8) {
00412         graphics[n].type = MOLFILE_NORMS;
00413         calcNormals (graphics[n-1].data, graphics[n].data);
00414         n++;
00415 
00416         graphics[n].type = MOLFILE_COLOR;
00417         graphics[n].data[0] = graphics[n].data[3] = graphics[n].data[6] = c[0];
00418         graphics[n].data[1] = graphics[n].data[4] = graphics[n].data[7] = c[1];
00419         graphics[n].data[2] = graphics[n].data[5] = graphics[n].data[8] = c[2];
00420         n++;
00421       } else if (hasColor) {
00422         graphics[n].type = MOLFILE_NORMS;
00423         calcNormals (graphics[n-1].data, graphics[n].data);
00424         n++;
00425 
00426         graphics[n].type = MOLFILE_COLOR;
00427         graphics[n].data[0] = vertColors[3*idx[2]];
00428         graphics[n].data[1] = vertColors[3*idx[2]+1];
00429         graphics[n].data[2] = vertColors[3*idx[2]+2];
00430         graphics[n].data[3] = vertColors[3*idx[3]];
00431         graphics[n].data[4] = vertColors[3*idx[3]+1];
00432         graphics[n].data[5] = vertColors[3*idx[3]+2];
00433         graphics[n].data[6] = vertColors[3*idx[0]];
00434         graphics[n].data[7] = vertColors[3*idx[0]+1];
00435         graphics[n].data[8] = vertColors[3*idx[0]+2];
00436         n++;
00437       }
00438     }
00439   }
00440 
00441   *nelem = n;
00442   *data = (molfile_graphics_t *) realloc(graphics, n*sizeof(molfile_graphics_t));
00443   return MOLFILE_SUCCESS;
00444 
00445   // goto jump target for disaster handling: free memory and bail out
00446   error:
00447     free (graphics);
00448     free (vertices);
00449 #endif
00450 
00451   printf("amiraplugin) read rawgraphics not implemented yet...\n");
00452   return MOLFILE_ERROR;
00453 }
00454 
00455 
00456 
00457 
00458 /*
00459  * Initialization stuff here
00460  */
00461 static molfile_plugin_t plugin;
00462 
00463 VMDPLUGIN_API int VMDPLUGIN_init(void) {
00464   memset(&plugin, 0, sizeof(molfile_plugin_t));
00465   plugin.abiversion = vmdplugin_ABIVERSION;
00466   plugin.type = MOLFILE_PLUGIN_TYPE;
00467   plugin.name = "am";
00468   plugin.prettyname = "Amira File Format (.am)";
00469   plugin.author = "John Stone";
00470   plugin.majorv = 0;
00471   plugin.minorv = 1;
00472   plugin.is_reentrant = VMDPLUGIN_THREADSAFE;
00473   plugin.filename_extension = "am";
00474   plugin.open_file_read = open_file_read;
00475   plugin.read_volumetric_metadata = read_volumetric_metadata;
00476   plugin.read_volumetric_data = read_volumetric_data;
00477   plugin.read_rawgraphics = read_rawgraphics;
00478   plugin.close_file_read = close_file_read;
00479   return VMDPLUGIN_SUCCESS;
00480 }
00481 
00482 VMDPLUGIN_API int VMDPLUGIN_register(void *v, vmdplugin_register_cb cb) {
00483   (*cb)(v, (vmdplugin_t *)&plugin);
00484   return VMDPLUGIN_SUCCESS;
00485 }
00486 
00487 VMDPLUGIN_API int VMDPLUGIN_fini(void) { return VMDPLUGIN_SUCCESS; }
00488 
00489 
00490 

Generated on Sat Aug 15 03:06:48 2020 for VMD Plugins (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002