| 27 |  | /* | 
| 28 |  | * The MODCONT structure is used to accumulate ray contributions | 
| 29 |  | * for a particular modifier, which may be subdivided into bins | 
| 30 | < | * if binv is non-zero.  If outspec contains a %s in it, this will | 
| 30 | > | * if binv evaluates > 0.  If outspec contains a %s in it, this will | 
| 31 |  | * be replaced with the modifier name.  If outspec contains a %d in it, | 
| 32 |  | * this will be used to create one output file per bin, otherwise all bins | 
| 33 |  | * will be written to the same file, in order.  If the global outfmt | 
| 46 |  |  | 
| 47 |  | LUTAB   modconttab = LU_SINIT(NULL,mcfree);     /* modifier lookup table */ | 
| 48 |  |  | 
| 49 | – | #define CNT_UNKNOWN     0               /* unknown record length */ | 
| 50 | – | #define CNT_PIPE        (-1)            /* output to a pipe */ | 
| 49 |  | /* | 
| 50 |  | * The STREAMOUT structure holds an open FILE pointer and a count of | 
| 51 | < | * the number of RGB triplets per record, or CNT_UNKNOWN (0) if | 
| 54 | < | * unknown or CNT_PIPE (-1) if writing to a command. | 
| 51 | > | * the number of RGB triplets per record, or 0 if unknown. | 
| 52 |  | */ | 
| 53 |  | typedef struct { | 
| 54 |  | FILE            *ofp;           /* output file pointer */ | 
| 55 | + | int             outpipe;        /* output is to a pipe */ | 
| 56 |  | int             reclen;         /* triplets/record */ | 
| 57 | + | int             xr, yr;         /* output resolution for picture */ | 
| 58 |  | } STREAMOUT; | 
| 59 |  |  | 
| 60 |  | /* close output stream and free record */ | 
| 62 |  | closestream(void *p) | 
| 63 |  | { | 
| 64 |  | STREAMOUT       *sop = (STREAMOUT *)p; | 
| 65 | < | if (sop->reclen == CNT_PIPE) | 
| 66 | < | pclose(sop->ofp); | 
| 65 | > | int             status; | 
| 66 | > | if (sop->outpipe) | 
| 67 | > | status = pclose(sop->ofp); | 
| 68 |  | else | 
| 69 | < | fclose(sop->ofp); | 
| 69 | > | status = fclose(sop->ofp); | 
| 70 | > | if (status) | 
| 71 | > | error(SYSTEM, "error closing output stream"); | 
| 72 |  | free(p); | 
| 73 |  | } | 
| 74 |  |  | 
| 80 |  | STREAMOUT *getostream(const char *ospec, const char *mname, int bn, int noopen); | 
| 81 |  | int ofname(char *oname, const char *ospec, const char *mname, int bn); | 
| 82 |  | void printheader(FILE *fout, const char *info); | 
| 83 | < | void printresolu(FILE *fout); | 
| 83 | > | void printresolu(FILE *fout, int xr, int yr); | 
| 84 |  |  | 
| 85 |  | /* | 
| 86 |  | * The rcont structure is used to manage i/o with a particular | 
| 99 |  |  | 
| 100 |  | /* rtrace command and defaults */ | 
| 101 |  | char            *rtargv[256+2*MAXMODLIST] = { "rtrace", | 
| 102 | < | "-dj", ".5", "-dr", "3", | 
| 103 | < | "-ab", "1", "-ad", "128", }; | 
| 102 | > | "-dj", ".9", "-dr", "3", | 
| 103 | > | "-ab", "1", "-ad", "350", }; | 
| 104 | > |  | 
| 105 |  | int  rtargc = 9; | 
| 106 |  | /* overriding rtrace options */ | 
| 107 | < | char            *myrtopts[] = { "-o~~TmWdp", "-h-", "-x", "1", "-y", "0", | 
| 107 | > | char            *myrtopts[] = { "-h-", "-x", "1", "-y", "0", | 
| 108 |  | "-dt", "0", "-as", "0", "-aa", "0", NULL }; | 
| 109 |  |  | 
| 110 | + | #define RTCOEFF         "-o~~TmWdp"     /* compute coefficients only */ | 
| 111 | + | #define RTCONTRIB       "-o~~TmVdp"     /* compute ray contributions */ | 
| 112 | + |  | 
| 113 |  | struct rtproc   rt0;                    /* head of rtrace process list */ | 
| 114 |  |  | 
| 115 |  | struct rtproc   *rt_unproc = NULL;      /* unprocessed ray trees */ | 
| 116 |  |  | 
| 117 | < | char    persistfn[] = "pfXXXXXX";       /* persist file name */ | 
| 117 | > | #define PERSIST_NONE    0               /* no persist file */ | 
| 118 | > | #define PERSIST_SINGLE  1               /* user set -P persist */ | 
| 119 | > | #define PERSIST_PARALL  2               /* user set -PP persist */ | 
| 120 | > | #define PERSIST_OURS    3               /* -PP persist belongs to us */ | 
| 121 | > | int     persist_state = PERSIST_NONE;   /* persist file state */ | 
| 122 | > | char    persistfn[] = "pfXXXXXX";       /* our persist file name, if set */ | 
| 123 |  |  | 
| 124 |  | int             gargc;                  /* global argc */ | 
| 125 |  | char            **gargv;                /* global argv */ | 
| 131 |  | int             outfmt = 'a';           /* output format */ | 
| 132 |  |  | 
| 133 |  | int             header = 1;             /* output header? */ | 
| 134 | + | int             force_open = 0;         /* truncate existing output? */ | 
| 135 | + | int             recover = 0;            /* recover previous output? */ | 
| 136 | + | int             accumulate = 1;         /* input rays per output record */ | 
| 137 |  | int             xres = 0;               /* horiz. output resolution */ | 
| 138 |  | int             yres = 0;               /* vert. output resolution */ | 
| 139 |  |  | 
| 140 | + | int             account;                /* current accumulation count */ | 
| 141 |  | unsigned long   raysleft;               /* number of rays left to trace */ | 
| 142 |  | long            waitflush;              /* how long until next flush */ | 
| 143 |  |  | 
| 157 |  |  | 
| 158 |  | void init(int np); | 
| 159 |  | int done_rprocs(struct rtproc *rtp); | 
| 160 | + | void reload_output(void); | 
| 161 |  | void recover_output(FILE *fin); | 
| 162 |  | void trace_contribs(FILE *fin); | 
| 163 |  | struct rtproc *wait_rproc(void); | 
| 167 |  |  | 
| 168 |  | void put_contrib(const DCOLOR cnt, FILE *fout); | 
| 169 |  | void add_contrib(const char *modn); | 
| 170 | < | void done_contrib(void); | 
| 170 | > | void done_contrib(int navg); | 
| 171 |  |  | 
| 172 |  | /* return number of open rtrace processes */ | 
| 173 |  | static int | 
| 220 |  | int | 
| 221 |  | main(int argc, char *argv[]) | 
| 222 |  | { | 
| 223 | + | int     contrib = 0; | 
| 224 |  | int     nprocs = 1; | 
| 208 | – | int     recover = 0; | 
| 225 |  | char    *curout = NULL; | 
| 226 |  | char    *binval = NULL; | 
| 227 |  | int     bincnt = 0; | 
| 230 |  | /* need at least one argument */ | 
| 231 |  | if (argc < 2) { | 
| 232 |  | fprintf(stderr, | 
| 233 | < | "Usage: %s [-n nprocs][-r][-e expr][-f source][-o ospec][-b binv] {-m mod | -M file} [rtrace options] octree\n", | 
| 233 | > | "Usage: %s [-n nprocs][-V][-r][-e expr][-f source][-o ospec][-b binv] {-m mod | -M file} [rtrace options] octree\n", | 
| 234 |  | argv[0]); | 
| 235 |  | exit(1); | 
| 236 |  | } | 
| 246 |  | while ((j = expandarg(&argc, &argv, i)) > 0) | 
| 247 |  | ; | 
| 248 |  | if (j < 0) { | 
| 249 | < | fprintf(stderr, "%s: cannot expand '%s'", | 
| 249 | > | fprintf(stderr, "%s: cannot expand '%s'\n", | 
| 250 |  | argv[0], argv[i]); | 
| 251 |  | exit(1); | 
| 252 |  | } | 
| 253 |  | if (argv[i][0] == '-') | 
| 254 |  | switch (argv[i][1]) { | 
| 239 | – | case 'r':               /* recover output */ | 
| 240 | – | if (argv[i][2]) break; | 
| 241 | – | recover++; | 
| 242 | – | continue; | 
| 255 |  | case 'n':               /* number of processes */ | 
| 256 |  | if (argv[i][2] || i >= argc-2) break; | 
| 257 |  | nprocs = atoi(argv[++i]); | 
| 258 |  | if (nprocs <= 0) | 
| 259 |  | error(USER, "illegal number of processes"); | 
| 260 |  | continue; | 
| 261 | + | case 'V':               /* output contributions */ | 
| 262 | + | switch (argv[i][2]) { | 
| 263 | + | case '\0': | 
| 264 | + | contrib = !contrib; | 
| 265 | + | continue; | 
| 266 | + | case '+': case '1': | 
| 267 | + | case 'T': case 't': | 
| 268 | + | case 'Y': case 'y': | 
| 269 | + | contrib = 1; | 
| 270 | + | continue; | 
| 271 | + | case '-': case '0': | 
| 272 | + | case 'F': case 'f': | 
| 273 | + | case 'N': case 'n': | 
| 274 | + | contrib = 0; | 
| 275 | + | continue; | 
| 276 | + | } | 
| 277 | + | break; | 
| 278 | + | case 'c':               /* input rays per output */ | 
| 279 | + | if (argv[i][2] || i >= argc-2) break; | 
| 280 | + | accumulate = atoi(argv[++i]); | 
| 281 | + | continue; | 
| 282 | + | case 'r':               /* recover output */ | 
| 283 | + | if (argv[i][2]) break; | 
| 284 | + | recover = 1; | 
| 285 | + | continue; | 
| 286 |  | case 'h':               /* output header? */ | 
| 287 |  | switch (argv[i][2]) { | 
| 288 |  | case '\0': | 
| 300 |  | continue; | 
| 301 |  | } | 
| 302 |  | break; | 
| 303 | < | case 'f':               /* file or i/o format */ | 
| 303 | > | case 'f':               /* file or force or format */ | 
| 304 |  | if (!argv[i][2]) { | 
| 305 |  | char    *fpath; | 
| 306 |  | if (i >= argc-2) break; | 
| 315 |  | fcompile(fpath); | 
| 316 |  | continue; | 
| 317 |  | } | 
| 318 | + | if (argv[i][2] == 'o') { | 
| 319 | + | force_open = 1; | 
| 320 | + | continue; | 
| 321 | + | } | 
| 322 |  | setformat(argv[i]+2); | 
| 323 |  | continue; | 
| 324 |  | case 'e':               /* expression */ | 
| 340 |  | case 'b':               /* bin expression/count */ | 
| 341 |  | if (i >= argc-2) break; | 
| 342 |  | if (argv[i][2] == 'n') { | 
| 343 | < | bincnt = atoi(argv[++i]); | 
| 343 | > | bincnt = (int)(eval(argv[++i]) + .5); | 
| 344 |  | continue; | 
| 345 |  | } | 
| 346 |  | if (argv[i][2]) break; | 
| 358 |  | rtargv[rtargc++] = argv[++i]; | 
| 359 |  | addmodfile(argv[i], curout, binval, bincnt); | 
| 360 |  | continue; | 
| 361 | + | case 'P':               /* persist file */ | 
| 362 | + | if (i >= argc-2) break; | 
| 363 | + | persist_state = (argv[i][2] == 'P') ? | 
| 364 | + | PERSIST_PARALL : PERSIST_SINGLE; | 
| 365 | + | rtargv[rtargc++] = argv[i]; | 
| 366 | + | rtargv[rtargc++] = argv[++i]; | 
| 367 | + | continue; | 
| 368 |  | } | 
| 369 |  | rtargv[rtargc++] = argv[i];     /* assume rtrace option */ | 
| 370 |  | } | 
| 371 | + | if (accumulate <= 0)    /* no output flushing for single record */ | 
| 372 | + | xres = yres = 0; | 
| 373 |  | /* set global argument list */ | 
| 374 |  | gargc = argc; gargv = argv; | 
| 375 |  | /* add "mandatory" rtrace settings */ | 
| 376 |  | for (j = 0; myrtopts[j] != NULL; j++) | 
| 377 |  | rtargv[rtargc++] = myrtopts[j]; | 
| 378 | + | rtargv[rtargc++] = contrib ? RTCONTRIB : RTCOEFF; | 
| 379 |  | /* just asking for defaults? */ | 
| 380 |  | if (!strcmp(argv[i], "-defaults")) { | 
| 381 |  | char    sxres[16], syres[16]; | 
| 382 |  | char    *rtpath; | 
| 383 | < | printf("-n  %-2d\t\t\t\t# number of processes\n", nprocs); | 
| 383 | > | printf("-n %-2d\t\t\t\t# number of processes\n", nprocs); | 
| 384 | > | printf("-c %-5d\t\t\t# accumulated rays per record\n", | 
| 385 | > | accumulate); | 
| 386 | > | printf("-V%c\t\t\t\t# output %s\n", contrib ? '+' : '-', | 
| 387 | > | contrib ? "contributions" : "coefficients"); | 
| 388 |  | fflush(stdout);                 /* report OUR options */ | 
| 389 |  | rtargv[rtargc++] = header ? "-h+" : "-h-"; | 
| 390 |  | sprintf(fmt, "-f%c%c", inpfmt, outfmt); | 
| 395 |  | rtargv[rtargc++] = "-y"; | 
| 396 |  | sprintf(syres, "%d", yres); | 
| 397 |  | rtargv[rtargc++] = syres; | 
| 343 | – | rtargv[rtargc++] = "-oTW"; | 
| 398 |  | rtargv[rtargc++] = "-defaults"; | 
| 399 |  | rtargv[rtargc] = NULL; | 
| 400 |  | rtpath = getpath(rtargv[0], getenv("PATH"), X_OK); | 
| 408 |  | exit(1); | 
| 409 |  | } | 
| 410 |  | if (nprocs > 1) {       /* add persist file if parallel */ | 
| 411 | < | rtargv[rtargc++] = "-PP"; | 
| 412 | < | rtargv[rtargc++] = mktemp(persistfn); | 
| 411 | > | if (persist_state == PERSIST_SINGLE) | 
| 412 | > | error(USER, "use -PP option for multiple processes"); | 
| 413 | > | if (persist_state == PERSIST_NONE) { | 
| 414 | > | rtargv[rtargc++] = "-PP"; | 
| 415 | > | rtargv[rtargc++] = mktemp(persistfn); | 
| 416 | > | persist_state = PERSIST_OURS; | 
| 417 | > | } | 
| 418 |  | } | 
| 419 |  | /* add format string */ | 
| 420 |  | sprintf(fmt, "-f%cf", inpfmt); | 
| 424 |  | error(USER, "missing octree argument"); | 
| 425 |  | rtargv[rtargc++] = octree = argv[i]; | 
| 426 |  | rtargv[rtargc] = NULL; | 
| 427 | < | /* start rtrace */ | 
| 427 | > | /* start rtrace & recover if requested */ | 
| 428 |  | init(nprocs); | 
| 429 | < | if (recover)            /* perform recovery if requested */ | 
| 430 | < | recover_output(stdin); | 
| 431 | < | trace_contribs(stdin);  /* compute contributions */ | 
| 429 | > | /* compute contributions */ | 
| 430 | > | trace_contribs(stdin); | 
| 431 | > | /* clean up */ | 
| 432 |  | quit(0); | 
| 433 |  | } | 
| 434 |  |  | 
| 474 |  | { | 
| 475 |  | int     rtstat; | 
| 476 |  |  | 
| 477 | < | if (rt0.next != NULL)           /* terminate persistent rtrace */ | 
| 477 | > | if (persist_state == PERSIST_OURS)  /* terminate waiting rtrace */ | 
| 478 |  | killpersist(); | 
| 479 |  | /* clean up rtrace process(es) */ | 
| 480 |  | rtstat = done_rprocs(&rt0); | 
| 541 |  | raysleft = yres; | 
| 542 |  | } else | 
| 543 |  | raysleft = 0; | 
| 544 | + | if ((account = accumulate) > 0) | 
| 545 | + | raysleft *= accumulate; | 
| 546 |  | waitflush = xres; | 
| 547 | + | if (!recover) | 
| 548 | + | return; | 
| 549 | + | /* recover previous values */ | 
| 550 | + | if (accumulate <= 0) | 
| 551 | + | reload_output(); | 
| 552 | + | else | 
| 553 | + | recover_output(stdin); | 
| 554 |  | } | 
| 555 |  |  | 
| 556 |  | /* grow modifier to accommodate more bins */ | 
| 580 |  | error(USER, errmsg); | 
| 581 |  | } | 
| 582 |  | if (nmods >= MAXMODLIST) | 
| 583 | < | error(USER, "too many modifiers"); | 
| 583 | > | error(INTERNAL, "too many modifiers"); | 
| 584 |  | modname[nmods++] = modn;        /* XXX assumes static string */ | 
| 585 |  | lep->key = modn;                /* XXX assumes static string */ | 
| 586 |  | mp = (MODCONT *)malloc(sizeof(MODCONT)); | 
| 588 |  | error(SYSTEM, "out of memory in addmodifier"); | 
| 589 |  | mp->outspec = outf;             /* XXX assumes static string */ | 
| 590 |  | mp->modname = modn;             /* XXX assumes static string */ | 
| 591 | < | if (binv != NULL) | 
| 592 | < | mp->binv = eparse(binv); | 
| 593 | < | else | 
| 594 | < | mp->binv = eparse("0"); | 
| 595 | < | mp->nbins = 1; | 
| 591 | > | if (binv == NULL) | 
| 592 | > | binv = "0";             /* use single bin if unspecified */ | 
| 593 | > | mp->binv = eparse(binv); | 
| 594 | > | if (mp->binv->type == NUM) {    /* check value if constant */ | 
| 595 | > | bincnt = (int)(evalue(mp->binv) + 1.5); | 
| 596 | > | if (bincnt != 1) { | 
| 597 | > | sprintf(errmsg, "illegal non-zero constant for bin (%s)", | 
| 598 | > | binv); | 
| 599 | > | error(USER, errmsg); | 
| 600 | > | } | 
| 601 | > | } | 
| 602 | > | mp->nbins = 1;                  /* initialize results holder */ | 
| 603 |  | setcolor(mp->cbin[0], 0., 0., 0.); | 
| 604 | < | if (mp->binv->type == NUM)      /* assume one bin if constant */ | 
| 530 | < | bincnt = 1; | 
| 531 | < | else if (bincnt > 1) | 
| 604 | > | if (bincnt > 1) | 
| 605 |  | mp = growmodifier(mp, bincnt); | 
| 606 |  | lep->data = (char *)mp; | 
| 607 |  | /* allocate output streams */ | 
| 608 | < | for (i = outf==NULL || outf[0]=='!' ? 0 : bincnt; i--; ) | 
| 608 | > | for (i = bincnt; i-- > 0; ) | 
| 609 |  | getostream(mp->outspec, mp->modname, i, 1); | 
| 610 |  | return mp; | 
| 611 |  | } | 
| 664 |  | mnp = cp; | 
| 665 |  | break; | 
| 666 |  | case 'd': | 
| 667 | + | case 'i': | 
| 668 | + | case 'o': | 
| 669 | + | case 'x': | 
| 670 | + | case 'X': | 
| 671 |  | if (bnp != NULL) | 
| 672 |  | return -1; | 
| 673 |  | bnp = cp; | 
| 730 |  |  | 
| 731 |  | /* write resolution string to given output stream */ | 
| 732 |  | void | 
| 733 | < | printresolu(FILE *fout) | 
| 733 | > | printresolu(FILE *fout, int xr, int yr) | 
| 734 |  | { | 
| 735 | < | if (xres > 0) { | 
| 736 | < | if (yres > 0)                   /* resolution string */ | 
| 737 | < | fprtresolu(xres, yres, fout); | 
| 735 | > | if ((xr > 0) & (yr > 0))        /* resolution string */ | 
| 736 | > | fprtresolu(xr, yr, fout); | 
| 737 | > | if (xres > 0)                   /* global flush flag */ | 
| 738 |  | fflush(fout); | 
| 662 | – | } | 
| 739 |  | } | 
| 740 |  |  | 
| 741 |  | /* Get output stream pointer (open and write header if new and noopen==0) */ | 
| 742 |  | STREAMOUT * | 
| 743 |  | getostream(const char *ospec, const char *mname, int bn, int noopen) | 
| 744 |  | { | 
| 745 | + | static const DCOLOR     nocontrib = BLKCOLOR; | 
| 746 |  | static STREAMOUT        stdos; | 
| 747 |  | int                     ofl; | 
| 748 |  | char                    oname[1024]; | 
| 751 |  |  | 
| 752 |  | if (ospec == NULL) {                    /* use stdout? */ | 
| 753 |  | if (!noopen && !using_stdout) { | 
| 677 | – | stdos.reclen = 0; | 
| 754 |  | if (outfmt != 'a') | 
| 755 |  | SET_FILE_BINARY(stdout); | 
| 756 |  | if (header) | 
| 757 |  | printheader(stdout, NULL); | 
| 758 | < | printresolu(stdout); | 
| 758 | > | printresolu(stdout, xres, yres); | 
| 759 | > | stdos.xr = xres; stdos.yr = yres; | 
| 760 |  | using_stdout = 1; | 
| 761 |  | } | 
| 762 |  | stdos.ofp = stdout; | 
| 776 |  | sop = (STREAMOUT *)malloc(sizeof(STREAMOUT)); | 
| 777 |  | if (sop == NULL) | 
| 778 |  | error(SYSTEM, "out of memory in getostream"); | 
| 779 | < | sop->reclen = oname[0] == '!' ? CNT_PIPE : 0; | 
| 779 | > | sop->outpipe = oname[0] == '!'; | 
| 780 | > | sop->reclen = 0; | 
| 781 |  | sop->ofp = NULL;                /* open iff noopen==0 */ | 
| 782 | + | sop->xr = xres; sop->yr = yres; | 
| 783 |  | lep->data = (char *)sop; | 
| 784 | + | if (!sop->outpipe & !force_open & !recover && | 
| 785 | + | access(oname, F_OK) == 0) { | 
| 786 | + | errno = EEXIST;         /* file exists */ | 
| 787 | + | goto openerr; | 
| 788 | + | } | 
| 789 |  | } | 
| 790 |  | if (!noopen && sop->ofp == NULL) {      /* open output stream */ | 
| 791 | < | int             i; | 
| 791 | > | long            i; | 
| 792 |  | if (oname[0] == '!')            /* output to command */ | 
| 793 |  | sop->ofp = popen(oname+1, "w"); | 
| 794 | < | else | 
| 794 | > | else                            /* else open file */ | 
| 795 |  | sop->ofp = fopen(oname, "w"); | 
| 796 | < | if (sop->ofp == NULL) { | 
| 797 | < | sprintf(errmsg, "cannot open '%s' for writing", oname); | 
| 714 | < | error(SYSTEM, errmsg); | 
| 715 | < | } | 
| 796 | > | if (sop->ofp == NULL) | 
| 797 | > | goto openerr; | 
| 798 |  | if (outfmt != 'a') | 
| 799 |  | SET_FILE_BINARY(sop->ofp); | 
| 800 |  | if (header) { | 
| 811 |  | *cp = '\0'; | 
| 812 |  | printheader(sop->ofp, info); | 
| 813 |  | } | 
| 814 | < | printresolu(sop->ofp); | 
| 814 | > | if (accumulate > 0) {           /* global resolution */ | 
| 815 | > | sop->xr = xres; sop->yr = yres; | 
| 816 | > | } | 
| 817 | > | printresolu(sop->ofp, sop->xr, sop->yr); | 
| 818 |  | /* play catch-up */ | 
| 819 | < | for (i = 0; i < lastdone; i++) { | 
| 820 | < | static const DCOLOR     nocontrib = BLKCOLOR; | 
| 821 | < | put_contrib(nocontrib, sop->ofp); | 
| 819 | > | for (i = accumulate > 0 ? lastdone/accumulate : 0; i--; ) { | 
| 820 | > | int     j = sop->reclen; | 
| 821 | > | if (j <= 0) j = 1; | 
| 822 | > | while (j--) | 
| 823 | > | put_contrib(nocontrib, sop->ofp); | 
| 824 |  | if (outfmt == 'a') | 
| 825 |  | putc('\n', sop->ofp); | 
| 826 |  | } | 
| 827 |  | if (xres > 0) | 
| 828 |  | fflush(sop->ofp); | 
| 829 |  | } | 
| 830 | < | if (sop->reclen != CNT_PIPE)            /* add to length if noopen */ | 
| 831 | < | sop->reclen += noopen; | 
| 832 | < | return sop;                             /* return open stream */ | 
| 830 | > | sop->reclen += noopen;                  /* add to length if noopen */ | 
| 831 | > | return sop;                             /* return output stream */ | 
| 832 | > | openerr: | 
| 833 | > | sprintf(errmsg, "cannot open '%s' for writing", oname); | 
| 834 | > | error(SYSTEM, errmsg); | 
| 835 | > | return NULL;    /* pro forma return */ | 
| 836 |  | } | 
| 837 |  |  | 
| 838 |  | /* read input ray into buffer */ | 
| 839 |  | int | 
| 840 |  | getinp(char *buf, FILE *fp) | 
| 841 |  | { | 
| 842 | + | double  dv[3], *dvp; | 
| 843 | + | float   *fvp; | 
| 844 |  | char    *cp; | 
| 845 |  | int     i; | 
| 846 |  |  | 
| 849 |  | cp = buf;               /* make sure we get 6 floats */ | 
| 850 |  | for (i = 0; i < 6; i++) { | 
| 851 |  | if (fgetword(cp, buf+127-cp, fp) == NULL) | 
| 852 | < | return 0; | 
| 852 | > | return -1; | 
| 853 | > | if (i >= 3) | 
| 854 | > | dv[i-3] = atof(cp); | 
| 855 |  | if ((cp = fskip(cp)) == NULL || *cp) | 
| 856 | < | return 0; | 
| 856 | > | return -1; | 
| 857 |  | *cp++ = ' '; | 
| 858 |  | } | 
| 859 |  | getc(fp);               /* get/put eol */ | 
| 860 |  | *cp-- = '\0'; *cp = '\n'; | 
| 861 | + | if (DOT(dv,dv) <= FTINY*FTINY) | 
| 862 | + | return 0;       /* dummy ray */ | 
| 863 |  | return strlen(buf); | 
| 864 |  | case 'f': | 
| 865 |  | if (fread(buf, sizeof(float), 6, fp) < 6) | 
| 866 | < | return 0; | 
| 866 | > | return -1; | 
| 867 | > | fvp = (float *)buf + 3; | 
| 868 | > | if (DOT(fvp,fvp) <= FTINY*FTINY) | 
| 869 | > | return 0;       /* dummy ray */ | 
| 870 |  | return sizeof(float)*6; | 
| 871 |  | case 'd': | 
| 872 |  | if (fread(buf, sizeof(double), 6, fp) < 6) | 
| 873 | < | return 0; | 
| 873 | > | return -1; | 
| 874 | > | dvp = (double *)buf + 3; | 
| 875 | > | if (DOT(dvp,dvp) <= FTINY*FTINY) | 
| 876 | > | return 0;       /* dummy ray */ | 
| 877 |  | return sizeof(double)*6; | 
| 878 |  | } | 
| 879 |  | error(INTERNAL, "botched input format"); | 
| 880 | < | return 0;       /* pro forma return */ | 
| 880 | > | return -1;      /* pro forma return */ | 
| 881 |  | } | 
| 882 |  |  | 
| 883 |  | static float    rparams[9];             /* traced ray parameters */ | 
| 941 |  | fprintf(fout, "%.6e\t%.6e\t%.6e\t", cnt[0], cnt[1], cnt[2]); | 
| 942 |  | break; | 
| 943 |  | case 'f': | 
| 944 | < | fv[0] = cnt[0]; | 
| 843 | < | fv[1] = cnt[1]; | 
| 844 | < | fv[2] = cnt[2]; | 
| 944 | > | copycolor(fv, cnt); | 
| 945 |  | fwrite(fv, sizeof(float), 3, fout); | 
| 946 |  | break; | 
| 947 |  | case 'd': | 
| 956 |  | } | 
| 957 |  | } | 
| 958 |  |  | 
| 959 | < | /* output ray tallies and clear for next primary */ | 
| 959 | > | /* output ray tallies and clear for next accumulation */ | 
| 960 |  | void | 
| 961 | < | done_contrib(void) | 
| 961 | > | done_contrib(int navg) | 
| 962 |  | { | 
| 963 | + | double          sf = 1.; | 
| 964 |  | int             i, j; | 
| 965 |  | MODCONT         *mp; | 
| 966 |  | STREAMOUT       *sop; | 
| 967 | + | /* set average scaling */ | 
| 968 | + | if (navg > 1) | 
| 969 | + | sf = 1. / (double)navg; | 
| 970 |  | /* output modifiers in order */ | 
| 971 |  | for (i = 0; i < nmods; i++) { | 
| 972 |  | mp = (MODCONT *)lu_find(&modconttab,modname[i])->data; | 
| 973 | + | if (navg > 1)                   /* average scaling */ | 
| 974 | + | for (j = mp->nbins; j--; ) | 
| 975 | + | scalecolor(mp->cbin[j], sf); | 
| 976 |  | sop = getostream(mp->outspec, mp->modname, 0,0); | 
| 977 |  | put_contrib(mp->cbin[0], sop->ofp); | 
| 978 |  | if (mp->nbins > 3 &&            /* minor optimization */ | 
| 983 |  | for (j = 1; j < mp->nbins; j++) | 
| 984 |  | put_contrib(mp->cbin[j], | 
| 985 |  | getostream(mp->outspec,mp->modname,j,0)->ofp); | 
| 986 | < | /* clear for next ray tree */ | 
| 986 | > | /* clear for next time */ | 
| 987 |  | memset(mp->cbin, 0, sizeof(DCOLOR)*mp->nbins); | 
| 988 |  | } | 
| 989 |  | --waitflush;                            /* terminate records */ | 
| 1042 |  | if (!n || !(isalpha(*cp) | (*cp == '_'))) | 
| 1043 |  | error(USER, "bad modifier name from rtrace"); | 
| 1044 |  | /* get modifier name */ | 
| 1045 | < | while (n > 0 && *cp != '\t') { | 
| 1045 | > | while (n > 1 && *cp != '\t') { | 
| 1046 | > | if (mnp - modname >= sizeof(modname)-2) | 
| 1047 | > | error(INTERNAL, "modifier name too long"); | 
| 1048 |  | *mnp++ = *cp++; n--; | 
| 1049 |  | } | 
| 1050 |  | *mnp = '\0'; | 
| 1056 |  | cp += sizeof(float)*9; n -= sizeof(float)*9; | 
| 1057 |  | add_contrib(modname); | 
| 1058 |  | } | 
| 1059 | < | done_contrib();         /* sum up contributions & output */ | 
| 1059 | > | /* time to produce record? */ | 
| 1060 | > | if (account > 0 && !--account) | 
| 1061 | > | done_contrib(account = accumulate); | 
| 1062 |  | lastdone = rtp->raynum; | 
| 1063 | < | free(rtp->buf);         /* free up buffer space */ | 
| 1063 | > | if (rtp->buf != NULL)   /* free up buffer space */ | 
| 1064 | > | free(rtp->buf); | 
| 1065 |  | rt_unproc = rtp->next; | 
| 1066 |  | free(rtp);              /* done with this ray tree */ | 
| 1067 |  | } | 
| 1145 |  | void | 
| 1146 |  | trace_contribs(FILE *fin) | 
| 1147 |  | { | 
| 1148 | + | static int      ignore_warning_given = 0; | 
| 1149 |  | char            inpbuf[128]; | 
| 1150 |  | int             iblen; | 
| 1151 |  | struct rtproc   *rtp; | 
| 1152 |  | /* loop over input */ | 
| 1153 | < | while ((iblen = getinp(inpbuf, fin)) > 0) { | 
| 1154 | < | if (lastray+1 < lastray ||      /* need reset? */ | 
| 1155 | < | queue_length() > 5*nrtprocs()) { | 
| 1153 | > | while ((iblen = getinp(inpbuf, fin)) >= 0) { | 
| 1154 | > | if (!iblen && accumulate != 1) { | 
| 1155 | > | if (!ignore_warning_given++) | 
| 1156 | > | error(WARNING, | 
| 1157 | > | "dummy ray(s) ignored during accumulation\n"); | 
| 1158 | > | continue; | 
| 1159 | > | } | 
| 1160 | > | if (!iblen ||                   /* need flush/reset? */ | 
| 1161 | > | queue_length() > 10*nrtprocs() || | 
| 1162 | > | lastray+1 < lastray) { | 
| 1163 |  | while (wait_rproc() != NULL) | 
| 1164 |  | process_queue(); | 
| 1165 | < | lastdone = lastray = 0; | 
| 1165 | > | if (lastray+1 < lastray) | 
| 1166 | > | lastdone = lastray = 0; | 
| 1167 |  | } | 
| 1168 |  | rtp = get_rproc();              /* get avail. rtrace process */ | 
| 1169 | < | rtp->raynum = ++lastray;        /* assign ray to it */ | 
| 1170 | < | writebuf(rtp->pd.w, inpbuf, iblen); | 
| 1171 | < | if (raysleft && !--raysleft) | 
| 1172 | < | break; | 
| 1169 | > | rtp->raynum = ++lastray;        /* assign ray */ | 
| 1170 | > | if (iblen) {                    /* trace ray if valid */ | 
| 1171 | > | writebuf(rtp->pd.w, inpbuf, iblen); | 
| 1172 | > | } else {                        /* else bypass dummy ray */ | 
| 1173 | > | queue_raytree(rtp);     /* queue empty ray/record */ | 
| 1174 | > | if ((yres <= 0) | (xres <= 0)) | 
| 1175 | > | waitflush = 1;  /* flush right after */ | 
| 1176 | > | } | 
| 1177 |  | process_queue();                /* catch up with results */ | 
| 1178 | + | if (raysleft && !--raysleft) | 
| 1179 | + | break;                  /* preemptive EOI */ | 
| 1180 |  | } | 
| 1181 |  | while (wait_rproc() != NULL)            /* process outstanding rays */ | 
| 1182 |  | process_queue(); | 
| 1183 | + | if (accumulate <= 0) | 
| 1184 | + | done_contrib(0);                /* output tallies */ | 
| 1185 | + | else if (account < accumulate) { | 
| 1186 | + | error(WARNING, "partial accumulation in final record"); | 
| 1187 | + | done_contrib(accumulate - account); | 
| 1188 | + | } | 
| 1189 |  | if (raysleft) | 
| 1190 |  | error(USER, "unexpected EOF on input"); | 
| 1191 |  | lu_done(&ofiletab);                     /* close output files */ | 
| 1192 |  | } | 
| 1193 |  |  | 
| 1194 | + | /* get ray contribution from previous file */ | 
| 1195 | + | static int | 
| 1196 | + | get_contrib(DCOLOR cnt, FILE *finp) | 
| 1197 | + | { | 
| 1198 | + | COLOR   fv; | 
| 1199 | + | COLR    cv; | 
| 1200 | + |  | 
| 1201 | + | switch (outfmt) { | 
| 1202 | + | case 'a': | 
| 1203 | + | return(fscanf(finp,"%lf %lf %lf",&cnt[0],&cnt[1],&cnt[2]) == 3); | 
| 1204 | + | case 'f': | 
| 1205 | + | if (fread(fv, sizeof(fv[0]), 3, finp) != 3) | 
| 1206 | + | return(0); | 
| 1207 | + | copycolor(cnt, fv); | 
| 1208 | + | return(1); | 
| 1209 | + | case 'd': | 
| 1210 | + | return(fread(cnt, sizeof(cnt[0]), 3, finp) == 3); | 
| 1211 | + | case 'c': | 
| 1212 | + | if (fread(cv, sizeof(cv), 1, finp) != 1) | 
| 1213 | + | return(0); | 
| 1214 | + | colr_color(fv, cv); | 
| 1215 | + | copycolor(cnt, fv); | 
| 1216 | + | return(1); | 
| 1217 | + | default: | 
| 1218 | + | error(INTERNAL, "botched output format"); | 
| 1219 | + | } | 
| 1220 | + | return(0);      /* pro forma return */ | 
| 1221 | + | } | 
| 1222 | + |  | 
| 1223 | + | /* close output file opened for input */ | 
| 1224 | + | static int | 
| 1225 | + | myclose(const LUENT *e, void *p) | 
| 1226 | + | { | 
| 1227 | + | STREAMOUT       *sop = (STREAMOUT *)e->data; | 
| 1228 | + |  | 
| 1229 | + | if (sop->ofp == NULL) | 
| 1230 | + | return(0); | 
| 1231 | + | fclose(sop->ofp); | 
| 1232 | + | sop->ofp = NULL; | 
| 1233 | + | return(0); | 
| 1234 | + | } | 
| 1235 | + |  | 
| 1236 | + | /* load previously accumulated values */ | 
| 1237 | + | void | 
| 1238 | + | reload_output(void) | 
| 1239 | + | { | 
| 1240 | + | int             i, j; | 
| 1241 | + | MODCONT         *mp; | 
| 1242 | + | int             ofl; | 
| 1243 | + | char            oname[1024]; | 
| 1244 | + | char            *fmode = "rb"; | 
| 1245 | + | char            *outvfmt; | 
| 1246 | + | LUENT           *ment, *oent; | 
| 1247 | + | int             xr, yr; | 
| 1248 | + | STREAMOUT       sout; | 
| 1249 | + | DCOLOR          rgbv; | 
| 1250 | + |  | 
| 1251 | + | switch (outfmt) { | 
| 1252 | + | case 'a': | 
| 1253 | + | outvfmt = "ascii"; | 
| 1254 | + | fmode = "r"; | 
| 1255 | + | break; | 
| 1256 | + | case 'f': | 
| 1257 | + | outvfmt = "float"; | 
| 1258 | + | break; | 
| 1259 | + | case 'd': | 
| 1260 | + | outvfmt = "double"; | 
| 1261 | + | break; | 
| 1262 | + | case 'c': | 
| 1263 | + | outvfmt = COLRFMT; | 
| 1264 | + | break; | 
| 1265 | + | default: | 
| 1266 | + | error(INTERNAL, "botched output format"); | 
| 1267 | + | return; | 
| 1268 | + | } | 
| 1269 | + | /* reload modifier values */ | 
| 1270 | + | for (i = 0; i < nmods; i++) { | 
| 1271 | + | ment = lu_find(&modconttab,modname[i]); | 
| 1272 | + | mp = (MODCONT *)ment->data; | 
| 1273 | + | if (mp->outspec == NULL) | 
| 1274 | + | error(USER, "cannot reload from stdout"); | 
| 1275 | + | if (mp->outspec[0] == '!') | 
| 1276 | + | error(USER, "cannot reload from command"); | 
| 1277 | + | for (j = 0; ; j++) {            /* load each modifier bin */ | 
| 1278 | + | ofl = ofname(oname, mp->outspec, mp->modname, j); | 
| 1279 | + | if (ofl < 0) | 
| 1280 | + | error(USER, "bad output file specification"); | 
| 1281 | + | oent = lu_find(&ofiletab, oname); | 
| 1282 | + | if (oent->data != NULL) { | 
| 1283 | + | sout = *(STREAMOUT *)oent->data; | 
| 1284 | + | } else { | 
| 1285 | + | sout.reclen = 0; | 
| 1286 | + | sout.outpipe = 0; | 
| 1287 | + | sout.xr = xres; sout.yr = yres; | 
| 1288 | + | sout.ofp = NULL; | 
| 1289 | + | } | 
| 1290 | + | if (sout.ofp == NULL) { /* open output as input */ | 
| 1291 | + | sout.ofp = fopen(oname, fmode); | 
| 1292 | + | if (sout.ofp == NULL) { | 
| 1293 | + | if (j) | 
| 1294 | + | break;  /* assume end of modifier */ | 
| 1295 | + | sprintf(errmsg, "missing reload file '%s'", | 
| 1296 | + | oname); | 
| 1297 | + | error(WARNING, errmsg); | 
| 1298 | + | break; | 
| 1299 | + | } | 
| 1300 | + | if (header && checkheader(sout.ofp, outvfmt, NULL) != 1) { | 
| 1301 | + | sprintf(errmsg, "format mismatch for '%s'", | 
| 1302 | + | oname); | 
| 1303 | + | error(USER, errmsg); | 
| 1304 | + | } | 
| 1305 | + | if ((sout.xr > 0) & (sout.yr > 0) && | 
| 1306 | + | (!fscnresolu(&xr, &yr, sout.ofp) || | 
| 1307 | + | (xr != sout.xr) | | 
| 1308 | + | (yr != sout.yr))) { | 
| 1309 | + | sprintf(errmsg, "resolution mismatch for '%s'", | 
| 1310 | + | oname); | 
| 1311 | + | error(USER, errmsg); | 
| 1312 | + | } | 
| 1313 | + | } | 
| 1314 | + | /* read in RGB value */ | 
| 1315 | + | if (!get_contrib(rgbv, sout.ofp)) { | 
| 1316 | + | if (!j) { | 
| 1317 | + | fclose(sout.ofp); | 
| 1318 | + | break;          /* ignore empty file */ | 
| 1319 | + | } | 
| 1320 | + | if (j < mp->nbins) { | 
| 1321 | + | sprintf(errmsg, "missing data in '%s'", | 
| 1322 | + | oname); | 
| 1323 | + | error(USER, errmsg); | 
| 1324 | + | } | 
| 1325 | + | break; | 
| 1326 | + | } | 
| 1327 | + | if (j >= mp->nbins)             /* grow modifier size */ | 
| 1328 | + | ment->data = (char *)(mp = growmodifier(mp, j+1)); | 
| 1329 | + | copycolor(mp->cbin[j], rgbv); | 
| 1330 | + | if (oent->key == NULL)          /* new file entry */ | 
| 1331 | + | oent->key = strcpy((char *) | 
| 1332 | + | malloc(strlen(oname)+1), oname); | 
| 1333 | + | if (oent->data == NULL) | 
| 1334 | + | oent->data = (char *)malloc(sizeof(STREAMOUT)); | 
| 1335 | + | *(STREAMOUT *)oent->data = sout; | 
| 1336 | + | } | 
| 1337 | + | } | 
| 1338 | + | lu_doall(&ofiletab, myclose, NULL);     /* close all files */ | 
| 1339 | + | } | 
| 1340 | + |  | 
| 1341 |  | /* seek on the given output file */ | 
| 1342 |  | static int | 
| 1343 |  | myseeko(const LUENT *e, void *p) | 
| 1351 |  | sprintf(errmsg, "seek error on file '%s'", e->key); | 
| 1352 |  | error(SYSTEM, errmsg); | 
| 1353 |  | } | 
| 1354 | + | return 0; | 
| 1355 |  | } | 
| 1356 |  |  | 
| 1357 |  | /* recover output if possible */ | 
| 1407 |  | sout = *(STREAMOUT *)oent->data; | 
| 1408 |  | } else { | 
| 1409 |  | sout.reclen = 0; | 
| 1410 | + | sout.outpipe = 0; | 
| 1411 |  | sout.ofp = NULL; | 
| 1412 |  | } | 
| 1413 |  | if (sout.ofp != NULL) { /* already open? */ | 
| 1422 |  | break;  /* assume end of modifier */ | 
| 1423 |  | sprintf(errmsg, "missing recover file '%s'", | 
| 1424 |  | oname); | 
| 1425 | < | error(USER, errmsg); | 
| 1425 | > | error(WARNING, errmsg); | 
| 1426 | > | break; | 
| 1427 |  | } | 
| 1428 |  | nvals = lseek(fileno(sout.ofp), 0, SEEK_END); | 
| 1429 |  | if (nvals <= 0) { | 
| 1431 |  | fclose(sout.ofp); | 
| 1432 |  | break; | 
| 1433 |  | } | 
| 1434 | < | if (sout.reclen == CNT_UNKNOWN) { | 
| 1434 | > | if (!sout.reclen) { | 
| 1435 |  | if (!(ofl & OF_BIN)) { | 
| 1436 |  | sprintf(errmsg, | 
| 1437 |  | "need -bn to recover file '%s'", | 
| 1448 |  | oname); | 
| 1449 |  | error(USER, errmsg); | 
| 1450 |  | } | 
| 1451 | < | if ((xres > 0) & (yres > 0) && | 
| 1451 | > | sout.xr = xres; sout.yr = yres; | 
| 1452 | > | if ((sout.xr > 0) & (sout.yr > 0) && | 
| 1453 |  | (!fscnresolu(&xr, &yr, sout.ofp) || | 
| 1454 | < | xr != xres || | 
| 1455 | < | yr != yres)) { | 
| 1454 | > | (xr != sout.xr) | | 
| 1455 | > | (yr != sout.yr))) { | 
| 1456 |  | sprintf(errmsg, "resolution mismatch for '%s'", | 
| 1457 |  | oname); | 
| 1458 |  | error(USER, errmsg); | 
| 1481 |  | error(WARNING, "no output files to recover"); | 
| 1482 |  | return; | 
| 1483 |  | } | 
| 1484 | < | if (raysleft && lastout >= raysleft) { | 
| 1484 | > | if (raysleft && lastout >= raysleft/accumulate) { | 
| 1485 |  | error(WARNING, "output appears to be complete"); | 
| 1486 |  | /* XXX should read & discard input? */ | 
| 1487 |  | quit(0); | 
| 1491 |  | lu_doall(&ofiletab, myseeko, &nvals); | 
| 1492 |  | /* skip repeated input */ | 
| 1493 |  | for (nvals = 0; nvals < lastout; nvals++) | 
| 1494 | < | if (getinp(oname, fin) <= 0) | 
| 1494 | > | if (getinp(oname, fin) < 0) | 
| 1495 |  | error(USER, "unexpected EOF on input"); | 
| 1496 | < | lastray = lastdone = (unsigned long)lastout; | 
| 1496 | > | lastray = lastdone = (unsigned long)lastout * accumulate; | 
| 1497 |  | if (raysleft) | 
| 1498 |  | raysleft -= lastray; | 
| 1499 |  | } |