Coverage Report

Created: 2025-11-15 08:43

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