ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.19
Committed: Wed Feb 3 00:22:55 2016 UTC (8 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.18: +6 -2 lines
Log Message:
Added -UU option to wrapBSDF to remove geometry file as well

File Contents

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