ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
Revision: 2.11
Committed: Wed Sep 19 22:03:37 2012 UTC (11 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.10: +126 -9 lines
Log Message:
Many additions, still untested

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pabopto2xml.c,v 2.10 2012/09/18 02:50:13 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 #define DEBUG 1
20
21 #ifndef GRIDRES
22 #define GRIDRES 200 /* grid resolution per side */
23 #endif
24
25 #define RSCA 2.7 /* radius scaling factor (empirical) */
26
27 /* convert to/from coded radians */
28 #define ANG2R(r) (int)((r)*((1<<16)/M_PI))
29 #define R2ANG(c) (((c)+.5)*(M_PI/(1<<16)))
30
31 typedef struct {
32 float vsum; /* DSF sum */
33 unsigned short nval; /* number of values in sum */
34 unsigned short crad; /* radius (coded angle) */
35 } GRIDVAL; /* grid value */
36
37 typedef struct {
38 float peak; /* lobe value at peak */
39 unsigned short crad; /* radius (coded angle) */
40 unsigned char gx, gy; /* grid position */
41 } RBFVAL; /* radial basis function value */
42
43 struct s_rbfnode; /* forward declaration of RBF struct */
44
45 typedef struct s_migration {
46 struct s_migration *next; /* next in global edge list */
47 struct s_rbfnode *rbfv[2]; /* from,to vertex */
48 struct s_migration *enxt[2]; /* next from,to sibling */
49 float mtx[1]; /* matrix (extends struct) */
50 } MIGRATION; /* migration link (winged edge structure) */
51
52 typedef struct s_rbfnode {
53 struct s_rbfnode *next; /* next in global RBF list */
54 MIGRATION *ejl; /* edge list for this vertex */
55 FVECT invec; /* incident vector direction */
56 double vtotal; /* volume for normalization */
57 int nrbf; /* number of RBFs */
58 RBFVAL rbfa[1]; /* RBF array (extends struct) */
59 } RBFNODE; /* RBF representation of DSF @ 1 incidence */
60
61 /* our loaded grid for this incident angle */
62 static double theta_in_deg, phi_in_deg;
63 static GRIDVAL dsf_grid[GRIDRES][GRIDRES];
64
65 /* all incident angles in-plane so far? */
66 static int single_plane_incident = -1;
67
68 /* input/output orientations */
69 static int input_orient = 0;
70 static int output_orient = 0;
71
72 /* processed incident DSF measurements */
73 static RBFNODE *dsf_list = NULL;
74
75 /* RBF-linking matrices (edges) */
76 static MIGRATION *mig_list = NULL;
77
78 /* migration edges drawn in raster fashion */
79 static MIGRATION *mig_grid[GRIDRES][GRIDRES];
80
81 #define mtx_nrows(m) ((m)->rbfv[0]->nrbf)
82 #define mtx_ncols(m) ((m)->rbfv[1]->nrbf)
83 #define mtx_ndx(m,i,j) ((i)*mtx_ncols(m) + (j))
84 #define is_src(rbf,m) ((rbf) == (m)->rbfv[0])
85 #define is_dest(rbf,m) ((rbf) == (m)->rbfv[1])
86 #define nextedge(rbf,m) (m)->enxt[is_dest(rbf,m)]
87 #define opp_rbf(rbf,m) (m)->rbfv[is_src(rbf,m)]
88
89 #define round(v) (int)((v) + .5 - ((v) < -.5))
90
91 /* Compute volume associated with Gaussian lobe */
92 static double
93 rbf_volume(const RBFVAL *rbfp)
94 {
95 double rad = R2ANG(rbfp->crad);
96
97 return((2.*M_PI) * rbfp->peak * rad*rad);
98 }
99
100 /* Compute outgoing vector from grid position */
101 static void
102 ovec_from_pos(FVECT vec, int xpos, int ypos)
103 {
104 double uv[2];
105 double r2;
106
107 SDsquare2disk(uv, (1./GRIDRES)*(xpos+.5), (1./GRIDRES)*(ypos+.5));
108 /* uniform hemispherical projection */
109 r2 = uv[0]*uv[0] + uv[1]*uv[1];
110 vec[0] = vec[1] = sqrt(2. - r2);
111 vec[0] *= uv[0];
112 vec[1] *= uv[1];
113 vec[2] = output_orient*(1. - r2);
114 }
115
116 /* Compute grid position from normalized input/output vector */
117 static void
118 pos_from_vec(int pos[2], const FVECT vec)
119 {
120 double sq[2]; /* uniform hemispherical projection */
121 double norm = 1./sqrt(1. + fabs(vec[2]));
122
123 SDdisk2square(sq, vec[0]*norm, vec[1]*norm);
124
125 pos[0] = (int)(sq[0]*GRIDRES);
126 pos[1] = (int)(sq[1]*GRIDRES);
127 }
128
129 /* Evaluate RBF for DSF at the given normalized outgoing direction */
130 static double
131 eval_rbfrep(const RBFNODE *rp, const FVECT outvec)
132 {
133 double res = .0;
134 const RBFVAL *rbfp;
135 FVECT odir;
136 double sig2;
137 int n;
138
139 rbfp = rp->rbfa;
140 for (n = rp->nrbf; n--; rbfp++) {
141 ovec_from_pos(odir, rbfp->gx, rbfp->gy);
142 sig2 = R2ANG(rbfp->crad);
143 sig2 = (DOT(odir,outvec) - 1.) / (sig2*sig2);
144 if (sig2 > -19.)
145 res += rbfp->peak * exp(sig2);
146 }
147 return(res);
148 }
149
150 /* Insert a new directional scattering function in our global list */
151 static void
152 insert_dsf(RBFNODE *newrbf)
153 {
154 RBFNODE *rbf, *rbf_last;
155
156 /* keep in ascending theta order */
157 for (rbf_last = NULL, rbf = dsf_list;
158 single_plane_incident & (rbf != NULL);
159 rbf_last = rbf, rbf = rbf->next)
160 if (input_orient*rbf->invec[2] < input_orient*newrbf->invec[2])
161 break;
162 if (rbf_last == NULL) {
163 newrbf->next = dsf_list;
164 dsf_list = newrbf;
165 return;
166 }
167 newrbf->next = rbf;
168 rbf_last->next = newrbf;
169 }
170
171 /* Count up filled nodes and build RBF representation from current grid */
172 static RBFNODE *
173 make_rbfrep(void)
174 {
175 int niter = 16;
176 double lastVar, thisVar = 100.;
177 int nn;
178 RBFNODE *newnode;
179 int i, j;
180
181 nn = 0; /* count selected bins */
182 for (i = 0; i < GRIDRES; i++)
183 for (j = 0; j < GRIDRES; j++)
184 nn += dsf_grid[i][j].nval;
185 /* allocate RBF array */
186 newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
187 if (newnode == NULL) {
188 fputs("Out of memory in make_rbfrep()\n", stderr);
189 exit(1);
190 }
191 newnode->next = NULL;
192 newnode->ejl = NULL;
193 newnode->invec[2] = sin(M_PI/180.*theta_in_deg);
194 newnode->invec[0] = cos(M_PI/180.*phi_in_deg)*newnode->invec[2];
195 newnode->invec[1] = sin(M_PI/180.*phi_in_deg)*newnode->invec[2];
196 newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
197 newnode->vtotal = 0;
198 newnode->nrbf = nn;
199 nn = 0; /* fill RBF array */
200 for (i = 0; i < GRIDRES; i++)
201 for (j = 0; j < GRIDRES; j++)
202 if (dsf_grid[i][j].nval) {
203 newnode->rbfa[nn].peak = dsf_grid[i][j].vsum;
204 newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5;
205 newnode->rbfa[nn].gx = i;
206 newnode->rbfa[nn].gy = j;
207 ++nn;
208 }
209 /* iterate to improve interpolation accuracy */
210 do {
211 double dsum = .0, dsum2 = .0;
212 nn = 0;
213 for (i = 0; i < GRIDRES; i++)
214 for (j = 0; j < GRIDRES; j++)
215 if (dsf_grid[i][j].nval) {
216 FVECT odir;
217 double corr;
218 ovec_from_pos(odir, i, j);
219 newnode->rbfa[nn++].peak *= corr =
220 dsf_grid[i][j].vsum /
221 eval_rbfrep(newnode, odir);
222 dsum += corr - 1.;
223 dsum2 += (corr-1.)*(corr-1.);
224 }
225 lastVar = thisVar;
226 thisVar = dsum2/(double)nn;
227 #ifdef DEBUG
228 fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n",
229 100.*dsum/(double)nn,
230 100.*sqrt(thisVar));
231 #endif
232 } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar);
233
234 nn = 0; /* compute sum for normalization */
235 while (nn < newnode->nrbf)
236 newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
237
238 insert_dsf(newnode);
239 return(newnode);
240 }
241
242 /* Load a set of measurements corresponding to a particular incident angle */
243 static int
244 load_pabopto_meas(const char *fname)
245 {
246 FILE *fp = fopen(fname, "r");
247 int inp_is_DSF = -1;
248 double new_phi, theta_out, phi_out, val;
249 char buf[2048];
250 int n, c;
251
252 if (fp == NULL) {
253 fputs(fname, stderr);
254 fputs(": cannot open\n", stderr);
255 return(0);
256 }
257 memset(dsf_grid, 0, sizeof(dsf_grid));
258 #ifdef DEBUG
259 fprintf(stderr, "Loading measurement file '%s'...\n", fname);
260 #endif
261 /* read header information */
262 while ((c = getc(fp)) == '#' || c == EOF) {
263 if (fgets(buf, sizeof(buf), fp) == NULL) {
264 fputs(fname, stderr);
265 fputs(": unexpected EOF\n", stderr);
266 fclose(fp);
267 return(0);
268 }
269 if (!strcmp(buf, "format: theta phi DSF\n")) {
270 inp_is_DSF = 1;
271 continue;
272 }
273 if (!strcmp(buf, "format: theta phi BSDF\n")) {
274 inp_is_DSF = 0;
275 continue;
276 }
277 if (sscanf(buf, "intheta %lf", &theta_in_deg) == 1)
278 continue;
279 if (sscanf(buf, "inphi %lf", &new_phi) == 1)
280 continue;
281 if (sscanf(buf, "incident_angle %lf %lf",
282 &theta_in_deg, &new_phi) == 2)
283 continue;
284 }
285 if (inp_is_DSF < 0) {
286 fputs(fname, stderr);
287 fputs(": unknown format\n", stderr);
288 fclose(fp);
289 return(0);
290 }
291 if (!input_orient) /* check input orientation */
292 input_orient = 1 - 2*(theta_in_deg > 90.);
293 else if (input_orient > 0 ^ theta_in_deg < 90.) {
294 fputs("Cannot handle input angles on both sides of surface\n",
295 stderr);
296 exit(1);
297 }
298 if (single_plane_incident > 0) /* check if still in plane */
299 single_plane_incident = (round(new_phi) == round(phi_in_deg));
300 else if (single_plane_incident < 0)
301 single_plane_incident = 1;
302 phi_in_deg = new_phi;
303 ungetc(c, fp); /* read actual data */
304 while (fscanf(fp, "%lf %lf %lf\n", &theta_out, &phi_out, &val) == 3) {
305 FVECT ovec;
306 int pos[2];
307
308 if (!output_orient) /* check output orientation */
309 output_orient = 1 - 2*(theta_out > 90.);
310 else if (output_orient > 0 ^ theta_out < 90.) {
311 fputs("Cannot handle output angles on both sides of surface\n",
312 stderr);
313 exit(1);
314 }
315 ovec[2] = sin(M_PI/180.*theta_out);
316 ovec[0] = cos(M_PI/180.*phi_out) * ovec[2];
317 ovec[1] = sin(M_PI/180.*phi_out) * ovec[2];
318 ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
319
320 if (!inp_is_DSF)
321 val *= ovec[2]; /* convert from BSDF to DSF */
322
323 pos_from_vec(pos, ovec);
324
325 dsf_grid[pos[0]][pos[1]].vsum += val;
326 dsf_grid[pos[0]][pos[1]].nval++;
327 }
328 n = 0;
329 while ((c = getc(fp)) != EOF)
330 n += !isspace(c);
331 if (n)
332 fprintf(stderr,
333 "%s: warning: %d unexpected characters past EOD\n",
334 fname, n);
335 fclose(fp);
336 return(1);
337 }
338
339 /* Compute radii for non-empty bins */
340 /* (distance to furthest empty bin for which non-empty bin is the closest) */
341 static void
342 compute_radii(void)
343 {
344 unsigned int fill_grid[GRIDRES][GRIDRES];
345 unsigned short fill_cnt[GRIDRES][GRIDRES];
346 FVECT ovec0, ovec1;
347 double ang2, lastang2;
348 int r, i, j, jn, ii, jj, inear, jnear;
349
350 r = GRIDRES/2; /* proceed in zig-zag */
351 for (i = 0; i < GRIDRES; i++)
352 for (jn = 0; jn < GRIDRES; jn++) {
353 j = (i&1) ? jn : GRIDRES-1-jn;
354 if (dsf_grid[i][j].nval) /* find empty grid pos. */
355 continue;
356 ovec_from_pos(ovec0, i, j);
357 inear = jnear = -1; /* find nearest non-empty */
358 lastang2 = M_PI*M_PI;
359 for (ii = i-r; ii <= i+r; ii++) {
360 if (ii < 0) continue;
361 if (ii >= GRIDRES) break;
362 for (jj = j-r; jj <= j+r; jj++) {
363 if (jj < 0) continue;
364 if (jj >= GRIDRES) break;
365 if (!dsf_grid[ii][jj].nval)
366 continue;
367 ovec_from_pos(ovec1, ii, jj);
368 ang2 = 2. - 2.*DOT(ovec0,ovec1);
369 if (ang2 >= lastang2)
370 continue;
371 lastang2 = ang2;
372 inear = ii; jnear = jj;
373 }
374 }
375 if (inear < 0) {
376 fputs("Could not find non-empty neighbor!\n", stderr);
377 exit(1);
378 }
379 ang2 = sqrt(lastang2);
380 r = ANG2R(ang2); /* record if > previous */
381 if (r > dsf_grid[inear][jnear].crad)
382 dsf_grid[inear][jnear].crad = r;
383 /* next search radius */
384 r = ang2*(2.*GRIDRES/M_PI) + 3;
385 }
386 /* blur radii over hemisphere */
387 memset(fill_grid, 0, sizeof(fill_grid));
388 memset(fill_cnt, 0, sizeof(fill_cnt));
389 for (i = 0; i < GRIDRES; i++)
390 for (j = 0; j < GRIDRES; j++) {
391 if (!dsf_grid[i][j].crad)
392 continue; /* missing distance */
393 r = R2ANG(dsf_grid[i][j].crad)*(2.*RSCA*GRIDRES/M_PI);
394 for (ii = i-r; ii <= i+r; ii++) {
395 if (ii < 0) continue;
396 if (ii >= GRIDRES) break;
397 for (jj = j-r; jj <= j+r; jj++) {
398 if (jj < 0) continue;
399 if (jj >= GRIDRES) break;
400 if ((ii-i)*(ii-i) + (jj-j)*(jj-j) > r*r)
401 continue;
402 fill_grid[ii][jj] += dsf_grid[i][j].crad;
403 fill_cnt[ii][jj]++;
404 }
405 }
406 }
407 /* copy back blurred radii */
408 for (i = 0; i < GRIDRES; i++)
409 for (j = 0; j < GRIDRES; j++)
410 if (fill_cnt[i][j])
411 dsf_grid[i][j].crad = fill_grid[i][j]/fill_cnt[i][j];
412 }
413
414 /* Cull points for more uniform distribution, leave all nval 0 or 1 */
415 static void
416 cull_values(void)
417 {
418 FVECT ovec0, ovec1;
419 double maxang, maxang2;
420 int i, j, ii, jj, r;
421 /* simple greedy algorithm */
422 for (i = 0; i < GRIDRES; i++)
423 for (j = 0; j < GRIDRES; j++) {
424 if (!dsf_grid[i][j].nval)
425 continue;
426 if (!dsf_grid[i][j].crad)
427 continue; /* shouldn't happen */
428 ovec_from_pos(ovec0, i, j);
429 maxang = 2.*R2ANG(dsf_grid[i][j].crad);
430 if (maxang > ovec0[2]) /* clamp near horizon */
431 maxang = ovec0[2];
432 r = maxang*(2.*GRIDRES/M_PI) + 1;
433 maxang2 = maxang*maxang;
434 for (ii = i-r; ii <= i+r; ii++) {
435 if (ii < 0) continue;
436 if (ii >= GRIDRES) break;
437 for (jj = j-r; jj <= j+r; jj++) {
438 if (jj < 0) continue;
439 if (jj >= GRIDRES) break;
440 if (!dsf_grid[ii][jj].nval)
441 continue;
442 if ((ii == i) & (jj == j))
443 continue; /* don't get self-absorbed */
444 ovec_from_pos(ovec1, ii, jj);
445 if (2. - 2.*DOT(ovec0,ovec1) >= maxang2)
446 continue;
447 /* absorb sum */
448 dsf_grid[i][j].vsum += dsf_grid[ii][jj].vsum;
449 dsf_grid[i][j].nval += dsf_grid[ii][jj].nval;
450 /* keep value, though */
451 dsf_grid[ii][jj].vsum /= (float)dsf_grid[ii][jj].nval;
452 dsf_grid[ii][jj].nval = 0;
453 }
454 }
455 }
456 /* final averaging pass */
457 for (i = 0; i < GRIDRES; i++)
458 for (j = 0; j < GRIDRES; j++)
459 if (dsf_grid[i][j].nval > 1) {
460 dsf_grid[i][j].vsum /= (float)dsf_grid[i][j].nval;
461 dsf_grid[i][j].nval = 1;
462 }
463 }
464
465 /* Compute (and allocate) migration price matrix for optimization */
466 static float *
467 price_routes(const RBFNODE *from_rbf, const RBFNODE *to_rbf)
468 {
469 float *pmtx = (float *)malloc(sizeof(float) *
470 from_rbf->nrbf * to_rbf->nrbf);
471 FVECT *vto = (FVECT *)malloc(sizeof(FVECT) * to_rbf->nrbf);
472 int i, j;
473
474 if ((pmtx == NULL) | (vto == NULL)) {
475 fputs("Out of memory in migration_costs()\n", stderr);
476 exit(1);
477 }
478 for (j = to_rbf->nrbf; j--; ) /* save repetitive ops. */
479 ovec_from_pos(vto[j], to_rbf->rbfa[j].gx, to_rbf->rbfa[j].gy);
480
481 for (i = from_rbf->nrbf; i--; ) {
482 const double from_ang = R2ANG(from_rbf->rbfa[i].crad);
483 FVECT vfrom;
484 ovec_from_pos(vfrom, from_rbf->rbfa[i].gx, from_rbf->rbfa[i].gy);
485 for (j = to_rbf->nrbf; j--; )
486 pmtx[i*to_rbf->nrbf + j] = acos(DOT(vfrom, vto[j])) +
487 fabs(R2ANG(to_rbf->rbfa[j].crad) - from_ang);
488 }
489 free(vto);
490 return(pmtx);
491 }
492
493 /* Comparison routine needed for sorting price row */
494 static const float *price_arr;
495 static int
496 msrt_cmp(const void *p1, const void *p2)
497 {
498 float c1 = price_arr[*(const int *)p1];
499 float c2 = price_arr[*(const int *)p2];
500
501 if (c1 > c2) return(1);
502 if (c1 < c2) return(-1);
503 return(0);
504 }
505
506 /* Compute minimum (optimistic) cost for moving the given source material */
507 static double
508 min_cost(double amt2move, const double *avail, const float *price, int n)
509 {
510 static int *price_sort = NULL;
511 static int n_alloc = 0;
512 double total_cost = 0;
513 int i;
514
515 if (amt2move <= FTINY) /* pre-emptive check */
516 return(0.);
517 if (n > n_alloc) { /* (re)allocate sort array */
518 if (n_alloc) free(price_sort);
519 price_sort = (int *)malloc(sizeof(int)*n);
520 if (price_sort == NULL) {
521 fputs("Out of memory in min_cost()\n", stderr);
522 exit(1);
523 }
524 n_alloc = n;
525 }
526 for (i = n; i--; )
527 price_sort[i] = i;
528 price_arr = price;
529 qsort(price_sort, n, sizeof(int), &msrt_cmp);
530 /* move cheapest first */
531 for (i = 0; i < n && amt2move > FTINY; i++) {
532 int d = price_sort[i];
533 double amt = (amt2move < avail[d]) ? amt2move : avail[d];
534
535 total_cost += amt * price[d];
536 amt2move -= amt;
537 }
538 return(total_cost);
539 }
540
541 /* Take a step in migration by choosing optimal bucket to transfer */
542 static double
543 migration_step(MIGRATION *mig, double *src_rem, double *dst_rem, const float *pmtx)
544 {
545 static double *src_cost = NULL;
546 int n_alloc = 0;
547 const double maxamt = 2./(mtx_nrows(mig)*mtx_ncols(mig));
548 double amt = 0;
549 struct {
550 int s, d; /* source and destination */
551 double price; /* price estimate per amount moved */
552 double amt; /* amount we can move */
553 } cur, best;
554 int i;
555
556 if (mtx_nrows(mig) > n_alloc) { /* allocate cost array */
557 if (n_alloc)
558 free(src_cost);
559 src_cost = (double *)malloc(sizeof(double)*mtx_nrows(mig));
560 if (src_cost == NULL) {
561 fputs("Out of memory in migration_step()\n", stderr);
562 exit(1);
563 }
564 n_alloc = mtx_nrows(mig);
565 }
566 for (i = mtx_nrows(mig); i--; ) /* starting costs for diff. */
567 src_cost[i] = min_cost(src_rem[i], dst_rem,
568 pmtx+i*mtx_ncols(mig), mtx_ncols(mig));
569
570 /* find best source & dest. */
571 best.s = best.d = -1; best.price = FHUGE; best.amt = 0;
572 for (cur.s = mtx_nrows(mig); cur.s--; ) {
573 const float *price = pmtx + cur.s*mtx_ncols(mig);
574 double cost_others = 0;
575 if (src_rem[cur.s] <= FTINY)
576 continue;
577 cur.d = -1; /* examine cheapest dest. */
578 for (i = mtx_ncols(mig); i--; )
579 if (dst_rem[i] > FTINY &&
580 (cur.d < 0 || price[i] < price[cur.d]))
581 cur.d = i;
582 if (cur.d < 0)
583 return(.0);
584 if ((cur.price = price[cur.d]) >= best.price)
585 continue; /* no point checking further */
586 cur.amt = (src_rem[cur.s] < dst_rem[cur.d]) ?
587 src_rem[cur.s] : dst_rem[cur.d];
588 if (cur.amt > maxamt) cur.amt = maxamt;
589 dst_rem[cur.d] -= cur.amt; /* add up differential costs */
590 for (i = mtx_nrows(mig); i--; ) {
591 if (i == cur.s) continue;
592 cost_others += min_cost(src_rem[i], dst_rem, price, mtx_ncols(mig))
593 - src_cost[i];
594 }
595 dst_rem[cur.d] += cur.amt; /* undo trial move */
596 cur.price += cost_others/cur.amt; /* adjust effective price */
597 if (cur.price < best.price) /* are we better than best? */
598 best = cur;
599 }
600 if ((best.s < 0) | (best.d < 0))
601 return(.0);
602 /* make the actual move */
603 mig->mtx[mtx_ndx(mig,best.s,best.d)] += best.amt;
604 src_rem[best.s] -= best.amt;
605 dst_rem[best.d] -= best.amt;
606 return(best.amt);
607 }
608
609 /* Compute (and insert) migration along directed edge */
610 static MIGRATION *
611 make_migration(RBFNODE *from_rbf, RBFNODE *to_rbf)
612 {
613 const double end_thresh = 0.02/(from_rbf->nrbf*to_rbf->nrbf);
614 float *pmtx = price_routes(from_rbf, to_rbf);
615 MIGRATION *newmig = (MIGRATION *)malloc(sizeof(MIGRATION) +
616 sizeof(float) *
617 (from_rbf->nrbf*to_rbf->nrbf - 1));
618 double *src_rem = (double *)malloc(sizeof(double)*from_rbf->nrbf);
619 double *dst_rem = (double *)malloc(sizeof(double)*to_rbf->nrbf);
620 double total_rem = 1.;
621 int i;
622
623 if ((newmig == NULL) | (src_rem == NULL) | (dst_rem == NULL)) {
624 fputs("Out of memory in make_migration()\n", stderr);
625 exit(1);
626 }
627 #ifdef DEBUG
628 {
629 double theta, phi;
630 theta = acos(from_rbf->invec[2])*(180./M_PI);
631 phi = atan2(from_rbf->invec[1],from_rbf->invec[0])*(180./M_PI);
632 fprintf(stderr, "Building path from (theta,phi) (%d,%d) to ",
633 round(theta), round(phi));
634 theta = acos(to_rbf->invec[2])*(180./M_PI);
635 phi = atan2(to_rbf->invec[1],to_rbf->invec[0])*(180./M_PI);
636 fprintf(stderr, "(%d,%d)\n", round(theta), round(phi));
637 }
638 #endif
639 newmig->next = NULL;
640 newmig->rbfv[0] = from_rbf;
641 newmig->rbfv[1] = to_rbf;
642 newmig->enxt[0] = newmig->enxt[1] = NULL;
643 memset(newmig->mtx, 0, sizeof(float)*from_rbf->nrbf*to_rbf->nrbf);
644 /* starting quantities */
645 for (i = from_rbf->nrbf; i--; )
646 src_rem[i] = rbf_volume(&from_rbf->rbfa[i]) / from_rbf->vtotal;
647 for (i = to_rbf->nrbf; i--; )
648 dst_rem[i] = rbf_volume(&to_rbf->rbfa[i]) / to_rbf->vtotal;
649 /* move a bit at a time */
650 while (total_rem > end_thresh)
651 total_rem -= migration_step(newmig, src_rem, dst_rem, pmtx);
652
653 free(pmtx); /* free working arrays */
654 free(src_rem);
655 free(dst_rem);
656 for (i = from_rbf->nrbf; i--; ) { /* normalize final matrix */
657 float nf = rbf_volume(&from_rbf->rbfa[i]);
658 int j;
659 if (nf <= FTINY) continue;
660 nf = from_rbf->vtotal / nf;
661 for (j = to_rbf->nrbf; j--; )
662 newmig->mtx[mtx_ndx(newmig,i,j)] *= nf;
663 }
664 /* insert in edge lists */
665 newmig->enxt[0] = from_rbf->ejl;
666 from_rbf->ejl = newmig;
667 newmig->enxt[1] = to_rbf->ejl;
668 to_rbf->ejl = newmig;
669 newmig->next = mig_list;
670 return(mig_list = newmig);
671 }
672
673 /* Get triangle surface orientation (unnormalized) */
674 static void
675 tri_orient(FVECT vres, const FVECT v1, const FVECT v2, const FVECT v3)
676 {
677 FVECT v2minus1, v3minus2;
678
679 VSUB(v2minus1, v2, v1);
680 VSUB(v3minus2, v3, v2);
681 VCROSS(vres, v2minus1, v3minus2);
682 }
683
684 /* Determine if vertex order is reversed (inward normal) */
685 static int
686 is_rev_tri(const FVECT v1, const FVECT v2, const FVECT v3)
687 {
688 FVECT tor;
689
690 tri_orient(tor, v1, v2, v3);
691
692 return(DOT(tor, v2) < 0.);
693 }
694
695 /* Find vertices completing triangles on either side of the given edge */
696 static int
697 get_triangles(RBFNODE *rbfv[2], const MIGRATION *mig)
698 {
699 const MIGRATION *ej, *ej2;
700 RBFNODE *tv;
701
702 rbfv[0] = rbfv[1] = NULL;
703 for (ej = mig->rbfv[0]->ejl; ej != NULL;
704 ej = nextedge(mig->rbfv[0],ej)) {
705 if (ej == mig)
706 continue;
707 tv = opp_rbf(mig->rbfv[0],ej);
708 for (ej2 = tv->ejl; ej2 != NULL; ej2 = nextedge(tv,ej2))
709 if (opp_rbf(tv,ej2) == mig->rbfv[1]) {
710 rbfv[is_rev_tri(mig->rbfv[0]->invec,
711 mig->rbfv[1]->invec,
712 tv->invec)] = tv;
713 break;
714 }
715 }
716 return((rbfv[0] != NULL) + (rbfv[1] != NULL));
717 }
718
719 /* Find context hull vertex to complete triangle (oriented call) */
720 static RBFNODE *
721 find_chull_vert(const RBFNODE *rbf0, const RBFNODE *rbf1)
722 {
723 FVECT vmid, vor;
724 RBFNODE *rbf, *rbfbest = NULL;
725 double dprod2, bestdprod2 = 0.5;
726
727 VADD(vmid, rbf0->invec, rbf1->invec);
728 if (normalize(vmid) == 0)
729 return(NULL);
730 /* XXX exhaustive search */
731 for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) {
732 if ((rbf == rbf0) | (rbf == rbf1))
733 continue;
734 tri_orient(vor, rbf0->invec, rbf1->invec, rbf->invec);
735 dprod2 = DOT(vor, vmid);
736 if (dprod2 <= FTINY)
737 continue; /* wrong orientation */
738 dprod2 *= dprod2 / DOT(vor,vor);
739 if (dprod2 > bestdprod2) { /* more convex than prev? */
740 rbfbest = rbf;
741 bestdprod2 = dprod2;
742 }
743 }
744 return(rbf);
745 }
746
747 /* Create new migration edge and grow mesh recursively around it */
748 static void
749 mesh_from_edge(RBFNODE *rbf0, RBFNODE *rbf1)
750 {
751 MIGRATION *newej;
752 RBFNODE *tvert[2];
753
754 if (rbf0 < rbf1) /* avoid migration loops */
755 newej = make_migration(rbf0, rbf1);
756 else
757 newej = make_migration(rbf1, rbf0);
758 /* triangle on either side? */
759 get_triangles(tvert, newej);
760 if (tvert[0] == NULL) { /* recurse on new right edge */
761 tvert[0] = find_chull_vert(newej->rbfv[0], newej->rbfv[1]);
762 if (tvert[0] != NULL) {
763 mesh_from_edge(rbf0, tvert[0]);
764 mesh_from_edge(rbf1, tvert[0]);
765 }
766 }
767 if (tvert[1] == NULL) { /* recurse on new left edge */
768 tvert[1] = find_chull_vert(newej->rbfv[1], newej->rbfv[0]);
769 if (tvert[1] != NULL) {
770 mesh_from_edge(rbf0, tvert[1]);
771 mesh_from_edge(rbf1, tvert[1]);
772 }
773 }
774 }
775
776 /* Draw edge list into mig_grid array */
777 static void
778 draw_edges()
779 {
780 int nnull = 0, ntot = 0;
781 MIGRATION *ej;
782 int p0[2], p1[2];
783
784 /* memset(mig_grid, 0, sizeof(mig_grid)); */
785 for (ej = mig_list; ej != NULL; ej = ej->next) {
786 ++ntot;
787 pos_from_vec(p0, ej->rbfv[0]->invec);
788 pos_from_vec(p1, ej->rbfv[1]->invec);
789 if ((p0[0] == p1[0]) & (p0[1] == p1[1])) {
790 ++nnull;
791 mig_grid[p0[0]][p0[1]] = ej;
792 continue;
793 }
794 if (abs(p1[0]-p0[0]) > abs(p1[1]-p0[1])) {
795 const int xstep = 2*(p1[0] > p0[0]) - 1;
796 const double ystep = (double)((p1[1]-p0[1])*xstep) /
797 (double)(p1[0]-p0[0]);
798 int x;
799 double y;
800 for (x = p0[0], y = p0[1]+.5; x != p1[0];
801 x += xstep, y += ystep)
802 mig_grid[x][(int)y] = ej;
803 mig_grid[x][(int)y] = ej;
804 } else {
805 const int ystep = 2*(p1[1] > p0[1]) - 1;
806 const double xstep = (double)((p1[0]-p0[0])*ystep) /
807 (double)(p1[1]-p0[1]);
808 int y;
809 double x;
810 for (y = p0[1], x = p0[0]+.5; y != p1[1];
811 y += ystep, x += xstep)
812 mig_grid[(int)x][y] = ej;
813 mig_grid[(int)x][y] = ej;
814 }
815 }
816 if (nnull)
817 fprintf(stderr, "Warning: %d of %d edges are null\n",
818 nnull, ntot);
819 }
820
821 /* Build our triangle mesh from recorded RBFs */
822 static void
823 build_mesh()
824 {
825 double best2 = M_PI*M_PI;
826 RBFNODE *rbf, *rbf_near = NULL;
827 /* check if isotropic */
828 if (single_plane_incident) {
829 for (rbf = dsf_list; rbf != NULL; rbf = rbf->next)
830 if (rbf->next != NULL)
831 make_migration(rbf, rbf->next);
832 return;
833 }
834 /* find RBF nearest to head */
835 if (dsf_list == NULL)
836 return;
837 for (rbf = dsf_list->next; rbf != NULL; rbf = rbf->next) {
838 double dist2 = 2. - 2.*DOT(dsf_list->invec,rbf->invec);
839 if (dist2 < best2) {
840 rbf_near = rbf;
841 best2 = dist2;
842 }
843 }
844 if (rbf_near == NULL) {
845 fputs("Cannot find nearest point for first edge\n", stderr);
846 exit(1);
847 }
848 /* build mesh from this edge */
849 mesh_from_edge(dsf_list, rbf_near);
850 /* draw edge list into grid */
851 draw_edges();
852 }
853
854 /* Identify enclosing triangle for this position (flood fill raster check) */
855 static int
856 identify_tri(MIGRATION *miga[3], unsigned char vmap[GRIDRES][(GRIDRES+7)/8],
857 int px, int py)
858 {
859 const int btest = 1<<(py&07);
860
861 if (vmap[px][py>>3] & btest) /* already visited here? */
862 return(1);
863 /* else mark it */
864 vmap[px][py>>3] |= btest;
865
866 if (mig_grid[px][py] != NULL) { /* are we on an edge? */
867 int i;
868 for (i = 0; i < 3; i++) {
869 if (miga[i] == mig_grid[px][py])
870 return(1);
871 if (miga[i] != NULL)
872 continue;
873 miga[i] = mig_grid[px][py];
874 return(1);
875 }
876 return(0); /* outside triangle! */
877 }
878 /* check neighbors (flood) */
879 if (px > 0 && !identify_tri(miga, vmap, px-1, py))
880 return(0);
881 if (px < GRIDRES-1 && !identify_tri(miga, vmap, px+1, py))
882 return(0);
883 if (py > 0 && !identify_tri(miga, vmap, px, py-1))
884 return(0);
885 if (py < GRIDRES-1 && !identify_tri(miga, vmap, px, py+1))
886 return(0);
887 return(1); /* this neighborhood done */
888 }
889
890 /* Find edge(s) for interpolating the given incident vector */
891 static int
892 get_interp(MIGRATION *miga[3], const FVECT invec)
893 {
894 miga[0] = miga[1] = miga[2] = NULL;
895 if (single_plane_incident) { /* isotropic BSDF? */
896 RBFNODE *rbf; /* find edge we're on */
897 for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) {
898 if (input_orient*rbf->invec[2] < input_orient*invec[2])
899 break;
900 if (rbf->next != NULL &&
901 input_orient*rbf->next->invec[2] <
902 input_orient*invec[2]) {
903 for (miga[0] = rbf->ejl; miga[0] != NULL;
904 miga[0] = nextedge(rbf,miga[0]))
905 if (opp_rbf(rbf,miga[0]) == rbf->next)
906 return(1);
907 break;
908 }
909 }
910 return(0); /* outside range! */
911 }
912 { /* else use triagnle mesh */
913 unsigned char floodmap[GRIDRES][(GRIDRES+7)/8];
914 int pstart[2];
915
916 pos_from_vec(pstart, invec);
917 memset(floodmap, 0, sizeof(floodmap));
918 /* call flooding function */
919 if (!identify_tri(miga, floodmap, pstart[0], pstart[1]))
920 return(0); /* outside mesh */
921 if ((miga[0] == NULL) | (miga[2] == NULL))
922 return(0); /* should never happen */
923 if (miga[1] == NULL)
924 return(1); /* on edge */
925 return(3); /* else in triangle */
926 }
927 }
928
929 /* Advect and allocate new RBF along edge */
930 static RBFNODE *
931 e_advect_rbf(const MIGRATION *mig, const FVECT invec)
932 {
933 RBFNODE *rbf;
934 int n, i, j;
935 double t, full_dist;
936 /* get relative position */
937 t = acos(DOT(invec, mig->rbfv[0]->invec));
938 if (t < M_PI/GRIDRES) { /* near first DSF */
939 n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[0]->nrbf-1);
940 rbf = (RBFNODE *)malloc(n);
941 if (rbf == NULL)
942 goto memerr;
943 memcpy(rbf, mig->rbfv[0], n); /* just duplicate */
944 return(rbf);
945 }
946 full_dist = acos(DOT(mig->rbfv[0]->invec, mig->rbfv[1]->invec));
947 if (t > full_dist-M_PI/GRIDRES) { /* near second DSF */
948 n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[1]->nrbf-1);
949 rbf = (RBFNODE *)malloc(n);
950 if (rbf == NULL)
951 goto memerr;
952 memcpy(rbf, mig->rbfv[1], n); /* just duplicate */
953 return(rbf);
954 }
955 t /= full_dist;
956 n = 0; /* count migrating particles */
957 for (i = 0; i < mtx_nrows(mig); i++)
958 for (j = 0; j < mtx_ncols(mig); j++)
959 n += (mig->mtx[mtx_ndx(mig,i,j)] > FTINY);
960
961 rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1));
962 if (rbf == NULL)
963 goto memerr;
964 rbf->next = NULL; rbf->ejl = NULL;
965 VCOPY(rbf->invec, invec);
966 rbf->nrbf = n;
967 rbf->vtotal = 1.-t + t*mig->rbfv[1]->vtotal/mig->rbfv[0]->vtotal;
968 n = 0; /* advect RBF lobes */
969 for (i = 0; i < mtx_nrows(mig); i++) {
970 const RBFVAL *rbf0i = &mig->rbfv[0]->rbfa[i];
971 const float peak0 = rbf0i->peak;
972 const double rad0 = R2ANG(rbf0i->crad);
973 FVECT v0;
974 float mv;
975 ovec_from_pos(v0, rbf0i->gx, rbf0i->gy);
976 for (j = 0; j < mtx_ncols(mig); j++)
977 if ((mv = mig->mtx[mtx_ndx(mig,i,j)]) > FTINY) {
978 const RBFVAL *rbf1j = &mig->rbfv[1]->rbfa[j];
979 double rad1 = R2ANG(rbf1j->crad);
980 FVECT v;
981 int pos[2];
982 rbf->rbfa[n].peak = peak0 * mv * rbf->vtotal;
983 rbf->rbfa[n].crad = ANG2R(sqrt(rad0*rad0*(1.-t) +
984 rad1*rad1*t));
985 ovec_from_pos(v, rbf1j->gx, rbf1j->gy);
986 geodesic(v, v0, v, t, GEOD_REL);
987 pos_from_vec(pos, v);
988 rbf->rbfa[n].gx = pos[0];
989 rbf->rbfa[n].gy = pos[1];
990 ++n;
991 }
992 }
993 rbf->vtotal *= mig->rbfv[0]->vtotal; /* turn ratio into actual */
994 return(rbf);
995 memerr:
996 fputs("Out of memory in e_advect_rbf()\n", stderr);
997 exit(1);
998 return(NULL); /* pro forma return */
999 }
1000
1001 /* Insert vertex in ordered list */
1002 static void
1003 insert_vert(RBFNODE **vlist, RBFNODE *v)
1004 {
1005 int i, j;
1006
1007 for (i = 0; vlist[i] != NULL; i++) {
1008 if (v == vlist[i])
1009 return;
1010 if (v < vlist[i])
1011 break;
1012 }
1013 for (j = i; vlist[j] != NULL; j++)
1014 ;
1015 while (j > i) {
1016 vlist[j] = vlist[j-1];
1017 --j;
1018 }
1019 vlist[i] = v;
1020 }
1021
1022 /* Sort triangle edges in standard order */
1023 static void
1024 order_triangle(MIGRATION *miga[3])
1025 {
1026 RBFNODE *vert[4];
1027 MIGRATION *ord[3];
1028 int i;
1029 /* order vertices, first */
1030 memset(vert, 0, sizeof(vert));
1031 for (i = 0; i < 3; i++) {
1032 insert_vert(vert, miga[i]->rbfv[0]);
1033 insert_vert(vert, miga[i]->rbfv[1]);
1034 }
1035 /* identify edge 0 */
1036 for (i = 0; i < 3; i++)
1037 if (miga[i]->rbfv[0] == vert[0] &&
1038 miga[i]->rbfv[1] == vert[1]) {
1039 ord[0] = miga[i];
1040 break;
1041 }
1042 /* identify edge 1 */
1043 for (i = 0; i < 3; i++)
1044 if (miga[i]->rbfv[0] == vert[1] &&
1045 miga[i]->rbfv[1] == vert[2]) {
1046 ord[1] = miga[i];
1047 break;
1048 }
1049 /* identify edge 2 */
1050 for (i = 0; i < 3; i++)
1051 if (miga[i]->rbfv[0] == vert[0] &&
1052 miga[i]->rbfv[1] == vert[2]) {
1053 ord[2] = miga[i];
1054 break;
1055 }
1056 miga[0] = ord[0]; miga[1] = ord[1]; miga[2] = ord[2];
1057 }
1058
1059 /* Partially advect between recorded incident angles and allocate new RBF */
1060 static RBFNODE *
1061 advect_rbf(const FVECT invec)
1062 {
1063 MIGRATION *miga[3];
1064 RBFNODE *rbf;
1065 float mbfact, mcfact;
1066 int n, i, j, k;
1067 FVECT v0, v1, v2;
1068 double s, t;
1069
1070 if (!get_interp(miga, invec)) /* can't interpolate? */
1071 return(NULL);
1072 if (miga[1] == NULL) /* along edge? */
1073 return(e_advect_rbf(miga[0], invec));
1074 /* put in standard order */
1075 order_triangle(miga);
1076 /* figure out position */
1077 fcross(v0, miga[2]->rbfv[0]->invec, miga[2]->rbfv[1]->invec);
1078 normalize(v0);
1079 fcross(v2, miga[1]->rbfv[0]->invec, miga[1]->rbfv[1]->invec);
1080 normalize(v2);
1081 fcross(v1, invec, miga[1]->rbfv[1]->invec);
1082 normalize(v1);
1083 s = acos(DOT(v0,v1)) / acos(DOT(v0,v2));
1084 geodesic(v1, miga[0]->rbfv[0]->invec, miga[0]->rbfv[1]->invec,
1085 s, GEOD_REL);
1086 t = acos(DOT(v1,invec)) / acos(DOT(v1,miga[1]->rbfv[1]->invec));
1087 n = 0; /* count migrating particles */
1088 for (i = 0; i < mtx_nrows(miga[0]); i++)
1089 for (j = 0; j < mtx_ncols(miga[0]); j++)
1090 for (k = (miga[0]->mtx[mtx_ndx(miga[0],i,j)] > FTINY) *
1091 mtx_ncols(miga[2]); k--; )
1092 n += (miga[2]->mtx[mtx_ndx(miga[2],i,k)] > FTINY &&
1093 miga[1]->mtx[mtx_ndx(miga[1],j,k)] > FTINY);
1094
1095 rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1));
1096 if (rbf == NULL) {
1097 fputs("Out of memory in advect_rbf()\n", stderr);
1098 exit(1);
1099 }
1100 rbf->next = NULL; rbf->ejl = NULL;
1101 VCOPY(rbf->invec, invec);
1102 rbf->nrbf = n;
1103 n = 0; /* compute RBF lobes */
1104 mbfact = s * miga[0]->rbfv[1]->vtotal/miga[0]->rbfv[0]->vtotal *
1105 (1.-t + t*miga[1]->rbfv[1]->vtotal/miga[1]->rbfv[0]->vtotal);
1106 mcfact = (1.-s) *
1107 (1.-t + t*miga[2]->rbfv[1]->vtotal/miga[2]->rbfv[0]->vtotal);
1108 for (i = 0; i < mtx_nrows(miga[0]); i++) {
1109 const RBFVAL *rbf0i = &miga[0]->rbfv[0]->rbfa[i];
1110 const float w0i = rbf0i->peak;
1111 const double rad0i = R2ANG(rbf0i->crad);
1112 ovec_from_pos(v0, rbf0i->gx, rbf0i->gy);
1113 for (j = 0; j < mtx_ncols(miga[0]); j++) {
1114 const float ma = miga[0]->mtx[mtx_ndx(miga[0],i,j)];
1115 const RBFVAL *rbf1j;
1116 double rad1j, srad2;
1117 if (ma <= FTINY)
1118 continue;
1119 rbf1j = &miga[0]->rbfv[1]->rbfa[j];
1120 rad1j = R2ANG(rbf1j->crad);
1121 srad2 = (1.-s)*(1.-t)*rad0i*rad0i + s*(1.-t)*rad1j*rad1j;
1122 ovec_from_pos(v1, rbf1j->gx, rbf1j->gy);
1123 geodesic(v1, v0, v1, s, GEOD_REL);
1124 for (k = 0; k < mtx_ncols(miga[2]); k++) {
1125 float mb = miga[1]->mtx[mtx_ndx(miga[1],j,k)];
1126 float mc = miga[2]->mtx[mtx_ndx(miga[2],i,k)];
1127 const RBFVAL *rbf2k;
1128 double rad2k;
1129 FVECT vout;
1130 int pos[2];
1131 if ((mb <= FTINY) | (mc <= FTINY))
1132 continue;
1133 rbf2k = &miga[2]->rbfv[1]->rbfa[k];
1134 rbf->rbfa[n].peak = w0i * ma * (mb*mbfact + mc*mcfact);
1135 rad2k = R2ANG(rbf2k->crad);
1136 rbf->rbfa[n].crad = RAD2R(sqrt(srad2 + t*rad2k*rad2k));
1137 ovec_from_pos(v2, rbf2k->gx, rbf2k->gy);
1138 geodesic(vout, v1, v2, t, GEOD_REL);
1139 pos_from_vec(pos, vout);
1140 rbf->rbfa[n].gx = pos[0];
1141 rbf->rbfa[n].gy = pos[1];
1142 ++n;
1143 }
1144 }
1145 }
1146 rbf->vtotal = miga[0]->rbfv[0]->vtotal * (mbfact + mcfact);
1147 return(rbf);
1148 }
1149
1150 #if 1
1151 /* Test main produces a Radiance model from the given input file */
1152 int
1153 main(int argc, char *argv[])
1154 {
1155 char buf[128];
1156 FILE *pfp;
1157 double bsdf;
1158 FVECT dir;
1159 int i, j, n;
1160
1161 if (argc != 2) {
1162 fprintf(stderr, "Usage: %s input.dat > output.rad\n", argv[0]);
1163 return(1);
1164 }
1165 if (!load_pabopto_meas(argv[1]))
1166 return(1);
1167
1168 compute_radii();
1169 cull_values();
1170 make_rbfrep();
1171 /* produce spheres at meas. */
1172 puts("void plastic yellow\n0\n0\n5 .6 .4 .01 .04 .08\n");
1173 puts("void plastic pink\n0\n0\n5 .5 .05 .9 .04 .08\n");
1174 n = 0;
1175 for (i = 0; i < GRIDRES; i++)
1176 for (j = 0; j < GRIDRES; j++)
1177 if (dsf_grid[i][j].vsum > .0f) {
1178 ovec_from_pos(dir, i, j);
1179 bsdf = dsf_grid[i][j].vsum / dir[2];
1180 if (dsf_grid[i][j].nval) {
1181 printf("pink cone c%04d\n0\n0\n8\n", ++n);
1182 printf("\t%.6g %.6g %.6g\n",
1183 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
1184 printf("\t%.6g %.6g %.6g\n",
1185 dir[0]*(bsdf+.005), dir[1]*(bsdf+.005),
1186 dir[2]*(bsdf+.005));
1187 puts("\t.003\t0\n");
1188 } else {
1189 ovec_from_pos(dir, i, j);
1190 printf("yellow sphere s%04d\n0\n0\n", ++n);
1191 printf("4 %.6g %.6g %.6g .0015\n\n",
1192 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
1193 }
1194 }
1195 /* output continuous surface */
1196 puts("void trans tgreen\n0\n0\n7 .7 1 .7 .04 .04 .9 .9\n");
1197 fflush(stdout);
1198 sprintf(buf, "gensurf tgreen bsdf - - - %d %d", GRIDRES-1, GRIDRES-1);
1199 pfp = popen(buf, "w");
1200 if (pfp == NULL) {
1201 fputs(buf, stderr);
1202 fputs(": cannot start command\n", stderr);
1203 return(1);
1204 }
1205 for (i = 0; i < GRIDRES; i++)
1206 for (j = 0; j < GRIDRES; j++) {
1207 ovec_from_pos(dir, i, j);
1208 bsdf = eval_rbfrep(dsf_list, dir) / dir[2];
1209 fprintf(pfp, "%.8e %.8e %.8e\n",
1210 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
1211 }
1212 return(pclose(pfp)==0 ? 0 : 1);
1213 }
1214 #endif