ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.27
Committed: Wed Sep 7 18:55:39 2022 UTC (2 months, 2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.26: +8 -6 lines
Log Message:
fix(wrapBSDF): Allow for commas in Klems matrix data

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: wrapBSDF.c,v 2.26 2020/10/26 21:12:20 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 80
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 data, 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, c, 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 ((c = getc(fp)) != ',')
440 ungetc(c, fp);
441 if (d < -1e-3) {
442 fputs("Negative BSDF data!\n", stderr);
443 return 0;
444 }
445 printf(" %.3e", d*corr*(d > 0));
446 }
447 fputc('\n', stdout);
448 }
449 while ((c = getc(fp)) != EOF)
450 if (!isspace(c)) {
451 fputs("Unexpected data past EOF\n", stderr);
452 return 0;
453 }
454 return 1; /* all is good */
455 }
456
457 /* Write out BSDF data block with surrounding tags */
458 static int
459 writeBSDFblock(const char *caller, struct s_dfile *df)
460 {
461 int correct_klems = correct_solid_angle;
462 char *cp;
463
464 puts("\t<WavelengthData>");
465 puts("\t\t<LayerNumber>System</LayerNumber>");
466 switch (df->spectrum) {
467 case DSvisible:
468 puts("\t\t<Wavelength unit=\"Integral\">Visible</Wavelength>");
469 puts("\t\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
470 puts("\t\t<DetectorSpectrum>ASTM E308 1931 Y.dsp</DetectorSpectrum>");
471 break;
472 case DSxbar31:
473 puts("\t\t<Wavelength unit=\"Integral\">CIE-X</Wavelength>");
474 puts("\t\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
475 puts("\t\t<DetectorSpectrum>ASTM E308 1931 X.dsp</DetectorSpectrum>");
476 break;
477 case DSzbar31:
478 puts("\t\t<Wavelength unit=\"Integral\">CIE-Z</Wavelength>");
479 puts("\t\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
480 puts("\t\t<DetectorSpectrum>ASTM E308 1931 Z.dsp</DetectorSpectrum>");
481 break;
482 case DSuprime:
483 puts("\t\t<Wavelength unit=\"Integral\">CIE-u</Wavelength>");
484 puts("\t\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
485 break;
486 case DSvprime:
487 puts("\t\t<Wavelength unit=\"Integral\">CIE-v</Wavelength>");
488 puts("\t\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
489 break;
490 case DSsolar:
491 puts("\t\t<Wavelength unit=\"Integral\">Solar</Wavelength>");
492 puts("\t\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
493 puts("\t\t<DetectorSpectrum>None</DetectorSpectrum>");
494 break;
495 case DSnir:
496 puts("\t\t<Wavelength unit=\"Integral\">NIR</Wavelength>");
497 puts("\t\t<SourceSpectrum>PLACE_HOLDER</SourceSpectrum>");
498 puts("\t\t<DetectorSpectrum>PLACE_HOLDER</DetectorSpectrum>");
499 break;
500 default:
501 cp = strrchr(spectr_file[df->spectrum], '.');
502 if (cp != NULL)
503 *cp = '\0';
504 printf("\t\t<Wavelength unit=\"Integral\">%s</Wavelength>\n",
505 spectr_file[df->spectrum]);
506 if (cp != NULL)
507 *cp = '.';
508 puts("\t\t<SourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
509 printf("\t\t<DetectorSpectrum>%s</DetectorSpectrum>\n",
510 spectr_file[df->spectrum]);
511 break;
512 }
513 puts("\t\t<WavelengthDataBlock>");
514 fputs("\t\t\t<WavelengthDataDirection>", stdout);
515 fputs(component_name[df->type], stdout);
516 puts("</WavelengthDataDirection>");
517 switch (angle_basis) {
518 case ABklemsFull:
519 case ABklemsHalf:
520 case ABklemsQuarter:
521 fputs("\t\t\t<ColumnAngleBasis>", stdout);
522 fputs(klems_basis_name[angle_basis], stdout);
523 puts("</ColumnAngleBasis>");
524 fputs("\t\t\t<RowAngleBasis>", stdout);
525 fputs(klems_basis_name[angle_basis], stdout);
526 puts("</RowAngleBasis>");
527 break;
528 case ABtensorTree3:
529 case ABtensorTree4:
530 puts("\t\t\t<AngleBasis>LBNL/Shirley-Chiu</AngleBasis>");
531 correct_klems = 0;
532 break;
533 default:
534 fprintf(stderr, "%s: bad angle basis (%d)\n", caller, angle_basis);
535 return 0;
536 }
537 puts("\t\t\t<ScatteringDataType>BTDF</ScatteringDataType>");
538 puts("\t\t\t<ScatteringData>");
539 fflush(stdout);
540 if (correct_klems) { /* correct Klems matrix data */
541 FILE *fp = stdin;
542 if (df->fname[0] == '!')
543 fp = popen(df->fname+1, "r");
544 else if (df->fname != stdin_name)
545 fp = fopen(df->fname, "r");
546 if (fp == NULL) {
547 fprintf(stderr, "%s: cannot open '%s'\n",
548 caller, df->fname);
549 return 0;
550 }
551 if (!filter_klems_matrix(fp)) {
552 fprintf(stderr, "%s: Klems data error from '%s'\n",
553 caller, df->fname);
554 return 0;
555 }
556 if (df->fname[0] != '!') {
557 fclose(fp);
558 } else if (pclose(fp)) {
559 fprintf(stderr, "%s: error running '%s'\n",
560 caller, df->fname);
561 return 0;
562 }
563 } else if (df->fname == stdin_name) {
564 copy_and_close(fileno(stdin));
565 } else if (df->fname[0] != '!') {
566 if (!copy_and_close(open(df->fname, O_RDONLY))) {
567 fprintf(stderr, "%s: error reading from '%s'\n",
568 caller, df->fname);
569 return 0;
570 }
571 } else if (system(df->fname+1)) {
572 fprintf(stderr, "%s: error running '%s'\n", caller, df->fname);
573 return 0;
574 }
575 puts("\t\t\t</ScatteringData>");
576 puts("\t\t</WavelengthDataBlock>");
577 puts("\t</WavelengthData>");
578 return 1;
579 }
580
581 /* Write out XML, interpolating BSDF data block(s) */
582 static int
583 writeBSDF(const char *caller, ezxml_t fl)
584 {
585 char *xml = ezxml_toxml(fl); /* store XML in string */
586 int ei, i;
587 /* locate trailer */
588 for (ei = strlen(xml)-strlen("</Layer></Optical></WindowElement>");
589 ei >= 0; ei--)
590 if (!strncmp(xml+ei, "</Layer>", 8))
591 break;
592 if (ei < 0) {
593 fprintf(stderr, "%s: internal - cannot find trailer\n",
594 caller);
595 free(xml);
596 return 0;
597 }
598 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
599 for (i = 0; i < ncomm; i++)
600 printf("<!-- %s -->\n", commlist[i]);
601 fflush(stdout); /* write previous XML info. */
602 if (write(fileno(stdout), xml, ei) != ei) {
603 free(xml);
604 return 0;
605 }
606 for (i = 0; i < ndataf; i++) /* interpolate new data */
607 if (!writeBSDFblock(caller, &data_file[i])) {
608 free(xml);
609 return 0;
610 }
611 fputs(xml+ei, stdout); /* write trailer */
612 free(xml); /* free string */
613 fputc('\n', stdout);
614 if (fflush(stdout) != 0)
615 return 0;
616 /* unlink data files if req. */
617 for (i = ndataf*(unlink_datafiles != 0); i--; )
618 if (data_file[i].fname != stdin_name &&
619 data_file[i].fname[0] != '!')
620 unlink(data_file[i].fname);
621 if (unlink_datafiles > 1 && mgf_geometry != NULL &&
622 mgf_geometry != stdin_name &&
623 mgf_geometry[0] != '!')
624 unlink(mgf_geometry);
625 return 1;
626 }
627
628 /* Insert BSDF data into XML wrapper */
629 static int
630 wrapBSDF(const char *caller)
631 {
632 const char *xml_path = xml_input;
633 ezxml_t fl, wtl;
634 /* load previous XML/template */
635 if (xml_input == stdin_name) {
636 fl = ezxml_parse_fp(stdin);
637 } else if (xml_input[0] == '!') {
638 FILE *pfp = popen(xml_input+1, "r");
639 if (pfp == NULL) {
640 fprintf(stderr, "%s: cannot start process '%s'\n",
641 caller, xml_input);
642 return 0;
643 }
644 fl = ezxml_parse_fp(pfp);
645 if (pclose(pfp)) {
646 fprintf(stderr, "%s: error running '%s'\n",
647 caller, xml_input);
648 return 0;
649 }
650 } else {
651 xml_path = getpath((char *)xml_input, getrlibpath(), R_OK);
652 if (xml_path == NULL) {
653 fprintf(stderr, "%s: cannot find XML file named '%s'\n",
654 caller, xml_input==NULL ? "NULL" : xml_input);
655 return 0;
656 }
657 fl = ezxml_parse_file(xml_path);
658 }
659 if (fl == NULL) {
660 fprintf(stderr, "%s: cannot load XML '%s'\n", caller, xml_path);
661 return 0;
662 }
663 if (ezxml_error(fl)[0]) {
664 fprintf(stderr, "%s: error in XML '%s': %s\n", caller, xml_path,
665 ezxml_error(fl));
666 goto failure;
667 }
668 if (strcmp(ezxml_name(fl), top_level_name)) {
669 fprintf(stderr, "%s: top level in XML '%s' not '%s'\n",
670 caller, xml_path, top_level_name);
671 goto failure;
672 }
673 wtl = ezxml_child(fl, "FileType");
674 if (wtl != NULL && strcmp(ezxml_txt(wtl), "BSDF")) {
675 fprintf(stderr, "%s: wrong FileType in XML '%s' (must be 'BSDF')",
676 caller, xml_path);
677 goto failure;
678 }
679 wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
680 if (wtl == NULL) {
681 fprintf(stderr, "%s: no optical layers in XML '%s'",
682 caller, xml_path);
683 goto failure;
684 }
685 /* make material assignments */
686 if (!mat_assignments(caller, xml_path, wtl))
687 goto failure;
688 if (mgf_geometry != NULL) { /* add geometry if specified */
689 ezxml_t geom = ezxml_child(wtl, "Geometry");
690 if (geom == NULL)
691 geom = ezxml_add_child(wtl, "Geometry", strlen(wtl->txt));
692 ezxml_set_attr(geom, "format", "MGF");
693 geom = ezxml_child(geom, "MGFblock");
694 if (geom == NULL) {
695 geom = ezxml_child(wtl, "Geometry");
696 geom = ezxml_add_child(geom, "MGFblock", 0);
697 }
698 ezxml_set_attr(geom, "unit", attr_unit);
699 ezxml_set_txt(geom, input2str(mgf_geometry));
700 if (geom->txt[0])
701 ezxml_set_flag(geom, EZXML_TXTM);
702 }
703 /* check basis */
704 if (angle_basis != ABdefault) {
705 size_t offset = 0;
706 ezxml_t ab, dd = ezxml_child(wtl, "DataDefinition");
707 if (dd != NULL) {
708 offset = dd->off;
709 if (dd->child != NULL)
710 fprintf(stderr,
711 "%s: warning - replacing existing <DataDefinition> in '%s'\n",
712 caller, xml_path);
713 ezxml_remove(dd);
714 } else
715 offset = strlen(wtl->txt);
716 dd = ezxml_insert(ezxml_parse_str(basis_definition[angle_basis],
717 strlen(basis_definition[angle_basis])),
718 wtl, offset);
719 if ((ab = ezxml_child(dd, "AngleBasis")) != NULL &&
720 !finish_angle_basis(ab))
721 goto failure;
722 } else if ((angle_basis = determine_angle_basis(xml_path, wtl)) < 0) {
723 fprintf(stderr, "%s: need -a option to set angle basis\n",
724 caller);
725 goto failure;
726 }
727 /* write & add BSDF data blocks */
728 if (!writeBSDF(caller, fl))
729 goto failure;
730 ezxml_free(fl); /* all done */
731 return 1;
732 failure:
733 ezxml_free(fl);
734 return 0;
735 }
736
737 /* Report usage and exit */
738 static void
739 UsageExit(const char *pname)
740 {
741 fputs("Usage: ", stderr);
742 fputs(pname, stderr);
743 fputs(" [-W][-c][-a {kf|kh|kq|t3|t4}][-u unit][-g geom][-f 'x=string;y=string']", stderr);
744 fputs(" [-s spectr][-tb inp][-tf inp][-rb inp][-rf inp][-C comm]", stderr);
745 fputs(" [input.xml]\n", stderr);
746 exit(1);
747 }
748
749 /* Load XML file and use to wrap BSDF data (or modify fields) */
750 int
751 main(int argc, char *argv[])
752 {
753 int cur_spectrum = DSvisible;
754 int ncust_spec = 0;
755 int used_stdin = 0;
756 int units_set = 0;
757 int i;
758 /* get/check arguments */
759 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
760 switch (argv[i][1]) {
761 case 'W': /* customize for WINDOW 6 output */
762 xml_input = win6_template;
763 continue;
764 case 'f': /* field assignment(s) */
765 if (++i >= argc)
766 UsageExit(argv[0]);
767 if (nfield_assign >= MAXASSIGN) {
768 fprintf(stderr, "%s: too many -f options",
769 argv[0]);
770 return 1;
771 }
772 field_assignment[nfield_assign++] = argv[i];
773 continue;
774 case 'u': /* unit */
775 if (++i >= argc)
776 UsageExit(argv[0]);
777 if (units_set++) {
778 fprintf(stderr, "%s: only one -u option allowed\n",
779 argv[0]);
780 return 1;
781 }
782 if (strstr(legal_units, argv[i]) == NULL) {
783 fprintf(stderr, "%s: -u unit must be one of (%s)\n",
784 argv[0], legal_units);
785 return 1;
786 }
787 attr_unit = argv[i];
788 continue;
789 case 'U': /* unlink data files when done */
790 unlink_datafiles = 1 + (argv[i][2] == 'U');
791 continue;
792 case 'a': /* angle basis */
793 if (++i >= argc)
794 UsageExit(argv[0]);
795 if (angle_basis != ABdefault) {
796 fprintf(stderr, "%s: only one -a option allowed\n",
797 argv[0]);
798 return 1;
799 }
800 if (!strcasecmp(argv[i], "kf"))
801 angle_basis = ABklemsFull;
802 else if (!strcasecmp(argv[i], "kh"))
803 angle_basis = ABklemsHalf;
804 else if (!strcasecmp(argv[i], "kq"))
805 angle_basis = ABklemsQuarter;
806 else if (!strcasecmp(argv[i], "t3"))
807 angle_basis = ABtensorTree3;
808 else if (!strcasecmp(argv[i], "t4"))
809 angle_basis = ABtensorTree4;
810 else
811 UsageExit(argv[0]);
812 continue;
813 case 'c': /* correct solid angle */
814 correct_solid_angle = 1;
815 continue;
816 case 'C': /* comment */
817 if (ncomm >= MAXCOMM) {
818 fprintf(stderr, "%s: too many comments\n",
819 argv[0]);
820 return 1;
821 }
822 if (strstr(argv[++i], "-->") != NULL) {
823 fprintf(stderr, "%s: illegal character in comment\n",
824 argv[0]);
825 return 1;
826 }
827 commlist[ncomm++] = argv[i];
828 continue;
829 case 't': /* transmission */
830 if (i >= argc-1)
831 UsageExit(argv[0]);
832 if (ndataf >= MAXFILES) {
833 fprintf(stderr, "%s: too many data files\n",
834 argv[0]);
835 return 1;
836 }
837 if (!strcmp(argv[i], "-tf"))
838 data_file[ndataf].type = DTransFront;
839 else if (!strcmp(argv[i], "-tb"))
840 data_file[ndataf].type = DTransBack;
841 else
842 UsageExit(argv[0]);
843 if (!strcmp(argv[++i], "-")) {
844 if (used_stdin++) UsageExit(argv[i]);
845 argv[i] = (char *)stdin_name;
846 }
847 data_file[ndataf].fname = argv[i];
848 data_file[ndataf].spectrum = cur_spectrum;
849 ndataf += check_new_data_file();
850 continue;
851 case 'r': /* reflection */
852 if (i >= argc-1)
853 UsageExit(argv[0]);
854 if (ndataf >= MAXFILES) {
855 fprintf(stderr, "%s: too many data files\n",
856 argv[0]);
857 return 1;
858 }
859 if (!strcmp(argv[i], "-rf"))
860 data_file[ndataf].type = DTreflFront;
861 else if (!strcmp(argv[i], "-rb"))
862 data_file[ndataf].type = DTreflBack;
863 else
864 UsageExit(argv[0]);
865 if (!strcmp(argv[++i], "-")) {
866 if (used_stdin++) UsageExit(argv[i]);
867 argv[i] = (char *)stdin_name;
868 }
869 data_file[ndataf].fname = argv[i];
870 data_file[ndataf].spectrum = cur_spectrum;
871 ndataf += check_new_data_file();
872 continue;
873 case 's': /* spectrum name or input file */
874 if (++i >= argc)
875 UsageExit(argv[0]);
876 if (!strcasecmp(argv[i], "Solar"))
877 cur_spectrum = DSsolar;
878 else if (!strcasecmp(argv[i], "Visible") ||
879 !strcasecmp(argv[i], "CIE-Y"))
880 cur_spectrum = DSvisible;
881 else if (!strcasecmp(argv[i], "CIE-X"))
882 cur_spectrum = DSxbar31;
883 else if (!strcasecmp(argv[i], "CIE-Z"))
884 cur_spectrum = DSzbar31;
885 else if (!strcasecmp(argv[i], "CIE-u"))
886 cur_spectrum = DSuprime;
887 else if (!strcasecmp(argv[i], "CIE-v"))
888 cur_spectrum = DSvprime;
889 else if (!strcasecmp(argv[i], "NIR"))
890 cur_spectrum = DSnir;
891 else {
892 if (!strcmp(argv[i], "-")) {
893 fprintf(stderr,
894 "%s: cannot read spectra from stdin",
895 argv[0]);
896 return 1;
897 }
898 cur_spectrum = ncust_spec;
899 spectr_file[ncust_spec++] = argv[i];
900 }
901 continue;
902 case 'g': /* MGF geometry file */
903 if (i >= argc-1)
904 UsageExit(argv[0]);
905 if (mgf_geometry != NULL) {
906 fprintf(stderr, "%s: only one -g option allowed\n",
907 argv[0]);
908 return 1;
909 }
910 if (!strcmp(argv[++i], "-")) {
911 if (used_stdin++) UsageExit(argv[i]);
912 argv[i] = (char *)stdin_name;
913 }
914 mgf_geometry = argv[i];
915 continue;
916 case '\0': /* input XML from stdin */
917 break;
918 default:
919 UsageExit(argv[0]);
920 break;
921 }
922 break;
923 }
924 /* get XML input */
925 if (i >= argc) {
926 if (xml_input == NULL)
927 xml_input = def_template;
928 } else if ((i < argc-1) | (xml_input != NULL)) {
929 fprintf(stderr, "%s: only one XML input allowed\n", argv[0]);
930 UsageExit(argv[0]);
931 } else if (!strcmp(argv[i], "-")) {
932 if (used_stdin++) UsageExit(argv[0]);
933 xml_input = stdin_name;
934 } else {
935 xml_input = argv[i];
936 }
937 /* wrap it! */
938 return !wrapBSDF(argv[0]);
939 }