ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/ies2rad.c
Revision: 2.31
Committed: Tue Jun 5 16:04:00 2018 UTC (5 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R2
Changes since 2.30: +11 -8 lines
Log Message:
Increased maximum line length and allow spaces before bracketed keywords

File Contents

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