ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.15
Committed: Fri May 29 07:16:49 2015 UTC (8 years, 10 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.14: +2 -1 lines
Log Message:
Added required character type information.

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: wrapBSDF.c,v 2.14 2015/04/02 16:40:32 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\t<SourceSpectrum>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\t<SourceSpectrum>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\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
459 break;
460 case DSvprime:
461 puts("\t\t<Wavelength unit=\"Integral\">CIE-v</Wavelength>");
462 puts("\t\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
463 break;
464 case DSsolar:
465 puts("\t\t<Wavelength unit=\"Integral\">Solar</Wavelength>");
466 puts("\t\t<SourceSpectrum>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\t<SourceSpectrum>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\t<SourceSpectrum>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 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
589 fflush(stdout); /* write previous XML info. */
590 if (write(fileno(stdout), xml, ei) != ei) {
591 free(xml);
592 return 0;
593 }
594 for (i = 0; i < ndataf; i++) /* interpolate new data */
595 if (!writeBSDFblock(caller, &data_file[i])) {
596 free(xml);
597 return 0;
598 }
599 fputs(xml+ei, stdout); /* write trailer */
600 free(xml); /* free string */
601 fputc('\n', stdout);
602 return (fflush(stdout) == 0);
603 }
604
605 /* Insert BSDF data into XML wrapper */
606 static int
607 wrapBSDF(const char *caller)
608 {
609 const char *xml_path = xml_input;
610 ezxml_t fl, wtl;
611 /* load previous XML/template */
612 if (xml_input == stdin_name) {
613 fl = ezxml_parse_fp(stdin);
614 } else if (xml_input[0] == '!') {
615 FILE *pfp = popen(xml_input+1, "r");
616 if (pfp == NULL) {
617 fprintf(stderr, "%s: cannot start process '%s'\n",
618 caller, xml_input);
619 return 0;
620 }
621 fl = ezxml_parse_fp(pfp);
622 if (pclose(pfp)) {
623 fprintf(stderr, "%s: error running '%s'\n",
624 caller, xml_input);
625 return 0;
626 }
627 } else {
628 xml_path = getpath((char *)xml_input, getrlibpath(), R_OK);
629 if (xml_path == NULL) {
630 fprintf(stderr, "%s: cannot find XML file named '%s'\n",
631 caller, xml_input==NULL ? "NULL" : xml_input);
632 return 0;
633 }
634 fl = ezxml_parse_file(xml_path);
635 }
636 if (fl == NULL) {
637 fprintf(stderr, "%s: cannot load XML '%s'\n", caller, xml_path);
638 return 0;
639 }
640 if (ezxml_error(fl)[0]) {
641 fprintf(stderr, "%s: error in XML '%s': %s\n", caller, xml_path,
642 ezxml_error(fl));
643 goto failure;
644 }
645 if (strcmp(ezxml_name(fl), top_level_name)) {
646 fprintf(stderr, "%s: top level in XML '%s' not '%s'\n",
647 caller, xml_path, top_level_name);
648 goto failure;
649 }
650 wtl = ezxml_child(fl, "FileType");
651 if (wtl != NULL && strcmp(ezxml_txt(wtl), "BSDF")) {
652 fprintf(stderr, "%s: wrong FileType in XML '%s' (must be 'BSDF')",
653 caller, xml_path);
654 goto failure;
655 }
656 wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
657 if (wtl == NULL) {
658 fprintf(stderr, "%s: no optical layers in XML '%s'",
659 caller, xml_path);
660 goto failure;
661 }
662 /* make material assignments */
663 if (!mat_assignments(caller, xml_path, wtl))
664 goto failure;
665 if (mgf_geometry != NULL) { /* add geometry if specified */
666 ezxml_t geom = ezxml_child(wtl, "Geometry");
667 if (geom == NULL)
668 geom = ezxml_add_child(wtl, "Geometry", strlen(wtl->txt));
669 ezxml_set_attr(geom, "format", "MGF");
670 geom = ezxml_child(geom, "MGFblock");
671 if (geom == NULL) {
672 geom = ezxml_child(wtl, "Geometry");
673 geom = ezxml_add_child(geom, "MGFblock", 0);
674 }
675 ezxml_set_attr(geom, "unit", attr_unit);
676 ezxml_set_txt(geom, input2str(mgf_geometry));
677 if (geom->txt[0])
678 ezxml_set_flag(geom, EZXML_TXTM);
679 }
680 /* check basis */
681 if (angle_basis != ABdefault) {
682 size_t offset = 0;
683 ezxml_t ab, dd = ezxml_child(wtl, "DataDefinition");
684 if (dd != NULL) {
685 offset = dd->off;
686 if (dd->child != NULL)
687 fprintf(stderr,
688 "%s: warning - replacing existing <DataDefinition> in '%s'\n",
689 caller, xml_path);
690 ezxml_remove(dd);
691 } else
692 offset = strlen(wtl->txt);
693 dd = ezxml_insert(ezxml_parse_str(basis_definition[angle_basis],
694 strlen(basis_definition[angle_basis])),
695 wtl, offset);
696 if ((ab = ezxml_child(dd, "AngleBasis")) != NULL &&
697 !finish_angle_basis(ab))
698 goto failure;
699 } else if ((angle_basis = determine_angle_basis(xml_path, wtl)) < 0) {
700 fprintf(stderr, "%s: need -a option to set angle basis\n",
701 caller);
702 goto failure;
703 }
704 /* write & add BSDF data blocks */
705 if (!writeBSDF(caller, fl))
706 goto failure;
707 ezxml_free(fl); /* all done */
708 return 1;
709 failure:
710 ezxml_free(fl);
711 return 0;
712 }
713
714 /* Report usage and exit */
715 static void
716 UsageExit(const char *pname)
717 {
718 fputs("Usage: ", stderr);
719 fputs(pname, stderr);
720 fputs(" [-W][-c][-a {kf|kh|kq|t3|t4}][-u unit][-g geom][-f 'x=string;y=string']", stderr);
721 fputs(" [-s spectr][-tb inp][-tf inp][-rb inp][-rf inp]", stderr);
722 fputs(" [input.xml]\n", stderr);
723 exit(1);
724 }
725
726 /* Load XML file and use to wrap BSDF data (or modify fields) */
727 int
728 main(int argc, char *argv[])
729 {
730 int cur_spectrum = DSvisible;
731 int ncust_spec = 0;
732 int used_stdin = 0;
733 int units_set = 0;
734 int i;
735 /* get/check arguments */
736 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
737 switch (argv[i][1]) {
738 case 'W': /* customize for WINDOW 6 output */
739 xml_input = win6_template;
740 continue;
741 case 'f': /* field assignment(s) */
742 if (++i >= argc)
743 UsageExit(argv[0]);
744 if (nfield_assign >= MAXASSIGN) {
745 fprintf(stderr, "%s: too many -f options",
746 argv[0]);
747 return 1;
748 }
749 field_assignment[nfield_assign++] = argv[i];
750 continue;
751 case 'u': /* unit */
752 if (++i >= argc)
753 UsageExit(argv[0]);
754 if (units_set++) {
755 fprintf(stderr, "%s: only one -u option allowed\n",
756 argv[0]);
757 return 1;
758 }
759 if (strstr(legal_units, argv[i]) == NULL) {
760 fprintf(stderr, "%s: -u unit must be one of (%s)\n",
761 argv[0], legal_units);
762 return 1;
763 }
764 attr_unit = argv[i];
765 continue;
766 case 'a': /* angle basis */
767 if (++i >= argc)
768 UsageExit(argv[0]);
769 if (angle_basis != ABdefault) {
770 fprintf(stderr, "%s: only one -a option allowed\n",
771 argv[0]);
772 return 1;
773 }
774 if (!strcasecmp(argv[i], "kf"))
775 angle_basis = ABklemsFull;
776 else if (!strcasecmp(argv[i], "kh"))
777 angle_basis = ABklemsHalf;
778 else if (!strcasecmp(argv[i], "kq"))
779 angle_basis = ABklemsQuarter;
780 else if (!strcasecmp(argv[i], "t3"))
781 angle_basis = ABtensorTree3;
782 else if (!strcasecmp(argv[i], "t4"))
783 angle_basis = ABtensorTree4;
784 else
785 UsageExit(argv[0]);
786 continue;
787 case 'c': /* correct solid angle */
788 correct_solid_angle = 1;
789 continue;
790 case 't': /* transmission */
791 if (i >= argc-1)
792 UsageExit(argv[0]);
793 if (ndataf >= MAXFILES) {
794 fprintf(stderr, "%s: too many data files\n",
795 argv[0]);
796 return 1;
797 }
798 if (!strcmp(argv[i], "-tf"))
799 data_file[ndataf].type = DTtransForward;
800 else if (!strcmp(argv[i], "-tb"))
801 data_file[ndataf].type = DTtransBackward;
802 else
803 UsageExit(argv[0]);
804 if (!strcmp(argv[++i], "-")) {
805 if (used_stdin++) UsageExit(argv[i]);
806 argv[i] = (char *)stdin_name;
807 }
808 data_file[ndataf].fname = argv[i];
809 data_file[ndataf].spectrum = cur_spectrum;
810 ndataf++;
811 continue;
812 case 'r': /* reflection */
813 if (i >= argc-1)
814 UsageExit(argv[0]);
815 if (ndataf >= MAXFILES) {
816 fprintf(stderr, "%s: too many data files\n",
817 argv[0]);
818 return 1;
819 }
820 if (!strcmp(argv[i], "-rf"))
821 data_file[ndataf].type = DTreflForward;
822 else if (!strcmp(argv[i], "-rb"))
823 data_file[ndataf].type = DTreflBackward;
824 else
825 UsageExit(argv[0]);
826 if (!strcmp(argv[++i], "-")) {
827 if (used_stdin++) UsageExit(argv[i]);
828 argv[i] = (char *)stdin_name;
829 }
830 data_file[ndataf].fname = argv[i];
831 data_file[ndataf].spectrum = cur_spectrum;
832 ndataf++;
833 continue;
834 case 's': /* spectrum name or input file */
835 if (++i >= argc)
836 UsageExit(argv[0]);
837 if (!strcasecmp(argv[i], "Solar"))
838 cur_spectrum = DSsolar;
839 else if (!strcasecmp(argv[i], "Visible") ||
840 !strcasecmp(argv[i], "CIE-Y"))
841 cur_spectrum = DSvisible;
842 else if (!strcasecmp(argv[i], "CIE-X"))
843 cur_spectrum = DSxbar31;
844 else if (!strcasecmp(argv[i], "CIE-Z"))
845 cur_spectrum = DSzbar31;
846 else if (!strcasecmp(argv[i], "CIE-u"))
847 cur_spectrum = DSuprime;
848 else if (!strcasecmp(argv[i], "CIE-v"))
849 cur_spectrum = DSvprime;
850 else if (!strcasecmp(argv[i], "NIR"))
851 cur_spectrum = DSnir;
852 else {
853 if (!strcmp(argv[i], "-")) {
854 fprintf(stderr,
855 "%s: cannot read spectra from stdin",
856 argv[0]);
857 return 1;
858 }
859 cur_spectrum = ncust_spec;
860 spectr_file[ncust_spec++] = argv[i];
861 }
862 continue;
863 case 'g': /* MGF geometry file */
864 if (i >= argc-1)
865 UsageExit(argv[0]);
866 if (mgf_geometry != NULL) {
867 fprintf(stderr, "%s: only one -g option allowed\n",
868 argv[0]);
869 return 1;
870 }
871 if (!strcmp(argv[++i], "-")) {
872 if (used_stdin++) UsageExit(argv[i]);
873 argv[i] = (char *)stdin_name;
874 }
875 mgf_geometry = argv[i];
876 continue;
877 case '\0': /* input XML from stdin */
878 break;
879 default:
880 UsageExit(argv[0]);
881 break;
882 }
883 break;
884 }
885 doneOptions: /* get XML input */
886 if (i >= argc) {
887 if (xml_input == NULL)
888 xml_input = def_template;
889 } else if ((i < argc-1) | (xml_input != NULL)) {
890 fprintf(stderr, "%s: only one XML input allowed\n", argv[0]);
891 UsageExit(argv[0]);
892 } else if (!strcmp(argv[i], "-")) {
893 if (used_stdin++) UsageExit(argv[0]);
894 xml_input = stdin_name;
895 } else {
896 xml_input = argv[i];
897 }
898 /* wrap it! */
899 return !wrapBSDF(argv[0]);
900 }