ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.13
Committed: Wed Mar 25 22:50:48 2015 UTC (9 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.12: +3 -3 lines
Log Message:
Fixed bug in CIE (u',v') BSDF output

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: wrapBSDF.c,v 2.12 2015/03/04 17:42:54 greg Exp $";
3 #endif
4 /*
5 * Wrap BSDF data in valid WINDOW XML file
6 *
7 * G. Ward February 2015
8 */
9
10 #include <ctype.h>
11 #include "rtio.h"
12 #include "rtprocess.h"
13 #include "ezxml.h"
14 #include "bsdf.h"
15 #include "bsdf_m.h"
16 /* XML template file names */
17 const char def_template[] = "minimalBSDFt.xml";
18 const char win6_template[] = "WINDOW6BSDFt.xml";
19
20 const char stdin_name[] = "<stdin>";
21 /* input files (can be stdin_name) */
22 const char *xml_input = NULL;
23 /* unit for materials & geometry */
24 const char *attr_unit = "meter";
25 const char legal_units[] = "meter|foot|inch|centimeter|millimeter";
26 /* system materials & geometry */
27 const char *mgf_geometry = NULL;
28
29 /* angle bases */
30 enum { ABdefault=-1, ABklemsFull=0, ABklemsHalf, ABklemsQuarter,
31 ABtensorTree3, ABtensorTree4, ABend };
32
33 int angle_basis = ABdefault;
34
35 int correct_solid_angle = 0;
36
37 const char *klems_basis_name[] = {
38 "LBNL/Klems Full",
39 "LBNL/Klems Half",
40 "LBNL/Klems Quarter",
41 };
42 /* field IDs and nicknames */
43 struct s_fieldID {
44 char nickName[4];
45 short has_unit;
46 short win_need;
47 const char *fullName;
48 } XMLfieldID[] = {
49 {"m", 0, 1, "Manufacturer"},
50 {"n", 0, 1, "Name"},
51 {"c", 0, 0, "ThermalConductivity"},
52 {"ef", 0, 0, "EmissivityFront"},
53 {"eb", 0, 0, "EmissivityBack"},
54 {"tir", 0, 0, "TIR"},
55 {"eo", 0, 0, "EffectiveOpennessFraction"},
56 {"t", 1, 1, "Thickness"},
57 {"h", 1, 0, "Height"},
58 {"w", 1, 0, "Width"},
59 {"\0", 0, 0, NULL} /* terminator */
60 };
61 /* field assignments */
62 #define MAXASSIGN 12
63 const char *field_assignment[MAXASSIGN];
64 int nfield_assign = 0;
65 #define FASEP ';'
66 /* data file(s) & spectra */
67 enum { DTtransForward, DTtransBackward, DTreflForward, DTreflBackward };
68
69 enum { DSsolar=-1, DSnir=-2, DSxbar31=-3, DSvisible=-4, DSzbar31=-5,
70 DSuprime=-6, DSvprime=-7 };
71
72 #define MAXFILES 20
73
74 struct s_dfile {
75 const char *fname; /* input data file name */
76 short type; /* BSDF data type */
77 short spectrum; /* BSDF sensor spectrum */
78 } data_file[MAXFILES];
79
80 int ndataf = 0; /* number of data files */
81
82 const char *spectr_file[MAXFILES]; /* custom spectral curve input */
83
84 const char top_level_name[] = "WindowElement";
85
86 static char basis_definition[][256] = {
87
88 "\t<DataDefinition>\n"
89 "\t\t<IncidentDataStructure>Columns</IncidentDataStructure>\n"
90 "\t\t<AngleBasis>\n"
91 "\t\t\t<AngleBasisName>LBNL/Klems Full</AngleBasisName>\n"
92 "\t\t\t</AngleBasis>\n"
93 "\t</DataDefinition>\n",
94
95 "\t<DataDefinition>\n"
96 "\t\t<IncidentDataStructure>Columns</IncidentDataStructure>\n"
97 "\t\t<AngleBasis>\n"
98 "\t\t\t<AngleBasisName>LBNL/Klems Half</AngleBasisName>\n"
99 "\t\t\t</AngleBasis>\n"
100 "\t</DataDefinition>\n",
101
102 "\t<DataDefinition>\n"
103 "\t\t<IncidentDataStructure>Columns</IncidentDataStructure>\n"
104 "\t\t<AngleBasis>\n"
105 "\t\t\t<AngleBasisName>LBNL/Klems Quarter</AngleBasisName>\n"
106 "\t\t\t</AngleBasis>\n"
107 "\t</DataDefinition>\n",
108
109 "\t<DataDefinition>\n"
110 "\t\t<IncidentDataStructure>TensorTree3</IncidentDataStructure>\n"
111 "\t</DataDefinition>\n",
112
113 "\t<DataDefinition>\n"
114 "\t\t<IncidentDataStructure>TensorTree4</IncidentDataStructure>\n"
115 "\t</DataDefinition>\n",
116 };
117
118 /* Copy data from file descriptor to stdout and close */
119 static int
120 copy_and_close(int fd)
121 {
122 int ok = 1;
123 char buf[8192];
124 int n;
125
126 if (fd < 0)
127 return 0;
128 while ((n = read(fd, buf, sizeof(buf))) > 0)
129 if (write(fileno(stdout), buf, n) != n) {
130 ok = 0;
131 break;
132 }
133 ok &= (n == 0);
134 close(fd);
135 return ok;
136 }
137
138 /* Allocate and assign string from file or stream */
139 static char *
140 input2str(const char *inpspec)
141 {
142 FILE *fp = NULL;
143 char *str;
144 int len, pos, n;
145
146 if (inpspec == NULL || !*inpspec)
147 return "";
148 if (inpspec == stdin_name) { /* read from stdin */
149 fp = stdin;
150 } else if (inpspec[0] == '!') { /* read from command */
151 fp = popen(inpspec+1, "r");
152 if (fp == NULL) {
153 fprintf(stderr, "Cannot start process '%s'\n",
154 inpspec);
155 return "";
156 }
157 } else { /* else load file */
158 int fd = open(inpspec, O_RDONLY);
159 if (fd < 0) {
160 fprintf(stderr, "%s: cannot open\n", inpspec);
161 return "";
162 }
163 #ifndef _WIN32 /* XXX somehow broken on Windows */
164 len = lseek(fd, 0L, SEEK_END);
165 if (len > 0) {
166 lseek(fd, 0L, SEEK_SET);
167 str = (char *)malloc(len+1);
168 if (str == NULL) {
169 close(fd);
170 goto memerr;
171 }
172 if (read(fd, str, len) != len) {
173 fprintf(stderr, "%s: read error\n", inpspec);
174 free(str);
175 close(fd);
176 return "";
177 }
178 str[len] = '\0';
179 close(fd);
180 return str;
181 }
182 #endif
183 fp = fdopen(fd, "r"); /* not a regular file */
184 }
185 /* reading from stream */
186 str = (char *)malloc((len=8192)+1);
187 if (str == NULL)
188 goto memerr;
189 pos = 0;
190 while ((n = read(fileno(fp), str+pos, len-pos)) > 0)
191 if ((pos += n) >= len) { /* need more space? */
192 str = (char *)realloc(str, (len += len>>2) + 1);
193 if (str == NULL)
194 goto memerr;
195 }
196 if (n < 0) {
197 fprintf(stderr, "%s: read error\n", inpspec);
198 free(str);
199 str = "";
200 } else { /* tidy up result */
201 str[pos] = '\0';
202 str = (char *)realloc(str, (len=pos)+1);
203 if (str == NULL)
204 goto memerr;
205 }
206 if (inpspec[0] != '!')
207 fclose(fp);
208 else if (pclose(fp))
209 fprintf(stderr, "Error running command '%s'\n", inpspec);
210 return str;
211 memerr:
212 fprintf(stderr, "%s: error allocating memory\n", inpspec);
213 if (fp != NULL)
214 (inpspec[0] == '!') ? pclose(fp) : fclose(fp);
215 return "";
216 }
217
218 /* Make material assignments in field_assignment to XML fields */
219 static int
220 mat_assignments(const char *caller, const char *fn, ezxml_t wtl)
221 {
222 int i;
223
224 wtl = ezxml_child(wtl, "Material");
225 if (wtl == NULL) {
226 fprintf(stderr, "%s: missing <Material> tag\n", fn);
227 return 0;
228 }
229 for (i = 0; i < nfield_assign; i++) {
230 const char *fnext = field_assignment[i];
231 for ( ; ; ) {
232 int added = 0;
233 ezxml_t fld;
234 char sbuf[512];
235 int j;
236
237 while (isspace(*fnext))
238 ++fnext;
239 if (!*fnext)
240 break;
241 for (j = 0; *fnext != '=' && !isspace(*fnext); ) {
242 if (!*fnext | (*fnext == FASEP) |
243 (j >= sizeof(sbuf)-1)) {
244 fprintf(stderr,
245 "%s: bad tag name in assignment '%s'\n",
246 caller, field_assignment[i]);
247 return 0;
248 }
249 sbuf[j++] = *fnext++;
250 }
251 sbuf[j] = '\0'; /* check known field */
252 for (j = 0; XMLfieldID[j].nickName[0]; j++)
253 if (!strcasecmp(sbuf, XMLfieldID[j].nickName) ||
254 !strcasecmp(sbuf, XMLfieldID[j].fullName)) {
255 strcpy(sbuf, XMLfieldID[j].fullName);
256 break;
257 }
258 /* check if tag exists */
259 fld = ezxml_child(wtl, sbuf);
260 if (fld == NULL) { /* otherwise, create one */
261 if (!XMLfieldID[j].nickName[0])
262 fprintf(stderr,
263 "%s: warning - adding tag <%s>\n",
264 fn, sbuf);
265 ezxml_add_txt(wtl, "\t");
266 fld = ezxml_add_child_d(wtl, sbuf, strlen(wtl->txt));
267 ++added;
268 }
269 if (XMLfieldID[j].has_unit)
270 ezxml_set_attr(fld, "unit", attr_unit);
271 XMLfieldID[j].win_need = 0;
272 while (isspace(*fnext))
273 ++fnext;
274 if (*fnext++ != '=') {
275 fprintf(stderr,
276 "%s: missing '=' in assignment '%s'\n",
277 caller, field_assignment[i]);
278 return 0;
279 }
280 for (j = 0; *fnext && *fnext != FASEP; ) {
281 if (j >= sizeof(sbuf)-1) {
282 fprintf(stderr,
283 "%s: field too long in '%s'\n",
284 caller, field_assignment[i]);
285 return 0;
286 }
287 sbuf[j++] = *fnext++;
288 }
289 sbuf[j] = '\0';
290 ezxml_set_txt_d(fld, sbuf);
291 if (added)
292 ezxml_add_txt(wtl, "\n\t");
293 fnext += (*fnext == FASEP);
294 }
295 }
296 /* check required WINDOW settings */
297 if (xml_input == win6_template)
298 for (i = 0; XMLfieldID[i].nickName[0]; i++)
299 if (XMLfieldID[i].win_need &&
300 !ezxml_txt(ezxml_child(wtl,XMLfieldID[i].fullName))[0])
301 fprintf(stderr,
302 "%s: warning - missing '%s' assignment for WINDOW <%s>\n",
303 caller, XMLfieldID[i].nickName,
304 XMLfieldID[i].fullName);
305 return 1;
306 }
307
308 /* Complete angle basis specification */
309 static int
310 finish_angle_basis(ezxml_t ab)
311 {
312 const char *bn = ezxml_txt(ezxml_child(ab, "AngleBasisName"));
313 int i, n = nabases;
314 char buf[32];
315
316 if (!*bn) {
317 fputs("Internal error - missing <AngleBasisName>!\n", stderr);
318 return 0;
319 }
320 while (n-- > 0)
321 if (!strcasecmp(bn, abase_list[n].name))
322 break;
323 if (n < 0) {
324 fprintf(stderr, "Internal error - unknown angle basis '%s'", bn);
325 return 0;
326 }
327 for (i = 0; abase_list[n].lat[i].nphis; i++) {
328 ezxml_t tb, abb = ezxml_add_child(ab, "AngleBasisBlock",
329 strlen(ab->txt));
330 sprintf(buf, "%g", i ?
331 .5*(abase_list[n].lat[i].tmin + abase_list[n].lat[i+1].tmin) :
332 .0);
333 ezxml_add_txt(abb, "\n\t\t\t\t");
334 ezxml_set_txt_d(ezxml_add_child(abb,"Theta",strlen(abb->txt)), buf);
335 sprintf(buf, "%d", abase_list[n].lat[i].nphis);
336 ezxml_add_txt(abb, "\n\t\t\t\t");
337 ezxml_set_txt_d(ezxml_add_child(abb,"nPhis",strlen(abb->txt)), buf);
338 ezxml_add_txt(abb, "\n\t\t\t\t");
339 tb = ezxml_add_child(abb, "ThetaBounds", strlen(abb->txt));
340 ezxml_add_txt(tb, "\n\t\t\t\t\t");
341 sprintf(buf, "%g", abase_list[n].lat[i].tmin);
342 ezxml_set_txt_d(ezxml_add_child(tb,"LowerTheta",strlen(tb->txt)), buf);
343 ezxml_add_txt(tb, "\n\t\t\t\t\t");
344 sprintf(buf, "%g", abase_list[n].lat[i+1].tmin);
345 ezxml_set_txt_d(ezxml_add_child(tb,"UpperTheta",strlen(tb->txt)), buf);
346 ezxml_add_txt(tb, "\n\t\t\t\t");
347 ezxml_add_txt(abb, "\n\t\t\t");
348 ezxml_add_txt(ab, "\n\t\t\t");
349 }
350 return 1;
351 }
352
353 /* Determine our angle basis from current tags */
354 static int
355 determine_angle_basis(const char *fn, ezxml_t wtl)
356 {
357 const char *ids;
358 int i;
359
360 wtl = ezxml_child(wtl, "DataDefinition");
361 if (wtl == NULL)
362 return -1;
363 ids = ezxml_txt(ezxml_child(wtl, "IncidentDataStructure"));
364 if (!ids[0])
365 return -1;
366 for (i = 0; i < ABend; i++) {
367 ezxml_t parsed = ezxml_parse_str(basis_definition[i],
368 strlen(basis_definition[i]));
369 int match = 0;
370 if (!strcmp(ids, ezxml_txt(ezxml_child(parsed,
371 "IncidentDataStructure")))) {
372 const char *abn0 = ezxml_txt(
373 ezxml_child(ezxml_child(wtl,
374 "AngleBasis"), "AngleBasisName"));
375 const char *abn1 = ezxml_txt(
376 ezxml_child(ezxml_child(parsed,
377 "AngleBasis"), "AngleBasisName"));
378 match = !strcmp(abn0, abn1);
379 }
380 ezxml_free(parsed);
381 if (match)
382 return i;
383 }
384 return -1;
385 }
386
387 /* Filter Klems angle basis, factoring out incident projected solid angle */
388 static int
389 filter_klems_matrix(FILE *fp)
390 {
391 #define MAX_COLUMNS 145
392 const char *bn = klems_basis_name[angle_basis];
393 float col_corr[MAX_COLUMNS];
394 int i, j, n = nabases;
395 /* get angle basis */
396 while (n-- > 0)
397 if (!strcasecmp(bn, abase_list[n].name))
398 break;
399 if (n < 0)
400 return 0;
401 if (abase_list[n].nangles > MAX_COLUMNS) {
402 fputs("Internal error - too many Klems columns!\n", stderr);
403 return 0;
404 }
405 /* get correction factors */
406 for (j = abase_list[n].nangles; j--; )
407 col_corr[j] = 1.f / io_getohm(j, &abase_list[n]);
408 /* read/correct/write matrix */
409 for (i = 0; i < abase_list[n].nangles; i++) {
410 for (j = 0; j < abase_list[n].nangles; j++) {
411 double d;
412 if (fscanf(fp, "%lf", &d) != 1)
413 return 0;
414 if (d < -1e-3) {
415 fputs("Negative BSDF data!\n", stderr);
416 return 0;
417 }
418 printf(" %.3e", d*col_corr[j]*(d > 0));
419 }
420 fputc('\n', stdout);
421 }
422 while ((i = getc(fp)) != EOF)
423 if (!isspace(i)) {
424 fputs("Unexpected data past EOF\n", stderr);
425 return 0;
426 }
427 return 1; /* all is good */
428 #undef MAX_COLUMNS
429 }
430
431 /* Write out BSDF data block with surrounding tags */
432 static int
433 writeBSDFblock(const char *caller, struct s_dfile *df)
434 {
435 int correct_klems = correct_solid_angle;
436 char *cp;
437
438 puts("\t<WavelengthData>");
439 puts("\t\t<LayerNumber>System</LayerNumber>");
440 switch (df->spectrum) {
441 case DSvisible:
442 puts("\t\t<Wavelength unit=\"Integral\">Visible</Wavelength>");
443 puts("\t\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
444 puts("\t\t<DetectorSpectrum>ASTM E308 1931 Y.dsp</DetectorSpectrum>");
445 break;
446 case DSxbar31:
447 puts("\t\t<Wavelength unit=\"Integral\">CIE-X</Wavelength>");
448 puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
449 puts("\t\t<DetectorSpectrum>ASTM E308 1931 X.dsp</DetectorSpectrum>");
450 break;
451 case DSzbar31:
452 puts("\t\t<Wavelength unit=\"Integral\">CIE-Z</Wavelength>");
453 puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
454 puts("\t\t<DetectorSpectrum>ASTM E308 1931 Z.dsp</DetectorSpectrum>");
455 break;
456 case DSuprime:
457 puts("\t\t<Wavelength unit=\"Integral\">CIE-u</Wavelength>");
458 puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
459 puts("\t\t<DetectorSpectrum>ASTM E308 1931 u.dsp</DetectorSpectrum>");
460 break;
461 case DSvprime:
462 puts("\t\t<Wavelength unit=\"Integral\">CIE-v</Wavelength>");
463 puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
464 puts("\t\t<DetectorSpectrum>ASTM E308 1931 v.dsp</DetectorSpectrum>");
465 break;
466 case DSsolar:
467 puts("\t\t<Wavelength unit=\"Integral\">Solar</Wavelength>");
468 puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
469 puts("\t\t<DetectorSpectrum>None</DetectorSpectrum>");
470 break;
471 case DSnir:
472 puts("\t\t<Wavelength unit=\"Integral\">NIR</Wavelength>");
473 puts("\t\tSourceSpectrum>PLACE_HOLDER</SourceSpectrum>");
474 puts("\t\t<DetectorSpectrum>PLACE_HOLDER</DetectorSpectrum>");
475 break;
476 default:
477 cp = strrchr(spectr_file[df->spectrum], '.');
478 if (cp != NULL)
479 *cp = '\0';
480 printf("\t\t<Wavelength unit=\"Integral\">%s</Wavelength>\n",
481 spectr_file[df->spectrum]);
482 if (cp != NULL)
483 *cp = '.';
484 puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
485 printf("\t\t<DetectorSpectrum>%s</DetectorSpectrum>\n",
486 spectr_file[df->spectrum]);
487 break;
488 }
489 puts("\t\t<WavelengthDataBlock>");
490 fputs("\t\t\t<WavelengthDataDirection>", stdout);
491 switch (df->type) {
492 case DTtransForward:
493 fputs("Transmission Front", stdout);
494 break;
495 case DTtransBackward:
496 fputs("Transmission Back", stdout);
497 break;
498 case DTreflForward:
499 fputs("Reflection Front", stdout);
500 break;
501 case DTreflBackward:
502 fputs("Reflection Back", stdout);
503 break;
504 default:
505 fprintf(stderr, "%s: internal - bad BSDF type (%d)\n", caller, df->type);
506 return 0;
507 }
508 puts("</WavelengthDataDirection>");
509 switch (angle_basis) {
510 case ABklemsFull:
511 case ABklemsHalf:
512 case ABklemsQuarter:
513 fputs("\t\t\t<ColumnAngleBasis>", stdout);
514 fputs(klems_basis_name[angle_basis], stdout);
515 puts("</ColumnAngleBasis>");
516 fputs("\t\t\t<RowAngleBasis>", stdout);
517 fputs(klems_basis_name[angle_basis], stdout);
518 puts("</RowAngleBasis>");
519 break;
520 case ABtensorTree3:
521 case ABtensorTree4:
522 puts("\t\t\t<AngleBasis>LBNL/Shirley-Chiu</AngleBasis>");
523 correct_klems = 0;
524 break;
525 default:
526 fprintf(stderr, "%s: bad angle basis (%d)\n", caller, angle_basis);
527 return 0;
528 }
529 puts("\t\t\t<ScatteringDataType>BTDF</ScatteringDataType>");
530 puts("\t\t\t<ScatteringData>");
531 fflush(stdout);
532 if (correct_klems) { /* correct Klems matrix data */
533 FILE *fp = stdin;
534 if (df->fname[0] == '!')
535 fp = popen(df->fname+1, "r");
536 else if (df->fname != stdin_name)
537 fp = fopen(df->fname, "r");
538 if (fp == NULL) {
539 fprintf(stderr, "%s: cannot open '%s'\n",
540 caller, df->fname);
541 return 0;
542 }
543 if (!filter_klems_matrix(fp)) {
544 fprintf(stderr, "%s: Klems data error from '%s'\n",
545 caller, df->fname);
546 return 0;
547 }
548 if (df->fname[0] != '!') {
549 fclose(fp);
550 } else if (pclose(fp)) {
551 fprintf(stderr, "%s: error running '%s'\n",
552 caller, df->fname);
553 return 0;
554 }
555 } else if (df->fname == stdin_name) {
556 copy_and_close(fileno(stdin));
557 } else if (df->fname[0] != '!') {
558 if (!copy_and_close(open(df->fname, O_RDONLY))) {
559 fprintf(stderr, "%s: error reading from '%s'\n",
560 caller, df->fname);
561 return 0;
562 }
563 } else if (system(df->fname+1)) {
564 fprintf(stderr, "%s: error running '%s'\n", caller, df->fname);
565 return 0;
566 }
567 puts("\t\t\t</ScatteringData>");
568 puts("\t\t</WavelengthDataBlock>");
569 puts("\t</WavelengthData>");
570 return 1;
571 }
572
573 /* Write out XML, interpolating BSDF data block(s) */
574 static int
575 writeBSDF(const char *caller, ezxml_t fl)
576 {
577 char *xml = ezxml_toxml(fl); /* store XML in string */
578 int ei, i;
579 /* locate trailer */
580 for (ei = strlen(xml)-strlen("</Layer></Optical></WindowElement>");
581 ei >= 0; ei--)
582 if (!strncmp(xml+ei, "</Layer>", 8))
583 break;
584 if (ei < 0) {
585 fprintf(stderr, "%s: internal - cannot find trailer\n",
586 caller);
587 free(xml);
588 return 0;
589 }
590 fflush(stdout); /* write previous XML info. */
591 if (write(fileno(stdout), xml, ei) != ei) {
592 free(xml);
593 return 0;
594 }
595 for (i = 0; i < ndataf; i++) /* interpolate new data */
596 if (!writeBSDFblock(caller, &data_file[i])) {
597 free(xml);
598 return 0;
599 }
600 fputs(xml+ei, stdout); /* write trailer */
601 free(xml); /* free string */
602 fputc('\n', stdout);
603 return (fflush(stdout) == 0);
604 }
605
606 /* Insert BSDF data into XML wrapper */
607 static int
608 wrapBSDF(const char *caller)
609 {
610 const char *xml_path = xml_input;
611 ezxml_t fl, wtl;
612 /* load previous XML/template */
613 if (xml_input == stdin_name) {
614 fl = ezxml_parse_fp(stdin);
615 } else if (xml_input[0] == '!') {
616 FILE *pfp = popen(xml_input+1, "r");
617 if (pfp == NULL) {
618 fprintf(stderr, "%s: cannot start process '%s'\n",
619 caller, xml_input);
620 return 0;
621 }
622 fl = ezxml_parse_fp(pfp);
623 if (pclose(pfp)) {
624 fprintf(stderr, "%s: error running '%s'\n",
625 caller, xml_input);
626 return 0;
627 }
628 } else {
629 xml_path = getpath((char *)xml_input, getrlibpath(), R_OK);
630 if (xml_path == NULL) {
631 fprintf(stderr, "%s: cannot find XML file named '%s'\n",
632 caller, xml_input==NULL ? "NULL" : xml_input);
633 return 0;
634 }
635 fl = ezxml_parse_file(xml_path);
636 }
637 if (fl == NULL) {
638 fprintf(stderr, "%s: cannot load XML '%s'\n", caller, xml_path);
639 return 0;
640 }
641 if (ezxml_error(fl)[0]) {
642 fprintf(stderr, "%s: error in XML '%s': %s\n", caller, xml_path,
643 ezxml_error(fl));
644 goto failure;
645 }
646 if (strcmp(ezxml_name(fl), top_level_name)) {
647 fprintf(stderr, "%s: top level in XML '%s' not '%s'\n",
648 caller, xml_path, top_level_name);
649 goto failure;
650 }
651 wtl = ezxml_child(fl, "FileType");
652 if (wtl != NULL && strcmp(ezxml_txt(wtl), "BSDF")) {
653 fprintf(stderr, "%s: wrong FileType in XML '%s' (must be 'BSDF')",
654 caller, xml_path);
655 goto failure;
656 }
657 wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
658 if (wtl == NULL) {
659 fprintf(stderr, "%s: no optical layers in XML '%s'",
660 caller, xml_path);
661 goto failure;
662 }
663 /* make material assignments */
664 if (!mat_assignments(caller, xml_path, wtl))
665 goto failure;
666 if (mgf_geometry != NULL) { /* add geometry if specified */
667 ezxml_t geom = ezxml_child(wtl, "Geometry");
668 if (geom == NULL)
669 geom = ezxml_add_child(wtl, "Geometry", strlen(wtl->txt));
670 ezxml_set_attr(geom, "format", "MGF");
671 geom = ezxml_child(geom, "MGFblock");
672 if (geom == NULL) {
673 geom = ezxml_child(wtl, "Geometry");
674 geom = ezxml_add_child(geom, "MGFblock", 0);
675 }
676 ezxml_set_attr(geom, "unit", attr_unit);
677 ezxml_set_txt(geom, input2str(mgf_geometry));
678 if (geom->txt[0])
679 ezxml_set_flag(geom, EZXML_TXTM);
680 }
681 /* check basis */
682 if (angle_basis != ABdefault) {
683 size_t offset = 0;
684 ezxml_t ab, dd = ezxml_child(wtl, "DataDefinition");
685 if (dd != NULL) {
686 offset = dd->off;
687 if (dd->child != NULL)
688 fprintf(stderr,
689 "%s: warning - replacing existing <DataDefinition> in '%s'\n",
690 caller, xml_path);
691 ezxml_remove(dd);
692 } else
693 offset = strlen(wtl->txt);
694 dd = ezxml_insert(ezxml_parse_str(basis_definition[angle_basis],
695 strlen(basis_definition[angle_basis])),
696 wtl, offset);
697 if ((ab = ezxml_child(dd, "AngleBasis")) != NULL &&
698 !finish_angle_basis(ab))
699 goto failure;
700 } else if ((angle_basis = determine_angle_basis(xml_path, wtl)) < 0) {
701 fprintf(stderr, "%s: need -a option to set angle basis\n",
702 caller);
703 goto failure;
704 }
705 /* write & add BSDF data blocks */
706 if (!writeBSDF(caller, fl))
707 goto failure;
708 ezxml_free(fl); /* all done */
709 return 1;
710 failure:
711 ezxml_free(fl);
712 return 0;
713 }
714
715 /* Report usage and exit */
716 static void
717 UsageExit(const char *pname)
718 {
719 fputs("Usage: ", stderr);
720 fputs(pname, stderr);
721 fputs(" [-W][-c][-a {kf|kh|kq|t3|t4}][-u unit][-g geom][-f 'x=string;y=string']", stderr);
722 fputs(" [-s spectr][-tb inp][-tf inp][-rb inp][-rf inp]", stderr);
723 fputs(" [input.xml]\n", stderr);
724 exit(1);
725 }
726
727 /* Load XML file and use to wrap BSDF data (or modify fields) */
728 int
729 main(int argc, char *argv[])
730 {
731 int cur_spectrum = DSvisible;
732 int ncust_spec = 0;
733 int used_stdin = 0;
734 int units_set = 0;
735 int i;
736 /* get/check arguments */
737 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
738 switch (argv[i][1]) {
739 case 'W': /* customize for WINDOW 6 output */
740 xml_input = win6_template;
741 continue;
742 case 'f': /* field assignment(s) */
743 if (++i >= argc)
744 UsageExit(argv[0]);
745 if (nfield_assign >= MAXASSIGN) {
746 fprintf(stderr, "%s: too many -f options",
747 argv[0]);
748 return 1;
749 }
750 field_assignment[nfield_assign++] = argv[i];
751 continue;
752 case 'u': /* unit */
753 if (++i >= argc)
754 UsageExit(argv[0]);
755 if (units_set++) {
756 fprintf(stderr, "%s: only one -u option allowed\n",
757 argv[0]);
758 return 1;
759 }
760 if (strstr(legal_units, argv[i]) == NULL) {
761 fprintf(stderr, "%s: -u unit must be one of (%s)\n",
762 argv[0], legal_units);
763 return 1;
764 }
765 attr_unit = argv[i];
766 continue;
767 case 'a': /* angle basis */
768 if (++i >= argc)
769 UsageExit(argv[0]);
770 if (angle_basis != ABdefault) {
771 fprintf(stderr, "%s: only one -a option allowed\n",
772 argv[0]);
773 return 1;
774 }
775 if (!strcasecmp(argv[i], "kf"))
776 angle_basis = ABklemsFull;
777 else if (!strcasecmp(argv[i], "kh"))
778 angle_basis = ABklemsHalf;
779 else if (!strcasecmp(argv[i], "kq"))
780 angle_basis = ABklemsQuarter;
781 else if (!strcasecmp(argv[i], "t3"))
782 angle_basis = ABtensorTree3;
783 else if (!strcasecmp(argv[i], "t4"))
784 angle_basis = ABtensorTree4;
785 else
786 UsageExit(argv[0]);
787 continue;
788 case 'c': /* correct solid angle */
789 correct_solid_angle = 1;
790 continue;
791 case 't': /* transmission */
792 if (i >= argc-1)
793 UsageExit(argv[0]);
794 if (ndataf >= MAXFILES) {
795 fprintf(stderr, "%s: too many data files\n",
796 argv[0]);
797 return 1;
798 }
799 if (!strcmp(argv[i], "-tf"))
800 data_file[ndataf].type = DTtransForward;
801 else if (!strcmp(argv[i], "-tb"))
802 data_file[ndataf].type = DTtransBackward;
803 else
804 UsageExit(argv[0]);
805 if (!strcmp(argv[++i], "-")) {
806 if (used_stdin++) UsageExit(argv[i]);
807 argv[i] = (char *)stdin_name;
808 }
809 data_file[ndataf].fname = argv[i];
810 data_file[ndataf].spectrum = cur_spectrum;
811 ndataf++;
812 continue;
813 case 'r': /* reflection */
814 if (i >= argc-1)
815 UsageExit(argv[0]);
816 if (ndataf >= MAXFILES) {
817 fprintf(stderr, "%s: too many data files\n",
818 argv[0]);
819 return 1;
820 }
821 if (!strcmp(argv[i], "-rf"))
822 data_file[ndataf].type = DTreflForward;
823 else if (!strcmp(argv[i], "-rb"))
824 data_file[ndataf].type = DTreflBackward;
825 else
826 UsageExit(argv[0]);
827 if (!strcmp(argv[++i], "-")) {
828 if (used_stdin++) UsageExit(argv[i]);
829 argv[i] = (char *)stdin_name;
830 }
831 data_file[ndataf].fname = argv[i];
832 data_file[ndataf].spectrum = cur_spectrum;
833 ndataf++;
834 continue;
835 case 's': /* spectrum name or input file */
836 if (++i >= argc)
837 UsageExit(argv[0]);
838 if (!strcasecmp(argv[i], "Solar"))
839 cur_spectrum = DSsolar;
840 else if (!strcasecmp(argv[i], "Visible") ||
841 !strcasecmp(argv[i], "CIE-Y"))
842 cur_spectrum = DSvisible;
843 else if (!strcasecmp(argv[i], "CIE-X"))
844 cur_spectrum = DSxbar31;
845 else if (!strcasecmp(argv[i], "CIE-Z"))
846 cur_spectrum = DSzbar31;
847 else if (!strcasecmp(argv[i], "CIE-u"))
848 cur_spectrum = DSuprime;
849 else if (!strcasecmp(argv[i], "CIE-v"))
850 cur_spectrum = DSvprime;
851 else if (!strcasecmp(argv[i], "NIR"))
852 cur_spectrum = DSnir;
853 else {
854 if (!strcmp(argv[i], "-")) {
855 fprintf(stderr,
856 "%s: cannot read spectra from stdin",
857 argv[0]);
858 return 1;
859 }
860 cur_spectrum = ncust_spec;
861 spectr_file[ncust_spec++] = argv[i];
862 }
863 continue;
864 case 'g': /* MGF geometry file */
865 if (i >= argc-1)
866 UsageExit(argv[0]);
867 if (mgf_geometry != NULL) {
868 fprintf(stderr, "%s: only one -g option allowed\n",
869 argv[0]);
870 return 1;
871 }
872 if (!strcmp(argv[++i], "-")) {
873 if (used_stdin++) UsageExit(argv[i]);
874 argv[i] = (char *)stdin_name;
875 }
876 mgf_geometry = argv[i];
877 continue;
878 case '\0': /* input XML from stdin */
879 break;
880 default:
881 UsageExit(argv[0]);
882 break;
883 }
884 break;
885 }
886 doneOptions: /* get XML input */
887 if (i >= argc) {
888 if (xml_input == NULL)
889 xml_input = def_template;
890 } else if ((i < argc-1) | (xml_input != NULL)) {
891 fprintf(stderr, "%s: only one XML input allowed\n", argv[0]);
892 UsageExit(argv[0]);
893 } else if (!strcmp(argv[i], "-")) {
894 if (used_stdin++) UsageExit(argv[0]);
895 xml_input = stdin_name;
896 } else {
897 xml_input = argv[i];
898 }
899 /* wrap it! */
900 return !wrapBSDF(argv[0]);
901 }