Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL algorithms |
4 | | * Purpose: Delaunay triangulation |
5 | | * Author: Even Rouault, even.rouault at spatialys.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #if defined(__MINGW32__) || defined(__MINGW64__) |
14 | | /* This avoids i586-mingw32msvc/include/direct.h from including libqhull/io.h |
15 | | * ... */ |
16 | | #define _DIRECT_H_ |
17 | | /* For __MINGW64__ */ |
18 | | #define _INC_DIRECT |
19 | | #define _INC_STAT |
20 | | #endif |
21 | | |
22 | | #if defined(INTERNAL_QHULL) && !defined(DONT_DEPRECATE_SPRINTF) |
23 | | #define DONT_DEPRECATE_SPRINTF |
24 | | #endif |
25 | | |
26 | | #include "cpl_error.h" |
27 | | #include "cpl_conv.h" |
28 | | #include "cpl_string.h" |
29 | | #include "gdal_alg.h" |
30 | | |
31 | | #include <stdio.h> |
32 | | #include <stdlib.h> |
33 | | #include <string.h> |
34 | | #include <ctype.h> |
35 | | #include <math.h> |
36 | | |
37 | | #if defined(INTERNAL_QHULL) || defined(EXTERNAL_QHULL) |
38 | | #define HAVE_INTERNAL_OR_EXTERNAL_QHULL 1 |
39 | | #endif |
40 | | |
41 | | #if HAVE_INTERNAL_OR_EXTERNAL_QHULL |
42 | | #ifdef INTERNAL_QHULL |
43 | | |
44 | | #include "internal_qhull_headers.h" |
45 | | |
46 | | #else /* INTERNAL_QHULL */ |
47 | | |
48 | | #ifdef _MSC_VER |
49 | | #pragma warning(push) |
50 | | #pragma warning( \ |
51 | | disable : 4324) // 'qhT': structure was padded due to alignment specifier |
52 | | #endif |
53 | | |
54 | | #if defined(__clang__) |
55 | | #pragma clang diagnostic push |
56 | | #pragma clang diagnostic ignored "-Wdocumentation" |
57 | | #endif |
58 | | |
59 | | #include "libqhull_r/libqhull_r.h" |
60 | | #include "libqhull_r/qset_r.h" |
61 | | |
62 | | #if defined(__clang__) |
63 | | #pragma clang diagnostic pop |
64 | | #endif |
65 | | |
66 | | #ifdef _MSC_VER |
67 | | #pragma warning(pop) |
68 | | #endif |
69 | | |
70 | | #endif /* INTERNAL_QHULL */ |
71 | | |
72 | | #endif /* HAVE_INTERNAL_OR_EXTERNAL_QHULL*/ |
73 | | |
74 | | /************************************************************************/ |
75 | | /* GDALHasTriangulation() */ |
76 | | /************************************************************************/ |
77 | | |
78 | | /** Returns if GDAL is built with Delaunay triangulation support. |
79 | | * |
80 | | * @return TRUE if GDAL is built with Delaunay triangulation support. |
81 | | * |
82 | | * @since GDAL 2.1 |
83 | | */ |
84 | | int GDALHasTriangulation() |
85 | 0 | { |
86 | 0 | #if HAVE_INTERNAL_OR_EXTERNAL_QHULL |
87 | 0 | return TRUE; |
88 | | #else |
89 | | return FALSE; |
90 | | #endif |
91 | 0 | } |
92 | | |
93 | | /************************************************************************/ |
94 | | /* GDALTriangulationCreateDelaunay() */ |
95 | | /************************************************************************/ |
96 | | |
97 | | /** Computes a Delaunay triangulation of the passed points |
98 | | * |
99 | | * @param nPoints number of points |
100 | | * @param padfX x coordinates of the points. |
101 | | * @param padfY y coordinates of the points. |
102 | | * @return triangulation that must be freed with GDALTriangulationFree(), or |
103 | | * NULL in case of error. |
104 | | * |
105 | | * @since GDAL 2.1 |
106 | | */ |
107 | | GDALTriangulation *GDALTriangulationCreateDelaunay(int nPoints, |
108 | | const double *padfX, |
109 | | const double *padfY) |
110 | 0 | { |
111 | 0 | #if HAVE_INTERNAL_OR_EXTERNAL_QHULL |
112 | 0 | coordT *points; |
113 | 0 | int i, j; |
114 | 0 | GDALTriangulation *psDT = NULL; |
115 | 0 | facetT *facet; |
116 | 0 | GDALTriFacet *pasFacets; |
117 | 0 | int *panMapQHFacetIdToFacetIdx; /* map from QHull facet ID to the index of |
118 | | our GDALTriFacet* array */ |
119 | 0 | int curlong, totlong; /* memory remaining after qh_memfreeshort */ |
120 | 0 | char *pszTempFilename = NULL; |
121 | 0 | FILE *fpTemp = NULL; |
122 | |
|
123 | 0 | qhT qh_qh; |
124 | 0 | qhT *qh = &qh_qh; |
125 | |
|
126 | | QHULL_LIB_CHECK /* Check for compatible library */ |
127 | |
|
128 | 0 | points = (coordT *)VSI_MALLOC2_VERBOSE(sizeof(double) * 2, nPoints); |
129 | 0 | if (points == NULL) |
130 | 0 | { |
131 | 0 | return NULL; |
132 | 0 | } |
133 | 0 | for (i = 0; i < nPoints; i++) |
134 | 0 | { |
135 | 0 | points[2 * i] = padfX[i]; |
136 | 0 | points[2 * i + 1] = padfY[i]; |
137 | 0 | } |
138 | |
|
139 | 0 | qh_meminit(qh, NULL); |
140 | |
|
141 | 0 | if (CPLTestBoolean(CPLGetConfigOption("QHULL_LOG_TO_TEMP_FILE", "NO"))) |
142 | 0 | { |
143 | 0 | pszTempFilename = CPLStrdup(CPLGenerateTempFilename(NULL)); |
144 | 0 | fpTemp = fopen(pszTempFilename, "wb"); |
145 | 0 | } |
146 | 0 | if (fpTemp == NULL) |
147 | 0 | fpTemp = stderr; |
148 | | |
149 | | /* d: Delaunay */ |
150 | | /* Qbb: scale last coordinate to [0,m] for Delaunay */ |
151 | | /* Qc: keep coplanar points with nearest facet */ |
152 | | /* Qz: add a point-at-infinity for Delaunay triangulation */ |
153 | | /* Qt: triangulated output */ |
154 | 0 | int ret = qh_new_qhull(qh, 2, nPoints, points, FALSE /* ismalloc */, |
155 | 0 | "qhull d Qbb Qc Qz Qt", NULL, fpTemp); |
156 | 0 | if (fpTemp != stderr) |
157 | 0 | { |
158 | 0 | fclose(fpTemp); |
159 | 0 | } |
160 | 0 | if (pszTempFilename != NULL) |
161 | 0 | { |
162 | 0 | VSIUnlink(pszTempFilename); |
163 | 0 | VSIFree(pszTempFilename); |
164 | 0 | } |
165 | |
|
166 | 0 | VSIFree(points); |
167 | 0 | points = NULL; |
168 | |
|
169 | 0 | if (ret != 0) |
170 | 0 | { |
171 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Delaunay triangulation failed"); |
172 | 0 | goto end; |
173 | 0 | } |
174 | | |
175 | | /* Establish a map from QHull facet id to the index in our array of |
176 | | * sequential facets */ |
177 | 0 | panMapQHFacetIdToFacetIdx = |
178 | 0 | (int *)VSI_MALLOC2_VERBOSE(sizeof(int), qh->facet_id); |
179 | 0 | if (panMapQHFacetIdToFacetIdx == NULL) |
180 | 0 | { |
181 | 0 | goto end; |
182 | 0 | } |
183 | 0 | memset(panMapQHFacetIdToFacetIdx, 0xFF, sizeof(int) * qh->facet_id); |
184 | |
|
185 | 0 | for (j = 0, facet = qh->facet_list; facet != NULL && facet->next != NULL; |
186 | 0 | facet = facet->next) |
187 | 0 | { |
188 | 0 | if (facet->upperdelaunay != qh->UPPERdelaunay) |
189 | 0 | continue; |
190 | | |
191 | 0 | if (qh_setsize(qh, facet->vertices) != 3 || |
192 | 0 | qh_setsize(qh, facet->neighbors) != 3) |
193 | 0 | { |
194 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
195 | 0 | "Triangulation resulted in non triangular facet %d: " |
196 | 0 | "vertices=%d", |
197 | 0 | facet->id, qh_setsize(qh, facet->vertices)); |
198 | 0 | VSIFree(panMapQHFacetIdToFacetIdx); |
199 | 0 | goto end; |
200 | 0 | } |
201 | | |
202 | 0 | CPLAssert(facet->id < qh->facet_id); |
203 | 0 | panMapQHFacetIdToFacetIdx[facet->id] = j++; |
204 | 0 | } |
205 | | |
206 | 0 | pasFacets = (GDALTriFacet *)VSI_MALLOC2_VERBOSE(j, sizeof(GDALTriFacet)); |
207 | 0 | if (pasFacets == NULL) |
208 | 0 | { |
209 | 0 | VSIFree(panMapQHFacetIdToFacetIdx); |
210 | 0 | goto end; |
211 | 0 | } |
212 | | |
213 | 0 | psDT = (GDALTriangulation *)CPLCalloc(1, sizeof(GDALTriangulation)); |
214 | 0 | psDT->nFacets = j; |
215 | 0 | psDT->pasFacets = pasFacets; |
216 | | |
217 | | // Store vertex and neighbor information for each triangle. |
218 | 0 | for (facet = qh->facet_list; facet != NULL && facet->next != NULL; |
219 | 0 | facet = facet->next) |
220 | 0 | { |
221 | 0 | int k; |
222 | 0 | if (facet->upperdelaunay != qh->UPPERdelaunay) |
223 | 0 | continue; |
224 | 0 | k = panMapQHFacetIdToFacetIdx[facet->id]; |
225 | 0 | pasFacets[k].anVertexIdx[0] = |
226 | 0 | qh_pointid(qh, ((vertexT *)facet->vertices->e[0].p)->point); |
227 | 0 | pasFacets[k].anVertexIdx[1] = |
228 | 0 | qh_pointid(qh, ((vertexT *)facet->vertices->e[1].p)->point); |
229 | 0 | pasFacets[k].anVertexIdx[2] = |
230 | 0 | qh_pointid(qh, ((vertexT *)facet->vertices->e[2].p)->point); |
231 | 0 | pasFacets[k].anNeighborIdx[0] = |
232 | 0 | panMapQHFacetIdToFacetIdx[((facetT *)facet->neighbors->e[0].p)->id]; |
233 | 0 | pasFacets[k].anNeighborIdx[1] = |
234 | 0 | panMapQHFacetIdToFacetIdx[((facetT *)facet->neighbors->e[1].p)->id]; |
235 | 0 | pasFacets[k].anNeighborIdx[2] = |
236 | 0 | panMapQHFacetIdToFacetIdx[((facetT *)facet->neighbors->e[2].p)->id]; |
237 | 0 | } |
238 | |
|
239 | 0 | VSIFree(panMapQHFacetIdToFacetIdx); |
240 | |
|
241 | 0 | end: |
242 | 0 | qh_freeqhull(qh, !qh_ALL); |
243 | 0 | qh_memfreeshort(qh, &curlong, &totlong); |
244 | |
|
245 | 0 | return psDT; |
246 | | #else /* HAVE_INTERNAL_OR_EXTERNAL_QHULL */ |
247 | | |
248 | | /* Suppress unused argument warnings. */ |
249 | | (void)nPoints; |
250 | | (void)padfX; |
251 | | (void)padfY; |
252 | | |
253 | | CPLError(CE_Failure, CPLE_NotSupported, |
254 | | "GDALTriangulationCreateDelaunay() unavailable since GDAL built " |
255 | | "without QHull support"); |
256 | | return NULL; |
257 | | #endif /* HAVE_INTERNAL_OR_EXTERNAL_QHULL */ |
258 | 0 | } |
259 | | |
260 | | /************************************************************************/ |
261 | | /* GDALTriangulationFree() */ |
262 | | /************************************************************************/ |
263 | | |
264 | | /** Free a triangulation. |
265 | | * |
266 | | * @param psDT triangulation. |
267 | | * @since GDAL 2.1 |
268 | | */ |
269 | | void GDALTriangulationFree(GDALTriangulation *psDT) |
270 | 0 | { |
271 | 0 | if (psDT) |
272 | 0 | { |
273 | 0 | VSIFree(psDT->pasFacets); |
274 | 0 | VSIFree(psDT->pasFacetCoefficients); |
275 | 0 | VSIFree(psDT); |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | /************************************************************************/ |
280 | | /* GDALTriangulationComputeBarycentricCoefficients() */ |
281 | | /************************************************************************/ |
282 | | |
283 | | /** Computes barycentric coefficients for each triangles of the triangulation. |
284 | | * |
285 | | * @param psDT triangulation. |
286 | | * @param padfX x coordinates of the points. Must be identical to the one passed |
287 | | * to GDALTriangulationCreateDelaunay(). |
288 | | * @param padfY y coordinates of the points. Must be identical to the one passed |
289 | | * to GDALTriangulationCreateDelaunay(). |
290 | | * |
291 | | * @return TRUE in case of success. |
292 | | * |
293 | | * @since GDAL 2.1 |
294 | | */ |
295 | | int GDALTriangulationComputeBarycentricCoefficients(GDALTriangulation *psDT, |
296 | | const double *padfX, |
297 | | const double *padfY) |
298 | 0 | { |
299 | 0 | int i; |
300 | |
|
301 | 0 | if (psDT->pasFacetCoefficients != NULL) |
302 | 0 | { |
303 | 0 | return TRUE; |
304 | 0 | } |
305 | 0 | psDT->pasFacetCoefficients = |
306 | 0 | (GDALTriBarycentricCoefficients *)VSI_MALLOC2_VERBOSE( |
307 | 0 | sizeof(GDALTriBarycentricCoefficients), psDT->nFacets); |
308 | 0 | if (psDT->pasFacetCoefficients == NULL) |
309 | 0 | { |
310 | 0 | return FALSE; |
311 | 0 | } |
312 | | |
313 | 0 | for (i = 0; i < psDT->nFacets; i++) |
314 | 0 | { |
315 | 0 | GDALTriFacet *psFacet = &(psDT->pasFacets[i]); |
316 | 0 | GDALTriBarycentricCoefficients *psCoeffs = |
317 | 0 | &(psDT->pasFacetCoefficients[i]); |
318 | 0 | double dfX1 = padfX[psFacet->anVertexIdx[0]]; |
319 | 0 | double dfY1 = padfY[psFacet->anVertexIdx[0]]; |
320 | 0 | double dfX2 = padfX[psFacet->anVertexIdx[1]]; |
321 | 0 | double dfY2 = padfY[psFacet->anVertexIdx[1]]; |
322 | 0 | double dfX3 = padfX[psFacet->anVertexIdx[2]]; |
323 | 0 | double dfY3 = padfY[psFacet->anVertexIdx[2]]; |
324 | | /* See https://en.wikipedia.org/wiki/Barycentric_coordinate_system */ |
325 | 0 | double dfDenom = |
326 | 0 | (dfY2 - dfY3) * (dfX1 - dfX3) + (dfX3 - dfX2) * (dfY1 - dfY3); |
327 | 0 | if (fabs(dfDenom) < 1e-5) |
328 | 0 | { |
329 | | // Degenerate triangle |
330 | 0 | psCoeffs->dfMul1X = 0.0; |
331 | 0 | psCoeffs->dfMul1Y = 0.0; |
332 | 0 | psCoeffs->dfMul2X = 0.0; |
333 | 0 | psCoeffs->dfMul2Y = 0.0; |
334 | 0 | psCoeffs->dfCstX = 0.0; |
335 | 0 | psCoeffs->dfCstY = 0.0; |
336 | 0 | } |
337 | 0 | else |
338 | 0 | { |
339 | 0 | psCoeffs->dfMul1X = (dfY2 - dfY3) / dfDenom; |
340 | 0 | psCoeffs->dfMul1Y = (dfX3 - dfX2) / dfDenom; |
341 | 0 | psCoeffs->dfMul2X = (dfY3 - dfY1) / dfDenom; |
342 | 0 | psCoeffs->dfMul2Y = (dfX1 - dfX3) / dfDenom; |
343 | 0 | psCoeffs->dfCstX = dfX3; |
344 | 0 | psCoeffs->dfCstY = dfY3; |
345 | 0 | } |
346 | 0 | } |
347 | 0 | return TRUE; |
348 | 0 | } |
349 | | |
350 | | /************************************************************************/ |
351 | | /* GDALTriangulationComputeBarycentricCoordinates() */ |
352 | | /************************************************************************/ |
353 | | |
354 | | #define BARYC_COORD_L1(psCoeffs, dfX, dfY) \ |
355 | 0 | (psCoeffs->dfMul1X * ((dfX)-psCoeffs->dfCstX) + \ |
356 | 0 | psCoeffs->dfMul1Y * ((dfY)-psCoeffs->dfCstY)) |
357 | | #define BARYC_COORD_L2(psCoeffs, dfX, dfY) \ |
358 | 0 | (psCoeffs->dfMul2X * ((dfX)-psCoeffs->dfCstX) + \ |
359 | 0 | psCoeffs->dfMul2Y * ((dfY)-psCoeffs->dfCstY)) |
360 | 0 | #define BARYC_COORD_L3(l1, l2) (1 - (l1) - (l2)) |
361 | | |
362 | | /** Computes the barycentric coordinates of a point. |
363 | | * |
364 | | * @param psDT triangulation. |
365 | | * @param nFacetIdx index of the triangle in the triangulation |
366 | | * @param dfX x coordinate of the point. |
367 | | * @param dfY y coordinate of the point. |
368 | | * @param pdfL1 (output) pointer to the 1st barycentric coordinate. |
369 | | * @param pdfL2 (output) pointer to the 2nd barycentric coordinate. |
370 | | * @param pdfL3 (output) pointer to the 2nd barycentric coordinate. |
371 | | * |
372 | | * @return TRUE in case of success. |
373 | | * |
374 | | * @since GDAL 2.1 |
375 | | */ |
376 | | |
377 | | int GDALTriangulationComputeBarycentricCoordinates( |
378 | | const GDALTriangulation *psDT, int nFacetIdx, double dfX, double dfY, |
379 | | double *pdfL1, double *pdfL2, double *pdfL3) |
380 | 0 | { |
381 | 0 | const GDALTriBarycentricCoefficients *psCoeffs; |
382 | 0 | if (psDT->pasFacetCoefficients == NULL) |
383 | 0 | { |
384 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
385 | 0 | "GDALTriangulationComputeBarycentricCoefficients() should be " |
386 | 0 | "called before"); |
387 | 0 | return FALSE; |
388 | 0 | } |
389 | 0 | CPLAssert(nFacetIdx >= 0 && nFacetIdx < psDT->nFacets); |
390 | 0 | psCoeffs = &(psDT->pasFacetCoefficients[nFacetIdx]); |
391 | 0 | *pdfL1 = BARYC_COORD_L1(psCoeffs, dfX, dfY); |
392 | 0 | *pdfL2 = BARYC_COORD_L2(psCoeffs, dfX, dfY); |
393 | 0 | *pdfL3 = BARYC_COORD_L3(*pdfL1, *pdfL2); |
394 | |
|
395 | 0 | return TRUE; |
396 | 0 | } |
397 | | |
398 | | /************************************************************************/ |
399 | | /* GDALTriangulationFindFacetBruteForce() */ |
400 | | /************************************************************************/ |
401 | | |
402 | 0 | #define EPS 1e-10 |
403 | | |
404 | | /** Returns the index of the triangle that contains the point by iterating |
405 | | * over all triangles. |
406 | | * |
407 | | * If the function returns FALSE and *panOutputFacetIdx >= 0, then it means |
408 | | * the point is outside the hull of the triangulation, and *panOutputFacetIdx |
409 | | * is the closest triangle to the point. |
410 | | * |
411 | | * @param psDT triangulation. |
412 | | * @param dfX x coordinate of the point. |
413 | | * @param dfY y coordinate of the point. |
414 | | * @param panOutputFacetIdx (output) pointer to the index of the triangle, |
415 | | * or -1 in case of failure. |
416 | | * |
417 | | * @return index >= 0 of the triangle in case of success, -1 otherwise. |
418 | | * |
419 | | * @since GDAL 2.1 |
420 | | */ |
421 | | |
422 | | int GDALTriangulationFindFacetBruteForce(const GDALTriangulation *psDT, |
423 | | double dfX, double dfY, |
424 | | int *panOutputFacetIdx) |
425 | 0 | { |
426 | 0 | int nFacetIdx; |
427 | 0 | *panOutputFacetIdx = -1; |
428 | 0 | if (psDT->pasFacetCoefficients == NULL) |
429 | 0 | { |
430 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
431 | 0 | "GDALTriangulationComputeBarycentricCoefficients() should be " |
432 | 0 | "called before"); |
433 | 0 | return FALSE; |
434 | 0 | } |
435 | 0 | for (nFacetIdx = 0; nFacetIdx < psDT->nFacets; nFacetIdx++) |
436 | 0 | { |
437 | 0 | double l1, l2, l3; |
438 | 0 | const GDALTriBarycentricCoefficients *psCoeffs = |
439 | 0 | &(psDT->pasFacetCoefficients[nFacetIdx]); |
440 | 0 | if (psCoeffs->dfMul1X == 0.0 && psCoeffs->dfMul2X == 0.0 && |
441 | 0 | psCoeffs->dfMul1Y == 0.0 && psCoeffs->dfMul2Y == 0.0) |
442 | 0 | { |
443 | | // Degenerate triangle |
444 | 0 | continue; |
445 | 0 | } |
446 | 0 | l1 = BARYC_COORD_L1(psCoeffs, dfX, dfY); |
447 | 0 | if (l1 < -EPS) |
448 | 0 | { |
449 | 0 | int neighbor = psDT->pasFacets[nFacetIdx].anNeighborIdx[0]; |
450 | 0 | if (neighbor < 0) |
451 | 0 | { |
452 | 0 | *panOutputFacetIdx = nFacetIdx; |
453 | 0 | return FALSE; |
454 | 0 | } |
455 | 0 | continue; |
456 | 0 | } |
457 | 0 | if (l1 > 1 + EPS) |
458 | 0 | continue; |
459 | 0 | l2 = BARYC_COORD_L2(psCoeffs, dfX, dfY); |
460 | 0 | if (l2 < -EPS) |
461 | 0 | { |
462 | 0 | int neighbor = psDT->pasFacets[nFacetIdx].anNeighborIdx[1]; |
463 | 0 | if (neighbor < 0) |
464 | 0 | { |
465 | 0 | *panOutputFacetIdx = nFacetIdx; |
466 | 0 | return FALSE; |
467 | 0 | } |
468 | 0 | continue; |
469 | 0 | } |
470 | 0 | if (l2 > 1 + EPS) |
471 | 0 | continue; |
472 | 0 | l3 = BARYC_COORD_L3(l1, l2); |
473 | 0 | if (l3 < -EPS) |
474 | 0 | { |
475 | 0 | int neighbor = psDT->pasFacets[nFacetIdx].anNeighborIdx[2]; |
476 | 0 | if (neighbor < 0) |
477 | 0 | { |
478 | 0 | *panOutputFacetIdx = nFacetIdx; |
479 | 0 | return FALSE; |
480 | 0 | } |
481 | 0 | continue; |
482 | 0 | } |
483 | 0 | if (l3 > 1 + EPS) |
484 | 0 | continue; |
485 | 0 | *panOutputFacetIdx = nFacetIdx; |
486 | 0 | return TRUE; |
487 | 0 | } |
488 | 0 | return FALSE; |
489 | 0 | } |
490 | | |
491 | | /************************************************************************/ |
492 | | /* GDALTriangulationFindFacetDirected() */ |
493 | | /************************************************************************/ |
494 | | |
495 | 0 | #define EPS 1e-10 |
496 | | |
497 | | /** Returns the index of the triangle that contains the point by walking in |
498 | | * the triangulation. |
499 | | * |
500 | | * If the function returns FALSE and *panOutputFacetIdx >= 0, then it means |
501 | | * the point is outside the hull of the triangulation, and *panOutputFacetIdx |
502 | | * is the closest triangle to the point. |
503 | | * |
504 | | * @param psDT triangulation. |
505 | | * @param nFacetIdx index of first triangle to start with. |
506 | | * Must be >= 0 && < psDT->nFacets |
507 | | * @param dfX x coordinate of the point. |
508 | | * @param dfY y coordinate of the point. |
509 | | * @param panOutputFacetIdx (output) pointer to the index of the triangle, |
510 | | * or -1 in case of failure. |
511 | | * |
512 | | * @return TRUE in case of success, FALSE otherwise. |
513 | | * |
514 | | * @since GDAL 2.1 |
515 | | */ |
516 | | |
517 | | int GDALTriangulationFindFacetDirected(const GDALTriangulation *psDT, |
518 | | int nFacetIdx, double dfX, double dfY, |
519 | | int *panOutputFacetIdx) |
520 | 0 | { |
521 | | #ifdef DEBUG_VERBOSE |
522 | | const int nFacetIdxInitial = nFacetIdx; |
523 | | #endif |
524 | 0 | int k, nIterMax; |
525 | 0 | *panOutputFacetIdx = -1; |
526 | 0 | if (psDT->pasFacetCoefficients == NULL) |
527 | 0 | { |
528 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
529 | 0 | "GDALTriangulationComputeBarycentricCoefficients() should be " |
530 | 0 | "called before"); |
531 | 0 | return FALSE; |
532 | 0 | } |
533 | 0 | CPLAssert(nFacetIdx >= 0 && nFacetIdx < psDT->nFacets); |
534 | | |
535 | 0 | nIterMax = 2 + psDT->nFacets / 4; |
536 | 0 | for (k = 0; k < nIterMax; k++) |
537 | 0 | { |
538 | 0 | double l1, l2, l3; |
539 | 0 | int bMatch = TRUE; |
540 | 0 | const GDALTriFacet *psFacet = &(psDT->pasFacets[nFacetIdx]); |
541 | 0 | const GDALTriBarycentricCoefficients *psCoeffs = |
542 | 0 | &(psDT->pasFacetCoefficients[nFacetIdx]); |
543 | 0 | if (psCoeffs->dfMul1X == 0.0 && psCoeffs->dfMul2X == 0.0 && |
544 | 0 | psCoeffs->dfMul1Y == 0.0 && psCoeffs->dfMul2Y == 0.0) |
545 | 0 | { |
546 | | // Degenerate triangle |
547 | 0 | break; |
548 | 0 | } |
549 | 0 | l1 = BARYC_COORD_L1(psCoeffs, dfX, dfY); |
550 | 0 | if (l1 < -EPS) |
551 | 0 | { |
552 | 0 | int neighbor = psFacet->anNeighborIdx[0]; |
553 | 0 | if (neighbor < 0) |
554 | 0 | { |
555 | | #ifdef DEBUG_VERBOSE |
556 | | CPLDebug("GDAL", "Outside %d in %d iters (initial = %d)", |
557 | | nFacetIdx, k, nFacetIdxInitial); |
558 | | #endif |
559 | 0 | *panOutputFacetIdx = nFacetIdx; |
560 | 0 | return FALSE; |
561 | 0 | } |
562 | 0 | nFacetIdx = neighbor; |
563 | 0 | continue; |
564 | 0 | } |
565 | 0 | else if (l1 > 1 + EPS) |
566 | 0 | bMatch = FALSE; // outside or degenerate |
567 | | |
568 | 0 | l2 = BARYC_COORD_L2(psCoeffs, dfX, dfY); |
569 | 0 | if (l2 < -EPS) |
570 | 0 | { |
571 | 0 | int neighbor = psFacet->anNeighborIdx[1]; |
572 | 0 | if (neighbor < 0) |
573 | 0 | { |
574 | | #ifdef DEBUG_VERBOSE |
575 | | CPLDebug("GDAL", "Outside %d in %d iters (initial = %d)", |
576 | | nFacetIdx, k, nFacetIdxInitial); |
577 | | #endif |
578 | 0 | *panOutputFacetIdx = nFacetIdx; |
579 | 0 | return FALSE; |
580 | 0 | } |
581 | 0 | nFacetIdx = neighbor; |
582 | 0 | continue; |
583 | 0 | } |
584 | 0 | else if (l2 > 1 + EPS) |
585 | 0 | bMatch = FALSE; // outside or degenerate |
586 | | |
587 | 0 | l3 = BARYC_COORD_L3(l1, l2); |
588 | 0 | if (l3 < -EPS) |
589 | 0 | { |
590 | 0 | int neighbor = psFacet->anNeighborIdx[2]; |
591 | 0 | if (neighbor < 0) |
592 | 0 | { |
593 | | #ifdef DEBUG_VERBOSE |
594 | | CPLDebug("GDAL", "Outside %d in %d iters (initial = %d)", |
595 | | nFacetIdx, k, nFacetIdxInitial); |
596 | | #endif |
597 | 0 | *panOutputFacetIdx = nFacetIdx; |
598 | 0 | return FALSE; |
599 | 0 | } |
600 | 0 | nFacetIdx = neighbor; |
601 | 0 | continue; |
602 | 0 | } |
603 | 0 | else if (l3 > 1 + EPS) |
604 | 0 | bMatch = FALSE; // outside or degenerate |
605 | | |
606 | 0 | if (bMatch) |
607 | 0 | { |
608 | | #ifdef DEBUG_VERBOSE |
609 | | CPLDebug("GDAL", "Inside %d in %d iters (initial = %d)", nFacetIdx, |
610 | | k, nFacetIdxInitial); |
611 | | #endif |
612 | 0 | *panOutputFacetIdx = nFacetIdx; |
613 | 0 | return TRUE; |
614 | 0 | } |
615 | 0 | else |
616 | 0 | { |
617 | 0 | break; |
618 | 0 | } |
619 | 0 | } |
620 | | |
621 | 0 | static int nDebugMsgCount = 0; |
622 | 0 | if (nDebugMsgCount <= 20) |
623 | 0 | { |
624 | 0 | CPLDebug("GDAL", "Using brute force lookup%s", |
625 | 0 | (nDebugMsgCount == 20) |
626 | 0 | ? " (this debug message will no longer be emitted)" |
627 | 0 | : ""); |
628 | 0 | nDebugMsgCount++; |
629 | 0 | } |
630 | |
|
631 | 0 | return GDALTriangulationFindFacetBruteForce(psDT, dfX, dfY, |
632 | 0 | panOutputFacetIdx); |
633 | 0 | } |