ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.18
Committed: Tue Feb 2 22:34:00 2016 UTC (8 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.17: +14 -2 lines
Log Message:
Refactored bsdf2ttree for Yuv representation

File Contents

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