ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.14
Committed: Thu Apr 2 16:40:32 2015 UTC (9 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.13: +8 -10 lines
Log Message:
Support for color BSDF rendering using Klems representation

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: wrapBSDF.c,v 2.13 2015/03/25 22:50:48 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 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 }