ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/ies2rad.c
Revision: 2.29
Committed: Mon Jun 4 18:53:09 2018 UTC (5 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.28: +2 -2 lines
Log Message:
Fixed bug in header parsing pointed out by Randolph Fritz

File Contents

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