| 1 | greg | 1.1 | /* Copyright (c) 1987 Regents of the University of California */ | 
| 2 |  |  |  | 
| 3 |  |  | #ifndef lint | 
| 4 |  |  | static char SCCSid[] = "$SunId$ LBL"; | 
| 5 |  |  | #endif | 
| 6 |  |  |  | 
| 7 |  |  | /* | 
| 8 |  |  | *  rview.c - routines and variables for interactive view generation. | 
| 9 |  |  | * | 
| 10 |  |  | *     3/24/87 | 
| 11 |  |  | */ | 
| 12 |  |  |  | 
| 13 | greg | 1.15 | #include  "ray.h" | 
| 14 | greg | 1.1 |  | 
| 15 |  |  | #include  "rpaint.h" | 
| 16 |  |  |  | 
| 17 |  |  | #include  <signal.h> | 
| 18 |  |  |  | 
| 19 |  |  | #include  <ctype.h> | 
| 20 |  |  |  | 
| 21 | greg | 1.9 | VIEW  ourview = STDVIEW;                /* viewing parameters */ | 
| 22 |  |  | int  hresolu, vresolu;                  /* image resolution */ | 
| 23 | greg | 1.15 |  | 
| 24 |  |  | int  dimlist[MAXDIM];                   /* sampling dimensions */ | 
| 25 |  |  | int  ndims = 0;                         /* number of sampling dimensions */ | 
| 26 |  |  | int  samplendx = 0;                     /* index for this sample */ | 
| 27 | greg | 1.1 |  | 
| 28 |  |  | int  psample = 8;                       /* pixel sample size */ | 
| 29 |  |  | double  maxdiff = .15;                  /* max. sample difference */ | 
| 30 |  |  |  | 
| 31 |  |  | double  exposure = 1.0;                 /* exposure for scene */ | 
| 32 |  |  |  | 
| 33 |  |  | double  dstrsrc = 0.0;                  /* square source distribution */ | 
| 34 | greg | 1.3 | double  shadthresh = .1;                /* shadow threshold */ | 
| 35 | greg | 1.4 | double  shadcert = .25;                 /* shadow certainty */ | 
| 36 | greg | 1.16 | int  directrelay = 0;                   /* number of source relays */ | 
| 37 | greg | 1.18 | int  vspretest = 128;                   /* virtual source pretest density */ | 
| 38 | greg | 1.19 | int  directinvis = 0;                   /* sources invisible? */ | 
| 39 | greg | 1.1 |  | 
| 40 |  |  | int  maxdepth = 4;                      /* maximum recursion depth */ | 
| 41 |  |  | double  minweight = 1e-2;               /* minimum ray weight */ | 
| 42 |  |  |  | 
| 43 |  |  | COLOR  ambval = BLKCOLOR;               /* ambient value */ | 
| 44 |  |  | double  ambacc = 0.2;                   /* ambient accuracy */ | 
| 45 | greg | 1.5 | int  ambres = 8;                        /* ambient resolution */ | 
| 46 | greg | 1.1 | int  ambdiv = 32;                       /* ambient divisions */ | 
| 47 |  |  | int  ambssamp = 0;                      /* ambient super-samples */ | 
| 48 |  |  | int  ambounce = 0;                      /* ambient bounces */ | 
| 49 |  |  | char  *amblist[128];                    /* ambient include/exclude list */ | 
| 50 |  |  | int  ambincl = -1;                      /* include == 1, exclude == 0 */ | 
| 51 |  |  |  | 
| 52 |  |  | int  greyscale = 0;                     /* map colors to brightness? */ | 
| 53 | greg | 1.11 | char  *devname = dev_default;           /* output device name */ | 
| 54 | greg | 1.1 |  | 
| 55 |  |  | struct driver  *dev = NULL;             /* driver functions */ | 
| 56 |  |  |  | 
| 57 |  |  | VIEW  oldview;                          /* previous view parameters */ | 
| 58 |  |  |  | 
| 59 |  |  | PNODE  ptrunk;                          /* the base of our image */ | 
| 60 |  |  | RECT  pframe;                           /* current frame boundaries */ | 
| 61 |  |  | int  pdepth;                            /* image depth in current frame */ | 
| 62 |  |  |  | 
| 63 | greg | 1.12 | static char  *reserve_mem = NULL;       /* pre-allocated reserve memory */ | 
| 64 |  |  |  | 
| 65 | greg | 1.20 | #define RESERVE_AMT     32768           /* amount of memory to reserve */ | 
| 66 | greg | 1.12 |  | 
| 67 | greg | 1.1 | #define  CTRL(c)        ('c'-'@') | 
| 68 |  |  |  | 
| 69 |  |  |  | 
| 70 |  |  | quit(code)                      /* quit program */ | 
| 71 |  |  | int  code; | 
| 72 |  |  | { | 
| 73 |  |  | devclose(); | 
| 74 |  |  | exit(code); | 
| 75 |  |  | } | 
| 76 |  |  |  | 
| 77 |  |  |  | 
| 78 |  |  | devopen(dname)                          /* open device driver */ | 
| 79 |  |  | char  *dname; | 
| 80 |  |  | { | 
| 81 | greg | 1.6 | extern char  *progname, *octname; | 
| 82 | greg | 1.7 | char  *id; | 
| 83 | greg | 1.1 | register int  i; | 
| 84 | greg | 1.6 |  | 
| 85 | greg | 1.7 | id = octname!=NULL ? octname : progname; | 
| 86 | greg | 1.1 | /* check device table */ | 
| 87 |  |  | for (i = 0; devtable[i].name; i++) | 
| 88 |  |  | if (!strcmp(dname, devtable[i].name)) | 
| 89 | greg | 1.7 | if ((dev = (*devtable[i].init)(dname, id)) == NULL) { | 
| 90 | greg | 1.1 | sprintf(errmsg, "cannot initialize %s", dname); | 
| 91 |  |  | error(USER, errmsg); | 
| 92 |  |  | } else | 
| 93 |  |  | return; | 
| 94 |  |  | /* not there, try exec */ | 
| 95 | greg | 1.7 | if ((dev = comm_init(dname, id)) == NULL) { | 
| 96 | greg | 1.1 | sprintf(errmsg, "cannot start device \"%s\"", dname); | 
| 97 |  |  | error(USER, errmsg); | 
| 98 |  |  | } | 
| 99 |  |  | } | 
| 100 |  |  |  | 
| 101 |  |  |  | 
| 102 |  |  | devclose()                              /* close our device */ | 
| 103 |  |  | { | 
| 104 |  |  | if (dev != NULL) | 
| 105 |  |  | (*dev->close)(); | 
| 106 |  |  | dev = NULL; | 
| 107 |  |  | } | 
| 108 |  |  |  | 
| 109 |  |  |  | 
| 110 |  |  | printdevices()                          /* print list of output devices */ | 
| 111 |  |  | { | 
| 112 |  |  | register int  i; | 
| 113 |  |  |  | 
| 114 |  |  | for (i = 0; devtable[i].name; i++) | 
| 115 |  |  | printf("%-16s # %s\n", devtable[i].name, devtable[i].descrip); | 
| 116 |  |  | } | 
| 117 |  |  |  | 
| 118 |  |  |  | 
| 119 |  |  | rview()                         /* do a view */ | 
| 120 |  |  | { | 
| 121 |  |  | char  buf[32]; | 
| 122 |  |  |  | 
| 123 |  |  | devopen(devname);               /* open device */ | 
| 124 | greg | 1.14 | newimage();                     /* start image (calls fillreserves) */ | 
| 125 |  |  |  | 
| 126 | greg | 1.1 | for ( ; ; ) {                   /* quit in command() */ | 
| 127 | greg | 1.14 | while (hresolu <= 1<<pdepth && vresolu <= 1<<pdepth) | 
| 128 | greg | 1.1 | command("done: "); | 
| 129 | greg | 1.14 | while (reserve_mem == NULL) | 
| 130 |  |  | command("out of memory: "); | 
| 131 |  |  | errno = 0; | 
| 132 |  |  | if (hresolu <= psample<<pdepth && vresolu <= psample<<pdepth) { | 
| 133 | greg | 1.1 | sprintf(buf, "%d sampling...\n", 1<<pdepth); | 
| 134 |  |  | (*dev->comout)(buf); | 
| 135 |  |  | rsample(); | 
| 136 |  |  | } else { | 
| 137 |  |  | sprintf(buf, "%d refining...\n", 1<<pdepth); | 
| 138 |  |  | (*dev->comout)(buf); | 
| 139 | greg | 1.9 | refine(&ptrunk, 0, 0, hresolu, vresolu, pdepth+1); | 
| 140 | greg | 1.1 | } | 
| 141 | greg | 1.14 | if (errno == ENOMEM)            /* ran out of memory */ | 
| 142 |  |  | freereserves(); | 
| 143 |  |  | else if (dev->inpready)         /* noticed some input */ | 
| 144 | greg | 1.1 | command(": "); | 
| 145 | greg | 1.14 | else                            /* finished this depth */ | 
| 146 | greg | 1.1 | pdepth++; | 
| 147 |  |  | } | 
| 148 |  |  | } | 
| 149 |  |  |  | 
| 150 |  |  |  | 
| 151 | greg | 1.14 | fillreserves()                  /* fill memory reserves */ | 
| 152 | greg | 1.12 | { | 
| 153 |  |  | if (reserve_mem != NULL) | 
| 154 | greg | 1.14 | return; | 
| 155 | greg | 1.12 | reserve_mem = malloc(RESERVE_AMT); | 
| 156 |  |  | } | 
| 157 |  |  |  | 
| 158 |  |  |  | 
| 159 | greg | 1.14 | freereserves()                  /* free memory reserves */ | 
| 160 | greg | 1.12 | { | 
| 161 | greg | 1.14 | if (reserve_mem == NULL) | 
| 162 |  |  | return; | 
| 163 | greg | 1.12 | free(reserve_mem); | 
| 164 |  |  | reserve_mem = NULL; | 
| 165 |  |  | } | 
| 166 |  |  |  | 
| 167 |  |  |  | 
| 168 | greg | 1.1 | command(prompt)                 /* get/execute command */ | 
| 169 |  |  | char  *prompt; | 
| 170 |  |  | { | 
| 171 |  |  | #define  badcom(s)      strncmp(s, inpbuf, args-inpbuf-1) | 
| 172 |  |  | double  atof(); | 
| 173 |  |  | char  inpbuf[256]; | 
| 174 |  |  | char  *args; | 
| 175 |  |  | again: | 
| 176 | greg | 1.10 | (*dev->comin)(inpbuf, prompt);          /* get command + arguments */ | 
| 177 | greg | 1.1 | for (args = inpbuf; *args && *args != ' '; args++) | 
| 178 |  |  | ; | 
| 179 |  |  | if (*args) *args++ = '\0'; | 
| 180 |  |  | else *++args = '\0'; | 
| 181 |  |  |  | 
| 182 |  |  | switch (inpbuf[0]) { | 
| 183 |  |  | case 'f':                               /* new frame */ | 
| 184 |  |  | if (badcom("frame")) | 
| 185 |  |  | goto commerr; | 
| 186 |  |  | getframe(args); | 
| 187 |  |  | break; | 
| 188 |  |  | case 'v':                               /* view */ | 
| 189 |  |  | if (badcom("view")) | 
| 190 |  |  | goto commerr; | 
| 191 |  |  | getview(args); | 
| 192 |  |  | break; | 
| 193 |  |  | case 'l':                               /* last view */ | 
| 194 |  |  | if (badcom("last")) | 
| 195 |  |  | goto commerr; | 
| 196 |  |  | lastview(args); | 
| 197 |  |  | break; | 
| 198 |  |  | case 'e':                               /* exposure */ | 
| 199 |  |  | if (badcom("exposure")) | 
| 200 |  |  | goto commerr; | 
| 201 |  |  | getexposure(args); | 
| 202 |  |  | break; | 
| 203 |  |  | case 's':                               /* set a parameter */ | 
| 204 |  |  | if (badcom("set")) | 
| 205 |  |  | goto commerr; | 
| 206 |  |  | setparam(args); | 
| 207 |  |  | break; | 
| 208 |  |  | case 'n':                               /* new picture */ | 
| 209 |  |  | if (badcom("new")) | 
| 210 |  |  | goto commerr; | 
| 211 |  |  | newimage(); | 
| 212 |  |  | break; | 
| 213 |  |  | case 't':                               /* trace a ray */ | 
| 214 |  |  | if (badcom("trace")) | 
| 215 |  |  | goto commerr; | 
| 216 |  |  | traceray(args); | 
| 217 |  |  | break; | 
| 218 |  |  | case 'a':                               /* aim camera */ | 
| 219 |  |  | if (badcom("aim")) | 
| 220 |  |  | goto commerr; | 
| 221 |  |  | getaim(args); | 
| 222 |  |  | break; | 
| 223 |  |  | case 'm':                               /* move camera */ | 
| 224 |  |  | if (badcom("move")) | 
| 225 |  |  | goto commerr; | 
| 226 |  |  | getmove(args); | 
| 227 |  |  | break; | 
| 228 | greg | 1.8 | case 'r':                               /* rotate/repaint */ | 
| 229 |  |  | if (badcom("rotate")) { | 
| 230 |  |  | if (badcom("repaint")) | 
| 231 |  |  | goto commerr; | 
| 232 |  |  | getrepaint(args); | 
| 233 |  |  | break; | 
| 234 |  |  | } | 
| 235 | greg | 1.1 | getrotate(args); | 
| 236 |  |  | break; | 
| 237 |  |  | case 'p':                               /* pivot view */ | 
| 238 |  |  | if (badcom("pivot")) | 
| 239 |  |  | goto commerr; | 
| 240 |  |  | getpivot(args); | 
| 241 |  |  | break; | 
| 242 |  |  | case CTRL(R):                           /* redraw */ | 
| 243 |  |  | redraw(); | 
| 244 |  |  | break; | 
| 245 |  |  | case 'w':                               /* write */ | 
| 246 |  |  | if (badcom("write")) | 
| 247 |  |  | goto commerr; | 
| 248 |  |  | writepict(args); | 
| 249 |  |  | break; | 
| 250 |  |  | case 'q':                               /* quit */ | 
| 251 |  |  | if (badcom("quit")) | 
| 252 |  |  | goto commerr; | 
| 253 |  |  | quit(0); | 
| 254 |  |  | case CTRL(C):                           /* interrupt */ | 
| 255 |  |  | goto again; | 
| 256 |  |  | #ifdef  SIGTSTP | 
| 257 |  |  | case CTRL(Z):                           /* stop */ | 
| 258 |  |  | devclose(); | 
| 259 |  |  | kill(0, SIGTSTP); | 
| 260 |  |  | /* pc stops here */ | 
| 261 |  |  | devopen(devname); | 
| 262 |  |  | redraw(); | 
| 263 |  |  | break; | 
| 264 |  |  | #endif | 
| 265 |  |  | case '\0':                              /* continue */ | 
| 266 |  |  | break; | 
| 267 |  |  | default:; | 
| 268 |  |  | commerr: | 
| 269 |  |  | if (iscntrl(inpbuf[0])) | 
| 270 |  |  | sprintf(errmsg, "^%c: unknown control", | 
| 271 |  |  | inpbuf[0]|0100); | 
| 272 |  |  | else | 
| 273 |  |  | sprintf(errmsg, "%s: unknown command", inpbuf); | 
| 274 |  |  | error(COMMAND, errmsg); | 
| 275 |  |  | break; | 
| 276 |  |  | } | 
| 277 |  |  | #undef  badcom | 
| 278 |  |  | } | 
| 279 |  |  |  | 
| 280 |  |  |  | 
| 281 |  |  | rsample()                       /* sample the image */ | 
| 282 |  |  | { | 
| 283 |  |  | int  xsiz, ysiz, y; | 
| 284 |  |  | RECT  r; | 
| 285 |  |  | PNODE  *p; | 
| 286 |  |  | register RECT  *rl; | 
| 287 |  |  | register PNODE  **pl; | 
| 288 |  |  | register int  x; | 
| 289 |  |  | /* | 
| 290 |  |  | *     We initialize the bottom row in the image at our current | 
| 291 |  |  | * resolution.  During sampling, we check super-pixels to the | 
| 292 |  |  | * right and above by calling bigdiff().  If there is a significant | 
| 293 |  |  | * difference, we subsample the super-pixels.  The testing process | 
| 294 |  |  | * includes initialization of the next row. | 
| 295 |  |  | */ | 
| 296 | greg | 1.9 | xsiz = (((pframe.r-pframe.l)<<pdepth)+hresolu-1) / hresolu; | 
| 297 |  |  | ysiz = (((pframe.u-pframe.d)<<pdepth)+vresolu-1) / vresolu; | 
| 298 | greg | 1.1 | rl = (RECT *)malloc(xsiz*sizeof(RECT)); | 
| 299 | greg | 1.12 | if (rl == NULL) | 
| 300 | greg | 1.14 | return; | 
| 301 | greg | 1.1 | pl = (PNODE **)malloc(xsiz*sizeof(PNODE *)); | 
| 302 | greg | 1.12 | if (pl == NULL) | 
| 303 | greg | 1.14 | return; | 
| 304 | greg | 1.1 | /* | 
| 305 |  |  | * Initialize the bottom row. | 
| 306 |  |  | */ | 
| 307 |  |  | rl[0].l = rl[0].d = 0; | 
| 308 | greg | 1.9 | rl[0].r = hresolu; rl[0].u = vresolu; | 
| 309 | greg | 1.1 | pl[0] = findrect(pframe.l, pframe.d, &ptrunk, rl, pdepth); | 
| 310 |  |  | for (x = 1; x < xsiz; x++) { | 
| 311 |  |  | rl[x].l = rl[x].d = 0; | 
| 312 | greg | 1.9 | rl[x].r = hresolu; rl[x].u = vresolu; | 
| 313 |  |  | pl[x] = findrect(pframe.l+((x*hresolu)>>pdepth), | 
| 314 | greg | 1.1 | pframe.d, &ptrunk, rl+x, pdepth); | 
| 315 |  |  | } | 
| 316 |  |  | /* sample the image */ | 
| 317 |  |  | for (y = 0; /* y < ysiz */ ; y++) { | 
| 318 |  |  | for (x = 0; x < xsiz-1; x++) { | 
| 319 | greg | 1.20 | if (dev->inpready || errno == ENOMEM) | 
| 320 | greg | 1.1 | goto escape; | 
| 321 |  |  | /* | 
| 322 |  |  | * Test super-pixel to the right. | 
| 323 |  |  | */ | 
| 324 |  |  | if (pl[x] != pl[x+1] && bigdiff(pl[x]->v, | 
| 325 |  |  | pl[x+1]->v, maxdiff)) { | 
| 326 |  |  | refine(pl[x], rl[x].l, rl[x].d, | 
| 327 |  |  | rl[x].r, rl[x].u, 1); | 
| 328 |  |  | refine(pl[x+1], rl[x+1].l, rl[x+1].d, | 
| 329 |  |  | rl[x+1].r, rl[x+1].u, 1); | 
| 330 |  |  | } | 
| 331 |  |  | } | 
| 332 |  |  | if (y >= ysiz-1) | 
| 333 |  |  | break; | 
| 334 |  |  | for (x = 0; x < xsiz; x++) { | 
| 335 | greg | 1.20 | if (dev->inpready || errno == ENOMEM) | 
| 336 | greg | 1.1 | goto escape; | 
| 337 |  |  | /* | 
| 338 |  |  | * Find super-pixel at this position in next row. | 
| 339 |  |  | */ | 
| 340 |  |  | r.l = r.d = 0; | 
| 341 | greg | 1.9 | r.r = hresolu; r.u = vresolu; | 
| 342 |  |  | p = findrect(pframe.l+((x*hresolu)>>pdepth), | 
| 343 |  |  | pframe.d+(((y+1)*vresolu)>>pdepth), | 
| 344 | greg | 1.1 | &ptrunk, &r, pdepth); | 
| 345 |  |  | /* | 
| 346 |  |  | * Test super-pixel in next row. | 
| 347 |  |  | */ | 
| 348 |  |  | if (pl[x] != p && bigdiff(pl[x]->v, p->v, maxdiff)) { | 
| 349 |  |  | refine(pl[x], rl[x].l, rl[x].d, | 
| 350 |  |  | rl[x].r, rl[x].u, 1); | 
| 351 |  |  | refine(p, r.l, r.d, r.r, r.u, 1); | 
| 352 |  |  | } | 
| 353 |  |  | /* | 
| 354 |  |  | * Copy into super-pixel array. | 
| 355 |  |  | */ | 
| 356 |  |  | rl[x].l = r.l; rl[x].d = r.d; | 
| 357 |  |  | rl[x].r = r.r; rl[x].u = r.u; | 
| 358 |  |  | pl[x] = p; | 
| 359 |  |  | } | 
| 360 |  |  | } | 
| 361 |  |  | escape: | 
| 362 |  |  | free((char *)rl); | 
| 363 |  |  | free((char *)pl); | 
| 364 |  |  | } | 
| 365 |  |  |  | 
| 366 |  |  |  | 
| 367 |  |  | int | 
| 368 |  |  | refine(p, xmin, ymin, xmax, ymax, pd)           /* refine a node */ | 
| 369 |  |  | register PNODE  *p; | 
| 370 |  |  | int  xmin, ymin, xmax, ymax; | 
| 371 |  |  | int  pd; | 
| 372 |  |  | { | 
| 373 |  |  | int  growth; | 
| 374 |  |  | int  mx, my; | 
| 375 |  |  | int  i; | 
| 376 |  |  |  | 
| 377 |  |  | if (dev->inpready)                      /* quit for input */ | 
| 378 |  |  | return(0); | 
| 379 |  |  |  | 
| 380 |  |  | if (pd <= 0)                            /* depth limit */ | 
| 381 |  |  | return(0); | 
| 382 |  |  |  | 
| 383 |  |  | mx = (xmin + xmax) >> 1; | 
| 384 |  |  | my = (ymin + ymax) >> 1; | 
| 385 |  |  | growth = 0; | 
| 386 |  |  |  | 
| 387 |  |  | if (p->kid == NULL) {                   /* subdivide */ | 
| 388 |  |  |  | 
| 389 |  |  | if ((p->kid = newptree()) == NULL) | 
| 390 | greg | 1.20 | return(0); | 
| 391 | greg | 1.1 | /* | 
| 392 |  |  | *  The following paint order can leave a black pixel | 
| 393 |  |  | *  when redraw() is called in (*dev->paintr)(). | 
| 394 |  |  | */ | 
| 395 |  |  | if (p->x >= mx && p->y >= my) | 
| 396 |  |  | pcopy(p, p->kid+UR); | 
| 397 |  |  | else | 
| 398 |  |  | paint(p->kid+UR, mx, my, xmax, ymax); | 
| 399 |  |  | if (p->x < mx && p->y >= my) | 
| 400 |  |  | pcopy(p, p->kid+UL); | 
| 401 |  |  | else | 
| 402 |  |  | paint(p->kid+UL, xmin, my, mx, ymax); | 
| 403 |  |  | if (p->x >= mx && p->y < my) | 
| 404 |  |  | pcopy(p, p->kid+DR); | 
| 405 |  |  | else | 
| 406 |  |  | paint(p->kid+DR, mx, ymin, xmax, my); | 
| 407 |  |  | if (p->x < mx && p->y < my) | 
| 408 |  |  | pcopy(p, p->kid+DL); | 
| 409 |  |  | else | 
| 410 |  |  | paint(p->kid+DL, xmin, ymin, mx, my); | 
| 411 |  |  |  | 
| 412 |  |  | growth++; | 
| 413 |  |  | } | 
| 414 |  |  | /* do children */ | 
| 415 |  |  | if (mx > pframe.l) { | 
| 416 |  |  | if (my > pframe.d) | 
| 417 |  |  | growth += refine(p->kid+DL, xmin, ymin, mx, my, pd-1); | 
| 418 |  |  | if (my < pframe.u) | 
| 419 |  |  | growth += refine(p->kid+UL, xmin, my, mx, ymax, pd-1); | 
| 420 |  |  | } | 
| 421 |  |  | if (mx < pframe.r) { | 
| 422 |  |  | if (my > pframe.d) | 
| 423 |  |  | growth += refine(p->kid+DR, mx, ymin, xmax, my, pd-1); | 
| 424 |  |  | if (my < pframe.u) | 
| 425 |  |  | growth += refine(p->kid+UR, mx, my, xmax, ymax, pd-1); | 
| 426 |  |  | } | 
| 427 |  |  | /* recompute sum */ | 
| 428 |  |  | if (growth) { | 
| 429 |  |  | setcolor(p->v, 0.0, 0.0, 0.0); | 
| 430 |  |  | for (i = 0; i < 4; i++) | 
| 431 |  |  | addcolor(p->v, p->kid[i].v); | 
| 432 |  |  | scalecolor(p->v, 0.25); | 
| 433 |  |  | } | 
| 434 |  |  | return(growth); | 
| 435 |  |  | } |