ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/interp2d.c
Revision: 2.1
Committed: Sat Feb 9 00:55:40 2013 UTC (11 years, 3 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Created routines for interpolating anisotropic 2-D samples

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2     static const char RCSid[] = "$Id$";
3     #endif
4     /*
5     * General interpolation method for unstructured values on 2-D plane.
6     *
7     * G.Ward Feb 2013
8     */
9    
10     #include "copyright.h"
11    
12     /*************************************************************
13     * This is a general method for 2-D interpolation similar to
14     * radial basis functions but allowing for a good deal of local
15     * anisotropy in the point distribution. Each sample point
16     * is examined to determine the closest neighboring samples in
17     * each of NI2DIR surrounding directions. To speed this
18     * calculation, we sort the data into 3 half-planes and
19     * perform simple tests to see which neighbor is closest in
20     * a each direction. Once we have our approximate neighborhood
21     * for a sample, we can use it in a Gaussian weighting scheme
22     * with anisotropic surround. This gives us a fairly smooth
23     * interpolation however the sample points may be initially
24     * distributed. Evaluation is accelerated by use of a fast
25     * approximation to the atan2(y,x) function.
26     **************************************************************/
27    
28     #include <stdio.h>
29     #include <stdlib.h>
30     #include "rtmath.h"
31     #include "interp2d.h"
32    
33     #define DECODE_RAD(ip,er) ((ip)->rmin*(1. + .5*(er)))
34     #define ENCODE_RAD(ip,r) ((int)(2.*(r)/(ip)->rmin) - 2)
35    
36     /* Sample order (private) */
37     typedef struct {
38     int si; /* sample index */
39     float dm; /* distance measure in this direction */
40     } SAMPORD;
41    
42     /* Allocate a new set of interpolation samples */
43     INTERP2 *
44     interp2_alloc(int nsamps)
45     {
46     INTERP2 *nip;
47    
48     if (nsamps <= 1)
49     return(NULL);
50    
51     nip = (INTERP2 *)malloc(sizeof(INTERP2) + sizeof(float)*2*(nsamps-1));
52     if (nip == NULL)
53     return(NULL);
54    
55     nip->ns = nsamps;
56     nip->rmin = .5; /* default radius minimum */
57     nip->smf = NI2DSMF; /* default smoothing factor */
58     nip->ra = NULL;
59     /* caller must assign spt[] array */
60     return(nip);
61     }
62    
63     /* private call-back to sort position index */
64     static int
65     cmp_spos(const void *p1, const void *p2)
66     {
67     const SAMPORD *so1 = (const SAMPORD *)p1;
68     const SAMPORD *so2 = (const SAMPORD *)p2;
69    
70     if (so1->dm > so2->dm)
71     return 1;
72     if (so1->dm < so2->dm)
73     return -1;
74     return 0;
75     }
76    
77     /* private routine to encode radius with range checks */
78     static int
79     encode_radius(const INTERP2 *ip, double r)
80     {
81     const int er = ENCODE_RAD(ip, r);
82    
83     if (er <= 0)
84     return(0);
85     if (er >= 0xffff)
86     return(0xffff);
87     return(er);
88     }
89    
90     /* Compute anisotropic Gaussian basis function interpolant */
91     static int
92     interp2_compute(INTERP2 *ip)
93     {
94     SAMPORD *sortord;
95     int *rightrndx, *leftrndx;
96     int bd;
97     /* sanity checks */
98     if (ip == NULL || (ip->ns <= 1) | (ip->rmin <= 0))
99     return(0);
100     /* need to allocate? */
101     if (ip->ra == NULL) {
102     ip->ra = (unsigned short (*)[NI2DIR])malloc(
103     sizeof(unsigned short)*NI2DIR*ip->ns);
104     if (ip->ra == NULL)
105     return(0);
106     }
107     /* get temporary arrays */
108     sortord = (SAMPORD *)malloc(sizeof(SAMPORD)*ip->ns);
109     rightrndx = (int *)malloc(sizeof(int)*ip->ns);
110     leftrndx = (int *)malloc(sizeof(int)*ip->ns);
111     if ((sortord == NULL) | (rightrndx == NULL) | (leftrndx == NULL))
112     return(0);
113     /* run through bidirections */
114     for (bd = 0; bd < NI2DIR/2; bd++) {
115     const double ang = 2.*PI/NI2DIR*bd;
116     double cosd, sind;
117     int i;
118     /* create right reverse index */
119     if (bd) { /* re-use from prev. iteration? */
120     int *sptr = rightrndx;
121     rightrndx = leftrndx;
122     leftrndx = sptr;
123     } else { /* else compute it */
124     cosd = cos(ang + (PI/2. - PI/NI2DIR));
125     sind = sin(ang + (PI/2. - PI/NI2DIR));
126     for (i = 0; i < ip->ns; i++) {
127     sortord[i].si = i;
128     sortord[i].dm = cosd*ip->spt[i][0] + sind*ip->spt[i][1];
129     }
130     qsort(sortord, ip->ns, sizeof(SAMPORD), &cmp_spos);
131     for (i = 0; i < ip->ns; i++)
132     rightrndx[sortord[i].si] = i;
133     }
134     /* create new left reverse index */
135     cosd = cos(ang + (PI/2. + PI/NI2DIR));
136     sind = sin(ang + (PI/2. + PI/NI2DIR));
137     for (i = 0; i < ip->ns; i++) {
138     sortord[i].si = i;
139     sortord[i].dm = cosd*ip->spt[i][0] + sind*ip->spt[i][1];
140     }
141     qsort(sortord, ip->ns, sizeof(SAMPORD), &cmp_spos);
142     for (i = 0; i < ip->ns; i++)
143     leftrndx[sortord[i].si] = i;
144     /* sort grid values in this direction */
145     cosd = cos(ang);
146     sind = sin(ang);
147     for (i = 0; i < ip->ns; i++) {
148     sortord[i].si = i;
149     sortord[i].dm = cosd*ip->spt[i][0] + sind*ip->spt[i][1];
150     }
151     qsort(sortord, ip->ns, sizeof(SAMPORD), &cmp_spos);
152     /* find nearest neighbors each side */
153     for (i = 0; i < ip->ns; i++) {
154     const int rpos = rightrndx[sortord[i].si];
155     const int lpos = leftrndx[sortord[i].si];
156     int j;
157     /* preload with large radius */
158     ip->ra[i][bd] = ip->ra[i][bd+NI2DIR/2] = encode_radius(ip,
159     .25*(sortord[ip->ns-1].dm - sortord[0].dm));
160     for (j = i; ++j < ip->ns; ) /* nearest above */
161     if (rightrndx[sortord[j].si] > rpos &&
162     leftrndx[sortord[j].si] < lpos) {
163     ip->ra[i][bd] = encode_radius(ip,
164     .5*(sortord[j].dm - sortord[i].dm));
165     break;
166     }
167     for (j = i; j-- > 0; ) /* nearest below */
168     if (rightrndx[sortord[j].si] < rpos &&
169     leftrndx[sortord[j].si] > lpos) {
170     ip->ra[i][bd+NI2DIR/2] = encode_radius(ip,
171     .5*(sortord[i].dm - sortord[j].dm));
172     break;
173     }
174     }
175     }
176     free(sortord); /* clean up */
177     free(rightrndx);
178     free(leftrndx);
179     return(1);
180     }
181    
182     /* private call returns log of raw weight for a particular sample */
183     static double
184     get_ln_wt(const INTERP2 *ip, const int i, double x, double y)
185     {
186     double dir, rd;
187     int ri;
188     /* get relative direction */
189     x -= ip->spt[i][0];
190     y -= ip->spt[i][1];
191     dir = atan2a(y, x);
192     dir += 2.*PI*(dir < 0);
193     /* linear radius interpolation */
194     rd = dir * (NI2DIR/2./PI);
195     ri = (int)rd;
196     rd -= (double)ri;
197     rd = (1.-rd)*ip->ra[i][ri] + rd*ip->ra[i][(ri+1)%NI2DIR];
198     rd = ip->smf * DECODE_RAD(ip, rd);
199     /* return log of Gaussian weight */
200     return( (x*x + y*y) / (-2.*rd*rd) );
201     }
202    
203     /* Assign full set of normalized weights to interpolate the given position */
204     int
205     interp2_weights(float wtv[], INTERP2 *ip, double x, double y)
206     {
207     double wnorm;
208     int i;
209    
210     if ((wtv == NULL) | (ip == NULL))
211     return(0);
212     /* need to compute interpolant? */
213     if (ip->ra == NULL && !interp2_compute(ip))
214     return(0);
215    
216     wnorm = 0; /* compute raw weights */
217     for (i = ip->ns; i--; ) {
218     double wt = get_ln_wt(ip, i, x, y);
219     if (wt < -21.) {
220     wtv[i] = 0; /* ignore weights < 1e-9 */
221     continue;
222     }
223     wt = exp(wt); /* Gaussian weight */
224     wtv[i] = wt;
225     wnorm += wt;
226     }
227     if (wnorm <= 0) /* too far from all our samples! */
228     return(0);
229     wnorm = 1./wnorm; /* normalize weights */
230     for (i = ip->ns; i--; )
231     wtv[i] *= wnorm;
232     return(ip->ns); /* all done */
233     }
234    
235    
236     /* Get normalized weights and indexes for n best samples in descending order */
237     int
238     interp2_topsamp(float wt[], int si[], const int n, INTERP2 *ip, double x, double y)
239     {
240     int nn = 0;
241     double wnorm;
242     int i, j;
243    
244     if ((n <= 0) | (wt == NULL) | (si == NULL) | (ip == NULL))
245     return(0);
246     /* need to compute interpolant? */
247     if (ip->ra == NULL && !interp2_compute(ip))
248     return(0);
249     /* identify top n weights */
250     for (i = ip->ns; i--; ) {
251     const double lnwt = get_ln_wt(ip, i, x, y);
252     for (j = nn; j > 0; j--) {
253     if (wt[j-1] >= lnwt)
254     break;
255     if (j < n) {
256     wt[j] = wt[j-1];
257     si[j] = si[j-1];
258     }
259     }
260     if (j < n) { /* add/insert sample */
261     wt[j] = lnwt;
262     si[j] = i;
263     nn += (nn < n);
264     }
265     }
266     wnorm = 0; /* exponentiate and normalize */
267     for (j = nn; j--; ) {
268     double dwt = exp(wt[j]);
269     wt[j] = dwt;
270     wnorm += dwt;
271     }
272     if (wnorm <= 0)
273     return(0);
274     wnorm = 1./wnorm;
275     for (j = nn; j--; )
276     wt[j] *= wnorm;
277     return(nn); /* return actual # samples */
278     }
279    
280     /* Free interpolant */
281     void
282     interp2_free(INTERP2 *ip)
283     {
284     if (ip == NULL)
285     return;
286     if (ip->ra != NULL)
287     free(ip->ra);
288     free(ip);
289     }