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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines