/* Compute and insert migration along directed edge (may fork child) */ static MIGRATION * create_migration(RBFNODE *from_rbf, RBFNODE *to_rbf) { const double end_thresh = 5e-6; PRICEMAT pmtx; MIGRATION *newmig; double *src_rem, *dst_rem; double total_rem = 1., move_amt; int i, j; /* check if exists already */ for (newmig = from_rbf->ejl; newmig != NULL; newmig = nextedge(from_rbf,newmig)) if (newmig->rbfv[1] == to_rbf) return(NULL); /* else allocate */ #ifdef DEBUG fprintf(stderr, "Building path from (theta,phi) (%.0f,%.0f) ", get_theta180(from_rbf->invec), get_phi360(from_rbf->invec)); fprintf(stderr, "to (%.0f,%.0f) with %d x %d matrix\n", get_theta180(to_rbf->invec), get_phi360(to_rbf->invec), from_rbf->nrbf, to_rbf->nrbf); #endif newmig = new_migration(from_rbf, to_rbf); if (run_subprocess()) return(newmig); /* child continues */ price_routes(&pmtx, from_rbf, to_rbf); src_rem = (double *)malloc(sizeof(double)*from_rbf->nrbf); dst_rem = (double *)malloc(sizeof(double)*to_rbf->nrbf); if ((src_rem == NULL) | (dst_rem == NULL)) { fprintf(stderr, "%s: Out of memory in create_migration()\n", progname); exit(1); } /* starting quantities */ memset(newmig->mtx, 0, sizeof(float)*from_rbf->nrbf*to_rbf->nrbf); for (i = from_rbf->nrbf; i--; ) src_rem[i] = rbf_volume(&from_rbf->rbfa[i]) / from_rbf->vtotal; for (j = to_rbf->nrbf; j--; ) dst_rem[j] = rbf_volume(&to_rbf->rbfa[j]) / to_rbf->vtotal; do { /* move a bit at a time */ move_amt = migration_step(newmig, src_rem, dst_rem, &pmtx); total_rem -= move_amt; } while ((total_rem > end_thresh) & (move_amt > 0)); for (i = from_rbf->nrbf; i--; ) { /* normalize final matrix */ double nf = rbf_volume(&from_rbf->rbfa[i]); if (nf <= FTINY) continue; nf = from_rbf->vtotal / nf; for (j = to_rbf->nrbf; j--; ) mtx_coef(newmig,i,j) *= nf; /* row now sums to 1.0 */ } end_subprocess(); /* exit here if subprocess */ free_routes(&pmtx); /* free working arrays */ free(src_rem); free(dst_rem); return(newmig); }
/* Compute and insert migration along directed edge (may fork child) */ static MIGRATION * create_migration(RBFNODE *from_rbf, RBFNODE *to_rbf) { MIGRATION *newmig; int i, j; /* check if exists already */ for (newmig = from_rbf->ejl; newmig != NULL; newmig = nextedge(from_rbf,newmig)) if (newmig->rbfv[1] == to_rbf) return(NULL); /* else allocate */ #ifdef DEBUG fprintf(stderr, "Building path from (theta,phi) (%.1f,%.1f) ", get_theta180(from_rbf->invec), get_phi360(from_rbf->invec)); fprintf(stderr, "to (%.1f,%.1f) with %d x %d matrix\n", get_theta180(to_rbf->invec), get_phi360(to_rbf->invec), from_rbf->nrbf, to_rbf->nrbf); #endif newmig = new_migration(from_rbf, to_rbf); if (run_subprocess()) return(newmig); /* child continues */ /* compute transport plan */ compute_nDSFs(from_rbf, to_rbf); plan_transport(newmig); for (i = from_rbf->nrbf; i--; ) { /* normalize final matrix */ double nf = rbf_volume(&from_rbf->rbfa[i]); if (nf <= FTINY) continue; nf = from_rbf->vtotal / nf; for (j = to_rbf->nrbf; j--; ) mtx_coef(newmig,i,j) *= nf; /* row now sums to 1.0 */ } end_subprocess(); /* exit here if subprocess */ return(newmig); }
/* Apply symmetry to the given vector based on distribution */ int use_symmetry(FVECT vec) { const double phi = get_phi360(vec); switch (inp_coverage) { case INP_QUAD1|INP_QUAD2|INP_QUAD3|INP_QUAD4: break; case INP_QUAD1|INP_QUAD2: if ((-FTINY > phi) | (phi > 180.+FTINY)) goto mir_y; break; case INP_QUAD2|INP_QUAD3: if ((90.-FTINY > phi) | (phi > 270.+FTINY)) goto mir_x; break; case INP_QUAD3|INP_QUAD4: if ((180.-FTINY > phi) | (phi > 360.+FTINY)) goto mir_y; break; case INP_QUAD4|INP_QUAD1: if ((270.-FTINY > phi) & (phi > 90.+FTINY)) goto mir_x; break; case INP_QUAD1: if ((-FTINY > phi) | (phi > 90.+FTINY)) switch ((int)(phi*(1./90.))) { case 1: goto mir_x; case 2: goto mir_xy; case 3: goto mir_y; } break; case INP_QUAD2: if ((90.-FTINY > phi) | (phi > 180.+FTINY)) switch ((int)(phi*(1./90.))) { case 0: goto mir_x; case 2: goto mir_y; case 3: goto mir_xy; } break; case INP_QUAD3: if ((180.-FTINY > phi) | (phi > 270.+FTINY)) switch ((int)(phi*(1./90.))) { case 0: goto mir_xy; case 1: goto mir_y; case 3: goto mir_x; } break; case INP_QUAD4: if ((270.-FTINY > phi) | (phi > 360.+FTINY)) switch ((int)(phi*(1./90.))) { case 0: goto mir_y; case 1: goto mir_xy; case 2: goto mir_x; } break; default: fprintf(stderr, "%s: Illegal input coverage (%d)\n", progname, inp_coverage); exit(1); } return(0); /* in range */ mir_x: vec[0] = -vec[0]; return(MIRROR_X); mir_y: vec[1] = -vec[1]; return(MIRROR_Y); mir_xy: vec[0] = -vec[0]; vec[1] = -vec[1]; return(MIRROR_X|MIRROR_Y); }
/* Add normal direction if missing */ static void check_normal_incidence(void) { static FVECT norm_vec = {.0, .0, 1.}; const int saved_nprocs = nprocs; RBFNODE *near_rbf, *mir_rbf, *rbf; double bestd; int n; if (dsf_list == NULL) return; /* XXX should be error? */ near_rbf = dsf_list; bestd = input_orient*near_rbf->invec[2]; if (single_plane_incident) { /* ordered plane incidence? */ if (bestd >= 1.-2.*FTINY) return; /* already have normal */ } else { switch (inp_coverage) { case INP_QUAD1: case INP_QUAD2: case INP_QUAD3: case INP_QUAD4: break; /* quadrilateral symmetry? */ default: return; /* else we can interpolate */ } for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) { const double d = input_orient*rbf->invec[2]; if (d >= 1.-2.*FTINY) return; /* seems we have normal */ if (d > bestd) { near_rbf = rbf; bestd = d; } } } if (mig_list != NULL) { /* need to be called first */ fprintf(stderr, "%s: Late call to check_normal_incidence()\n", progname); exit(1); } #ifdef DEBUG fprintf(stderr, "Interpolating normal incidence by mirroring (%.1f,%.1f)\n", get_theta180(near_rbf->invec), get_phi360(near_rbf->invec)); #endif /* mirror nearest incidence */ n = sizeof(RBFNODE) + sizeof(RBFVAL)*(near_rbf->nrbf-1); mir_rbf = (RBFNODE *)malloc(n); if (mir_rbf == NULL) goto memerr; memcpy(mir_rbf, near_rbf, n); mir_rbf->ord = near_rbf->ord - 1; /* not used, I think */ mir_rbf->next = NULL; mir_rbf->ejl = NULL; rev_rbf_symmetry(mir_rbf, MIRROR_X|MIRROR_Y); nprocs = 1; /* compute migration matrix */ if (create_migration(mir_rbf, near_rbf) == NULL) exit(1); /* XXX should never happen! */ norm_vec[2] = input_orient; /* interpolate normal dist. */ rbf = e_advect_rbf(mig_list, norm_vec, 0); nprocs = saved_nprocs; /* final clean-up */ free(mir_rbf); free(mig_list); mig_list = near_rbf->ejl = NULL; insert_dsf(rbf); /* insert interpolated normal */ return; memerr: fprintf(stderr, "%s: Out of memory in check_normal_incidence()\n", progname); exit(1); }
/* Count up filled nodes and build RBF representation from current grid */ RBFNODE * make_rbfrep(void) { int niter = 16; double lastVar, thisVar = 100.; int nn; RBFNODE *newnode; RBFVAL *itera; int i, j; /* compute RBF radii */ compute_radii(); /* coagulate lobes */ cull_values(); nn = 0; /* count selected bins */ for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) nn += dsf_grid[i][j].nval; /* compute minimum BSDF */ comp_bsdf_min(); /* allocate RBF array */ newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1)); if (newnode == NULL) goto memerr; newnode->ord = -1; newnode->next = NULL; newnode->ejl = NULL; newnode->invec[2] = sin((M_PI/180.)*theta_in_deg); newnode->invec[0] = cos((M_PI/180.)*phi_in_deg)*newnode->invec[2]; newnode->invec[1] = sin((M_PI/180.)*phi_in_deg)*newnode->invec[2]; newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]); newnode->vtotal = 0; newnode->nrbf = nn; nn = 0; /* fill RBF array */ for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) if (dsf_grid[i][j].nval) { newnode->rbfa[nn].peak = dsf_grid[i][j].vsum; newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5; newnode->rbfa[nn].gx = i; newnode->rbfa[nn].gy = j; ++nn; } /* iterate to improve interpolation accuracy */ itera = (RBFVAL *)malloc(sizeof(RBFVAL)*newnode->nrbf); if (itera == NULL) goto memerr; memcpy(itera, newnode->rbfa, sizeof(RBFVAL)*newnode->nrbf); do { double dsum = 0, dsum2 = 0; nn = 0; for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) if (dsf_grid[i][j].nval) { FVECT odir; double corr; ovec_from_pos(odir, i, j); itera[nn++].peak *= corr = dsf_grid[i][j].vsum / eval_rbfrep(newnode, odir); dsum += 1. - corr; dsum2 += (1.-corr)*(1.-corr); } memcpy(newnode->rbfa, itera, sizeof(RBFVAL)*newnode->nrbf); lastVar = thisVar; thisVar = dsum2/(double)nn; #ifdef DEBUG fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n", 100.*dsum/(double)nn, 100.*sqrt(thisVar)); #endif } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar); free(itera); nn = 0; /* compute sum for normalization */ while (nn < newnode->nrbf) newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]); #ifdef DEBUG fprintf(stderr, "Integrated DSF at (%.1f,%.1f) deg. is %.2f\n", get_theta180(newnode->invec), get_phi360(newnode->invec), newnode->vtotal); #endif insert_dsf(newnode); return(newnode); memerr: fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname); exit(1); }