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

File Contents

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