ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
(Generate patch)

Comparing ray/src/cv/pabopto2xml.c (file contents):
Revision 2.7 by greg, Sun Sep 2 15:33:15 2012 UTC vs.
Revision 2.17 by greg, Tue Oct 16 22:00:51 2012 UTC

# Line 8 | Line 8 | static const char RCSid[] = "$Id$";
8   *      G.Ward
9   */
10  
11 + #ifndef _WIN32
12 + #include <unistd.h>
13 + #include <sys/wait.h>
14 + #include <sys/mman.h>
15 + #endif
16   #define _USE_MATH_DEFINES
17   #include <stdio.h>
18   #include <stdlib.h>
# Line 16 | Line 21 | static const char RCSid[] = "$Id$";
21   #include <math.h>
22   #include "bsdf.h"
23  
24 + #define DEBUG           1
25 +
26   #ifndef GRIDRES
27 < #define GRIDRES         200             /* max. grid resolution per side */
27 > #define GRIDRES         200             /* grid resolution per side */
28   #endif
29  
30   #define RSCA            2.7             /* radius scaling factor (empirical) */
# Line 51 | Line 58 | typedef struct s_rbfnode {
58          struct s_rbfnode        *next;          /* next in global RBF list */
59          MIGRATION               *ejl;           /* edge list for this vertex */
60          FVECT                   invec;          /* incident vector direction */
61 +        double                  vtotal;         /* volume for normalization */
62          int                     nrbf;           /* number of RBFs */
63          RBFVAL                  rbfa[1];        /* RBF array (extends struct) */
64 < } RBFLIST;                      /* RBF representation of DSF @ 1 incidence */
64 > } RBFNODE;                      /* RBF representation of DSF @ 1 incidence */
65  
66                                  /* our loaded grid for this incident angle */
67 < static double   theta_in_deg, phi_in_deg;
68 < static GRIDVAL  dsf_grid[GRIDRES][GRIDRES];
67 > static double           theta_in_deg, phi_in_deg;
68 > static GRIDVAL          dsf_grid[GRIDRES][GRIDRES];
69  
70 +                                /* all incident angles in-plane so far? */
71 + static int              single_plane_incident = -1;
72 +
73 +                                /* input/output orientations */
74 + static int              input_orient = 0;
75 + static int              output_orient = 0;
76 +
77                                  /* processed incident DSF measurements */
78 < static RBFLIST          *dsf_list = NULL;
78 > static RBFNODE          *dsf_list = NULL;
79  
80 <                                /* edge (linking) matrices */
80 >                                /* RBF-linking matrices (edges) */
81   static MIGRATION        *mig_list = NULL;
82  
83 < #define mtxval(m,i,j)   (m)->mtx[(i)*(m)->rbfv[1]->nrbf+(j)]
84 < #define nextedge(rbf,m) (m)->enxt[(rbf)==(m)->rbfv[1]]
83 >                                /* migration edges drawn in raster fashion */
84 > static MIGRATION        *mig_grid[GRIDRES][GRIDRES];
85  
86 + #define mtx_nrows(m)    ((m)->rbfv[0]->nrbf)
87 + #define mtx_ncols(m)    ((m)->rbfv[1]->nrbf)
88 + #define mtx_ndx(m,i,j)  ((i)*mtx_ncols(m) + (j))
89 + #define is_src(rbf,m)   ((rbf) == (m)->rbfv[0])
90 + #define is_dest(rbf,m)  ((rbf) == (m)->rbfv[1])
91 + #define nextedge(rbf,m) (m)->enxt[is_dest(rbf,m)]
92 + #define opp_rbf(rbf,m)  (m)->rbfv[is_src(rbf,m)]
93 +
94 + #define round(v)        (int)((v) + .5 - ((v) < -.5))
95 +
96 + char                    *progname;
97 +
98 + #ifdef DEBUG                    /* percentage to cull (<0 to turn off) */
99 + int                     pctcull = -1;
100 + #else
101 + int                     pctcull = 90;
102 + #endif
103 +                                /* number of processes to run */
104 + int                     nprocs = 1;
105 +                                /* number of children (-1 in child) */
106 + int                     nchild = 0;
107 +
108 +                                /* sampling order (set by data density) */
109 + int                     samp_order = 0;
110 +
111 + /* 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 +
120   /* Compute outgoing vector from grid position */
121   static void
122 < vec_from_pos(FVECT vec, int xpos, int ypos)
122 > ovec_from_pos(FVECT vec, int xpos, int ypos)
123   {
124          double  uv[2];
125          double  r2;
# Line 81 | Line 130 | vec_from_pos(FVECT vec, int xpos, int ypos)
130          vec[0] = vec[1] = sqrt(2. - r2);
131          vec[0] *= uv[0];
132          vec[1] *= uv[1];
133 <        vec[2] = 1. - r2;
133 >        vec[2] = output_orient*(1. - r2);
134   }
135  
136 < /* Compute grid position from normalized outgoing vector */
136 > /* Compute grid position from normalized input/output vector */
137   static void
138   pos_from_vec(int pos[2], const FVECT vec)
139   {
140          double  sq[2];          /* uniform hemispherical projection */
141 <        double  norm = 1./sqrt(1. + vec[2]);
141 >        double  norm = 1./sqrt(1. + fabs(vec[2]));
142  
143          SDdisk2square(sq, vec[0]*norm, vec[1]*norm);
144  
# Line 99 | Line 148 | pos_from_vec(int pos[2], const FVECT vec)
148  
149   /* Evaluate RBF for DSF at the given normalized outgoing direction */
150   static double
151 < eval_rbfrep(const RBFLIST *rp, const FVECT outvec)
151 > eval_rbfrep(const RBFNODE *rp, const FVECT outvec)
152   {
153          double          res = .0;
154          const RBFVAL    *rbfp;
# Line 107 | Line 156 | eval_rbfrep(const RBFLIST *rp, const FVECT outvec)
156          double          sig2;
157          int             n;
158  
159 +        if (rp == NULL)
160 +                return(.0);
161          rbfp = rp->rbfa;
162          for (n = rp->nrbf; n--; rbfp++) {
163 <                vec_from_pos(odir, rbfp->gx, rbfp->gy);
163 >                ovec_from_pos(odir, rbfp->gx, rbfp->gy);
164                  sig2 = R2ANG(rbfp->crad);
165                  sig2 = (DOT(odir,outvec) - 1.) / (sig2*sig2);
166                  if (sig2 > -19.)
# Line 118 | Line 169 | eval_rbfrep(const RBFLIST *rp, const FVECT outvec)
169          return(res);
170   }
171  
172 + /* 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 +                                        /* 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 +                                        /* 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   /* Count up filled nodes and build RBF representation from current grid */
200 < static RBFLIST *
200 > static RBFNODE *
201   make_rbfrep(void)
202   {
203          int     niter = 16;
204 +        int     minrad = ANG2R(pow(2., 1.-samp_order));
205          double  lastVar, thisVar = 100.;
206          int     nn;
207 <        RBFLIST *newnode;
207 >        RBFNODE *newnode;
208          int     i, j;
209          
210          nn = 0;                 /* count selected bins */
# Line 133 | Line 212 | make_rbfrep(void)
212              for (j = 0; j < GRIDRES; j++)
213                  nn += dsf_grid[i][j].nval;
214                                  /* allocate RBF array */
215 <        newnode = (RBFLIST *)malloc(sizeof(RBFLIST) + sizeof(RBFVAL)*(nn-1));
215 >        newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
216          if (newnode == NULL) {
217 <                fputs("Out of memory in make_rbfrep\n", stderr);
217 >                fputs("Out of memory in make_rbfrep()\n", stderr);
218                  exit(1);
219          }
220          newnode->next = NULL;
# Line 143 | Line 222 | make_rbfrep(void)
222          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 <        newnode->invec[2] = sqrt(1. - newnode->invec[2]*newnode->invec[2]);
225 >        newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
226 >        newnode->vtotal = 0;
227          newnode->nrbf = nn;
228          nn = 0;                 /* fill RBF array */
229          for (i = 0; i < GRIDRES; i++)
# Line 153 | Line 233 | make_rbfrep(void)
233                          newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5;
234                          newnode->rbfa[nn].gx = i;
235                          newnode->rbfa[nn].gy = j;
236 +                        if (newnode->rbfa[nn].crad < minrad)
237 +                                minrad = newnode->rbfa[nn].crad;
238                          ++nn;
239                  }
240                                  /* iterate to improve interpolation accuracy */
241          do {
242 <                double  dsum = .0, dsum2 = .0;
242 >                double  dsum = 0, dsum2 = 0;
243                  nn = 0;
244                  for (i = 0; i < GRIDRES; i++)
245                      for (j = 0; j < GRIDRES; j++)
246                          if (dsf_grid[i][j].nval) {
247                                  FVECT   odir;
248                                  double  corr;
249 <                                vec_from_pos(odir, i, j);
249 >                                ovec_from_pos(odir, i, j);
250                                  newnode->rbfa[nn++].peak *= corr =
251                                          dsf_grid[i][j].vsum /
252                                                  eval_rbfrep(newnode, odir);
# Line 173 | Line 255 | make_rbfrep(void)
255                          }
256                  lastVar = thisVar;
257                  thisVar = dsum2/(double)nn;
258 <                /*
258 > #ifdef DEBUG
259                  fprintf(stderr, "Avg., RMS error: %.1f%%  %.1f%%\n",
260                                          100.*dsum/(double)nn,
261                                          100.*sqrt(thisVar));
262 <                */
262 > #endif
263          } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar);
264  
265 <        newnode->next = dsf_list;
266 <        return(dsf_list = newnode);
265 >        nn = 0;                 /* compute sum for normalization */
266 >        while (nn < newnode->nrbf)
267 >                newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
268 >
269 >        insert_dsf(newnode);
270 >                                /* adjust sampling resolution */
271 >        samp_order = log(2./R2ANG(minrad))/M_LN2 + .5;
272 >
273 >        return(newnode);
274   }
275  
276   /* Load a set of measurements corresponding to a particular incident angle */
277   static int
278 < load_bsdf_meas(const char *fname)
278 > load_pabopto_meas(const char *fname)
279   {
280          FILE    *fp = fopen(fname, "r");
281          int     inp_is_DSF = -1;
282 <        double  theta_out, phi_out, val;
282 >        double  new_phi, theta_out, phi_out, val;
283          char    buf[2048];
284          int     n, c;
285          
# Line 200 | Line 289 | load_bsdf_meas(const char *fname)
289                  return(0);
290          }
291          memset(dsf_grid, 0, sizeof(dsf_grid));
292 + #ifdef DEBUG
293 +        fprintf(stderr, "Loading measurement file '%s'...\n", fname);
294 + #endif
295                                  /* read header information */
296          while ((c = getc(fp)) == '#' || c == EOF) {
297                  if (fgets(buf, sizeof(buf), fp) == NULL) {
# Line 218 | Line 310 | load_bsdf_meas(const char *fname)
310                  }
311                  if (sscanf(buf, "intheta %lf", &theta_in_deg) == 1)
312                          continue;
313 <                if (sscanf(buf, "inphi %lf", &phi_in_deg) == 1)
313 >                if (sscanf(buf, "inphi %lf", &new_phi) == 1)
314                          continue;
315                  if (sscanf(buf, "incident_angle %lf %lf",
316 <                                &theta_in_deg, &phi_in_deg) == 2)
316 >                                &theta_in_deg, &new_phi) == 2)
317                          continue;
318          }
319          if (inp_is_DSF < 0) {
# Line 230 | Line 322 | load_bsdf_meas(const char *fname)
322                  fclose(fp);
323                  return(0);
324          }
325 <        ungetc(c, fp);          /* read actual data */
325 >        if (!input_orient)              /* check input orientation */
326 >                input_orient = 1 - 2*(theta_in_deg > 90.);
327 >        else if (input_orient > 0 ^ theta_in_deg < 90.) {
328 >                fputs("Cannot handle input angles on both sides of surface\n",
329 >                                stderr);
330 >                exit(1);
331 >        }
332 >        if (single_plane_incident > 0)  /* check if still in plane */
333 >                single_plane_incident = (round(new_phi) == round(phi_in_deg));
334 >        else if (single_plane_incident < 0)
335 >                single_plane_incident = 1;
336 >        phi_in_deg = new_phi;
337 >        ungetc(c, fp);                  /* read actual data */
338          while (fscanf(fp, "%lf %lf %lf\n", &theta_out, &phi_out, &val) == 3) {
339                  FVECT   ovec;
340                  int     pos[2];
341  
342 +                if (!output_orient)     /* check output orientation */
343 +                        output_orient = 1 - 2*(theta_out > 90.);
344 +                else if (output_orient > 0 ^ theta_out < 90.) {
345 +                        fputs("Cannot handle output angles on both sides of surface\n",
346 +                                        stderr);
347 +                        exit(1);
348 +                }
349                  ovec[2] = sin(M_PI/180.*theta_out);
350                  ovec[0] = cos(M_PI/180.*phi_out) * ovec[2];
351                  ovec[1] = sin(M_PI/180.*phi_out) * ovec[2];
# Line 276 | Line 387 | compute_radii(void)
387                  j = (i&1) ? jn : GRIDRES-1-jn;
388                  if (dsf_grid[i][j].nval)        /* find empty grid pos. */
389                          continue;
390 <                vec_from_pos(ovec0, i, j);
390 >                ovec_from_pos(ovec0, i, j);
391                  inear = jnear = -1;             /* find nearest non-empty */
392                  lastang2 = M_PI*M_PI;
393                  for (ii = i-r; ii <= i+r; ii++) {
# Line 287 | Line 398 | compute_radii(void)
398                          if (jj >= GRIDRES) break;
399                          if (!dsf_grid[ii][jj].nval)
400                                  continue;
401 <                        vec_from_pos(ovec1, ii, jj);
401 >                        ovec_from_pos(ovec1, ii, jj);
402                          ang2 = 2. - 2.*DOT(ovec0,ovec1);
403                          if (ang2 >= lastang2)
404                                  continue;
# Line 304 | Line 415 | compute_radii(void)
415                  if (r > dsf_grid[inear][jnear].crad)
416                          dsf_grid[inear][jnear].crad = r;
417                                                  /* next search radius */
418 <                r = ang2*(2.*GRIDRES/M_PI) + 1;
418 >                r = ang2*(2.*GRIDRES/M_PI) + 3;
419              }
420                                                  /* blur radii over hemisphere */
421          memset(fill_grid, 0, sizeof(fill_grid));
# Line 348 | Line 459 | cull_values(void)
459                          continue;
460                  if (!dsf_grid[i][j].crad)
461                          continue;               /* shouldn't happen */
462 <                vec_from_pos(ovec0, i, j);
462 >                ovec_from_pos(ovec0, i, j);
463                  maxang = 2.*R2ANG(dsf_grid[i][j].crad);
464                  if (maxang > ovec0[2])          /* clamp near horizon */
465                          maxang = ovec0[2];
# Line 364 | Line 475 | cull_values(void)
475                                  continue;
476                          if ((ii == i) & (jj == j))
477                                  continue;       /* don't get self-absorbed */
478 <                        vec_from_pos(ovec1, ii, jj);
478 >                        ovec_from_pos(ovec1, ii, jj);
479                          if (2. - 2.*DOT(ovec0,ovec1) >= maxang2)
480                                  continue;
481                                                  /* absorb sum */
# Line 385 | Line 496 | cull_values(void)
496                  }
497   }
498  
499 + /* Compute (and allocate) migration price matrix for optimization */
500 + static float *
501 + price_routes(const RBFNODE *from_rbf, const RBFNODE *to_rbf)
502 + {
503 +        float   *pmtx = (float *)malloc(sizeof(float) *
504 +                                        from_rbf->nrbf * to_rbf->nrbf);
505 +        FVECT   *vto = (FVECT *)malloc(sizeof(FVECT) * to_rbf->nrbf);
506 +        int     i, j;
507  
508 +        if ((pmtx == NULL) | (vto == NULL)) {
509 +                fputs("Out of memory in migration_costs()\n", stderr);
510 +                exit(1);
511 +        }
512 +        for (j = to_rbf->nrbf; j--; )           /* save repetitive ops. */
513 +                ovec_from_pos(vto[j], to_rbf->rbfa[j].gx, to_rbf->rbfa[j].gy);
514 +
515 +        for (i = from_rbf->nrbf; i--; ) {
516 +            const double        from_ang = R2ANG(from_rbf->rbfa[i].crad);
517 +            FVECT               vfrom;
518 +            ovec_from_pos(vfrom, from_rbf->rbfa[i].gx, from_rbf->rbfa[i].gy);
519 +            for (j = to_rbf->nrbf; j--; )
520 +                pmtx[i*to_rbf->nrbf + j] = acos(DOT(vfrom, vto[j])) +
521 +                                fabs(R2ANG(to_rbf->rbfa[j].crad) - from_ang);
522 +        }
523 +        free(vto);
524 +        return(pmtx);
525 + }
526 +
527 + /* Comparison routine needed for sorting price row */
528 + static const float      *price_arr;
529 + static int
530 + msrt_cmp(const void *p1, const void *p2)
531 + {
532 +        float   c1 = price_arr[*(const int *)p1];
533 +        float   c2 = price_arr[*(const int *)p2];
534 +
535 +        if (c1 > c2) return(1);
536 +        if (c1 < c2) return(-1);
537 +        return(0);
538 + }
539 +
540 + /* Compute minimum (optimistic) cost for moving the given source material */
541 + static double
542 + min_cost(double amt2move, const double *avail, const float *price, int n)
543 + {
544 +        static int      *price_sort = NULL;
545 +        static int      n_alloc = 0;
546 +        double          total_cost = 0;
547 +        int             i;
548 +
549 +        if (amt2move <= FTINY)                  /* pre-emptive check */
550 +                return(0.);
551 +        if (n > n_alloc) {                      /* (re)allocate sort array */
552 +                if (n_alloc) free(price_sort);
553 +                price_sort = (int *)malloc(sizeof(int)*n);
554 +                if (price_sort == NULL) {
555 +                        fputs("Out of memory in min_cost()\n", stderr);
556 +                        exit(1);
557 +                }
558 +                n_alloc = n;
559 +        }
560 +        for (i = n; i--; )
561 +                price_sort[i] = i;
562 +        price_arr = price;
563 +        qsort(price_sort, n, sizeof(int), &msrt_cmp);
564 +                                                /* move cheapest first */
565 +        for (i = 0; i < n && amt2move > FTINY; i++) {
566 +                int     d = price_sort[i];
567 +                double  amt = (amt2move < avail[d]) ? amt2move : avail[d];
568 +
569 +                total_cost += amt * price[d];
570 +                amt2move -= amt;
571 +        }
572 +        return(total_cost);
573 + }
574 +
575 + /* Take a step in migration by choosing optimal bucket to transfer */
576 + static double
577 + migration_step(MIGRATION *mig, double *src_rem, double *dst_rem, const float *pmtx)
578 + {
579 +        static double   *src_cost = NULL;
580 +        static int      n_alloc = 0;
581 +        const double    maxamt = .1; /* 2./(mtx_nrows(mig)*mtx_ncols(mig)); */
582 +        double          amt = 0;
583 +        struct {
584 +                int     s, d;   /* source and destination */
585 +                double  price;  /* price estimate per amount moved */
586 +                double  amt;    /* amount we can move */
587 +        } cur, best;
588 +        int             i;
589 +
590 +        if (mtx_nrows(mig) > n_alloc) {         /* allocate cost array */
591 +                if (n_alloc)
592 +                        free(src_cost);
593 +                src_cost = (double *)malloc(sizeof(double)*mtx_nrows(mig));
594 +                if (src_cost == NULL) {
595 +                        fputs("Out of memory in migration_step()\n", stderr);
596 +                        exit(1);
597 +                }
598 +                n_alloc = mtx_nrows(mig);
599 +        }
600 +        for (i = mtx_nrows(mig); i--; )         /* starting costs for diff. */
601 +                src_cost[i] = min_cost(src_rem[i], dst_rem,
602 +                                        pmtx+i*mtx_ncols(mig), mtx_ncols(mig));
603 +
604 +                                                /* find best source & dest. */
605 +        best.s = best.d = -1; best.price = FHUGE; best.amt = 0;
606 +        for (cur.s = mtx_nrows(mig); cur.s--; ) {
607 +            const float *price = pmtx + cur.s*mtx_ncols(mig);
608 +            double      cost_others = 0;
609 +            if (src_rem[cur.s] <= FTINY)
610 +                    continue;
611 +            cur.d = -1;                         /* examine cheapest dest. */
612 +            for (i = mtx_ncols(mig); i--; )
613 +                if (dst_rem[i] > FTINY &&
614 +                                (cur.d < 0 || price[i] < price[cur.d]))
615 +                        cur.d = i;
616 +            if (cur.d < 0)
617 +                    return(.0);
618 +            if ((cur.price = price[cur.d]) >= best.price)
619 +                    continue;                   /* no point checking further */
620 +            cur.amt = (src_rem[cur.s] < dst_rem[cur.d]) ?
621 +                                src_rem[cur.s] : dst_rem[cur.d];
622 +            if (cur.amt > maxamt) cur.amt = maxamt;
623 +            dst_rem[cur.d] -= cur.amt;          /* add up differential costs */
624 +            for (i = mtx_nrows(mig); i--; ) {
625 +                if (i == cur.s) continue;
626 +                cost_others += min_cost(src_rem[i], dst_rem, price, mtx_ncols(mig))
627 +                                        - src_cost[i];
628 +            }
629 +            dst_rem[cur.d] += cur.amt;          /* undo trial move */
630 +            cur.price += cost_others/cur.amt;   /* adjust effective price */
631 +            if (cur.price < best.price)         /* are we better than best? */
632 +                    best = cur;
633 +        }
634 +        if ((best.s < 0) | (best.d < 0))
635 +                return(.0);
636 +                                                /* make the actual move */
637 +        mig->mtx[mtx_ndx(mig,best.s,best.d)] += best.amt;
638 +        src_rem[best.s] -= best.amt;
639 +        dst_rem[best.d] -= best.amt;
640 +        return(best.amt);
641 + }
642 +
643 + #ifdef DEBUG
644 + static char *
645 + thetaphi(const FVECT v)
646 + {
647 +        static char     buf[128];
648 +        double          theta, phi;
649 +
650 +        theta = 180./M_PI*acos(v[2]);
651 +        phi = 180./M_PI*atan2(v[1],v[0]);
652 +        sprintf(buf, "(%.0f,%.0f)", theta, phi);
653 +
654 +        return(buf);
655 + }
656 + #endif
657 +
658 + /* Create a new migration holder (sharing memory for multiprocessing) */
659 + static MIGRATION *
660 + new_migration(RBFNODE *from_rbf, RBFNODE *to_rbf)
661 + {
662 +        size_t          memlen = sizeof(MIGRATION) +
663 +                                sizeof(float)*(from_rbf->nrbf*to_rbf->nrbf - 1);
664 +        MIGRATION       *newmig;
665 + #ifdef _WIN32
666 +        newmig = (MIGRATION *)malloc(memlen);
667 + #else
668 +        if (nprocs <= 1) {                      /* single process? */
669 +                newmig = (MIGRATION *)malloc(memlen);
670 +        } else {                                /* else need to share memory */
671 +                newmig = (MIGRATION *)mmap(NULL, memlen, PROT_READ|PROT_WRITE,
672 +                                                MAP_ANON|MAP_SHARED, -1, 0);
673 +                if ((void *)newmig == MAP_FAILED)
674 +                        newmig = NULL;
675 +        }
676 + #endif
677 +        if (newmig == NULL) {
678 +                fprintf(stderr, "%s: cannot allocate new migration\n", progname);
679 +                exit(1);
680 +        }
681 +        newmig->rbfv[0] = from_rbf;
682 +        newmig->rbfv[1] = to_rbf;
683 +                                                /* insert in edge lists */
684 +        newmig->enxt[0] = from_rbf->ejl;
685 +        from_rbf->ejl = newmig;
686 +        newmig->enxt[1] = to_rbf->ejl;
687 +        to_rbf->ejl = newmig;
688 +        newmig->next = mig_list;                /* push onto global list */
689 +        return(mig_list = newmig);
690 + }
691 +
692 + #ifdef _WIN32
693 + #define await_children(n)       (void)(n)
694 + #define run_subprocess()        0
695 + #define end_subprocess()        (void)0
696 + #else
697 +
698 + /* Wait for the specified number of child processes to complete */
699 + static void
700 + await_children(int n)
701 + {
702 +        int     exit_status = 0;
703 +
704 +        if (n > nchild)
705 +                n = nchild;
706 +        while (n-- > 0) {
707 +                int     status;
708 +                if (wait(&status) < 0) {
709 +                        fprintf(stderr, "%s: missing child(ren)!\n", progname);
710 +                        nchild = 0;
711 +                        break;
712 +                }
713 +                --nchild;
714 +                if (status) {                   /* something wrong */
715 +                        if ((status = WEXITSTATUS(status)))
716 +                                exit_status = status;
717 +                        else
718 +                                exit_status += !exit_status;
719 +                        fprintf(stderr, "%s: subprocess died\n", progname);
720 +                        n = nchild;             /* wait for the rest */
721 +                }
722 +        }
723 +        if (exit_status)
724 +                exit(exit_status);
725 + }
726 +
727 + /* Start child process if multiprocessing selected */
728 + static pid_t
729 + run_subprocess(void)
730 + {
731 +        int     status;
732 +        pid_t   pid;
733 +
734 +        if (nprocs <= 1)                        /* any children requested? */
735 +                return(0);
736 +        await_children(nchild + 1 - nprocs);    /* free up child process */
737 +        if ((pid = fork())) {
738 +                if (pid < 0) {
739 +                        fprintf(stderr, "%s: cannot fork subprocess\n",
740 +                                        progname);
741 +                        exit(1);
742 +                }
743 +                ++nchild;                       /* subprocess started */
744 +                return(pid);
745 +        }
746 +        nchild = -1;
747 +        return(0);                              /* put child to work */
748 + }
749 +
750 + /* If we are in subprocess, call exit */
751 + #define end_subprocess()        if (nchild < 0) _exit(0); else
752 +
753 + #endif  /* ! _WIN32 */
754 +
755 + /* Compute and insert migration along directed edge (may fork child) */
756 + static MIGRATION *
757 + create_migration(RBFNODE *from_rbf, RBFNODE *to_rbf)
758 + {
759 +        const double    end_thresh = 0.1/(from_rbf->nrbf*to_rbf->nrbf);
760 +        const double    rel_thresh = 0.0001;
761 +        float           *pmtx;
762 +        MIGRATION       *newmig;
763 +        double          *src_rem, *dst_rem;
764 +        double          total_rem = 1., move_amt;
765 +        int             i;
766 +                                                /* check if exists already */
767 +        for (newmig = from_rbf->ejl; newmig != NULL;
768 +                        newmig = nextedge(from_rbf,newmig))
769 +                if (newmig->rbfv[1] == to_rbf)
770 +                        return(NULL);
771 +                                                /* else allocate */
772 +        newmig = new_migration(from_rbf, to_rbf);
773 +        if (run_subprocess())
774 +                return(newmig);                 /* child continues */
775 +        pmtx = price_routes(from_rbf, to_rbf);
776 +        src_rem = (double *)malloc(sizeof(double)*from_rbf->nrbf);
777 +        dst_rem = (double *)malloc(sizeof(double)*to_rbf->nrbf);
778 +        if ((src_rem == NULL) | (dst_rem == NULL)) {
779 +                fputs("Out of memory in create_migration()\n", stderr);
780 +                exit(1);
781 +        }
782 + #ifdef DEBUG
783 +        fprintf(stderr, "Building path from (theta,phi) %s ",
784 +                        thetaphi(from_rbf->invec));
785 +        fprintf(stderr, "to %s", thetaphi(to_rbf->invec));
786 +        /* if (nchild) */ fputc('\n', stderr);
787 + #endif
788 +                                                /* starting quantities */
789 +        memset(newmig->mtx, 0, sizeof(float)*from_rbf->nrbf*to_rbf->nrbf);
790 +        for (i = from_rbf->nrbf; i--; )
791 +                src_rem[i] = rbf_volume(&from_rbf->rbfa[i]) / from_rbf->vtotal;
792 +        for (i = to_rbf->nrbf; i--; )
793 +                dst_rem[i] = rbf_volume(&to_rbf->rbfa[i]) / to_rbf->vtotal;
794 +        do {                                    /* move a bit at a time */
795 +                move_amt = migration_step(newmig, src_rem, dst_rem, pmtx);
796 +                total_rem -= move_amt;
797 + #ifdef DEBUG
798 +                if (!nchild)
799 +                        /* fputc('.', stderr); */
800 +                        fprintf(stderr, "%.9f remaining...\r", total_rem);
801 + #endif
802 +        } while ((total_rem > end_thresh) & (move_amt > rel_thresh*total_rem));
803 + #ifdef DEBUG
804 +        if (!nchild) fputs("\ndone.\n", stderr);
805 + #endif
806 +
807 +        free(pmtx);                             /* free working arrays */
808 +        free(src_rem);
809 +        free(dst_rem);
810 +        for (i = from_rbf->nrbf; i--; ) {       /* normalize final matrix */
811 +            float       nf = rbf_volume(&from_rbf->rbfa[i]);
812 +            int         j;
813 +            if (nf <= FTINY) continue;
814 +            nf = from_rbf->vtotal / nf;
815 +            for (j = to_rbf->nrbf; j--; )
816 +                newmig->mtx[mtx_ndx(newmig,i,j)] *= nf;
817 +        }
818 +        end_subprocess();                       /* exit here if subprocess */
819 +        return(newmig);
820 + }
821 +
822 + /* 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 +        if (mig == NULL)
853 +                return(0);
854 +        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 + /* 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 +                                                /* find shared edge in mesh */
878 +        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 +        if (!get_triangles(vother, ej))         /* triangle on same side? */
892 +                return(0);
893 +        return(vother[im_rev] != NULL);
894 + }
895 +
896 + /* Find context hull vertex to complete triangle (oriented call) */
897 + static RBFNODE *
898 + find_chull_vert(const RBFNODE *rbf0, const RBFNODE *rbf1)
899 + {
900 +        FVECT   vmid, vejn, vp;
901 +        RBFNODE *rbf, *rbfbest = NULL;
902 +        double  dprod, area2, bestarea2 = FHUGE, bestdprod = -.5;
903 +
904 +        VSUB(vejn, rbf1->invec, rbf0->invec);
905 +        VADD(vmid, rbf0->invec, rbf1->invec);
906 +        if (normalize(vejn) == 0 || normalize(vmid) == 0)
907 +                return(NULL);
908 +                                                /* XXX exhaustive search */
909 +        /* Find triangle with minimum rotation from perpendicular */
910 +        for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) {
911 +                if ((rbf == rbf0) | (rbf == rbf1))
912 +                        continue;
913 +                tri_orient(vp, rbf0->invec, rbf1->invec, rbf->invec);
914 +                if (DOT(vp, vmid) <= FTINY)
915 +                        continue;               /* wrong orientation */
916 +                area2 = .25*DOT(vp,vp);
917 +                VSUB(vp, rbf->invec, rbf0->invec);
918 +                dprod = -DOT(vp, vejn);
919 +                VSUM(vp, vp, vejn, dprod);      /* above guarantees non-zero */
920 +                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 +        }
929 +        return(rbfbest);
930 + }
931 +
932 + /* Create new migration edge and grow mesh recursively around it */
933 + static void
934 + mesh_from_edge(MIGRATION *edge)
935 + {
936 +        MIGRATION       *ej0, *ej1;
937 +        RBFNODE         *tvert[2];
938 +        
939 +        if (edge == NULL)
940 +                return;
941 +                                                /* triangle on either side? */
942 +        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 +                if (tvert[0] != NULL) {
946 +                        if (tvert[0] > edge->rbfv[0])
947 +                                ej0 = create_migration(edge->rbfv[0], tvert[0]);
948 +                        else
949 +                                ej0 = create_migration(tvert[0], edge->rbfv[0]);
950 +                        if (tvert[0] > edge->rbfv[1])
951 +                                ej1 = create_migration(edge->rbfv[1], tvert[0]);
952 +                        else
953 +                                ej1 = create_migration(tvert[0], edge->rbfv[1]);
954 +                        mesh_from_edge(ej0);
955 +                        mesh_from_edge(ej1);
956 +                }
957 +        } else if (tvert[1] == NULL) {          /* grow mesh on left */
958 +                tvert[1] = find_chull_vert(edge->rbfv[1], edge->rbfv[0]);
959 +                if (tvert[1] != NULL) {
960 +                        if (tvert[1] > edge->rbfv[0])
961 +                                ej0 = create_migration(edge->rbfv[0], tvert[1]);
962 +                        else
963 +                                ej0 = create_migration(tvert[1], edge->rbfv[0]);
964 +                        if (tvert[1] > edge->rbfv[1])
965 +                                ej1 = create_migration(edge->rbfv[1], tvert[1]);
966 +                        else
967 +                                ej1 = create_migration(tvert[1], edge->rbfv[1]);
968 +                        mesh_from_edge(ej0);
969 +                        mesh_from_edge(ej1);
970 +                }
971 +        }
972 + }
973 +
974 + #ifdef DEBUG
975 + #include "random.h"
976 + #include "bmpfile.h"
977 + /* Hash pointer to byte value (must return 0 for NULL) */
978 + 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 +                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 +        }
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 + /* 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 + #ifdef DEBUG
1066 +        write_edge_image("bsdf_edges.bmp");
1067 + #endif
1068 + }
1069 +        
1070 + /* Build our triangle mesh from recorded RBFs */
1071 + static void
1072 + build_mesh()
1073 + {
1074 +        double          best2 = M_PI*M_PI;
1075 +        RBFNODE         *shrt_edj[2];
1076 +        RBFNODE         *rbf0, *rbf1;
1077 +                                                /* check if isotropic */
1078 +        if (single_plane_incident) {
1079 +                for (rbf0 = dsf_list; rbf0 != NULL; rbf0 = rbf0->next)
1080 +                        if (rbf0->next != NULL)
1081 +                                create_migration(rbf0, rbf0->next);
1082 +                await_children(nchild);
1083 +                return;
1084 +        }
1085 +                                                /* 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 +                if (dist2 < best2) {
1091 +                        shrt_edj[0] = rbf0;
1092 +                        shrt_edj[1] = rbf1;
1093 +                        best2 = dist2;
1094 +                }
1095 +        }
1096 +        if (shrt_edj[0] == NULL) {
1097 +                fputs("Cannot find shortest edge\n", stderr);
1098 +                exit(1);
1099 +        }
1100 +                                                /* build mesh from this edge */
1101 +        if (shrt_edj[0] < shrt_edj[1])
1102 +                mesh_from_edge(create_migration(shrt_edj[0], shrt_edj[1]));
1103 +        else
1104 +                mesh_from_edge(create_migration(shrt_edj[1], shrt_edj[0]));
1105 +                                                /* complete migrations */
1106 +        await_children(nchild);
1107 +                                                /* draw edge list into grid */
1108 +        draw_edges();
1109 + }
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 + /* 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 + /* 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 +        {                                       /* else use triangle mesh */
1241 +                unsigned char   floodmap[GRIDRES][(GRIDRES+7)/8];
1242 +                int             pstart[2];
1243 +                RBFNODE         *vother;
1244 +                MIGRATION       *ej;
1245 +                int             i;
1246 +
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 +                                                /* 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 +        }
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 + #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 +        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 +        float           mbfact, mcfact;
1379 +        int             n, i, j, k;
1380 +        FVECT           v0, v1, v2;
1381 +        double          s, t;
1382 +
1383 +        if (!get_interp(miga, invec))           /* can't interpolate? */
1384 +                return(NULL);
1385 +        if (miga[1] == NULL)                    /* advect along edge? */
1386 +                return(e_advect_rbf(miga[0], invec));
1387 + #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 +                                                /* figure out position */
1396 +        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 + #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 +        rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1));
1419 +        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 +        rbf->nrbf = n;
1426 +        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 +                    rbf->rbfa[n].crad = ANG2R(sqrt(srad2 + t*rad2k*rad2k));
1460 +                    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 +        return(rbf);
1471 + }
1472 +
1473 + /* 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 + #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 +        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 +                        fprintf(stderr, "%s: cannot create pipe to rttree_reduce\n",
1495 +                                        progname);
1496 +                        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 +                        fprintf(stderr, "%s: error running '%s'\n",
1523 +                                        progname, cmd);
1524 +                        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 + #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 +        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 +                        fprintf(stderr, "%s: cannot create pipe to rttree_reduce\n",
1555 +                                        progname);
1556 +                        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 +                        fprintf(stderr, "%s: error running '%s'\n",
1584 +                                        progname, cmd);
1585 +                        exit(1);
1586 +                }
1587 +        } else
1588 +                fputs("}\n", stdout);
1589 + }
1590 +
1591   #if 1
1592 + /* 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 +        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 +                argv += 2; argc -= 2;
1613 +        }
1614 +        if (argc < 3)
1615 +                goto userr;
1616 + #ifdef _WIN32
1617 +        if (nprocs > 1) {
1618 +                fprintf(stderr, "%s: multiprocessing not supported\n",
1619 +                                progname);
1620 +                return(1);
1621 +        }
1622 + #endif
1623 +        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 + userr:
1639 +        fprintf(stderr,
1640 +        "Usage: %s [-n nprocs][-t pctcull] meas1.dat meas2.dat .. > bsdf.xml\n",
1641 +                                progname);
1642 +        return(1);
1643 + }
1644 + #else
1645   /* Test main produces a Radiance model from the given input file */
1646   int
1647   main(int argc, char *argv[])
# Line 401 | Line 1656 | main(int argc, char *argv[])
1656                  fprintf(stderr, "Usage: %s input.dat > output.rad\n", argv[0]);
1657                  return(1);
1658          }
1659 <        if (!load_bsdf_meas(argv[1]))
1659 >        if (!load_pabopto_meas(argv[1]))
1660                  return(1);
1661  
1662          compute_radii();
# Line 414 | Line 1669 | main(int argc, char *argv[])
1669          for (i = 0; i < GRIDRES; i++)
1670              for (j = 0; j < GRIDRES; j++)
1671                  if (dsf_grid[i][j].vsum > .0f) {
1672 <                        vec_from_pos(dir, i, j);
1672 >                        ovec_from_pos(dir, i, j);
1673                          bsdf = dsf_grid[i][j].vsum / dir[2];
1674                          if (dsf_grid[i][j].nval) {
1675                                  printf("pink cone c%04d\n0\n0\n8\n", ++n);
# Line 425 | Line 1680 | main(int argc, char *argv[])
1680                                          dir[2]*(bsdf+.005));
1681                                  puts("\t.003\t0\n");
1682                          } else {
1683 <                                vec_from_pos(dir, i, j);
1683 >                                ovec_from_pos(dir, i, j);
1684                                  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);
# Line 443 | Line 1698 | main(int argc, char *argv[])
1698          }
1699          for (i = 0; i < GRIDRES; i++)
1700              for (j = 0; j < GRIDRES; j++) {
1701 <                vec_from_pos(dir, i, j);
1701 >                ovec_from_pos(dir, i, j);
1702                  bsdf = eval_rbfrep(dsf_list, dir) / dir[2];
1703                  fprintf(pfp, "%.8e %.8e %.8e\n",
1704                                  dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines