ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.6
Committed: Sun Feb 15 17:23:06 2015 UTC (7 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.5: +71 -3 lines
Log Message:
Added solid angle correction for Klems matrix input

File Contents

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