ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.3
Committed: Fri Feb 13 20:49:59 2015 UTC (9 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.2: +58 -48 lines
Log Message:
Added XML templates and fixed bugs for first running version

File Contents

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