ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
Revision: 2.9
Committed: Thu Sep 6 00:07:43 2012 UTC (11 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.8: +2 -2 lines
Log Message:
Created geodesic() function for vector rotation along great circles

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.9 static const char RCSid[] = "$Id: pabopto2xml.c,v 2.8 2012/09/05 00:39:32 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Convert PAB-Opto measurements to XML format using tensor tree representation
6     * Employs Bonneel et al. Earth Mover's Distance interpolant.
7     *
8     * G.Ward
9     */
10    
11     #define _USE_MATH_DEFINES
12     #include <stdio.h>
13     #include <stdlib.h>
14     #include <string.h>
15     #include <ctype.h>
16     #include <math.h>
17     #include "bsdf.h"
18    
19     #ifndef GRIDRES
20     #define GRIDRES 200 /* max. grid resolution per side */
21     #endif
22    
23 greg 2.3 #define RSCA 2.7 /* radius scaling factor (empirical) */
24 greg 2.2
25 greg 2.6 /* convert to/from coded radians */
26     #define ANG2R(r) (int)((r)*((1<<16)/M_PI))
27 greg 2.2 #define R2ANG(c) (((c)+.5)*(M_PI/(1<<16)))
28 greg 2.1
29     typedef struct {
30 greg 2.5 float vsum; /* DSF sum */
31 greg 2.1 unsigned short nval; /* number of values in sum */
32 greg 2.2 unsigned short crad; /* radius (coded angle) */
33 greg 2.1 } GRIDVAL; /* grid value */
34    
35     typedef struct {
36 greg 2.5 float peak; /* lobe value at peak */
37 greg 2.2 unsigned short crad; /* radius (coded angle) */
38 greg 2.1 unsigned char gx, gy; /* grid position */
39     } RBFVAL; /* radial basis function value */
40    
41 greg 2.7 struct s_rbfnode; /* forward declaration of RBF struct */
42    
43     typedef struct s_migration {
44     struct s_migration *next; /* next in global edge list */
45     struct s_rbfnode *rbfv[2]; /* from,to vertex */
46     struct s_migration *enxt[2]; /* next from,to sibling */
47     float mtx[1]; /* matrix (extends struct) */
48     } MIGRATION; /* migration link (winged edge structure) */
49    
50     typedef struct s_rbfnode {
51     struct s_rbfnode *next; /* next in global RBF list */
52     MIGRATION *ejl; /* edge list for this vertex */
53 greg 2.1 FVECT invec; /* incident vector direction */
54 greg 2.8 double vtotal; /* volume for normalization */
55 greg 2.1 int nrbf; /* number of RBFs */
56     RBFVAL rbfa[1]; /* RBF array (extends struct) */
57 greg 2.5 } RBFLIST; /* RBF representation of DSF @ 1 incidence */
58 greg 2.1
59     /* our loaded grid for this incident angle */
60     static double theta_in_deg, phi_in_deg;
61 greg 2.5 static GRIDVAL dsf_grid[GRIDRES][GRIDRES];
62 greg 2.1
63 greg 2.5 /* processed incident DSF measurements */
64 greg 2.7 static RBFLIST *dsf_list = NULL;
65    
66 greg 2.8 /* RBF-linking matrices (edges) */
67 greg 2.7 static MIGRATION *mig_list = NULL;
68    
69 greg 2.8 #define mtx_nrows(m) ((m)->rbfv[0]->nrbf)
70     #define mtx_ncols(m) ((m)->rbfv[1]->nrbf)
71     #define mtx_ndx(m,i,j) ((i)*mtx_ncols(m) + (j))
72     #define is_src(rbf,m) ((rbf) == (m)->rbfv[0])
73     #define is_dest(rbf,m) ((rbf) == (m)->rbfv[1])
74     #define nextedge(rbf,m) (m)->enxt[is_dest(rbf,m)]
75    
76     /* Compute volume associated with Gaussian lobe */
77     static double
78     rbf_volume(const RBFVAL *rbfp)
79     {
80     double rad = R2ANG(rbfp->crad);
81    
82     return((2.*M_PI) * rbfp->peak * rad*rad);
83     }
84 greg 2.1
85 greg 2.3 /* Compute outgoing vector from grid position */
86     static void
87     vec_from_pos(FVECT vec, int xpos, int ypos)
88 greg 2.1 {
89 greg 2.3 double uv[2];
90     double r2;
91    
92     SDsquare2disk(uv, (1./GRIDRES)*(xpos+.5), (1./GRIDRES)*(ypos+.5));
93     /* uniform hemispherical projection */
94     r2 = uv[0]*uv[0] + uv[1]*uv[1];
95     vec[0] = vec[1] = sqrt(2. - r2);
96     vec[0] *= uv[0];
97     vec[1] *= uv[1];
98     vec[2] = 1. - r2;
99 greg 2.1 }
100    
101     /* Compute grid position from normalized outgoing vector */
102     static void
103     pos_from_vec(int pos[2], const FVECT vec)
104     {
105     double sq[2]; /* uniform hemispherical projection */
106     double norm = 1./sqrt(1. + vec[2]);
107    
108     SDdisk2square(sq, vec[0]*norm, vec[1]*norm);
109    
110     pos[0] = (int)(sq[0]*GRIDRES);
111     pos[1] = (int)(sq[1]*GRIDRES);
112     }
113    
114 greg 2.5 /* Evaluate RBF for DSF at the given normalized outgoing direction */
115 greg 2.1 static double
116     eval_rbfrep(const RBFLIST *rp, const FVECT outvec)
117     {
118     double res = .0;
119     const RBFVAL *rbfp;
120     FVECT odir;
121     double sig2;
122     int n;
123    
124     rbfp = rp->rbfa;
125     for (n = rp->nrbf; n--; rbfp++) {
126     vec_from_pos(odir, rbfp->gx, rbfp->gy);
127 greg 2.2 sig2 = R2ANG(rbfp->crad);
128     sig2 = (DOT(odir,outvec) - 1.) / (sig2*sig2);
129 greg 2.1 if (sig2 > -19.)
130 greg 2.5 res += rbfp->peak * exp(sig2);
131 greg 2.1 }
132     return(res);
133     }
134    
135 greg 2.3 /* Count up filled nodes and build RBF representation from current grid */
136     static RBFLIST *
137     make_rbfrep(void)
138     {
139 greg 2.6 int niter = 16;
140     double lastVar, thisVar = 100.;
141 greg 2.3 int nn;
142     RBFLIST *newnode;
143     int i, j;
144    
145     nn = 0; /* count selected bins */
146     for (i = 0; i < GRIDRES; i++)
147     for (j = 0; j < GRIDRES; j++)
148 greg 2.6 nn += dsf_grid[i][j].nval;
149 greg 2.3 /* allocate RBF array */
150     newnode = (RBFLIST *)malloc(sizeof(RBFLIST) + sizeof(RBFVAL)*(nn-1));
151     if (newnode == NULL) {
152 greg 2.8 fputs("Out of memory in make_rbfrep()\n", stderr);
153 greg 2.3 exit(1);
154     }
155     newnode->next = NULL;
156 greg 2.7 newnode->ejl = NULL;
157 greg 2.3 newnode->invec[2] = sin(M_PI/180.*theta_in_deg);
158     newnode->invec[0] = cos(M_PI/180.*phi_in_deg)*newnode->invec[2];
159     newnode->invec[1] = sin(M_PI/180.*phi_in_deg)*newnode->invec[2];
160     newnode->invec[2] = sqrt(1. - newnode->invec[2]*newnode->invec[2]);
161 greg 2.8 newnode->vtotal = 0;
162 greg 2.3 newnode->nrbf = nn;
163     nn = 0; /* fill RBF array */
164     for (i = 0; i < GRIDRES; i++)
165     for (j = 0; j < GRIDRES; j++)
166 greg 2.5 if (dsf_grid[i][j].nval) {
167 greg 2.6 newnode->rbfa[nn].peak = dsf_grid[i][j].vsum;
168 greg 2.5 newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5;
169 greg 2.3 newnode->rbfa[nn].gx = i;
170     newnode->rbfa[nn].gy = j;
171     ++nn;
172     }
173 greg 2.6 /* iterate to improve interpolation accuracy */
174     do {
175 greg 2.4 double dsum = .0, dsum2 = .0;
176 greg 2.3 nn = 0;
177     for (i = 0; i < GRIDRES; i++)
178     for (j = 0; j < GRIDRES; j++)
179 greg 2.5 if (dsf_grid[i][j].nval) {
180 greg 2.3 FVECT odir;
181 greg 2.6 double corr;
182 greg 2.3 vec_from_pos(odir, i, j);
183 greg 2.6 newnode->rbfa[nn++].peak *= corr =
184 greg 2.5 dsf_grid[i][j].vsum /
185 greg 2.3 eval_rbfrep(newnode, odir);
186 greg 2.4 dsum += corr - 1.;
187     dsum2 += (corr-1.)*(corr-1.);
188 greg 2.3 }
189 greg 2.6 lastVar = thisVar;
190     thisVar = dsum2/(double)nn;
191 greg 2.4 /*
192     fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n",
193     100.*dsum/(double)nn,
194 greg 2.6 100.*sqrt(thisVar));
195 greg 2.4 */
196 greg 2.6 } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar);
197    
198 greg 2.8 nn = 0; /* compute sum for normalization */
199     while (nn < newnode->nrbf)
200     newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
201    
202 greg 2.5 newnode->next = dsf_list;
203     return(dsf_list = newnode);
204 greg 2.3 }
205    
206 greg 2.1 /* Load a set of measurements corresponding to a particular incident angle */
207     static int
208     load_bsdf_meas(const char *fname)
209     {
210     FILE *fp = fopen(fname, "r");
211     int inp_is_DSF = -1;
212     double theta_out, phi_out, val;
213     char buf[2048];
214     int n, c;
215    
216     if (fp == NULL) {
217     fputs(fname, stderr);
218     fputs(": cannot open\n", stderr);
219     return(0);
220     }
221 greg 2.5 memset(dsf_grid, 0, sizeof(dsf_grid));
222 greg 2.1 /* read header information */
223     while ((c = getc(fp)) == '#' || c == EOF) {
224     if (fgets(buf, sizeof(buf), fp) == NULL) {
225     fputs(fname, stderr);
226     fputs(": unexpected EOF\n", stderr);
227     fclose(fp);
228     return(0);
229     }
230     if (!strcmp(buf, "format: theta phi DSF\n")) {
231     inp_is_DSF = 1;
232     continue;
233     }
234     if (!strcmp(buf, "format: theta phi BSDF\n")) {
235     inp_is_DSF = 0;
236     continue;
237     }
238     if (sscanf(buf, "intheta %lf", &theta_in_deg) == 1)
239     continue;
240     if (sscanf(buf, "inphi %lf", &phi_in_deg) == 1)
241     continue;
242     if (sscanf(buf, "incident_angle %lf %lf",
243     &theta_in_deg, &phi_in_deg) == 2)
244     continue;
245     }
246     if (inp_is_DSF < 0) {
247     fputs(fname, stderr);
248     fputs(": unknown format\n", stderr);
249     fclose(fp);
250     return(0);
251     }
252     ungetc(c, fp); /* read actual data */
253     while (fscanf(fp, "%lf %lf %lf\n", &theta_out, &phi_out, &val) == 3) {
254     FVECT ovec;
255     int pos[2];
256    
257     ovec[2] = sin(M_PI/180.*theta_out);
258     ovec[0] = cos(M_PI/180.*phi_out) * ovec[2];
259     ovec[1] = sin(M_PI/180.*phi_out) * ovec[2];
260     ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
261    
262 greg 2.5 if (!inp_is_DSF)
263     val *= ovec[2]; /* convert from BSDF to DSF */
264 greg 2.1
265     pos_from_vec(pos, ovec);
266    
267 greg 2.5 dsf_grid[pos[0]][pos[1]].vsum += val;
268     dsf_grid[pos[0]][pos[1]].nval++;
269 greg 2.1 }
270     n = 0;
271     while ((c = getc(fp)) != EOF)
272     n += !isspace(c);
273     if (n)
274     fprintf(stderr,
275     "%s: warning: %d unexpected characters past EOD\n",
276     fname, n);
277     fclose(fp);
278     return(1);
279     }
280    
281     /* Compute radii for non-empty bins */
282     /* (distance to furthest empty bin for which non-empty bin is the closest) */
283     static void
284     compute_radii(void)
285     {
286 greg 2.4 unsigned int fill_grid[GRIDRES][GRIDRES];
287     unsigned short fill_cnt[GRIDRES][GRIDRES];
288 greg 2.2 FVECT ovec0, ovec1;
289     double ang2, lastang2;
290     int r, i, j, jn, ii, jj, inear, jnear;
291    
292     r = GRIDRES/2; /* proceed in zig-zag */
293 greg 2.1 for (i = 0; i < GRIDRES; i++)
294     for (jn = 0; jn < GRIDRES; jn++) {
295     j = (i&1) ? jn : GRIDRES-1-jn;
296 greg 2.5 if (dsf_grid[i][j].nval) /* find empty grid pos. */
297 greg 2.1 continue;
298 greg 2.2 vec_from_pos(ovec0, i, j);
299 greg 2.1 inear = jnear = -1; /* find nearest non-empty */
300 greg 2.2 lastang2 = M_PI*M_PI;
301 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
302     if (ii < 0) continue;
303     if (ii >= GRIDRES) break;
304     for (jj = j-r; jj <= j+r; jj++) {
305     if (jj < 0) continue;
306     if (jj >= GRIDRES) break;
307 greg 2.5 if (!dsf_grid[ii][jj].nval)
308 greg 2.1 continue;
309 greg 2.2 vec_from_pos(ovec1, ii, jj);
310     ang2 = 2. - 2.*DOT(ovec0,ovec1);
311     if (ang2 >= lastang2)
312 greg 2.1 continue;
313 greg 2.2 lastang2 = ang2;
314 greg 2.1 inear = ii; jnear = jj;
315     }
316     }
317 greg 2.2 if (inear < 0) {
318     fputs("Could not find non-empty neighbor!\n", stderr);
319     exit(1);
320     }
321     ang2 = sqrt(lastang2);
322     r = ANG2R(ang2); /* record if > previous */
323 greg 2.5 if (r > dsf_grid[inear][jnear].crad)
324     dsf_grid[inear][jnear].crad = r;
325 greg 2.2 /* next search radius */
326     r = ang2*(2.*GRIDRES/M_PI) + 1;
327 greg 2.1 }
328 greg 2.4 /* blur radii over hemisphere */
329 greg 2.1 memset(fill_grid, 0, sizeof(fill_grid));
330 greg 2.4 memset(fill_cnt, 0, sizeof(fill_cnt));
331 greg 2.1 for (i = 0; i < GRIDRES; i++)
332     for (j = 0; j < GRIDRES; j++) {
333 greg 2.5 if (!dsf_grid[i][j].crad)
334 greg 2.4 continue; /* missing distance */
335 greg 2.5 r = R2ANG(dsf_grid[i][j].crad)*(2.*RSCA*GRIDRES/M_PI);
336 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
337     if (ii < 0) continue;
338     if (ii >= GRIDRES) break;
339     for (jj = j-r; jj <= j+r; jj++) {
340     if (jj < 0) continue;
341     if (jj >= GRIDRES) break;
342 greg 2.4 if ((ii-i)*(ii-i) + (jj-j)*(jj-j) > r*r)
343 greg 2.1 continue;
344 greg 2.5 fill_grid[ii][jj] += dsf_grid[i][j].crad;
345 greg 2.4 fill_cnt[ii][jj]++;
346 greg 2.1 }
347     }
348     }
349 greg 2.6 /* copy back blurred radii */
350 greg 2.1 for (i = 0; i < GRIDRES; i++)
351     for (j = 0; j < GRIDRES; j++)
352 greg 2.4 if (fill_cnt[i][j])
353 greg 2.5 dsf_grid[i][j].crad = fill_grid[i][j]/fill_cnt[i][j];
354 greg 2.1 }
355    
356 greg 2.6 /* Cull points for more uniform distribution, leave all nval 0 or 1 */
357 greg 2.1 static void
358     cull_values(void)
359     {
360 greg 2.2 FVECT ovec0, ovec1;
361     double maxang, maxang2;
362     int i, j, ii, jj, r;
363 greg 2.1 /* simple greedy algorithm */
364     for (i = 0; i < GRIDRES; i++)
365     for (j = 0; j < GRIDRES; j++) {
366 greg 2.5 if (!dsf_grid[i][j].nval)
367 greg 2.1 continue;
368 greg 2.5 if (!dsf_grid[i][j].crad)
369 greg 2.2 continue; /* shouldn't happen */
370     vec_from_pos(ovec0, i, j);
371 greg 2.5 maxang = 2.*R2ANG(dsf_grid[i][j].crad);
372 greg 2.2 if (maxang > ovec0[2]) /* clamp near horizon */
373     maxang = ovec0[2];
374     r = maxang*(2.*GRIDRES/M_PI) + 1;
375     maxang2 = maxang*maxang;
376 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
377     if (ii < 0) continue;
378     if (ii >= GRIDRES) break;
379     for (jj = j-r; jj <= j+r; jj++) {
380     if (jj < 0) continue;
381     if (jj >= GRIDRES) break;
382 greg 2.5 if (!dsf_grid[ii][jj].nval)
383 greg 2.1 continue;
384 greg 2.2 if ((ii == i) & (jj == j))
385     continue; /* don't get self-absorbed */
386     vec_from_pos(ovec1, ii, jj);
387     if (2. - 2.*DOT(ovec0,ovec1) >= maxang2)
388 greg 2.1 continue;
389 greg 2.2 /* absorb sum */
390 greg 2.5 dsf_grid[i][j].vsum += dsf_grid[ii][jj].vsum;
391     dsf_grid[i][j].nval += dsf_grid[ii][jj].nval;
392 greg 2.2 /* keep value, though */
393 greg 2.6 dsf_grid[ii][jj].vsum /= (float)dsf_grid[ii][jj].nval;
394 greg 2.5 dsf_grid[ii][jj].nval = 0;
395 greg 2.1 }
396     }
397     }
398 greg 2.6 /* final averaging pass */
399     for (i = 0; i < GRIDRES; i++)
400     for (j = 0; j < GRIDRES; j++)
401     if (dsf_grid[i][j].nval > 1) {
402     dsf_grid[i][j].vsum /= (float)dsf_grid[i][j].nval;
403     dsf_grid[i][j].nval = 1;
404     }
405 greg 2.1 }
406    
407 greg 2.8 /* Compute (and allocate) migration price matrix for optimization */
408     static float *
409     price_routes(const RBFLIST *from_rbf, const RBFLIST *to_rbf)
410     {
411     float *pmtx = (float *)malloc(sizeof(float) *
412     from_rbf->nrbf * to_rbf->nrbf);
413     FVECT *vto = (FVECT *)malloc(sizeof(FVECT) * to_rbf->nrbf);
414     int i, j;
415    
416     if ((pmtx == NULL) | (vto == NULL)) {
417     fputs("Out of memory in migration_costs()\n", stderr);
418     exit(1);
419     }
420     for (j = to_rbf->nrbf; j--; ) /* save repetitive ops. */
421     vec_from_pos(vto[j], to_rbf->rbfa[j].gx, to_rbf->rbfa[j].gy);
422    
423     for (i = from_rbf->nrbf; i--; ) {
424     const double from_ang = R2ANG(from_rbf->rbfa[i].crad);
425     FVECT vfrom;
426     vec_from_pos(vfrom, from_rbf->rbfa[i].gx, from_rbf->rbfa[i].gy);
427     for (j = to_rbf->nrbf; j--; )
428     pmtx[i*to_rbf->nrbf + j] = acos(DOT(vfrom, vto[j])) +
429     fabs(R2ANG(to_rbf->rbfa[j].crad) - from_ang);
430     }
431     free(vto);
432     return(pmtx);
433     }
434    
435     /* Comparison routine needed for sorting price row */
436     static const float *price_arr;
437     static int
438     msrt_cmp(const void *p1, const void *p2)
439     {
440     float c1 = price_arr[*(const int *)p1];
441     float c2 = price_arr[*(const int *)p2];
442    
443     if (c1 > c2) return(1);
444     if (c1 < c2) return(-1);
445     return(0);
446     }
447    
448     /* Compute minimum (optimistic) cost for moving the given source material */
449     static double
450     min_cost(double amt2move, const double *avail, const float *price, int n)
451     {
452     static int *price_sort = NULL;
453     static int n_alloc = 0;
454     double total_cost = 0;
455     int i;
456    
457     if (amt2move <= FTINY) /* pre-emptive check */
458     return(0.);
459     if (n > n_alloc) { /* (re)allocate sort array */
460     if (n_alloc) free(price_sort);
461     price_sort = (int *)malloc(sizeof(int)*n);
462     if (price_sort == NULL) {
463     fputs("Out of memory in min_cost()\n", stderr);
464     exit(1);
465     }
466     n_alloc = n;
467     }
468     for (i = n; i--; )
469     price_sort[i] = i;
470     price_arr = price;
471     qsort(price_sort, n, sizeof(int), &msrt_cmp);
472     /* move cheapest first */
473     for (i = 0; i < n && amt2move > FTINY; i++) {
474     int d = price_sort[i];
475     double amt = (amt2move < avail[d]) ? amt2move : avail[d];
476    
477     total_cost += amt * price[d];
478     amt2move -= amt;
479     }
480     if (amt2move > 1e-5) fprintf(stderr, "%g leftover!\n", amt2move);
481     return(total_cost);
482     }
483    
484     /* Take a step in migration by choosing optimal bucket to transfer */
485     static double
486     migration_step(MIGRATION *mig, double *src_rem, double *dst_rem, const float *pmtx)
487     {
488     static double *src_cost = NULL;
489     int n_alloc = 0;
490 greg 2.9 const double maxamt = 2./(mtx_nrows(mig)*mtx_ncols(mig));
491 greg 2.8 double amt = 0;
492     struct {
493     int s, d; /* source and destination */
494     double price; /* price estimate per amount moved */
495     double amt; /* amount we can move */
496     } cur, best;
497     int i;
498    
499     if (mtx_nrows(mig) > n_alloc) { /* allocate cost array */
500     if (n_alloc)
501     free(src_cost);
502     src_cost = (double *)malloc(sizeof(double)*mtx_nrows(mig));
503     if (src_cost == NULL) {
504     fputs("Out of memory in migration_step()\n", stderr);
505     exit(1);
506     }
507     n_alloc = mtx_nrows(mig);
508     }
509     for (i = mtx_nrows(mig); i--; ) /* starting costs for diff. */
510     src_cost[i] = min_cost(src_rem[i], dst_rem,
511     pmtx+i*mtx_ncols(mig), mtx_ncols(mig));
512    
513     /* find best source & dest. */
514     best.s = best.d = -1; best.price = FHUGE; best.amt = 0;
515     for (cur.s = mtx_nrows(mig); cur.s--; ) {
516     const float *price = pmtx + cur.s*mtx_ncols(mig);
517     double cost_others = 0;
518     if (src_rem[cur.s] <= FTINY)
519     continue;
520     cur.d = -1; /* examine cheapest dest. */
521     for (i = mtx_ncols(mig); i--; )
522     if (dst_rem[i] > FTINY &&
523     (cur.d < 0 || price[i] < price[cur.d]))
524     cur.d = i;
525     if (cur.d < 0)
526     return(.0);
527     if ((cur.price = price[cur.d]) >= best.price)
528     continue; /* no point checking further */
529     cur.amt = (src_rem[cur.s] < dst_rem[cur.d]) ?
530     src_rem[cur.s] : dst_rem[cur.d];
531     if (cur.amt > maxamt) cur.amt = maxamt;
532     dst_rem[cur.d] -= cur.amt; /* add up differential costs */
533     for (i = mtx_nrows(mig); i--; ) {
534     if (i == cur.s) continue;
535     cost_others += min_cost(src_rem[i], dst_rem, price, mtx_ncols(mig))
536     - src_cost[i];
537     }
538     dst_rem[cur.d] += cur.amt; /* undo trial move */
539     cur.price += cost_others/cur.amt; /* adjust effective price */
540     if (cur.price < best.price) /* are we better than best? */
541     best = cur;
542     }
543     if ((best.s < 0) | (best.d < 0))
544     return(.0);
545     /* make the actual move */
546     mig->mtx[mtx_ndx(mig,best.s,best.d)] += best.amt;
547     src_rem[best.s] -= best.amt;
548     dst_rem[best.d] -= best.amt;
549     return(best.amt);
550     }
551    
552     /* Compute (and insert) migration along directed edge */
553     static MIGRATION *
554     make_migration(RBFLIST *from_rbf, RBFLIST *to_rbf)
555     {
556     const double end_thresh = 0.02/(from_rbf->nrbf*to_rbf->nrbf);
557     float *pmtx = price_routes(from_rbf, to_rbf);
558     MIGRATION *newmig = (MIGRATION *)malloc(sizeof(MIGRATION) +
559     sizeof(float) *
560     (from_rbf->nrbf*to_rbf->nrbf - 1));
561     double *src_rem = (double *)malloc(sizeof(double)*from_rbf->nrbf);
562     double *dst_rem = (double *)malloc(sizeof(double)*to_rbf->nrbf);
563     double total_rem = 1.;
564     int i;
565    
566     if ((newmig == NULL) | (src_rem == NULL) | (dst_rem == NULL)) {
567     fputs("Out of memory in make_migration()\n", stderr);
568     exit(1);
569     }
570     newmig->next = NULL;
571     newmig->rbfv[0] = from_rbf;
572     newmig->rbfv[1] = to_rbf;
573     newmig->enxt[0] = newmig->enxt[1] = NULL;
574     memset(newmig->mtx, 0, sizeof(float)*from_rbf->nrbf*to_rbf->nrbf);
575     /* starting quantities */
576     for (i = from_rbf->nrbf; i--; )
577     src_rem[i] = rbf_volume(&from_rbf->rbfa[i]) / from_rbf->vtotal;
578     for (i = to_rbf->nrbf; i--; )
579     dst_rem[i] = rbf_volume(&to_rbf->rbfa[i]) / to_rbf->vtotal;
580     /* move a bit at a time */
581     while (total_rem > end_thresh)
582     total_rem -= migration_step(newmig, src_rem, dst_rem, pmtx);
583    
584     free(pmtx); /* free working arrays */
585     free(src_rem);
586     free(dst_rem);
587     for (i = from_rbf->nrbf; i--; ) { /* normalize final matrix */
588     float nf = rbf_volume(&from_rbf->rbfa[i]);
589     int j;
590     if (nf <= FTINY) continue;
591     nf = from_rbf->vtotal / nf;
592     for (j = to_rbf->nrbf; j--; )
593     newmig->mtx[mtx_ndx(newmig,i,j)] *= nf;
594     }
595     /* insert in edge lists */
596     newmig->enxt[0] = from_rbf->ejl;
597     from_rbf->ejl = newmig;
598     newmig->enxt[1] = to_rbf->ejl;
599     to_rbf->ejl = newmig;
600     newmig->next = mig_list;
601     return(mig_list = newmig);
602     }
603    
604     #if 0
605     /* Partially advect between the given RBFs to a newly allocated one */
606     static RBFLIST *
607     advect_rbf(const RBFLIST *from_rbf, const RBFLIST *to_rbf,
608     const float *mtx, const FVECT invec)
609     {
610     RBFLIST *rbf;
611    
612     if (from_rbf->nrbf > to_rbf->nrbf) {
613     fputs("Internal error: source RBF won't fit destination\n",
614     stderr);
615     exit(1);
616     }
617     rbf = (RBFLIST *)malloc(sizeof(RBFLIST) + sizeof(RBFVAL)*(to_rbf->nrbf-1));
618     if (rbf == NULL) {
619     fputs("Out of memory in advect_rbf()\n", stderr);
620     exit(1);
621     }
622     rbf->next = NULL; rbf->ejl = NULL;
623     VCOPY(rbf->invec, invec);
624     rbf->vtotal = 0;
625     rbf->nrbf = to_rbf->nrbf;
626    
627     return rbf;
628     }
629     #endif
630 greg 2.1
631     #if 1
632     /* Test main produces a Radiance model from the given input file */
633     int
634     main(int argc, char *argv[])
635     {
636     char buf[128];
637     FILE *pfp;
638     double bsdf;
639     FVECT dir;
640     int i, j, n;
641    
642     if (argc != 2) {
643     fprintf(stderr, "Usage: %s input.dat > output.rad\n", argv[0]);
644     return(1);
645     }
646     if (!load_bsdf_meas(argv[1]))
647     return(1);
648    
649     compute_radii();
650     cull_values();
651 greg 2.3 make_rbfrep();
652     /* produce spheres at meas. */
653     puts("void plastic yellow\n0\n0\n5 .6 .4 .01 .04 .08\n");
654 greg 2.1 puts("void plastic pink\n0\n0\n5 .5 .05 .9 .04 .08\n");
655     n = 0;
656     for (i = 0; i < GRIDRES; i++)
657     for (j = 0; j < GRIDRES; j++)
658 greg 2.5 if (dsf_grid[i][j].vsum > .0f) {
659 greg 2.1 vec_from_pos(dir, i, j);
660 greg 2.5 bsdf = dsf_grid[i][j].vsum / dir[2];
661     if (dsf_grid[i][j].nval) {
662 greg 2.3 printf("pink cone c%04d\n0\n0\n8\n", ++n);
663     printf("\t%.6g %.6g %.6g\n",
664 greg 2.1 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
665 greg 2.3 printf("\t%.6g %.6g %.6g\n",
666 greg 2.1 dir[0]*(bsdf+.005), dir[1]*(bsdf+.005),
667     dir[2]*(bsdf+.005));
668 greg 2.3 puts("\t.003\t0\n");
669     } else {
670     vec_from_pos(dir, i, j);
671     printf("yellow sphere s%04d\n0\n0\n", ++n);
672     printf("4 %.6g %.6g %.6g .0015\n\n",
673     dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
674     }
675 greg 2.1 }
676     /* output continuous surface */
677     puts("void trans tgreen\n0\n0\n7 .7 1 .7 .04 .04 .9 .9\n");
678     fflush(stdout);
679 greg 2.5 sprintf(buf, "gensurf tgreen bsdf - - - %d %d", GRIDRES-1, GRIDRES-1);
680 greg 2.1 pfp = popen(buf, "w");
681     if (pfp == NULL) {
682     fputs(buf, stderr);
683     fputs(": cannot start command\n", stderr);
684     return(1);
685     }
686     for (i = 0; i < GRIDRES; i++)
687     for (j = 0; j < GRIDRES; j++) {
688     vec_from_pos(dir, i, j);
689 greg 2.5 bsdf = eval_rbfrep(dsf_list, dir) / dir[2];
690 greg 2.1 fprintf(pfp, "%.8e %.8e %.8e\n",
691     dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
692     }
693     return(pclose(pfp)==0 ? 0 : 1);
694     }
695     #endif