ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/ranimate.c
Revision: 2.40
Committed: Mon Jul 21 22:30:19 2003 UTC (20 years, 9 months ago) by schorsch
Content type: text/plain
Branch: MAIN
Changes since 2.39: +5 -4 lines
Log Message:
Eliminated copystruct() macro, which is unnecessary in ANSI.
Reduced ambiguity warnings for nested if/if/else clauses.

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: ranimate.c,v 2.39 2003/06/30 14:59:13 schorsch Exp $";
3 #endif
4 /*
5 * Radiance animation control program
6 *
7 * The main difference between this program and ranimove is that
8 * we have many optimizations here for camera motion in static
9 * environments, calling rpict and pinterp on multiple processors,
10 * where ranimove puts its emphasis on object motion, and does
11 * not use any external programs for image generation.
12 *
13 * See the ranimate(1) man page for further details.
14 */
15
16 #include "copyright.h"
17
18 #include <ctype.h>
19 #include <sys/stat.h>
20
21 #include "standard.h"
22 #include "paths.h"
23 #include "view.h"
24 #include "vars.h"
25 #include "netproc.h"
26 /* default blur samples */
27 #ifndef DEF_NBLUR
28 #define DEF_NBLUR 5
29 #endif
30 /* default remote shell */
31 #ifdef _AUX_SOURCE
32 #define REMSH "remsh"
33 #else
34 #define REMSH "rsh"
35 #endif
36 /* input variables (alphabetical by name) */
37 #define ANIMATE 0 /* animation command */
38 #define ARCHIVE 1 /* archiving command */
39 #define BASENAME 2 /* output image base name */
40 #define DIRECTORY 3 /* working (sub)directory */
41 #define DISKSPACE 4 /* how much disk space to use */
42 #define END 5 /* ending frame number */
43 #define EXPOSURE 6 /* how to compute exposure */
44 #define HOST 7 /* rendering host machine */
45 #define INTERP 8 /* # frames to interpolate */
46 #define MBLUR 9 /* motion blur parameters */
47 #define NEXTANIM 10 /* next animation file */
48 #define OCTREE 11 /* octree file name */
49 #define OVERSAMP 12 /* # times to oversample image */
50 #define PFILT 13 /* pfilt options */
51 #define PINTERP 14 /* pinterp options */
52 #define RENDER 15 /* rendering options */
53 #define RESOLUTION 16 /* desired final resolution */
54 #define RIF 17 /* rad input file */
55 #define RSH 18 /* remote shell script or program */
56 #define RTRACE 19 /* use rtrace with pinterp? */
57 #define START 20 /* starting frame number */
58 #define TRANSFER 21 /* frame transfer command */
59 #define VIEWFILE 22 /* animation frame views */
60
61 int NVARS = 23; /* total number of variables */
62
63 VARIABLE vv[] = { /* variable-value pairs */
64 {"ANIMATE", 2, 0, NULL, onevalue},
65 {"ARCHIVE", 2, 0, NULL, onevalue},
66 {"BASENAME", 3, 0, NULL, onevalue},
67 {"DIRECTORY", 3, 0, NULL, onevalue},
68 {"DISKSPACE", 3, 0, NULL, fltvalue},
69 {"END", 2, 0, NULL, intvalue},
70 {"EXPOSURE", 3, 0, NULL, onevalue},
71 {"host", 4, 0, NULL, NULL},
72 {"INTERPOLATE", 3, 0, NULL, intvalue},
73 {"MBLUR", 2, 0, NULL, onevalue},
74 {"NEXTANIM", 3, 0, NULL, onevalue},
75 {"OCTREE", 3, 0, NULL, onevalue},
76 {"OVERSAMPLE", 2, 0, NULL, fltvalue},
77 {"pfilt", 2, 0, NULL, catvalues},
78 {"pinterp", 2, 0, NULL, catvalues},
79 {"render", 3, 0, NULL, catvalues},
80 {"RESOLUTION", 3, 0, NULL, onevalue},
81 {"RIF", 3, 0, NULL, onevalue},
82 {"RSH", 3, 0, NULL, onevalue},
83 {"RTRACE", 2, 0, NULL, boolvalue},
84 {"START", 2, 0, NULL, intvalue},
85 {"TRANSFER", 2, 0, NULL, onevalue},
86 {"VIEWFILE", 2, 0, NULL, onevalue},
87 };
88
89 #define SFNAME "STATUS" /* status file name */
90
91 struct {
92 char host[64]; /* control host name */
93 int pid; /* control process id */
94 char cfname[128]; /* control file name */
95 int rnext; /* next frame to render */
96 int fnext; /* next frame to filter */
97 int tnext; /* next frame to transfer */
98 } astat; /* animation status */
99
100 char *progname; /* our program name */
101 char *cfname; /* our control file name */
102
103 int nowarn = 0; /* turn warnings off? */
104 int silent = 0; /* silent mode? */
105 int noaction = 0; /* take no action? */
106
107 char *remsh; /* remote shell program/script */
108 char rendopt[2048]; /* rendering options */
109 char rresopt[32]; /* rendering resolution options */
110 char fresopt[32]; /* filter resolution options */
111 int pfiltalways; /* always use pfilt? */
112
113 char arcargs[10240]; /* files to archive */
114 char *arcfirst, *arcnext; /* pointers to first and next argument */
115
116 struct pslot {
117 int pid; /* process ID (0 if empty) */
118 int fout; /* output frame number */
119 int (*rcvf)(); /* recover function */
120 } *pslot; /* process slots */
121 int npslots; /* number of process slots */
122
123 #define phostname(ps) ((ps)->hostname[0] ? (ps)->hostname : astat.host)
124
125 struct pslot *findpslot();
126
127 PSERVER *lastpserver; /* last process server with error */
128
129 VIEW *getview();
130 char *getexp(), *dirfile();
131 int getblur();
132
133 extern time_t time();
134
135
136 main(argc, argv)
137 int argc;
138 char *argv[];
139 {
140 int explicate = 0;
141 int i;
142
143 progname = argv[0]; /* get arguments */
144 for (i = 1; i < argc && argv[i][0] == '-'; i++)
145 switch (argv[i][1]) {
146 case 'e': /* print variables */
147 explicate++;
148 break;
149 case 'w': /* turn off warnings */
150 nowarn++;
151 break;
152 case 's': /* silent mode */
153 silent++;
154 break;
155 case 'n': /* take no action */
156 noaction++;
157 break;
158 default:
159 goto userr;
160 }
161 if (i != argc-1)
162 goto userr;
163 cfname = argv[i];
164 /* load variables */
165 loadvars(cfname);
166 /* check variables */
167 checkvalues();
168 /* did we get DIRECTORY? */
169 checkdir();
170 /* check status */
171 if (getastat() < 0) {
172 fprintf(stderr, "%s: exiting\n", progname);
173 quit(1);
174 }
175 /* pfilt always if options given */
176 pfiltalways = vdef(PFILT);
177 /* load RIF if any */
178 if (vdef(RIF))
179 getradfile(vval(RIF));
180 /* set defaults */
181 setdefaults();
182 /* print variables */
183 if (explicate)
184 printvars(stdout);
185 /* set up process servers */
186 sethosts();
187 /* run animation */
188 animate();
189 /* all done */
190 if (vdef(NEXTANIM)) {
191 argv[i] = vval(NEXTANIM); /* just change input file */
192 if (!silent)
193 printargs(argc, argv, stdout);
194 if ((argv[0] = getpath(progname,getenv("PATH"),X_OK)) == NULL)
195 fprintf(stderr, "%s: command not found\n", progname);
196 else
197 execv(progname, argv);
198 quit(1);
199 }
200 quit(0);
201 userr:
202 fprintf(stderr, "Usage: %s [-s][-n][-w][-e] anim_file\n", progname);
203 quit(1);
204 }
205
206
207 getastat() /* check/set animation status */
208 {
209 char sfname[256];
210 FILE *fp;
211
212 sprintf(sfname, "%s/%s", vval(DIRECTORY), SFNAME);
213 if ((fp = fopen(sfname, "r")) == NULL) {
214 if (errno != ENOENT) {
215 perror(sfname);
216 return(-1);
217 }
218 astat.rnext = astat.fnext = astat.tnext = 0;
219 goto setours;
220 }
221 if (fscanf(fp, "Control host: %s\n", astat.host) != 1)
222 goto fmterr;
223 if (fscanf(fp, "Control PID: %d\n", &astat.pid) != 1)
224 goto fmterr;
225 if (fscanf(fp, "Control file: %s\n", astat.cfname) != 1)
226 goto fmterr;
227 if (fscanf(fp, "Next render: %d\n", &astat.rnext) != 1)
228 goto fmterr;
229 if (fscanf(fp, "Next filter: %d\n", &astat.fnext) != 1)
230 goto fmterr;
231 if (fscanf(fp, "Next transfer: %d\n", &astat.tnext) != 1)
232 goto fmterr;
233 fclose(fp);
234 if (astat.pid != 0) { /* thinks it's still running */
235 if (strcmp(myhostname(), astat.host)) {
236 fprintf(stderr,
237 "%s: process %d may still be running on host %s\n",
238 progname, astat.pid, astat.host);
239 return(-1);
240 }
241 if (kill(astat.pid, 0) != -1 || errno != ESRCH) {
242 fprintf(stderr, "%s: process %d is still running\n",
243 progname, astat.pid);
244 return(-1);
245 }
246 /* assume it is dead */
247 }
248 if (strcmp(cfname, astat.cfname) && astat.pid != 0) { /* other's */
249 fprintf(stderr, "%s: unfinished job \"%s\"\n",
250 progname, astat.cfname);
251 return(-1);
252 }
253 /* check control file mods. */
254 if (!nowarn && fdate(cfname) > fdate(sfname))
255 fprintf(stderr,
256 "%s: warning - control file modified since last run\n",
257 progname);
258 setours: /* set our values */
259 strcpy(astat.host, myhostname());
260 astat.pid = getpid();
261 strcpy(astat.cfname, cfname);
262 return(0);
263 fmterr:
264 fprintf(stderr, "%s: format error in status file \"%s\"\n",
265 progname, sfname);
266 fclose(fp);
267 return(-1);
268 }
269
270
271 putastat() /* put out current status */
272 {
273 char buf[256];
274 FILE *fp;
275
276 if (noaction)
277 return;
278 sprintf(buf, "%s/%s", vval(DIRECTORY), SFNAME);
279 if ((fp = fopen(buf, "w")) == NULL) {
280 perror(buf);
281 quit(1);
282 }
283 fprintf(fp, "Control host: %s\n", astat.host);
284 fprintf(fp, "Control PID: %d\n", astat.pid);
285 fprintf(fp, "Control file: %s\n", astat.cfname);
286 fprintf(fp, "Next render: %d\n", astat.rnext);
287 fprintf(fp, "Next filter: %d\n", astat.fnext);
288 fprintf(fp, "Next transfer: %d\n", astat.tnext);
289 fclose(fp);
290 }
291
292
293 checkdir() /* make sure we have our directory */
294 {
295 struct stat stb;
296
297 if (!vdef(DIRECTORY)) {
298 fprintf(stderr, "%s: %s undefined\n",
299 progname, vnam(DIRECTORY));
300 quit(1);
301 }
302 if (stat(vval(DIRECTORY), &stb) == -1) {
303 if (errno == ENOENT && mkdir(vval(DIRECTORY), 0777) == 0)
304 return;
305 perror(vval(DIRECTORY));
306 quit(1);
307 }
308 if (!(stb.st_mode & S_IFDIR)) {
309 fprintf(stderr, "%s: not a directory\n", vval(DIRECTORY));
310 quit(1);
311 }
312 }
313
314
315 setdefaults() /* set default values */
316 {
317 extern char *atos();
318 int decades;
319 char buf[256];
320
321 if (vdef(ANIMATE)) {
322 vval(OCTREE) = NULL;
323 vdef(OCTREE) = 0;
324 } else if (!vdef(OCTREE)) {
325 fprintf(stderr, "%s: either %s or %s must be defined\n",
326 progname, vnam(OCTREE), vnam(ANIMATE));
327 quit(1);
328 }
329 if (!vdef(VIEWFILE)) {
330 fprintf(stderr, "%s: %s undefined\n", progname, vnam(VIEWFILE));
331 quit(1);
332 }
333 if (!vdef(HOST)) {
334 vval(HOST) = LHOSTNAME;
335 vdef(HOST)++;
336 }
337 if (!vdef(START)) {
338 vval(START) = "1";
339 vdef(START)++;
340 }
341 if (!vdef(END)) {
342 sprintf(buf, "%d", countviews()+vint(START)-1);
343 vval(END) = savqstr(buf);
344 vdef(END)++;
345 }
346 if (vint(END) < vint(START)) {
347 fprintf(stderr, "%s: ending frame less than starting frame\n",
348 progname);
349 quit(1);
350 }
351 if (!vdef(BASENAME)) {
352 decades = (int)log10((double)vint(END)) + 1;
353 if (decades < 3) decades = 3;
354 sprintf(buf, "%s/frame%%0%dd", vval(DIRECTORY), decades);
355 vval(BASENAME) = savqstr(buf);
356 vdef(BASENAME)++;
357 }
358 if (!vdef(RESOLUTION)) {
359 vval(RESOLUTION) = "640";
360 vdef(RESOLUTION)++;
361 }
362 if (!vdef(OVERSAMP)) {
363 vval(OVERSAMP) = "2";
364 vdef(OVERSAMP)++;
365 }
366 if (!vdef(INTERP)) {
367 vval(INTERP) = "0";
368 vdef(INTERP)++;
369 }
370 if (!vdef(MBLUR)) {
371 vval(MBLUR) = "0";
372 vdef(MBLUR)++;
373 }
374 if (!vdef(RTRACE)) {
375 vval(RTRACE) = "F";
376 vdef(RTRACE)++;
377 }
378 if (!vdef(DISKSPACE)) {
379 if (!nowarn)
380 fprintf(stderr,
381 "%s: warning - no %s setting, assuming 100 Mbytes available\n",
382 progname, vnam(DISKSPACE));
383 vval(DISKSPACE) = "100";
384 vdef(DISKSPACE)++;
385 }
386 if (!vdef(RSH)) {
387 vval(RSH) = REMSH;
388 vdef(RSH)++;
389 }
390 /* locate remote shell program */
391 atos(buf, sizeof(buf), vval(RSH));
392 if ((remsh = getpath(buf, getenv("PATH"), X_OK)) != NULL)
393 remsh = savqstr(remsh);
394 else
395 remsh = vval(RSH); /* will generate error if used */
396
397 /* append rendering options */
398 if (vdef(RENDER))
399 sprintf(rendopt+strlen(rendopt), " %s", vval(RENDER));
400 }
401
402
403 sethosts() /* set up process servers */
404 {
405 extern char *iskip();
406 char buf[256], *dir, *uname;
407 int np;
408 register char *cp;
409 int i;
410
411 npslots = 0;
412 if (noaction)
413 return;
414 for (i = 0; i < vdef(HOST); i++) { /* add each host */
415 dir = uname = NULL;
416 np = 1;
417 strcpy(cp=buf, nvalue(HOST, i)); /* copy to buffer */
418 cp = sskip(cp); /* skip host name */
419 while (isspace(*cp))
420 *cp++ = '\0';
421 if (*cp) { /* has # processes? */
422 np = atoi(cp);
423 if ((cp = iskip(cp)) == NULL || (*cp && !isspace(*cp)))
424 badvalue(HOST);
425 while (isspace(*cp))
426 cp++;
427 if (*cp) { /* has directory? */
428 dir = cp;
429 cp = sskip(cp); /* skip dir. */
430 while (isspace(*cp))
431 *cp++ = '\0';
432 if (*cp) { /* has user? */
433 uname = cp;
434 if (*sskip(cp))
435 badvalue(HOST);
436 }
437 }
438 }
439 if (addpserver(buf, dir, uname, np) == NULL) {
440 if (!nowarn)
441 fprintf(stderr,
442 "%s: cannot execute on host \"%s\"\n",
443 progname, buf);
444 } else
445 npslots += np;
446 }
447 if (npslots == 0) {
448 fprintf(stderr, "%s: no working process servers\n", progname);
449 quit(1);
450 }
451 pslot = (struct pslot *)calloc(npslots, sizeof(struct pslot));
452 if (pslot == NULL) {
453 perror("malloc");
454 quit(1);
455 }
456 }
457
458
459 getradfile(rfargs) /* run rad and get needed variables */
460 char *rfargs;
461 {
462 static short mvar[] = {OCTREE,PFILT,RESOLUTION,EXPOSURE,-1};
463 char combuf[256];
464 register int i;
465 register char *cp;
466 char *pippt;
467 /* create rad command */
468 sprintf(rendopt, " @%s/render.opt", vval(DIRECTORY));
469 sprintf(combuf,
470 "rad -v 0 -s -e -w %s OPTFILE=%s | egrep '^[ \t]*(NOMATCH",
471 rfargs, rendopt+2);
472 cp = combuf;
473 while (*cp) {
474 if (*cp == '|') pippt = cp;
475 cp++;
476 } /* match unset variables */
477 for (i = 0; mvar[i] >= 0; i++)
478 if (!vdef(mvar[i])) {
479 *cp++ = '|';
480 strcpy(cp, vnam(mvar[i]));
481 while (*cp) cp++;
482 pippt = NULL;
483 }
484 if (pippt != NULL)
485 strcpy(pippt, "> " NULL_DEVICE); /* nothing to match */
486 else {
487 sprintf(cp, ")[ \t]*=' > %s/radset.var", vval(DIRECTORY));
488 cp += 11; /* point to file name */
489 }
490 system(combuf); /* ignore exit code */
491 if (pippt == NULL) { /* load variables and remove file */
492 loadvars(cp);
493 unlink(cp);
494 }
495 }
496
497
498 animate() /* run animation */
499 {
500 int xres, yres;
501 float pa, mult;
502 int frames_batch;
503 register int i;
504 double d1, d2;
505 /* compute rpict resolution */
506 i = sscanf(vval(RESOLUTION), "%d %d %f", &xres, &yres, &pa);
507 mult = vflt(OVERSAMP);
508 if (i == 3) {
509 sprintf(rresopt, "-x %d -y %d -pa %.3f", (int)(mult*xres),
510 (int)(mult*yres), pa);
511 sprintf(fresopt, "-x %d -y %d -pa %.3f", xres, yres, pa);
512 } else if (i) {
513 if (i == 1) yres = xres;
514 sprintf(rresopt, "-x %d -y %d", (int)(mult*xres),
515 (int)(mult*yres));
516 sprintf(fresopt, "-x %d -y %d -pa 1", xres, yres);
517 } else
518 badvalue(RESOLUTION);
519 /* consistency checks */
520 if (vdef(ANIMATE)) {
521 if (vint(INTERP)) {
522 if (!nowarn)
523 fprintf(stderr,
524 "%s: resetting %s=0 for animation\n",
525 progname, vnam(INTERP));
526 vval(INTERP) = "0";
527 }
528 if (strcmp(vval(MBLUR),"0")) { /* can't handle this */
529 if (!nowarn)
530 fprintf(stderr,
531 "%s: resetting %s=0 for animation\n",
532 progname, vnam(MBLUR));
533 vval(MBLUR) = "0";
534 }
535 }
536 /* figure # frames per batch */
537 d1 = mult*xres*mult*yres*4; /* space for orig. picture */
538 if ((i=vint(INTERP)) || getblur(NULL) > 1)
539 d1 += mult*xres*mult*yres*sizeof(float); /* Z-buffer */
540 d2 = xres*yres*4; /* space for final picture */
541 frames_batch = (i+1)*(vflt(DISKSPACE)*1048576.-d1)/(d1+i*d2);
542 if (frames_batch < i+2) {
543 fprintf(stderr, "%s: insufficient disk space allocated\n",
544 progname);
545 quit(1);
546 }
547 /* initialize archive argument list */
548 i = vdef(ARCHIVE) ? strlen(vval(ARCHIVE))+132 : 132;
549 arcnext = arcfirst = arcargs + i;
550 /* initialize status file */
551 if (astat.rnext == 0)
552 astat.rnext = astat.fnext = astat.tnext = vint(START);
553 putastat();
554 /* render in batches */
555 while (astat.tnext <= vint(END)) {
556 renderframes(frames_batch);
557 filterframes();
558 transferframes();
559 }
560 /* mark status as finished */
561 astat.pid = 0;
562 putastat();
563 /* close open files */
564 getview(0);
565 getexp(0);
566 }
567
568
569 renderframes(nframes) /* render next nframes frames */
570 int nframes;
571 {
572 static char vendbuf[16];
573 VIEW *vp;
574 FILE *fp = NULL;
575 char vfname[128];
576 int lastframe;
577 register int i;
578
579 if (astat.tnext < astat.rnext) /* other work to do first */
580 return;
581 /* create batch view file */
582 if (!vdef(ANIMATE)) {
583 sprintf(vfname, "%s/anim.vf", vval(DIRECTORY));
584 if ((fp = fopen(vfname, "w")) == NULL) {
585 perror(vfname);
586 quit(1);
587 }
588 }
589 /* bound batch properly */
590 lastframe = astat.rnext + nframes - 1;
591 if ((lastframe-1) % (vint(INTERP)+1)) /* need even interval */
592 lastframe += vint(INTERP)+1 - ((lastframe-1)%(vint(INTERP)+1));
593 if (lastframe > vint(END)) /* check for end */
594 lastframe = vint(END);
595 /* render each view */
596 for (i = astat.rnext; i <= lastframe; i++) {
597 if ((vp = getview(i)) == NULL) {
598 if (!nowarn)
599 fprintf(stderr,
600 "%s: ran out of views before last frame\n",
601 progname);
602 sprintf(vval(END)=vendbuf, "%d", i-1);
603 lastframe = i - 1;
604 break;
605 }
606 if (vdef(ANIMATE)) /* animate frame */
607 animrend(i, vp);
608 else { /* else record it */
609 fputs(VIEWSTR, fp);
610 fprintview(vp, fp);
611 putc('\n', fp);
612 }
613 }
614 if (vdef(ANIMATE)) /* wait for renderings to finish */
615 bwait(0);
616 else { /* else if walk-through */
617 fclose(fp); /* close view file */
618 walkwait(astat.rnext, lastframe, vfname); /* walk it */
619 unlink(vfname); /* remove view file */
620 }
621 astat.rnext = i; /* update status */
622 putastat();
623 }
624
625
626 filterframes() /* catch up with filtering */
627 {
628 VIEW *vp;
629 register int i;
630
631 if (astat.tnext < astat.fnext) /* other work to do first */
632 return;
633 /* filter each view */
634 for (i = astat.fnext; i < astat.rnext; i++)
635 dofilt(i, 0);
636
637 bwait(0); /* wait for filter processes */
638 archive(); /* archive originals */
639 astat.fnext = i; /* update status */
640 putastat();
641 }
642
643
644 transferframes() /* catch up with picture transfers */
645 {
646 char combuf[10240], *fbase;
647 register char *cp;
648 register int i;
649
650 if (astat.tnext >= astat.fnext) /* nothing to do, yet */
651 return;
652 if (!vdef(TRANSFER)) { /* no transfer function -- leave 'em */
653 astat.tnext = astat.fnext;
654 putastat(); /* update status */
655 return;
656 }
657 strcpy(combuf, "cd "); /* start transfer command */
658 fbase = dirfile(cp = combuf+3, vval(BASENAME));
659 if (*cp) {
660 while (*++cp) ;
661 *cp++ = ';'; *cp++ = ' ';
662 } else
663 cp = combuf;
664 strcpy(cp, vval(TRANSFER));
665 while (*cp) cp++;
666 /* make argument list */
667 for (i = astat.tnext; i < astat.fnext; i++) {
668 *cp++ = ' ';
669 sprintf(cp, fbase, i);
670 while (*cp) cp++;
671 strcpy(cp, ".pic");
672 cp += 4;
673 }
674 if (runcom(combuf)) { /* transfer frames */
675 fprintf(stderr, "%s: error running transfer command\n",
676 progname);
677 quit(1);
678 }
679 astat.tnext = i; /* update status */
680 putastat();
681 }
682
683
684 animrend(frame, vp) /* start animation frame */
685 int frame;
686 VIEW *vp;
687 {
688 extern int recover();
689 char combuf[2048];
690 char fname[128];
691
692 sprintf(fname, vval(BASENAME), frame);
693 strcat(fname, ".unf");
694 if (access(fname, F_OK) == 0)
695 return;
696 sprintf(combuf, "%s %d | rpict%s%s -w0 %s > %s", vval(ANIMATE), frame,
697 rendopt, viewopt(vp), rresopt, fname);
698 bruncom(combuf, frame, recover); /* run in background */
699 }
700
701
702 walkwait(first, last, vfn) /* walk-through frames */
703 int first, last;
704 char *vfn;
705 {
706 double blurf;
707 int nblur = getblur(&blurf);
708 char combuf[2048];
709 register char *inspoint;
710 register int i;
711
712 if (!noaction && vint(INTERP)) /* create dummy frames */
713 for (i = first; i <= last; i++)
714 if (i < vint(END) && (i-1) % (vint(INTERP)+1)) {
715 sprintf(combuf, vval(BASENAME), i);
716 strcat(combuf, ".unf");
717 close(open(combuf, O_RDONLY|O_CREAT, 0666));
718 }
719 /* create command */
720 sprintf(combuf, "rpict%s%s -w0", rendopt,
721 viewopt(getview(first>1 ? first-1 : 1)));
722 inspoint = combuf;
723 while (*inspoint) inspoint++;
724 if (nblur) {
725 sprintf(inspoint, " -pm %.3f", blurf/nblur);
726 while (*inspoint) inspoint++;
727 }
728 if (nblur > 1 || vint(INTERP)) {
729 sprintf(inspoint, " -z %s.zbf", vval(BASENAME));
730 while (*inspoint) inspoint++;
731 }
732 sprintf(inspoint, " -o %s.unf %s -S %d",
733 vval(BASENAME), rresopt, first);
734 while (*inspoint) inspoint++;
735 sprintf(inspoint, " %s < %s", vval(OCTREE), vfn);
736 /* run in parallel */
737 i = (last-first+1)/(vint(INTERP)+1);
738 if (i < 1) i = 1;
739 if (pruncom(combuf, inspoint, i)) {
740 fprintf(stderr, "%s: error rendering frames %d through %d\n",
741 progname, first, last);
742 quit(1);
743 }
744 if (!noaction && vint(INTERP)) /* remove dummy frames */
745 for (i = first; i <= last; i++)
746 if (i < vint(END) && (i-1) % (vint(INTERP)+1)) {
747 sprintf(combuf, vval(BASENAME), i);
748 strcat(combuf, ".unf");
749 unlink(combuf);
750 }
751 }
752
753
754 int
755 recover(frame) /* recover the specified frame */
756 int frame;
757 {
758 static int *rfrm; /* list of recovered frames */
759 static int nrfrms = 0;
760 double blurf;
761 int nblur = getblur(&blurf);
762 char combuf[2048];
763 char fname[128];
764 register char *cp;
765 register int i;
766 /* check to see if recovered already */
767 for (i = nrfrms; i--; )
768 if (rfrm[i] == frame)
769 return(0);
770 /* build command */
771 sprintf(fname, vval(BASENAME), frame);
772 if (vdef(ANIMATE))
773 sprintf(combuf, "%s %d | rpict%s -w0",
774 vval(ANIMATE), frame, rendopt);
775 else
776 sprintf(combuf, "rpict%s -w0", rendopt);
777 cp = combuf;
778 while (*cp) cp++;
779 if (nblur) {
780 sprintf(cp, " -pm %.3f", blurf/nblur);
781 while (*cp) cp++;
782 }
783 if (nblur > 1 || vint(INTERP)) {
784 sprintf(cp, " -z %s.zbf", fname);
785 while (*cp) cp++;
786 }
787 sprintf(cp, " -ro %s.unf", fname);
788 while (*cp) cp++;
789 if (!vdef(ANIMATE)) {
790 *cp++ = ' ';
791 strcpy(cp, vval(OCTREE));
792 }
793 if (runcom(combuf)) /* run command */
794 return(1);
795 /* add frame to recovered list */
796 if (nrfrms)
797 rfrm = (int *)realloc((void *)rfrm, (nrfrms+1)*sizeof(int));
798 else
799 rfrm = (int *)malloc(sizeof(int));
800 if (rfrm == NULL) {
801 perror("malloc");
802 quit(1);
803 }
804 rfrm[nrfrms++] = frame;
805 return(0);
806 }
807
808
809 int
810 frecover(frame) /* recover filtered frame */
811 int frame;
812 {
813 if (dofilt(frame, 2) && dofilt(frame, 1))
814 return(1);
815 return(0);
816 }
817
818
819 archive() /* archive and remove renderings */
820 {
821 #define RMCOML (sizeof(rmcom)-1)
822 static char rmcom[] = "rm -f";
823 char basedir[128];
824 int dlen, alen;
825 register int j;
826
827 if (arcnext == arcfirst)
828 return; /* nothing to do */
829 dirfile(basedir, vval(BASENAME));
830 dlen = strlen(basedir);
831 if (vdef(ARCHIVE)) { /* run archive command */
832 alen = strlen(vval(ARCHIVE));
833 if (dlen) {
834 j = alen + dlen + 5;
835 strncpy(arcfirst-j, "cd ", 3);
836 strncpy(arcfirst-j+3, basedir, dlen);
837 (arcfirst-j)[dlen+3] = ';'; (arcfirst-j)[dlen+4] = ' ';
838 } else
839 j = alen;
840 strncpy(arcfirst-alen, vval(ARCHIVE), alen);
841 if (runcom(arcfirst-j)) {
842 fprintf(stderr, "%s: error running archive command\n",
843 progname);
844 quit(1);
845 }
846 }
847 if (dlen) {
848 j = RMCOML + dlen + 5;
849 strncpy(arcfirst-j, "cd ", 3);
850 strncpy(arcfirst-j+3, basedir, dlen);
851 (arcfirst-j)[dlen+3] = ';'; (arcfirst-j)[dlen+4] = ' ';
852 } else
853 j = RMCOML;
854 /* run remove command */
855 strncpy(arcfirst-RMCOML, rmcom, RMCOML);
856 runcom(arcfirst-j);
857 arcnext = arcfirst; /* reset argument list */
858 #undef RMCOML
859 }
860
861
862 int
863 dofilt(frame, rvr) /* filter frame */
864 int frame;
865 int rvr;
866 {
867 extern int frecover();
868 static int iter = 0;
869 double blurf;
870 int nblur = getblur(&blurf);
871 VIEW *vp = getview(frame);
872 char *ep = getexp(frame);
873 char fnbefore[128], fnafter[128], *fbase;
874 char combuf[1024], fname0[128], fname1[128];
875 int usepinterp, usepfilt, nora_rgbe;
876 int frseq[2];
877 /* check what is needed */
878 if (vp == NULL) {
879 fprintf(stderr,
880 "%s: unexpected error reading view for frame %d\n",
881 progname, frame);
882 quit(1);
883 }
884 if (ep == NULL) {
885 fprintf(stderr,
886 "%s: unexpected error reading exposure for frame %d\n",
887 progname, frame);
888 quit(1);
889 }
890 usepinterp = (nblur > 1);
891 usepfilt = pfiltalways | ep==NULL;
892 if (ep != NULL && !strcmp(ep, "1"))
893 ep = "+0";
894 nora_rgbe = strcmp(vval(OVERSAMP),"1") || ep==NULL ||
895 *ep != '+' || *ep != '-' || !isint(ep);
896 /* compute rendered views */
897 frseq[0] = frame - ((frame-1) % (vint(INTERP)+1));
898 frseq[1] = frseq[0] + vint(INTERP) + 1;
899 fbase = dirfile(NULL, vval(BASENAME));
900 if (frseq[1] > vint(END))
901 frseq[1] = vint(END);
902 if (frseq[1] == frame) { /* pfilt only */
903 frseq[0] = frseq[1];
904 usepinterp = 0; /* update what's needed */
905 usepfilt |= nora_rgbe;
906 } else if (frseq[0] == frame) { /* no interpolation needed */
907 if (!rvr && frame > 1+vint(INTERP)) { /* archive previous */
908 *arcnext++ = ' ';
909 sprintf(arcnext, fbase, frame-vint(INTERP)-1);
910 while (*arcnext) arcnext++;
911 strcpy(arcnext, ".unf");
912 arcnext += 4;
913 if (usepinterp || vint(INTERP)) { /* and Z-buf */
914 *arcnext++ = ' ';
915 sprintf(arcnext, fbase, frame-vint(INTERP)-1);
916 while (*arcnext) arcnext++;
917 strcpy(arcnext, ".zbf");
918 arcnext += 4;
919 }
920 }
921 if (!usepinterp) /* update what's needed */
922 usepfilt |= nora_rgbe;
923 } else /* interpolation needed */
924 usepinterp++;
925 if (frseq[1] >= astat.rnext) /* next batch unavailable */
926 frseq[1] = frseq[0];
927 sprintf(fnbefore, vval(BASENAME), frseq[0]);
928 sprintf(fnafter, vval(BASENAME), frseq[1]);
929 if (rvr == 1 && recover(frseq[0])) /* recover before frame? */
930 return(1);
931 /* generate command */
932 if (usepinterp) { /* using pinterp */
933 if (rvr == 2 && recover(frseq[1])) /* recover after? */
934 return(1);
935 if (nblur > 1) { /* with pmblur */
936 sprintf(fname0, "%s/vw0%c", vval(DIRECTORY),
937 'a'+(iter%26));
938 sprintf(fname1, "%s/vw1%c", vval(DIRECTORY),
939 'a'+(iter%26));
940 if (!noaction) {
941 FILE *fp; /* motion blurring */
942 if ((fp = fopen(fname0, "w")) == NULL) {
943 perror(fname0); quit(1);
944 }
945 fputs(VIEWSTR, fp);
946 fprintview(vp, fp);
947 putc('\n', fp); fclose(fp);
948 if ((vp = getview(frame+1)) == NULL) {
949 fprintf(stderr,
950 "%s: unexpected error reading view for frame %d\n",
951 progname, frame+1);
952 quit(1);
953 }
954 if ((fp = fopen(fname1, "w")) == NULL) {
955 perror(fname1); quit(1);
956 }
957 fputs(VIEWSTR, fp);
958 fprintview(vp, fp);
959 putc('\n', fp); fclose(fp);
960 }
961 sprintf(combuf,
962 "(pmblur %.3f %d %s %s; rm -f %s %s) | pinterp -B -a",
963 blurf, nblur,
964 fname0, fname1, fname0, fname1);
965 iter++;
966 } else /* no blurring */
967 strcpy(combuf, "pinterp");
968 strcat(combuf, viewopt(vp));
969 if (vbool(RTRACE))
970 sprintf(combuf+strlen(combuf), " -ff -fr '%s -w0 %s'",
971 rendopt+1, vval(OCTREE));
972 if (vdef(PINTERP))
973 sprintf(combuf+strlen(combuf), " %s", vval(PINTERP));
974 if (usepfilt)
975 sprintf(combuf+strlen(combuf), " %s", rresopt);
976 else
977 sprintf(combuf+strlen(combuf), " -a %s -e %s",
978 fresopt, ep);
979 sprintf(combuf+strlen(combuf), " %s.unf %s.zbf",
980 fnbefore, fnbefore);
981 if (frseq[1] != frseq[0])
982 sprintf(combuf+strlen(combuf), " %s.unf %s.zbf",
983 fnafter, fnafter);
984 if (usepfilt) { /* also pfilt */
985 if (vdef(PFILT))
986 sprintf(combuf+strlen(combuf), " | pfilt %s",
987 vval(PFILT));
988 else
989 strcat(combuf, " | pfilt");
990 if (ep != NULL)
991 sprintf(combuf+strlen(combuf), " -1 -e %s %s",
992 ep, fresopt);
993 else
994 sprintf(combuf+strlen(combuf), " %s", fresopt);
995 }
996 } else if (usepfilt) { /* pfilt only */
997 if (rvr == 2)
998 return(1);
999 if (vdef(PFILT))
1000 sprintf(combuf, "pfilt %s", vval(PFILT));
1001 else
1002 strcpy(combuf, "pfilt");
1003 if (ep != NULL)
1004 sprintf(combuf+strlen(combuf), " -1 -e %s %s %s.unf",
1005 ep, fresopt, fnbefore);
1006 else
1007 sprintf(combuf+strlen(combuf), " %s %s.unf",
1008 fresopt, fnbefore);
1009 } else { /* else just check it */
1010 if (rvr == 2)
1011 return(1);
1012 sprintf(combuf, "ra_rgbe -e %s -r %s.unf", ep, fnbefore);
1013 }
1014 /* output file name */
1015 sprintf(fname0, vval(BASENAME), frame);
1016 sprintf(combuf+strlen(combuf), " > %s.pic", fname0);
1017 if (rvr) /* in recovery */
1018 return(runcom(combuf));
1019 bruncom(combuf, frame, frecover); /* else run in background */
1020 return(0);
1021 }
1022
1023
1024 VIEW *
1025 getview(n) /* get view number n */
1026 int n;
1027 {
1028 static FILE *viewfp = NULL; /* view file pointer */
1029 static int viewnum = 0; /* current view number */
1030 static VIEW curview = STDVIEW; /* current view */
1031 char linebuf[256];
1032
1033 if (n == 0) { /* signal to close file and clean up */
1034 if (viewfp != NULL) {
1035 fclose(viewfp);
1036 viewfp = NULL;
1037 viewnum = 0;
1038 curview = stdview;
1039 }
1040 return(NULL);
1041 }
1042 if (viewfp == NULL) { /* open file */
1043 if ((viewfp = fopen(vval(VIEWFILE), "r")) == NULL) {
1044 perror(vval(VIEWFILE));
1045 quit(1);
1046 }
1047 } else if (n > 0 && n < viewnum) { /* rewind file */
1048 if (viewnum == 1 && feof(viewfp))
1049 return(&curview); /* just one view */
1050 if (fseek(viewfp, 0L, 0) == EOF) {
1051 perror(vval(VIEWFILE));
1052 quit(1);
1053 }
1054 curview = stdview;
1055 viewnum = 0;
1056 }
1057 if (n < 0) { /* get next view */
1058 register int c = getc(viewfp);
1059 if (c == EOF)
1060 return((VIEW *)NULL); /* that's it */
1061 ungetc(c, viewfp);
1062 n = viewnum + 1;
1063 }
1064 while (n > viewnum) { /* scan to desired view */
1065 if (fgets(linebuf, sizeof(linebuf), viewfp) == NULL)
1066 return(viewnum==1 ? &curview : (VIEW *)NULL);
1067 if (isview(linebuf) && sscanview(&curview, linebuf) > 0)
1068 viewnum++;
1069 }
1070 return(&curview); /* return it */
1071 }
1072
1073
1074 int
1075 countviews() /* count views in view file */
1076 {
1077 int n;
1078
1079 if (getview(n=1) == NULL)
1080 return(0);
1081 while (getview(-1) != NULL)
1082 n++;
1083 return(n);
1084 }
1085
1086
1087 char *
1088 getexp(n) /* get exposure for nth frame */
1089 int n;
1090 {
1091 extern char *fskip();
1092 static char expval[32];
1093 static FILE *expfp = NULL;
1094 static long *exppos;
1095 static int curfrm;
1096 register char *cp;
1097
1098 if (n == 0) { /* signal to close file */
1099 if (expfp != NULL) {
1100 fclose(expfp);
1101 free((void *)exppos);
1102 expfp = NULL;
1103 }
1104 return(NULL);
1105 } else if (n > vint(END)) /* request past end (error?) */
1106 return(NULL);
1107 if (!vdef(EXPOSURE)) /* no setting (auto) */
1108 return(NULL);
1109 if (isflt(vval(EXPOSURE))) /* always the same */
1110 return(vval(EXPOSURE));
1111 if (expfp == NULL) { /* open exposure file */
1112 if ((expfp = fopen(vval(EXPOSURE), "r")) == NULL) {
1113 fprintf(stderr,
1114 "%s: cannot open exposure file \"%s\"\n",
1115 progname, vval(EXPOSURE));
1116 quit(1);
1117 }
1118 curfrm = vint(END) + 1; /* init lookup tab. */
1119 exppos = (long *)malloc(curfrm*sizeof(long *));
1120 if (exppos == NULL) {
1121 perror(progname);
1122 quit(1);
1123 }
1124 while (curfrm--)
1125 exppos[curfrm] = -1L;
1126 curfrm = 0;
1127 }
1128 /* find position in file */
1129 if (n-1 != curfrm && n != curfrm && exppos[n-1] >= 0 &&
1130 fseek(expfp, exppos[curfrm=n-1], 0) == EOF) {
1131 fprintf(stderr, "%s: seek error on exposure file\n", progname);
1132 quit(1);
1133 }
1134 while (n > curfrm) { /* read exposure */
1135 if (exppos[curfrm] < 0)
1136 exppos[curfrm] = ftell(expfp);
1137 if (fgets(expval, sizeof(expval), expfp) == NULL) {
1138 fprintf(stderr, "%s: too few exposures\n",
1139 vval(EXPOSURE));
1140 quit(1);
1141 }
1142 curfrm++;
1143 cp = fskip(expval); /* check format */
1144 if (cp != NULL)
1145 while (isspace(*cp))
1146 *cp++ = '\0';
1147 if (cp == NULL || *cp) {
1148 fprintf(stderr,
1149 "%s: exposure format error on line %d\n",
1150 vval(EXPOSURE), curfrm);
1151 quit(1);
1152 }
1153 }
1154 return(expval); /* return value */
1155 }
1156
1157
1158 struct pslot *
1159 findpslot(pid) /* find or allocate a process slot */
1160 int pid;
1161 {
1162 register struct pslot *psempty = NULL;
1163 register int i;
1164
1165 for (i = 0; i < npslots; i++) { /* look for match */
1166 if (pslot[i].pid == pid)
1167 return(pslot+i);
1168 if (psempty == NULL && pslot[i].pid == 0)
1169 psempty = pslot+i;
1170 }
1171 return(psempty); /* return emtpy slot (error if NULL) */
1172 }
1173
1174
1175 int
1176 donecom(ps, pn, status) /* clean up after finished process */
1177 PSERVER *ps;
1178 int pn;
1179 int status;
1180 {
1181 register PROC *pp;
1182 register struct pslot *psl;
1183
1184 pp = ps->proc + pn;
1185 if (pp->elen) { /* pass errors */
1186 if (ps->hostname[0])
1187 fprintf(stderr, "%s: ", ps->hostname);
1188 fprintf(stderr, "Error output from: %s\n", pp->com);
1189 fputs(pp->errs, stderr);
1190 fflush(stderr);
1191 if (ps->hostname[0])
1192 status = 1; /* because rsh doesn't return status */
1193 }
1194 lastpserver = NULL;
1195 psl = findpslot(pp->pid); /* check for bruncom() slot */
1196 if (psl->pid) {
1197 if (status) {
1198 if (psl->rcvf != NULL) /* attempt recovery */
1199 status = (*psl->rcvf)(psl->fout);
1200 if (status) {
1201 fprintf(stderr,
1202 "%s: error rendering frame %d\n",
1203 progname, psl->fout);
1204 quit(1);
1205 }
1206 lastpserver = ps;
1207 }
1208 psl->pid = 0; /* free process slot */
1209 } else if (status)
1210 lastpserver = ps;
1211 freestr(pp->com); /* free command string */
1212 return(status);
1213 }
1214
1215
1216 int
1217 serverdown() /* check status of last process server */
1218 {
1219 if (lastpserver == NULL || !lastpserver->hostname[0])
1220 return(0);
1221 if (pserverOK(lastpserver)) /* server still up? */
1222 return(0);
1223 delpserver(lastpserver); /* else delete it */
1224 if (pslist == NULL) {
1225 fprintf(stderr, "%s: all process servers are down\n",
1226 progname);
1227 quit(1);
1228 }
1229 return(1);
1230 }
1231
1232
1233 int
1234 bruncom(com, fout, rf) /* run a command in the background */
1235 char *com;
1236 int fout;
1237 int (*rf)();
1238 {
1239 int pid;
1240 register struct pslot *psl;
1241
1242 if (noaction) {
1243 if (!silent)
1244 printf("\t%s\n", com); /* echo command */
1245 return(0);
1246 }
1247 com = savestr(com); /* else start it when we can */
1248 while ((pid = startjob(NULL, com, donecom)) == -1)
1249 bwait(1);
1250 if (!silent) { /* echo command */
1251 PSERVER *ps;
1252 int psn = pid;
1253 ps = findjob(&psn);
1254 printf("\t%s\n", com);
1255 printf("\tProcess started on %s\n", phostname(ps));
1256 fflush(stdout);
1257 }
1258 psl = findpslot(pid); /* record info. in appropriate slot */
1259 psl->pid = pid;
1260 psl->fout = fout;
1261 psl->rcvf = rf;
1262 return(pid);
1263 }
1264
1265
1266 bwait(ncoms) /* wait for batch job(s) to finish */
1267 int ncoms;
1268 {
1269 int status;
1270
1271 if (noaction)
1272 return;
1273 while ((status = wait4job(NULL, -1)) != -1) {
1274 serverdown(); /* update server status */
1275 if (--ncoms == 0)
1276 break; /* done enough */
1277 }
1278 }
1279
1280
1281 int
1282 pruncom(com, ppins, maxcopies) /* run a command in parallel over network */
1283 char *com, *ppins;
1284 int maxcopies;
1285 {
1286 int retstatus = 0;
1287 int hostcopies;
1288 char buf[10240], *com1, *s;
1289 int status;
1290 int pfd;
1291 register int n;
1292 register PSERVER *ps;
1293
1294 if (!silent)
1295 printf("\t%s\n", com); /* echo command */
1296 if (noaction)
1297 return(0);
1298 fflush(stdout);
1299 /* start jobs on each server */
1300 for (ps = pslist; ps != NULL; ps = ps->next) {
1301 hostcopies = 0;
1302 if (maxcopies > 1 && ps->nprocs > 1 && ppins != NULL) {
1303 strcpy(com1=buf, com); /* build -PP command */
1304 sprintf(com1+(ppins-com), " -PP %s/%s.persist",
1305 vval(DIRECTORY), phostname(ps));
1306 unlink(com1+(ppins-com)+5);
1307 strcat(com1, ppins);
1308 } else
1309 com1 = com;
1310 while (maxcopies > 0) {
1311 s = savestr(com1);
1312 if (startjob(ps, s, donecom) != -1) {
1313 sleep(20);
1314 hostcopies++;
1315 maxcopies--;
1316 } else {
1317 freestr(s);
1318 break;
1319 }
1320 }
1321 if (!silent && hostcopies) {
1322 if (hostcopies > 1)
1323 printf("\t%d duplicate processes", hostcopies);
1324 else
1325 printf("\tProcess");
1326 printf(" started on %s\n", phostname(ps));
1327 fflush(stdout);
1328 }
1329 }
1330 /* wait for jobs to finish */
1331 while ((status = wait4job(NULL, -1)) != -1)
1332 retstatus += status && !serverdown();
1333 /* terminate parallel rpict's */
1334 for (ps = pslist; ps != NULL; ps = ps->next) {
1335 sprintf(buf, "%s/%s.persist", vval(DIRECTORY), phostname(ps));
1336 if ((pfd = open(buf, O_RDONLY)) >= 0) {
1337 n = read(pfd, buf, sizeof(buf)-1); /* get PID */
1338 buf[n] = '\0';
1339 close(pfd);
1340 for (n = 0; buf[n] && !isspace(buf[n]); n++)
1341 ;
1342 /* terminate */
1343 sprintf(buf, "kill -ALRM %d", atoi(buf+n));
1344 wait4job(ps, startjob(ps, buf, NULL));
1345 }
1346 }
1347 return(retstatus);
1348 }
1349
1350
1351 runcom(cs) /* run a command locally and wait for it */
1352 char *cs;
1353 {
1354 if (!silent) /* echo it */
1355 printf("\t%s\n", cs);
1356 if (noaction)
1357 return(0);
1358 fflush(stdout); /* flush output and pass to shell */
1359 return(system(cs));
1360 }
1361
1362
1363 rmfile(fn) /* remove a file */
1364 char *fn;
1365 {
1366 if (!silent)
1367 #ifdef _WIN32
1368 printf("\tdel %s\n", fn);
1369 #else
1370 printf("\trm -f %s\n", fn);
1371 #endif
1372 if (noaction)
1373 return(0);
1374 return(unlink(fn));
1375 }
1376
1377
1378 badvalue(vc) /* report bad variable value and exit */
1379 int vc;
1380 {
1381 fprintf(stderr, "%s: bad value for variable '%s'\n",
1382 progname, vnam(vc));
1383 quit(1);
1384 }
1385
1386
1387 char *
1388 dirfile(df, path) /* separate path into directory and file */
1389 char *df;
1390 register char *path;
1391 {
1392 register int i;
1393 int psep;
1394
1395 for (i = 0, psep = -1; path[i]; i++)
1396 if (path[i] == '/')
1397 psep = i;
1398 if (df != NULL) {
1399 if (psep == 0) {
1400 df[0] = '/';
1401 df[1] = '\0';
1402 } else if (psep > 0) {
1403 strncpy(df, path, psep);
1404 df[psep] = '\0';
1405 } else
1406 df[0] = '\0';
1407 }
1408 return(path+psep+1);
1409 }
1410
1411
1412 int
1413 getblur(double *bf) /* get # blur samples (and fraction) */
1414 {
1415 double blurf;
1416 int nblur;
1417 char *s;
1418
1419 if (!vdef(MBLUR)) {
1420 if (bf != NULL)
1421 *bf = 0.0;
1422 return(0);
1423 }
1424 blurf = atof(vval(MBLUR));
1425 if (blurf < 0.0)
1426 blurf = 0.0;
1427 if (bf != NULL)
1428 *bf = blurf;
1429 if (blurf <= FTINY)
1430 return(0);
1431 s = sskip(vval(MBLUR));
1432 if (!*s)
1433 return(DEF_NBLUR);
1434 nblur = atoi(s);
1435 if (nblur <= 0)
1436 return(1);
1437 return(nblur);
1438 }