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