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