ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
Revision: 2.20
Committed: Sat Oct 19 00:11:50 2013 UTC (10 years, 7 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.19: +1 -1 lines
State: FILE REMOVED
Error occurred while calculating annotation data.
Log Message:
Fixed overflow and tweaked culling operations

File Contents

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