1 /******************************************************************************
2 *
3 * File: nnai.c
4 *
5 * Created: 15/11/2002
6 *
7 * Author: Pavel Sakov
8 * CSIRO Marine Research
9 *
10 * Purpose: Code for:
11 * -- Natural Neighbours Array Interpolator
12 *
13 * Description: `nnai' is a structure for conducting repeated Natural
14 * Neighbours interpolations when locations of input and output
15 * data points do not change. It re-uses interpolation weights
16 * calculated during initialisation and can be substantially
17 * faster than the more generic Natural Neighbours Point
18 * Interpolator `nnpi'.
19 *
20 * Revisions: None
21 *
22 *****************************************************************************/
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <math.h>
28 #include "nan.h"
29 #include "delaunay.h"
30 #include "nn.h"
31 #include "nn_internal.h"
32
33 typedef struct {
34 int nvertices;
35 int* vertices; /* vertex indices [nvertices] */
36 double* weights; /* vertex weights [nvertices] */
37 } nn_weights;
38
39 struct nnai {
40 delaunay* d;
41 double wmin;
42 double n; /* number of output points */
43 double* x; /* [n] */
44 double* y; /* [n] */
45 nn_weights* weights;
46 };
47
48 /** Builds Natural Neighbours array interpolator.
49 *
50 * This includes calculation of weights used in nnai_interpolate().
51 *
52 * @param d Delaunay triangulation
53 * @return Natural Neighbours interpolation
54 */
nnai_build(delaunay * d,int n,double * x,double * y)55 nnai* nnai_build(delaunay* d, int n, double* x, double* y)
56 {
57 nnai* nn = malloc(sizeof(nnai));
58 nnpi* nnpi = nnpi_create(d);
59 int* vertices;
60 double* weights;
61 int i;
62
63 if (n <= 0)
64 nn_quit("nnai_create(): n = %d\n", n);
65
66 nn->d = d;
67 nn->n = n;
68 nn->x = malloc(n * sizeof(double));
69 memcpy(nn->x, x, n * sizeof(double));
70 nn->y = malloc(n * sizeof(double));
71 memcpy(nn->y, y, n * sizeof(double));
72 nn->weights = malloc(n * sizeof(nn_weights));
73
74 for (i = 0; i < n; ++i) {
75 nn_weights* w = &nn->weights[i];
76 point p;
77
78 p.x = x[i];
79 p.y = y[i];
80
81 nnpi_calculate_weights(nnpi, &p);
82
83 vertices = nnpi_get_vertices(nnpi);
84 weights = nnpi_get_weights(nnpi);
85
86 w->nvertices = nnpi_get_nvertices(nnpi);
87 w->vertices = malloc(w->nvertices * sizeof(int));
88 memcpy(w->vertices, vertices, w->nvertices * sizeof(int));
89 w->weights = malloc(w->nvertices * sizeof(double));
90 memcpy(w->weights, weights, w->nvertices * sizeof(double));
91 }
92
93 nnpi_destroy(nnpi);
94
95 return nn;
96 }
97
98 /* Destroys Natural Neighbours array interpolator.
99 *
100 * @param nn Structure to be destroyed
101 */
nnai_destroy(nnai * nn)102 void nnai_destroy(nnai* nn)
103 {
104 int i;
105
106 for (i = 0; i < nn->n; ++i) {
107 nn_weights* w = &nn->weights[i];
108
109 free(w->vertices);
110 free(w->weights);
111 }
112
113 free(nn->x);
114 free(nn->y);
115 free(nn->weights);
116 free(nn);
117 }
118
119 /* Conducts NN interpolation in a fixed array of output points using
120 * data specified in a fixed array of input points. Uses pre-calculated
121 * weights.
122 *
123 * @param nn NN array interpolator
124 * @param zin input data [nn->d->npoints]
125 * @param zout output data [nn->n]. Must be pre-allocated!
126 */
nnai_interpolate(nnai * nn,double * zin,double * zout)127 void nnai_interpolate(nnai* nn, double* zin, double* zout)
128 {
129 int i;
130
131 for (i = 0; i < nn->n; ++i) {
132 nn_weights* w = &nn->weights[i];
133 double z = 0.0;
134 int j;
135
136 for (j = 0; j < w->nvertices; ++j) {
137 double weight = w->weights[j];
138
139 if (weight < nn->wmin) {
140 z = NaN;
141 break;
142 }
143 z += weight * zin[w->vertices[j]];
144 }
145
146 zout[i] = z;
147 }
148 }
149
150 /* Sets minimal allowed weight for Natural Neighbours interpolation.
151 *
152 * For Sibson interpolation, setting wmin = 0 is equivalent to interpolating
153 * inside convex hall of the data only (returning NaNs otherwise).
154 *
155 * @param nn Natural Neighbours array interpolator
156 * @param wmin Minimal allowed weight
157 */
nnai_setwmin(nnai * nn,double wmin)158 void nnai_setwmin(nnai* nn, double wmin)
159 {
160 nn->wmin = wmin;
161 }
162
163 /* The rest of this file contains a number of test programs.
164 */
165 #if defined(NNAI_TEST)
166
167 #include <sys/time.h>
168
169 #define NPOINTSIN 10000
170 #define NMIN 10
171 #define NX 101
172 #define NXMIN 1
173
174 #define SQ(x) ((x) * (x))
175
franke(double x,double y)176 static double franke(double x, double y)
177 {
178 x *= 9.0;
179 y *= 9.0;
180 return 0.75 * exp((-SQ(x - 2.0) - SQ(y - 2.0)) / 4.0)
181 + 0.75 * exp(-SQ(x - 2.0) / 49.0 - (y - 2.0) / 10.0)
182 + 0.5 * exp((-SQ(x - 7.0) - SQ(y - 3.0)) / 4.0)
183 - 0.2 * exp(-SQ(x - 4.0) - SQ(y - 7.0));
184 }
185
usage()186 static void usage()
187 {
188 printf("Usage: nnai_test [-v|-V] [-n <nin> <nxout>]\n");
189 printf("Options:\n");
190 printf(" -a -- use non-Sibsonian interpolation rule\n");
191 printf(" -n <nin> <nout>:\n");
192 printf(" <nin> -- number of input points (default = 10000)\n");
193 printf(" <nout> -- number of output points per side (default = 64)\n");
194 printf(" -v -- verbose\n");
195 printf(" -V -- very verbose\n");
196 }
197
main(int argc,char * argv[])198 int main(int argc, char* argv[])
199 {
200 int nin = NPOINTSIN;
201 int nx = NX;
202 int nout = 0;
203 point* pin = NULL;
204 delaunay* d = NULL;
205 point* pout = NULL;
206 nnai* nn = NULL;
207 double* zin = NULL;
208 double* xout = NULL;
209 double* yout = NULL;
210 double* zout = NULL;
211 int cpi = -1; /* control point index */
212 struct timeval tv0, tv1, tv2;
213 struct timezone tz;
214 int i;
215
216 i = 1;
217 while (i < argc) {
218 switch (argv[i][1]) {
219 case 'a':
220 i++;
221 nn_rule = NON_SIBSONIAN;
222 break;
223 case 'n':
224 i++;
225 if (i >= argc)
226 nn_quit("no number of data points found after -i\n");
227 nin = atoi(argv[i]);
228 i++;
229 if (i >= argc)
230 nn_quit("no number of ouput points per side found after -i\n");
231 nx = atoi(argv[i]);
232 i++;
233 break;
234 case 'v':
235 i++;
236 nn_verbose = 1;
237 break;
238 case 'V':
239 i++;
240 nn_verbose = 2;
241 break;
242 default:
243 usage();
244 break;
245 }
246 }
247
248 if (nin < NMIN)
249 nin = NMIN;
250 if (nx < NXMIN)
251 nx = NXMIN;
252
253 printf("\nTest of Natural Neighbours array interpolator:\n\n");
254 printf(" %d data points\n", nin);
255 printf(" %d output points\n", nx * nx);
256
257 /*
258 * generate data
259 */
260 printf(" generating data:\n");
261 fflush(stdout);
262 pin = malloc(nin * sizeof(point));
263 zin = malloc(nin * sizeof(double));
264 for (i = 0; i < nin; ++i) {
265 point* p = &pin[i];
266
267 p->x = (double) random() / RAND_MAX;
268 p->y = (double) random() / RAND_MAX;
269 p->z = franke(p->x, p->y);
270 zin[i] = p->z;
271 if (nn_verbose)
272 printf(" (%f, %f, %f)\n", p->x, p->y, p->z);
273 }
274
275 /*
276 * triangulate
277 */
278 printf(" triangulating:\n");
279 fflush(stdout);
280 d = delaunay_build(nin, pin, 0, NULL, 0, NULL);
281
282 /*
283 * generate output points
284 */
285 points_generate(-0.1, 1.1, -0.1, 1.1, nx, nx, &nout, &pout);
286 xout = malloc(nout * sizeof(double));
287 yout = malloc(nout * sizeof(double));
288 zout = malloc(nout * sizeof(double));
289 for (i = 0; i < nout; ++i) {
290 point* p = &pout[i];
291
292 xout[i] = p->x;
293 yout[i] = p->y;
294 zout[i] = NaN;
295 }
296 cpi = (nx / 2) * (nx + 1);
297
298 gettimeofday(&tv0, &tz);
299
300 /*
301 * create interpolator
302 */
303 printf(" creating interpolator:\n");
304 fflush(stdout);
305 nn = nnai_build(d, nout, xout, yout);
306
307 fflush(stdout);
308 gettimeofday(&tv1, &tz);
309 {
310 long dt = 1000000 * (tv1.tv_sec - tv0.tv_sec) + tv1.tv_usec - tv0.tv_usec;
311
312 printf(" interpolator creation time = %ld us (%.2f us / point)\n", dt, (double) dt / nout);
313 }
314
315 /*
316 * interpolate
317 */
318 printf(" interpolating:\n");
319 fflush(stdout);
320 nnai_interpolate(nn, zin, zout);
321 if (nn_verbose)
322 for (i = 0; i < nout; ++i)
323 printf(" (%f, %f, %f)\n", xout[i], yout[i], zout[i]);
324
325 fflush(stdout);
326 gettimeofday(&tv2, &tz);
327 {
328 long dt = 1000000.0 * (tv2.tv_sec - tv1.tv_sec) + tv2.tv_usec - tv1.tv_usec;
329
330 printf(" interpolation time = %ld us (%.2f us / point)\n", dt, (double) dt / nout);
331 }
332
333 if (!nn_verbose)
334 printf(" control point: (%f, %f, %f) (expected z = %f)\n", xout[cpi], yout[cpi], zout[cpi], franke(xout[cpi], yout[cpi]));
335
336 /*
337 * interpolate one more time
338 */
339 printf(" interpolating one more time:\n");
340 fflush(stdout);
341 nnai_interpolate(nn, zin, zout);
342 if (nn_verbose)
343 for (i = 0; i < nout; ++i)
344 printf(" (%f, %f, %f)\n", xout[i], yout[i], zout[i]);
345
346 fflush(stdout);
347 gettimeofday(&tv0, &tz);
348 {
349 long dt = 1000000.0 * (tv0.tv_sec - tv2.tv_sec) + tv0.tv_usec - tv2.tv_usec;
350
351 printf(" interpolation time = %ld us (%.2f us / point)\n", dt, (double) dt / nout);
352 }
353
354 if (!nn_verbose)
355 printf(" control point: (%f, %f, %f) (expected z = %f)\n", xout[cpi], yout[cpi], zout[cpi], franke(xout[cpi], yout[cpi]));
356
357 /*
358 * change the data
359 */
360 printf(" entering new data:\n");
361 fflush(stdout);
362 for (i = 0; i < nin; ++i) {
363 point* p = &pin[i];
364
365 p->z = p->x * p->x - p->y * p->y;
366 zin[i] = p->z;
367 if (nn_verbose)
368 printf(" (%f, %f, %f)\n", p->x, p->y, p->z);
369 }
370
371 /*
372 * interpolate
373 */
374 printf(" interpolating:\n");
375 fflush(stdout);
376 nnai_interpolate(nn, zin, zout);
377 if (nn_verbose)
378 for (i = 0; i < nout; ++i)
379 printf(" (%f, %f, %f)\n", xout[i], yout[i], zout[i]);
380
381 if (!nn_verbose)
382 printf(" control point: (%f, %f, %f) (expected z = %f)\n", xout[cpi], yout[cpi], zout[cpi], xout[cpi] * xout[cpi] - yout[cpi] * yout[cpi]);
383
384 /*
385 * restore old data
386 */
387 printf(" restoring data:\n");
388 fflush(stdout);
389 for (i = 0; i < nin; ++i) {
390 point* p = &pin[i];
391
392 p->z = franke(p->x, p->y);
393 zin[i] = p->z;
394 if (nn_verbose)
395 printf(" (%f, %f, %f)\n", p->x, p->y, p->z);
396 }
397
398 /*
399 * interpolate
400 */
401 printf(" interpolating:\n");
402 fflush(stdout);
403 nnai_interpolate(nn, zin, zout);
404 if (nn_verbose)
405 for (i = 0; i < nout; ++i)
406 printf(" (%f, %f, %f)\n", xout[i], yout[i], zout[i]);
407
408 if (!nn_verbose)
409 printf(" control point: (%f, %f, %f) (expected z = %f)\n", xout[cpi], yout[cpi], zout[cpi], franke(xout[cpi], yout[cpi]));
410
411 printf("\n");
412
413 nnai_destroy(nn);
414 free(zin);
415 free(xout);
416 free(yout);
417 free(zout);
418 free(pout);
419 delaunay_destroy(d);
420 free(pin);
421
422 return 0;
423 }
424
425 #endif
426