ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.24
Committed: Thu Apr 4 00:21:14 2019 UTC (4 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.23: +18 -26 lines
Log Message:
Fixed check for duplicate component

File Contents

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