ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
Revision: 2.18
Committed: Wed Oct 17 19:01:47 2012 UTC (12 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.17: +17 -17 lines
Log Message:
Put upper limit on output resolution and other minor changes

File Contents

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