/* Compute (and allocate) migration price matrix for optimization */ static void price_routes(PRICEMAT *pm, const RBFNODE *from_rbf, const RBFNODE *to_rbf) { FVECT *vto = (FVECT *)malloc(sizeof(FVECT) * to_rbf->nrbf); int i, j; pm->nrows = from_rbf->nrbf; pm->ncols = to_rbf->nrbf; pm->price = (float *)malloc(sizeof(float) * pm->nrows*pm->ncols); pm->sord = (short *)malloc(sizeof(short) * pm->nrows*pm->ncols); if ((pm->price == NULL) | (pm->sord == NULL) | (vto == NULL)) { fprintf(stderr, "%s: Out of memory in migration_costs()\n", progname); exit(1); } for (j = to_rbf->nrbf; j--; ) /* save repetitive ops. */ ovec_from_pos(vto[j], to_rbf->rbfa[j].gx, to_rbf->rbfa[j].gy); for (i = from_rbf->nrbf; i--; ) { const double from_ang = R2ANG(from_rbf->rbfa[i].crad); FVECT vfrom; ovec_from_pos(vfrom, from_rbf->rbfa[i].gx, from_rbf->rbfa[i].gy); for (j = to_rbf->nrbf; j--; ) { double dprod = DOT(vfrom, vto[j]); pricerow(pm,i)[j] = ((dprod >= 1.) ? .0 : acos(dprod)) + fabs(R2ANG(to_rbf->rbfa[j].crad) - from_ang); psortrow(pm,i)[j] = j; } qsort_r(psortrow(pm,i), pm->ncols, sizeof(short), pm, &msrt_cmp); } free(vto); }
/* Evaluate RBF for DSF at the given normalized outgoing direction */ double eval_rbfrep(const RBFNODE *rp, const FVECT outvec) { double minval = bsdf_min*output_orient*outvec[2]; double res = 0; const RBFVAL *rbfp; FVECT odir; double sig2; int n; /* use minimum if no information avail. */ if (rp == NULL) { if (outvec[2] > 0 ^ output_orient > 0) return(.0); return(minval); } rbfp = rp->rbfa; for (n = rp->nrbf; n--; rbfp++) { ovec_from_pos(odir, rbfp->gx, rbfp->gy); sig2 = R2ANG(rbfp->crad); sig2 = (DOT(odir,outvec) - 1.) / (sig2*sig2); if (sig2 > -19.) res += rbfp->peak * exp(sig2); } if (res < minval) /* never return less than minval */ return(minval); return(res); }
/* Compute volume associated with Gaussian lobe */ double rbf_volume(const RBFVAL *rbfp) { double rad = R2ANG(rbfp->crad); return((2.*M_PI) * rbfp->peak * rad*rad); }
/* Compute distance between two RBF lobes */ double lobe_distance(RBFVAL *rbf1, RBFVAL *rbf2) { FVECT vfrom, vto; double d, res; /* quadratic cost function */ ovec_from_pos(vfrom, rbf1->gx, rbf1->gy); ovec_from_pos(vto, rbf2->gx, rbf2->gy); d = Acos(DOT(vfrom, vto)); res = d*d; d = R2ANG(rbf2->crad) - R2ANG(rbf1->crad); res += d*d; /* neighborhood difference */ res += NEIGH_FACT2 * neighborhood_dist2( rbf1->gx, rbf1->gy, rbf2->gx, rbf2->gy ); return(res); }
/* Cull points for more uniform distribution, leave all nval 0 or 1 */ static void cull_values(void) { FVECT ovec0, ovec1; double maxang, maxang2; int i, j, ii, jj, r; /* simple greedy algorithm */ for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) { if (!dsf_grid[i][j].nval) continue; if (!dsf_grid[i][j].crad) continue; /* shouldn't happen */ ovec_from_pos(ovec0, i, j); maxang = 2.*R2ANG(dsf_grid[i][j].crad); if (maxang > ovec0[2]) /* clamp near horizon */ maxang = ovec0[2]; r = maxang*(2.*GRIDRES/M_PI) + 1; maxang2 = maxang*maxang; for (ii = i-r; ii <= i+r; ii++) { if (ii < 0) continue; if (ii >= GRIDRES) break; for (jj = j-r; jj <= j+r; jj++) { if (jj < 0) continue; if (jj >= GRIDRES) break; if (!dsf_grid[ii][jj].nval) continue; if ((ii == i) & (jj == j)) continue; /* don't get self-absorbed */ ovec_from_pos(ovec1, ii, jj); if (2. - 2.*DOT(ovec0,ovec1) >= maxang2) continue; /* absorb sum */ dsf_grid[i][j].vsum += dsf_grid[ii][jj].vsum; dsf_grid[i][j].nval += dsf_grid[ii][jj].nval; /* keep value, though */ dsf_grid[ii][jj].vsum /= (float)dsf_grid[ii][jj].nval; dsf_grid[ii][jj].nval = 0; } } } /* final averaging pass */ for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) if (dsf_grid[i][j].nval > 1) { dsf_grid[i][j].vsum /= (float)dsf_grid[i][j].nval; dsf_grid[i][j].nval = 1; } }
/* (distance to furthest empty bin for which non-empty bin is the closest) */ static void compute_radii(void) { unsigned int fill_grid[GRIDRES][GRIDRES]; unsigned short fill_cnt[GRIDRES][GRIDRES]; FVECT ovec0, ovec1; double ang2, lastang2; int r, i, j, jn, ii, jj, inear, jnear; r = GRIDRES/2; /* proceed in zig-zag */ for (i = 0; i < GRIDRES; i++) for (jn = 0; jn < GRIDRES; jn++) { j = (i&1) ? jn : GRIDRES-1-jn; if (dsf_grid[i][j].nval) /* find empty grid pos. */ continue; ovec_from_pos(ovec0, i, j); inear = jnear = -1; /* find nearest non-empty */ lastang2 = M_PI*M_PI; for (ii = i-r; ii <= i+r; ii++) { if (ii < 0) continue; if (ii >= GRIDRES) break; for (jj = j-r; jj <= j+r; jj++) { if (jj < 0) continue; if (jj >= GRIDRES) break; if (!dsf_grid[ii][jj].nval) continue; ovec_from_pos(ovec1, ii, jj); ang2 = 2. - 2.*DOT(ovec0,ovec1); if (ang2 >= lastang2) continue; lastang2 = ang2; inear = ii; jnear = jj; } } if (inear < 0) { fprintf(stderr, "%s: Could not find non-empty neighbor!\n", progname); exit(1); } ang2 = sqrt(lastang2); r = ANG2R(ang2); /* record if > previous */ if (r > dsf_grid[inear][jnear].crad) dsf_grid[inear][jnear].crad = r; /* next search radius */ r = ang2*(2.*GRIDRES/M_PI) + 3; } /* blur radii over hemisphere */ memset(fill_grid, 0, sizeof(fill_grid)); memset(fill_cnt, 0, sizeof(fill_cnt)); for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) { if (!dsf_grid[i][j].crad) continue; /* missing distance */ r = R2ANG(dsf_grid[i][j].crad)*(2.*RSCA*GRIDRES/M_PI); for (ii = i-r; ii <= i+r; ii++) { if (ii < 0) continue; if (ii >= GRIDRES) break; for (jj = j-r; jj <= j+r; jj++) { if (jj < 0) continue; if (jj >= GRIDRES) break; if ((ii-i)*(ii-i) + (jj-j)*(jj-j) > r*r) continue; fill_grid[ii][jj] += dsf_grid[i][j].crad; fill_cnt[ii][jj]++; } } } /* copy back blurred radii */ for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) if (fill_cnt[i][j]) dsf_grid[i][j].crad = fill_grid[i][j]/fill_cnt[i][j]; }