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