Coverage Report

Created: 2025-06-13 06:18

/src/gdal/alg/delaunay.c
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
}