ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
Revision: 2.14
Committed: Sat Sep 22 23:10:24 2012 UTC (11 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.13: +77 -48 lines
Log Message:
Fixed most problems with Delaunay triangulation on sphere

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.14 static const char RCSid[] = "$Id: pabopto2xml.c,v 2.13 2012/09/21 05:17:22 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 greg 2.10 #define DEBUG 1
20    
21 greg 2.1 #ifndef GRIDRES
22 greg 2.10 #define GRIDRES 200 /* grid resolution per side */
23 greg 2.1 #endif
24    
25 greg 2.3 #define RSCA 2.7 /* radius scaling factor (empirical) */
26 greg 2.2
27 greg 2.6 /* convert to/from coded radians */
28     #define ANG2R(r) (int)((r)*((1<<16)/M_PI))
29 greg 2.2 #define R2ANG(c) (((c)+.5)*(M_PI/(1<<16)))
30 greg 2.1
31     typedef struct {
32 greg 2.5 float vsum; /* DSF sum */
33 greg 2.1 unsigned short nval; /* number of values in sum */
34 greg 2.2 unsigned short crad; /* radius (coded angle) */
35 greg 2.1 } GRIDVAL; /* grid value */
36    
37     typedef struct {
38 greg 2.5 float peak; /* lobe value at peak */
39 greg 2.2 unsigned short crad; /* radius (coded angle) */
40 greg 2.1 unsigned char gx, gy; /* grid position */
41     } RBFVAL; /* radial basis function value */
42    
43 greg 2.7 struct s_rbfnode; /* forward declaration of RBF struct */
44    
45     typedef struct s_migration {
46     struct s_migration *next; /* next in global edge list */
47     struct s_rbfnode *rbfv[2]; /* from,to vertex */
48     struct s_migration *enxt[2]; /* next from,to sibling */
49     float mtx[1]; /* matrix (extends struct) */
50     } MIGRATION; /* migration link (winged edge structure) */
51    
52     typedef struct s_rbfnode {
53     struct s_rbfnode *next; /* next in global RBF list */
54     MIGRATION *ejl; /* edge list for this vertex */
55 greg 2.1 FVECT invec; /* incident vector direction */
56 greg 2.8 double vtotal; /* volume for normalization */
57 greg 2.1 int nrbf; /* number of RBFs */
58     RBFVAL rbfa[1]; /* RBF array (extends struct) */
59 greg 2.10 } RBFNODE; /* RBF representation of DSF @ 1 incidence */
60 greg 2.1
61     /* our loaded grid for this incident angle */
62 greg 2.10 static double theta_in_deg, phi_in_deg;
63     static GRIDVAL dsf_grid[GRIDRES][GRIDRES];
64    
65     /* all incident angles in-plane so far? */
66     static int single_plane_incident = -1;
67    
68     /* input/output orientations */
69     static int input_orient = 0;
70     static int output_orient = 0;
71 greg 2.1
72 greg 2.5 /* processed incident DSF measurements */
73 greg 2.10 static RBFNODE *dsf_list = NULL;
74 greg 2.7
75 greg 2.8 /* RBF-linking matrices (edges) */
76 greg 2.7 static MIGRATION *mig_list = NULL;
77    
78 greg 2.10 /* migration edges drawn in raster fashion */
79     static MIGRATION *mig_grid[GRIDRES][GRIDRES];
80    
81 greg 2.8 #define mtx_nrows(m) ((m)->rbfv[0]->nrbf)
82     #define mtx_ncols(m) ((m)->rbfv[1]->nrbf)
83     #define mtx_ndx(m,i,j) ((i)*mtx_ncols(m) + (j))
84     #define is_src(rbf,m) ((rbf) == (m)->rbfv[0])
85     #define is_dest(rbf,m) ((rbf) == (m)->rbfv[1])
86     #define nextedge(rbf,m) (m)->enxt[is_dest(rbf,m)]
87 greg 2.10 #define opp_rbf(rbf,m) (m)->rbfv[is_src(rbf,m)]
88    
89     #define round(v) (int)((v) + .5 - ((v) < -.5))
90 greg 2.8
91 greg 2.12 char *progname;
92 greg 2.13
93     #ifdef DEBUG /* percentage to cull (<0 to turn off) */
94     int pctcull = -1;
95     #else
96 greg 2.12 int pctcull = 90;
97 greg 2.13 #endif
98     /* sampling order (set by data density) */
99 greg 2.12 int samp_order = 0;
100    
101 greg 2.8 /* Compute volume associated with Gaussian lobe */
102     static double
103     rbf_volume(const RBFVAL *rbfp)
104     {
105     double rad = R2ANG(rbfp->crad);
106    
107     return((2.*M_PI) * rbfp->peak * rad*rad);
108     }
109 greg 2.1
110 greg 2.3 /* Compute outgoing vector from grid position */
111     static void
112 greg 2.10 ovec_from_pos(FVECT vec, int xpos, int ypos)
113 greg 2.1 {
114 greg 2.3 double uv[2];
115     double r2;
116    
117     SDsquare2disk(uv, (1./GRIDRES)*(xpos+.5), (1./GRIDRES)*(ypos+.5));
118     /* uniform hemispherical projection */
119     r2 = uv[0]*uv[0] + uv[1]*uv[1];
120     vec[0] = vec[1] = sqrt(2. - r2);
121     vec[0] *= uv[0];
122     vec[1] *= uv[1];
123 greg 2.10 vec[2] = output_orient*(1. - r2);
124 greg 2.1 }
125    
126 greg 2.10 /* Compute grid position from normalized input/output vector */
127 greg 2.1 static void
128     pos_from_vec(int pos[2], const FVECT vec)
129     {
130     double sq[2]; /* uniform hemispherical projection */
131 greg 2.10 double norm = 1./sqrt(1. + fabs(vec[2]));
132 greg 2.1
133     SDdisk2square(sq, vec[0]*norm, vec[1]*norm);
134    
135     pos[0] = (int)(sq[0]*GRIDRES);
136     pos[1] = (int)(sq[1]*GRIDRES);
137     }
138    
139 greg 2.5 /* Evaluate RBF for DSF at the given normalized outgoing direction */
140 greg 2.1 static double
141 greg 2.10 eval_rbfrep(const RBFNODE *rp, const FVECT outvec)
142 greg 2.1 {
143     double res = .0;
144     const RBFVAL *rbfp;
145     FVECT odir;
146     double sig2;
147     int n;
148    
149 greg 2.12 if (rp == NULL)
150     return(.0);
151 greg 2.1 rbfp = rp->rbfa;
152     for (n = rp->nrbf; n--; rbfp++) {
153 greg 2.10 ovec_from_pos(odir, rbfp->gx, rbfp->gy);
154 greg 2.2 sig2 = R2ANG(rbfp->crad);
155     sig2 = (DOT(odir,outvec) - 1.) / (sig2*sig2);
156 greg 2.1 if (sig2 > -19.)
157 greg 2.5 res += rbfp->peak * exp(sig2);
158 greg 2.1 }
159     return(res);
160     }
161    
162 greg 2.10 /* Insert a new directional scattering function in our global list */
163     static void
164     insert_dsf(RBFNODE *newrbf)
165     {
166     RBFNODE *rbf, *rbf_last;
167 greg 2.14 /* check for redundant meas. */
168     for (rbf = dsf_list; rbf != NULL; rbf = rbf->next)
169     if (DOT(rbf->invec, newrbf->invec) >= 1.-FTINY) {
170     fputs("Duplicate incident measurement (ignored)\n", stderr);
171     free(newrbf);
172     return;
173     }
174 greg 2.10 /* keep in ascending theta order */
175     for (rbf_last = NULL, rbf = dsf_list;
176     single_plane_incident & (rbf != NULL);
177     rbf_last = rbf, rbf = rbf->next)
178     if (input_orient*rbf->invec[2] < input_orient*newrbf->invec[2])
179     break;
180     if (rbf_last == NULL) {
181     newrbf->next = dsf_list;
182     dsf_list = newrbf;
183     return;
184     }
185     newrbf->next = rbf;
186     rbf_last->next = newrbf;
187     }
188    
189 greg 2.3 /* Count up filled nodes and build RBF representation from current grid */
190 greg 2.10 static RBFNODE *
191 greg 2.3 make_rbfrep(void)
192     {
193 greg 2.6 int niter = 16;
194 greg 2.12 int minrad = ANG2R(pow(2., 1.-samp_order));
195 greg 2.6 double lastVar, thisVar = 100.;
196 greg 2.3 int nn;
197 greg 2.10 RBFNODE *newnode;
198 greg 2.3 int i, j;
199    
200     nn = 0; /* count selected bins */
201     for (i = 0; i < GRIDRES; i++)
202     for (j = 0; j < GRIDRES; j++)
203 greg 2.6 nn += dsf_grid[i][j].nval;
204 greg 2.3 /* allocate RBF array */
205 greg 2.10 newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
206 greg 2.3 if (newnode == NULL) {
207 greg 2.8 fputs("Out of memory in make_rbfrep()\n", stderr);
208 greg 2.3 exit(1);
209     }
210     newnode->next = NULL;
211 greg 2.7 newnode->ejl = NULL;
212 greg 2.3 newnode->invec[2] = sin(M_PI/180.*theta_in_deg);
213     newnode->invec[0] = cos(M_PI/180.*phi_in_deg)*newnode->invec[2];
214     newnode->invec[1] = sin(M_PI/180.*phi_in_deg)*newnode->invec[2];
215 greg 2.10 newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
216 greg 2.8 newnode->vtotal = 0;
217 greg 2.3 newnode->nrbf = nn;
218     nn = 0; /* fill RBF array */
219     for (i = 0; i < GRIDRES; i++)
220     for (j = 0; j < GRIDRES; j++)
221 greg 2.5 if (dsf_grid[i][j].nval) {
222 greg 2.6 newnode->rbfa[nn].peak = dsf_grid[i][j].vsum;
223 greg 2.5 newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5;
224 greg 2.3 newnode->rbfa[nn].gx = i;
225     newnode->rbfa[nn].gy = j;
226 greg 2.12 if (newnode->rbfa[nn].crad < minrad)
227     minrad = newnode->rbfa[nn].crad;
228 greg 2.3 ++nn;
229     }
230 greg 2.6 /* iterate to improve interpolation accuracy */
231     do {
232 greg 2.13 double dsum = 0, dsum2 = 0;
233 greg 2.3 nn = 0;
234     for (i = 0; i < GRIDRES; i++)
235     for (j = 0; j < GRIDRES; j++)
236 greg 2.5 if (dsf_grid[i][j].nval) {
237 greg 2.3 FVECT odir;
238 greg 2.6 double corr;
239 greg 2.10 ovec_from_pos(odir, i, j);
240 greg 2.6 newnode->rbfa[nn++].peak *= corr =
241 greg 2.5 dsf_grid[i][j].vsum /
242 greg 2.3 eval_rbfrep(newnode, odir);
243 greg 2.4 dsum += corr - 1.;
244     dsum2 += (corr-1.)*(corr-1.);
245 greg 2.3 }
246 greg 2.6 lastVar = thisVar;
247     thisVar = dsum2/(double)nn;
248 greg 2.10 #ifdef DEBUG
249 greg 2.4 fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n",
250     100.*dsum/(double)nn,
251 greg 2.6 100.*sqrt(thisVar));
252 greg 2.10 #endif
253 greg 2.6 } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar);
254    
255 greg 2.8 nn = 0; /* compute sum for normalization */
256     while (nn < newnode->nrbf)
257     newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
258    
259 greg 2.10 insert_dsf(newnode);
260 greg 2.12 /* adjust sampling resolution */
261 greg 2.13 samp_order = log(2./R2ANG(minrad))/M_LN2 + .5;
262 greg 2.12
263 greg 2.10 return(newnode);
264 greg 2.3 }
265    
266 greg 2.1 /* Load a set of measurements corresponding to a particular incident angle */
267     static int
268 greg 2.10 load_pabopto_meas(const char *fname)
269 greg 2.1 {
270     FILE *fp = fopen(fname, "r");
271     int inp_is_DSF = -1;
272 greg 2.10 double new_phi, theta_out, phi_out, val;
273 greg 2.1 char buf[2048];
274     int n, c;
275    
276     if (fp == NULL) {
277     fputs(fname, stderr);
278     fputs(": cannot open\n", stderr);
279     return(0);
280     }
281 greg 2.5 memset(dsf_grid, 0, sizeof(dsf_grid));
282 greg 2.10 #ifdef DEBUG
283     fprintf(stderr, "Loading measurement file '%s'...\n", fname);
284     #endif
285 greg 2.1 /* read header information */
286     while ((c = getc(fp)) == '#' || c == EOF) {
287     if (fgets(buf, sizeof(buf), fp) == NULL) {
288     fputs(fname, stderr);
289     fputs(": unexpected EOF\n", stderr);
290     fclose(fp);
291     return(0);
292     }
293     if (!strcmp(buf, "format: theta phi DSF\n")) {
294     inp_is_DSF = 1;
295     continue;
296     }
297     if (!strcmp(buf, "format: theta phi BSDF\n")) {
298     inp_is_DSF = 0;
299     continue;
300     }
301     if (sscanf(buf, "intheta %lf", &theta_in_deg) == 1)
302     continue;
303 greg 2.10 if (sscanf(buf, "inphi %lf", &new_phi) == 1)
304 greg 2.1 continue;
305     if (sscanf(buf, "incident_angle %lf %lf",
306 greg 2.10 &theta_in_deg, &new_phi) == 2)
307 greg 2.1 continue;
308     }
309     if (inp_is_DSF < 0) {
310     fputs(fname, stderr);
311     fputs(": unknown format\n", stderr);
312     fclose(fp);
313     return(0);
314     }
315 greg 2.10 if (!input_orient) /* check input orientation */
316     input_orient = 1 - 2*(theta_in_deg > 90.);
317     else if (input_orient > 0 ^ theta_in_deg < 90.) {
318     fputs("Cannot handle input angles on both sides of surface\n",
319     stderr);
320     exit(1);
321     }
322     if (single_plane_incident > 0) /* check if still in plane */
323     single_plane_incident = (round(new_phi) == round(phi_in_deg));
324     else if (single_plane_incident < 0)
325     single_plane_incident = 1;
326     phi_in_deg = new_phi;
327     ungetc(c, fp); /* read actual data */
328 greg 2.1 while (fscanf(fp, "%lf %lf %lf\n", &theta_out, &phi_out, &val) == 3) {
329     FVECT ovec;
330     int pos[2];
331    
332 greg 2.10 if (!output_orient) /* check output orientation */
333     output_orient = 1 - 2*(theta_out > 90.);
334     else if (output_orient > 0 ^ theta_out < 90.) {
335     fputs("Cannot handle output angles on both sides of surface\n",
336     stderr);
337     exit(1);
338     }
339 greg 2.1 ovec[2] = sin(M_PI/180.*theta_out);
340     ovec[0] = cos(M_PI/180.*phi_out) * ovec[2];
341     ovec[1] = sin(M_PI/180.*phi_out) * ovec[2];
342     ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
343    
344 greg 2.5 if (!inp_is_DSF)
345     val *= ovec[2]; /* convert from BSDF to DSF */
346 greg 2.1
347     pos_from_vec(pos, ovec);
348    
349 greg 2.5 dsf_grid[pos[0]][pos[1]].vsum += val;
350     dsf_grid[pos[0]][pos[1]].nval++;
351 greg 2.1 }
352     n = 0;
353     while ((c = getc(fp)) != EOF)
354     n += !isspace(c);
355     if (n)
356     fprintf(stderr,
357     "%s: warning: %d unexpected characters past EOD\n",
358     fname, n);
359     fclose(fp);
360     return(1);
361     }
362    
363     /* Compute radii for non-empty bins */
364     /* (distance to furthest empty bin for which non-empty bin is the closest) */
365     static void
366     compute_radii(void)
367     {
368 greg 2.4 unsigned int fill_grid[GRIDRES][GRIDRES];
369     unsigned short fill_cnt[GRIDRES][GRIDRES];
370 greg 2.2 FVECT ovec0, ovec1;
371     double ang2, lastang2;
372     int r, i, j, jn, ii, jj, inear, jnear;
373    
374     r = GRIDRES/2; /* proceed in zig-zag */
375 greg 2.1 for (i = 0; i < GRIDRES; i++)
376     for (jn = 0; jn < GRIDRES; jn++) {
377     j = (i&1) ? jn : GRIDRES-1-jn;
378 greg 2.5 if (dsf_grid[i][j].nval) /* find empty grid pos. */
379 greg 2.1 continue;
380 greg 2.10 ovec_from_pos(ovec0, i, j);
381 greg 2.1 inear = jnear = -1; /* find nearest non-empty */
382 greg 2.2 lastang2 = M_PI*M_PI;
383 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
384     if (ii < 0) continue;
385     if (ii >= GRIDRES) break;
386     for (jj = j-r; jj <= j+r; jj++) {
387     if (jj < 0) continue;
388     if (jj >= GRIDRES) break;
389 greg 2.5 if (!dsf_grid[ii][jj].nval)
390 greg 2.1 continue;
391 greg 2.10 ovec_from_pos(ovec1, ii, jj);
392 greg 2.2 ang2 = 2. - 2.*DOT(ovec0,ovec1);
393     if (ang2 >= lastang2)
394 greg 2.1 continue;
395 greg 2.2 lastang2 = ang2;
396 greg 2.1 inear = ii; jnear = jj;
397     }
398     }
399 greg 2.2 if (inear < 0) {
400     fputs("Could not find non-empty neighbor!\n", stderr);
401     exit(1);
402     }
403     ang2 = sqrt(lastang2);
404     r = ANG2R(ang2); /* record if > previous */
405 greg 2.5 if (r > dsf_grid[inear][jnear].crad)
406     dsf_grid[inear][jnear].crad = r;
407 greg 2.2 /* next search radius */
408 greg 2.10 r = ang2*(2.*GRIDRES/M_PI) + 3;
409 greg 2.1 }
410 greg 2.4 /* blur radii over hemisphere */
411 greg 2.1 memset(fill_grid, 0, sizeof(fill_grid));
412 greg 2.4 memset(fill_cnt, 0, sizeof(fill_cnt));
413 greg 2.1 for (i = 0; i < GRIDRES; i++)
414     for (j = 0; j < GRIDRES; j++) {
415 greg 2.5 if (!dsf_grid[i][j].crad)
416 greg 2.4 continue; /* missing distance */
417 greg 2.5 r = R2ANG(dsf_grid[i][j].crad)*(2.*RSCA*GRIDRES/M_PI);
418 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
419     if (ii < 0) continue;
420     if (ii >= GRIDRES) break;
421     for (jj = j-r; jj <= j+r; jj++) {
422     if (jj < 0) continue;
423     if (jj >= GRIDRES) break;
424 greg 2.4 if ((ii-i)*(ii-i) + (jj-j)*(jj-j) > r*r)
425 greg 2.1 continue;
426 greg 2.5 fill_grid[ii][jj] += dsf_grid[i][j].crad;
427 greg 2.4 fill_cnt[ii][jj]++;
428 greg 2.1 }
429     }
430     }
431 greg 2.6 /* copy back blurred radii */
432 greg 2.1 for (i = 0; i < GRIDRES; i++)
433     for (j = 0; j < GRIDRES; j++)
434 greg 2.4 if (fill_cnt[i][j])
435 greg 2.5 dsf_grid[i][j].crad = fill_grid[i][j]/fill_cnt[i][j];
436 greg 2.1 }
437    
438 greg 2.6 /* Cull points for more uniform distribution, leave all nval 0 or 1 */
439 greg 2.1 static void
440     cull_values(void)
441     {
442 greg 2.2 FVECT ovec0, ovec1;
443     double maxang, maxang2;
444     int i, j, ii, jj, r;
445 greg 2.1 /* simple greedy algorithm */
446     for (i = 0; i < GRIDRES; i++)
447     for (j = 0; j < GRIDRES; j++) {
448 greg 2.5 if (!dsf_grid[i][j].nval)
449 greg 2.1 continue;
450 greg 2.5 if (!dsf_grid[i][j].crad)
451 greg 2.2 continue; /* shouldn't happen */
452 greg 2.10 ovec_from_pos(ovec0, i, j);
453 greg 2.5 maxang = 2.*R2ANG(dsf_grid[i][j].crad);
454 greg 2.2 if (maxang > ovec0[2]) /* clamp near horizon */
455     maxang = ovec0[2];
456     r = maxang*(2.*GRIDRES/M_PI) + 1;
457     maxang2 = maxang*maxang;
458 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
459     if (ii < 0) continue;
460     if (ii >= GRIDRES) break;
461     for (jj = j-r; jj <= j+r; jj++) {
462     if (jj < 0) continue;
463     if (jj >= GRIDRES) break;
464 greg 2.5 if (!dsf_grid[ii][jj].nval)
465 greg 2.1 continue;
466 greg 2.2 if ((ii == i) & (jj == j))
467     continue; /* don't get self-absorbed */
468 greg 2.10 ovec_from_pos(ovec1, ii, jj);
469 greg 2.2 if (2. - 2.*DOT(ovec0,ovec1) >= maxang2)
470 greg 2.1 continue;
471 greg 2.2 /* absorb sum */
472 greg 2.5 dsf_grid[i][j].vsum += dsf_grid[ii][jj].vsum;
473     dsf_grid[i][j].nval += dsf_grid[ii][jj].nval;
474 greg 2.2 /* keep value, though */
475 greg 2.6 dsf_grid[ii][jj].vsum /= (float)dsf_grid[ii][jj].nval;
476 greg 2.5 dsf_grid[ii][jj].nval = 0;
477 greg 2.1 }
478     }
479     }
480 greg 2.6 /* final averaging pass */
481     for (i = 0; i < GRIDRES; i++)
482     for (j = 0; j < GRIDRES; j++)
483     if (dsf_grid[i][j].nval > 1) {
484     dsf_grid[i][j].vsum /= (float)dsf_grid[i][j].nval;
485     dsf_grid[i][j].nval = 1;
486     }
487 greg 2.1 }
488    
489 greg 2.8 /* Compute (and allocate) migration price matrix for optimization */
490     static float *
491 greg 2.10 price_routes(const RBFNODE *from_rbf, const RBFNODE *to_rbf)
492 greg 2.8 {
493     float *pmtx = (float *)malloc(sizeof(float) *
494     from_rbf->nrbf * to_rbf->nrbf);
495     FVECT *vto = (FVECT *)malloc(sizeof(FVECT) * to_rbf->nrbf);
496     int i, j;
497    
498     if ((pmtx == NULL) | (vto == NULL)) {
499     fputs("Out of memory in migration_costs()\n", stderr);
500     exit(1);
501     }
502     for (j = to_rbf->nrbf; j--; ) /* save repetitive ops. */
503 greg 2.10 ovec_from_pos(vto[j], to_rbf->rbfa[j].gx, to_rbf->rbfa[j].gy);
504 greg 2.8
505     for (i = from_rbf->nrbf; i--; ) {
506     const double from_ang = R2ANG(from_rbf->rbfa[i].crad);
507     FVECT vfrom;
508 greg 2.10 ovec_from_pos(vfrom, from_rbf->rbfa[i].gx, from_rbf->rbfa[i].gy);
509 greg 2.8 for (j = to_rbf->nrbf; j--; )
510     pmtx[i*to_rbf->nrbf + j] = acos(DOT(vfrom, vto[j])) +
511     fabs(R2ANG(to_rbf->rbfa[j].crad) - from_ang);
512     }
513     free(vto);
514     return(pmtx);
515     }
516    
517     /* Comparison routine needed for sorting price row */
518     static const float *price_arr;
519     static int
520     msrt_cmp(const void *p1, const void *p2)
521     {
522     float c1 = price_arr[*(const int *)p1];
523     float c2 = price_arr[*(const int *)p2];
524    
525     if (c1 > c2) return(1);
526     if (c1 < c2) return(-1);
527     return(0);
528     }
529    
530     /* Compute minimum (optimistic) cost for moving the given source material */
531     static double
532     min_cost(double amt2move, const double *avail, const float *price, int n)
533     {
534     static int *price_sort = NULL;
535     static int n_alloc = 0;
536     double total_cost = 0;
537     int i;
538    
539     if (amt2move <= FTINY) /* pre-emptive check */
540     return(0.);
541     if (n > n_alloc) { /* (re)allocate sort array */
542     if (n_alloc) free(price_sort);
543     price_sort = (int *)malloc(sizeof(int)*n);
544     if (price_sort == NULL) {
545     fputs("Out of memory in min_cost()\n", stderr);
546     exit(1);
547     }
548     n_alloc = n;
549     }
550     for (i = n; i--; )
551     price_sort[i] = i;
552     price_arr = price;
553     qsort(price_sort, n, sizeof(int), &msrt_cmp);
554     /* move cheapest first */
555     for (i = 0; i < n && amt2move > FTINY; i++) {
556     int d = price_sort[i];
557     double amt = (amt2move < avail[d]) ? amt2move : avail[d];
558    
559     total_cost += amt * price[d];
560     amt2move -= amt;
561     }
562     return(total_cost);
563     }
564    
565     /* Take a step in migration by choosing optimal bucket to transfer */
566     static double
567     migration_step(MIGRATION *mig, double *src_rem, double *dst_rem, const float *pmtx)
568     {
569     static double *src_cost = NULL;
570     int n_alloc = 0;
571 greg 2.14 const double maxamt = .1; /* 2./(mtx_nrows(mig)*mtx_ncols(mig)); */
572 greg 2.8 double amt = 0;
573     struct {
574     int s, d; /* source and destination */
575     double price; /* price estimate per amount moved */
576     double amt; /* amount we can move */
577     } cur, best;
578     int i;
579    
580     if (mtx_nrows(mig) > n_alloc) { /* allocate cost array */
581     if (n_alloc)
582     free(src_cost);
583     src_cost = (double *)malloc(sizeof(double)*mtx_nrows(mig));
584     if (src_cost == NULL) {
585     fputs("Out of memory in migration_step()\n", stderr);
586     exit(1);
587     }
588     n_alloc = mtx_nrows(mig);
589     }
590     for (i = mtx_nrows(mig); i--; ) /* starting costs for diff. */
591     src_cost[i] = min_cost(src_rem[i], dst_rem,
592     pmtx+i*mtx_ncols(mig), mtx_ncols(mig));
593    
594     /* find best source & dest. */
595     best.s = best.d = -1; best.price = FHUGE; best.amt = 0;
596     for (cur.s = mtx_nrows(mig); cur.s--; ) {
597     const float *price = pmtx + cur.s*mtx_ncols(mig);
598     double cost_others = 0;
599     if (src_rem[cur.s] <= FTINY)
600     continue;
601     cur.d = -1; /* examine cheapest dest. */
602     for (i = mtx_ncols(mig); i--; )
603     if (dst_rem[i] > FTINY &&
604     (cur.d < 0 || price[i] < price[cur.d]))
605     cur.d = i;
606     if (cur.d < 0)
607     return(.0);
608     if ((cur.price = price[cur.d]) >= best.price)
609     continue; /* no point checking further */
610     cur.amt = (src_rem[cur.s] < dst_rem[cur.d]) ?
611     src_rem[cur.s] : dst_rem[cur.d];
612     if (cur.amt > maxamt) cur.amt = maxamt;
613     dst_rem[cur.d] -= cur.amt; /* add up differential costs */
614     for (i = mtx_nrows(mig); i--; ) {
615     if (i == cur.s) continue;
616     cost_others += min_cost(src_rem[i], dst_rem, price, mtx_ncols(mig))
617     - src_cost[i];
618     }
619     dst_rem[cur.d] += cur.amt; /* undo trial move */
620     cur.price += cost_others/cur.amt; /* adjust effective price */
621     if (cur.price < best.price) /* are we better than best? */
622     best = cur;
623     }
624     if ((best.s < 0) | (best.d < 0))
625     return(.0);
626     /* make the actual move */
627     mig->mtx[mtx_ndx(mig,best.s,best.d)] += best.amt;
628     src_rem[best.s] -= best.amt;
629     dst_rem[best.d] -= best.amt;
630     return(best.amt);
631     }
632    
633 greg 2.14 #ifdef DEBUG
634     static char *
635     thetaphi(const FVECT v)
636     {
637     static char buf[128];
638     double theta, phi;
639    
640     theta = 180./M_PI*acos(v[2]);
641     phi = 180./M_PI*atan2(v[1],v[0]);
642     sprintf(buf, "(%.0f,%.0f)", theta, phi);
643    
644     return(buf);
645     }
646     #endif
647    
648 greg 2.8 /* Compute (and insert) migration along directed edge */
649     static MIGRATION *
650 greg 2.14 make_migration(RBFNODE *from_rbf, RBFNODE *to_rbf, int creat_only)
651 greg 2.8 {
652     const double end_thresh = 0.02/(from_rbf->nrbf*to_rbf->nrbf);
653 greg 2.14 float *pmtx;
654     MIGRATION *newmig;
655     double *src_rem, *dst_rem;
656 greg 2.8 double total_rem = 1.;
657     int i;
658 greg 2.14 /* check if exists already */
659     for (newmig = from_rbf->ejl; newmig != NULL;
660     newmig = nextedge(from_rbf,newmig))
661     if (newmig->rbfv[1] == to_rbf)
662     return(creat_only ? (MIGRATION *)NULL : newmig);
663     /* else allocate */
664     pmtx = price_routes(from_rbf, to_rbf);
665     newmig = (MIGRATION *)malloc(sizeof(MIGRATION) + sizeof(float) *
666     (from_rbf->nrbf*to_rbf->nrbf - 1));
667     src_rem = (double *)malloc(sizeof(double)*from_rbf->nrbf);
668     dst_rem = (double *)malloc(sizeof(double)*to_rbf->nrbf);
669 greg 2.8 if ((newmig == NULL) | (src_rem == NULL) | (dst_rem == NULL)) {
670     fputs("Out of memory in make_migration()\n", stderr);
671     exit(1);
672     }
673 greg 2.10 #ifdef DEBUG
674     {
675 greg 2.14 fprintf(stderr, "Building path from (theta,phi) %s ",
676     thetaphi(from_rbf->invec));
677     fprintf(stderr, "to %s", thetaphi(to_rbf->invec));
678 greg 2.10 }
679     #endif
680 greg 2.8 newmig->next = NULL;
681     newmig->rbfv[0] = from_rbf;
682     newmig->rbfv[1] = to_rbf;
683     newmig->enxt[0] = newmig->enxt[1] = NULL;
684     memset(newmig->mtx, 0, sizeof(float)*from_rbf->nrbf*to_rbf->nrbf);
685     /* starting quantities */
686     for (i = from_rbf->nrbf; i--; )
687     src_rem[i] = rbf_volume(&from_rbf->rbfa[i]) / from_rbf->vtotal;
688     for (i = to_rbf->nrbf; i--; )
689     dst_rem[i] = rbf_volume(&to_rbf->rbfa[i]) / to_rbf->vtotal;
690     /* move a bit at a time */
691 greg 2.13 while (total_rem > end_thresh) {
692 greg 2.8 total_rem -= migration_step(newmig, src_rem, dst_rem, pmtx);
693 greg 2.13 #ifdef DEBUG
694 greg 2.14 /* fputc('.', stderr); */
695     fprintf(stderr, "\n%.9f remaining...", total_rem);
696 greg 2.13 #endif
697     }
698     #ifdef DEBUG
699     fputs("done.\n", stderr);
700     #endif
701 greg 2.8
702     free(pmtx); /* free working arrays */
703     free(src_rem);
704     free(dst_rem);
705     for (i = from_rbf->nrbf; i--; ) { /* normalize final matrix */
706     float nf = rbf_volume(&from_rbf->rbfa[i]);
707     int j;
708     if (nf <= FTINY) continue;
709     nf = from_rbf->vtotal / nf;
710     for (j = to_rbf->nrbf; j--; )
711     newmig->mtx[mtx_ndx(newmig,i,j)] *= nf;
712     }
713     /* insert in edge lists */
714     newmig->enxt[0] = from_rbf->ejl;
715     from_rbf->ejl = newmig;
716     newmig->enxt[1] = to_rbf->ejl;
717     to_rbf->ejl = newmig;
718     newmig->next = mig_list;
719     return(mig_list = newmig);
720     }
721    
722 greg 2.10 /* Get triangle surface orientation (unnormalized) */
723     static void
724     tri_orient(FVECT vres, const FVECT v1, const FVECT v2, const FVECT v3)
725     {
726     FVECT v2minus1, v3minus2;
727    
728     VSUB(v2minus1, v2, v1);
729     VSUB(v3minus2, v3, v2);
730     VCROSS(vres, v2minus1, v3minus2);
731     }
732    
733     /* Determine if vertex order is reversed (inward normal) */
734     static int
735     is_rev_tri(const FVECT v1, const FVECT v2, const FVECT v3)
736     {
737     FVECT tor;
738    
739     tri_orient(tor, v1, v2, v3);
740    
741     return(DOT(tor, v2) < 0.);
742     }
743    
744     /* Find vertices completing triangles on either side of the given edge */
745     static int
746     get_triangles(RBFNODE *rbfv[2], const MIGRATION *mig)
747     {
748     const MIGRATION *ej, *ej2;
749     RBFNODE *tv;
750    
751     rbfv[0] = rbfv[1] = NULL;
752 greg 2.13 if (mig == NULL)
753     return(0);
754 greg 2.10 for (ej = mig->rbfv[0]->ejl; ej != NULL;
755     ej = nextedge(mig->rbfv[0],ej)) {
756     if (ej == mig)
757     continue;
758     tv = opp_rbf(mig->rbfv[0],ej);
759     for (ej2 = tv->ejl; ej2 != NULL; ej2 = nextedge(tv,ej2))
760     if (opp_rbf(tv,ej2) == mig->rbfv[1]) {
761     rbfv[is_rev_tri(mig->rbfv[0]->invec,
762     mig->rbfv[1]->invec,
763     tv->invec)] = tv;
764     break;
765     }
766     }
767     return((rbfv[0] != NULL) + (rbfv[1] != NULL));
768     }
769    
770 greg 2.13 /* Check if prospective vertex would create overlapping triangle */
771     static int
772     overlaps_tri(const RBFNODE *bv0, const RBFNODE *bv1, const RBFNODE *pv)
773     {
774     const MIGRATION *ej;
775     RBFNODE *vother[2];
776     int im_rev;
777     /* find shared edge in mesh */
778     for (ej = pv->ejl; ej != NULL; ej = nextedge(pv,ej)) {
779     const RBFNODE *tv = opp_rbf(pv,ej);
780     if (tv == bv0) {
781     im_rev = is_rev_tri(ej->rbfv[0]->invec,
782     ej->rbfv[1]->invec, bv1->invec);
783     break;
784     }
785     if (tv == bv1) {
786     im_rev = is_rev_tri(ej->rbfv[0]->invec,
787     ej->rbfv[1]->invec, bv0->invec);
788     break;
789     }
790     }
791     if (!get_triangles(vother, ej))
792     return(0);
793     return(vother[im_rev] != NULL);
794     }
795    
796 greg 2.10 /* Find context hull vertex to complete triangle (oriented call) */
797     static RBFNODE *
798     find_chull_vert(const RBFNODE *rbf0, const RBFNODE *rbf1)
799 greg 2.8 {
800 greg 2.14 FVECT vmid, vejn, vp;
801 greg 2.10 RBFNODE *rbf, *rbfbest = NULL;
802 greg 2.14 double dprod, area2, bestarea2 = FHUGE, bestdprod = -.5;
803 greg 2.10
804 greg 2.14 VSUB(vejn, rbf1->invec, rbf0->invec);
805 greg 2.10 VADD(vmid, rbf0->invec, rbf1->invec);
806 greg 2.14 if (normalize(vejn) == 0 || normalize(vmid) == 0)
807 greg 2.10 return(NULL);
808     /* XXX exhaustive search */
809     for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) {
810     if ((rbf == rbf0) | (rbf == rbf1))
811     continue;
812 greg 2.14 tri_orient(vp, rbf0->invec, rbf1->invec, rbf->invec);
813     if (DOT(vp, vmid) <= FTINY)
814 greg 2.10 continue; /* wrong orientation */
815 greg 2.14 area2 = DOT(vp,vp);
816     VSUB(vp, rbf->invec, rbf0->invec);
817     dprod = -DOT(vp, vejn);
818     VSUM(vp, vp, vejn, dprod);
819     dprod = DOT(vp, vmid) / VLEN(vp);
820     if (dprod <= bestdprod + FTINY*(1 - 2*(area2 < bestarea2)))
821     continue; /* found better already */
822     if (overlaps_tri(rbf0, rbf1, rbf))
823     continue; /* overlaps another triangle */
824     rbfbest = rbf;
825     bestdprod = dprod; /* new one to beat */
826     bestarea2 = area2;
827 greg 2.10 }
828 greg 2.13 return(rbfbest);
829 greg 2.10 }
830    
831     /* Create new migration edge and grow mesh recursively around it */
832     static void
833 greg 2.13 mesh_from_edge(MIGRATION *edge)
834 greg 2.10 {
835 greg 2.13 MIGRATION *ej0, *ej1;
836 greg 2.10 RBFNODE *tvert[2];
837 greg 2.14
838     if (edge == NULL)
839     return;
840 greg 2.10 /* triangle on either side? */
841 greg 2.13 get_triangles(tvert, edge);
842     if (tvert[0] == NULL) { /* grow mesh on right */
843     tvert[0] = find_chull_vert(edge->rbfv[0], edge->rbfv[1]);
844 greg 2.10 if (tvert[0] != NULL) {
845 greg 2.13 if (tvert[0] > edge->rbfv[0])
846 greg 2.14 ej0 = make_migration(edge->rbfv[0], tvert[0], 1);
847 greg 2.13 else
848 greg 2.14 ej0 = make_migration(tvert[0], edge->rbfv[0], 1);
849 greg 2.13 if (tvert[0] > edge->rbfv[1])
850 greg 2.14 ej1 = make_migration(edge->rbfv[1], tvert[0], 1);
851 greg 2.13 else
852 greg 2.14 ej1 = make_migration(tvert[0], edge->rbfv[1], 1);
853 greg 2.13 mesh_from_edge(ej0);
854     mesh_from_edge(ej1);
855 greg 2.10 }
856 greg 2.14 } else if (tvert[1] == NULL) { /* grow mesh on left */
857 greg 2.13 tvert[1] = find_chull_vert(edge->rbfv[1], edge->rbfv[0]);
858 greg 2.10 if (tvert[1] != NULL) {
859 greg 2.13 if (tvert[1] > edge->rbfv[0])
860 greg 2.14 ej0 = make_migration(edge->rbfv[0], tvert[1], 1);
861 greg 2.13 else
862 greg 2.14 ej0 = make_migration(tvert[1], edge->rbfv[0], 1);
863 greg 2.13 if (tvert[1] > edge->rbfv[1])
864 greg 2.14 ej1 = make_migration(edge->rbfv[1], tvert[1], 1);
865 greg 2.13 else
866 greg 2.14 ej1 = make_migration(tvert[1], edge->rbfv[1], 1);
867 greg 2.13 mesh_from_edge(ej0);
868     mesh_from_edge(ej1);
869 greg 2.10 }
870     }
871     }
872 greg 2.8
873 greg 2.13 #ifdef DEBUG
874     #include "random.h"
875     #include "bmpfile.h"
876     /* Hash pointer to byte value */
877     static int
878     byte_hash(const void *p)
879     {
880     size_t h = (size_t)p;
881     h ^= (size_t)p >> 8;
882     h ^= (size_t)p >> 16;
883     h ^= (size_t)p >> 24;
884     return(h & 0xff);
885     }
886     /* Write out BMP image showing edges */
887     static void
888     write_edge_image(const char *fname)
889     {
890     BMPHeader *hdr = BMPmappedHeader(GRIDRES, GRIDRES, 0, 256);
891     BMPWriter *wtr;
892     int i, j;
893    
894     fprintf(stderr, "Writing incident mesh drawing to '%s'\n", fname);
895     hdr->compr = BI_RLE8;
896     for (i = 256; --i; ) { /* assign random color map */
897     hdr->palette[i].r = random() & 0xff;
898     hdr->palette[i].r = random() & 0xff;
899     hdr->palette[i].r = random() & 0xff;
900     }
901     hdr->palette[0].r = hdr->palette[0].g = hdr->palette[0].b = 0;
902     /* open output */
903     wtr = BMPopenOutputFile(fname, hdr);
904     if (wtr == NULL) {
905     free(hdr);
906     return;
907     }
908     for (i = 0; i < GRIDRES; i++) { /* write scanlines */
909     for (j = 0; j < GRIDRES; j++)
910     wtr->scanline[j] = byte_hash(mig_grid[i][j]);
911     if (BMPwriteScanline(wtr) != BIR_OK)
912     break;
913     }
914     BMPcloseOutput(wtr); /* close & clean up */
915     }
916     #endif
917    
918 greg 2.10 /* Draw edge list into mig_grid array */
919     static void
920     draw_edges()
921     {
922     int nnull = 0, ntot = 0;
923     MIGRATION *ej;
924     int p0[2], p1[2];
925    
926     /* memset(mig_grid, 0, sizeof(mig_grid)); */
927     for (ej = mig_list; ej != NULL; ej = ej->next) {
928     ++ntot;
929     pos_from_vec(p0, ej->rbfv[0]->invec);
930     pos_from_vec(p1, ej->rbfv[1]->invec);
931     if ((p0[0] == p1[0]) & (p0[1] == p1[1])) {
932     ++nnull;
933     mig_grid[p0[0]][p0[1]] = ej;
934     continue;
935     }
936     if (abs(p1[0]-p0[0]) > abs(p1[1]-p0[1])) {
937     const int xstep = 2*(p1[0] > p0[0]) - 1;
938     const double ystep = (double)((p1[1]-p0[1])*xstep) /
939     (double)(p1[0]-p0[0]);
940     int x;
941     double y;
942     for (x = p0[0], y = p0[1]+.5; x != p1[0];
943     x += xstep, y += ystep)
944     mig_grid[x][(int)y] = ej;
945     mig_grid[x][(int)y] = ej;
946     } else {
947     const int ystep = 2*(p1[1] > p0[1]) - 1;
948     const double xstep = (double)((p1[0]-p0[0])*ystep) /
949     (double)(p1[1]-p0[1]);
950     int y;
951     double x;
952     for (y = p0[1], x = p0[0]+.5; y != p1[1];
953     y += ystep, x += xstep)
954     mig_grid[(int)x][y] = ej;
955     mig_grid[(int)x][y] = ej;
956     }
957     }
958     if (nnull)
959     fprintf(stderr, "Warning: %d of %d edges are null\n",
960     nnull, ntot);
961 greg 2.13 #ifdef DEBUG
962     write_edge_image("bsdf_edges.bmp");
963     #endif
964 greg 2.10 }
965    
966     /* Build our triangle mesh from recorded RBFs */
967     static void
968     build_mesh()
969     {
970     double best2 = M_PI*M_PI;
971 greg 2.13 RBFNODE *shrt_edj[2];
972     RBFNODE *rbf0, *rbf1;
973 greg 2.10 /* check if isotropic */
974     if (single_plane_incident) {
975 greg 2.13 for (rbf0 = dsf_list; rbf0 != NULL; rbf0 = rbf0->next)
976     if (rbf0->next != NULL)
977 greg 2.14 make_migration(rbf0, rbf0->next, 1);
978 greg 2.10 return;
979     }
980 greg 2.13 /* start w/ shortest edge */
981     shrt_edj[0] = shrt_edj[1] = NULL;
982     for (rbf0 = dsf_list; rbf0 != NULL; rbf0 = rbf0->next)
983     for (rbf1 = rbf0->next; rbf1 != NULL; rbf1 = rbf1->next) {
984     double dist2 = 2. - 2.*DOT(rbf0->invec,rbf1->invec);
985 greg 2.10 if (dist2 < best2) {
986 greg 2.13 shrt_edj[0] = rbf0;
987     shrt_edj[1] = rbf1;
988 greg 2.10 best2 = dist2;
989     }
990     }
991 greg 2.13 if (shrt_edj[0] == NULL) {
992     fputs("Cannot find shortest edge\n", stderr);
993 greg 2.8 exit(1);
994     }
995 greg 2.10 /* build mesh from this edge */
996 greg 2.13 if (shrt_edj[0] < shrt_edj[1])
997 greg 2.14 mesh_from_edge(make_migration(shrt_edj[0], shrt_edj[1], 0));
998 greg 2.13 else
999 greg 2.14 mesh_from_edge(make_migration(shrt_edj[1], shrt_edj[0], 0));
1000 greg 2.10 /* draw edge list into grid */
1001     draw_edges();
1002     }
1003    
1004     /* Identify enclosing triangle for this position (flood fill raster check) */
1005     static int
1006     identify_tri(MIGRATION *miga[3], unsigned char vmap[GRIDRES][(GRIDRES+7)/8],
1007     int px, int py)
1008     {
1009     const int btest = 1<<(py&07);
1010    
1011     if (vmap[px][py>>3] & btest) /* already visited here? */
1012     return(1);
1013     /* else mark it */
1014     vmap[px][py>>3] |= btest;
1015    
1016     if (mig_grid[px][py] != NULL) { /* are we on an edge? */
1017     int i;
1018     for (i = 0; i < 3; i++) {
1019     if (miga[i] == mig_grid[px][py])
1020     return(1);
1021     if (miga[i] != NULL)
1022     continue;
1023     miga[i] = mig_grid[px][py];
1024     return(1);
1025     }
1026     return(0); /* outside triangle! */
1027     }
1028     /* check neighbors (flood) */
1029     if (px > 0 && !identify_tri(miga, vmap, px-1, py))
1030     return(0);
1031     if (px < GRIDRES-1 && !identify_tri(miga, vmap, px+1, py))
1032     return(0);
1033     if (py > 0 && !identify_tri(miga, vmap, px, py-1))
1034     return(0);
1035     if (py < GRIDRES-1 && !identify_tri(miga, vmap, px, py+1))
1036     return(0);
1037     return(1); /* this neighborhood done */
1038     }
1039    
1040     /* Find edge(s) for interpolating the given incident vector */
1041     static int
1042     get_interp(MIGRATION *miga[3], const FVECT invec)
1043     {
1044     miga[0] = miga[1] = miga[2] = NULL;
1045     if (single_plane_incident) { /* isotropic BSDF? */
1046     RBFNODE *rbf; /* find edge we're on */
1047     for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) {
1048     if (input_orient*rbf->invec[2] < input_orient*invec[2])
1049     break;
1050     if (rbf->next != NULL &&
1051     input_orient*rbf->next->invec[2] <
1052     input_orient*invec[2]) {
1053     for (miga[0] = rbf->ejl; miga[0] != NULL;
1054     miga[0] = nextedge(rbf,miga[0]))
1055     if (opp_rbf(rbf,miga[0]) == rbf->next)
1056     return(1);
1057     break;
1058     }
1059     }
1060     return(0); /* outside range! */
1061     }
1062 greg 2.12 { /* else use triangle mesh */
1063 greg 2.10 unsigned char floodmap[GRIDRES][(GRIDRES+7)/8];
1064     int pstart[2];
1065    
1066     pos_from_vec(pstart, invec);
1067     memset(floodmap, 0, sizeof(floodmap));
1068     /* call flooding function */
1069     if (!identify_tri(miga, floodmap, pstart[0], pstart[1]))
1070     return(0); /* outside mesh */
1071     if ((miga[0] == NULL) | (miga[2] == NULL))
1072     return(0); /* should never happen */
1073     if (miga[1] == NULL)
1074     return(1); /* on edge */
1075     return(3); /* else in triangle */
1076     }
1077     }
1078    
1079     /* Advect and allocate new RBF along edge */
1080     static RBFNODE *
1081     e_advect_rbf(const MIGRATION *mig, const FVECT invec)
1082     {
1083     RBFNODE *rbf;
1084     int n, i, j;
1085     double t, full_dist;
1086     /* get relative position */
1087     t = acos(DOT(invec, mig->rbfv[0]->invec));
1088     if (t < M_PI/GRIDRES) { /* near first DSF */
1089     n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[0]->nrbf-1);
1090     rbf = (RBFNODE *)malloc(n);
1091     if (rbf == NULL)
1092     goto memerr;
1093     memcpy(rbf, mig->rbfv[0], n); /* just duplicate */
1094     return(rbf);
1095     }
1096     full_dist = acos(DOT(mig->rbfv[0]->invec, mig->rbfv[1]->invec));
1097     if (t > full_dist-M_PI/GRIDRES) { /* near second DSF */
1098     n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[1]->nrbf-1);
1099     rbf = (RBFNODE *)malloc(n);
1100     if (rbf == NULL)
1101     goto memerr;
1102     memcpy(rbf, mig->rbfv[1], n); /* just duplicate */
1103     return(rbf);
1104     }
1105     t /= full_dist;
1106     n = 0; /* count migrating particles */
1107     for (i = 0; i < mtx_nrows(mig); i++)
1108     for (j = 0; j < mtx_ncols(mig); j++)
1109     n += (mig->mtx[mtx_ndx(mig,i,j)] > FTINY);
1110 greg 2.12 #ifdef DEBUG
1111     fprintf(stderr, "Input RBFs have %d, %d nodes -> output has %d\n",
1112     mig->rbfv[0]->nrbf, mig->rbfv[1]->nrbf, n);
1113     #endif
1114 greg 2.10 rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1));
1115     if (rbf == NULL)
1116     goto memerr;
1117     rbf->next = NULL; rbf->ejl = NULL;
1118     VCOPY(rbf->invec, invec);
1119     rbf->nrbf = n;
1120     rbf->vtotal = 1.-t + t*mig->rbfv[1]->vtotal/mig->rbfv[0]->vtotal;
1121     n = 0; /* advect RBF lobes */
1122     for (i = 0; i < mtx_nrows(mig); i++) {
1123     const RBFVAL *rbf0i = &mig->rbfv[0]->rbfa[i];
1124     const float peak0 = rbf0i->peak;
1125     const double rad0 = R2ANG(rbf0i->crad);
1126     FVECT v0;
1127     float mv;
1128     ovec_from_pos(v0, rbf0i->gx, rbf0i->gy);
1129     for (j = 0; j < mtx_ncols(mig); j++)
1130     if ((mv = mig->mtx[mtx_ndx(mig,i,j)]) > FTINY) {
1131     const RBFVAL *rbf1j = &mig->rbfv[1]->rbfa[j];
1132     double rad1 = R2ANG(rbf1j->crad);
1133     FVECT v;
1134     int pos[2];
1135     rbf->rbfa[n].peak = peak0 * mv * rbf->vtotal;
1136     rbf->rbfa[n].crad = ANG2R(sqrt(rad0*rad0*(1.-t) +
1137     rad1*rad1*t));
1138     ovec_from_pos(v, rbf1j->gx, rbf1j->gy);
1139     geodesic(v, v0, v, t, GEOD_REL);
1140     pos_from_vec(pos, v);
1141     rbf->rbfa[n].gx = pos[0];
1142     rbf->rbfa[n].gy = pos[1];
1143     ++n;
1144     }
1145     }
1146     rbf->vtotal *= mig->rbfv[0]->vtotal; /* turn ratio into actual */
1147     return(rbf);
1148     memerr:
1149     fputs("Out of memory in e_advect_rbf()\n", stderr);
1150     exit(1);
1151     return(NULL); /* pro forma return */
1152     }
1153    
1154 greg 2.11 /* Insert vertex in ordered list */
1155     static void
1156     insert_vert(RBFNODE **vlist, RBFNODE *v)
1157     {
1158     int i, j;
1159    
1160     for (i = 0; vlist[i] != NULL; i++) {
1161     if (v == vlist[i])
1162     return;
1163     if (v < vlist[i])
1164     break;
1165     }
1166     for (j = i; vlist[j] != NULL; j++)
1167     ;
1168     while (j > i) {
1169     vlist[j] = vlist[j-1];
1170     --j;
1171     }
1172     vlist[i] = v;
1173     }
1174    
1175     /* Sort triangle edges in standard order */
1176     static void
1177     order_triangle(MIGRATION *miga[3])
1178     {
1179     RBFNODE *vert[4];
1180     MIGRATION *ord[3];
1181     int i;
1182     /* order vertices, first */
1183     memset(vert, 0, sizeof(vert));
1184     for (i = 0; i < 3; i++) {
1185     insert_vert(vert, miga[i]->rbfv[0]);
1186     insert_vert(vert, miga[i]->rbfv[1]);
1187     }
1188     /* identify edge 0 */
1189     for (i = 0; i < 3; i++)
1190     if (miga[i]->rbfv[0] == vert[0] &&
1191     miga[i]->rbfv[1] == vert[1]) {
1192     ord[0] = miga[i];
1193     break;
1194     }
1195     /* identify edge 1 */
1196     for (i = 0; i < 3; i++)
1197     if (miga[i]->rbfv[0] == vert[1] &&
1198     miga[i]->rbfv[1] == vert[2]) {
1199     ord[1] = miga[i];
1200     break;
1201     }
1202     /* identify edge 2 */
1203     for (i = 0; i < 3; i++)
1204     if (miga[i]->rbfv[0] == vert[0] &&
1205     miga[i]->rbfv[1] == vert[2]) {
1206     ord[2] = miga[i];
1207     break;
1208     }
1209     miga[0] = ord[0]; miga[1] = ord[1]; miga[2] = ord[2];
1210     }
1211    
1212 greg 2.10 /* Partially advect between recorded incident angles and allocate new RBF */
1213     static RBFNODE *
1214     advect_rbf(const FVECT invec)
1215     {
1216     MIGRATION *miga[3];
1217     RBFNODE *rbf;
1218 greg 2.11 float mbfact, mcfact;
1219     int n, i, j, k;
1220     FVECT v0, v1, v2;
1221 greg 2.10 double s, t;
1222    
1223     if (!get_interp(miga, invec)) /* can't interpolate? */
1224     return(NULL);
1225 greg 2.13 if (miga[1] == NULL) /* advect along edge? */
1226 greg 2.10 return(e_advect_rbf(miga[0], invec));
1227 greg 2.11 /* put in standard order */
1228     order_triangle(miga);
1229 greg 2.12 #ifdef DEBUG
1230     if (miga[0]->rbfv[0] != miga[2]->rbfv[0] |
1231     miga[0]->rbfv[1] != miga[1]->rbfv[0] |
1232     miga[1]->rbfv[1] != miga[2]->rbfv[1]) {
1233     fputs("Triangle vertex screw-up!\n", stderr);
1234     exit(1);
1235     }
1236     #endif
1237 greg 2.10 /* figure out position */
1238 greg 2.11 fcross(v0, miga[2]->rbfv[0]->invec, miga[2]->rbfv[1]->invec);
1239     normalize(v0);
1240     fcross(v2, miga[1]->rbfv[0]->invec, miga[1]->rbfv[1]->invec);
1241     normalize(v2);
1242     fcross(v1, invec, miga[1]->rbfv[1]->invec);
1243     normalize(v1);
1244     s = acos(DOT(v0,v1)) / acos(DOT(v0,v2));
1245     geodesic(v1, miga[0]->rbfv[0]->invec, miga[0]->rbfv[1]->invec,
1246     s, GEOD_REL);
1247     t = acos(DOT(v1,invec)) / acos(DOT(v1,miga[1]->rbfv[1]->invec));
1248     n = 0; /* count migrating particles */
1249     for (i = 0; i < mtx_nrows(miga[0]); i++)
1250     for (j = 0; j < mtx_ncols(miga[0]); j++)
1251     for (k = (miga[0]->mtx[mtx_ndx(miga[0],i,j)] > FTINY) *
1252     mtx_ncols(miga[2]); k--; )
1253     n += (miga[2]->mtx[mtx_ndx(miga[2],i,k)] > FTINY &&
1254     miga[1]->mtx[mtx_ndx(miga[1],j,k)] > FTINY);
1255 greg 2.12 #ifdef DEBUG
1256     fprintf(stderr, "Input RBFs have %d, %d, %d nodes -> output has %d\n",
1257     miga[0]->rbfv[0]->nrbf, miga[0]->rbfv[1]->nrbf,
1258     miga[2]->rbfv[1]->nrbf, n);
1259     #endif
1260 greg 2.10 rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1));
1261 greg 2.8 if (rbf == NULL) {
1262     fputs("Out of memory in advect_rbf()\n", stderr);
1263     exit(1);
1264     }
1265     rbf->next = NULL; rbf->ejl = NULL;
1266     VCOPY(rbf->invec, invec);
1267 greg 2.10 rbf->nrbf = n;
1268 greg 2.11 n = 0; /* compute RBF lobes */
1269     mbfact = s * miga[0]->rbfv[1]->vtotal/miga[0]->rbfv[0]->vtotal *
1270     (1.-t + t*miga[1]->rbfv[1]->vtotal/miga[1]->rbfv[0]->vtotal);
1271     mcfact = (1.-s) *
1272     (1.-t + t*miga[2]->rbfv[1]->vtotal/miga[2]->rbfv[0]->vtotal);
1273     for (i = 0; i < mtx_nrows(miga[0]); i++) {
1274     const RBFVAL *rbf0i = &miga[0]->rbfv[0]->rbfa[i];
1275     const float w0i = rbf0i->peak;
1276     const double rad0i = R2ANG(rbf0i->crad);
1277     ovec_from_pos(v0, rbf0i->gx, rbf0i->gy);
1278     for (j = 0; j < mtx_ncols(miga[0]); j++) {
1279     const float ma = miga[0]->mtx[mtx_ndx(miga[0],i,j)];
1280     const RBFVAL *rbf1j;
1281     double rad1j, srad2;
1282     if (ma <= FTINY)
1283     continue;
1284     rbf1j = &miga[0]->rbfv[1]->rbfa[j];
1285     rad1j = R2ANG(rbf1j->crad);
1286     srad2 = (1.-s)*(1.-t)*rad0i*rad0i + s*(1.-t)*rad1j*rad1j;
1287     ovec_from_pos(v1, rbf1j->gx, rbf1j->gy);
1288     geodesic(v1, v0, v1, s, GEOD_REL);
1289     for (k = 0; k < mtx_ncols(miga[2]); k++) {
1290     float mb = miga[1]->mtx[mtx_ndx(miga[1],j,k)];
1291     float mc = miga[2]->mtx[mtx_ndx(miga[2],i,k)];
1292     const RBFVAL *rbf2k;
1293     double rad2k;
1294     FVECT vout;
1295     int pos[2];
1296     if ((mb <= FTINY) | (mc <= FTINY))
1297     continue;
1298     rbf2k = &miga[2]->rbfv[1]->rbfa[k];
1299     rbf->rbfa[n].peak = w0i * ma * (mb*mbfact + mc*mcfact);
1300     rad2k = R2ANG(rbf2k->crad);
1301 greg 2.12 rbf->rbfa[n].crad = ANG2R(sqrt(srad2 + t*rad2k*rad2k));
1302 greg 2.11 ovec_from_pos(v2, rbf2k->gx, rbf2k->gy);
1303     geodesic(vout, v1, v2, t, GEOD_REL);
1304     pos_from_vec(pos, vout);
1305     rbf->rbfa[n].gx = pos[0];
1306     rbf->rbfa[n].gy = pos[1];
1307     ++n;
1308     }
1309     }
1310     }
1311     rbf->vtotal = miga[0]->rbfv[0]->vtotal * (mbfact + mcfact);
1312 greg 2.10 return(rbf);
1313 greg 2.8 }
1314 greg 2.1
1315 greg 2.12 /* Interpolate and output isotropic BSDF data */
1316     static void
1317     interp_isotropic()
1318     {
1319     const int sqres = 1<<samp_order;
1320     FILE *ofp = NULL;
1321     char cmd[128];
1322     int ix, ox, oy;
1323     FVECT ivec, ovec;
1324     double bsdf;
1325 greg 2.13 #if DEBUG
1326     fprintf(stderr, "Writing isotropic order %d ", samp_order);
1327     if (pctcull >= 0) fprintf(stderr, "data with %d%% culling\n", pctcull);
1328     else fputs("raw data\n", stderr);
1329     #endif
1330 greg 2.12 if (pctcull >= 0) { /* begin output */
1331     sprintf(cmd, "rttree_reduce -h -a -fd -r 3 -t %d -g %d",
1332     pctcull, samp_order);
1333     fflush(stdout);
1334     ofp = popen(cmd, "w");
1335     if (ofp == NULL) {
1336     fputs("Cannot create pipe for rttree_reduce\n", stderr);
1337     exit(1);
1338     }
1339     } else
1340     fputs("{\n", stdout);
1341     /* run through directions */
1342     for (ix = 0; ix < sqres/2; ix++) {
1343     RBFNODE *rbf;
1344     SDsquare2disk(ivec, (ix+.5)/sqres, .5);
1345     ivec[2] = input_orient *
1346     sqrt(1. - ivec[0]*ivec[0] - ivec[1]*ivec[1]);
1347     rbf = advect_rbf(ivec);
1348     for (ox = 0; ox < sqres; ox++)
1349     for (oy = 0; oy < sqres; oy++) {
1350     SDsquare2disk(ovec, (ox+.5)/sqres, (oy+.5)/sqres);
1351     ovec[2] = output_orient *
1352     sqrt(1. - ovec[0]*ovec[0] - ovec[1]*ovec[1]);
1353     bsdf = eval_rbfrep(rbf, ovec) / fabs(ovec[2]);
1354     if (pctcull >= 0)
1355     fwrite(&bsdf, sizeof(bsdf), 1, ofp);
1356     else
1357     printf("\t%.3e\n", bsdf);
1358     }
1359     free(rbf);
1360     }
1361     if (pctcull >= 0) { /* finish output */
1362     if (pclose(ofp)) {
1363     fprintf(stderr, "Error running '%s'\n", cmd);
1364     exit(1);
1365     }
1366     } else {
1367     for (ix = sqres*sqres*sqres/2; ix--; )
1368     fputs("\t0\n", stdout);
1369     fputs("}\n", stdout);
1370     }
1371     }
1372    
1373     /* Interpolate and output anisotropic BSDF data */
1374     static void
1375     interp_anisotropic()
1376     {
1377     const int sqres = 1<<samp_order;
1378     FILE *ofp = NULL;
1379     char cmd[128];
1380     int ix, iy, ox, oy;
1381     FVECT ivec, ovec;
1382     double bsdf;
1383 greg 2.13 #if DEBUG
1384     fprintf(stderr, "Writing anisotropic order %d ", samp_order);
1385     if (pctcull >= 0) fprintf(stderr, "data with %d%% culling\n", pctcull);
1386     else fputs("raw data\n", stderr);
1387     #endif
1388 greg 2.12 if (pctcull >= 0) { /* begin output */
1389     sprintf(cmd, "rttree_reduce -h -a -fd -r 4 -t %d -g %d",
1390     pctcull, samp_order);
1391     fflush(stdout);
1392     ofp = popen(cmd, "w");
1393     if (ofp == NULL) {
1394     fputs("Cannot create pipe for rttree_reduce\n", stderr);
1395     exit(1);
1396     }
1397     } else
1398     fputs("{\n", stdout);
1399     /* run through directions */
1400     for (ix = 0; ix < sqres; ix++)
1401     for (iy = 0; iy < sqres; iy++) {
1402     RBFNODE *rbf;
1403     SDsquare2disk(ivec, (ix+.5)/sqres, (iy+.5)/sqres);
1404     ivec[2] = input_orient *
1405     sqrt(1. - ivec[0]*ivec[0] - ivec[1]*ivec[1]);
1406     rbf = advect_rbf(ivec);
1407     for (ox = 0; ox < sqres; ox++)
1408     for (oy = 0; oy < sqres; oy++) {
1409     SDsquare2disk(ovec, (ox+.5)/sqres, (oy+.5)/sqres);
1410     ovec[2] = output_orient *
1411     sqrt(1. - ovec[0]*ovec[0] - ovec[1]*ovec[1]);
1412     bsdf = eval_rbfrep(rbf, ovec) / fabs(ovec[2]);
1413     if (pctcull >= 0)
1414     fwrite(&bsdf, sizeof(bsdf), 1, ofp);
1415     else
1416     printf("\t%.3e\n", bsdf);
1417     }
1418     free(rbf);
1419     }
1420     if (pctcull >= 0) { /* finish output */
1421     if (pclose(ofp)) {
1422     fprintf(stderr, "Error running '%s'\n", cmd);
1423     exit(1);
1424     }
1425     } else
1426     fputs("}\n", stdout);
1427     }
1428    
1429 greg 2.1 #if 1
1430 greg 2.12 /* Read in BSDF files and interpolate as tensor tree representation */
1431     int
1432     main(int argc, char *argv[])
1433     {
1434     RBFNODE *rbf;
1435     double bsdf;
1436     int i;
1437    
1438     progname = argv[0];
1439     if (argc > 2 && !strcmp(argv[1], "-t")) {
1440     pctcull = atoi(argv[2]);
1441     argv += 2; argc -= 2;
1442     }
1443     if (argc < 3) {
1444     fprintf(stderr,
1445     "Usage: %s [-t pctcull] meas1.dat meas2.dat .. > bsdf.xml\n",
1446     progname);
1447     return(1);
1448     }
1449     for (i = 1; i < argc; i++) { /* compile measurements */
1450     if (!load_pabopto_meas(argv[i]))
1451     return(1);
1452     compute_radii();
1453     cull_values();
1454     make_rbfrep();
1455     }
1456     build_mesh(); /* create interpolation */
1457     /* xml_prologue(); /* start XML output */
1458     if (single_plane_incident) /* resample dist. */
1459     interp_isotropic();
1460     else
1461     interp_anisotropic();
1462     /* xml_epilogue(); /* finish XML output */
1463     return(0);
1464     }
1465     #else
1466 greg 2.1 /* Test main produces a Radiance model from the given input file */
1467     int
1468     main(int argc, char *argv[])
1469     {
1470     char buf[128];
1471     FILE *pfp;
1472     double bsdf;
1473     FVECT dir;
1474     int i, j, n;
1475    
1476     if (argc != 2) {
1477     fprintf(stderr, "Usage: %s input.dat > output.rad\n", argv[0]);
1478     return(1);
1479     }
1480 greg 2.10 if (!load_pabopto_meas(argv[1]))
1481 greg 2.1 return(1);
1482    
1483     compute_radii();
1484     cull_values();
1485 greg 2.3 make_rbfrep();
1486     /* produce spheres at meas. */
1487     puts("void plastic yellow\n0\n0\n5 .6 .4 .01 .04 .08\n");
1488 greg 2.1 puts("void plastic pink\n0\n0\n5 .5 .05 .9 .04 .08\n");
1489     n = 0;
1490     for (i = 0; i < GRIDRES; i++)
1491     for (j = 0; j < GRIDRES; j++)
1492 greg 2.5 if (dsf_grid[i][j].vsum > .0f) {
1493 greg 2.10 ovec_from_pos(dir, i, j);
1494 greg 2.5 bsdf = dsf_grid[i][j].vsum / dir[2];
1495     if (dsf_grid[i][j].nval) {
1496 greg 2.3 printf("pink cone c%04d\n0\n0\n8\n", ++n);
1497     printf("\t%.6g %.6g %.6g\n",
1498 greg 2.1 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
1499 greg 2.3 printf("\t%.6g %.6g %.6g\n",
1500 greg 2.1 dir[0]*(bsdf+.005), dir[1]*(bsdf+.005),
1501     dir[2]*(bsdf+.005));
1502 greg 2.3 puts("\t.003\t0\n");
1503     } else {
1504 greg 2.10 ovec_from_pos(dir, i, j);
1505 greg 2.3 printf("yellow sphere s%04d\n0\n0\n", ++n);
1506     printf("4 %.6g %.6g %.6g .0015\n\n",
1507     dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
1508     }
1509 greg 2.1 }
1510     /* output continuous surface */
1511     puts("void trans tgreen\n0\n0\n7 .7 1 .7 .04 .04 .9 .9\n");
1512     fflush(stdout);
1513 greg 2.5 sprintf(buf, "gensurf tgreen bsdf - - - %d %d", GRIDRES-1, GRIDRES-1);
1514 greg 2.1 pfp = popen(buf, "w");
1515     if (pfp == NULL) {
1516     fputs(buf, stderr);
1517     fputs(": cannot start command\n", stderr);
1518     return(1);
1519     }
1520     for (i = 0; i < GRIDRES; i++)
1521     for (j = 0; j < GRIDRES; j++) {
1522 greg 2.10 ovec_from_pos(dir, i, j);
1523 greg 2.5 bsdf = eval_rbfrep(dsf_list, dir) / dir[2];
1524 greg 2.1 fprintf(pfp, "%.8e %.8e %.8e\n",
1525     dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
1526     }
1527     return(pclose(pfp)==0 ? 0 : 1);
1528     }
1529     #endif