ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/ies2rad.c
(Generate patch)

Comparing ray/src/cv/ies2rad.c (file contents):
Revision 2.2 by greg, Thu Dec 19 15:10:45 1991 UTC vs.
Revision 2.35 by greg, Mon Nov 29 16:07:36 2021 UTC

# Line 1 | Line 1
1 /* Copyright (c) 1990 Regents of the University of California */
2
1   #ifndef lint
2 < static char SCCSid[] = "$SunId$ LBL";
2 > static const char       RCSid[] = "$Id$";
3   #endif
6
4   /*
5 < * Convert IES luminaire data to Radiance description
5 > * ies2rad -- Convert IES luminaire data to Radiance description
6   *
7 + * ies2rad converts an IES LM-63 luminare description to a Radiance
8 + * luminaire description.  In addition, ies2rad manages a local
9 + * database of Radiance luminaire files.
10 + *
11 + * Ies2rad generates two or three files for each luminaire. For a
12 + * luminaire named LUM, ies2rad will generate LUM.rad, a Radiance
13 + * scene description file which describes the light source, LUM.dat,
14 + * which contains the photometric data from the IES LM-63 file, and
15 + * (if tilt data is provided) LUM%.dat, which contains the tilt data
16 + * from the IES file.
17 + *
18 + * Ies2rad is supported by the Radiance function files source.cal and
19 + * tilt.cal, which transform the coordinates in the IES data into
20 + * Radiance (θ,φ) luminaire coordinates and then apply photometric and
21 + * tilt data to generate Radiance light. θ is altitude from the
22 + * negative z-axis and φ is azimuth from the positive x-axis,
23 + * increasing towards the positive y-axis. This system matches none of
24 + * the usual goniophotometric conventions, but it is closest to IES
25 + * type C; V in type C photometry is θ in Radiance and L is -φ.
26 + *
27 + * The ies2rad scene description for a luminaire LUM, with tilt data,
28 + * uses the following Radiance scene description primitives:
29 + *
30 + *     void brightdata LUM_tilt
31 + *     …
32 + *     LUM_tilt brightdata LUM_dist
33 + *     …
34 + *     LUM_dist light LUM_light
35 + *     …
36 + *     LUM_light surface1 name1
37 + *     …
38 + *     LUM_light surface2 name2
39 + *     …
40 + *     LUM_light surface_n name_n
41 + *
42 + * Without tilt data, the primitives are:
43 + *
44 + *     void brightdata LUM_dist
45 + *     …
46 + *     LUM_dist light LUM_light
47 + *     …
48 + *     LUM_light surface1 name1
49 + *     …
50 + *     LUM_light surface2 name2
51 + *     …
52 + *     LUM_light surface_n name_n
53 + *
54 + * As many surfaces are given as required to describe the light
55 + * source. Illum may be used rather than light so that a visible form
56 + * (impostor) may be given to the luminaire, rather than a simple
57 + * glowing shape. If an impostor is provided, it must be wholly
58 + * contained within the illum and if it provides impostor light
59 + * sources, those must be given with glow, so that they do not
60 + * themselves illuminate the scene, providing incorrect results.
61 + *
62 + * Overview of the LM-63 file format
63 + * =================================
64 + * Here we offer a summary of the IESNA LM-63 photometry file format
65 + * for the perplexed reader.  Dear reader, do remember that this is
66 + * our interpretation of the five different versions of the standard.
67 + * When our interpretation of the standard conflicts with the official
68 + * standard, the official document is to be respected.  In conflicts
69 + * with practice, do take into account the robustness principle and be
70 + * permissive, accepting reasonable deviations from the standard.
71 + *
72 + * LM-63 files are organized as a version tag, followed by a series of
73 + * luminaire data sets.  The luminaire data sets, in turn, are
74 + * organized into a label, a tilt data section, and a photometric data
75 + * section.  Finally, the data sections are organized into records,
76 + * which are made up of lines of numeric values delimited by spaces or
77 + * commas.  Lines are delimited by CR LF sequences.  Records are made
78 + * up of one or more lines, and every record must be made up of some
79 + * number of complete lines, but there is no delimiter which makes the
80 + * end of a record.  The first records of the tilt and photometric
81 + * data sections have fixed numbers of numeric values; the initial
82 + * records contain counts that describe the remaining records.
83 + *
84 + * Ies2rad allows only one luminaire data set per file.
85 + *
86 + * The tilt section is made up of exactly four records; the second gives
87 + * the number of values in the third and fourth records.
88 + *
89 + * The photometric section begins with two records, which give both the
90 + * number of records following and the number of values in each of the
91 + * following records.
92 + *
93 + * The original 1986 version of LM-63 does not have a version tag.
94 + *
95 + * The 1986, 1991, and 1995 versions allow 80 characters for the label
96 + * lines and the "TILT=" line which begins the tilt data section, and
97 + * 132 characters thereafter.  (Those counts do not include the CR LF
98 + * line terminator.)  The 2002 version dispenses with those limits,
99 + * allowing 256 characters per line, including the CR LF line
100 + * terminator.  The 2019 version does not specify a line length at
101 + * all.  Ies2rad allows lines of up to 256 characters and will accept
102 + * CR LF or LF alone as line terminators.
103 + *
104 + * In the 1986 version, the label is a series of free-form lines of up
105 + * to 80 characters.  In later versions, the label is a series of
106 + * lines of beginning with keywords in brackets with interpretation
107 + * rules which differ between versions.
108 + *
109 + * The tilt data section begins with a line beginning with "TILT=",
110 + * optionally followed by either a file name or four records of
111 + * numerical data.  The 2019 version no longer allows a file name to
112 + * be given.
113 + *
114 + * The main photometric data section contains two header records
115 + * followed by a record of vertical angles, a record of horizontal
116 + * angles, and one record of candela values for each horizontal angle.
117 + * Each record of candela values contains exactly one value for each
118 + * vertical angle. Data values in records are separated by spaces or
119 + * commas.  In keeping with the robustness principle, commas
120 + * surrounded by spaces will also be accepted as separators.
121 + *
122 + * The first header record of the photometric data section contains
123 + * exactly 10 values.  The second contains exactly 3 values.  Most of
124 + * the data values are floating point numbers; the exceptions are
125 + * various counts and enumerators, which are integers: the number of
126 + * lamps, the numbers of vertical and horizontal angles, the
127 + * photometric type identifier, and the units type identifier.  In the
128 + * 2019 version, a field with information about how the file was
129 + * generated has replaced a field unused since 1995; it is a textual
130 + * representation of a bit string, but may - we hope! - safely be
131 + * interpreted as a floating point number and decoded later.
132 + *
133 + * Style Note
134 + * ==========
135 + * The ies2rad code uses the "bsd" style. For emacs, this is set up
136 + * automatically in the "Local Variables" section at the end of the
137 + * file. For vim, use ":set tabstop=8 shiftwidth=8".
138 + *
139 + * History
140 + * =======
141 + *
142   *      07Apr90         Greg Ward
143 + *
144 + *  Fixed correction factor for flat sources 29Oct2001 GW
145 + *  Extensive comments added by Randolph Fritz May2018
146   */
147  
148 < #include <stdio.h>
148 > #include <math.h>
149   #include <ctype.h>
150 +
151 + #include "rtio.h"
152   #include "color.h"
153 + #include "paths.h"
154  
155   #define PI              3.14159265358979323846
156 <                                        /* floating comparisons */
156 >
157 > #define FAIL            (-1)
158 > #define SUCCESS         0
159 >
160 > /* floating point comparisons -- floating point numbers within FTINY
161 > * of each other are considered equal */
162   #define FTINY           1e-6
163   #define FEQ(a,b)        ((a)<=(b)+FTINY&&(a)>=(b)-FTINY)
164 <                                        /* tilt specs */
164 >
165 > #define IESFIRSTVER 1986
166 > #define IESLASTVER 2019
167 >
168 > /* tilt specs
169 > *
170 > * This next series of definitions address metal-halide lamps, which
171 > * change their brightness depending on the angle at which they are
172 > * mounted. The section begins with "TILT=".  The constants in this
173 > * section are all defined in LM-63.
174 > *
175 > */
176 >
177   #define TLTSTR          "TILT="
178   #define TLTSTRLEN       5
179   #define TLTNONE         "NONE"
# Line 26 | Line 181 | static char SCCSid[] = "$SunId$ LBL";
181   #define TLT_VERT        1
182   #define TLT_H0          2
183   #define TLT_H90         3
184 <                                        /* photometric types */
184 >
185 > /* Constants from LM-63 files */
186 >
187 > /* photometric types
188 > *
189 > * This enumeration reflects three different methods of measuring the
190 > * distribution of light from a luminaire -- "goniophotometry" -- and
191 > * the different coordinate systems related to these
192 > * goniophotometers.  All are described in IES standard LM-75-01.
193 > * Earlier and shorter descriptions may be found the LM-63 standards
194 > * from 1986, 1991, and 1995.
195 > *
196 > * ies2rad does not support type A photometry.
197 > *
198 > * In the 1986 file format, LM-63-86, 1 is used for type C and type A
199 > * photometric data.
200 > *
201 > */
202   #define PM_C            1
203   #define PM_B            2
204 <                                        /* unit types */
204 > #define PM_A            3
205 >
206 > /* unit types */
207   #define U_FEET          1
208   #define U_METERS        2
209 <                                        /* string lengths */
210 < #define MAXLINE         132
211 < #define MAXWORD         76
212 < #define MAXPATH         128
213 <                                        /* file types */
209 >
210 > /* string lengths */
211 > /* Maximum length of a keyword, including brackets and NUL */
212 > #define MAXKW           21
213 > /* Maximum input line is 256 characters including CR LF and NUL at end. */
214 > #define MAXLINE         257
215 > #define MAXUNITNAME     64
216 > #define RMAXWORD        76
217 >
218 > /* Shapes defined in the IES LM-63 standards
219 > *
220 > * PH stands for photometric horizontal
221 > * PPH stands for perpendicular to photometric horizontal
222 > * Cylinders are vertical and circular unless otherwise stated
223 > *
224 > * The numbers assigned here are not part of any LM-63 standard; they
225 > * are for programming convenience.
226 > */
227 > /* Error and not-yet-assigned constants */
228 > #define IESERROR        -2
229 > #define IESNONE         -1
230 > /* Shapes */
231 > #define IESPT            0
232 > #define IESRECT          1
233 > #define IESBOX           2
234 > #define IESDISK          3
235 > #define IESELLIPSE       4
236 > #define IESVCYL          5
237 > #define IESVECYL         6
238 > #define IESSPHERE        7
239 > #define IESELLIPSOID     8
240 > #define IESHCYL_PH       9
241 > #define IESHECYL_PH     10
242 > #define IESHCYL_PPH     11
243 > #define IESHECYL_PPH    12
244 > #define IESVDISK_PH     13
245 > #define IESVEL_PH       14
246 >
247 > /* End of LM-63 related #defines */
248 >
249 > /* file extensions */
250   #define T_RAD           ".rad"
251   #define T_DST           ".dat"
252 < #define T_TLT           "+.dat"
253 <                                        /* shape types */
252 > #define T_TLT           "%.dat"
253 > #define T_OCT           ".oct"
254 >
255 > /* Radiance shape types
256 > * These #defines enumerate the shapes of the Radiance objects which
257 > * emit the light.
258 > */
259   #define RECT            1
260   #define DISK            2
261   #define SPHERE          3
262  
263 < #define MINDIM          .001            /* minimum dimension (point source) */
263 > /* 1mm.  The diameter of a point source luminaire model. Also the minimum
264 > * size (in meters) that the luminous opening of a luminaire must have
265 > * to be treated as other than a point source. */
266 > #define MINDIM          .001
267  
268 < #define F_M             .3048           /* feet to meters */
268 > /* feet to meters */
269 > /* length_in_meters = length_in_feet * F_M */
270 > #define F_M             .3048
271  
272 < #define abspath(p)      ((p)[0] == '/' || (p)[0] == '.')
272 > /* abspath - return true if a path begins with a directory separator
273 > * or a '.' (current directory) */
274 > #define abspath(p)      (ISDIRSEP((p)[0]) || (p)[0] == '.')
275  
276 + /* LM-63 related constants */
277 + typedef struct {
278 +        char *tag;
279 +        int yr; } IESversions;
280 +
281 + IESversions IESFILEVERSIONS[] = {
282 +        { "IESNA91", 1991 },
283 +        { "IESNA:LM-63-1995", 1995 },
284 +        { "IESNA:LM-63-2002", 2002 },
285 +        { "IES:LM-63-2019", 2019 },
286 +        { NULL, 1986 }
287 + };
288 +
289 + char *IESHAPENAMES[] = {
290 +        "point", "rectangle", "box", "disk", "ellipse", "vertical cylinder",
291 +        "vertical elliptical cylinder", "sphere", "ellipsoid",
292 +        "horizontal cylinder along photometric horizontal",
293 +        "horizontal elliptical cylinder along photometric horizontal",
294 +        "horizontal cylinder perpendicular to photometric horizontal",
295 +        "horizontal elliptical cylinder perpendicular to photometric horizontal",
296 +        "vertical disk facing photometric horizontal",
297 +        "vertical ellipse facing photometric horizontal" };
298 +
299 + /* end of LM-63 related constants */
300 +
301 + /* Radiance shape names */
302 + char *RADSHAPENAMES[] = { "rectangle or box", "disk or cylinder", "sphere" };
303 +
304 + /* Global variables.
305 + *
306 + * Mostly, these are a way of communicating command line parameters to
307 + * the rest of the program.
308 + */
309   static char     default_name[] = "default";
310  
311   char    *libdir = NULL;                 /* library directory location */
# Line 63 | Line 318 | char   *deflamp = NULL;                /* default lamp type */
318   float   defcolor[3] = {1.,1.,1.};       /* default lamp color */
319   float   *lampcolor = defcolor;          /* pointer to current lamp color */
320   double  multiplier = 1.0;               /* multiplier for all light sources */
321 < char    units[64] = "meters";           /* output units */
322 < double  minaspect = 0.0;                /* minimum allowed aspect ratio */
323 < int     maxemitters = 1;                /* maximum emitters per hemisphere */
321 > char    units[MAXUNITNAME] = "meters";  /* output units */
322 > int     out2stdout = 0;                 /* put out to stdout r.t. file */
323 > int     instantiate = 0;                /* instantiate geometry */
324   double  illumrad = 0.0;                 /* radius for illum sphere */
325  
326 + /* This struct describes the Radiance source object */
327   typedef struct {
328 +        int     isillum;                        /* do as illum */
329          int     type;                           /* RECT, DISK, SPHERE */
330 +        double  mult;                           /* candela multiplier */
331          double  w, l, h;                        /* width, length, height */
332          double  area;                           /* max. projected area */
333 < } SHAPE;                                /* a source shape */
333 >        int     filerev;                        /* IES file version */
334 >        int     havelamppos;                    /* Lamp position was given */
335 >        float   lamppos[2];                     /* Lamp position */
336 >        int     iesshape;                       /* Shape number */
337 >        char   *warn;                           /* Warning message */
338 > } SRCINFO;                              /* a source shape (units=meters) */
339  
340 < int     gargc;                          /* global argc (minus filenames) */
340 > /* A count and pointer to the list of input file names */
341 > int     gargc;                          /* global argc */
342   char    **gargv;                        /* global argv */
343  
344 < extern char     *strcpy(), *strcat(), *stradd(), *tailtrunc(), *filetrunc(),
345 <                *filename(), *libname(), *fullname(), *malloc();
346 < extern float    *matchlamp();
344 > /* macros to scan numbers out of IES files
345 > *
346 > * fp is a file pointer.  scnint() places the number in the integer
347 > * indicated by ip; scnflt() places the number in the double indicated
348 > * by rp. The macros return 1 if successful, 0 if not.
349 > *
350 > */
351 > #define scnint(fp,ip)   cvtint(ip,getword(fp))
352 > #define scnflt(fp,rp)   cvtflt(rp,getword(fp))
353  
354 + /* The original (1986) version of LM-63 allows decimals points in
355 + * integers, so that, for instance, the number of lamps may be written
356 + * 3.0 (the number, obviously, must still be an integer.) This
357 + * confusing define accommodates that.  */
358 + #define isint           isflt
359  
360 < main(argc, argv)
361 < int     argc;
362 < char    *argv[];
360 > /* IES file conversion functions */
361 > static int ies2rad(char *inpname, char *outname);
362 > static void initlamps(void);
363 > static int dosource(SRCINFO *sinf, FILE *in, FILE *out, char *mod, char *name);
364 > static int dotilt(FILE *in, FILE *out, char *dir, char *tltspec,
365 >                char *dfltname, char *tltid);
366 > static int cvgeometry(char *inpname, SRCINFO *sinf, char *outname, FILE *outfp);
367 > static int cvtint(int *ip, char *wrd);
368 > static int cvdata(FILE *in, FILE *out, int ndim, int npts[], double mult,
369 >                double lim[][2]);
370 > static int cvtflt(double *rp, char *wrd);
371 > static int makeiesshape(SRCINFO *shp, double length, double width, double height);
372 > static int makeillumsphere(SRCINFO *shp);
373 > static int makeshape(SRCINFO *shp, double width, double length, double height);
374 > static void makecylshape(SRCINFO *shp, double diam, double height);
375 > static void makeelshape(SRCINFO *shp, double width, double length, double height);
376 > static void makeecylshape(SRCINFO *shp, double width, double length, double height);
377 > static void makeelshape(SRCINFO *shp, double width, double length, double height);
378 > static void makeboxshape(SRCINFO *shp, double length, double width, double height);
379 > static int makepointshape(SRCINFO *shp);
380 > static int putsource(SRCINFO *shp, FILE *fp, char *mod, char *name,
381 >                int dolower, int doupper, int dosides);
382 > static void putrectsrc(SRCINFO *shp, FILE *fp, char *mod, char *name, int up);
383 > static void putsides(SRCINFO *shp, FILE *fp, char *mod, char *name);
384 > static void putdisksrc(SRCINFO *shp, FILE *fp, char *mod, char *name, int up);
385 > static void putspheresrc(SRCINFO *shp, FILE *fp, char *mod, char *name);
386 > static void putrect(SRCINFO *shp, FILE *fp, char *mod, char *name, char *suffix,
387 >                int a, int b, int c, int d);
388 > static void putpoint(SRCINFO *shp, FILE *fp, int p);
389 > static void putcyl(SRCINFO *shp, FILE *fp, char *mod, char *name);
390 > static void shapearea(SRCINFO *shp);
391 >
392 > /* string and filename functions */
393 > static int isprefix(char *p, char *s);
394 > static char * matchprefix(char *p, char *s);
395 > static char * tailtrunc(char *name);
396 > static char * filename(char *path);
397 > static char * libname(char *path, char *fname, char *suffix);
398 > static char * getword(FILE *fp);
399 > static char * fullnam(char *path, char *fname, char *suffix);
400 >
401 > /* output function */
402 > static void fpcomment(FILE *fp, char *prefix, char *s);
403 >
404 > /* main - process arguments and run the conversion
405 > *
406 > * Refer to the man page for details of the arguments.
407 > *
408 > * Following Unix environment conventions, main() exits with 0 on
409 > * success and 1 on failure.
410 > *
411 > * ies2rad outputs either two or three files for a given IES
412 > * file. There is always a .rad file containing Radiance scene
413 > * description primitives and a .dat file for the photometric data. If
414 > * tilt data is given, that is placed in a separate .dat file.  So
415 > * ies2rad must have a filename to operate. Sometimes this name is the
416 > * input file name, shorn of its extension; sometimes it is given in
417 > * the -o option. But an output file name is required for ies2rad to
418 > * do its work.
419 > *
420 > * Older versions of the LM-63 standard allowed inclusion of multiple
421 > * luminaires in one IES file; this is not supported by ies2rad.
422 > *
423 > * This code sometimes does not check to make sure it has not run out
424 > * of arguments; this can lead to segmentation faults and perhaps
425 > * other errors.
426 > *
427 > */
428 > int
429 > main(
430 >        int     argc,
431 >        char    *argv[]
432 > )
433   {
434          char    *outfile = NULL;
435          int     status;
436 <        char    outname[MAXWORD];
436 >        char    outname[RMAXWORD];
437          double  d1;
438          int     i;
439 <        
439 >
440 >        /* Scan the options */
441          for (i = 1; i < argc && argv[i][0] == '-'; i++)
442                  switch (argv[i][1]) {
443                  case 'd':               /* dimensions */
# Line 153 | Line 499 | char   *argv[];
499                  case 'f':               /* lamp data file */
500                          lampdat = argv[++i];
501                          break;
502 <                case 'o':               /* output file name */
502 >                case 'o':               /* output file root name */
503                          outfile = argv[++i];
504                          break;
505 <                case 's':               /* square emitters */
506 <                        minaspect = .6;
161 <                        if (argv[i][2] == '/') {
162 <                                maxemitters = atoi(argv[i]+3);
163 <                                if (maxemitters < 1)
164 <                                        goto badopt;
165 <                        }
505 >                case 's':               /* output to stdout */
506 >                        out2stdout = !out2stdout;
507                          break;
508                  case 'i':               /* illum */
509                          illumrad = atof(argv[++i]);
169                        if (illumrad < MINDIM)
170                                illumrad = MINDIM;
510                          break;
511 +                case 'g':               /* instantiate geometry? */
512 +                        instantiate = !instantiate;
513 +                        break;
514                  case 't':               /* override lamp type */
515                          lamptype = argv[++i];
516                          break;
# Line 189 | Line 531 | char   *argv[];
531                                          argv[0], argv[i]);
532                          exit(1);
533                  }
534 +        /* Save pointers to the list of input file names */
535          gargc = i;
536          gargv = argv;
537 <        initlamps();                    /* get lamp data (if needed) */
538 <                                        /* convert ies file(s) */
537 >
538 >        /* get lamp data (if needed) */
539 >        initlamps();
540 >
541 >        /* convert ies file(s) */
542 >        /* If an output file name is specified */
543          if (outfile != NULL) {
544                  if (i == argc)
545 +                        /* If no input filename is given, use stdin as
546 +                         * the source for the IES file */
547                          exit(ies2rad(NULL, outfile) == 0 ? 0 : 1);
548                  else if (i == argc-1)
549 +                        /* If exactly one input file name is given, use it. */
550                          exit(ies2rad(argv[i], outfile) == 0 ? 0 : 1);
551 <                else {
552 <                        fprintf(stderr, "%s: single input file required\n",
203 <                                        argv[0]);
204 <                        exit(1);
205 <                }
551 >                else
552 >                        goto needsingle; /* Otherwise, error. */
553          } else if (i >= argc) {
554 +                /* If an output file and an input file are not give, error. */
555                  fprintf(stderr, "%s: missing output file specification\n",
556                                  argv[0]);
557                  exit(1);
558          }
559 +        /* If no input or output file is given, error. */
560 +        if (out2stdout && i != argc-1)
561 +                goto needsingle;
562 +        /* Otherwise, process each input file in turn. */
563          status = 0;
564          for ( ; i < argc; i++) {
565                  tailtrunc(strcpy(outname,filename(argv[i])));
# Line 215 | Line 567 | char   *argv[];
567                          status = 1;
568          }
569          exit(status);
570 + needsingle:
571 +        fprintf(stderr, "%s: single input file required\n", argv[0]);
572 +        exit(1);
573   }
574  
575 <
576 < initlamps()                             /* set up lamps */
575 > /* Initlamps -- If necessary, read lamp data table */
576 > void
577 > initlamps(void)                         /* set up lamps */
578   {
579          float   *lcol;
580          int     status;
581  
582 +        /* If the lamp name is set to default, don't bother to read
583 +         * the lamp data table. */
584          if (lamptype != NULL && !strcmp(lamptype, default_name) &&
585                          deflamp == NULL)
586 <                return;                         /* no need for data */
587 <                                                /* else load file */
588 <        if ((status = loadlamps(lampdat)) < 0)
589 <                exit(1);
586 >                return;
587 >
588 >        if ((status = loadlamps(lampdat)) < 0) /* Load the lamp data table */
589 >                exit(1);                       /* Exit if problems
590 >                                                * with the file. */
591          if (status == 0) {
592 +                /* If can't open the file, just use the standard default lamp */
593                  fprintf(stderr, "%s: warning - no lamp data\n", lampdat);
594                  lamptype = default_name;
595                  return;
596          }
597 <        if (deflamp != NULL) {                  /* match default type */
597 >        if (deflamp != NULL) {
598 >                /* Look up the specified default lamp type */
599                  if ((lcol = matchlamp(deflamp)) == NULL)
600 +                        /* If it can't be found, use the default */
601                          fprintf(stderr,
602                                  "%s: warning - unknown default lamp type\n",
603                                          deflamp);
604                  else
605 +                        /* Use the selected default lamp color */
606                          copycolor(defcolor, lcol);
607          }
608 <        if (lamptype != NULL) {                 /* match selected type */
608 >        /* If a lamp type is specified and can be found, use it, and
609 >         * release the lamp data table memory; it won't be needed any more. */
610 >        if (lamptype != NULL) {
611                  if (strcmp(lamptype, default_name)) {
612                          if ((lcol = matchlamp(lamptype)) == NULL) {
613                                  fprintf(stderr,
# Line 254 | Line 619 | initlamps()                            /* set up lamps */
619                  }
620                  freelamps();                    /* all done with data */
621          }
622 <                                                /* else keep lamp data */
622 >        /* else keep lamp data */
623   }
624  
625 + /*
626 + * String functions
627 + */
628  
629 + /*
630 + * isprefix - return 1 (true) if p is a prefix of s, 0 otherwise
631 + *
632 + * For this to work properly, s must be as long or longer than p.
633 + */
634 + int
635 + isprefix(char *p, char *s) {
636 +        return matchprefix(p,s) != NULL;
637 + }
638 +
639 + /*
640 + * matchprefix - match p against s
641 + *
642 + * If p is a prefix of s, return a pointer to the character of s just
643 + * past p.
644 + *
645 + * For this to work properly, s must be as long or longer than p.
646 + */
647   char *
648 < stradd(dst, src, sep)                   /* add a string at dst */
649 < register char   *dst, *src;
650 < int     sep;
648 > matchprefix(char *p, char *s) {
649 >        int c;
650 >        
651 >        while ((c = *p++)) {
652 >                if (c != *s++)
653 >                        return NULL;
654 >        }
655 >        return s;
656 > }
657 >
658 > /*
659 > * skipws - skip whitespace
660 > */
661 > char *
662 > skipws(char *s) {
663 >        while (isspace(*s))
664 >                s++;
665 >        return s;
666 > }
667 >
668 > /*
669 > * streq - test strings for equality
670 > */
671 > int
672 > streq(char *s1, char *s2) {
673 >        return strcmp(s1,s2) == 0;
674 > }
675 >
676 > /*
677 > * strneq - test strings for equality, with a length limit
678 > */
679 > int
680 > strneq(char *s1, char *s2, int n) {
681 >        return strncmp(s1,s2,n) == 0;
682 > }
683 >
684 > /*
685 > * IES (LM-63) file functions
686 > */
687 >
688 > /*
689 > * prockwd - process keywords on a label line
690 > *
691 > * We're looking for four keywords: LAMP, LAMPCAT, LAMPPOSITION, and
692 > * LUMINOUSGEOMETRY.  Any other keywords are ignored.
693 > *
694 > * LAMP and LAMPCAT are searched for a known lamp type name.
695 > * LAMPPOSITION is stored.
696 > * LUMINOUSGEOMETRY contains the name of an MGF file, which is stored.
697 > */
698 > void
699 > prockwd(char *bp, char *geomfile, char *inpname, SRCINFO *srcinfo) {
700 >        char *kwbegin;
701 >        int kwlen;
702 >        
703 >        bp = skipws(bp);        /* Skip leading whitespace. */
704 >        if (*bp != '[')
705 >                return;         /* If there's no keyword on this line,
706 >                                 * do nothing */
707 >        kwbegin = bp;
708 >        while (*bp && *bp != ']')       /* Skip to the end of the keyword or
709 >                                 * end of the buffer. */
710 >                bp++;
711 >        if (!(*bp))             /* If the keyword doesn't have a
712 >                                 * terminating ']', return. */
713 >                return;
714 >        kwlen = bp - kwbegin + 1;
715 >        bp++;
716 >        if (lampcolor == NULL && strneq("[LAMP]", kwbegin, kwlen))
717 >                lampcolor = matchlamp(bp);
718 >        else if (lampcolor == NULL && strneq("[LAMPCAT]", kwbegin, kwlen))
719 >                lampcolor = matchlamp(bp);
720 >        else if (strneq("[LUMINOUSGEOMETRY]", kwbegin, kwlen)) {
721 >                bp = skipws(bp);        /* Skip leading whitespace. */
722 >                strcpy(geomfile, inpname); /* Copy the input file path */
723 >                /* Replace the filename in the input file path with
724 >                 * the name of the MGF file.  Trailing spaces were
725 >                 * trimmed before this routine was called. */
726 >                strcpy(filename(geomfile), bp);
727 >                srcinfo->isillum = 1;
728 >        }
729 >        else if (strneq("[LAMPPOSITION]", kwbegin, kwlen)) {
730 >                srcinfo->havelamppos = 1;
731 >                sscanf(bp,"%f%f", &(srcinfo->lamppos[0]),
732 >                       &(srcinfo->lamppos[1]));
733 >        }
734 > }
735 >
736 > /*
737 > * iesversion - examine the first line of an IES file and return the version
738 > *
739 > * Returns the year of the version.  If the version is unknown,
740 > * returns 1986, since the first line of a 1986-format IES file can be
741 > * anything.
742 > */
743 > int
744 > iesversion(char *buf) {
745 >        IESversions *v;
746 >
747 >        for(v = IESFILEVERSIONS; v != NULL; v++)
748 >                if (streq(v->tag,buf))
749 >                        return v->yr;
750 >        return v->yr;
751 > }
752 >
753 >
754 > /*
755 > * File path operations
756 > *
757 > * These provide file path operations that operate on both MS-Windows
758 > * and *nix. They will ignore and pass, but will not necessarily
759 > * process correctly, Windows drive letters. Paths including Windows
760 > * UNC network names (\\server\folder\file) may also cause problems.
761 > *
762 > */
763 >
764 > /*
765 > * stradd()
766 > *
767 > * Add a string to the end of a string, optionally concatenating a
768 > * file path separator character.  If the path already ends with a
769 > * path separator, no additional separator is appended.
770 > *
771 > */
772 > char *
773 > stradd(                 /* add a string at dst */
774 >        char    *dst,
775 >        char    *src,
776 >        int     sep
777 > )
778   {
779          if (src && *src) {
780                  do
# Line 274 | Line 787 | int    sep;
787          return(dst);
788   }
789  
790 <
790 > /*
791 > * fullnam () - return a usable path name for an output file
792 > */
793   char *
794 < fullname(path, fname, suffix)           /* return full path name */
795 < char    *path, *fname, *suffix;
794 > fullnam(
795 >        char    *path,          /* The base directory path */
796 >        char    *fname,         /* The file name */
797 >        char    *suffix         /* A suffix, which usually contains
798 >                                 * a file name extension. */
799 > )
800   {
801 +        extern char *prefdir;
802 +        extern char *libdir;
803 +
804          if (prefdir != NULL && abspath(prefdir))
805 +                /* If the subdirectory path is absolute or '.', just
806 +                 * concatenate the names together */
807                  libname(path, fname, suffix);
808          else if (abspath(fname))
809 +                /* If there is no subdirectory, and the file name is
810 +                 * an absolute path or '.', concatenate the path,
811 +                 * filename, and suffix. */
812                  strcpy(stradd(path, fname, 0), suffix);
813          else
814 <                libname(stradd(path, libdir, '/'), fname, suffix);
814 >                /* If the file name is relative, concatenate path,
815 >                 * library directory, directory separator, file name,
816 >                 * and suffix.  */
817 >                libname(stradd(path, libdir, DIRSEP), fname, suffix);
818  
819          return(path);
820   }
821  
822  
823 + /*
824 + * libname - convert a file name to a path
825 + */
826   char *
827 < libname(path, fname, suffix)            /* return library relative name */
828 < char    *path, *fname, *suffix;
827 > libname(
828 >        char    *path,          /* The base directory path */
829 >        char    *fname,         /* The file name */
830 >        char    *suffix         /* A suffix, which usually contains
831 >                                 * a file name extension. */
832 > )
833   {
834 +        extern char *prefdir;   /* The subdirectory where the file
835 +                                 * name is stored. */
836 +
837          if (abspath(fname))
838 +                /* If the file name begins with '/' or '.', combine
839 +                 * it with the path and attach the suffix */
840                  strcpy(stradd(path, fname, 0), suffix);
841          else
842 <                strcpy(stradd(stradd(path, prefdir, '/'), fname, 0), suffix);
842 >                /* If the file name is relative, attach it to the
843 >                 * path, include the subdirectory, and append the suffix. */
844 >                strcpy(stradd(stradd(path, prefdir, DIRSEP), fname, 0), suffix);
845  
846          return(path);
847   }
848  
849 <
849 > /* filename - pointer to filename in buffer containing path
850 > *
851 > * Scan the path, recording directory separators.  Return the location
852 > * of the character past the last one.  If no directory separators are
853 > * found, returns a pointer to beginning of the path.
854 > */
855   char *
856 < filename(path)                  /* get final component of pathname */
857 < register char   *path;
856 > filename(
857 >        char    *path
858 > )
859   {
860 <        register char   *cp;
860 >        char    *cp = path;
861  
862 <        for (cp = path; *path; path++)
863 <                if (*path == '/')
862 >        for (; *path; path++)
863 >                if (ISDIRSEP(*path))
864                          cp = path+1;
865          return(cp);
866   }
867  
868  
869 + /* filetrunc() - return the directory portion of a path
870 + *
871 + * The path is passed in in a pointer to a buffer; a null character is
872 + * inserted in the buffer after the last directory separator
873 + *
874 + */
875   char *
876 < filetrunc(path)                         /* truncate filename at end of path */
877 < char    *path;
876 > filetrunc(
877 >        char    *path
878 > )
879   {
880 <        register char   *p1, *p2;
880 >        char    *p1, *p2;
881  
882          for (p1 = p2 = path; *p2; p2++)
883 <                if (*p2 == '/')
883 >                if (ISDIRSEP(*p2))
884                          p1 = p2;
885 +        if (p1 == path && ISDIRSEP(*p1))
886 +                p1++;
887          *p1 = '\0';
888          return(path);
889   }
890  
891 <
891 > /* tailtrunc() - trim a file name extension, if any.
892 > *
893 > * The file name is passed in in a buffer indicated by *name; the
894 > * period which begins the extension is replaced with a 0 byte.
895 > */
896   char *
897 < tailtrunc(name)                         /* truncate tail of filename */
898 < char    *name;
897 > tailtrunc(
898 >        char    *name
899 > )
900   {
901 <        register char   *p1, *p2;
901 >        char    *p1, *p2;
902  
903 +        /* Skip leading periods */
904          for (p1 = filename(name); *p1 == '.'; p1++)
905                  ;
906 +        /* Find the last period in a file name */
907          p2 = NULL;
908          for ( ; *p1; p1++)
909                  if (*p1 == '.')
910                          p2 = p1;
911 +        /* If present, trim the filename at that period */
912          if (p2 != NULL)
913                  *p2 = '\0';
914          return(name);
915   }
916  
917 <
918 < blanktrunc(s)                           /* truncate spaces at end of line */
919 < char    *s;
917 > /* blanktrunc() - trim spaces at the end of a string
918 > *
919 > * the string is passed in a character array, which is modified
920 > */
921 > void
922 > blanktrunc(
923 >        char    *s
924 > )
925   {
926 <        register char   *cp;
926 >        char    *cp;
927  
928          for (cp = s; *cp; cp++)
929                  ;
# Line 360 | Line 932 | char   *s;
932          *++cp = '\0';
933   }
934  
935 + /* fpcomment - output a multi-line comment
936 + *
937 + * The comment may be multiple lines, with each line separated by a
938 + * newline.  Each line is prefixed by prefix.  If the last line isn't
939 + * terminated by a newline, no newline will be output.
940 + */
941 + void
942 + fpcomment(FILE *fp, char *prefix, char *s) {
943 +  while (*s) {                  /* While there are characters left to output */
944 +    fprintf(fp, "%s", prefix);  /* Output the prefix */
945 +    for (; *s && *s != '\n'; s++) /* Output a line */
946 +      putc(*s, fp);
947 +    if (*s == '\n') {           /* Including the newline, if any */
948 +      putc(*s, fp);
949 +      s++;
950 +    }
951 +  }
952 + }
953  
954 < putheader(out)                          /* print header */
955 < FILE    *out;
954 > /* putheader - output the header of the .rad file
955 > *
956 > * Header is:
957 > *   # <file> <file> <file> (all files from input line)
958 > *   # Dimensions in [feet,meters,etc.]
959 > *
960 > * ??? Is listing all the input file names correct behavior?
961 > *
962 > */
963 > void
964 >
965 > putheader(
966 >        FILE    *out
967 > )
968   {
969 <        register int    i;
970 <        
969 >        int     i;
970 >
971          putc('#', out);
972          for (i = 0; i < gargc; i++) {
973                  putc(' ', out);
# Line 376 | Line 978 | FILE   *out;
978          putc('\n', out);
979   }
980  
981 <
982 < ies2rad(inpname, outname)               /* convert IES file */
983 < char    *inpname, *outname;
981 > /* ies2rad - convert an IES LM-63 file to a Radiance light source desc.
982 > *
983 > * Return -1 in case of failure, 0 in case of success.
984 > *
985 > */
986 > int
987 > ies2rad(                /* convert IES file */
988 >        char    *inpname,
989 >        char    *outname
990 > )
991   {
992 <        char    buf[MAXLINE], tltid[MAXWORD];
992 >        SRCINFO srcinfo;
993 >        char    buf[MAXLINE], tltid[RMAXWORD];
994 >        char    geomfile[MAXLINE];
995          FILE    *inpfp, *outfp;
996 +        int     lineno = 0;
997  
998 +
999 +        /* Initialize srcinfo */
1000 +        srcinfo.filerev = IESFIRSTVER;
1001 +        srcinfo.iesshape = IESNONE;
1002 +        srcinfo.warn = NULL;
1003 +        srcinfo.isillum = 0;
1004 +        srcinfo.havelamppos = 0;
1005 +        /* Open input and output files */
1006 +        geomfile[0] = '\0';
1007          if (inpname == NULL) {
1008                  inpname = "<stdin>";
1009                  inpfp = stdin;
# Line 390 | Line 1011 | char   *inpname, *outname;
1011                  perror(inpname);
1012                  return(-1);
1013          }
1014 <        if ((outfp = fopen(fullname(buf,outname,T_RAD), "w")) == NULL) {
1014 >        if (out2stdout)
1015 >                outfp = stdout;
1016 >        else if ((outfp = fopen(fullnam(buf,outname,T_RAD), "w")) == NULL) {
1017                  perror(buf);
1018                  fclose(inpfp);
1019                  return(-1);
1020          }
1021 +
1022 +        /* Output the output file header */
1023          putheader(outfp);
1024 +
1025 +        /* If the lamp type wasn't given on the command line, mark
1026 +         * the lamp color as missing */
1027          if (lamptype == NULL)
1028                  lampcolor = NULL;
1029 +
1030 +        /* Read the input file header, copying lines to the .rad file
1031 +         * and looking for a lamp type. Stop at EOF or a line
1032 +         * beginning with "TILT=". */
1033          while (fgets(buf,sizeof(buf),inpfp) != NULL
1034                          && strncmp(buf,TLTSTR,TLTSTRLEN)) {
1035 <                blanktrunc(buf);
1036 <                if (!buf[0])
1035 >                blanktrunc(buf); /* Trim trailing spaces, CR, LF. */
1036 >                if (!buf[0])     /* Skip blank lines */
1037                          continue;
1038 +                /* increment the header line count. If we are on the
1039 +                 * first line of the file, check for a version tag. If
1040 +                 * one is not found, assume the first version of the
1041 +                 * file. */
1042 +                if (!lineno++)
1043 +                        srcinfo.filerev = iesversion(buf);
1044 +                /* Output the header line as a comment in the .rad file. */
1045                  fputs("#<", outfp);
1046                  fputs(buf, outfp);
1047                  putc('\n', outfp);
1048 <                if (lampcolor == NULL)
1048 >
1049 >                /* For post-1986 version files, process a keyword
1050 >                 * line.  Otherwise, just scan the line for a lamp
1051 >                 * name */
1052 >                if (srcinfo.filerev != 1986)
1053 >                        prockwd(buf, geomfile, inpname, &srcinfo);
1054 >                else if (lampcolor == NULL)
1055                          lampcolor = matchlamp(buf);
1056          }
1057 +
1058 +        /* Done reading header information. If a lamp color still
1059 +         * hasn't been found, print a warning and use the default
1060 +         * color; if a lamp type hasn't been found, but a color has
1061 +         * been specified, used the specified color. */
1062          if (lampcolor == NULL) {
1063                  fprintf(stderr, "%s: warning - no lamp type\n", inpname);
1064 +                fputs("# Unknown lamp type (used default)\n", outfp);
1065                  lampcolor = defcolor;
1066 <        }
1066 >        } else if (lamptype == NULL)
1067 >                fprintf(outfp,"# CIE(x,y) = (%f,%f)\n# Depreciation = %.1f%%\n",
1068 >                                lampcolor[3], lampcolor[4], 100.*lampcolor[5]);
1069 >
1070 >        /* If the file ended before a "TILT=" line, that's an error. */
1071          if (feof(inpfp)) {
1072                  fprintf(stderr, "%s: not in IES format\n", inpname);
1073                  goto readerr;
1074          }
1075 <        sscanf(buf+TLTSTRLEN, "%s", tltid);
1075 >
1076 >        /* Process the tilt section of the file. */
1077 >        /* Get the tilt file name, or the keyword "INCLUDE". */
1078 >        atos(tltid, RMAXWORD, buf+TLTSTRLEN);
1079          if (inpfp == stdin)
1080                  buf[0] = '\0';
1081          else
1082                  filetrunc(strcpy(buf, inpname));
1083 +        /* Process the tilt data. */
1084          if (dotilt(inpfp, outfp, buf, tltid, outname, tltid) != 0) {
1085                  fprintf(stderr, "%s: bad tilt data\n", inpname);
1086                  goto readerr;
1087          }
1088 <        if (dosource(inpfp, outfp, tltid, outname) != 0) {
1088 >
1089 >        /* Process the luminaire data. */
1090 >        if (dosource(&srcinfo, inpfp, outfp, tltid, outname) != 0) {
1091                  fprintf(stderr, "%s: bad luminaire data\n", inpname);
1092                  goto readerr;
1093          }
1094 <        fclose(outfp);
1094 >
1095 >        /* Close the input file */
1096          fclose(inpfp);
1097 +
1098 +        /* Process an MGF file, if present. cvgeometry() closes outfp. */
1099 +        if (cvgeometry(geomfile, &srcinfo, outname, outfp) != 0) {
1100 +                fprintf(stderr, "%s: bad geometry file\n", geomfile);
1101 +                return(-1);
1102 +        }
1103          return(0);
1104 +
1105   readerr:
1106 <        fclose(outfp);
1106 >        /* If there is an error reading the file, close the input and
1107 >         * .rad output files, and delete the .rad file, returning -1. */
1108          fclose(inpfp);
1109 <        unlink(fullname(buf,outname,T_RAD));
1109 >        fclose(outfp);
1110 >        unlink(fullnam(buf,outname,T_RAD));
1111          return(-1);
1112   }
1113  
1114 <
1115 < dotilt(in, out, dir, tltspec, dfltname, tltid)  /* convert tilt data */
1116 < FILE    *in, *out;
1117 < char    *dir, *tltspec, *dfltname, *tltid;
1114 > /* dotilt -- process tilt data
1115 > *
1116 > * Generate a brightdata primitive which describes the effect of
1117 > * luminaire tilt on luminaire output and return its identifier in tltid.
1118 > *
1119 > * Tilt data (if present) is given as a number 1, 2, or 3, which
1120 > * specifies the orientation of the lamp within the luminaire, a
1121 > * number, n, of (angle, multiplier) pairs, followed by n angles and n
1122 > * multipliers.
1123 > *
1124 > * returns 0 for success, -1 for error
1125 > */
1126 > int
1127 > dotilt(
1128 >        FILE    *in,
1129 >        FILE    *out,
1130 >        char    *dir,
1131 >        char    *tltspec,
1132 >        char    *dfltname,
1133 >        char    *tltid
1134 > )
1135   {
1136          int     nangles, tlt_type;
1137 <        double  minmax[2];
1138 <        char    buf[MAXPATH], tltname[MAXWORD];
1137 >        double  minmax[1][2];
1138 >        char    buf[PATH_MAX], tltname[RMAXWORD];
1139          FILE    *datin, *datout;
1140  
1141 +        /* Decide where the tilt data is; if the luminaire description
1142 +         * doesn't have a tilt section, set the identifier to "void". */
1143          if (!strcmp(tltspec, TLTNONE)) {
1144 +                /* If the line is "TILT=NONE", set the input file
1145 +                 * pointer to NULL and the identifier to "void". */
1146                  datin = NULL;
1147                  strcpy(tltid, "void");
1148          } else if (!strcmp(tltspec, TLTINCL)) {
1149 +                /* If the line is "TILT=INCLUDE" use the main IES
1150 +                 * file as the source of tilt data. */
1151                  datin = in;
1152                  strcpy(tltname, dfltname);
1153          } else {
1154 <                if (tltspec[0] == '/')
1154 >                /* If the line is "TILT=<filename>", use that file
1155 >                 * name as the source of tilt data. */
1156 >                if (ISDIRSEP(tltspec[0]))
1157                          strcpy(buf, tltspec);
1158                  else
1159 <                        strcpy(stradd(buf, dir, '/'), tltspec);
1159 >                        strcpy(stradd(buf, dir, DIRSEP), tltspec);
1160                  if ((datin = fopen(buf, "r")) == NULL) {
1161                          perror(buf);
1162                          return(-1);
1163                  }
1164                  tailtrunc(strcpy(tltname,filename(tltspec)));
1165          }
1166 +        /* If tilt data is present, read, process, and output it. */
1167          if (datin != NULL) {
1168 <                if ((datout = fopen(fullname(buf,tltname,T_TLT),"w")) == NULL) {
1168 >                /* Try to open the output file */
1169 >                if ((datout = fopen(fullnam(buf,tltname,T_TLT),"w")) == NULL) {
1170                          perror(buf);
1171                          if (datin != in)
1172                                  fclose(datin);
1173                          return(-1);
1174                  }
1175 <                if (fscanf(datin, "%d %d", &tlt_type, &nangles) != 2
1175 >                /* Try to copy the tilt data to the tilt data file */
1176 >                if (!scnint(datin,&tlt_type) || !scnint(datin,&nangles)
1177                          || cvdata(datin,datout,1,&nangles,1.,minmax) != 0) {
1178                          fprintf(stderr, "%s: data format error\n", tltspec);
1179                          fclose(datout);
1180                          if (datin != in)
1181                                  fclose(datin);
1182 <                        unlink(fullname(buf,tltname,T_TLT));
1182 >                        unlink(fullnam(buf,tltname,T_TLT));
1183                          return(-1);
1184                  }
1185                  fclose(datout);
1186                  if (datin != in)
1187                          fclose(datin);
1188 +
1189 +                /* Generate the identifier of the brightdata; the filename
1190 +                 * with "_tilt" appended. */
1191                  strcat(strcpy(tltid, filename(tltname)), "_tilt");
1192 +                /* Write out the brightdata primitive */
1193                  fprintf(out, "\nvoid brightdata %s\n", tltid);
1194                  libname(buf,tltname,T_TLT);
1195 +                /* Generate the tilt description */
1196                  switch (tlt_type) {
1197 <                case TLT_VERT:                  /* vertical */
1197 >                case TLT_VERT:
1198 >                        /* The lamp is mounted vertically; either
1199 >                         * base up or base down. */
1200                          fprintf(out, "4 noop %s tilt.cal %s\n", buf,
1201 <                                minmax[1]>90.+FTINY ? "tilt_ang" : "tilt_ang2");
1201 >                                minmax[0][1]>90.+FTINY ? "tilt_ang" : "tilt_ang2");
1202                          break;
1203 <                case TLT_H0:                    /* horiz. in 0 deg. plane */
1203 >                case TLT_H0:
1204 >                        /* The lamp is mounted horizontally and
1205 >                         * rotates but does not tilt when the
1206 >                         * luminaire is tilted. */
1207                          fprintf(out, "6 noop %s tilt.cal %s -rz 90\n", buf,
1208 <                        minmax[1]>90.+FTINY ? "tilt_xang" : "tilt_xang2");
1208 >                        minmax[0][1]>90.+FTINY ? "tilt_xang" : "tilt_xang2");
1209                          break;
1210                  case TLT_H90:
1211 +                        /* The lamp is mounted horizontally, and
1212 +                         * tilts when the luminaire is tilted. */
1213                          fprintf(out, "4 noop %s tilt.cal %s\n", buf,
1214 <                        minmax[1]>90.+FTINY ? "tilt_xang" : "tilt_xang2");
1214 >                        minmax[0][1]>90.+FTINY ? "tilt_xang" : "tilt_xang2");
1215                          break;
1216                  default:
1217 +                        /* otherwise, this is a bad IES file */
1218                          fprintf(stderr,
1219                                  "%s: illegal lamp to luminaire geometry (%d)\n",
1220                                  tltspec, tlt_type);
1221                          return(-1);
1222                  }
1223 +                /* And finally output the numbers of integer and real
1224 +                 * arguments, of which there are none. */
1225                  fprintf(out, "0\n0\n");
1226          }
1227          return(0);
1228   }
1229  
1230 <
1231 < dosource(in, out, mod, name)            /* create source and distribution */
1232 < FILE    *in, *out;
1233 < char    *mod, *name;
1230 > /* dosource -- create the source and distribution primitives */
1231 > int
1232 > dosource(
1233 >        SRCINFO *sinf,
1234 >        FILE    *in,
1235 >        FILE    *out,
1236 >        char    *mod,
1237 >        char    *name
1238 > )
1239   {
1240 <        SHAPE   srcshape;
522 <        char    buf[MAXPATH], id[MAXWORD];
1240 >        char    buf[PATH_MAX], id[RMAXWORD];
1241          FILE    *datout;
1242          double  mult, bfactor, pfactor, width, length, height, wattage;
1243          double  bounds[2][2];
1244          int     nangles[2], pmtype, unitype;
1245          double  d1;
1246 +        int     doupper, dolower, dosides;
1247  
1248 <        if (fscanf(in, "%*d %*f %lf %d %d %d %d %lf %lf %lf %lf %lf %lf",
1249 <                        &mult, &nangles[0], &nangles[1], &pmtype, &unitype,
1250 <                        &width, &length, &height, &bfactor, &pfactor,
1251 <                        &wattage) != 11) {
1248 >        /* Read in the luminaire description header */
1249 >        if (!isint(getword(in)) || !isflt(getword(in)) || !scnflt(in,&mult)
1250 >                        || !scnint(in,&nangles[0]) || !scnint(in,&nangles[1])
1251 >                        || !scnint(in,&pmtype) || !scnint(in,&unitype)
1252 >                        || !scnflt(in,&width) || !scnflt(in,&length)
1253 >                        || !scnflt(in,&height) || !scnflt(in,&bfactor)
1254 >                        || !scnflt(in,&pfactor) || !scnflt(in,&wattage)) {
1255                  fprintf(stderr, "dosource: bad lamp specification\n");
1256                  return(-1);
1257          }
1258 +        
1259 +        /* pfactor is only provided in 1986 and 1991 format files, and
1260 +         * is something completely different in 2019 files.  If the
1261 +         * file version is 1995 or later, set it to 1.0 to avoid
1262 +         * error. */
1263 +        if (sinf->filerev >= 1995)
1264 +                pfactor = 1.0;
1265 +
1266 +        /* Type A photometry is not supported */
1267 +        if (pmtype != PM_C && pmtype != PM_B) {
1268 +                fprintf(stderr, "dosource: unsupported photometric type (%d)\n",
1269 +                                pmtype);
1270 +                return(-1);
1271 +        }
1272 +
1273 +        /* Multiplier = the multiplier from the -m option, times the
1274 +         * multiplier from the IES file, times the ballast factor,
1275 +         * times the "ballast lamp photometric factor," (pfactor)
1276 +         * which was part of the 1986 and 1991 standards. In the 1995
1277 +         * and 2002 standards, it is always supposed to be 1 and in
1278 +         * the 2019 standard it encodes information about the source
1279 +         * of the file.  For those files, pfactor is set to 1.0,
1280 +         * above.  */
1281 +        sinf->mult = multiplier*mult*bfactor*pfactor;
1282 +
1283 +        /* If the count of angles is wrong, raise an error and quit. */
1284          if (nangles[0] < 2 || nangles[1] < 1) {
1285                  fprintf(stderr, "dosource: too few measured angles\n");
1286                  return(-1);
1287          }
1288 +
1289 +        /* For internal computation, convert units to meters. */
1290          if (unitype == U_FEET) {
1291                  width *= F_M;
1292                  length *= F_M;
1293                  height *= F_M;
1294          }
1295 <        if (makeshape(&srcshape, width, length, height) != 0) {
1296 <                fprintf(stderr, "dosource: illegal source dimensions");
1295 >
1296 >        /* Make decisions about the shape of the light source
1297 >         * geometry, and store them in sinf. */
1298 >        if (makeshape(sinf, width, length, height) != 0) {
1299 >                fprintf(stderr, "dosource: illegal source dimensions\n");
1300                  return(-1);
1301          }
1302 <        if ((datout = fopen(fullname(buf,name,T_DST), "w")) == NULL) {
1302 >        /* If any warning messages were generated by makeshape(), output them */
1303 >        if ((sinf->warn) != NULL)
1304 >                fputs(sinf->warn, stderr);
1305 >
1306 >        /* Copy the candela values into a Radiance data file. */
1307 >        if ((datout = fopen(fullnam(buf,name,T_DST), "w")) == NULL) {
1308                  perror(buf);
1309                  return(-1);
1310          }
1311          if (cvdata(in, datout, 2, nangles, 1./WHTEFFICACY, bounds) != 0) {
1312                  fprintf(stderr, "dosource: bad distribution data\n");
1313                  fclose(datout);
1314 <                unlink(fullname(buf,name,T_DST));
1314 >                unlink(fullnam(buf,name,T_DST));
1315                  return(-1);
1316          }
1317          fclose(datout);
1318 <        fprintf(out, "# %g watt luminaire, lamp*ballast factor = %g\n",
1318 >
1319 >        /* Output explanatory comment */
1320 >        fprintf(out, "\n# %g watt luminaire, lamp*ballast factor = %g\n",
1321                          wattage, bfactor*pfactor);
1322 +        if (sinf->iesshape >= 0)
1323 +                fprintf(out, "# IES file shape = %s\n",
1324 +                        IESHAPENAMES[sinf->iesshape]);
1325 +        else
1326 +                fprintf(out, "# IES file shape overridden\n");
1327 +        fprintf(out, "# Radiance geometry shape = %s\n",
1328 +                RADSHAPENAMES[sinf->type - 1]);
1329 +        if (sinf->warn != NULL)
1330 +                fpcomment(out, "# ", sinf->warn);
1331 +
1332 +        /* Output distribution "brightdata" primitive. Start handling
1333 +           the various cases of symmetry of the distribution.  This
1334 +           code reflects the complexity of the LM-63 format, as
1335 +           described under "<horizontal angles>" in the various
1336 +           versions of the standard. */
1337          strcat(strcpy(id, filename(name)), "_dist");
1338 <        fprintf(out, "\n%s brightdata %s\n", mod, id);
1338 >        fprintf(out, "\n'%s' brightdata '%s'\n", mod, id);
1339          if (nangles[1] < 2)
1340 +                 /* if it's a radially-symmetric type C distribution */
1341                  fprintf(out, "4 ");
1342          else if (pmtype == PM_B)
1343 +                /* Photometry type B */
1344                  fprintf(out, "5 ");
1345          else if (FEQ(bounds[1][0],90.) && FEQ(bounds[1][1],270.))
1346 <                fprintf(out, "8 ");
1346 >                /* Symmetric around the 90-270 degree plane */
1347 >                fprintf(out, "7 ");
1348          else
1349 <                fprintf(out, "6 ");
1350 <        fprintf(out, "%s %s source.cal ",
1351 <                        srcshape.type==SPHERE ? "corr" : "flatcorr",
1349 >                /* Just regular type C photometry */
1350 >                fprintf(out, "5 ");
1351 >
1352 >        /* If the generated source geometry will be a box, a flat
1353 >         * rectangle, or a disk figure out if it needs a top, a
1354 >         * bottom, and/or sides. */
1355 >        dolower = (bounds[0][0] < 90.-FTINY); /* Smallest vertical angle */
1356 >        doupper = (bounds[0][1] > 90.+FTINY); /* Largest vertical angle */
1357 >        dosides = (doupper & dolower && sinf->h > MINDIM); /* Sides */
1358 >
1359 >        /* Select the appropriate function and parameters from source.cal */
1360 >        fprintf(out, "%s '%s' source.cal ",
1361 >                        sinf->type==SPHERE ? "corr" :
1362 >                        !dosides ? "flatcorr" :
1363 >                        sinf->type==DISK ? "cylcorr" : "boxcorr",
1364                          libname(buf,name,T_DST));
1365          if (pmtype == PM_B) {
1366 +                /* Type B photometry */
1367                  if (FEQ(bounds[1][0],0.))
1368 +                        /* laterally symmetric around a vertical plane */
1369                          fprintf(out, "srcB_horiz2 ");
1370                  else
1371                          fprintf(out, "srcB_horiz ");
1372                  fprintf(out, "srcB_vert ");
1373 <        } else {
1373 >        } else /* pmtype == PM_C */ {
1374                  if (nangles[1] >= 2) {
1375 +                        /* Not radially symmetric */
1376                          d1 = bounds[1][1] - bounds[1][0];
1377                          if (d1 <= 90.+FTINY)
1378 +                                /* Data for a quadrant */
1379                                  fprintf(out, "src_phi4 ");
1380 <                        else if (d1 <= 180.+FTINY)
1381 <                                fprintf(out, "src_phi2 ");
1382 <                        else
1380 >                        else if (d1 <= 180.+FTINY) {
1381 >                                /* Data for a hemisphere */
1382 >                                if (FEQ(bounds[1][0],90.))
1383 >                                        fprintf(out, "src_phi2+90 ");
1384 >                                else
1385 >                                        fprintf(out, "src_phi2 ");
1386 >                        } else  /* Data for a whole sphere */
1387                                  fprintf(out, "src_phi ");
1388 <                        fprintf(out, "src_theta -my ");
1388 >                        fprintf(out, "src_theta ");
1389 >                        /* For the hemisphere around the 90-270 degree plane */
1390                          if (FEQ(bounds[1][0],90.) && FEQ(bounds[1][1],270.))
1391                                  fprintf(out, "-rz -90 ");
1392 <                } else
1392 >                } else          /* Radially symmetric */
1393                          fprintf(out, "src_theta ");
1394          }
1395 <        fprintf(out, "\n0\n1 %g\n", multiplier*mult*bfactor*pfactor);
1396 <        if (putsource(&srcshape, out, id, filename(name),
1397 <                        bounds[0][0]<90., bounds[0][1]>90.) != 0)
1395 >        /* finish the brightdata primitive with appropriate data */
1396 >        if (!dosides || sinf->type == SPHERE)
1397 >                fprintf(out, "\n0\n1 %g\n", sinf->mult/sinf->area);
1398 >        else if (sinf->type == DISK)
1399 >                fprintf(out, "\n0\n3 %g %g %g\n", sinf->mult,
1400 >                                sinf->w, sinf->h);
1401 >        else
1402 >                fprintf(out, "\n0\n4 %g %g %g %g\n", sinf->mult,
1403 >                                sinf->l, sinf->w, sinf->h);
1404 >        /* Brightdata primitive written out. */
1405 >
1406 >        /* Finally, output the descriptions of the actual radiant
1407 >         * surfaces. */
1408 >        if (putsource(sinf, out, id, filename(name),
1409 >                        dolower, doupper, dosides) != 0)
1410                  return(-1);
1411          return(0);
1412   }
1413  
1414 <
1415 < putsource(shp, fp, mod, name, dolower, doupper)         /* put out source */
1416 < SHAPE   *shp;
1417 < FILE    *fp;
1418 < char    *mod, *name;
1419 < int     dolower, doupper;
1414 > /* putsource - output the actual light emitting geometry
1415 > *
1416 > * Three kinds of geometry are produced: rectangles and boxes, disks
1417 > * ("ring" primitive, but the radius of the hole is always zero) and
1418 > * cylinders, and spheres.
1419 > */
1420 > int
1421 > putsource(
1422 >        SRCINFO *shp,
1423 >        FILE    *fp,
1424 >        char    *mod,
1425 >        char    *name,
1426 >        int     dolower,
1427 >        int     doupper,
1428 >        int     dosides
1429 > )
1430   {
1431 <        char    buf[MAXWORD];
1432 <        
1433 <        fprintf(fp, "\n%s %s %s_light\n", mod,
1434 <                        illumrad>=MINDIM/2. ? "illum" : "light",
1435 <                        name);
1431 >        char    lname[RMAXWORD];
1432 >
1433 >        /* First, describe the light. If a materials and geometry
1434 >         * file is given, generate an illum instead. */
1435 >        strcat(strcpy(lname, name), "_light");
1436 >        fprintf(fp, "\n'%s' %s '%s'\n", mod,
1437 >                        shp->isillum ? "illum" : "light", lname);
1438          fprintf(fp, "0\n0\n3 %g %g %g\n",
1439 <                        lampcolor[0]/shp->area,
617 <                        lampcolor[1]/shp->area,
618 <                        lampcolor[2]/shp->area);
619 <        if (doupper && dolower && shp->type != SPHERE && shp->h > MINDIM) {
620 <                fprintf(fp, "\n%s glow %s_glow\n", mod, name);
621 <                fprintf(fp, "0\n0\n4 %g %g %g 0\n",
622 <                                lampcolor[0]/shp->area,
623 <                                lampcolor[1]/shp->area,
624 <                                lampcolor[2]/shp->area);
625 <        }
1439 >                        lampcolor[0], lampcolor[1], lampcolor[2]);
1440          switch (shp->type) {
1441          case RECT:
1442 <                strcat(strcpy(buf, name), "_light");
1442 >                /* Output at least one rectangle. If light is radiated
1443 >                 * from the sides of the luminaire, output rectangular
1444 >                 * sides as well. */
1445                  if (dolower)
1446 <                        putrectsrc(shp, fp, buf, name, 0);
1446 >                        putrectsrc(shp, fp, lname, name, 0);
1447                  if (doupper)
1448 <                        putrectsrc(shp, fp, buf, name, 1);
1449 <                if (doupper && dolower && shp->h > MINDIM) {
1450 <                        strcat(strcpy(buf, name), "_glow");
635 <                        putsides(shp, fp, buf, name);
636 <                }
1448 >                        putrectsrc(shp, fp, lname, name, 1);
1449 >                if (dosides)
1450 >                        putsides(shp, fp, lname, name);
1451                  break;
1452          case DISK:
1453 <                strcat(strcpy(buf, name), "_light");
1453 >                /* Output at least one disk. If light is radiated from
1454 >                 * the sides of luminaire, output a cylinder as well. */
1455                  if (dolower)
1456 <                        putdisksrc(shp, fp, buf, name, 0);
1456 >                        putdisksrc(shp, fp, lname, name, 0);
1457                  if (doupper)
1458 <                        putdisksrc(shp, fp, buf, name, 1);
1459 <                if (doupper && dolower && shp->h > MINDIM) {
1460 <                        strcat(strcpy(buf, name), "_glow");
646 <                        putcyl(shp, fp, buf, name);
647 <                }
1458 >                        putdisksrc(shp, fp, lname, name, 1);
1459 >                if (dosides)
1460 >                        putcyl(shp, fp, lname, name);
1461                  break;
1462          case SPHERE:
1463 <                strcat(strcpy(buf, name), "_light");
1464 <                putspheresrc(shp, fp, buf, name);
1463 >                /* Output a sphere. */
1464 >                putspheresrc(shp, fp, lname, name);
1465                  break;
1466          }
1467          return(0);
1468   }
1469  
1470 <
1471 < makeshape(shp, width, length, height)           /* make source shape */
1472 < register SHAPE  *shp;
1473 < double  width, length, height;
1470 > /* makeshape -- decide what shape will be used
1471 > *
1472 > * Makeshape decides what Radiance geometry will be used to represent
1473 > * the light source and stores information about it in shp.
1474 > *
1475 > * The height, width, and length parameters are values from the
1476 > * IES file, given in meters.
1477 > *
1478 > * The various versions of the IES LM-63 standard give a "luminous
1479 > * opening" (really a crude shape) a width, a length (or depth), and a
1480 > * height.  If all three values are positive, they describe a box.  If
1481 > * they are all zero, they describe a point.  Various combinations of
1482 > * negative values are used to denote disks, circular or elliptical
1483 > * cylinders, spheres, and ellipsoids.  This encoding differs from
1484 > * version to version of LM-63.
1485 > *
1486 > * Ies2rad simplifies this, reducing the geometry of LM-63 files to
1487 > * three forms which can be easily represented by Radiance primitives:
1488 > * boxes (RECT), cylinders or disks (DISK), and spheres (SPHERE.)  A
1489 > * point is necessarily represented by a small sphere, since a point
1490 > * is not a Radiance object.
1491 > *
1492 > * Makeshape() returns 0 if it succeeds in choosing a shape, and -1 if
1493 > * it fails.
1494 > *
1495 > */
1496 > int
1497 > makeshape(
1498 >        SRCINFO *shp,
1499 >        double  width,
1500 >        double  length,
1501 >        double  height
1502 > )
1503   {
1504 <        if (illumrad >= MINDIM/2.) {
1504 >        int rc;
1505 >        
1506 >        if (illumrad != 0.0)
1507 >                rc = makeillumsphere(shp);
1508 >        else
1509 >                rc = makeiesshape(shp, length, width, height);
1510 >        if (rc == SUCCESS)
1511 >                shapearea(shp);
1512 >        return rc;
1513 > }
1514 >
1515 > /*
1516 > * Return 1 if d < 0, 2 if d == 0, 3 if d > 0.  This is used to encode
1517 > * the signs of IES file dimensions for quick lookup.  As usual with
1518 > * macros, don't use an expression with side effects as an argument.
1519 > */
1520 > #define CONVSGN(d) ((d) < 0 ? 1 : ((d) == 0 ? 2 : 3))
1521 >
1522 > /* makeiesshape - convert IES shape to Radiance shape
1523 > *
1524 > * Some 34 cases in the various versions of the IES LM-63 standard are
1525 > * handled, though some only by approximation.  For each case which is
1526 > * processed a Radiance box, cylinder, or sphere is selected.
1527 > *
1528 > * Shapes are categorized by version year of the standard and the
1529 > * signs of the LM-63 length, width (depth), and height fields.  These
1530 > * are combined and converted to an integer, which is then used as the
1531 > * argument to switch().  The last two digits of the IES file version
1532 > * year are used and the signs of length, width, and height are
1533 > * encoded, in that order, as 1 for negative, 2 for zero, and 3 for
1534 > * positive.  These are then combined into a numeric key by the
1535 > * following formula:
1536 > *
1537 > *   version * 1000 + sgn(length) * 100 + sgn(width) * 10 + sgn(height).
1538 > *
1539 > * Since the 1991 version uses the same encoding as the 1986 version,
1540 > * and the 2019 version uses the same encoding as the 2002 version,
1541 > * these are collapsed into the earlier years.
1542 > *
1543 > * In the cases of the switch() statement, further processing takes
1544 > * place. Circles and ellipses are distinguished by comparisons.  Then
1545 > * routines are called to fill out the fields of the shp structure.
1546 > *
1547 > * As per the conventions of the rest of ies2rad, makeiesshape()
1548 > * returns 0 on success and -1 on failure.  -1 reflects an error in
1549 > * the IES file and is unusual.
1550 > *
1551 > * By convention, the shape generating routines are always given
1552 > * positive values for dimensions and always succeed; all errors are
1553 > * caught before they are called.  The absolute values of all three
1554 > * dimensions are calculated at the beginning of makeiesshape() and
1555 > * used throughout the function, this has a low cost and eliminates
1556 > * the chance of sign errors.
1557 > *
1558 > * There is one extension to the ies standard here, devised to
1559 > * accomdate wall-mounted fixtures; vertical rectangles, not formally
1560 > * supported by any version of LM-63, are treated as boxes.
1561 > *
1562 > * The code is complicated by the way that earlier versions of the
1563 > * standard (1986 and 1991) prioritize width in their discussions, and
1564 > * later versions prioritize length.  It is not always clear which to
1565 > * write first and there is hesitation between the older code which
1566 > * invokes makeiesshape() and makeiesshape() itself.
1567 > */
1568 > int
1569 > makeiesshape(SRCINFO *shp, double l, double w, double h) {
1570 >        int rc = SUCCESS;
1571 >        int shape = IESNONE;
1572 >        /* Get the last two digits of the standard year  */
1573 >        int ver = shp->filerev % 100;
1574 >        /* Make positive versions of all dimensions, for clarity in
1575 >         * function calls.  If you like, read this as l', w', and h'. */
1576 >        double lp = fabs(l), wp = fabs(w), hp = fabs(h);
1577 >        int thumbprint;
1578 >
1579 >        /* Change 1991 into 1986 and 2019 in 2002 */
1580 >        switch (ver) {
1581 >        case 91:
1582 >                ver = 86;
1583 >                break;
1584 >        case 19:
1585 >                ver = 02;
1586 >                break;
1587 >        }
1588 >        
1589 >        thumbprint =
1590 >                ver * 1000 + CONVSGN(l) * 100 + CONVSGN(w) * 10 + CONVSGN(h);
1591 >        switch(thumbprint) {
1592 >        case 86222: case 95222: case 2222:
1593 >                shp->iesshape = IESPT;
1594                  shp->type = SPHERE;
1595 <                shp->w = shp->l = shp->h = 2.*illumrad;
1596 <        } else if (width < MINDIM) {
1597 <                width = -width;
1598 <                if (width < MINDIM) {
1599 <                        shp->type = SPHERE;
1600 <                        shp->w = shp->l = shp->h = MINDIM;
1601 <                } else if (height < .5*width) {
1602 <                        shp->type = DISK;
1603 <                        shp->w = shp->l = width;
1604 <                        if (height >= MINDIM)
1605 <                                shp->h = height;
1606 <                        else
1607 <                                shp->h = .5*MINDIM;
1608 <                } else {
1609 <                        shp->type = SPHERE;
1610 <                        shp->w = shp->l = shp->h = width;
1595 >                shp->w = shp->l = shp->h = MINDIM;
1596 >                break;
1597 >        case 86332: case 95332: case 2332:
1598 >                shp->iesshape = IESRECT;
1599 >                makeboxshape(shp, lp, wp, hp);
1600 >                break;
1601 >        case 86333: case 86233: case 86323:
1602 >        case 95333: case 95233: case 95323:
1603 >        case 2333: case 2233: case 2323:
1604 >                shp->iesshape = IESBOX;
1605 >                makeboxshape(shp, lp, wp, hp);
1606 >                break;
1607 >        case 86212: case 95212:
1608 >                shp->iesshape = IESDISK;
1609 >                makecylshape(shp, wp, hp);
1610 >                break;
1611 >        case 86213:
1612 >                shp->iesshape = IESVCYL;
1613 >                makecylshape(shp, wp, hp);
1614 >                break;
1615 >        case 86312:
1616 >                shp->iesshape = IESELLIPSE;
1617 >                makeecylshape(shp, lp, wp, 0);
1618 >                break;
1619 >        case 86313:
1620 >                shp->iesshape = IESELLIPSOID;
1621 >                makeelshape(shp, wp, lp, hp);
1622 >                break;
1623 >        case 95211:
1624 >                shp->iesshape = FEQ(lp,hp) ? IESSPHERE : IESNONE;
1625 >                if (shp->iesshape == IESNONE) {
1626 >                        shp->warn = "makeshape: cannot determine shape\n";
1627 >                        rc = FAIL;
1628 >                        break;
1629                  }
1630 <        } else {
1631 <                shp->type = RECT;
1632 <                shp->w = width;
1633 <                if (length >= MINDIM)
1634 <                        shp->l = length;
1630 >                shp->type = SPHERE;
1631 >                shp->w = shp->l = shp->h = wp;
1632 >                break;
1633 >        case 95213:
1634 >                shp->iesshape = IESVCYL;
1635 >                makecylshape(shp, wp, hp);
1636 >                break;
1637 >        case 95321:
1638 >                shp->iesshape = IESHCYL_PH;
1639 >                shp->warn = "makeshape: shape is a horizontal cylinder, which is not supported.\nmakeshape: replaced with box\n";
1640 >                makeboxshape(shp, lp, wp, hp);
1641 >                break;
1642 >        case 95231:
1643 >                shp->iesshape = IESHCYL_PPH;
1644 >                shp->warn = "makeshape: shape is a horizontal cylinder, which is not supported.\nmakeshape: replaced with box\n";
1645 >                makeboxshape(shp, lp, wp, hp);
1646 >                break;
1647 >        case 95133: case 95313:
1648 >                shp->iesshape = IESVECYL;
1649 >                makeecylshape(shp, lp, wp, hp);
1650 >                break;
1651 >        case 95131: case 95311:
1652 >                shp->iesshape = IESELLIPSOID;
1653 >                makeelshape(shp, lp, wp, hp);
1654 >                break;
1655 >        case 2112:
1656 >                shp->iesshape = FEQ(l,w) ? IESDISK : IESELLIPSE;
1657 >                if (shp->iesshape == IESDISK)
1658 >                        makecylshape(shp, wp, hp);
1659                  else
1660 <                        shp->l = MINDIM;
1661 <                if (height >= MINDIM)
1662 <                        shp->h = height;
1660 >                        makeecylshape(shp, wp, lp, hp);
1661 >                break;
1662 >        case 2113:
1663 >                shp->iesshape = FEQ(l,w) ? IESVCYL : IESVECYL;
1664 >                if (shp->iesshape == IESVCYL)
1665 >                        makecylshape(shp, wp, hp);
1666                  else
1667 <                        shp->h = .5*MINDIM;
1667 >                        makeecylshape(shp, wp, lp, hp);
1668 >                break;
1669 >        case 2111:
1670 >                shp->iesshape = FEQ(l,w) && FEQ(l,h) ? IESSPHERE : IESELLIPSOID;
1671 >                if (shp->iesshape == IESSPHERE) {
1672 >                        shp->type = SPHERE;
1673 >                        shp->w = shp->l = shp->h = wp;
1674 >                }
1675 >                else
1676 >                        makeelshape(shp, lp, wp, hp);
1677 >                break;
1678 >        case 2311:
1679 >                shp->iesshape = FEQ(w,h) ? IESHCYL_PH : IESHECYL_PH;
1680 >                shp->warn = "makeshape: shape is a horizontal cylinder, which is not supported.\nmakeshape: replaced with box\n";
1681 >                makeboxshape(shp, lp, wp, hp);
1682 >                break;
1683 >        case 2131:
1684 >                shp->iesshape = FEQ(l,h) ? IESHCYL_PPH : IESHECYL_PPH;
1685 >                shp->warn = "makeshape: shape is a horizontal cylinder, which is not supported.\nmakeshape: replaced with box\n";
1686 >                makeboxshape(shp, lp, wp, hp);
1687 >                break;
1688 >        case 2121:
1689 >                shp->iesshape = FEQ(w,h) ? IESVDISK_PH : IESVEL_PH;
1690 >                shp->warn = "makeshape: shape is a vertical ellipse, which is not supported.\nmakeshape: replaced with rectangle\n";
1691 >                makeboxshape(shp, lp, wp, hp);
1692 >                break;
1693 >        default:
1694 >                /* We don't recognize the shape - report an error. */
1695 >                rc = FAIL;
1696          }
1697 +        return rc;
1698 + }
1699 +
1700 + /* makeillumsphere - create an illum sphere */
1701 + int
1702 + makeillumsphere(SRCINFO *shp) {
1703 +        /* If the size is too small or negative, error. */
1704 +        if (illumrad/meters2out < MINDIM/2.) {
1705 +                fprintf(stderr, "makeillumsphere: -i argument is too small or negative\n");
1706 +                return FAIL;
1707 +        }
1708 +        shp->isillum = 1;
1709 +        shp->type = SPHERE;
1710 +        shp->w = shp->l = shp->h = 2.*illumrad / meters2out;
1711 +        return SUCCESS;
1712 + }
1713 +
1714 + /* makeboxshape - create a box */
1715 + void
1716 + makeboxshape(SRCINFO *shp, double l, double w, double h) {
1717 +        shp->type = RECT;
1718 +        shp->l = fmax(l, MINDIM);
1719 +        shp->w = fmax(w, MINDIM);
1720 +        shp->h = fmax(h, .5*MINDIM);
1721 + }
1722 +
1723 + /* makecylshape - output a vertical cylinder or disk
1724 + *
1725 + * If the shape has no height, make it a half-millimeter.
1726 + */
1727 + void
1728 + makecylshape(SRCINFO *shp, double diam, double height) {
1729 +        shp->type = DISK;
1730 +        shp->w = shp->l = diam;
1731 +        shp->h = fmax(height, .5*MINDIM);
1732 + }
1733 +
1734 + /* makeelshape - create a substitute for an ellipsoid
1735 + *
1736 + * Because we don't actually support ellipsoids, and they don't seem
1737 + * to be common in actual IES files.
1738 + */
1739 + void
1740 + makeelshape(SRCINFO *shp, double w, double l, double h) {
1741 +        float avg = (w + l + h) / 3;
1742 +        float bot = .5 * avg;
1743 +        float top = 1.5 * avg;
1744 +
1745 +        if (bot < w && w < top
1746 +            && bot < l && l < top
1747 +            && bot < h && h > top) {
1748 +                /* it's sort of spherical, replace it with a sphere */
1749 +                shp->warn = "makeshape: shape is an ellipsoid, which is not supported.\nmakeshape: replaced with sphere\n";
1750 +                shp->type = SPHERE;
1751 +                shp->w = shp->l = shp->h = avg;
1752 +        } else if (bot < w && w < top
1753 +                   && bot < l && l < top
1754 +                   && h <= .5*MINDIM) {
1755 +                /* It's flat and sort of circular, replace it
1756 +                 * with a disk. */
1757 +                shp->warn = "makeshape: shape is an ellipse, which is not supported.\nmakeshape: replaced with disk\n";
1758 +                makecylshape(shp, w, 0);
1759 +        } else {
1760 +                shp->warn = "makeshape: shape is an ellipsoid, which is not supported.\nmakeshape: replaced with box\n";
1761 +                makeboxshape(shp, w, l, h);
1762 +        }
1763 + }
1764 +
1765 + /* makeecylshape - create a substitute for an elliptical cylinder or disk */
1766 + void
1767 + makeecylshape(SRCINFO *shp, double l, double w, double h) {
1768 +        float avg = (w + l) / 2;
1769 +        float bot = .5 * avg;
1770 +        float top = 1.5 * avg;
1771 +
1772 +        if (bot < w && w < top
1773 +                   && bot < l && l < top) {
1774 +                /* It's sort of circular, replace it
1775 +                 * with a circular cylinder. */
1776 +                shp->warn = "makeshape: shape is a vertical elliptical cylinder, which is not supported.\nmakeshape: replaced with circular cylinder\n";
1777 +                makecylshape(shp, w, h);
1778 +        } else {
1779 +                shp->warn = "makeshape: shape is a  vertical elliptical cylinder, which is not supported.\nmakeshape: replaced with box\n";
1780 +                makeboxshape(shp, w, l, h);
1781 +        }
1782 + }
1783 +
1784 + void
1785 + shapearea(SRCINFO *shp) {
1786          switch (shp->type) {
1787          case RECT:
1788                  shp->area = shp->w * shp->l;
# Line 699 | Line 1792 | double width, length, height;
1792                  shp->area = PI/4. * shp->w * shp->w;
1793                  break;
1794          }
1795 <        return(0);
703 < }
1795 > }      
1796  
1797 + /* Rectangular or box-shaped light source.
1798 + *
1799 + * putrectsrc, putsides, putrect, and putpoint are used to output the
1800 + * Radiance description of a box.  The box is centered on the origin
1801 + * and has the dimensions given in the IES file.  The coordinates
1802 + * range from [-1/2*length, -1/2*width, -1/2*height] to [1/2*length,
1803 + * 1/2*width, 1/2*height].
1804 + *
1805 + * The location of the point is encoded in the low-order three bits of
1806 + * an integer. If the integer is p, then: bit 0 is (p & 1),
1807 + * representing length (x), bit 1 is (p & 2) representing width (y),
1808 + * and bit 2 is (p & 4), representing height (z).
1809 + *
1810 + * Looking down from above (towards -z), the vertices of the box or
1811 + * rectangle are numbered so:
1812 + *
1813 + *     2,6                                        3,7
1814 + *        +--------------------------------------+
1815 + *        |                                      |
1816 + *        |                                      |
1817 + *        |                                      |
1818 + *        |                                      |
1819 + *        +--------------------------------------+
1820 + *     0,4                                        1,5
1821 + *
1822 + * The higher number of each pair is above the x-y plane (positive z),
1823 + * the lower number is below the x-y plane (negative z.)
1824 + *
1825 + */
1826  
1827 < putrectsrc(shp, fp, mod, name, up)              /* rectangular source */
1828 < SHAPE   *shp;
1829 < FILE    *fp;
1830 < char    *mod, *name;
1831 < int     up;
1827 > /* putrecsrc - output a rectangle parallel to the x-y plane
1828 > *
1829 > * Putrecsrc calls out the vertices of a rectangle parallel to the x-y
1830 > * plane.  The order of the vertices is different for the upper and
1831 > * lower rectangles of a box, since a right-hand rule based on the
1832 > * order of the vertices is used to determine the surface normal of
1833 > * the rectangle, and the surface normal determines the direction the
1834 > * light radiated by the rectangle.
1835 > *
1836 > */
1837 > void
1838 > putrectsrc(
1839 >        SRCINFO *shp,
1840 >        FILE    *fp,
1841 >        char    *mod,
1842 >        char    *name,
1843 >        int     up
1844 > )
1845   {
1846          if (up)
1847                  putrect(shp, fp, mod, name, ".u", 4, 5, 7, 6);
# Line 715 | Line 1849 | int    up;
1849                  putrect(shp, fp, mod, name, ".d", 0, 2, 3, 1);
1850   }
1851  
1852 <
1853 < putsides(shp, fp, mod, name)                    /* put out sides of box */
1854 < register SHAPE  *shp;
1855 < FILE    *fp;
1856 < char    *mod, *name;
1852 > /* putsides - put out sides of box */
1853 > void
1854 > putsides(
1855 >        SRCINFO *shp,
1856 >        FILE    *fp,
1857 >        char    *mod,
1858 >        char    *name
1859 > )
1860   {
1861          putrect(shp, fp, mod, name, ".1", 0, 1, 5, 4);
1862          putrect(shp, fp, mod, name, ".2", 1, 3, 7, 5);
1863          putrect(shp, fp, mod, name, ".3", 3, 2, 6, 7);
1864          putrect(shp, fp, mod, name, ".4", 2, 0, 4, 6);
1865   }
729        
1866  
1867 < putrect(shp, fp, mod, name, suffix, a, b, c, d) /* put out a rectangle */
1868 < SHAPE   *shp;
1869 < FILE    *fp;
1870 < char    *mod, *name, *suffix;
1871 < int     a, b, c, d;
1867 > /* putrect - put out a rectangle
1868 > *
1869 > * putrect generates the "polygon" primitive which describes a
1870 > * rectangle.
1871 > */
1872 > void
1873 > putrect(
1874 >        SRCINFO *shp,
1875 >        FILE    *fp,
1876 >        char    *mod,
1877 >        char    *name,
1878 >        char    *suffix,
1879 >        int     a,
1880 >        int b,
1881 >        int c,
1882 >        int d
1883 > )
1884   {
1885 <        fprintf(fp, "\n%s polygon %s%s\n0\n0\n12\n", mod, name, suffix);
1885 >        fprintf(fp, "\n'%s' polygon '%s%s'\n0\n0\n12\n", mod, name, suffix);
1886          putpoint(shp, fp, a);
1887          putpoint(shp, fp, b);
1888          putpoint(shp, fp, c);
1889          putpoint(shp, fp, d);
1890   }
1891  
1892 <
1893 < putpoint(shp, fp, p)                            /* put out a point */
1894 < register SHAPE  *shp;
1895 < FILE    *fp;
1896 < int     p;
1892 > /* putpoint -- output a the coordinates of a vertex
1893 > *
1894 > * putpoint maps vertex numbers to coordinates and outputs the
1895 > * coordinates.
1896 > */
1897 > void
1898 > putpoint(
1899 >        SRCINFO *shp,
1900 >        FILE    *fp,
1901 >        int     p
1902 > )
1903   {
1904          static double   mult[2] = {-.5, .5};
1905  
# Line 755 | Line 1909 | int    p;
1909                          mult[p>>2]*shp->h*meters2out);
1910   }
1911  
1912 + /* End of routines to output a box-shaped light source */
1913  
1914 < putdisksrc(shp, fp, mod, name, up)              /* put out a disk source */
1915 < register SHAPE  *shp;
1916 < FILE    *fp;
1917 < char    *mod, *name;
1918 < int     up;
1914 > /* Routines to output a cylindrical or disk shaped light source
1915 > *
1916 > * As with other shapes, the light source is centered on the origin.
1917 > * The "ring" and "cylinder" primitives are used.
1918 > *
1919 > */
1920 > void
1921 > putdisksrc(             /* put out a disk source */
1922 >        SRCINFO *shp,
1923 >        FILE    *fp,
1924 >        char    *mod,
1925 >        char    *name,
1926 >        int     up
1927 > )
1928   {
1929          if (up) {
1930 <                fprintf(fp, "\n%s ring %s.u\n", mod, name);
1930 >                fprintf(fp, "\n'%s' ring '%s.u'\n", mod, name);
1931                  fprintf(fp, "0\n0\n8\n");
1932                  fprintf(fp, "\t0 0 %g\n", .5*shp->h*meters2out);
1933                  fprintf(fp, "\t0 0 1\n");
1934                  fprintf(fp, "\t0 %g\n", .5*shp->w*meters2out);
1935          } else {
1936 <                fprintf(fp, "\n%s ring %s.d\n", mod, name);
1936 >                fprintf(fp, "\n'%s' ring '%s.d'\n", mod, name);
1937                  fprintf(fp, "0\n0\n8\n");
1938                  fprintf(fp, "\t0 0 %g\n", -.5*shp->h*meters2out);
1939                  fprintf(fp, "\t0 0 -1\n");
# Line 778 | Line 1942 | int    up;
1942   }
1943  
1944  
1945 < putcyl(shp, fp, mod, name)                      /* put out a cylinder */
1946 < register SHAPE  *shp;
1947 < FILE    *fp;
1948 < char    *mod, *name;
1945 > void
1946 > putcyl(                 /* put out a cylinder */
1947 >        SRCINFO *shp,
1948 >        FILE    *fp,
1949 >        char    *mod,
1950 >        char    *name
1951 > )
1952   {
1953 <        fprintf(fp, "\n%s cylinder %s.c\n", mod, name);
1953 >        fprintf(fp, "\n'%s' cylinder '%s.c'\n", mod, name);
1954          fprintf(fp, "0\n0\n7\n");
1955          fprintf(fp, "\t0 0 %g\n", .5*shp->h*meters2out);
1956          fprintf(fp, "\t0 0 %g\n", -.5*shp->h*meters2out);
1957          fprintf(fp, "\t%g\n", .5*shp->w*meters2out);
1958   }
1959  
1960 + /* end of of routines to output cylinders and disks */
1961  
1962 < putspheresrc(shp, fp, mod, name)                /* put out a sphere source */
1963 < SHAPE   *shp;
1964 < FILE    *fp;
1965 < char    *mod, *name;
1962 > void
1963 > putspheresrc(           /* put out a sphere source */
1964 >        SRCINFO *shp,
1965 >        FILE    *fp,
1966 >        char    *mod,
1967 >        char    *name
1968 > )
1969   {
1970 <        fprintf(fp, "\n%s sphere %s.s\n", mod, name);
1970 >        fprintf(fp, "\n'%s' sphere '%s.s'\n", mod, name);
1971          fprintf(fp, "0\n0\n4 0 0 0 %g\n", .5*shp->w*meters2out);
1972   }
1973  
1974 <
1975 < cvdata(in, out, ndim, npts, mult, lim)          /* convert data */
1976 < FILE    *in, *out;
1977 < int     ndim, npts[];
1978 < double  mult, lim[][2];
1974 > /* cvdata - convert LM-63 tilt and candela data to Radiance brightdata format
1975 > *
1976 > * The files created by this routine are intended for use with the Radiance
1977 > * "brightdata" material type.
1978 > *
1979 > * Two types of data are converted; one-dimensional tilt data, which
1980 > * is given in polar coordinates, and two-dimensional candela data,
1981 > * which is given in spherical co-ordinates.
1982 > *
1983 > * Return 0 for success, -1 for failure.
1984 > *
1985 > */
1986 > int
1987 > cvdata(
1988 >        FILE    *in,            /* Input file */
1989 >        FILE    *out,           /* Output file */
1990 >        int     ndim,           /* Number of dimensions; 1 for
1991 >                                 * tilt data, 2 for photometric data. */
1992 >        int     npts[],         /* Number of points in each dimension */
1993 >        double  mult,           /* Multiple each value by this
1994 >                                 * number. For tilt data, always
1995 >                                 * 1. For candela values, the
1996 >                                 * efficacy of white Radiance light.  */
1997 >        double  lim[][2]        /* The range of angles in each dimension. */
1998 > )
1999   {
2000 <        register double *pt[4];
2001 <        register int    i, j;
2000 >        double  *pt[4];         /* Four is the expected maximum of ndim. */
2001 >        int     i, j;
2002          double  val;
2003          int     total;
2004  
2005 +        /* Calculate and output the number of data values */
2006          total = 1; j = 0;
2007          for (i = 0; i < ndim; i++)
2008                  if (npts[i] > 1) {
# Line 818 | Line 2010 | double mult, lim[][2];
2010                          j++;
2011                  }
2012          fprintf(out, "%d\n", j);
2013 <                                        /* get coordinates */
2013 >
2014 >        /* Read in the angle values, and note the first and last in
2015 >         * each dimension, if there is a place to store them. In the
2016 >         * case of tilt data, there is only one list of angles. In the
2017 >         * case of candela values, vertical angles appear first, and
2018 >         * horizontal angles occur second. */
2019          for (i = 0; i < ndim; i++) {
2020 +                /* Allocate space for the angle values. */
2021                  pt[i] = (double *)malloc(npts[i]*sizeof(double));
2022                  for (j = 0; j < npts[i]; j++)
2023 <                        fscanf(in, "%lf", &pt[i][j]);
2023 >                        if (!scnflt(in, &pt[i][j]))
2024 >                                return(-1);
2025                  if (lim != NULL) {
2026                          lim[i][0] = pt[i][0];
2027                          lim[i][1] = pt[i][npts[i]-1];
2028                  }
2029          }
2030 <                                        /* write out in reverse */
2030 >
2031 >        /* Output the angles. If this is candela data, horizontal
2032 >         * angles output first. There are two cases: the first where
2033 >         * the angles are evenly spaced, the second where they are
2034 >         * not.
2035 >         *
2036 >         * When the angles are evenly spaced, three numbers are
2037 >         * output: the first angle, the last angle, and the number of
2038 >         * angles.  When the angles are not evenly spaced, instead
2039 >         * zero, zero, and the count of angles is given, followed by a
2040 >         * list of angles.  In this case, angles are output four to a line.
2041 >         */
2042          for (i = ndim-1; i >= 0; i--) {
2043                  if (npts[i] > 1) {
2044 +                        /* Determine if the angles are evenly spaces */
2045                          for (j = 1; j < npts[i]-1; j++)
2046                                  if (!FEQ(pt[i][j]-pt[i][j-1],
2047                                                  pt[i][j+1]-pt[i][j]))
2048                                          break;
2049 +                        /* If they are, output the first angle, the
2050 +                         * last angle, and a count */
2051                          if (j == npts[i]-1)
2052                                  fprintf(out, "%g %g %d\n", pt[i][0], pt[i][j],
2053                                                  npts[i]);
2054                          else {
2055 +                                /* otherwise, output 0, 0, and a
2056 +                                 * count, followed by the list of
2057 +                                 * angles, one to a line. */
2058                                  fprintf(out, "0 0 %d", npts[i]);
2059                                  for (j = 0; j < npts[i]; j++) {
2060                                          if (j%4 == 0)
# Line 848 | Line 2064 | double mult, lim[][2];
2064                                  putc('\n', out);
2065                          }
2066                  }
2067 <                free((char *)pt[i]);
2067 >                /* Free the storage containing the angle values. */
2068 >                free((void *)pt[i]);
2069          }
2070 +
2071 +        /* Finally, read in the data values (candela or multiplier values,
2072 +         * depending on the part of the file) and output them four to
2073 +         * a line. */
2074          for (i = 0; i < total; i++) {
2075                  if (i%4 == 0)
2076                          putc('\n', out);
2077 <                if (fscanf(in, "%lf", &val) != 1)
2077 >                if (!scnflt(in, &val))
2078                          return(-1);
2079                  fprintf(out, "\t%g", val*mult);
2080          }
2081          putc('\n', out);
2082          return(0);
2083   }
2084 +
2085 + /* getword - get an LM-63 delimited word from fp
2086 + *
2087 + * Getword gets a word from an IES file delimited by either white
2088 + * space or a comma surrounded by white space. A pointer to the word
2089 + * is returned, which will persist only until getword is called again.
2090 + * At EOF, return NULL instead.
2091 + *
2092 + */
2093 + char *
2094 + getword(                        /* scan a word from fp */
2095 +        FILE    *fp
2096 + )
2097 + {
2098 +        static char     wrd[RMAXWORD];
2099 +        char    *cp;
2100 +        int     c;
2101 +
2102 +        /* Skip initial spaces */
2103 +        while (isspace(c=getc(fp)))
2104 +                ;
2105 +        /* Get characters to a delimiter or until wrd is full */
2106 +        for (cp = wrd; c != EOF && cp < wrd+RMAXWORD-1;
2107 +                        *cp++ = c, c = getc(fp))
2108 +                if (isspace(c) || c == ',') {
2109 +                        /* If we find a delimiter */
2110 +                        /* Gobble up whitespace */
2111 +                        while (isspace(c))
2112 +                                c = getc(fp);
2113 +                        /* If it's not a comma, put the first
2114 +                         * character of the next data item back */
2115 +                        if ((c != EOF) & (c != ','))
2116 +                                ungetc(c, fp);
2117 +                        /* Close out the strimg */
2118 +                        *cp = '\0';
2119 +                        /* return it */
2120 +                        return(wrd);
2121 +                }
2122 +        /* If we ran out of space or are at the end of the file,
2123 +         * return either the word or NULL, as appropriate. */
2124 +        *cp = '\0';
2125 +        return(cp > wrd ? wrd : NULL);
2126 + }
2127 +
2128 + /* cvtint - convert an IES word to an integer
2129 + *
2130 + * A pointer to the word is passed in wrd; ip is expected to point to
2131 + * an integer.  cvtint() will silently truncate a floating point value
2132 + * to an integer; "1", "1.0", and "1.5" will all return 1.
2133 + *
2134 + * cvtint() returns 0 if it fails, 1 if it succeeds.
2135 + */
2136 + int
2137 + cvtint(
2138 +        int     *ip,
2139 +        char    *wrd
2140 + )
2141 + {
2142 +        if (wrd == NULL || !isint(wrd))
2143 +                return(0);
2144 +        *ip = atoi(wrd);
2145 +        return(1);
2146 + }
2147 +
2148 +
2149 + /* cvtflt - convert an IES word to a double precision floating-point number
2150 + *
2151 + * A pointer to the word is passed in wrd; rp is expected to point to
2152 + * a double.
2153 + *
2154 + * cvtflt returns 0 if it fails, 1 if it succeeds.
2155 + */
2156 + int
2157 + cvtflt(
2158 +        double  *rp,
2159 +        char    *wrd
2160 + )
2161 + {
2162 +        if (wrd == NULL || !isflt(wrd))
2163 +                return(0);
2164 +        *rp = atof(wrd);
2165 +        return(1);
2166 + }
2167 +
2168 + /* cvgeometry - process materials and geometry format luminaire data
2169 + *
2170 + * The materials and geometry format (MGF) for describing luminaires
2171 + * was a part of Radiance that was first adopted and then retracted by
2172 + * the IES as part of LM-63.  It provides a way of describing
2173 + * luminaire geometry similar to the Radiance scene description
2174 + * format.
2175 + *
2176 + * cvgeometry() generates an mgf2rad command and then, if "-g" is given
2177 + * on the command line, an oconv command, both of which are then
2178 + * executed with the system() function.
2179 + *
2180 + * The generated commands are:
2181 + *   mgf2rad -e <multiplier> -g <size> <mgf_filename> \
2182 + *     | xform -s <scale_factor> \
2183 + *     >> <luminare_scene_description_file
2184 + * or:
2185 + *   mgf2rad -e <multiplier> -g <size> <mgf_filename> \
2186 + *     oconv - > <instance_filename>
2187 + */
2188 + int
2189 + cvgeometry(
2190 +        char    *inpname,
2191 +        SRCINFO *sinf,
2192 +        char    *outname,
2193 +        FILE    *outfp                  /* close output file upon return */
2194 + )
2195 + {
2196 +        char    buf[256];
2197 +        char    *cp;
2198 +
2199 +        if (inpname == NULL || !inpname[0]) {   /* no geometry file */
2200 +                fclose(outfp);
2201 +                return(0);
2202 +        }
2203 +        putc('\n', outfp);
2204 +        strcpy(buf, "mgf2rad ");                /* build mgf2rad command */
2205 +        cp = buf+8;
2206 +        if (!FEQ(sinf->mult, 1.0)) {
2207 +                /* if there's an output multiplier, include in the
2208 +                 * mgf2rad command */
2209 +                sprintf(cp, "-e %f ", sinf->mult);
2210 +                cp += strlen(cp);
2211 +        }
2212 +        /* Include the glow distance for the geometry */
2213 +        sprintf(cp, "-g %f %s ",
2214 +                sqrt(sinf->w*sinf->w + sinf->h*sinf->h + sinf->l*sinf->l),
2215 +                        inpname);
2216 +        cp += strlen(cp);
2217 +        if (instantiate) {              /* instantiate octree */
2218 +                /* If "-g" is given on the command line, include an
2219 +                 * "oconv" command in the pipe. */
2220 +                strcpy(cp, "| oconv - > ");
2221 +                cp += 12;
2222 +                fullnam(cp,outname,T_OCT);
2223 +                /* Only update if the input file is newer than the
2224 +                 * output file */
2225 +                if (fdate(inpname) > fdate(outname) &&
2226 +                                system(buf)) {          /* create octree */
2227 +                        fclose(outfp);
2228 +                        return(-1);
2229 +                }
2230 +                /* Reference the instance file in the scene description */
2231 +                fprintf(outfp, "void instance %s_inst\n", outname);
2232 +                /* If the geometry isn't in meters, scale it appropriately. */
2233 +                if (!FEQ(meters2out, 1.0))
2234 +                        fprintf(outfp, "3 %s -s %f\n",
2235 +                                        libname(buf,outname,T_OCT),
2236 +                                        meters2out);
2237 +                else
2238 +                        fprintf(outfp, "1 %s\n", libname(buf,outname,T_OCT));
2239 +                /* Close off the "instance" primitive. */
2240 +                fprintf(outfp, "0\n0\n");
2241 +                /* And the Radiance scene description. */
2242 +                fclose(outfp);
2243 +        } else {                        /* else append to luminaire file */
2244 +                if (!FEQ(meters2out, 1.0)) {    /* apply scalefactor */
2245 +                        sprintf(cp, "| xform -s %f ", meters2out);
2246 +                        cp += strlen(cp);
2247 +                }
2248 +                if (!out2stdout) {
2249 +                        fclose(outfp);
2250 +                        strcpy(cp, ">> ");      /* append works for DOS? */
2251 +                        cp += 3;
2252 +                        fullnam(cp,outname,T_RAD);
2253 +                }
2254 +                if (system(buf))
2255 +                        return(-1);
2256 +        }
2257 +        return(0);
2258 + }
2259 +
2260 + /* Set up emacs indentation */
2261 + /* Local Variables: */
2262 + /*   c-file-style: "bsd" */
2263 + /* End: */
2264 +
2265 + /* For vim, use ":set tabstop=8 shiftwidth=8" */

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines