1 /*****************************************************************************
2  *
3  * File:           nnpi.c
4  *
5  * Created:        15/11/2002
6  *
7  * Author:         Pavel Sakov
8  *                 CSIRO Marine Research
9  *
10  * Purpose:        Code for:
11  *                 -- Natural Neighbours Point Interpolator
12  *                 -- Natural Neighbours Point Hashing Interpolator
13  *
14  * Description:    `nnpi' -- "Natural Neighbours Point Interpolator" -- is a
15  *                 structure for conducting Natural Neighbours interpolation on
16  *                 a "point-to-point" basis. Because it calculates weights for
17  *                 each output point, `nnpi' does not take advantage of
18  *                 repeated interpolations when locations of input and output
19  *                 data points do not change -- use `nnhpi' or `nnai' in these
20  *                 cases.
21  *
22  *                 `nnhpi' -- "Natural Neighbours Hashing Point Interpolator"
23  *                 is a structure for conducting repeated Natural Neighbours
24  *                 interpolations when (i) input data points have constant
25  *                 locations and (ii) locations of output data points are often
26  *                 repeated.
27  *
28  *                 For Sibson NN interpolation this code uses Dave Watson's
29  *                 algorithm (Watson, D. F. nngridr: An implementation of
30  *                 natural neighbour interpolation. David Watson, 1994).
31  *
32  *                 For non-Sibsonian NN interpolation this code uses Eq.(40)
33  *                 from Sukumar, N., Moran, B., Semenov, A. Yu, and
34  *                 Belikov V. V. Natural neighbour Galerkin methods.
35  *                 Int. J. Numer. Meth. Engng 2001, v.50: 1�27.
36  *
37  *
38  * Revisions:      01/04/2003 PS: modified nnpi_triangle_process(): for
39  *                   Sibson interpolation, if circle_build fails(), now a
40  *                   local copy of a point is moved slightly rather than the
41  *                   data point itself. The later approach have found leading
42  *                   to inconsistencies of the new point position with the
43  *                   earlier built triangulation.
44  *                 22/11/2006 PS: introduced special treatment for big circles
45  *                   by moving their centers in a certain way, closer to the
46  *                   data points; added hashtable nn->bad to account for
47  *                   such events. Modified _nnpi_calculate_weights() to handle
48  *                   the case when instead of being in between two data points
49  *                   the interpolation point is close to a data point.
50  *                 30/10/2007 PS: Modified treatment of degenerate cases in
51  *                   nnpi_triangle_process(), many thanks to John Gerschwitz,
52  *                   Petroleum Geo-Services, for exposing the defect introduced
53  *                   in v. 1.69. Changed EPS_SAME from 1.0e-15 to 1.0e-8. Also
54  *                   modified nnpi_calculate_weights().
55  *                 30/10/2007 PS: Got rid of memset(nn->d->flags, ...) in
56  *                   nnpi_reset(). The flags are reset now internally on return
57  *                   from delaunay_circles_find(). This is very important
58  *                   for large datasets, many thanks to John Gerschwitz,
59  *                   Petroleum Geo-Services, for identifying the problem.
60  *
61  *****************************************************************************/
62 
63 #include <stdlib.h>
64 #include <stdio.h>
65 #include <limits.h>
66 #include <float.h>
67 #include <string.h>
68 #include <assert.h>
69 #include <math.h>
70 #include "config.h"
71 #include "nan.h"
72 #include "hash.h"
73 #include "istack.h"
74 #include "delaunay.h"
75 #include "nn.h"
76 #include "nn_internal.h"
77 
78 struct nnpi {
79     delaunay* d;
80     double wmin;
81     int n;                      /* number of points processed */
82     /*
83      * work variables
84      */
85     int ncircles;
86     int nvertices;
87     int nallocated;
88     int* vertices;              /* vertex indices */
89     double* weights;
90     double dx, dy;              /* vertex perturbation */
91     hashtable* bad;             /* ids of vertices that require a special
92                                  * treatment */
93 };
94 
95 #define NSTART 10
96 #define NINC 10
97 #define EPS_SHIFT 1.0e-5
98 #define BIGNUMBER 1.0e+100
99 #define EPS_WMIN 1.0e-6
100 #define HT_SIZE 100
101 #define EPS_SAME 1.0e-8
102 
103 /* Creates Natural Neighbours point interpolator.
104  *
105  * @param d Delaunay triangulation
106  * @return Natural Neighbours interpolation
107  */
nnpi_create(delaunay * d)108 nnpi* nnpi_create(delaunay* d)
109 {
110     nnpi* nn = malloc(sizeof(nnpi));
111 
112     nn->d = d;
113     nn->wmin = -DBL_MAX;
114     nn->n = 0;
115     nn->ncircles = 0;
116     nn->vertices = calloc(NSTART, sizeof(int));
117     nn->weights = calloc(NSTART, sizeof(double));
118     nn->nvertices = 0;
119     nn->nallocated = NSTART;
120     nn->bad = NULL;
121 
122     return nn;
123 }
124 
125 /* Destroys Natural Neighbours point interpolator.
126  *
127  * @param nn Structure to be destroyed
128  */
nnpi_destroy(nnpi * nn)129 void nnpi_destroy(nnpi* nn)
130 {
131     free(nn->weights);
132     free(nn->vertices);
133     free(nn);
134 }
135 
nnpi_reset(nnpi * nn)136 void nnpi_reset(nnpi* nn)
137 {
138     nn->nvertices = 0;
139     nn->ncircles = 0;
140     if (nn->bad != NULL) {
141         ht_destroy(nn->bad);
142         nn->bad = NULL;
143     }
144 }
145 
nnpi_add_weight(nnpi * nn,int vertex,double w)146 static void nnpi_add_weight(nnpi* nn, int vertex, double w)
147 {
148     int i;
149 
150     /*
151      * find whether the vertex is already in the list
152      */
153     /*
154      * For clustered data the number of natural neighbours for a point may
155      * be quite big ( a few hundreds in example 2), and using hashtable here
156      * could accelerate things a bit. However, profiling shows that use of
157      * linear search is not a major issue.
158      */
159     for (i = 0; i < nn->nvertices; ++i)
160         if (nn->vertices[i] == vertex)
161             break;
162 
163     if (i == nn->nvertices) {   /* not in the list */
164         /*
165          * get more memory if necessary
166          */
167         if (nn->nvertices == nn->nallocated) {
168             nn->vertices = realloc(nn->vertices, (nn->nallocated + NINC) * sizeof(int));
169             nn->weights = realloc(nn->weights, (nn->nallocated + NINC) * sizeof(double));
170             nn->nallocated += NINC;
171         }
172 
173         /*
174          * add the vertex to the list
175          */
176         nn->vertices[i] = vertex;
177         nn->weights[i] = w;
178         nn->nvertices++;
179     } else                      /* in the list */
180         nn->weights[i] += w;
181 }
182 
183 /* This is a central procedure for the Natural Neighbours interpolation. It
184  * uses the Watson's algorithm for the required areas calculation and implies
185  * that the vertices of the delaunay triangulation are listed in uniform
186  * (clockwise or counterclockwise) order.
187  */
nnpi_triangle_process(nnpi * nn,point * p,int i)188 static void nnpi_triangle_process(nnpi* nn, point* p, int i)
189 {
190     delaunay* d = nn->d;
191     triangle* t = &d->triangles[i];
192     circle* c = &d->circles[i];
193     circle cs[3];
194     int j;
195 
196     /*
197      * There used to be a useful assertion here:
198      *
199      * assert(circle_contains(c, p));
200      *
201      * I removed it after introducing flag `contains' to
202      * delaunay_circles_find(). It looks like the code is robust enough to
203      * run without this assertion.
204      */
205 
206     /*
207      * Sibson interpolation by using Watson's algorithm
208      */
209     for (j = 0; j < 3; ++j) {
210         int j1 = (j + 1) % 3;
211         int j2 = (j + 2) % 3;
212         int v1 = t->vids[j1];
213         int v2 = t->vids[j2];
214 
215         if (!circle_build2(&cs[j], &d->points[v1], &d->points[v2], p)) {
216             point* p1 = &d->points[v1];
217             point* p2 = &d->points[v2];
218 
219             if ((fabs(p1->x - p->x) + fabs(p1->y - p->y)) / c->r < EPS_SAME) {
220                 /*
221                  * if (p1->x == p->x && p1->y == p->y) {
222                  */
223                 nnpi_add_weight(nn, v1, BIGNUMBER);
224                 return;
225             } else if ((fabs(p2->x - p->x) + fabs(p2->y - p->y)) / c->r < EPS_SAME) {
226                 /*
227                  * } else if (p2->x == p->x && p2->y == p->y) {
228                  */
229                 nnpi_add_weight(nn, v2, BIGNUMBER);
230                 return;
231             }
232         }
233     }
234 
235     for (j = 0; j < 3; ++j) {
236         int j1 = (j + 1) % 3;
237         int j2 = (j + 2) % 3;
238         double det = ((cs[j1].x - c->x) * (cs[j2].y - c->y) - (cs[j2].x - c->x) * (cs[j1].y - c->y));
239 
240         if (isnan(det)) {
241             /*
242              * Here, if the determinant is NaN, then the interpolation point
243              * is almost in between two data points. This case is difficult to
244              * handle robustly because the areas (determinants) calculated by
245              * Watson's algorithm are obtained as a diference between two big
246              * numbers. This case is handled here in the following way.
247              *
248              * If a circle is recognised as very large in circle_build2(), then
249              * its parameters are replaced by NaNs, which results in the
250              * variable `det' above being NaN.
251              *
252              * When this happens inside convex hall of the data, there is
253              * always a triangle on another side of the edge, processing of
254              * which also produces an invalid circle. Processing of this edge
255              * yields two pairs of infinite determinants, with singularities
256              * of each pair cancelling if the point moves slightly off the edge.
257              *
258              * Each of the determinants corresponds to the (signed) area of a
259              * triangle, and an inifinite determinant corresponds to the area of
260              * a triangle with one vertex moved to infinity. "Subtracting" one
261              * triangle from another within each pair yields a valid
262              * quadrilateral (in fact, a trapezoid). The doubled area of these
263              * quadrilaterals is calculated in the cycle over ii below.
264              */
265             int j1bad = isnan(cs[j1].x);
266             int key[2];
267             double* v = NULL;
268 
269             key[0] = t->vids[j];
270 
271             if (nn->bad == NULL)
272                 nn->bad = ht_create_i2(HT_SIZE);
273 
274             key[1] = (j1bad) ? t->vids[j2] : t->vids[j1];
275             v = ht_find(nn->bad, &key);
276 
277             if (v == NULL) {
278                 v = malloc(8 * sizeof(double));
279                 if (j1bad) {
280                     v[0] = cs[j2].x;
281                     v[1] = cs[j2].y;
282                 } else {
283                     v[0] = cs[j1].x;
284                     v[1] = cs[j1].y;
285                 }
286                 v[2] = c->x;
287                 v[3] = c->y;
288                 (void) ht_insert(nn->bad, &key, v);
289                 det = 0.0;
290             } else {
291                 int ii;
292 
293                 if (j1bad) {
294                     v[6] = cs[j2].x;
295                     v[7] = cs[j2].y;
296                 } else {
297                     v[6] = cs[j1].x;
298                     v[7] = cs[j1].y;
299                 }
300                 v[4] = c->x;
301                 v[5] = c->y;
302 
303                 det = 0;
304                 for (ii = 0; ii < 4; ++ii) {
305                     int ii1 = (ii + 1) % 4;
306 
307                     det += (v[ii * 2] + v[ii1 * 2]) * (v[ii * 2 + 1] - v[ii1 * 2 + 1]);
308                 }
309                 det = fabs(det);
310 
311                 free(v);
312                 ht_delete(nn->bad, &key);
313             }
314         }
315 
316         nnpi_add_weight(nn, t->vids[j], det);
317     }
318 }
319 
compare_int(const void * p1,const void * p2)320 static int compare_int(const void* p1, const void* p2)
321 {
322     int* v1 = (int*) p1;
323     int* v2 = (int*) p2;
324 
325     if (*v1 > *v2)
326         return 1;
327     else if (*v1 < *v2)
328         return -1;
329     else
330         return 0;
331 }
332 
333 typedef struct {
334     point* p0;
335     point* p1;
336     point* p;
337     int i;
338 } indexedpoint;
339 
onleftside(point * p,point * p0,point * p1)340 static int onleftside(point* p, point* p0, point* p1)
341 {
342     return (p0->x - p->x) * (p1->y - p->y) > (p1->x - p->x) * (p0->y - p->y);
343 }
344 
compare_indexedpoints(const void * pp1,const void * pp2)345 static int compare_indexedpoints(const void* pp1, const void* pp2)
346 {
347     indexedpoint* ip1 = (indexedpoint*) pp1;
348     indexedpoint* ip2 = (indexedpoint*) pp2;
349     point* p0 = ip1->p0;
350     point* p1 = ip1->p1;
351     point* a = ip1->p;
352     point* b = ip2->p;
353 
354     if (onleftside(a, p0, b)) {
355         if (onleftside(a, p0, p1) && !onleftside(b, p0, p1))
356             /*
357              * (the reason for the second check is that while we want to sort
358              * the natural neighbours in a clockwise manner, one needs to break
359              * the circuit at some point)
360              */
361             return 1;
362         else
363             return -1;
364     } else {
365         if (onleftside(b, p0, p1) && !onleftside(a, p0, p1))
366             /*
367              * (see the comment above)
368              */
369             return -1;
370         else
371             return 1;
372     }
373 }
374 
nnpi_getneighbours(nnpi * nn,point * p,int nt,int * tids,int * n,int ** nids)375 static void nnpi_getneighbours(nnpi* nn, point* p, int nt, int* tids, int* n, int** nids)
376 {
377     delaunay* d = nn->d;
378     istack* neighbours = istack_create();
379     indexedpoint* v = NULL;
380     int i;
381 
382     for (i = 0; i < nt; ++i) {
383         triangle* t = &d->triangles[tids[i]];
384 
385         istack_push(neighbours, t->vids[0]);
386         istack_push(neighbours, t->vids[1]);
387         istack_push(neighbours, t->vids[2]);
388     }
389     qsort(neighbours->v, neighbours->n, sizeof(int), compare_int);
390 
391     v = malloc(sizeof(indexedpoint) * neighbours->n);
392 
393     v[0].p = &d->points[neighbours->v[0]];
394     v[0].i = neighbours->v[0];
395     *n = 1;
396     for (i = 1; i < neighbours->n; ++i) {
397         if (neighbours->v[i] == neighbours->v[i - 1])
398             continue;
399         v[*n].p = &d->points[neighbours->v[i]];
400         v[*n].i = neighbours->v[i];
401         (*n)++;
402     }
403 
404     /*
405      * I assume that if there is exactly one tricircle the point belongs to,
406      * then number of natural neighbours *n = 3, and they are already sorted
407      * in the right way in triangulation process.
408      */
409     if (*n > 3) {
410         v[0].p0 = NULL;
411         v[0].p1 = NULL;
412         for (i = 1; i < *n; ++i) {
413             v[i].p0 = p;
414             v[i].p1 = v[0].p;
415         }
416 
417         qsort(&v[1], *n - 1, sizeof(indexedpoint), compare_indexedpoints);
418     }
419 
420     (*nids) = malloc(*n * sizeof(int));
421 
422     for (i = 0; i < *n; ++i)
423         (*nids)[i] = v[i].i;
424 
425     istack_destroy(neighbours);
426     free(v);
427 }
428 
nnpi_neighbours_process(nnpi * nn,point * p,int n,int * nids)429 static int nnpi_neighbours_process(nnpi* nn, point* p, int n, int* nids)
430 {
431     delaunay* d = nn->d;
432     int i;
433 
434     for (i = 0; i < n; ++i) {
435         int im1 = (i + n - 1) % n;
436         int ip1 = (i + 1) % n;
437         point* p0 = &d->points[nids[i]];
438         point* pp1 = &d->points[nids[ip1]];
439         point* pm1 = &d->points[nids[im1]];
440         double nom1, nom2, denom1, denom2;
441 
442         denom1 = (p0->x - p->x) * (pp1->y - p->y) - (p0->y - p->y) * (pp1->x - p->x);
443         denom2 = (p0->x - p->x) * (pm1->y - p->y) - (p0->y - p->y) * (pm1->x - p->x);
444         if (denom1 == 0.0) {
445             if (p->x == p0->x && p->y == p0->y) {
446                 nnpi_add_weight(nn, nids[i], BIGNUMBER);
447                 return 1;
448             } else if (p->x == pp1->x && p->y == pp1->y) {
449                 nnpi_add_weight(nn, nids[ip1], BIGNUMBER);
450                 return 1;
451             } else {
452                 nn->dx = EPS_SHIFT * (pp1->y - p0->y);
453                 nn->dy = -EPS_SHIFT * (pp1->x - p0->x);
454                 return 0;
455             }
456         }
457         if (denom2 == 0.0) {
458             if (p->x == pm1->x && p->y == pm1->y) {
459                 nnpi_add_weight(nn, nids[im1], BIGNUMBER);
460                 return 1;
461             } else {
462                 nn->dx = EPS_SHIFT * (pm1->y - p0->y);
463                 nn->dy = -EPS_SHIFT * (pm1->x - p0->x);
464                 return 0;
465             }
466         }
467 
468         nom1 = (p0->x - pp1->x) * (pp1->x - p->x) + (p0->y - pp1->y) * (pp1->y - p->y);
469         nom2 = (p0->x - pm1->x) * (pm1->x - p->x) + (p0->y - pm1->y) * (pm1->y - p->y);
470         nnpi_add_weight(nn, nids[i], nom1 / denom1 - nom2 / denom2);
471     }
472 
473     return 1;
474 }
475 
_nnpi_calculate_weights(nnpi * nn,point * p)476 static int _nnpi_calculate_weights(nnpi* nn, point* p)
477 {
478     int* tids = NULL;
479     int i;
480 
481     delaunay_circles_find(nn->d, p, &nn->ncircles, &tids);
482     if (nn->ncircles == 0)
483         return 1;
484 
485     /*
486      * The algorithms of calculating weights for Sibson and non-Sibsonian
487      * interpolations are quite different; in the first case, the weights are
488      * calculated by processing Delaunay triangles whose tricircles contain
489      * the interpolated point; in the second case, they are calculated by
490      * processing triplets of natural neighbours by moving clockwise or
491      * counterclockwise around the interpolated point.
492      */
493     if (nn_rule == SIBSON) {
494         for (i = 0; i < nn->ncircles; ++i)
495             nnpi_triangle_process(nn, p, tids[i]);
496         if (nn->bad != NULL) {
497             int nentries = ht_getnentries(nn->bad);
498 
499             if (nentries > 0) {
500                 ht_process(nn->bad, free);
501                 return 0;
502             }
503         }
504         return 1;
505     } else if (nn_rule == NON_SIBSONIAN) {
506         int nneigh = 0;
507         int* nids = NULL;
508         int status;
509 
510         nnpi_getneighbours(nn, p, nn->ncircles, tids, &nneigh, &nids);
511         status = nnpi_neighbours_process(nn, p, nneigh, nids);
512         free(nids);
513 
514         return status;
515     } else
516         nn_quit("programming error");
517 
518     return 0;
519 }
520 
nnpi_normalize_weights(nnpi * nn)521 static void nnpi_normalize_weights(nnpi* nn)
522 {
523     int n = nn->nvertices;
524     double sum = 0.0;
525     int i;
526 
527     for (i = 0; i < n; ++i)
528         sum += nn->weights[i];
529 
530     for (i = 0; i < n; ++i)
531         nn->weights[i] /= sum;
532 }
533 
534 #define RANDOM (double) rand() / ((double) RAND_MAX + 1.0)
535 
nnpi_calculate_weights(nnpi * nn,point * p)536 void nnpi_calculate_weights(nnpi* nn, point* p)
537 {
538     point pp;
539     int nvertices = 0;
540     int* vertices = NULL;
541     double* weights = NULL;
542     int i;
543 
544     nnpi_reset(nn);
545 
546     if (_nnpi_calculate_weights(nn, p)) {
547         nnpi_normalize_weights(nn);
548         return;
549     }
550 
551     nnpi_reset(nn);
552 
553     nn->dx = (nn->d->xmax - nn->d->xmin) * EPS_SHIFT;
554     nn->dy = (nn->d->ymax - nn->d->ymin) * EPS_SHIFT;
555 
556     pp.x = p->x + nn->dx;
557     pp.y = p->y + nn->dy;
558 
559     while (!_nnpi_calculate_weights(nn, &pp)) {
560         nnpi_reset(nn);
561         pp.x = p->x + nn->dx * RANDOM;
562         pp.y = p->y + nn->dy * RANDOM;
563     }
564     nnpi_normalize_weights(nn);
565 
566     nvertices = nn->nvertices;
567     if (nvertices > 0) {
568         vertices = malloc(nvertices * sizeof(int));
569         memcpy(vertices, nn->vertices, nvertices * sizeof(int));
570         weights = malloc(nvertices * sizeof(double));
571         memcpy(weights, nn->weights, nvertices * sizeof(double));
572     }
573 
574     nnpi_reset(nn);
575 
576     pp.x = 2.0 * p->x - pp.x;
577     pp.y = 2.0 * p->y - pp.y;
578 
579     while (!_nnpi_calculate_weights(nn, &pp) || nn->nvertices == 0) {
580         nnpi_reset(nn);
581         pp.x = p->x + nn->dx * RANDOM;
582         pp.y = p->y + nn->dy * RANDOM;
583     }
584     nnpi_normalize_weights(nn);
585 
586     if (nvertices > 0)
587         for (i = 0; i < nn->nvertices; ++i)
588             nn->weights[i] /= 2.0;
589 
590     for (i = 0; i < nvertices; ++i)
591         nnpi_add_weight(nn, vertices[i], weights[i] / 2.0);
592 
593     if (nvertices > 0) {
594         free(vertices);
595         free(weights);
596     }
597 }
598 
599 typedef struct {
600     double* v;
601     int i;
602 } indexedvalue;
603 
cmp_iv(const void * p1,const void * p2)604 static int cmp_iv(const void* p1, const void* p2)
605 {
606     double v1 = *((indexedvalue *) p1)->v;
607     double v2 = *((indexedvalue *) p2)->v;
608 
609     if (v1 > v2)
610         return -1;
611     if (v1 < v2)
612         return 1;
613     return 0;
614 }
615 
616 /* Performs Natural Neighbours interpolation in a point.
617  *
618  * @param nn NN interpolation
619  * @param p Point to be interpolated (p->x, p->y -- input; p->z -- output)
620  */
nnpi_interpolate_point(nnpi * nn,point * p)621 void nnpi_interpolate_point(nnpi* nn, point* p)
622 {
623     delaunay* d = nn->d;
624     int i;
625 
626     nnpi_calculate_weights(nn, p);
627 
628     if (nn_verbose) {
629         if (nn_test_vertice == -1) {
630             indexedvalue* ivs = NULL;
631 
632             if (nn->nvertices > 0) {
633                 ivs = malloc(nn->nvertices * sizeof(indexedvalue));
634 
635                 for (i = 0; i < nn->nvertices; ++i) {
636                     ivs[i].i = nn->vertices[i];
637                     ivs[i].v = &nn->weights[i];
638                 }
639 
640                 qsort(ivs, nn->nvertices, sizeof(indexedvalue), cmp_iv);
641             }
642 
643             if (nn->n == 0)
644                 fprintf(stderr, "weights:\n");
645             fprintf(stderr, "  %d: (%.10g, %10g)\n", nn->n, p->x, p->y);
646             fprintf(stderr, "  %4s %15s %15s %15s %15s\n", "id", "x", "y", "z", "w");
647             for (i = 0; i < nn->nvertices; ++i) {
648                 int ii = ivs[i].i;
649                 point* pp = &d->points[ii];
650 
651                 fprintf(stderr, "  %5d %15.10g %15.10g %15.10g %15f\n", ii, pp->x, pp->y, pp->z, *ivs[i].v);
652             }
653 
654             if (nn->nvertices > 0)
655                 free(ivs);
656         } else {
657             double w = 0.0;
658 
659             if (nn->n == 0)
660                 fprintf(stderr, "weight of vertex %d:\n", nn_test_vertice);
661             for (i = 0; i < nn->nvertices; ++i) {
662                 if (nn->vertices[i] == nn_test_vertice) {
663                     w = nn->weights[i];
664                     break;
665                 }
666             }
667             fprintf(stderr, "  (%.10g, %.10g): %.7g\n", p->x, p->y, w);
668         }
669     }
670 
671     nn->n++;
672 
673     if (nn->nvertices == 0) {
674         p->z = NaN;
675         return;
676     }
677 
678     p->z = 0.0;
679     for (i = 0; i < nn->nvertices; ++i) {
680         double weight = nn->weights[i];
681 
682         if (weight < nn->wmin) {
683             p->z = NaN;
684             return;
685         }
686         p->z += d->points[nn->vertices[i]].z * weight;
687     }
688 }
689 
690 /* Performs Natural Neighbours interpolation for an array of points.
691  *
692  * @param nin Number of input points
693  * @param pin Array of input points [pin]
694  * @param wmin Minimal allowed weight
695  * @param nout Number of output points
696  * @param pout Array of output points [nout]
697  */
nnpi_interpolate_points(int nin,point pin[],double wmin,int nout,point pout[])698 void nnpi_interpolate_points(int nin, point pin[], double wmin, int nout, point pout[])
699 {
700     delaunay* d = delaunay_build(nin, pin, 0, NULL, 0, NULL);
701     nnpi* nn = nnpi_create(d);
702     int seed = 0;
703     int i;
704 
705     nnpi_setwmin(nn, wmin);
706 
707     if (nn_verbose) {
708         fprintf(stderr, "xytoi:\n");
709         for (i = 0; i < nout; ++i) {
710             point* p = &pout[i];
711 
712             fprintf(stderr, "(%.7g,%.7g) -> %d\n", p->x, p->y, delaunay_xytoi(d, p, seed));
713         }
714     }
715 
716     for (i = 0; i < nout; ++i)
717         nnpi_interpolate_point(nn, &pout[i]);
718 
719     if (nn_verbose) {
720         fprintf(stderr, "output:\n");
721         for (i = 0; i < nout; ++i) {
722             point* p = &pout[i];
723 
724             fprintf(stderr, "  %d:%15.7g %15.7g %15.7g\n", i, p->x, p->y, p->z);
725         }
726     }
727 
728     nnpi_destroy(nn);
729     delaunay_destroy(d);
730 }
731 
732 /* Sets minimal allowed weight for Natural Neighbours interpolation.
733  *
734  * For Sibson interpolation, setting wmin = 0 is equivalent to interpolating
735  * inside convex hall of the data only (returning NaNs otherwise).
736  *
737  * @param nn Natural Neighbours point interpolator
738  * @param wmin Minimal allowed weight
739  */
nnpi_setwmin(nnpi * nn,double wmin)740 void nnpi_setwmin(nnpi* nn, double wmin)
741 {
742     nn->wmin = (wmin == 0) ? -EPS_WMIN : wmin;
743 }
744 
745 /* Gets number of data points involved in current interpolation. For use by
746  * `nnai'.
747  *
748  * @return Number of data points involved in current interpolation
749  */
nnpi_get_nvertices(nnpi * nn)750 int nnpi_get_nvertices(nnpi* nn)
751 {
752     return nn->nvertices;
753 }
754 
755 /* Gets indices of data points involved in current interpolation. For use by
756  * `nnai'.
757  *
758  * @return indices of data points involved in current interpolation
759  */
nnpi_get_vertices(nnpi * nn)760 int* nnpi_get_vertices(nnpi* nn)
761 {
762     return nn->vertices;
763 }
764 
765 /* Gets weights of data points involved in current interpolation. For use by
766  * `nnai'.
767  * @return weights of data points involved in current interpolation
768  */
nnpi_get_weights(nnpi * nn)769 double* nnpi_get_weights(nnpi* nn)
770 {
771     return nn->weights;
772 }
773 
774 /*
775  * nnhpi
776  */
777 
778 struct nnhpi {
779     nnpi* nnpi;
780     hashtable* ht_data;
781     hashtable* ht_weights;
782     int n;                      /* number of points processed */
783 };
784 
785 typedef struct {
786     int nvertices;
787     int* vertices;              /* vertex indices [nvertices] */
788     double* weights;            /* vertex weights [nvertices] */
789 } nn_weights;
790 
791 /* Creates Natural Neighbours hashing point interpolator.
792  *
793  * @param d Delaunay triangulation
794  * @param size Hash table size (should be of order of number of output points)
795  * @return Natural Neighbours interpolation
796  */
nnhpi_create(delaunay * d,int size)797 nnhpi* nnhpi_create(delaunay* d, int size)
798 {
799     nnhpi* nn = malloc(sizeof(nnhpi));
800     int i;
801 
802     nn->nnpi = nnpi_create(d);
803 
804     nn->ht_data = ht_create_d2(d->npoints);
805     nn->ht_weights = ht_create_d2(size);
806     nn->n = 0;
807 
808     for (i = 0; i < d->npoints; ++i)
809         ht_insert(nn->ht_data, &d->points[i], &d->points[i]);
810 
811     return nn;
812 }
813 
free_nn_weights(void * data)814 static void free_nn_weights(void* data)
815 {
816     nn_weights* weights = (nn_weights*) data;
817 
818     free(weights->vertices);
819     free(weights->weights);
820     free(weights);
821 }
822 
823 /* Destroys Natural Neighbours hashing point interpolation.
824  *
825  * @param nn Structure to be destroyed
826  */
nnhpi_destroy(nnhpi * nn)827 void nnhpi_destroy(nnhpi* nn)
828 {
829     ht_destroy(nn->ht_data);
830     ht_process(nn->ht_weights, free_nn_weights);
831     ht_destroy(nn->ht_weights);
832     nnpi_destroy(nn->nnpi);
833 }
834 
835 /* Finds Natural Neighbours-interpolated value in a point.
836  *
837  * @param nnhpi NN point hashing interpolator
838  * @param p Point to be interpolated (p->x, p->y -- input; p->z -- output)
839  */
nnhpi_interpolate(nnhpi * nnhpi,point * p)840 void nnhpi_interpolate(nnhpi* nnhpi, point* p)
841 {
842     nnpi* nnpi = nnhpi->nnpi;
843     delaunay* d = nnpi->d;
844     hashtable* ht_weights = nnhpi->ht_weights;
845     nn_weights* weights;
846     int i;
847 
848     if (ht_find(ht_weights, p) != NULL) {
849         weights = ht_find(ht_weights, p);
850         if (nn_verbose)
851             fprintf(stderr, "  <hashtable>\n");
852     } else {
853         nnpi_calculate_weights(nnpi, p);
854 
855         weights = malloc(sizeof(nn_weights));
856         weights->vertices = malloc(sizeof(int) * nnpi->nvertices);
857         weights->weights = malloc(sizeof(double) * nnpi->nvertices);
858 
859         weights->nvertices = nnpi->nvertices;
860 
861         for (i = 0; i < nnpi->nvertices; ++i) {
862             weights->vertices[i] = nnpi->vertices[i];
863             weights->weights[i] = nnpi->weights[i];
864         }
865 
866         ht_insert(ht_weights, p, weights);
867 
868         if (nn_verbose) {
869             if (nn_test_vertice == -1) {
870                 if (nnpi->n == 0)
871                     fprintf(stderr, "weights:\n");
872                 fprintf(stderr, "  %d: {", nnpi->n);
873 
874                 for (i = 0; i < nnpi->nvertices; ++i) {
875                     fprintf(stderr, "(%d,%.5g)", nnpi->vertices[i], nnpi->weights[i]);
876 
877                     if (i < nnpi->nvertices - 1)
878                         fprintf(stderr, ", ");
879                 }
880                 fprintf(stderr, "}\n");
881             } else {
882                 double w = 0.0;
883 
884                 if (nnpi->n == 0)
885                     fprintf(stderr, "weights for vertex %d:\n", nn_test_vertice);
886                 for (i = 0; i < nnpi->nvertices; ++i) {
887                     if (nnpi->vertices[i] == nn_test_vertice) {
888                         w = nnpi->weights[i];
889 
890                         break;
891                     }
892                 }
893                 fprintf(stderr, "%15.7g %15.7g %15.7g\n", p->x, p->y, w);
894             }
895         }
896 
897         nnpi->n++;
898     }
899 
900     nnhpi->n++;
901 
902     if (weights->nvertices == 0) {
903         p->z = NaN;
904         return;
905     }
906 
907     p->z = 0.0;
908     for (i = 0; i < weights->nvertices; ++i) {
909         if (weights->weights[i] < nnpi->wmin) {
910             p->z = NaN;
911             return;
912         }
913         p->z += d->points[weights->vertices[i]].z * weights->weights[i];
914     }
915 }
916 
917 /* Modifies interpolated data.
918  *
919  * Finds point* pd in the underlying Delaunay triangulation such that
920  * pd->x = p->x and pd->y = p->y, and copies p->z to pd->z. Exits with error
921  * if the point is not found.
922  *
923  * @param nnhpi Natural Neighbours hashing point interpolator
924  * @param p New data
925  */
nnhpi_modify_data(nnhpi * nnhpi,point * p)926 void nnhpi_modify_data(nnhpi* nnhpi, point* p)
927 {
928     point* orig = ht_find(nnhpi->ht_data, p);
929 
930     assert(orig != NULL);
931     orig->z = p->z;
932 }
933 
934 /* Sets minimal allowed weight for Natural Neighbours interpolation.
935  *
936  * For Sibson interpolation, setting wmin = 0 is equivalent to interpolating
937  * inside convex hall of the data only (returning NaNs otherwise).
938  *
939  * @param nn Natural Neighbours point hashing interpolator
940  * @param wmin Minimal allowed weight
941  */
nnhpi_setwmin(nnhpi * nn,double wmin)942 void nnhpi_setwmin(nnhpi* nn, double wmin)
943 {
944     nn->nnpi->wmin = wmin;
945 }
946 
947 #if defined(NNPHI_TEST)
948 
949 #include <sys/time.h>
950 
951 #define NPOINTSIN 10000
952 #define NMIN 10
953 #define NX 101
954 #define NXMIN 1
955 
956 #define SQ(x) ((x) * (x))
957 
franke(double x,double y)958 static double franke(double x, double y)
959 {
960     x *= 9.0;
961     y *= 9.0;
962     return 0.75 * exp((-SQ(x - 2.0) - SQ(y - 2.0)) / 4.0)
963         + 0.75 * exp(-SQ(x - 2.0) / 49.0 - (y - 2.0) / 10.0)
964         + 0.5 * exp((-SQ(x - 7.0) - SQ(y - 3.0)) / 4.0)
965         - 0.2 * exp(-SQ(x - 4.0) - SQ(y - 7.0));
966 }
967 
usage()968 static void usage()
969 {
970     printf("Usage: nnhpi_test [-a] [-n <nin> <nxout>] [-v|-V]\n");
971     printf("Options:\n");
972     printf("  -a              -- use non-Sibsonian interpolation rule\n");
973     printf("  -n <nin> <nout>:\n");
974     printf("            <nin> -- number of input points (default = 10000)\n");
975     printf("           <nout> -- number of output points per side (default = 64)\n");
976     printf("  -v              -- verbose\n");
977     printf("  -V              -- very verbose\n");
978 
979     exit(0);
980 }
981 
main(int argc,char * argv[])982 int main(int argc, char* argv[])
983 {
984     int nin = NPOINTSIN;
985     int nx = NX;
986     int nout = 0;
987     point* pin = NULL;
988     delaunay* d = NULL;
989     point* pout = NULL;
990     nnhpi* nn = NULL;
991     int cpi = -1;               /* control point index */
992     struct timeval tv0, tv1;
993     struct timezone tz;
994     int i;
995 
996     i = 1;
997     while (i < argc) {
998         switch (argv[i][1]) {
999         case 'a':
1000             i++;
1001             nn_rule = NON_SIBSONIAN;
1002             break;
1003         case 'n':
1004             i++;
1005             if (i >= argc)
1006                 nn_quit("no number of data points found after -n\n");
1007             nin = atoi(argv[i]);
1008             i++;
1009             if (i >= argc)
1010                 nn_quit("no number of ouput points per side found after -i\n");
1011             nx = atoi(argv[i]);
1012             i++;
1013             break;
1014         case 'v':
1015             i++;
1016             nn_verbose = 1;
1017             break;
1018         case 'V':
1019             i++;
1020             nn_verbose = 2;
1021             break;
1022         default:
1023             usage();
1024             break;
1025         }
1026     }
1027 
1028     if (nin < NMIN)
1029         nin = NMIN;
1030     if (nx < NXMIN)
1031         nx = NXMIN;
1032 
1033     printf("\nTest of Natural Neighbours hashing point interpolator:\n\n");
1034     printf("  %d data points\n", nin);
1035     printf("  %d output points\n", nx * nx);
1036 
1037     /*
1038      * generate data
1039      */
1040     printf("  generating data:\n");
1041     fflush(stdout);
1042     pin = malloc(nin * sizeof(point));
1043     for (i = 0; i < nin; ++i) {
1044         point* p = &pin[i];
1045 
1046         p->x = (double) random() / RAND_MAX;
1047         p->y = (double) random() / RAND_MAX;
1048         p->z = franke(p->x, p->y);
1049         if (nn_verbose)
1050             printf("    (%f, %f, %f)\n", p->x, p->y, p->z);
1051     }
1052 
1053     /*
1054      * triangulate
1055      */
1056     printf("  triangulating:\n");
1057     fflush(stdout);
1058     d = delaunay_build(nin, pin, 0, NULL, 0, NULL);
1059 
1060     /*
1061      * generate output points
1062      */
1063     points_generate(-0.1, 1.1, -0.1, 1.1, nx, nx, &nout, &pout);
1064     cpi = (nx / 2) * (nx + 1);
1065 
1066     gettimeofday(&tv0, &tz);
1067 
1068     /*
1069      * create interpolator
1070      */
1071     printf("  creating interpolator:\n");
1072     fflush(stdout);
1073     nn = nnhpi_create(d, nout);
1074 
1075     fflush(stdout);
1076     gettimeofday(&tv1, &tz);
1077     {
1078         long dt = 1000000 * (tv1.tv_sec - tv0.tv_sec) + tv1.tv_usec - tv0.tv_usec;
1079 
1080         printf("    interpolator creation time = %ld us (%.2f us / point)\n", dt, (double) dt / nout);
1081     }
1082 
1083     /*
1084      * interpolate
1085      */
1086     printf("  interpolating:\n");
1087     fflush(stdout);
1088     gettimeofday(&tv1, &tz);
1089     for (i = 0; i < nout; ++i) {
1090         point* p = &pout[i];
1091 
1092         nnhpi_interpolate(nn, p);
1093         if (nn_verbose)
1094             printf("    (%f, %f, %f)\n", p->x, p->y, p->z);
1095     }
1096 
1097     fflush(stdout);
1098     gettimeofday(&tv0, &tz);
1099     {
1100         long dt = 1000000.0 * (tv0.tv_sec - tv1.tv_sec) + tv0.tv_usec - tv1.tv_usec;
1101 
1102         printf("    interpolation time = %ld us (%.2f us / point)\n", dt, (double) dt / nout);
1103     }
1104 
1105     if (!nn_verbose)
1106         printf("    control point: (%f, %f, %f) (expected z = %f)\n", pout[cpi].x, pout[cpi].y, pout[cpi].z, franke(pout[cpi].x, pout[cpi].y));
1107 
1108     printf("  interpolating one more time:\n");
1109     fflush(stdout);
1110     gettimeofday(&tv0, &tz);
1111     for (i = 0; i < nout; ++i) {
1112         point* p = &pout[i];
1113 
1114         nnhpi_interpolate(nn, p);
1115         if (nn_verbose)
1116             printf("    (%f, %f, %f)\n", p->x, p->y, p->z);
1117     }
1118 
1119     fflush(stdout);
1120     gettimeofday(&tv1, &tz);
1121     {
1122         long dt = 1000000.0 * (tv1.tv_sec - tv0.tv_sec) + tv1.tv_usec - tv0.tv_usec;
1123 
1124         printf("    interpolation time = %ld us (%.2f us / point)\n", dt, (double) dt / nout);
1125     }
1126 
1127     if (!nn_verbose)
1128         printf("    control point: (%f, %f, %f) (expected z = %f)\n", pout[cpi].x, pout[cpi].y, pout[cpi].z, franke(pout[cpi].x, pout[cpi].y));
1129 
1130     printf("  entering new data:\n");
1131     fflush(stdout);
1132     for (i = 0; i < nin; ++i) {
1133         point* p = &pin[i];
1134 
1135         p->z = p->x * p->x - p->y * p->y;
1136         nnhpi_modify_data(nn, p);
1137         if (nn_verbose)
1138             printf("    (%f, %f, %f)\n", p->x, p->y, p->z);
1139     }
1140 
1141     printf("  interpolating:\n");
1142     fflush(stdout);
1143     gettimeofday(&tv1, &tz);
1144     for (i = 0; i < nout; ++i) {
1145         point* p = &pout[i];
1146 
1147         nnhpi_interpolate(nn, p);
1148         if (nn_verbose)
1149             printf("    (%f, %f, %f)\n", p->x, p->y, p->z);
1150     }
1151 
1152     fflush(stdout);
1153     gettimeofday(&tv0, &tz);
1154     {
1155         long dt = 1000000.0 * (tv0.tv_sec - tv1.tv_sec) + tv0.tv_usec - tv1.tv_usec;
1156 
1157         printf("    interpolation time = %ld us (%.2f us / point)\n", dt, (double) dt / nout);
1158     }
1159 
1160     if (!nn_verbose)
1161         printf("    control point: (%f, %f, %f) (expected z = %f)\n", pout[cpi].x, pout[cpi].y, pout[cpi].z, pout[cpi].x * pout[cpi].x - pout[cpi].y * pout[cpi].y);
1162 
1163     printf("  restoring data:\n");
1164     fflush(stdout);
1165     for (i = 0; i < nin; ++i) {
1166         point* p = &pin[i];
1167 
1168         p->z = franke(p->x, p->y);
1169         nnhpi_modify_data(nn, p);
1170         if (nn_verbose)
1171             printf("    (%f, %f, %f)\n", p->x, p->y, p->z);
1172     }
1173 
1174     printf("  interpolating:\n");
1175     fflush(stdout);
1176     gettimeofday(&tv0, &tz);
1177     for (i = 0; i < nout; ++i) {
1178         point* p = &pout[i];
1179 
1180         nnhpi_interpolate(nn, p);
1181         if (nn_verbose)
1182             printf("    (%f, %f, %f)\n", p->x, p->y, p->z);
1183     }
1184 
1185     fflush(stdout);
1186     gettimeofday(&tv1, &tz);
1187     {
1188         long dt = 1000000.0 * (tv1.tv_sec - tv0.tv_sec) + tv1.tv_usec - tv0.tv_usec;
1189 
1190         printf("    interpolation time = %ld us (%.2f us / point)\n", dt, (double) dt / nout);
1191     }
1192 
1193     if (!nn_verbose)
1194         printf("    control point: (%f, %f, %f) (expected z = %f)\n", pout[cpi].x, pout[cpi].y, pout[cpi].z, franke(pout[cpi].x, pout[cpi].y));
1195 
1196     printf("  hashtable stats:\n");
1197     fflush(stdout);
1198     {
1199         hashtable* ht = nn->ht_data;
1200 
1201         printf("    input points: %d entries, %d table elements, %d filled elements\n", ht_getnentries(ht), ht_getsize(ht), ht_getnfilled(ht));
1202         ht = nn->ht_weights;
1203         printf("    weights: %d entries, %d table elements, %d filled elements\n", ht_getnentries(ht), ht_getsize(ht), ht_getnfilled(ht));
1204     }
1205     printf("\n");
1206 
1207     nnhpi_destroy(nn);
1208     free(pout);
1209     delaunay_destroy(d);
1210     free(pin);
1211 
1212     return 0;
1213 }
1214 
1215 #endif
1216