1 /* Copyright (c) 1999-2017 Massachusetts Institute of Technology
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be
12  * included in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <math.h>
27 #include <ctype.h>
28 
29 #include <unistd.h>
30 
31 #include "config.h"
32 
33 #ifdef HAVE_STDINT_H
34 #  include <stdint.h>
35 #endif
36 #ifdef HAVE_INTTYPES_H
37 #  include <inttypes.h>
38 #endif
39 
40 #if defined(HAVE_ARPA_INET_H)
41 #  include <arpa/inet.h>
42 #elif defined(HAVE_NETINET_IN_H)
43 #  include <netinet/in.h>
44 #endif
45 
46 #include "arrayh5.h"
47 #include "copyright.h"
48 #include "h5utils.h"
49 
50 #ifdef HAVE_UINT16_T
51 typedef uint16_t my_uint16_t;
52 #else
53 typedef unsigned short my_uint16_t;
54 #endif
55 #ifdef HAVE_UINT32_T
56 typedef uint32_t my_uint32_t;
57 #else
58 typedef unsigned long my_uint32_t;
59 #endif
60 
61 #define CHECK(cond, msg) { if (!(cond)) { fprintf(stderr, "h5tovtk error: %s\n", msg); exit(EXIT_FAILURE); } }
62 
usage(FILE * f)63 void usage(FILE *f)
64 {
65      fprintf(f, "Usage: h5tovtk [options] [<filenames>]\n"
66 	     "Options:\n"
67 	     "         -h : this help message\n"
68              "         -V : print version number and copyright\n"
69 	     "         -v : verbose output\n"
70 	     "  -o <file> : output datasets from all input files to <file>;\n"
71 	     "              combines 3 datasets to a vector, and 2 or 4+ to a field\n"
72 	     "         -4 : 4-byte floating-point binary output (default)\n"
73 	     "         -a : ASCII floating-point output\n"
74 	     "         -1 : 1-byte (0-255) binary output\n"
75 	     "         -2 : 2-byte (0-65535) binary output\n"
76 	     "         -n : don't convert binary output to bigendian\n"
77 	     "   -m <min> : set bottom of scale for 1/2 byte encoding\n"
78 	     "   -M <max> : set top of scale for 1/2 byte encoding\n"
79 	     "         -Z : center scale at zero for 1/2 byte encoding\n"
80 	     "         -r : invert scale & data values\n"
81 	     "    -x <ix> : take x=<ix> slice of data\n"
82 	     "    -y <iy> : take y=<iy> slice of data\n"
83 	     "    -z <iz> : take z=<iz> slice of data\n"
84 	     "    -t <it> : take t=<it> slice of data's last dimension\n"
85 	     "         -0 : use dataset center as origin for -x/-y/-z\n"
86 	     "  -d <name> : use dataset <name> in the input files (default: first dataset)\n"
87 	     "              -- you can also specify a dataset via <filename>:<name>\n"
88 	  );
89 }
90 
whitespace_to_underscores(char * s)91 static void whitespace_to_underscores(char *s)
92 {
93      while (*s) {
94 	  if (isspace(*s))
95 	       *s = '-';
96 	  ++s;
97      }
98 }
99 
100 static const char vtk_datatype[][20] = {
101      "float", "unsigned_char", "unsigned_short", "none", "float"
102 };
103 
write_vtk_header(FILE * f,int is_binary,int nx,int ny,int nz,double ox,double oy,double oz,double sx,double sy,double sz)104 static void write_vtk_header(FILE *f, int is_binary,
105 			     int nx, int ny, int nz,
106 			     double ox, double oy, double oz,
107 			     double sx, double sy, double sz)
108 {
109      fprintf(f,
110 	     "# vtk DataFile Version 2.0\n"
111 	     "Generated by h5tovtk.\n"
112 	     "%s\n"
113 	     "DATASET STRUCTURED_POINTS\n"
114 	     "DIMENSIONS %d %d %d\n"
115 	     "ORIGIN %g %g %g\n"
116 	     "SPACING %g %g %g\n",
117 	     is_binary ? "BINARY" : "ASCII",
118 	     nx, ny, nz,
119 	     ox, oy, oz, sx, sy, sz);
120 }
121 
write_vtk_value(FILE * f,double v,int store_bytes,int fix_bytes,double min,double max,int invert)122 static void write_vtk_value(FILE *f, double v, int store_bytes, int fix_bytes,
123 			    double min, double max, int invert)
124 {
125      if (invert)
126 	  v = max - (v - min);
127      switch (store_bytes) {
128 	 case 0:
129 	      fprintf(f, "%g ", v);
130 	      break;
131 	 case 1:
132 	 {
133 	      unsigned char c;
134 	      c = floor((v - min) * 255.0 / (max - min) + 0.5);
135 	      fwrite(&c, 1, 1, f);
136 	      break;
137 	 }
138 	 case 2:
139 	 {
140 	      my_uint16_t i;
141 	      i = floor((v - min) * 65535.0 / (max - min) + 0.5);
142 	      if (fix_bytes) {
143 #if defined(HAVE_HTONS)
144 		   i = htons(i);
145 #elif ! defined(WORDS_BIGENDIAN)
146 		   unsigned char swap, *bytes;
147 		   bytes = (unsigned char *) &i;
148 		   swap = bytes[0]; bytes[0] = bytes[1]; bytes[1] = swap;
149 #endif
150 	      }
151 	      fwrite(&i, 2, 1, f);
152 	      break;
153 	 }
154 	 case 4:
155 	 {
156 	      float fv = v;
157 	      if (fix_bytes) {
158 #if defined(HAVE_HTONL) && (SIZEOF_FLOAT == 4)
159 		   my_uint32_t *i = (my_uint32_t *) &fv;
160 		   *i = htonl(*i);
161 #elif ! defined(WORDS_BIGENDIAN)
162 		   unsigned char swap, *bytes;
163 		   bytes = (unsigned char *) &fv;
164 		   swap = bytes[0]; bytes[0] = bytes[3]; bytes[3] = swap;
165 		   swap = bytes[1]; bytes[1] = bytes[2]; bytes[2] = swap;
166 #endif
167 	      }
168 	      fwrite(&fv, 4, 1, f);
169 	      break;
170 	 }
171      }
172 }
173 
main(int argc,char ** argv)174 int main(int argc, char **argv)
175 {
176      arrayh5 *a = NULL;
177      char *vtk_fname = NULL, *data_name = NULL;
178      extern char *optarg;
179      extern int optind;
180      double ox = 0, oy = 0, oz = 0, sx = 1, sy = 1, sz = 1;
181      int c, ifile;
182      int zero_center = 0;
183      int invert = 0;
184      double min = 0, max = 0;
185      int min_set = 0, max_set = 0;
186      int verbose = 0, combine = 0;
187      int slicedim[4] = {NO_SLICE_DIM,NO_SLICE_DIM,NO_SLICE_DIM,NO_SLICE_DIM};
188      int islice[4], center_slice[4] = {0,0,0,0};
189      int nx = 0, ny = 0, nz = 0, na;
190      int store_bytes = 4, fix_byte_order = 1;
191 
192      while ((c = getopt(argc, argv, "ho:d:vV124mMZranx:y:z:t:0")) != -1)
193 	  switch (c) {
194 	      case 'h':
195 		   usage(stdout);
196 		   return EXIT_SUCCESS;
197 	      case 'V':
198 		   printf("h5tovtk " PACKAGE_VERSION " by Steven G. Johnson\n"
199 			  COPYRIGHT);
200 		   return EXIT_SUCCESS;
201 	      case 'v':
202 		   verbose = 1;
203 		   break;
204 	      case 'x':
205 		   islice[0] = atoi(optarg);
206 		   slicedim[0] = 0;
207 		   break;
208 	      case 'y':
209 		   islice[1] = atoi(optarg);
210 		   slicedim[1] = 1;
211 		   break;
212 	      case 'z':
213 		   islice[2] = atoi(optarg);
214 		   slicedim[2] = 2;
215 		   break;
216 	      case 't':
217 		   islice[3] = atoi(optarg);
218 		   slicedim[3] = LAST_SLICE_DIM;
219 		   break;
220               case '0':
221                    center_slice[0] = center_slice[1] = center_slice[2] = 1;
222                    break;
223 	      case 'n':
224 		   fix_byte_order = 0;
225 		   break;
226 	      case 'a':
227 		   store_bytes = 0; /* ascii */
228 		   break;
229 	      case '1':
230 		   store_bytes = 1;
231 		   break;
232 	      case '2':
233 		   store_bytes = 2;
234 		   break;
235 	      case '4':
236 		   store_bytes = 4;
237 		   break;
238 	      case 'm':
239 		   min = atof(optarg);
240 		   min_set = 1;
241 		   break;
242 	      case 'M':
243 		   max = atof(optarg);
244 		   max_set = 1;
245 		   break;
246 	      case 'Z':
247 		   zero_center = 1;
248 		   break;
249 	      case 'r':
250 		   invert = 1;
251 		   break;
252 	      case 'o':
253 		   vtk_fname = my_strdup(optarg);
254 		   combine = 1;
255 		   break;
256 	      case 'd':
257 		   data_name = my_strdup(optarg);
258 		   break;
259 	      default:
260 		   fprintf(stderr, "Invalid argument -%c\n", c);
261 		   usage(stderr);
262 		   return EXIT_FAILURE;
263 	  }
264      if (optind == argc) {  /* no parameters left */
265 	  usage(stderr);
266 	  return EXIT_FAILURE;
267      }
268 
269      CHECK(store_bytes != 4 || sizeof(float) == 4,
270 	   "'float' is wrong size for -4");
271      CHECK(store_bytes != 4 || sizeof(my_uint32_t) == 4,
272 	   "missing 4-byte integer type for -4");
273      CHECK(store_bytes != 2 || sizeof(my_uint16_t) == 2,
274 	   "missing 2-byte integer type for -2");
275 
276      a = (arrayh5*) malloc(sizeof(arrayh5) * (na = argc - optind));
277      CHECK(a, "out of memory");
278 
279      combine = combine && (na > 1);
280 
281      for (ifile = optind; ifile < argc; ++ifile) {
282           char *dname, *found_dname, *h5_fname;
283 	  int err, ia = ifile - optind;
284           h5_fname = split_fname(argv[ifile], &dname);
285           if (!dname[0])
286                dname = data_name;
287 
288 	  err = arrayh5_read(&a[ia], h5_fname, dname, &found_dname,
289 			     4, slicedim, islice, center_slice);
290 	  CHECK(!err, arrayh5_read_strerror[err]);
291 	  CHECK(a[ia].rank >= 1, "data must have at least one dimension");
292 	  CHECK(a[ia].rank <= 3, "data can have at most 3 dimensions (try taking a slice");
293 
294 	  CHECK(!combine || !ia || arrayh5_conformant(a[ia], a[0]),
295 		"all arrays must be conformant to combine them");
296 
297 	  if (!vtk_fname)
298 	       vtk_fname = replace_suffix(h5_fname, ".h5", ".vtk");
299 
300 	  {
301 	       double a_min, a_max;
302 	       arrayh5_getrange(a[ia], &a_min, &a_max);
303 	       if (verbose)
304 		    printf("data in %s ranges from %g to %g.\n",
305 			   h5_fname, a_min, a_max);
306 	       if (!min_set)
307 		    min = (!combine || !ia || a_min < min) ? a_min : min;
308 	       if (!max_set)
309 		    max = (!combine || !ia || a_max > max) ? a_max : max;
310 	       if (min > max) {
311 		    invert = !invert;
312 		    a_min = min;
313 		    min = max;
314 		    max = a_min;
315 	       }
316 	       if (zero_center) {
317 		    max = fabs(max) > fabs(min) ? fabs(max) : fabs(min);
318 		    min = -max;
319 	       }
320 	  }
321 
322 	  nx = a[ia].dims[0];
323 	  ny = a[ia].rank < 2 ? 1 : a[ia].dims[1];
324 	  nz = a[ia].rank < 3 ? 1 : a[ia].dims[2];
325 
326 	  if (!combine) {
327 	       FILE *f;
328 	       int ix, iy, iz, N = nx * ny * nz;
329 
330 	       if (verbose)
331 		    printf("writing \"%s\" from %dx%dx%d input data.\n",
332 			   vtk_fname, nx, ny, nz);
333 
334 	       if (strcmp(vtk_fname, "-")) {
335 		    f = fopen(vtk_fname, "w");
336 		    CHECK(f, "error creating file");
337 	       }
338 	       else
339 		    f = stdout;
340 
341 	       write_vtk_header(f, store_bytes,
342 				nx, ny, nz, ox, oy, oz, sx, sy, sz);
343 	       whitespace_to_underscores(found_dname);
344 	       fprintf(f, "POINT_DATA %d\n"
345 		       "SCALARS %s %s 1\n"
346 		       "LOOKUP_TABLE default\n",
347 		       N, found_dname, vtk_datatype[store_bytes]);
348 
349 	       for (iz = 0; iz < nz; ++iz)
350 	       for (iy = 0; iy < ny; ++iy)
351 	       for (ix = 0; ix < nx; ++ix) {
352 		    int i = (ix*ny + iy)*nz + iz;
353 		    write_vtk_value(f, a[ia].data[i], store_bytes,
354 				    fix_byte_order, min, max, invert);
355 	       }
356 
357 	       if (f != stdout)
358 		    fclose(f);
359 	       arrayh5_destroy(a[ia]);
360 	       free(vtk_fname); vtk_fname = NULL;
361 	  }
362 	  free(found_dname);
363 	  free(h5_fname);
364      }
365 
366      if (combine) {
367 	  FILE *f;
368 	  int ix, iy, iz, N = nx * ny * nz;
369 
370 	  if (verbose)
371 	       printf("writing \"%s\" from %dx%dx%d input data.\n",
372 		      vtk_fname, nx, ny, nz);
373 
374 	  if (strcmp(vtk_fname, "-")) {
375 	       f = fopen(vtk_fname, "w");
376 	       CHECK(f, "error creating file");
377 	  }
378 	  else
379 	       f = stdout;
380 
381 	  write_vtk_header(f, store_bytes,
382 			   nx, ny, nz, ox, oy, oz, sx, sy, sz);
383 	  fprintf(f, "POINT_DATA %d\n", N);
384 	  switch (na) {
385 	      case 1:
386 		   fprintf(f, "SCALARS scalars %s 1\nLOOKUP_TABLE default\n",
387 			   vtk_datatype[store_bytes]);
388 		   break;
389 	      case 3:
390 		   fprintf(f, "VECTORS vectors %s\n",
391 			   vtk_datatype[store_bytes]);
392 		   break;
393 	      default:
394 		   fprintf(f, "FIELD fields 1\narray %d %d %s\n",
395 			   na, N, vtk_datatype[store_bytes]);
396 	  }
397 
398 	  for (iz = 0; iz < nz; ++iz)
399 	  for (iy = 0; iy < ny; ++iy)
400 	  for (ix = 0; ix < nx; ++ix) {
401                int ia, i = (ix*ny + iy)*nz + iz;
402 	       for (ia = 0; ia < na; ++ia)
403 		    write_vtk_value(f, a[ia].data[i], store_bytes,
404 				    fix_byte_order, min, max, invert);
405 	  }
406 	  if (f != stdout)
407 	       fclose(f);
408 	  {
409 	       int ia;
410 	       for (ia = 0; ia < na; ++ia)
411 		    arrayh5_destroy(a[ia]);
412 	  }
413      }
414 
415      free(a);
416 
417      if (data_name)
418 	  free(data_name);
419 
420      return EXIT_SUCCESS;
421 }
422