| 94 |  | cos_a = DOT(ej->rbfv[0]->invec, ivec); | 
| 95 |  | if (cos_a <= 0) | 
| 96 |  | return(0); | 
| 97 | + | if (cos_a >= 1.)                /* handles rounding error */ | 
| 98 | + | return(1); | 
| 99 |  |  | 
| 100 |  | cos_b = DOT(ej->rbfv[1]->invec, ivec); | 
| 101 |  | if (cos_b <= 0) | 
| 102 |  | return(0); | 
| 103 | + | if (cos_b >= 1.) | 
| 104 | + | return(1); | 
| 105 |  |  | 
| 106 |  | cos_aplusb = cos_a*cos_b - sqrt((1.-cos_a*cos_a)*(1.-cos_b*cos_b)); | 
| 107 |  | if (cos_aplusb <= 0) | 
| 130 |  | return(sgn2 == sgn3); | 
| 131 |  | } | 
| 132 |  |  | 
| 133 | < | /* Test and set for edge */ | 
| 133 | > | /* Test (and set) bitmap for edge */ | 
| 134 |  | static int | 
| 135 |  | check_edge(unsigned char *emap, int nedges, const MIGRATION *mig, int mark) | 
| 136 |  | { | 
| 155 |  | in_mesh(MIGRATION *miga[3], unsigned char *emap, int nedges, | 
| 156 |  | const FVECT ivec, MIGRATION *mig) | 
| 157 |  | { | 
| 158 | < | MIGRATION       *ej1, *ej2; | 
| 159 | < | RBFNODE         *tv; | 
| 158 | > | RBFNODE         *tv[2]; | 
| 159 | > | MIGRATION       *sej[2], *dej[2]; | 
| 160 | > | int             i; | 
| 161 |  | /* check visitation record */ | 
| 162 |  | if (!check_edge(emap, nedges, mig, 1)) | 
| 163 |  | return(0); | 
| 165 |  | miga[0] = mig;                  /* close enough to edge */ | 
| 166 |  | return(1); | 
| 167 |  | } | 
| 168 | < | /* do triangles either side */ | 
| 169 | < | for (ej1 = mig->rbfv[0]->ejl; ej1 != NULL; | 
| 170 | < | ej1 = nextedge(mig->rbfv[0],ej1)) { | 
| 171 | < | if (ej1 == mig) | 
| 172 | < | continue; | 
| 173 | < | tv = opp_rbf(mig->rbfv[0],ej1); | 
| 174 | < | for (ej2 = tv->ejl; ej2 != NULL; ej2 = nextedge(tv,ej2)) | 
| 175 | < | if (opp_rbf(tv,ej2) == mig->rbfv[1]) { | 
| 176 | < | int     do_ej1 = check_edge(emap, nedges, ej1, 0); | 
| 177 | < | int     do_ej2 = check_edge(emap, nedges, ej2, 0); | 
| 178 | < | if (do_ej1 && in_mesh(miga, emap, nedges, ivec, ej1)) | 
| 179 | < | return(1); | 
| 180 | < | if (do_ej2 && in_mesh(miga, emap, nedges, ivec, ej2)) | 
| 181 | < | return(1); | 
| 182 | < | /* check just once */ | 
| 178 | < | if (do_ej1 & do_ej2 && in_tri(mig->rbfv[0], | 
| 179 | < | mig->rbfv[1], tv, ivec)) { | 
| 180 | < | miga[0] = mig; | 
| 181 | < | miga[1] = ej1; | 
| 182 | < | miga[2] = ej2; | 
| 183 | < | return(1); | 
| 168 | > | if (!get_triangles(tv, mig))            /* do triangles either side? */ | 
| 169 | > | return(0); | 
| 170 | > | for (i = 2; i--; ) {                    /* identify edges to check */ | 
| 171 | > | MIGRATION       *ej; | 
| 172 | > | sej[i] = dej[i] = NULL; | 
| 173 | > | if (tv[i] == NULL) | 
| 174 | > | continue; | 
| 175 | > | for (ej = tv[i]->ejl; ej != NULL; ej = nextedge(tv[i],ej)) { | 
| 176 | > | RBFNODE *rbfop = opp_rbf(tv[i],ej); | 
| 177 | > | if (rbfop == mig->rbfv[0]) { | 
| 178 | > | if (check_edge(emap, nedges, ej, 0)) | 
| 179 | > | sej[i] = ej; | 
| 180 | > | } else if (rbfop == mig->rbfv[1]) { | 
| 181 | > | if (check_edge(emap, nedges, ej, 0)) | 
| 182 | > | dej[i] = ej; | 
| 183 |  | } | 
| 184 |  | } | 
| 185 |  | } | 
| 186 | + | for (i = 2; i--; ) {                    /* check triangles just once */ | 
| 187 | + | if (sej[i] != NULL && in_mesh(miga, emap, nedges, ivec, sej[i])) | 
| 188 | + | return(1); | 
| 189 | + | if (dej[i] != NULL && in_mesh(miga, emap, nedges, ivec, dej[i])) | 
| 190 | + | return(1); | 
| 191 | + | if ((sej[i] == NULL) | (dej[i] == NULL)) | 
| 192 | + | continue; | 
| 193 | + | if (in_tri(mig->rbfv[0], mig->rbfv[1], tv[i], ivec)) { | 
| 194 | + | miga[0] = mig; | 
| 195 | + | miga[1] = sej[i]; | 
| 196 | + | miga[2] = dej[i]; | 
| 197 | + | return(1); | 
| 198 | + | } | 
| 199 | + | } | 
| 200 |  | return(0);                              /* not near this edge */ | 
| 201 |  | } | 
| 202 |  |  | 
| 206 |  | { | 
| 207 |  | miga[0] = miga[1] = miga[2] = NULL; | 
| 208 |  | if (single_plane_incident) {            /* isotropic BSDF? */ | 
| 209 | < | RBFNODE *rbf;                   /* find edge we're on */ | 
| 210 | < | for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) { | 
| 211 | < | if (input_orient*rbf->invec[2] < input_orient*invec[2]) | 
| 212 | < | break; | 
| 213 | < | if (rbf->next != NULL && | 
| 214 | < | input_orient*rbf->next->invec[2] < | 
| 215 | < | input_orient*invec[2]) { | 
| 216 | < | for (miga[0] = rbf->ejl; miga[0] != NULL; | 
| 217 | < | miga[0] = nextedge(rbf,miga[0])) | 
| 218 | < | if (opp_rbf(rbf,miga[0]) == rbf->next) { | 
| 219 | < | double  nf = 1.-rbf->invec[2]*rbf->invec[2]; | 
| 220 | < | if (nf > FTINY) { | 
| 221 | < | nf = sqrt((1.-invec[2]*invec[2])/nf); | 
| 222 | < | invec[0] = nf*rbf->invec[0]; | 
| 223 | < | invec[1] = nf*rbf->invec[1]; | 
| 224 | < | } | 
| 225 | < | return(0); | 
| 213 | < | } | 
| 214 | < | break; | 
| 209 | > | RBFNODE     *rbf;                   /* find edge we're on */ | 
| 210 | > | for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) { | 
| 211 | > | if (input_orient*rbf->invec[2] < input_orient*invec[2]-FTINY) | 
| 212 | > | break; | 
| 213 | > | if (rbf->next != NULL && input_orient*rbf->next->invec[2] < | 
| 214 | > | input_orient*invec[2]+FTINY) { | 
| 215 | > | for (miga[0] = rbf->ejl; miga[0] != NULL; | 
| 216 | > | miga[0] = nextedge(rbf,miga[0])) | 
| 217 | > | if (opp_rbf(rbf,miga[0]) == rbf->next) { | 
| 218 | > | double  nf = 1. - | 
| 219 | > | rbf->next->invec[2]*rbf->next->invec[2]; | 
| 220 | > | if (nf > FTINY) {       /* rotate to match */ | 
| 221 | > | nf = sqrt((1.-invec[2]*invec[2])/nf); | 
| 222 | > | invec[0] = nf*rbf->next->invec[0]; | 
| 223 | > | invec[1] = nf*rbf->next->invec[1]; | 
| 224 | > | } | 
| 225 | > | return(0);      /* rotational symmetry */ | 
| 226 |  | } | 
| 227 | + | break; | 
| 228 |  | } | 
| 229 | < | return(-1);                     /* outside range! */ | 
| 229 | > | } | 
| 230 | > | return(-1);                         /* outside range! */ | 
| 231 |  | } | 
| 232 |  | {                                       /* else use triangle mesh */ | 
| 233 |  | int             sym = use_symmetry(invec); | 
| 244 |  | exit(1); | 
| 245 |  | } | 
| 246 |  | /* identify intersection  */ | 
| 247 | < | if (!in_mesh(miga, emap, nedges, invec, mig_list)) | 
| 247 | > | if (!in_mesh(miga, emap, nedges, invec, mig_list)) { | 
| 248 | > | #ifdef DEBUG | 
| 249 | > | fprintf(stderr, | 
| 250 | > | "Incident angle (%.1f,%.1f) deg. outside mesh\n", | 
| 251 | > | get_theta180(invec), get_phi360(invec)); | 
| 252 | > | #endif | 
| 253 |  | sym = -1;               /* outside mesh */ | 
| 254 | < | else if (miga[1] != NULL && | 
| 254 | > | } else if (miga[1] != NULL && | 
| 255 |  | (miga[2] == NULL || !order_triangle(miga))) { | 
| 256 |  | #ifdef DEBUG | 
| 257 |  | fputs("Munged triangle in get_interp()\n", stderr); | 
| 263 |  | } | 
| 264 |  | } | 
| 265 |  |  | 
| 266 | < | /* Advect and allocate new RBF along edge */ | 
| 249 | < | static RBFNODE * | 
| 250 | < | e_advect_rbf(const MIGRATION *mig, const FVECT invec) | 
| 251 | < | { | 
| 252 | < | RBFNODE         *rbf; | 
| 253 | < | int             n, i, j; | 
| 254 | < | double          t, full_dist; | 
| 255 | < | /* get relative position */ | 
| 256 | < | t = acos(DOT(invec, mig->rbfv[0]->invec)); | 
| 257 | < | if (t < M_PI/grid_res) {                /* near first DSF */ | 
| 258 | < | n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[0]->nrbf-1); | 
| 259 | < | rbf = (RBFNODE *)malloc(n); | 
| 260 | < | if (rbf == NULL) | 
| 261 | < | goto memerr; | 
| 262 | < | memcpy(rbf, mig->rbfv[0], n);   /* just duplicate */ | 
| 263 | < | return(rbf); | 
| 264 | < | } | 
| 265 | < | full_dist = acos(DOT(mig->rbfv[0]->invec, mig->rbfv[1]->invec)); | 
| 266 | < | if (t > full_dist-M_PI/grid_res) {      /* near second DSF */ | 
| 267 | < | n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[1]->nrbf-1); | 
| 268 | < | rbf = (RBFNODE *)malloc(n); | 
| 269 | < | if (rbf == NULL) | 
| 270 | < | goto memerr; | 
| 271 | < | memcpy(rbf, mig->rbfv[1], n);   /* just duplicate */ | 
| 272 | < | return(rbf); | 
| 273 | < | } | 
| 274 | < | t /= full_dist; | 
| 275 | < | n = 0;                                  /* count migrating particles */ | 
| 276 | < | for (i = 0; i < mtx_nrows(mig); i++) | 
| 277 | < | for (j = 0; j < mtx_ncols(mig); j++) | 
| 278 | < | n += (mtx_coef(mig,i,j) > FTINY); | 
| 279 | < | #ifdef DEBUG | 
| 280 | < | fprintf(stderr, "Input RBFs have %d, %d nodes -> output has %d\n", | 
| 281 | < | mig->rbfv[0]->nrbf, mig->rbfv[1]->nrbf, n); | 
| 282 | < | #endif | 
| 283 | < | rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1)); | 
| 284 | < | if (rbf == NULL) | 
| 285 | < | goto memerr; | 
| 286 | < | rbf->next = NULL; rbf->ejl = NULL; | 
| 287 | < | VCOPY(rbf->invec, invec); | 
| 288 | < | rbf->nrbf = n; | 
| 289 | < | rbf->vtotal = 1.-t + t*mig->rbfv[1]->vtotal/mig->rbfv[0]->vtotal; | 
| 290 | < | n = 0;                                  /* advect RBF lobes */ | 
| 291 | < | for (i = 0; i < mtx_nrows(mig); i++) { | 
| 292 | < | const RBFVAL        *rbf0i = &mig->rbfv[0]->rbfa[i]; | 
| 293 | < | const float         peak0 = rbf0i->peak; | 
| 294 | < | const double        rad0 = R2ANG(rbf0i->crad); | 
| 295 | < | FVECT               v0; | 
| 296 | < | float               mv; | 
| 297 | < | ovec_from_pos(v0, rbf0i->gx, rbf0i->gy); | 
| 298 | < | for (j = 0; j < mtx_ncols(mig); j++) | 
| 299 | < | if ((mv = mtx_coef(mig,i,j)) > FTINY) { | 
| 300 | < | const RBFVAL    *rbf1j = &mig->rbfv[1]->rbfa[j]; | 
| 301 | < | double          rad1 = R2ANG(rbf1j->crad); | 
| 302 | < | FVECT           v; | 
| 303 | < | int             pos[2]; | 
| 304 | < | rbf->rbfa[n].peak = peak0 * mv * rbf->vtotal; | 
| 305 | < | rbf->rbfa[n].crad = ANG2R(sqrt(rad0*rad0*(1.-t) + | 
| 306 | < | rad1*rad1*t)); | 
| 307 | < | ovec_from_pos(v, rbf1j->gx, rbf1j->gy); | 
| 308 | < | geodesic(v, v0, v, t, GEOD_REL); | 
| 309 | < | pos_from_vec(pos, v); | 
| 310 | < | rbf->rbfa[n].gx = pos[0]; | 
| 311 | < | rbf->rbfa[n].gy = pos[1]; | 
| 312 | < | ++n; | 
| 313 | < | } | 
| 314 | < | } | 
| 315 | < | rbf->vtotal *= mig->rbfv[0]->vtotal;    /* turn ratio into actual */ | 
| 316 | < | return(rbf); | 
| 317 | < | memerr: | 
| 318 | < | fprintf(stderr, "%s: Out of memory in e_advect_rbf()\n", progname); | 
| 319 | < | exit(1); | 
| 320 | < | return(NULL);   /* pro forma return */ | 
| 321 | < | } | 
| 322 | < |  | 
| 323 | < | /* Partially advect between recorded incident angles and allocate new RBF */ | 
| 266 | > | /* Advect between recorded incident angles and allocate new RBF */ | 
| 267 |  | RBFNODE * | 
| 268 | < | advect_rbf(const FVECT invec) | 
| 268 | > | advect_rbf(const FVECT invec, int lobe_lim) | 
| 269 |  | { | 
| 270 | + | double          cthresh = FTINY; | 
| 271 |  | FVECT           sivec; | 
| 272 |  | MIGRATION       *miga[3]; | 
| 273 |  | RBFNODE         *rbf; | 
| 280 |  | VCOPY(sivec, invec);                    /* find triangle/edge */ | 
| 281 |  | sym = get_interp(miga, sivec); | 
| 282 |  | if (sym < 0)                            /* can't interpolate? */ | 
| 283 | < | return(NULL); | 
| 283 | > | return(def_rbf_spec(invec)); | 
| 284 |  | if (miga[1] == NULL) {                  /* advect along edge? */ | 
| 285 | < | rbf = e_advect_rbf(miga[0], sivec); | 
| 285 | > | rbf = e_advect_rbf(miga[0], sivec, lobe_lim); | 
| 286 |  | if (single_plane_incident) | 
| 287 |  | rotate_rbf(rbf, invec); | 
| 288 |  | else | 
| 290 |  | return(rbf); | 
| 291 |  | } | 
| 292 |  | #ifdef DEBUG | 
| 293 | < | if (miga[0]->rbfv[0] != miga[2]->rbfv[0] | | 
| 294 | < | miga[0]->rbfv[1] != miga[1]->rbfv[0] | | 
| 295 | < | miga[1]->rbfv[1] != miga[2]->rbfv[1]) { | 
| 293 | > | if ((miga[0]->rbfv[0] != miga[2]->rbfv[0]) | | 
| 294 | > | (miga[0]->rbfv[1] != miga[1]->rbfv[0]) | | 
| 295 | > | (miga[1]->rbfv[1] != miga[2]->rbfv[1])) { | 
| 296 |  | fprintf(stderr, "%s: Triangle vertex screw-up!\n", progname); | 
| 297 |  | exit(1); | 
| 298 |  | } | 
| 308 |  | geodesic(v1, miga[0]->rbfv[0]->invec, miga[0]->rbfv[1]->invec, | 
| 309 |  | s, GEOD_REL); | 
| 310 |  | t = acos(DOT(v1,sivec)) / acos(DOT(v1,miga[1]->rbfv[1]->invec)); | 
| 311 | + | tryagain: | 
| 312 |  | n = 0;                                  /* count migrating particles */ | 
| 313 |  | for (i = 0; i < mtx_nrows(miga[0]); i++) | 
| 314 |  | for (j = 0; j < mtx_ncols(miga[0]); j++) | 
| 315 | < | for (k = (mtx_coef(miga[0],i,j) > FTINY) * | 
| 315 | > | for (k = (mtx_coef(miga[0],i,j) > cthresh) * | 
| 316 |  | mtx_ncols(miga[2]); k--; ) | 
| 317 | < | n += (mtx_coef(miga[2],i,k) > FTINY || | 
| 318 | < | mtx_coef(miga[1],j,k) > FTINY); | 
| 317 | > | n += (mtx_coef(miga[2],i,k) > cthresh || | 
| 318 | > | mtx_coef(miga[1],j,k) > cthresh); | 
| 319 | > | /* are we over our limit? */ | 
| 320 | > | if ((lobe_lim > 0) & (n > lobe_lim)) { | 
| 321 | > | cthresh = cthresh*2. + 10.*FTINY; | 
| 322 | > | goto tryagain; | 
| 323 | > | } | 
| 324 |  | #ifdef DEBUG | 
| 325 |  | fprintf(stderr, "Input RBFs have %d, %d, %d nodes -> output has %d\n", | 
| 326 |  | miga[0]->rbfv[0]->nrbf, miga[0]->rbfv[1]->nrbf, | 
| 347 |  | for (j = 0; j < mtx_ncols(miga[0]); j++) { | 
| 348 |  | const float     ma = mtx_coef(miga[0],i,j); | 
| 349 |  | const RBFVAL    *rbf1j; | 
| 350 | < | double          rad1j, srad2; | 
| 351 | < | if (ma <= FTINY) | 
| 350 | > | double          srad2; | 
| 351 | > | if (ma <= cthresh) | 
| 352 |  | continue; | 
| 353 |  | rbf1j = &miga[0]->rbfv[1]->rbfa[j]; | 
| 354 | < | rad1j = R2ANG(rbf1j->crad); | 
| 355 | < | srad2 = (1.-s)*(1.-t)*rad0i*rad0i + s*(1.-t)*rad1j*rad1j; | 
| 354 | > | srad2 = R2ANG(rbf1j->crad); | 
| 355 | > | srad2 = (1.-s)*(1.-t)*rad0i*rad0i + s*(1.-t)*srad2*srad2; | 
| 356 |  | ovec_from_pos(v1, rbf1j->gx, rbf1j->gy); | 
| 357 |  | geodesic(v1, v0, v1, s, GEOD_REL); | 
| 358 |  | for (k = 0; k < mtx_ncols(miga[2]); k++) { | 
| 359 |  | float               mb = mtx_coef(miga[1],j,k); | 
| 360 |  | float               mc = mtx_coef(miga[2],i,k); | 
| 361 |  | const RBFVAL        *rbf2k; | 
| 362 | < | double              rad2k; | 
| 413 | < | FVECT               vout; | 
| 362 | > | double              rad2; | 
| 363 |  | int                 pos[2]; | 
| 364 | < | if ((mb <= FTINY) & (mc <= FTINY)) | 
| 364 | > | if ((mb <= cthresh) & (mc <= cthresh)) | 
| 365 |  | continue; | 
| 366 |  | rbf2k = &miga[2]->rbfv[1]->rbfa[k]; | 
| 367 | < | rbf->rbfa[n].peak = w0i * ma * (mb*mbfact + mc*mcfact); | 
| 368 | < | rad2k = R2ANG(rbf2k->crad); | 
| 369 | < | rbf->rbfa[n].crad = ANG2R(sqrt(srad2 + t*rad2k*rad2k)); | 
| 367 | > | rad2 = R2ANG(rbf2k->crad); | 
| 368 | > | rad2 = srad2 + t*rad2*rad2; | 
| 369 | > | rbf->rbfa[n].peak = w0i * ma * (mb*mbfact + mc*mcfact) * | 
| 370 | > | rad0i*rad0i/rad2; | 
| 371 | > | rbf->rbfa[n].crad = ANG2R(sqrt(rad2)); | 
| 372 |  | ovec_from_pos(v2, rbf2k->gx, rbf2k->gy); | 
| 373 | < | geodesic(vout, v1, v2, t, GEOD_REL); | 
| 374 | < | pos_from_vec(pos, vout); | 
| 373 | > | geodesic(v2, v1, v2, t, GEOD_REL); | 
| 374 | > | pos_from_vec(pos, v2); | 
| 375 |  | rbf->rbfa[n].gx = pos[0]; | 
| 376 |  | rbf->rbfa[n].gy = pos[1]; | 
| 377 |  | ++n; |