ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/rc2.c
Revision: 2.26
Committed: Wed Nov 15 18:02:53 2023 UTC (6 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.25: +57 -55 lines
Log Message:
feat(rpict,rtrace,rcontrib,rtpict): Hyperspectral rendering (except photon map)

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.26 static const char RCSid[] = "$Id: rc2.c,v 2.25 2023/04/07 00:03:26 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Accumulate ray contributions for a set of materials
6     * File i/o and recovery
7     */
8    
9 schorsch 2.17 #include <ctype.h>
10     #include "platform.h"
11 greg 2.1 #include "rcontrib.h"
12     #include "resolu.h"
13    
14 greg 2.3 /* Close output stream and free record */
15     static void
16     closestream(void *p)
17     {
18     STREAMOUT *sop = (STREAMOUT *)p;
19    
20     if (sop->ofp != NULL) {
21     int status = 0;
22     if (sop->outpipe)
23     status = pclose(sop->ofp);
24     else if (sop->ofp != stdout)
25     status = fclose(sop->ofp);
26     if (status)
27     error(SYSTEM, "error closing output stream");
28     }
29     free(p);
30     }
31    
32     LUTAB ofiletab = LU_SINIT(free,closestream); /* output file table */
33    
34 greg 2.1 #define OF_MODIFIER 01
35     #define OF_BIN 02
36    
37     /************************** STREAM & FILE I/O ***************************/
38    
39     /* Construct output file name and return flags whether modifier/bin present */
40     static int
41     ofname(char *oname, const char *ospec, const char *mname, int bn)
42     {
43     const char *mnp = NULL;
44     const char *bnp = NULL;
45     const char *cp;
46    
47     if (ospec == NULL)
48     return(-1);
49     for (cp = ospec; *cp; cp++) /* check format position(s) */
50     if (*cp == '%') {
51     do
52     ++cp;
53     while (isdigit(*cp));
54     switch (*cp) {
55     case '%':
56     break;
57     case 's':
58     if (mnp != NULL)
59     return(-1);
60     mnp = cp;
61     break;
62     case 'd':
63     case 'i':
64     case 'o':
65     case 'x':
66     case 'X':
67     if (bnp != NULL)
68     return(-1);
69     bnp = cp;
70     break;
71     default:
72     return(-1);
73     }
74     }
75     if (mnp != NULL) { /* create file name */
76     if (bnp != NULL) {
77     if (bnp > mnp)
78     sprintf(oname, ospec, mname, bn);
79     else
80     sprintf(oname, ospec, bn, mname);
81     return(OF_MODIFIER|OF_BIN);
82     }
83     sprintf(oname, ospec, mname);
84     return(OF_MODIFIER);
85     }
86     if (bnp != NULL) {
87     sprintf(oname, ospec, bn);
88     return(OF_BIN);
89     }
90     strcpy(oname, ospec);
91     return(0);
92     }
93    
94    
95     /* Write header to the given output stream */
96     static void
97     printheader(FILE *fout, const char *info)
98     {
99     extern char VersionID[];
100     /* copy octree header */
101     if (octname[0] == '!') {
102     newheader("RADIANCE", fout);
103     fputs(octname+1, fout);
104     if (octname[strlen(octname)-1] != '\n')
105     fputc('\n', fout);
106     } else {
107 greg 2.2 FILE *fin = fopen(octname, (outfmt=='a') ? "r" : "rb");
108 greg 2.1 if (fin == NULL)
109     quit(1);
110 greg 2.2 checkheader(fin, OCTFMT, fout);
111 greg 2.1 fclose(fin);
112     }
113     printargs(gargc-1, gargv, fout); /* add our command */
114     fprintf(fout, "SOFTWARE= %s\n", VersionID);
115     fputnow(fout);
116 greg 2.26 fputncomp(NCSAMP, fout);
117 greg 2.1 if (info != NULL) /* add extra info if given */
118     fputs(info, fout);
119 greg 2.23 if ((outfmt == 'f') | (outfmt == 'd'))
120     fputendian(fout);
121 greg 2.1 fputformat(formstr(outfmt), fout);
122     fputc('\n', fout); /* empty line ends header */
123     }
124    
125    
126     /* Write resolution string to given output stream */
127     static void
128     printresolu(FILE *fout, int xr, int yr)
129     {
130     if ((xr > 0) & (yr > 0)) /* resolution string */
131     fprtresolu(xr, yr, fout);
132     }
133    
134    
135     /* Get output stream pointer (open and write header if new and noopen==0) */
136     STREAMOUT *
137     getostream(const char *ospec, const char *mname, int bn, int noopen)
138     {
139     static STREAMOUT stdos;
140 greg 2.9 char info[1024];
141 greg 2.1 int ofl;
142     char oname[1024];
143     LUENT *lep;
144     STREAMOUT *sop;
145 greg 2.11 char *cp;
146 greg 2.25
147     info[0] = '\0';
148 greg 2.1 if (ospec == NULL) { /* use stdout? */
149 greg 2.3 if (!noopen & !using_stdout) {
150 greg 2.1 if (outfmt != 'a')
151     SET_FILE_BINARY(stdout);
152 greg 2.24 #ifdef getc_unlocked
153     flockfile(stdout); /* avoid lock/unlock overhead */
154     #endif
155 greg 2.9 if (header) {
156 greg 2.11 cp = info;
157     if (yres > 0) {
158     sprintf(cp, "NROWS=%d\n", yres *
159     (xres + !xres) );
160     while (*cp) ++cp;
161     }
162 greg 2.13 if ((xres <= 0) | (stdos.reclen > 1))
163     sprintf(cp, "NCOLS=%d\n", stdos.reclen);
164 greg 2.9 printheader(stdout, info);
165     }
166 greg 2.13 if (stdos.reclen == 1)
167     printresolu(stdout, xres, yres);
168 greg 2.1 if (waitflush > 0)
169     fflush(stdout);
170     stdos.xr = xres; stdos.yr = yres;
171     using_stdout = 1;
172     }
173     stdos.ofp = stdout;
174     stdos.reclen += noopen;
175     return(&stdos);
176     }
177     ofl = ofname(oname, ospec, mname, bn); /* get output name */
178     if (ofl < 0) {
179     sprintf(errmsg, "bad output format '%s'", ospec);
180     error(USER, errmsg);
181     }
182     lep = lu_find(&ofiletab, oname); /* look it up */
183     if (lep->key == NULL) /* new entry */
184     lep->key = strcpy((char *)malloc(strlen(oname)+1), oname);
185     sop = (STREAMOUT *)lep->data;
186     if (sop == NULL) { /* allocate stream */
187     sop = (STREAMOUT *)malloc(sizeof(STREAMOUT));
188     if (sop == NULL)
189     error(SYSTEM, "out of memory in getostream");
190 greg 2.19 sop->outpipe = (oname[0] == '!');
191 greg 2.1 sop->reclen = 0;
192     sop->ofp = NULL; /* open iff noopen==0 */
193     sop->xr = xres; sop->yr = yres;
194     lep->data = (char *)sop;
195     if (!sop->outpipe & !force_open & !recover &&
196     access(oname, F_OK) == 0) {
197     errno = EEXIST; /* file exists */
198     goto openerr;
199     }
200 greg 2.19 } else if (noopen && outfmt == 'c' && /* stream exists to picture? */
201     (sop->xr > 0) & (sop->yr > 0)) {
202     if (ofl & OF_BIN)
203     return(NULL); /* let caller offset bins */
204     sprintf(errmsg, "output '%s' not a valid picture", oname);
205     error(WARNING, errmsg);
206 greg 2.1 }
207     if (!noopen && sop->ofp == NULL) { /* open output stream */
208     if (oname[0] == '!') /* output to command */
209     sop->ofp = popen(oname+1, "w");
210     else /* else open file */
211     sop->ofp = fopen(oname, "w");
212     if (sop->ofp == NULL)
213     goto openerr;
214     if (outfmt != 'a')
215     SET_FILE_BINARY(sop->ofp);
216 greg 2.15 #ifdef getc_unlocked
217 greg 2.1 flockfile(sop->ofp); /* avoid lock/unlock overhead */
218     #endif
219 greg 2.11 if (accumulate > 0) { /* global resolution */
220     sop->xr = xres; sop->yr = yres;
221     }
222 greg 2.1 if (header) {
223 greg 2.11 cp = info;
224 greg 2.1 if (ofl & OF_MODIFIER || sop->reclen == 1) {
225     sprintf(cp, "MODIFIER=%s\n", mname);
226     while (*cp) ++cp;
227     }
228     if (ofl & OF_BIN) {
229     sprintf(cp, "BIN=%d\n", bn);
230     while (*cp) ++cp;
231     }
232 greg 2.11 if (sop->yr > 0) {
233     sprintf(cp, "NROWS=%d\n", sop->yr *
234     (sop->xr + !sop->xr) );
235     while (*cp) ++cp;
236     }
237 greg 2.13 if ((sop->xr <= 0) | (sop->reclen > 1))
238     sprintf(cp, "NCOLS=%d\n", sop->reclen);
239 greg 2.1 printheader(sop->ofp, info);
240     }
241 greg 2.13 if (sop->reclen == 1)
242     printresolu(sop->ofp, sop->xr, sop->yr);
243 greg 2.1 if (waitflush > 0)
244     fflush(sop->ofp);
245     }
246     sop->reclen += noopen; /* add to length if noopen */
247     return(sop); /* return output stream */
248     openerr:
249     sprintf(errmsg, "cannot open '%s' for writing", oname);
250     error(SYSTEM, errmsg);
251     return(NULL); /* pro forma return */
252     }
253    
254    
255     /* Get a vector from stdin */
256     int
257     getvec(FVECT vec)
258     {
259     float vf[3];
260     double vd[3];
261     char buf[32];
262     int i;
263    
264     switch (inpfmt) {
265     case 'a': /* ascii */
266     for (i = 0; i < 3; i++) {
267     if (fgetword(buf, sizeof(buf), stdin) == NULL ||
268     !isflt(buf))
269     return(-1);
270     vec[i] = atof(buf);
271     }
272     break;
273     case 'f': /* binary float */
274 greg 2.26 if (getbinary(vf, sizeof(float), 3, stdin) != 3)
275 greg 2.1 return(-1);
276     VCOPY(vec, vf);
277     break;
278     case 'd': /* binary double */
279 greg 2.26 if (getbinary(vd, sizeof(double), 3, stdin) != 3)
280 greg 2.1 return(-1);
281     VCOPY(vec, vd);
282     break;
283     default:
284     error(CONSISTENCY, "botched input format");
285     }
286     return(0);
287     }
288    
289    
290     /* Put out ray contribution to file */
291     static void
292 greg 2.26 put_contrib(const DCOLORV *cnt, FILE *fout)
293 greg 2.1 {
294     double sf = 1;
295 greg 2.26 SCOLOR fv;
296     SCOLR cv;
297     int i;
298 greg 2.1
299     if (accumulate > 1)
300     sf = 1./(double)accumulate;
301     switch (outfmt) {
302     case 'a':
303 greg 2.26 for (i = 0; i < NCSAMP; i++)
304     fprintf(fout, "%.6e\t", sf*cnt[i]);
305 greg 2.1 break;
306     case 'f':
307 greg 2.26 for (i = NCSAMP; i-- > 0; )
308     fv[i] = cnt[i];
309     if (accumulate > 1)
310     scalescolor(fv, sf);
311     putbinary(fv, sizeof(COLORV), NCSAMP, fout);
312 greg 2.1 break;
313     case 'd':
314     if (accumulate > 1) {
315 greg 2.26 DCOLORV dv[MAXCSAMP];
316     for (i = NCSAMP; i-- > 0; )
317     dv[i] = sf*cnt[i];
318     putbinary(dv, sizeof(DCOLORV), NCSAMP, fout);
319 greg 2.1 } else
320 greg 2.26 putbinary(cnt, sizeof(DCOLORV), NCSAMP, fout);
321 greg 2.1 break;
322     case 'c':
323 greg 2.26 for (i = NCSAMP; i-- > 0; )
324     fv[i] = cnt[i];
325 greg 2.1 if (accumulate > 1)
326 greg 2.26 scalescolor(fv, sf);
327     scolor_scolr(cv, fv);
328     putbinary(cv, 1, LSCOLR, fout);
329 greg 2.1 break;
330     default:
331     error(INTERNAL, "botched output format");
332     }
333     }
334    
335    
336     /* Output modifier values to appropriate stream(s) */
337     void
338     mod_output(MODCONT *mp)
339     {
340 greg 2.19 STREAMOUT *sop = getostream(mp->outspec, mp->modname, mp->bin0, 0);
341 greg 2.1 int j;
342    
343 greg 2.26 put_contrib(mp->cbin, sop->ofp);
344 greg 2.1 if (mp->nbins > 3 && /* minor optimization */
345 greg 2.19 sop == getostream(mp->outspec, mp->modname, mp->bin0+1, 0)) {
346 greg 2.1 for (j = 1; j < mp->nbins; j++)
347 greg 2.26 put_contrib(mcbin(mp,j), sop->ofp);
348 greg 2.3 } else {
349 greg 2.1 for (j = 1; j < mp->nbins; j++) {
350 greg 2.19 sop = getostream(mp->outspec, mp->modname, mp->bin0+j, 0);
351 greg 2.26 put_contrib(mcbin(mp,j), sop->ofp);
352 greg 2.1 }
353 greg 2.3 }
354 greg 2.1 }
355    
356    
357     /* callback to output newline to ASCII file and/or flush as requested */
358     static int
359     puteol(const LUENT *e, void *p)
360     {
361     STREAMOUT *sop = (STREAMOUT *)e->data;
362    
363     if (outfmt == 'a')
364     putc('\n', sop->ofp);
365     if (!waitflush)
366     fflush(sop->ofp);
367     if (ferror(sop->ofp)) {
368     sprintf(errmsg, "write error on file '%s'", e->key);
369     error(SYSTEM, errmsg);
370     }
371     return(0);
372     }
373    
374    
375     /* Terminate record output and flush if time */
376     void
377     end_record()
378     {
379     --waitflush;
380 greg 2.7 lu_doall(&ofiletab, &puteol, NULL);
381 greg 2.1 if (using_stdout & (outfmt == 'a'))
382     putc('\n', stdout);
383     if (!waitflush) {
384     waitflush = (yres > 0) & (xres > 1) ? 0 : xres;
385     if (using_stdout)
386     fflush(stdout);
387     }
388     }
389    
390     /************************** PARTIAL RESULTS RECOVERY ***********************/
391    
392     /* Get ray contribution from previous file */
393     static int
394 greg 2.26 get_contrib(DCOLORV *cnt, FILE *finp)
395 greg 2.1 {
396 greg 2.26 SCOLOR fv;
397     SCOLR cv;
398     int i;
399 greg 2.1
400     switch (outfmt) {
401     case 'a':
402 greg 2.26 for (i = 0; i < NCSAMP; i++)
403     if (fscanf(finp, "%lf", &cnt[i]) != 1)
404     return(0);
405     return(1);
406     case 'd':
407     return(getbinary(cnt, sizeof(DCOLORV), NCSAMP, finp) == NCSAMP);
408 greg 2.1 case 'f':
409 greg 2.26 if (getbinary(fv, sizeof(COLORV), NCSAMP, finp) != NCSAMP)
410 greg 2.1 return(0);
411 greg 2.26 break;
412 greg 2.1 case 'c':
413 greg 2.26 if (getbinary(cv, 1, LSCOLR, finp) != LSCOLR)
414 greg 2.1 return(0);
415 greg 2.26 scolr_scolor(fv, cv);
416     break;
417 greg 2.1 default:
418     error(INTERNAL, "botched output format");
419     }
420 greg 2.26 /* copy result from SCOLOR */
421     for (i = NCSAMP; i-- > 0; )
422     cnt[i] = fv[i];
423     return(1);
424 greg 2.1 }
425    
426    
427     /* Close output file opened for input */
428     static int
429     myclose(const LUENT *e, void *p)
430     {
431     STREAMOUT *sop = (STREAMOUT *)e->data;
432    
433     if (sop->ofp == NULL)
434     return(0);
435     fclose(sop->ofp);
436     sop->ofp = NULL;
437     return(0);
438     }
439    
440    
441     /* Load previously accumulated values */
442     void
443     reload_output()
444     {
445     int i, j;
446     MODCONT *mp;
447     int ofl;
448     char oname[1024];
449     char *fmode = "rb";
450     char *outvfmt;
451     LUENT *oent;
452     int xr, yr;
453 greg 2.20 STREAMOUT *sop;
454 greg 2.26 DCOLORV contr[MAXCSAMP];
455 greg 2.1
456     if (outfmt == 'a')
457     fmode = "r";
458 greg 2.26 outvfmt = (char *)formstr(outfmt);
459 greg 2.1 /* reload modifier values */
460     for (i = 0; i < nmods; i++) {
461     mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
462     if (mp->outspec == NULL)
463     error(USER, "cannot reload from stdout");
464     if (mp->outspec[0] == '!')
465     error(USER, "cannot reload from command");
466 greg 2.20 for (j = 0; j < mp->nbins; j++) { /* load each modifier bin */
467     ofl = ofname(oname, mp->outspec, mp->modname, mp->bin0+j);
468 greg 2.1 if (ofl < 0)
469     error(USER, "bad output file specification");
470     oent = lu_find(&ofiletab, oname);
471 greg 2.20 if (oent->data == NULL)
472     error(INTERNAL, "unallocated stream in reload_output()");
473     sop = (STREAMOUT *)oent->data;
474     if (sop->ofp == NULL) { /* open output as input */
475     sop->ofp = fopen(oname, fmode);
476     if (sop->ofp == NULL) {
477 greg 2.1 sprintf(errmsg, "missing reload file '%s'",
478     oname);
479     error(WARNING, errmsg);
480     break;
481     }
482 greg 2.15 #ifdef getc_unlocked
483 greg 2.20 flockfile(sop->ofp);
484 greg 2.1 #endif
485 greg 2.20 if (header && checkheader(sop->ofp, outvfmt, NULL) != 1) {
486 greg 2.1 sprintf(errmsg, "format mismatch for '%s'",
487     oname);
488     error(USER, errmsg);
489     }
490 greg 2.20 if ((sop->reclen == 1) & (sop->xr > 0) & (sop->yr > 0) &&
491     (!fscnresolu(&xr, &yr, sop->ofp) ||
492     (xr != sop->xr) |
493     (yr != sop->yr))) {
494 greg 2.1 sprintf(errmsg, "resolution mismatch for '%s'",
495     oname);
496     error(USER, errmsg);
497     }
498     }
499 greg 2.26 /* read in spectral value */
500     if (!get_contrib(contr, sop->ofp)) {
501 greg 2.1 if (!j) {
502 greg 2.20 fclose(sop->ofp);
503 greg 2.1 break; /* ignore empty file */
504     }
505     if (j < mp->nbins) {
506     sprintf(errmsg, "missing data in '%s'",
507     oname);
508     error(USER, errmsg);
509     }
510     break;
511 greg 2.26 }
512     memcpy(mcbin(mp,j), contr, DCOLORSIZ);
513 greg 2.1 }
514     }
515 greg 2.7 lu_doall(&ofiletab, &myclose, NULL); /* close all files */
516 greg 2.1 }
517    
518    
519     /* Seek on the given output file */
520     static int
521     myseeko(const LUENT *e, void *p)
522     {
523     STREAMOUT *sop = (STREAMOUT *)e->data;
524     off_t nbytes = *(off_t *)p;
525    
526     if (sop->reclen > 1)
527 greg 2.22 nbytes *= (off_t)sop->reclen;
528 greg 2.1 if (fseeko(sop->ofp, nbytes, SEEK_CUR) < 0) {
529     sprintf(errmsg, "seek error on file '%s'", e->key);
530     error(SYSTEM, errmsg);
531     }
532     return(0);
533     }
534    
535    
536     /* Recover output if possible */
537     void
538     recover_output()
539     {
540     off_t lastout = -1;
541     int outvsiz, recsiz;
542     char *outvfmt;
543     int i, j;
544     MODCONT *mp;
545     int ofl;
546     char oname[1024];
547     LUENT *oent;
548 greg 2.20 STREAMOUT *sop;
549 greg 2.1 off_t nvals;
550     int xr, yr;
551    
552     switch (outfmt) {
553     case 'a':
554     error(USER, "cannot recover ASCII output");
555     return;
556     case 'f':
557 greg 2.26 outvsiz = sizeof(float)*NCSAMP;
558 greg 2.1 break;
559     case 'd':
560 greg 2.26 outvsiz = sizeof(double)*NCSAMP;
561 greg 2.1 break;
562     case 'c':
563 greg 2.26 outvsiz = LSCOLR;
564 greg 2.1 break;
565     default:
566     error(INTERNAL, "botched output format");
567     return;
568     }
569 greg 2.26 outvfmt = (char *)formstr(outfmt);
570 greg 2.1 /* check modifier outputs */
571     for (i = 0; i < nmods; i++) {
572     mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
573     if (mp->outspec == NULL)
574     error(USER, "cannot recover from stdout");
575     if (mp->outspec[0] == '!')
576     error(USER, "cannot recover from command");
577 greg 2.20 for (j = 0; j < mp->nbins; j++) { /* check each bin's file */
578     ofl = ofname(oname, mp->outspec, mp->modname, mp->bin0+j);
579 greg 2.1 if (ofl < 0)
580     error(USER, "bad output file specification");
581     oent = lu_find(&ofiletab, oname);
582 greg 2.20 if (oent->data == NULL)
583     error(INTERNAL, "unallocated stream in recover_output()");
584     sop = (STREAMOUT *)oent->data;
585     if (sop->ofp != NULL) { /* already open? */
586 greg 2.1 if (ofl & OF_BIN)
587     continue;
588     break;
589     }
590     /* open output */
591 greg 2.20 sop->ofp = fopen(oname, "rb+");
592     if (sop->ofp == NULL) {
593 greg 2.1 sprintf(errmsg, "missing recover file '%s'",
594     oname);
595     error(WARNING, errmsg);
596 greg 2.21 lastout = 0;
597 greg 2.1 break;
598     }
599 greg 2.20 nvals = lseek(fileno(sop->ofp), 0, SEEK_END);
600 greg 2.1 if (nvals <= 0) {
601     lastout = 0; /* empty output, quit here */
602 greg 2.20 fclose(sop->ofp);
603 greg 2.1 break;
604     }
605 greg 2.20 recsiz = outvsiz * sop->reclen;
606 greg 2.1
607 greg 2.20 lseek(fileno(sop->ofp), 0, SEEK_SET);
608     if (header && checkheader(sop->ofp, outvfmt, NULL) != 1) {
609 greg 2.1 sprintf(errmsg, "format mismatch for '%s'",
610     oname);
611     error(USER, errmsg);
612     }
613 greg 2.20 if ((sop->reclen == 1) & (sop->xr > 0) & (sop->yr > 0) &&
614     (!fscnresolu(&xr, &yr, sop->ofp) ||
615     (xr != sop->xr) |
616     (yr != sop->yr))) {
617 greg 2.1 sprintf(errmsg, "resolution mismatch for '%s'",
618     oname);
619     error(USER, errmsg);
620     }
621 greg 2.20 nvals = (nvals - (off_t)ftell(sop->ofp)) / recsiz;
622 greg 2.1 if ((lastout < 0) | (nvals < lastout))
623     lastout = nvals;
624     if (!(ofl & OF_BIN))
625     break; /* no bin separation */
626     }
627     if (!lastout) { /* empty output */
628     error(WARNING, "no previous data to recover");
629 greg 2.21 /* reclose all outputs */
630     lu_doall(&ofiletab, &myclose, NULL);
631 greg 2.1 return;
632     }
633     }
634     if (lastout < 0) {
635     error(WARNING, "no output files to recover");
636     return;
637     }
638     if (raysleft && lastout >= raysleft/accumulate) {
639     error(WARNING, "output appears to be complete");
640     /* XXX should read & discard input? */
641     quit(0);
642     }
643     /* seek on all files */
644     nvals = lastout * outvsiz;
645 greg 2.7 lu_doall(&ofiletab, &myseeko, &nvals);
646 greg 2.1 /* skip repeated input */
647 greg 2.5 lastout *= accumulate;
648 greg 2.1 for (nvals = 0; nvals < lastout; nvals++) {
649     FVECT vdummy;
650     if (getvec(vdummy) < 0 || getvec(vdummy) < 0)
651     error(USER, "unexpected EOF on input");
652     }
653 greg 2.5 lastray = lastdone = (RNUMBER)lastout;
654 greg 2.1 if (raysleft)
655     raysleft -= lastray;
656     }