ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.11
Committed: Fri Feb 20 18:07:10 2015 UTC (7 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.10: +18 -3 lines
Log Message:
Added -s CIE-u and -s CIE-v support

File Contents

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