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, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Created routines for interpolating anisotropic 2-D samples

File Contents

# Content
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 }