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

File Contents

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