ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/hd/rhcopy.c
Revision: 3.34
Committed: Wed Nov 16 00:12:49 2022 UTC (18 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.33: +374 -72 lines
Log Message:
feat(rhcopy): Added -i* and -o* options for reading and writing rays

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rhcopy.c,v 3.33 2019/11/07 23:17:58 greg Exp $";
3 #endif
4 /*
5 * Copy data into a holodeck file
6 */
7
8 #include "platform.h"
9 #include "rterror.h"
10 #include "holo.h"
11 #include "view.h"
12
13 #ifndef BKBSIZE
14 #define BKBSIZE 256 /* beam clump size (kilobytes) */
15 #endif
16 /* possible operations */
17 #define FROM_HOLO 1 /* copy between holodecks */
18 #define FROM_PICZ 2 /* copy from HDR + depth to holodeck */
19 #define FROM_STDIN 3 /* copy from stdin to holodeck */
20 #define TO_STDOUT 4 /* copy rays from holodeck to stdout */
21
22 int operation = 0; /* what we are doing */
23 char *rspec = ""; /* ray details for i/o */
24 int checkdepth = 1; /* check depth (!-d option)? */
25 int checkrepeats = 0; /* check for repeats (-u option)? */
26 int nholosects; /* number of original holodeck sections */
27 int iofmt = 'a'; /* input/output format for rays */
28
29 /* holodeck flags */
30 #define H_BADF 01 /* bad format */
31 #define H_OBST 02 /* OBSTRUCTIONS= True */
32 #define H_OBSF 04 /* OBSTRUCTIONS= False */
33 #define H_VDST 010 /* VDISTANCE= True */
34 #define H_SWAP 020 /* byte order is different */
35
36 char *progname; /* global argv[0] */
37
38 struct phead {
39 VIEW vw;
40 double expos;
41 short gotview;
42 short badfmt;
43 short altprims;
44 };
45
46 typedef struct {
47 FVECT ro;
48 FVECT rd;
49 RREAL d;
50 COLR cv;
51 } RAYPAR;
52
53 static int openholo(char *fname, int append);
54 static void addray(RAYPAR *rp);
55 static int readval(RREAL *v, int n, FILE *fp);
56 static void readrays(FILE *fp);
57 static int writeval(RREAL *v, int n, FILE *fp);
58 static int write_ray(RAYPAR *rp, FILE *fp);
59 static void writerays(FILE *fp);
60 static gethfunc holheadline;
61 static int bpcmp(const void *b1p, const void *b2p);
62 static int addclump(HOLO *hp, int *bq, int nb);
63 static void addholo(char *hdf);
64 static gethfunc picheadline;
65 static void addpicz(char *pcf, char *zbf);
66
67
68 int
69 main(
70 int argc,
71 char *argv[]
72 )
73 {
74 int i;
75
76 progname = argv[0];
77 for (i = 2; i < argc && argv[i][0] == '-'; i++)
78 switch (argv[i][1]) {
79 case 'u':
80 checkrepeats = 1;
81 break;
82 case 'd':
83 checkdepth = 0;
84 break;
85 case 'f':
86 iofmt = argv[i][2];
87 if (!strchr("afd", iofmt))
88 error(USER, "-f? i/o format must be 'a', 'f', or 'd'");
89 break;
90 case 'h':
91 operation = FROM_HOLO;
92 break;
93 case 'p':
94 operation = FROM_PICZ;
95 break;
96 case 'i':
97 operation = FROM_STDIN;
98 rspec = argv[i]+2;
99 break;
100 case 'o':
101 operation = TO_STDOUT;
102 rspec = argv[i]+2;
103 break;
104 default:
105 goto userr;
106 }
107 if (!operation | (i > argc-((operation==FROM_HOLO)|(operation==FROM_PICZ))))
108 goto userr;
109 if (operation == FROM_PICZ && (argc-i)%2)
110 goto userr;
111 nholosects = openholo(argv[1], (operation != TO_STDOUT));
112 /* check requested i/o is compatible */
113 if (strchr(rspec, 'l') && !(*(int *)hdlist[0]->priv & H_VDST))
114 error(USER, "i/o parameter 'l' incompatible with VDISTANCE=False");
115 if (strchr(rspec, 'L') && *(int *)hdlist[0]->priv & H_VDST)
116 error(USER, "i/o parameter 'L' incompatible with VDISTANCE=True");
117
118 switch (operation) { /* perform requested operation */
119 case FROM_PICZ:
120 for ( ; i < argc; i += 2)
121 addpicz(argv[i], argv[i+1]);
122 break;
123 case FROM_HOLO:
124 if (BKBSIZE*1024*1.5 > hdcachesize)
125 hdcachesize = BKBSIZE*1024*1.5;
126 for ( ; i < argc; i++)
127 addholo(argv[i]);
128 break;
129 case FROM_STDIN:
130 readrays(stdin);
131 break;
132 case TO_STDOUT:
133 writerays(stdout);
134 break;
135 }
136 quit(0);
137 userr:
138 fprintf(stderr, "Usage: %s dest.hdk [-u][-d] -h inp1.hdk ..\n",
139 progname);
140 fprintf(stderr, " Or: %s dest.hdk [-u][-d] -p inp1.hdr inp1.zbf ..\n",
141 progname);
142 fprintf(stderr, " Or: %s dest.hdk [-f{a|f|d}][-u][-d] -i[odplLv]\n",
143 progname);
144 fprintf(stderr, " Or: %s src.hdk [-f{a|f|d}] -o[odplLv] ..\n",
145 progname);
146 exit(1);
147 }
148
149 static int
150 holheadline( /* check holodeck header line */
151 char *s,
152 void *vhf
153 )
154 {
155 int be;
156 char fmt[MAXFMTLEN];
157 int *hf = (int *)vhf;
158
159 if (formatval(fmt, s)) {
160 if (strcmp(fmt, HOLOFMT))
161 *hf |= H_BADF;
162 else
163 *hf &= ~H_BADF;
164 return(0);
165 }
166 if (!strncmp(s, "OBSTRUCTIONS=", 13)) {
167 s += 13;
168 while (*s == ' ') s++;
169 if ((*s == 't') | (*s == 'T'))
170 *hf |= H_OBST;
171 else if ((*s == 'f') | (*s == 'F'))
172 *hf |= H_OBSF;
173 else
174 error(WARNING, "bad OBSTRUCTIONS value in holodeck");
175 return(0);
176 }
177 if (!strncmp(s, "VDISTANCE=", 10)) {
178 s += 10;
179 while (*s == ' ') s++;
180 if ((*s == 't') | (*s == 'T'))
181 *hf |= H_VDST;
182 else if ((*s != 'f') % (*s != 'F'))
183 error(WARNING, "bad VDISTANCE value in holodeck");
184 return(0);
185 }
186 if ((be = isbigendian(s)) >= 0) {
187 if (be != nativebigendian())
188 *hf |= H_SWAP;
189 return(0);
190 }
191 return(0);
192 }
193
194 int
195 openholo( /* open existing holodeck file for i/o */
196 char *fname,
197 int append
198 )
199 {
200 FILE *fp;
201 int fd;
202 int hflags = 0;
203 int *hfstore;
204 off_t nextloc;
205 int n;
206 /* open holodeck file */
207 if ((fp = fopen(fname, append ? "rb+" : "rb")) == NULL) {
208 sprintf(errmsg, "cannot open \"%s\" for %s", fname,
209 append ? "appending" : "reading");
210 error(SYSTEM, errmsg);
211 }
212 /* check header and magic number */
213 if (getheader(fp, holheadline, &hflags) < 0 ||
214 hflags&(H_BADF|H_SWAP) || getw(fp) != HOLOMAGIC) {
215 sprintf(errmsg, "holodeck \"%s\" not in expected format", fname);
216 error(USER, errmsg);
217 }
218 fd = dup(fileno(fp)); /* dup file handle */
219 nextloc = ftell(fp); /* get stdio position */
220 fclose(fp); /* done with stdio */
221 hfstore = (int *)malloc(sizeof(int)); /* tiny memory leak but who cares? */
222 *hfstore = hflags;
223 for (n = 0; nextloc > 0L; n++) { /* initialize each section */
224 lseek(fd, nextloc, SEEK_SET);
225 read(fd, (char *)&nextloc, sizeof(nextloc));
226 hdinit(fd, NULL)->priv = hfstore;
227 }
228 return(n);
229 }
230
231 void
232 addray( /* add a ray to our output holodeck */
233 RAYPAR *rp
234 )
235 {
236 int sn, bi, n;
237 HOLO *hp;
238 GCOORD gc[2];
239 uby8 rr[2][2];
240 BEAM *bp;
241 double d0, d1;
242 unsigned dc;
243 RAYVAL *rv;
244 /* check each output section */
245 for (sn = nholosects; sn--; ) {
246 hp = hdlist[sn];
247 d0 = hdinter(gc, rr, &d1, hp, rp->ro, rp->rd);
248 if (rp->d <= d0 || d1 < -0.001)
249 continue; /* missed section */
250 if (checkdepth) { /* check depth */
251 if (*(int *)hp->priv & H_OBST && d0 < -0.001)
252 continue; /* ray starts too late */
253 if (*(int *)hp->priv & H_OBSF && rp->d < 0.999*d1)
254 continue; /* ray ends too soon */
255 }
256 dc = hdcode(hp, rp->d-d0);
257 bi = hdbindex(hp, gc); /* check for duplicates */
258 if (checkrepeats && (bp = hdgetbeam(hp, bi)) != NULL) {
259 for (n = bp->nrm, rv = hdbray(bp); n--; rv++)
260 if ((rv->d == dc || *(int *)hp->priv & (H_OBST|H_OBSF)) &&
261 rv->r[0][0] == rr[0][0] &&
262 rv->r[0][1] == rr[0][1] &&
263 rv->r[1][0] == rr[1][0] &&
264 rv->r[1][1] == rr[1][1])
265 break;
266 if (n >= 0)
267 continue; /* found a matching ray */
268 }
269 rv = hdnewrays(hp, bi, 1);
270 rv->d = dc;
271 rv->r[0][0] = rr[0][0]; rv->r[0][1] = rr[0][1];
272 rv->r[1][0] = rr[1][0]; rv->r[1][1] = rr[1][1];
273 copycolr(rv->v, rp->cv);
274 }
275 }
276
277 /* Read n-vector from file stream */
278 static int
279 readval(RREAL *v, int n, FILE *fp)
280 {
281 int i;
282 #ifdef SMLFLT
283 double vd[3];
284 switch (iofmt) {
285 case 'f':
286 return getbinary(v, sizeof(float), n, fp);
287 case 'd':
288 n = getbinary(vd, sizeof(double), n, fp);
289 for (i = n; i-- > 0; ) v[i] = vd[i];
290 return n;
291 case 'a':
292 for (i = 0; i < n; i++)
293 if (fscanf(fp, "%f ", &v[i]) != 1)
294 break;
295 return i;
296 }
297 #else
298 float vf[3];
299 switch (iofmt) {
300 case 'd':
301 return getbinary(v, sizeof(double), n, fp);
302 case 'f':
303 n = getbinary(vf, sizeof(float), n, fp);
304 for (i = n; i-- > 0; ) v[i] = vf[i];
305 return n;
306 case 'a':
307 for (i = 0; i < n; i++)
308 if (fscanf(fp, "%lf ", &v[i]) != 1)
309 break;
310 return i;
311 }
312 #endif
313 return -1;
314 }
315
316 #define GOT_ORG 0x01
317 #define GOT_DIR 0x02
318 #define GOT_LEN 0x04
319 #define GOT_VAL 0x10
320 #define ALSO_POS 0x20
321 #define BAD_DIR 0x40
322 #define BAD_LEN 0x80
323
324 /* Read rays from stream and add to holodeck */
325 static void
326 readrays(FILE *fp)
327 {
328 if (iofmt != 'a')
329 SET_FILE_BINARY(fp);
330 #ifdef getc_unlocked
331 flockfile(fp);
332 #endif
333 while (!feof(fp)) { /* read entirety of input */
334 RAYPAR ryp;
335 FVECT pos;
336 FVECT col;
337 int flags = 0;
338 int i;
339 for (i = 0; rspec[i]; i++) {
340 switch (rspec[i]) {
341 case 'o': /* ray origin */
342 if (readval(ryp.ro, 3, fp) < 3)
343 break;
344 flags |= GOT_ORG;
345 continue;
346 case 'd': /* ray direction */
347 if (readval(ryp.rd, 3, fp) < 3)
348 break;
349 if (normalize(ryp.rd) == 0)
350 flags |= BAD_DIR;
351 else
352 flags |= GOT_DIR;
353 continue;
354 case 'p': /* ray intersection */
355 if (readval(pos, 3, fp) < 3)
356 break;
357 flags |= ALSO_POS;
358 continue;
359 case 'L': /* ray first length */
360 case 'l': /* ray virtual length */
361 if (readval(&ryp.d, 1, fp) < 1)
362 break;
363 if (ryp.d <= FTINY)
364 flags |= BAD_LEN;
365 else
366 flags |= GOT_LEN;
367 continue;
368 case 'v': /* ray value */
369 if (readval(col, 3, fp) < 3)
370 break;
371 setcolr(ryp.cv, col[0], col[1], col[2]);
372 flags |= GOT_VAL;
373 continue;
374 default:
375 sprintf(errmsg, "unsupported parameter '%c' in -i%s",
376 rspec[i], rspec);
377 error(USER, errmsg);
378 }
379 if (!flags) /* got nothing, so may be normal EOF */
380 return;
381 }
382 if (flags & (BAD_DIR|BAD_LEN))
383 continue; /* just a bad ray is all -- skip */
384 if (!(flags & GOT_VAL))
385 goto missingData;
386 if ((flags & (GOT_ORG|GOT_DIR|GOT_LEN)) != (GOT_ORG|GOT_DIR|GOT_LEN)) {
387 if (!(flags & ALSO_POS))
388 goto missingData;
389 if (flags & GOT_ORG) {
390 VSUB(ryp.rd, pos, ryp.ro);
391 ryp.d = normalize(ryp.rd);
392 if (ryp.d == 0)
393 continue;
394 } else if ((flags & (GOT_DIR|GOT_LEN)) == (GOT_DIR|GOT_LEN)) {
395 VSUM(ryp.ro, pos, ryp.rd, -ryp.d);
396 } else
397 goto missingData;
398 }
399 addray(&ryp); /* add our ray to holodeck */
400 }
401 return;
402 missingData:
403 sprintf(errmsg, "insufficient data in -i%s", rspec);
404 error(USER, errmsg);
405 }
406
407 /* Write vector value to file stream */
408 static int
409 writeval(RREAL *v, int n, FILE *fp)
410 {
411 int i;
412
413 if (iofmt == 'a') {
414 for (i = 0; i < n; i++)
415 if (fprintf(fp, "\t%.4e", v[i]) < 0)
416 break;
417 return i;
418 }
419 #ifdef SMLFLT
420 if (iofmt == 'd') {
421 double vd[3];
422 for (i = n; i--; ) vd[i] = v[i];
423 return putbinary(vd, sizeof(double), n, fp);
424 }
425 #else
426 if (iofmt == 'f') {
427 float vf[3];
428 for (i = n; i--; ) vf[i] = v[i];
429 return putbinary(vf, sizeof(float), n, fp);
430 }
431 #endif
432 return putbinary(v, sizeof(*v), n, fp);
433 }
434
435 /* Write out an individual ray as requested */
436 static int
437 write_ray(RAYPAR *rp, FILE *fp)
438 {
439 COLOR cval;
440 FVECT v3;
441 char *typ = rspec;
442
443 for ( ; ; ) {
444 switch (*typ++) {
445 case 'o': /* ray origin */
446 if (writeval(rp->ro, 3, fp) < 3)
447 break;
448 continue;
449 case 'd': /* ray direction */
450 if (writeval(rp->rd, 3, fp) < 3)
451 break;
452 continue;
453 case 'p': /* ray intersection */
454 VSUM(v3, rp->ro, rp->rd, rp->d);
455 if (writeval(v3, 3, fp) < 3)
456 break;
457 continue;
458 case 'L': /* ray first length */
459 case 'l': /* ray virtual length */
460 if (writeval(&rp->d, 1, fp) < 1)
461 break;
462 continue;
463 case 'v': /* ray value */
464 colr_color(cval, rp->cv);
465 VCOPY(v3, cval);
466 if (writeval(v3, 3, fp) < 3)
467 break;
468 continue;
469 case '\0': /* end of spec -- success */
470 if (iofmt == 'a')
471 fputc('\n', fp);
472 return(1);
473 default:
474 sprintf(errmsg, "unsupported parameter '%c' in -o%s", typ[-1], rspec);
475 }
476 break; /* land here on error */
477 }
478 return 0; /* write error? */
479 }
480
481 /* Write all rays from holodeck to stream */
482 static void
483 writerays(FILE *fp)
484 {
485 int sn, bi, k;
486 GCOORD gc[2];
487 RAYVAL *rv;
488 RAYPAR ryp;
489
490 if (!*rspec) {
491 error(WARNING, "empty -o* output spec, quitting");
492 return;
493 }
494 if (iofmt != 'a')
495 SET_FILE_BINARY(fp);
496 #ifdef getc_unlocked
497 flockfile(fp);
498 #endif
499 for (sn = 0; sn < nholosects; sn++) { /* write each holodeck section */
500 HOLO *hp = hdlist[sn];
501 for (bi = nbeams(hp); bi > 0; bi--) {
502 BEAM *bp = hdgetbeam(hp, bi);
503 if (!bp) /* empty beam? */
504 continue;
505 hdbcoord(gc, hp, bi); /* else write rays */
506 rv = hdbray(bp);
507 for (k = bp->nrm; k--; rv++) {
508 ryp.d = hdray(ryp.ro, ryp.rd, hp, gc, rv->r);
509 if (*(int *)hp->priv & H_OBSF)
510 VSUM(ryp.ro, ryp.ro, ryp.rd, ryp.d);
511 else
512 ryp.d = 0.;
513 ryp.d = hddepth(hp, rv->d) - ryp.d;
514 copycolr(ryp.cv, rv->v);
515 if (!write_ray(&ryp, fp))
516 goto writError;
517 }
518 hdfreebeam(hp, bi);
519 }
520 }
521 if (fflush(fp) != EOF)
522 return;
523 writError:
524 error(SYSTEM, "error writing holodeck rays");
525 }
526
527 static BEAMI *beamdir;
528
529 static int
530 bpcmp( /* compare beam positions on disk */
531 const void *b1p,
532 const void *b2p
533 )
534 {
535 off_t pdif = beamdir[*(int*)b1p].fo - beamdir[*(int*)b2p].fo;
536
537 if (pdif > 0L) return(1);
538 if (pdif < 0L) return(-1);
539 return(0);
540 }
541
542 static int
543 addclump( /* transfer the given clump and free */
544 HOLO *hp,
545 int *bq,
546 int nb
547 )
548 {
549 GCOORD gc[2];
550 RAYPAR ryp;
551 RAYVAL *rv;
552 int i;
553 int k;
554 BEAM *bp;
555 /* sort based on file position */
556 beamdir = hp->bi;
557 qsort((char *)bq, nb, sizeof(*bq), bpcmp);
558 /* transfer each beam */
559 for (i = 0; i < nb; i++) {
560 bp = hdgetbeam(hp, bq[i]);
561 hdbcoord(gc, hp, bq[i]);
562 rv = hdbray(bp); /* add each ray to output */
563 for (k = bp->nrm; k--; rv++) {
564 ryp.d = hdray(ryp.ro, ryp.rd, hp, gc, rv->r);
565 if (*(int *)hp->priv & H_OBSF)
566 VSUM(ryp.ro, ryp.ro, ryp.rd, ryp.d);
567 else
568 ryp.d = 0.;
569 ryp.d = hddepth(hp, rv->d) - ryp.d;
570 copycolr(ryp.cv, rv->v);
571 addray(&ryp);
572 }
573 hdfreebeam(hp, bq[i]); /* free the beam */
574 }
575 return(0);
576 }
577
578
579 void
580 addholo( /* add a holodeck file */
581 char *hdf
582 )
583 {
584 int fd;
585 /* open the holodeck for reading */
586 openholo(hdf, 0);
587 fd = hdlist[nholosects]->fd; /* remember the file handle */
588 while (hdlist[nholosects] != NULL) { /* load each section */
589 /* clump the beams */
590 clumpbeams(hdlist[nholosects], 0, BKBSIZE*1024, addclump);
591 hddone(hdlist[nholosects]); /* free the section */
592 }
593 close(fd); /* close input file */
594 hdflush(NULL); /* flush output */
595 }
596
597
598
599 static int
600 picheadline( /* process picture header line */
601 char *s,
602 void *vph
603 )
604 {
605 char fmt[32];
606 struct phead *ph = (struct phead *)vph;
607
608 if (formatval(fmt, s)) {
609 ph->badfmt = strcmp(fmt, COLRFMT);
610 return(0);
611 }
612 if (isprims(s)) {
613 ph->altprims++; /* don't want to deal with this */
614 return(0);
615 }
616 if (isexpos(s)) {
617 ph->expos *= exposval(s);
618 return(0);
619 }
620 if (isview(s)) {
621 ph->gotview += sscanview(&ph->vw, s);
622 return(0);
623 }
624 return(0);
625 }
626
627
628 void
629 addpicz( /* add a picture + depth-buffer */
630 char *pcf,
631 char *zbf
632 )
633 {
634 FILE *pfp;
635 int zfd;
636 COLR *cscn;
637 float *zscn;
638 struct phead phd;
639 int eshft;
640 double emult;
641 RESOLU prs;
642 RREAL vl[2];
643 RAYPAR ryp;
644 double aftd;
645 int j, i;
646 /* open picture & get header */
647 if ((pfp = fopen(pcf, "rb")) == NULL) {
648 sprintf(errmsg, "cannot open picture file \"%s\"", pcf);
649 error(SYSTEM, pcf);
650 }
651 phd.vw = stdview;
652 phd.expos = 1.0;
653 phd.badfmt = phd.gotview = phd.altprims = 0;
654 if (getheader(pfp, picheadline, &phd) < 0 ||
655 phd.badfmt || !fgetsresolu(&prs, pfp)) {
656 sprintf(errmsg, "bad format for picture file \"%s\"", pcf);
657 error(USER, errmsg);
658 }
659 if (!phd.gotview || setview(&phd.vw) != NULL) {
660 sprintf(errmsg, "missing/illegal view in picture \"%s\"",
661 pcf);
662 error(USER, errmsg);
663 }
664 if (phd.altprims) {
665 sprintf(errmsg, "ignoring color primaries in picture \"%s\"",
666 pcf);
667 error(WARNING, errmsg);
668 }
669 /* open depth buffer */
670 if ((zfd = open_float_depth(zbf, prs.xr*prs.yr)) < 0)
671 quit(1);
672 /* figure out what to do about exposure */
673 if ((phd.expos < 0.99) | (phd.expos > 1.01)) {
674 emult = -log(phd.expos)/log(2.);
675 eshft = emult >= 0. ? emult+.5 : emult-.5;
676 emult -= (double)eshft;
677 if ((emult <= 0.01) & (emult >= -0.01))
678 emult = -1.;
679 else {
680 emult = 1./phd.expos;
681 eshft = 0;
682 }
683 } else {
684 emult = -1.;
685 eshft = 0;
686 }
687 /* allocate buffers */
688 cscn = (COLR *)malloc(scanlen(&prs)*sizeof(COLR));
689 zscn = (float *)malloc(scanlen(&prs)*sizeof(float));
690 if ((cscn == NULL) | (zscn == NULL))
691 error(SYSTEM, "out of memory in addpicz");
692 /* read and process each scanline */
693 for (j = 0; j < numscans(&prs); j++) {
694 i = scanlen(&prs); /* read colrs */
695 if (freadcolrs(cscn, i, pfp) < 0) {
696 sprintf(errmsg, "error reading picture \"%s\"", pcf);
697 error(USER, errmsg);
698 }
699 if (eshft) /* shift exposure */
700 shiftcolrs(cscn, i, eshft);
701 /* read depth */
702 if (read(zfd, zscn, i*sizeof(float)) != i*sizeof(float)) {
703 sprintf(errmsg, "error reading depth file \"%s\"", zbf);
704 error(USER, errmsg);
705 }
706 while (i--) { /* process each pixel */
707 if (zscn[i] <= 0.0)
708 continue; /* illegal depth */
709 pix2loc(vl, &prs, i, j);
710 aftd = viewray(ryp.ro, ryp.rd, &phd.vw, vl[0], vl[1]);
711 if (aftd < -FTINY)
712 continue; /* off view */
713 if (aftd > FTINY && zscn[i] > aftd)
714 continue; /* aft clipped */
715 ryp.d = (RREAL)zscn[i];
716 copycolr(ryp.cv, cscn[i]);
717 if (emult > 0.) { /* whatta pain */
718 COLOR ctmp;
719 colr_color(ctmp, ryp.cv);
720 scalecolor(ctmp, emult);
721 setcolr(ryp.cv, colval(ctmp,RED),
722 colval(ctmp,GRN), colval(ctmp,BLU));
723 }
724 addray(&ryp);
725 }
726 }
727 /* write output and free beams */
728 hdflush(NULL);
729 /* clean up */
730 free((void *)cscn);
731 free((void *)zscn);
732 fclose(pfp);
733 close(zfd);
734 }
735
736
737 void
738 eputs( /* put error message to stderr */
739 char *s
740 )
741 {
742 static int midline = 0;
743
744 if (!*s)
745 return;
746 if (!midline++) { /* prepend line with program name */
747 fputs(progname, stderr);
748 fputs(": ", stderr);
749 }
750 fputs(s, stderr);
751 if (s[strlen(s)-1] == '\n') {
752 fflush(stderr);
753 midline = 0;
754 }
755 }
756
757
758 void
759 quit( /* exit the program gracefully */
760 int code
761 )
762 {
763 hdsync(NULL, 1); /* write out any buffered data */
764 exit(code);
765 }