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.27 by greg, Sat Aug 1 23:27:04 2015 UTC vs.
Revision 2.28 by greg, Wed May 30 22:12:17 2018 UTC

# Line 2 | Line 2
2   static const char       RCSid[] = "$Id$";
3   #endif
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. [??? Greg, is there a
24 + * source for this convention?] This system matches none of the usual
25 + * goniophotometric conventions, but it is closest to IES type C; V in
26 + * type C photometry is θ in Radiance and L is -φ.
27 + *
28 + * The ies2rad scene description for a luminaire LUM, with tilt data,
29 + * uses the following Radiance scene description primitives:
30 + *
31 + *     void brightdata LUM_tilt
32 + *     …
33 + *     LUM_tilt brightdata LUM_dist
34 + *     …
35 + *     LUM_dist light LUM_light
36 + *     …
37 + *     LUM_light surface1 name1
38 + *     …
39 + *     LUM_light surface2 name2
40 + *     …
41 + *     LUM_light surface_n name_n
42 + *
43 + * Without tilt data, the primitives are:
44 + *
45 + *     void brightdata LUM_dist
46 + *     …
47 + *     LUM_dist light LUM_light
48 + *     …
49 + *     LUM_light surface1 name1
50 + *     …
51 + *     LUM_light surface2 name2
52 + *     …
53 + *     LUM_light surface_n name_n
54 + *
55 + * As many surfaces are given as required to describe the light
56 + * source. Illum may be used rather than light so that a visible form
57 + * (impostor) may be given to the luminaire, rather than a simple
58 + * glowing shape. If an impostor is provided, it must be wholly
59 + * contained within the illum and if it provides impostor light
60 + * sources, those must be given with glow, so that they do not
61 + * themselves illuminate the scene, providing incorrect results.
62 + *
63 + * The ies2rad code uses the "bsd" style. For emacs, this is set up
64 + * automatically in the "Local Variables" section at the end of the
65 + * file. For vim, use ":set tabstop=8 shiftwidth=8".
66 + *
67   *      07Apr90         Greg Ward
68   *
69   *  Fixed correction factor for flat sources 29Oct2001 GW
70 + *  Extensive comments added by Randolph Fritz May2018
71   */
72  
73   #include <stdio.h>
# Line 20 | Line 81 | static const char      RCSid[] = "$Id$";
81   #include "paths.h"
82  
83   #define PI              3.14159265358979323846
84 <                                        /* floating comparisons */
84 >
85 > /* floating comparisons -- floating point numbers within FTINY of each
86 > * other are considered equal */
87   #define FTINY           1e-6
88   #define FEQ(a,b)        ((a)<=(b)+FTINY&&(a)>=(b)-FTINY)
89 <                                        /* keywords */
89 >
90 >
91 > /* IESNA LM-63 keywords and constants */
92 > /* Since 1991, LM-63 files have begun with the magic keyword IESNA */
93   #define MAGICID         "IESNA"
94   #define LMAGICID        5
95 + /* ies2rad supports the 1986, 1991, and 1995 versions of
96 + * LM-63. FIRSTREV describes the first version; LASTREV describes the
97 + * 1995 version. */
98   #define FIRSTREV        86
99   #define LASTREV         95
100  
101 < #define D86             0               /* keywords defined in LM-63-1986 */
101 > /* The following definitions support LM-63 file keyword reading and
102 > * analysis.
103 > *
104 > * This section defines two function-like macros: keymatch(i,s), which
105 > * checks to see if keyword i matches string s, and checklamp(s),
106 > * which checks to see if a string matches the keywords "LAMP" or
107 > * "LAMPCAT".
108 > *
109 > * LM-63-1986 files begin with a list of free-form label lines.
110 > * LM-63-1991 files begin with the identifying line "IESNA91" followed
111 > * by a list of formatted keywords.  LM-63-1995 files begin with the
112 > * identifying line "IESNA:LM-63-1995" followed by a list of formatted
113 > * keywords.
114 > *
115 > * The K_* #defines enumerate the keywords used in the different
116 > * versions of the file and give them symbolic names.
117 > *
118 > * The D86, D91, and D95 #defines validate the keywords in the 1986,
119 > * 1991, and 1995 versions of the standard, one bit per keyword.
120 > * Since the 1986 standard does not use keywords, D86 is zero.  The
121 > * 1991 standard has 13 keywords, and D91 has the lower 13 bits set.
122 > * The 1995 standard has 14 keywords, and D95 has the lower 14 bits
123 > * set.
124 > *
125 > */
126 > #define D86             0
127  
128   #define K_TST           0
129   #define K_MAN           1
# Line 45 | Line 139 | static const char      RCSid[] = "$Id$";
139   #define K_BLK           11
140   #define K_EBK           12
141  
142 < #define D91             ((1L<<13)-1)    /* keywords defined in LM-63-1991 */
142 > /* keywords defined in LM-63-1991 */
143 > #define D91             ((1L<<13)-1)
144  
145   #define K_LMG           13
146  
147 < #define D95             ((1L<<14)-1)    /* keywords defined in LM-63-1995 */
147 > /* keywords defined in LM-63-1995 */
148 > #define D95             ((1L<<14)-1)
149  
150   char    k_kwd[][20] = {"TEST", "MANUFAC", "LUMCAT", "LUMINAIRE", "LAMPCAT",
151                          "LAMP", "BALLAST", "MAINTCAT", "OTHER", "SEARCH",
# Line 65 | Line 161 | int    filerev = FIRSTREV;
161   #define checklamp(s)    (!(k_defined[filerev-FIRSTREV]&(1<<K_LMP|1<<K_LPC)) ||\
162                                  keymatch(K_LMP,s) || keymatch(K_LPC,s))
163  
164 <                                        /* tilt specs */
164 > /* tilt specs
165 > *
166 > * This next series of definitions address metal-halide lamps, which
167 > * change their brightness depending on the angle at which they are
168 > * mounted. The section begins with "TILT=".  The constants in this
169 > * section are all defined in LM-63.
170 > *
171 > */
172 >
173   #define TLTSTR          "TILT="
174   #define TLTSTRLEN       5
175   #define TLTNONE         "NONE"
# Line 73 | Line 177 | int    filerev = FIRSTREV;
177   #define TLT_VERT        1
178   #define TLT_H0          2
179   #define TLT_H90         3
180 <                                        /* photometric types */
180 >
181 > /* Constants from LM-63 files */
182 >
183 > /* photometric types
184 > *
185 > * This enumeration reflects three different methods of measuring the
186 > * distribution of light from a luminaire -- "goniophotometry" -- and
187 > * the different coordinate systems related to these
188 > * goniophotometers.  All are described in IES standard LM-75-01.
189 > * Earlier and shorter descriptions may be found the LM-63 standards
190 > * from 1986, 1991, and 1995.
191 > *
192 > * ies2rad does not support type A photometry.
193 > *
194 > * In the 1986 file format, LM-63-86, 1 is used for type C and type A
195 > * photometric data.
196 > *
197 > */
198   #define PM_C            1
199   #define PM_B            2
200   #define PM_A            3
201 <                                        /* unit types */
201 >
202 > /* unit types */
203   #define U_FEET          1
204   #define U_METERS        2
205 <                                        /* string lengths */
206 < #define MAXLINE         132
205 >
206 > /* string lengths */
207 > /* Maximum input line is 132 characters including CR LF at end. */
208 > #define MAXLINE         133
209   #define RMAXWORD        76
210 <                                        /* file types */
210 >
211 > /* End of LM-63-related #defines */
212 >
213 > /* file extensions */
214   #define T_RAD           ".rad"
215   #define T_DST           ".dat"
216   #define T_TLT           "%.dat"
217   #define T_OCT           ".oct"
218 <                                        /* shape types */
218 >
219 > /* shape types
220 > * These #defines enumerate the shapes of the Radiance objects which
221 > * emit the light.
222 > */
223   #define RECT            1
224   #define DISK            2
225   #define SPHERE          3
226  
227 < #define MINDIM          .001            /* minimum dimension (point source) */
227 > /* The diameter of a point source luminaire model. Also the minimum
228 > * size (in meters) that the luminous opening of a luminaire must have
229 > * to be treated as other than a point source. */
230 > #define MINDIM          .001
231  
232 < #define F_M             .3048           /* feet to meters */
232 > /* feet to meters */
233 > /* length_in_meters = length_in_feet * F_M */
234 > #define F_M             .3048
235  
236 + /* abspath - return true if a path begins with a directory separator
237 + * or a '.' (current directory) */
238   #define abspath(p)      (ISDIRSEP((p)[0]) || (p)[0] == '.')
239  
240 + /* Global variables.
241 + *
242 + * Mostly, these are a way of communicating command line parameters to
243 + * the rest of the program.
244 + */
245   static char     default_name[] = "default";
246  
247   char    *libdir = NULL;                 /* library directory location */
# Line 116 | Line 259 | int    out2stdout = 0;                 /* put out to stdout r.t. file *
259   int     instantiate = 0;                /* instantiate geometry */
260   double  illumrad = 0.0;                 /* radius for illum sphere */
261  
262 + /* This struct describes the Radiance source object */
263   typedef struct {
264          int     isillum;                        /* do as illum */
265          int     type;                           /* RECT, DISK, SPHERE */
# Line 124 | Line 268 | typedef struct {
268          double  area;                           /* max. projected area */
269   } SRCINFO;                              /* a source shape (units=meters) */
270  
271 < int     gargc;                          /* global argc (minus filenames) */
271 > /* A count and pointer to the list of input file names */
272 > int     gargc;                          /* global argc */
273   char    **gargv;                        /* global argv */
274  
275 <
275 > /* macros to scan numbers out of IES files
276 > *
277 > * fp is a file pointer.  scnint() places the number in the integer
278 > * indicated by ip; scnflt() places the number in the double indicated
279 > * by rp. The macros return 1 if successful, 0 if not.
280 > *
281 > */
282   #define scnint(fp,ip)   cvtint(ip,getword(fp))
283   #define scnflt(fp,rp)   cvtflt(rp,getword(fp))
133 #define isint           isflt                   /* IES allows real as integer */
284  
285 + /* The original (1986) version of LM-63 allows decimals points in
286 + * integers, so that, for instance, the number of lamps may be written
287 + * 3.0 (the number, obviously, must still be an integer.) This
288 + * confusing define accommodates that.  */
289 + #define isint           isflt
290  
291 + /* Function declarations */
292   static int ies2rad(char *inpname, char *outname);
293   static void initlamps(void);
294   static int dosource(SRCINFO *sinf, FILE *in, FILE *out, char *mod, char *name);
# Line 160 | Line 316 | static char * libname(char *path, char *fname, char *s
316   static char * getword(FILE *fp);
317   static char * fullnam(char *path, char *fname, char *suffix);
318  
319 <
319 > /* main - process arguments and run the conversion
320 > *
321 > * Refer to the man page for details of the arguments.
322 > *
323 > * Following Unix environment conventions, main() exits with 0 on
324 > * success and 1 on failure.
325 > *
326 > * ies2rad outputs either two or three files for a given IES
327 > * file. There is always a .rad file containing Radiance scene
328 > * description primitives and a .dat file for the photometric data. If
329 > * tilt data is given, that is placed in a separate .dat file.  So
330 > * ies2rad must have a filename to operate. Sometimes this name is the
331 > * input file name, shorn of its extension; sometimes it is given in
332 > * the -o option. But an output file name is required for ies2rad to
333 > * do its work.
334 > *
335 > * Older versions of the LM-63 standard allowed inclusion of multiple
336 > * luminaires in one IES file; this is not supported by ies2rad.
337 > *
338 > * This code sometimes does not check to make sure it has not run out
339 > * of arguments; this can lead to segmentation faults and perhaps
340 > * other errors.
341 > *
342 > */
343   int
344   main(
345          int     argc,
# Line 172 | Line 351 | main(
351          char    outname[RMAXWORD];
352          double  d1;
353          int     i;
354 <        
354 >
355 >        /* Scan the options */
356          for (i = 1; i < argc && argv[i][0] == '-'; i++)
357                  switch (argv[i][1]) {
358                  case 'd':               /* dimensions */
# Line 243 | Line 423 | main(
423                  case 'i':               /* illum */
424                          illumrad = atof(argv[++i]);
425                          break;
426 <                case 'g':               /* instatiate geometry? */
426 >                case 'g':               /* instantiate geometry? */
427                          instantiate = !instantiate;
428                          break;
429                  case 't':               /* override lamp type */
# Line 266 | Line 446 | main(
446                                          argv[0], argv[i]);
447                          exit(1);
448                  }
449 +        /* Save pointers to the list of input file names */
450          gargc = i;
451          gargv = argv;
452 <        initlamps();                    /* get lamp data (if needed) */
453 <                                        /* convert ies file(s) */
452 >
453 >        /* get lamp data (if needed) */
454 >        initlamps();
455 >
456 >        /* convert ies file(s) */
457 >        /* If an output file name is specified */
458          if (outfile != NULL) {
459                  if (i == argc)
460 +                        /* If no input filename is given, use stdin as
461 +                         * the source for the IES file */
462                          exit(ies2rad(NULL, outfile) == 0 ? 0 : 1);
463                  else if (i == argc-1)
464 +                        /* If exactly one input file name is given, use it. */
465                          exit(ies2rad(argv[i], outfile) == 0 ? 0 : 1);
466                  else
467 <                        goto needsingle;
467 >                        goto needsingle; /* Otherwise, error. */
468          } else if (i >= argc) {
469 +                /* If an output file and an input file are not give, error. */
470                  fprintf(stderr, "%s: missing output file specification\n",
471                                  argv[0]);
472                  exit(1);
473          }
474 +        /* If no input or output file is given, error. */
475          if (out2stdout && i != argc-1)
476                  goto needsingle;
477 +        /* Otherwise, process each input file in turn. */
478          status = 0;
479          for ( ; i < argc; i++) {
480                  tailtrunc(strcpy(outname,filename(argv[i])));
# Line 296 | Line 487 | needsingle:
487          exit(1);
488   }
489  
490 + /* Initlamps -- If necessary, read lamp data table */
491   void
492   initlamps(void)                         /* set up lamps */
493   {
494          float   *lcol;
495          int     status;
496  
497 +        /* If the lamp name is set to default, don't bother to read
498 +         * the lamp data table. */
499          if (lamptype != NULL && !strcmp(lamptype, default_name) &&
500                          deflamp == NULL)
501 <                return;                         /* no need for data */
502 <                                                /* else load file */
503 <        if ((status = loadlamps(lampdat)) < 0)
504 <                exit(1);
501 >                return;
502 >
503 >        if ((status = loadlamps(lampdat)) < 0) /* Load the lamp data table */
504 >                exit(1);                       /* Exit if problems
505 >                                                * with the file. */
506          if (status == 0) {
507 +                /* If can't open the file, just use the standard default lamp */
508                  fprintf(stderr, "%s: warning - no lamp data\n", lampdat);
509                  lamptype = default_name;
510                  return;
511          }
512 <        if (deflamp != NULL) {                  /* match default type */
512 >        if (deflamp != NULL) {
513 >                /* Look up the specified default lamp type */
514                  if ((lcol = matchlamp(deflamp)) == NULL)
515 +                        /* If it can't be found, use the default */
516                          fprintf(stderr,
517                                  "%s: warning - unknown default lamp type\n",
518                                          deflamp);
519                  else
520 +                        /* Use the selected default lamp color */
521                          copycolor(defcolor, lcol);
522          }
523 <        if (lamptype != NULL) {                 /* match selected type */
523 >        /* If a lamp type is specified and can be found, use it, and
524 >         * release the lamp data table memory; it won't be needed any more. */
525 >        if (lamptype != NULL) {
526                  if (strcmp(lamptype, default_name)) {
527                          if ((lcol = matchlamp(lamptype)) == NULL) {
528                                  fprintf(stderr,
# Line 333 | Line 534 | initlamps(void)                                /* set up lamps */
534                  }
535                  freelamps();                    /* all done with data */
536          }
537 <                                                /* else keep lamp data */
537 >        /* else keep lamp data */
538   }
539  
540 + /*
541 + * File path operations
542 + *
543 + * These provide file path operations that operate on both MS-Windows
544 + * and *nix. They will ignore and pass, but will not necessarily
545 + * process correctly, Windows drive letters. Paths including Windows
546 + * UNC network names (\\server\folder\file) may also cause problems.
547 + *
548 + */
549  
550 + /*
551 + * stradd()
552 + *
553 + * Add a string to the end of a string, optionally concatenating a
554 + * file path separator character.  If the path already ends with a
555 + * path separator, no additional separator is appended.
556 + *
557 + */
558   char *
559   stradd(                 /* add a string at dst */
560          char    *dst,
# Line 355 | Line 573 | stradd(                        /* add a string at dst */
573          return(dst);
574   }
575  
576 <
576 > /*
577 > * fullnam () - return a usable path name for an output file
578 > */
579   char *
580 < fullnam(                /* return full path name */
581 <        char    *path,
582 <        char    *fname,
583 <        char    *suffix
580 > fullnam(
581 >        char    *path,          /* The base directory path */
582 >        char    *fname,         /* The file name */
583 >        char    *suffix         /* A suffix, which usually contains
584 >                                 * a file name extension. */
585   )
586   {
587 +        extern char *prefdir;
588 +        extern char *libdir;
589 +
590          if (prefdir != NULL && abspath(prefdir))
591 +                /* If the subdirectory path is absolute or '.', just
592 +                 * concatenate the names together */
593                  libname(path, fname, suffix);
594          else if (abspath(fname))
595 +                /* If there is no subdirectory, and the file name is
596 +                 * an absolute path or '.', concatenate the path,
597 +                 * filename, and suffix. */
598                  strcpy(stradd(path, fname, 0), suffix);
599          else
600 +                /* If the file name is relative, concatenate path,
601 +                 * library directory, directory separator, file name,
602 +                 * and suffix.  */
603                  libname(stradd(path, libdir, DIRSEP), fname, suffix);
604  
605          return(path);
606   }
607  
608  
609 + /*
610 + * libname - convert a file name to a path
611 + */
612   char *
613 < libname(                /* return library relative name */
614 <        char    *path,
615 <        char    *fname,
616 <        char    *suffix
613 > libname(
614 >        char    *path,          /* The base directory path */
615 >        char    *fname,         /* The file name */
616 >        char    *suffix         /* A suffix, which usually contains
617 >                                 * a file name extension. */
618   )
619   {
620 +        extern char *prefdir;   /* The subdirectory where the file
621 +                                 * name is stored. */
622 +
623          if (abspath(fname))
624 +                /* If the file name begins with '/' or '.', combine
625 +                 * it with the path and attach the suffix */
626                  strcpy(stradd(path, fname, 0), suffix);
627          else
628 +                /* If the file name is relative, attach it to the
629 +                 * path, include the subdirectory, and append the suffix. */
630                  strcpy(stradd(stradd(path, prefdir, DIRSEP), fname, 0), suffix);
631  
632          return(path);
633   }
634  
635 <
635 > /* filename - find the base file name in a buffer containing a path
636 > *
637 > * The pointer is to a character within the buffer, not a string in itself;
638 > * it will become invalid when the buffer is freed.
639 > *
640 > */
641   char *
642 < filename(                       /* get final component of pathname */
642 > filename(
643          char    *path
644   )
645   {
# Line 404 | Line 652 | filename(                      /* get final component of pathname */
652   }
653  
654  
655 + /* filetrunc() - return the directory portion of a path
656 + *
657 + * The path is passed in in a pointer to a buffer; a null character is
658 + * inserted in the buffer after the last directory separator
659 + *
660 + */
661   char *
662 < filetrunc(                              /* truncate filename at end of path */
662 > filetrunc(
663          char    *path
664   )
665   {
# Line 420 | Line 674 | filetrunc(                             /* truncate filename at end of path */
674          return(path);
675   }
676  
677 <
677 > /* tailtrunc() - trim a file name extension, if any.
678 > *
679 > * The file name is passed in in a buffer indicated by *name; the
680 > * period which begins the extension is replaced with a 0 byte.
681 > */
682   char *
683 < tailtrunc(                              /* truncate tail of filename */
683 > tailtrunc(
684          char    *name
685   )
686   {
687          char    *p1, *p2;
688  
689 +        /* Skip leading periods */
690          for (p1 = filename(name); *p1 == '.'; p1++)
691                  ;
692 +        /* Find the last period in a file name */
693          p2 = NULL;
694          for ( ; *p1; p1++)
695                  if (*p1 == '.')
696                          p2 = p1;
697 +        /* If present, trim the filename at that period */
698          if (p2 != NULL)
699                  *p2 = '\0';
700          return(name);
701   }
702  
703 <
703 > /* blanktrunc() - trim spaces at the end of a string
704 > *
705 > * the string is passed in a character array, which is modified
706 > */
707   void
708 < blanktrunc(                             /* truncate spaces at end of line */
708 > blanktrunc(
709          char    *s
710   )
711   {
# Line 454 | Line 718 | blanktrunc(                            /* truncate spaces at end of line */
718          *++cp = '\0';
719   }
720  
721 <
721 > /* k_match - return true if keyword matches header line */
722   int
723 < k_match(                        /* header line matches keyword? */
724 <        char    *kwd,
725 <        char    *hdl
723 > k_match(
724 >        char    *kwd,           /* keyword */
725 >        char    *hdl            /* header line */
726   )
727   {
728 +        /* The line has to begin with '[' */
729          if (*hdl++ != '[')
730                  return(0);
731 <        while (islower(*hdl) ? toupper(*hdl) == *kwd++ : *hdl == *kwd++)
731 >        /* case-independent keyword match */
732 >        while (toupper(*hdl) == *kwd++)
733                  if (!*hdl++)
734                          return(0);
735 +        /* If we have come to the end of the keyword, and the keyword
736 +         * at the beginning of the matched line is terminated with
737 +         * ']', return 1 */
738          return((!*kwd) & (*hdl == ']'));
739   }
740  
741 <
741 > /* keyargs - return the argument of a keyword, without leading spaces
742 > *
743 > * keyargs is passed a pointer to a buffer; it returns a pointer to
744 > * where the argument starts in the buffer
745 > *
746 > */
747   char *
748 < keyargs(                                /* return keyword arguments */
749 <        char    *hdl
748 > keyargs(
749 >        char    *hdl /* header line */
750   )
751   {
752          while (*hdl && *hdl++ != ']')
# Line 483 | Line 757 | keyargs(                               /* return keyword arguments */
757   }
758  
759  
760 + /* putheader - output the header of the .rad file
761 + *
762 + * Header is:
763 + *   # <file> <file> <file> (all files from input line)
764 + *   # Dimensions in [feet,meters,etc.]
765 + *
766 + * ??? Is listing all the input file names correct behavior?
767 + *
768 + */
769   void
770 < putheader(                              /* print header */
770 >
771 > putheader(
772          FILE    *out
773   )
774   {
775          int     i;
776 <        
776 >
777          putc('#', out);
778          for (i = 0; i < gargc; i++) {
779                  putc(' ', out);
# Line 500 | Line 784 | putheader(                             /* print header */
784          putc('\n', out);
785   }
786  
787 <
787 > /* ies2rad - convert an IES LM-63 file to a Radiance light source desc.
788 > *
789 > * Return -1 in case of failure, 0 in case of success.
790 > *
791 > * The file version recognition is confused and will treat 1995 and
792 > * 2002 version files as 1986 version files.
793 > *
794 > */
795   int
796   ies2rad(                /* convert IES file */
797          char    *inpname,
# Line 513 | Line 804 | ies2rad(               /* convert IES file */
804          FILE    *inpfp, *outfp;
805          int     lineno = 0;
806  
807 +        /* Open input and output files */
808          geomfile[0] = '\0';
809          srcinfo.isillum = 0;
810          if (inpname == NULL) {
# Line 529 | Line 821 | ies2rad(               /* convert IES file */
821                  fclose(inpfp);
822                  return(-1);
823          }
824 +
825 +        /* Output the output file header */
826          putheader(outfp);
827 +
828 +        /* If the lamp type wasn't given on the command line, mark
829 +         * the lamp color as missing */
830          if (lamptype == NULL)
831                  lampcolor = NULL;
832 +
833 +        /* Read the input file header, copying lines to the .rad file
834 +         * and looking for a lamp type. Stop at EOF or a line
835 +         * beginning with "TILT=". */
836          while (fgets(buf,sizeof(buf),inpfp) != NULL
837                          && strncmp(buf,TLTSTR,TLTSTRLEN)) {
838 <                blanktrunc(buf);
839 <                if (!buf[0])
838 >                blanktrunc(buf); /* Trim trailing spaces, CR, LF. */
839 >                if (!buf[0])     /* Skip blank lines */
840                          continue;
841 <                if (!lineno++ && !strncmp(buf, MAGICID, LMAGICID)) {
841 >                /* increment the header line count, and check for the
842 >                 * "TILT=" line that terminates the header */
843 >                if (!lineno++ && strncmp(buf, MAGICID, LMAGICID) == 0) {
844 >                        /* This code doesn't work for LM-63-95 and
845 >                         * LM-63-02 files and will instead default to
846 >                         * LM-63-86. */
847                          filerev = atoi(buf+LMAGICID);
848                          if (filerev < FIRSTREV)
849                                  filerev = FIRSTREV;
850                          else if (filerev > LASTREV)
851                                  filerev = LASTREV;
852                  }
853 +                /* Output the header line as a comment in the .rad file. */
854                  fputs("#<", outfp);
855                  fputs(buf, outfp);
856                  putc('\n', outfp);
857 +
858 +                /* If the header line is a keyword line (file version
859 +                 * later than 1986 and begins with '['), check a lamp
860 +                 * in the "[LAMP]" and "[LAMPCAT]" keyword lines;
861 +                 * otherwise check all lines.  */
862                  if (lampcolor == NULL && checklamp(buf))
863                          lampcolor = matchlamp( buf[0] == '[' ?
864                                                  keyargs(buf) : buf );
865 <                if (keymatch(K_LMG, buf)) {             /* geometry file */
865 >                /* Look for a materials and geometry file in the keywords. */
866 >                if (keymatch(K_LMG, buf)) {
867                          strcpy(geomfile, inpname);
868                          strcpy(filename(geomfile), keyargs(buf));
869                          srcinfo.isillum = 1;
870                  }
871          }
872 +
873 +        /* Done reading header information. If a lamp color still
874 +         * hasn't been found, print a warning and use the default
875 +         * color; if a lamp type hasn't been found, but a color has
876 +         * been specified, used the specified color. */
877          if (lampcolor == NULL) {
878                  fprintf(stderr, "%s: warning - no lamp type\n", inpname);
879                  fputs("# Unknown lamp type (used default)\n", outfp);
# Line 563 | Line 881 | ies2rad(               /* convert IES file */
881          } else if (lamptype == NULL)
882                  fprintf(outfp,"# CIE(x,y) = (%f,%f)\n# Depreciation = %.1f%%\n",
883                                  lampcolor[3], lampcolor[4], 100.*lampcolor[5]);
884 +        /* If the file ended before a "TILT=" line, that's an error. */
885          if (feof(inpfp)) {
886                  fprintf(stderr, "%s: not in IES format\n", inpname);
887                  goto readerr;
888          }
889 +
890 +        /* Process the tilt section of the file. */
891 +        /* Get the tilt file name, or the keyword "INCLUDE". */
892          atos(tltid, RMAXWORD, buf+TLTSTRLEN);
893          if (inpfp == stdin)
894                  buf[0] = '\0';
895          else
896                  filetrunc(strcpy(buf, inpname));
897 +        /* Process the tilt data. */
898          if (dotilt(inpfp, outfp, buf, tltid, outname, tltid) != 0) {
899                  fprintf(stderr, "%s: bad tilt data\n", inpname);
900                  goto readerr;
901          }
902 +
903 +        /* Process the luminaire data. */
904          if (dosource(&srcinfo, inpfp, outfp, tltid, outname) != 0) {
905                  fprintf(stderr, "%s: bad luminaire data\n", inpname);
906                  goto readerr;
907          }
908 +
909 +        /* Close the input file */
910          fclose(inpfp);
911 <                                        /* cvgeometry closes outfp */
911 >
912 >        /* Process an MGF file, if present. cvgeometry() closes outfp. */
913          if (cvgeometry(geomfile, &srcinfo, outname, outfp) != 0) {
914                  fprintf(stderr, "%s: bad geometry file\n", geomfile);
915                  return(-1);
916          }
917          return(0);
918 +
919   readerr:
920 +        /* If there is an error reading the file, close the input and
921 +         * .rad output files, and delete the .rad file, returning -1. */
922          fclose(inpfp);
923          fclose(outfp);
924          unlink(fullnam(buf,outname,T_RAD));
925          return(-1);
926   }
927  
928 <
928 > /* dotilt -- process tilt data
929 > *
930 > * Generate a brightdata primitive which describes the effect of
931 > * luminaire tilt on luminaire output and return its identifier in tltid.
932 > *
933 > * Tilt data (if present) is given as a number 1, 2, or 3, which
934 > * specifies the orientation of the lamp within the luminaire, a
935 > * number, n, of (angle, multiplier) pairs, followed by n angles and n
936 > * multipliers.
937 > *
938 > * returns 0 for success, -1 for error
939 > */
940   int
941 < dotilt( /* convert tilt data */
941 > dotilt(
942          FILE    *in,
943          FILE    *out,
944          char    *dir,
# Line 610 | Line 952 | dotilt(        /* convert tilt data */
952          char    buf[PATH_MAX], tltname[RMAXWORD];
953          FILE    *datin, *datout;
954  
955 +        /* Decide where the tilt data is; if the luminaire description
956 +         * doesn't have a tilt section, set the identifier to "void". */
957          if (!strcmp(tltspec, TLTNONE)) {
958 +                /* If the line is "TILT=NONE", set the input file
959 +                 * pointer to NULL and the identifier to "void". */
960                  datin = NULL;
961                  strcpy(tltid, "void");
962          } else if (!strcmp(tltspec, TLTINCL)) {
963 +                /* If the line is "TILT=INCLUDE" use the main IES
964 +                 * file as the source of tilt data. */
965                  datin = in;
966                  strcpy(tltname, dfltname);
967          } else {
968 +                /* If the line is "TILE=<filename>", use that file
969 +                 * name as the source of tilt data. */
970                  if (ISDIRSEP(tltspec[0]))
971                          strcpy(buf, tltspec);
972                  else
# Line 627 | Line 977 | dotilt(        /* convert tilt data */
977                  }
978                  tailtrunc(strcpy(tltname,filename(tltspec)));
979          }
980 +        /* If tilt data is present, read, process, and output it. */
981          if (datin != NULL) {
982 +                /* Try to open the output file */
983                  if ((datout = fopen(fullnam(buf,tltname,T_TLT),"w")) == NULL) {
984                          perror(buf);
985                          if (datin != in)
986                                  fclose(datin);
987                          return(-1);
988                  }
989 +                /* Try to copy the tilt data to the tilt data file */
990                  if (!scnint(datin,&tlt_type) || !scnint(datin,&nangles)
991                          || cvdata(datin,datout,1,&nangles,1.,minmax) != 0) {
992                          fprintf(stderr, "%s: data format error\n", tltspec);
# Line 646 | Line 999 | dotilt(        /* convert tilt data */
999                  fclose(datout);
1000                  if (datin != in)
1001                          fclose(datin);
1002 +
1003 +                /* Generate the identifier of the brightdata; the filename
1004 +                 * with "_tilt" appended. */
1005                  strcat(strcpy(tltid, filename(tltname)), "_tilt");
1006 +                /* Write out the brightdata primitive */
1007                  fprintf(out, "\nvoid brightdata %s\n", tltid);
1008                  libname(buf,tltname,T_TLT);
1009 +                /* Generate the tilt description */
1010                  switch (tlt_type) {
1011 <                case TLT_VERT:                  /* vertical */
1011 >                case TLT_VERT:
1012 >                        /* The lamp is mounted vertically; either
1013 >                         * base up or base down. */
1014                          fprintf(out, "4 noop %s tilt.cal %s\n", buf,
1015                                  minmax[0][1]>90.+FTINY ? "tilt_ang" : "tilt_ang2");
1016                          break;
1017 <                case TLT_H0:                    /* horiz. in 0 deg. plane */
1017 >                case TLT_H0:
1018 >                        /* The lamp is mounted horizontally and
1019 >                         * rotates but does not tilt when the
1020 >                         * luminaire is tilted. */
1021                          fprintf(out, "6 noop %s tilt.cal %s -rz 90\n", buf,
1022                          minmax[0][1]>90.+FTINY ? "tilt_xang" : "tilt_xang2");
1023                          break;
1024                  case TLT_H90:
1025 +                        /* The lamp is mounted horizontally, and
1026 +                         * tilts when the luminaire is tilted. */
1027                          fprintf(out, "4 noop %s tilt.cal %s\n", buf,
1028                          minmax[0][1]>90.+FTINY ? "tilt_xang" : "tilt_xang2");
1029                          break;
1030                  default:
1031 +                        /* otherwise, this is a bad IES file */
1032                          fprintf(stderr,
1033                                  "%s: illegal lamp to luminaire geometry (%d)\n",
1034                                  tltspec, tlt_type);
1035                          return(-1);
1036                  }
1037 +                /* And finally output the numbers of integer and real
1038 +                 * arguments, of which there are none. */
1039                  fprintf(out, "0\n0\n");
1040          }
1041          return(0);
1042   }
1043  
1044 <
1044 > /* dosource -- create the source and distribution primitives */
1045   int
1046 < dosource(       /* create source and distribution */
1046 > dosource(
1047          SRCINFO *sinf,
1048          FILE    *in,
1049          FILE    *out,
# Line 689 | Line 1057 | dosource(      /* create source and distribution */
1057          double  bounds[2][2];
1058          int     nangles[2], pmtype, unitype;
1059          double  d1;
1060 <        int     doupper, dolower, dosides;
1060 >        int     doupper, dolower, dosides;
1061  
1062 +        /* Read in the luminaire description header */
1063          if (!isint(getword(in)) || !isflt(getword(in)) || !scnflt(in,&mult)
1064                          || !scnint(in,&nangles[0]) || !scnint(in,&nangles[1])
1065                          || !scnint(in,&pmtype) || !scnint(in,&unitype)
# Line 700 | Line 1069 | dosource(      /* create source and distribution */
1069                  fprintf(stderr, "dosource: bad lamp specification\n");
1070                  return(-1);
1071          }
1072 +        /* Type A photometry is not supported */
1073          if (pmtype != PM_C && pmtype != PM_B) {
1074                  fprintf(stderr, "dosource: unsupported photometric type (%d)\n",
1075                                  pmtype);
1076                  return(-1);
1077          }
1078 +
1079 +        /* Multiplier = the multiplier from the -m option, times the
1080 +         * multiplier from the IES file, times the ballast factor,
1081 +         * times the "ballast lamp photometric factor," which was part
1082 +         * of the 1986 and 1991 standards. In the 1995 standard, it is
1083 +         * always supposed to be 1. */
1084          sinf->mult = multiplier*mult*bfactor*pfactor;
1085 +
1086 +        /* If the count of angles is wrong, raise an error and quit. */
1087          if (nangles[0] < 2 || nangles[1] < 1) {
1088                  fprintf(stderr, "dosource: too few measured angles\n");
1089                  return(-1);
1090          }
1091 +
1092 +        /* For internal computation, convert units to meters. */
1093          if (unitype == U_FEET) {
1094                  width *= F_M;
1095                  length *= F_M;
1096                  height *= F_M;
1097          }
1098 +
1099 +        /* Make decisions about the shape of the light source
1100 +         * geometry, and store them in sinf. */
1101          if (makeshape(sinf, width, length, height) != 0) {
1102                  fprintf(stderr, "dosource: illegal source dimensions");
1103                  return(-1);
1104          }
1105 +
1106 +        /* Copy the candela values into a Radiance data file. */
1107          if ((datout = fopen(fullnam(buf,name,T_DST), "w")) == NULL) {
1108                  perror(buf);
1109                  return(-1);
# Line 730 | Line 1115 | dosource(      /* create source and distribution */
1115                  return(-1);
1116          }
1117          fclose(datout);
1118 +
1119 +        /* Output explanatory comment */
1120          fprintf(out, "# %g watt luminaire, lamp*ballast factor = %g\n",
1121                          wattage, bfactor*pfactor);
1122 +        /* Output distribution "brightdata" primitive. Start handling
1123 +           the various cases of symmetry of the distribution. */
1124          strcat(strcpy(id, filename(name)), "_dist");
1125          fprintf(out, "\n%s brightdata %s\n", mod, id);
1126          if (nangles[1] < 2)
# Line 742 | Line 1131 | dosource(      /* create source and distribution */
1131                  fprintf(out, "7 ");
1132          else
1133                  fprintf(out, "5 ");
1134 <        dolower = (bounds[0][0] < 90.-FTINY);
1135 <        doupper = (bounds[0][1] > 90.+FTINY);
1136 <        dosides = (doupper & dolower && sinf->h > MINDIM);
1134 >
1135 >        /* If the generated source geometry will be a box, a flat
1136 >         * rectangle, or a disk figure out if it needs a top, a
1137 >         * bottom, and/or sides. */
1138 >        dolower = (bounds[0][0] < 90.-FTINY); /* Bottom */
1139 >        doupper = (bounds[0][1] > 90.+FTINY); /* Top */
1140 >        dosides = (doupper & dolower && sinf->h > MINDIM); /* Sides */
1141 >
1142 >        /* Select the appropriate function and parameters from source.cal */
1143          fprintf(out, "%s %s source.cal ",
1144                          sinf->type==SPHERE ? "corr" :
1145                          !dosides ? "flatcorr" :
# Line 774 | Line 1169 | dosource(      /* create source and distribution */
1169                  } else
1170                          fprintf(out, "src_theta ");
1171          }
1172 +        /* finish the brightdata primitive with appropriate data */
1173          if (!dosides || sinf->type == SPHERE)
1174                  fprintf(out, "\n0\n1 %g\n", sinf->mult/sinf->area);
1175          else if (sinf->type == DISK)
# Line 782 | Line 1178 | dosource(      /* create source and distribution */
1178          else
1179                  fprintf(out, "\n0\n4 %g %g %g %g\n", sinf->mult,
1180                                  sinf->l, sinf->w, sinf->h);
1181 +        /* Brightdata primitive written out. */
1182 +
1183 +        /* Finally, output the descriptions of the actual radiant
1184 +         * surfaces. */
1185          if (putsource(sinf, out, id, filename(name),
1186                          dolower, doupper, dosides) != 0)
1187                  return(-1);
1188          return(0);
1189   }
1190  
1191 <
1191 > /* putsource - output the actual light emitting geometry
1192 > *
1193 > * Three kinds of geometry are produced: rectangles and boxes, disks
1194 > * ("ring" primitive, but the radius of the hole is always zero) and
1195 > * cylinders, and spheres.
1196 > */
1197   int
1198 < putsource( /* put out source */
1198 > putsource(
1199          SRCINFO *shp,
1200          FILE    *fp,
1201          char    *mod,
1202          char    *name,
1203          int     dolower,
1204          int     doupper,
1205 <        int dosides
1205 >        int     dosides
1206   )
1207   {
1208          char    lname[RMAXWORD];
1209 <        
1209 >
1210 >        /* First, describe the light. If a materials and geometry
1211 >         * file is given, generate an illum instead. */
1212          strcat(strcpy(lname, name), "_light");
1213          fprintf(fp, "\n%s %s %s\n", mod,
1214                          shp->isillum ? "illum" : "light", lname);
# Line 809 | Line 1216 | putsource( /* put out source */
1216                          lampcolor[0], lampcolor[1], lampcolor[2]);
1217          switch (shp->type) {
1218          case RECT:
1219 +                /* Output at least one rectangle. If light is radiated
1220 +                 * from the sides of the luminaire, output rectangular
1221 +                 * sides as well. */
1222                  if (dolower)
1223                          putrectsrc(shp, fp, lname, name, 0);
1224                  if (doupper)
# Line 817 | Line 1227 | putsource( /* put out source */
1227                          putsides(shp, fp, lname, name);
1228                  break;
1229          case DISK:
1230 +                /* Output at least one disk. If light is radiated from
1231 +                 * the sides of luminaire, output a cylinder as well. */
1232                  if (dolower)
1233                          putdisksrc(shp, fp, lname, name, 0);
1234                  if (doupper)
# Line 825 | Line 1237 | putsource( /* put out source */
1237                          putcyl(shp, fp, lname, name);
1238                  break;
1239          case SPHERE:
1240 +                /* Output a sphere. */
1241                  putspheresrc(shp, fp, lname, name);
1242                  break;
1243          }
1244          return(0);
1245   }
1246  
1247 <
1247 > /* makeshape -- decide what shape will be used
1248 > *
1249 > * makeshape decides what Radiance geometry will be used to represent
1250 > * the light source and stores information about it in shp.
1251 > */
1252   int
1253 < makeshape(              /* make source shape */
1253 > makeshape(
1254          SRCINFO *shp,
1255          double  width,
1256          double  length,
1257          double  height
1258   )
1259   {
1260 +        /* Categorize the shape */
1261          if (illumrad/meters2out >= MINDIM/2.) {
1262 +                /* If the -i command line option is used, and the
1263 +                 * object is not a point source, output an "illum"
1264 +                 * sphere */
1265                  shp->isillum = 1;
1266                  shp->type = SPHERE;
1267                  shp->w = shp->l = shp->h = 2.*illumrad / meters2out;
1268          } else if (width < MINDIM) {
1269 +                /* The width is either zero or negative. */
1270                  width = -width;
1271                  if (width < MINDIM) {
1272 +                        /* The width is zero. Use a tiny sphere to
1273 +                         * represent a point source. */
1274                          shp->type = SPHERE;
1275                          shp->w = shp->l = shp->h = MINDIM;
1276                  } else if (height < .5*width) {
1277 +                        /* The width is negative and the height is
1278 +                         * modest; output either a disk or a thin
1279 +                         * vertical cylinder. */
1280                          shp->type = DISK;
1281                          shp->w = shp->l = width;
1282                          if (height >= MINDIM)
# Line 857 | Line 1284 | makeshape(             /* make source shape */
1284                          else
1285                                  shp->h = .5*MINDIM;
1286                  } else {
1287 +                        /* The width is negative and the object is
1288 +                         * tall; output a sphere. */
1289                          shp->type = SPHERE;
1290                          shp->w = shp->l = shp->h = width;
1291                  }
1292          } else {
1293 +                /* The width is positive. Output a box, possibly very
1294 +                 * thin. */
1295                  shp->type = RECT;
1296                  shp->w = width;
1297                  if (length >= MINDIM)
# Line 872 | Line 1303 | makeshape(             /* make source shape */
1303                  else
1304                          shp->h = .5*MINDIM;
1305          }
1306 +
1307 +        /* Done choosing the shape; calculate its area in the x-y plane. */
1308          switch (shp->type) {
1309          case RECT:
1310                  shp->area = shp->w * shp->l;
# Line 884 | Line 1317 | makeshape(             /* make source shape */
1317          return(0);
1318   }
1319  
1320 + /* Rectangular or box-shaped light source.
1321 + *
1322 + * putrectsrc, putsides, putrect, and putpoint are used to output the
1323 + * Radiance description of a box.  The box is centered on the origin
1324 + * and has the dimensions given in the IES file.  The coordinates
1325 + * range from [-1/2*length, -1/2*width, -1/2*height] to [1/2*length,
1326 + * 1/2*width, 1/2*height].
1327 + *
1328 + * The location of the point is encoded in the low-order three bits of
1329 + * an integer. If the integer is p, then: bit 0 is (p & 1),
1330 + * representing length (x), bit 1 is (p & 2) representing width (y),
1331 + * and bit 2 is (p & 4), representing height (z).
1332 + *
1333 + * Looking down from above (towards -z), the vertices of the box or
1334 + * rectangle are numbered so:
1335 + *
1336 + *     2,6                                        3,7
1337 + *        +--------------------------------------+
1338 + *        |                                      |
1339 + *        |                                      |
1340 + *        |                                      |
1341 + *        |                                      |
1342 + *        +--------------------------------------+
1343 + *     0,4                                        1,5
1344 + *
1345 + * The higher number of each pair is above the x-y plane (positive z),
1346 + * the lower number is below the x-y plane (negative z.)
1347 + *
1348 + */
1349  
1350 + /* putrecsrc - output a rectangle parallel to the x-y plane
1351 + *
1352 + * Putrecsrc calls out the vertices of a rectangle parallel to the x-y
1353 + * plane.  The order of the vertices is different for the upper and
1354 + * lower rectangles of a box, since a right-hand rule based on the
1355 + * order of the vertices is used to determine the surface normal of
1356 + * the rectangle, and the surface normal determines the direction the
1357 + * light radiated by the rectangle.
1358 + *
1359 + */
1360   void
1361 < putrectsrc(             /* rectangular source */
1361 > putrectsrc(
1362          SRCINFO *shp,
1363          FILE    *fp,
1364          char    *mod,
# Line 900 | Line 1372 | putrectsrc(            /* rectangular source */
1372                  putrect(shp, fp, mod, name, ".d", 0, 2, 3, 1);
1373   }
1374  
1375 <
1375 > /* putsides - put out sides of box */
1376   void
1377 < putsides(                       /* put out sides of box */
1377 > putsides(
1378          SRCINFO *shp,
1379          FILE    *fp,
1380          char    *mod,
# Line 914 | Line 1386 | putsides(                      /* put out sides of box */
1386          putrect(shp, fp, mod, name, ".3", 3, 2, 6, 7);
1387          putrect(shp, fp, mod, name, ".4", 2, 0, 4, 6);
1388   }
917        
1389  
1390 + /* putrect - put out a rectangle
1391 + *
1392 + * putrect generates the "polygon" primitive which describes a
1393 + * rectangle.
1394 + */
1395   void
1396 < putrect(        /* put out a rectangle */
1396 > putrect(
1397          SRCINFO *shp,
1398          FILE    *fp,
1399          char    *mod,
# Line 936 | Line 1412 | putrect(       /* put out a rectangle */
1412          putpoint(shp, fp, d);
1413   }
1414  
1415 <
1415 > /* putpoint -- output a the coordinates of a vertex
1416 > *
1417 > * putpoint maps vertex numbers to coordinates and outputs the
1418 > * coordinates.
1419 > */
1420   void
1421 < putpoint(                               /* put out a point */
1421 > putpoint(
1422          SRCINFO *shp,
1423          FILE    *fp,
1424          int     p
# Line 952 | Line 1432 | putpoint(                              /* put out a point */
1432                          mult[p>>2]*shp->h*meters2out);
1433   }
1434  
1435 + /* End of routines to output a box-shaped light source */
1436  
1437 + /* Routines to output a cylindrical or disk shaped light source
1438 + *
1439 + * As with other shapes, the light source is centered on the origin.
1440 + * The "ring" and "cylinder" primitives are used.
1441 + *
1442 + */
1443   void
1444   putdisksrc(             /* put out a disk source */
1445          SRCINFO *shp,
# Line 993 | Line 1480 | putcyl(                        /* put out a cylinder */
1480          fprintf(fp, "\t%g\n", .5*shp->w*meters2out);
1481   }
1482  
1483 + /* end of of routines to output cylinders and disks */
1484  
1485   void
1486   putspheresrc(           /* put out a sphere source */
# Line 1006 | Line 1494 | putspheresrc(          /* put out a sphere source */
1494          fprintf(fp, "0\n0\n4 0 0 0 %g\n", .5*shp->w*meters2out);
1495   }
1496  
1497 <
1497 > /* cvdata - convert LM-63 tilt and candela data to Radiance brightdata format
1498 > *
1499 > * The files created by this routine are intended for use with the Radiance
1500 > * "brightdata" material type.
1501 > *
1502 > * Two types of data are converted; one-dimensional tilt data, which
1503 > * is given in polar coordinates, and two-dimensional candela data,
1504 > * which is given in spherical co-ordinates.
1505 > *
1506 > * Return 0 for success, -1 for failure.
1507 > *
1508 > */
1509   int
1510 < cvdata(         /* convert data */
1511 <        FILE    *in,
1512 <        FILE    *out,
1513 <        int     ndim,
1514 <        int     npts[],
1515 <        double  mult,
1516 <        double  lim[][2]
1510 > cvdata(
1511 >        FILE    *in,            /* Input file */
1512 >        FILE    *out,           /* Output file */
1513 >        int     ndim,           /* Number of dimensions; 1 for
1514 >                                 * tilt data, 2 for photometric data. */
1515 >        int     npts[],         /* Number of points in each dimension */
1516 >        double  mult,           /* Multiple each value by this
1517 >                                 * number. For tilt data, always
1518 >                                 * 1. For candela values, the
1519 >                                 * efficacy of white Radiance light.  */
1520 >        double  lim[][2]        /* The range of angles in each dimension. */
1521   )
1522   {
1523 <        double  *pt[4];
1523 >        double  *pt[4];         /* Four is the expected maximum of ndim. */
1524          int     i, j;
1525          double  val;
1526          int     total;
1527  
1528 +        /* Calculate and output the number of data values */
1529          total = 1; j = 0;
1530          for (i = 0; i < ndim; i++)
1531                  if (npts[i] > 1) {
# Line 1029 | Line 1533 | cvdata(                /* convert data */
1533                          j++;
1534                  }
1535          fprintf(out, "%d\n", j);
1536 <                                        /* get coordinates */
1536 >
1537 >        /* Read in the angle values, and note the first and last in
1538 >         * each dimension, if there is a place to store them. In the
1539 >         * case of tilt data, there is only one list of angles. In the
1540 >         * case of candela values, vertical angles appear first, and
1541 >         * horizontal angles occur second. */
1542          for (i = 0; i < ndim; i++) {
1543 +                /* Allocate space for the angle values. */
1544                  pt[i] = (double *)malloc(npts[i]*sizeof(double));
1545                  for (j = 0; j < npts[i]; j++)
1546                          if (!scnflt(in, &pt[i][j]))
# Line 1040 | Line 1550 | cvdata(                /* convert data */
1550                          lim[i][1] = pt[i][npts[i]-1];
1551                  }
1552          }
1553 <                                        /* write out in reverse */
1553 >
1554 >        /* Output the angles. If this is candela data, horizontal
1555 >         * angles output first. There are two cases: the first where
1556 >         * the angles are evenly spaced, the second where they are
1557 >         * not.
1558 >         *
1559 >         * When the angles are evenly spaced, three numbers are
1560 >         * output: the first angle, the last angle, and the number of
1561 >         * angles.  When the angles are not evenly spaced, instead
1562 >         * zero, zero, and the count of angles is given, followed by a
1563 >         * list of angles.  In this case, angles are output four to a line.
1564 >         */
1565          for (i = ndim-1; i >= 0; i--) {
1566                  if (npts[i] > 1) {
1567 +                        /* Determine if the angles are evenly spaces */
1568                          for (j = 1; j < npts[i]-1; j++)
1569                                  if (!FEQ(pt[i][j]-pt[i][j-1],
1570                                                  pt[i][j+1]-pt[i][j]))
1571                                          break;
1572 +                        /* If they are, output the first angle, the
1573 +                         * last angle, and a count */
1574                          if (j == npts[i]-1)
1575                                  fprintf(out, "%g %g %d\n", pt[i][0], pt[i][j],
1576                                                  npts[i]);
1577                          else {
1578 +                                /* otherwise, output 0, 0, and a
1579 +                                 * count, followed by the list of
1580 +                                 * angles, one to a line. */
1581                                  fprintf(out, "0 0 %d", npts[i]);
1582                                  for (j = 0; j < npts[i]; j++) {
1583                                          if (j%4 == 0)
# Line 1060 | Line 1587 | cvdata(                /* convert data */
1587                                  putc('\n', out);
1588                          }
1589                  }
1590 +                /* Free the storage containing the angle values. */
1591                  free((void *)pt[i]);
1592          }
1593 +
1594 +        /* Finally, read in the data values (candela or multiplier values,
1595 +         * depending on the part of the file) and output them four to
1596 +         * a line. */
1597          for (i = 0; i < total; i++) {
1598                  if (i%4 == 0)
1599                          putc('\n', out);
# Line 1073 | Line 1605 | cvdata(                /* convert data */
1605          return(0);
1606   }
1607  
1608 <
1608 > /* getword - get an LM-63 delimited word from fp
1609 > *
1610 > * Getword gets a word from an IES file delimited by either white
1611 > * space or a comma surrounded by white space. A pointer to the word
1612 > * is returned, which will persist only until getword is called again.
1613 > * At EOF, return NULL instead.
1614 > *
1615 > */
1616   char *
1617   getword(                        /* scan a word from fp */
1618          FILE    *fp
# Line 1083 | Line 1622 | getword(                       /* scan a word from fp */
1622          char    *cp;
1623          int     c;
1624  
1625 +        /* Skip initial spaces */
1626          while (isspace(c=getc(fp)))
1627                  ;
1628 +        /* Get characters to a delimiter or until wrd is full */
1629          for (cp = wrd; c != EOF && cp < wrd+RMAXWORD-1;
1630                          *cp++ = c, c = getc(fp))
1631                  if (isspace(c) || c == ',') {
1632 +                        /* If we find a delimiter */
1633 +                        /* Gobble up whitespace */
1634                          while (isspace(c))
1635                                  c = getc(fp);
1636 +                        /* If it's not a comma, put the first
1637 +                         * character of the next data item back */
1638                          if ((c != EOF) & (c != ','))
1639                                  ungetc(c, fp);
1640 +                        /* Close out the strimg */
1641                          *cp = '\0';
1642 +                        /* return it */
1643                          return(wrd);
1644                  }
1645 +        /* If we ran out of space or are at the end of the file,
1646 +         * return either the word or NULL, as appropriate. */
1647          *cp = '\0';
1648          return(cp > wrd ? wrd : NULL);
1649   }
1650  
1651 <
1651 > /* cvtint - convert an IES word to an integer
1652 > *
1653 > * A pointer to the word is passed in wrd; ip is expected to point to
1654 > * an integer.  cvtint() will silently truncate a floating point value
1655 > * to an integer; "1", "1.0", and "1.5" will all return 1.
1656 > *
1657 > * cvtint() returns 0 if it fails, 1 if it succeeds.
1658 > */
1659   int
1660 < cvtint(                 /* convert a word to an integer */
1660 > cvtint(
1661          int     *ip,
1662          char    *wrd
1663   )
# Line 1113 | Line 1669 | cvtint(                        /* convert a word to an integer */
1669   }
1670  
1671  
1672 + /* cvtflt - convert an IES word to a double precision floating-point number
1673 + *
1674 + * A pointer to the word is passed in wrd; rp is expected to point to
1675 + * a double.
1676 + *
1677 + * cvtflt returns 0 if it fails, 1 if it succeeds.
1678 + */
1679   int
1680 < cvtflt(                 /* convert a word to a double */
1680 > cvtflt(
1681          double  *rp,
1682          char    *wrd
1683   )
# Line 1125 | Line 1688 | cvtflt(                        /* convert a word to a double */
1688          return(1);
1689   }
1690  
1691 <
1691 > /* cvgeometry - process materials and geometry format luminaire data
1692 > *
1693 > * The materials and geometry format (MGF) for describing luminaires
1694 > * was a part of Radiance that was first adopted and then retracted by
1695 > * the IES as part of LM-63.  It provides a way of describing
1696 > * luminaire geometry similar to the Radiance scene description
1697 > * format.
1698 > *
1699 > * cvgeometry() generates an mgf2rad command and then, if "-g" is given
1700 > * on the command line, an oconv command, both of which are then
1701 > * executed with the system() function.
1702 > *
1703 > * The generated commands are:
1704 > *   mgf2rad -e <multiplier> -g <size> <mgf_filename> \
1705 > *     | xform -s <scale_factor> \
1706 > *     >> <luminare_scene_description_file
1707 > * or:
1708 > *   mgf2rad -e <multiplier> -g <size> <mgf_filename> \
1709 > *     oconv - > <instance_filename>
1710 > */
1711   int
1712   cvgeometry(
1713          char    *inpname,
# Line 1145 | Line 1727 | cvgeometry(
1727          strcpy(buf, "mgf2rad ");                /* build mgf2rad command */
1728          cp = buf+8;
1729          if (!FEQ(sinf->mult, 1.0)) {
1730 +                /* if there's an output multiplier, include in the
1731 +                 * mgf2rad command */
1732                  sprintf(cp, "-e %f ", sinf->mult);
1733                  cp += strlen(cp);
1734          }
1735 +        /* Include the glow distance for the geometry */
1736          sprintf(cp, "-g %f %s ",
1737                  sqrt(sinf->w*sinf->w + sinf->h*sinf->h + sinf->l*sinf->l),
1738                          inpname);
1739          cp += strlen(cp);
1740          if (instantiate) {              /* instantiate octree */
1741 +                /* If "-g" is given on the command line, include an
1742 +                 * "oconv" command in the pipe. */
1743                  strcpy(cp, "| oconv - > ");
1744                  cp += 12;
1745                  fullnam(cp,outname,T_OCT);
1746 +                /* Only update if the input file is newer than the
1747 +                 * output file */
1748                  if (fdate(inpname) > fdate(outname) &&
1749                                  system(buf)) {          /* create octree */
1750                          fclose(outfp);
1751                          return(-1);
1752                  }
1753 +                /* Reference the instance file in the scene description */
1754                  fprintf(outfp, "void instance %s_inst\n", outname);
1755 +                /* If the geometry isn't in meters, scale it appropriately. */
1756                  if (!FEQ(meters2out, 1.0))
1757                          fprintf(outfp, "3 %s -s %f\n",
1758                                          libname(buf,outname,T_OCT),
1759                                          meters2out);
1760                  else
1761                          fprintf(outfp, "1 %s\n", libname(buf,outname,T_OCT));
1762 +                /* Close off the "instance" primitive. */
1763                  fprintf(outfp, "0\n0\n");
1764 +                /* And the Radiance scene description. */
1765                  fclose(outfp);
1766          } else {                        /* else append to luminaire file */
1767                  if (!FEQ(meters2out, 1.0)) {    /* apply scalefactor */
# Line 1186 | Line 1779 | cvgeometry(
1779          }
1780          return(0);
1781   }
1782 +
1783 + /* Set up emacs indentation */
1784 + /* Local Variables: */
1785 + /*   c-file-style: "bsd" */
1786 + /* End: */
1787 +
1788 + /* For vim, use ":set tabstop=8 shiftwidth=8" */

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines