ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
Revision: 2.19
Committed: Thu Oct 18 04:28:20 2012 UTC (11 years, 7 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.18: +179 -35 lines
Log Message:
Added symmetry handling (still produces 0 output)

File Contents

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