Coverage Report

Created: 2025-10-12 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/triangulate/quadedge/QuadEdgeSubdivision.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2012 Excensus LLC.
7
 * Copyright (C) 2019 Daniel Baston
8
 *
9
 * This is free software; you can redistribute and/or modify it under
10
 * the terms of the GNU Lesser General Licence as published
11
 * by the Free Software Foundation.
12
 * See the COPYING file for more information.
13
 *
14
 **********************************************************************
15
 *
16
 * Last port: triangulate/quadedge/QuadEdgeSubdivision.java r524
17
 *
18
 **********************************************************************/
19
#include <geos/triangulate/quadedge/QuadEdgeSubdivision.h>
20
21
#include <algorithm>
22
#include <vector>
23
#include <set>
24
#include <iostream>
25
26
#include <geos/geom/Polygon.h>
27
#include <geos/geom/LineSegment.h>
28
#include <geos/geom/LineString.h>
29
#include <geos/geom/CoordinateSequence.h>
30
#include <geos/geom/CoordinateList.h>
31
#include <geos/geom/GeometryCollection.h>
32
#include <geos/geom/GeometryFactory.h>
33
#include <geos/util.h>
34
#include <geos/triangulate/quadedge/QuadEdge.h>
35
#include <geos/triangulate/quadedge/QuadEdgeLocator.h>
36
#include <geos/triangulate/quadedge/LastFoundQuadEdgeLocator.h>
37
#include <geos/triangulate/quadedge/LocateFailureException.h>
38
#include <geos/triangulate/quadedge/TriangleVisitor.h>
39
#include <geos/geom/Triangle.h>
40
#include <geos/util.h>
41
42
43
using namespace geos::geom;
44
45
46
namespace geos {
47
namespace triangulate { //geos.triangulate
48
namespace quadedge { //geos.triangulate.quadedge
49
50
const double EDGE_COINCIDENCE_TOL_FACTOR = 1000;
51
52
//-- Frame size factor for initializing subdivision frame.  Larger is more robust
53
const double FRAME_SIZE_FACTOR = 10;
54
55
void
56
QuadEdgeSubdivision::getTriangleEdges(const QuadEdge& startQE,
57
                                      const QuadEdge* triEdge[3])
58
0
{
59
0
    triEdge[0] = &startQE;
60
0
    triEdge[1] = &triEdge[0]->lNext();
61
0
    triEdge[2] = &triEdge[1]->lNext();
62
0
    if(&triEdge[2]->lNext() != triEdge[0]) {
63
0
        throw util::IllegalArgumentException("Edges do not form a triangle");
64
0
    }
65
0
}
66
67
QuadEdgeSubdivision::QuadEdgeSubdivision(const geom::Envelope& env, double p_tolerance) :
68
0
    tolerance(p_tolerance),
69
0
    locator(new LastFoundQuadEdgeLocator(this)),
70
0
    visit_state_clean(true)
71
0
{
72
0
    edgeCoincidenceTolerance = tolerance / EDGE_COINCIDENCE_TOL_FACTOR;
73
0
    createFrame(env);
74
0
    initSubdiv();
75
0
}
76
77
void
78
QuadEdgeSubdivision::createFrame(const geom::Envelope& env)
79
0
{
80
0
    if (env.isNull()) {
81
0
        throw util::IllegalArgumentException("Cannot create frame from empty Envelope.");
82
0
    }
83
84
0
    double deltaX = env.getWidth();
85
0
    double deltaY = env.getHeight();
86
0
    double offset = std::max(deltaX, deltaY) * FRAME_SIZE_FACTOR;
87
88
0
    frameVertex[0] = Vertex((env.getMaxX() + env.getMinX()) / 2.0,
89
0
                            env.getMaxY() + offset);
90
0
    frameVertex[1] = Vertex(env.getMinX() - offset, env.getMinY() - offset);
91
0
    frameVertex[2] = Vertex(env.getMaxX() + offset, env.getMinY() - offset);
92
93
0
    frameEnv = Envelope(frameVertex[0].getCoordinate(),
94
0
                        frameVertex[1].getCoordinate());
95
0
    frameEnv.expandToInclude(frameVertex[2].getCoordinate());
96
0
}
97
void
98
QuadEdgeSubdivision::initSubdiv()
99
0
{
100
0
    assert(quadEdges.empty());
101
102
    // build initial subdivision from frame
103
0
    startingEdges[0] = QuadEdge::makeEdge(frameVertex[0], frameVertex[1], quadEdges);
104
0
    startingEdges[1] = QuadEdge::makeEdge(frameVertex[1], frameVertex[2], quadEdges);
105
0
    QuadEdge::splice(startingEdges[0]->sym(), *startingEdges[1]);
106
107
0
    startingEdges[2] = QuadEdge::makeEdge(frameVertex[2], frameVertex[0], quadEdges);
108
0
    QuadEdge::splice(startingEdges[1]->sym(), *startingEdges[2]);
109
0
    QuadEdge::splice(startingEdges[2]->sym(), *startingEdges[0]);
110
0
}
111
112
QuadEdge&
113
QuadEdgeSubdivision::makeEdge(const Vertex& o, const Vertex& d)
114
0
{
115
0
    QuadEdge* e = QuadEdge::makeEdge(o, d, quadEdges);
116
0
    return *e;
117
0
}
118
119
QuadEdge&
120
QuadEdgeSubdivision::connect(QuadEdge& a, QuadEdge& b)
121
0
{
122
0
    QuadEdge* e = QuadEdge::connect(a, b, quadEdges);
123
0
    return *e;
124
0
}
125
126
void
127
QuadEdgeSubdivision::remove(QuadEdge& e)
128
0
{
129
0
    QuadEdge::splice(e, e.oPrev());
130
0
    QuadEdge::splice(e.sym(), e.sym().oPrev());
131
132
    // because QuadEdge pointers must be stable, do not remove edge from quadedges container
133
    // This is fine since they are detached from the subdivision
134
135
0
    e.remove();
136
0
}
137
138
QuadEdge*
139
QuadEdgeSubdivision::locateFromEdge(const Vertex& v,
140
                                    const QuadEdge& startEdge) const
141
0
{
142
0
    ::geos::ignore_unused_variable_warning(startEdge);
143
144
0
    std::size_t iter = 0;
145
0
    auto maxIter = quadEdges.size();
146
147
0
    QuadEdge* e = startingEdges[0];
148
149
0
    for(;;) {
150
0
        ++iter;
151
        /*
152
         * So far it has always been the case that failure to locate indicates an
153
         * invalid subdivision. So just fail completely. (An alternative would be
154
         * to perform an exhaustive search for the containing triangle, but this
155
         * would mask errors in the subdivision topology)
156
         *
157
         * This can also happen if two vertices are located very close together,
158
         * since the orientation predicates may experience precision failures.
159
         */
160
0
        if(iter > maxIter) {
161
0
            throw LocateFailureException("Could not locate vertex.");
162
0
        }
163
164
0
        if((v.equals(e->orig())) || (v.equals(e->dest()))) {
165
0
            break;
166
0
        }
167
0
        else if(v.rightOf(*e)) {
168
0
            e = &e->sym();
169
0
        }
170
0
        else if(!v.rightOf(e->oNext())) {
171
0
            e = &e->oNext();
172
0
        }
173
0
        else if(!v.rightOf(e->dPrev())) {
174
0
            e = &e->dPrev();
175
0
        }
176
0
        else {
177
            // on edge or in triangle containing edge
178
0
            break;
179
0
        }
180
0
    }
181
0
    return e;
182
0
}
183
184
QuadEdge*
185
QuadEdgeSubdivision::locate(const Coordinate& p0, const Coordinate& p1)
186
0
{
187
    // find an edge containing one of the points
188
0
    QuadEdge* e = locator->locate(Vertex(p0));
189
0
    if(e == nullptr) {
190
0
        return nullptr;
191
0
    }
192
193
    // normalize so that p0 is origin of base edge
194
0
    QuadEdge* base = e;
195
0
    if(e->dest().getCoordinate().equals2D(p0)) {
196
0
        base = &e->sym();
197
0
    }
198
    // check all edges around origin of base edge
199
0
    QuadEdge* locEdge = base;
200
0
    do {
201
0
        if(locEdge->dest().getCoordinate().equals2D(p1)) {
202
0
            return locEdge;
203
0
        }
204
0
        locEdge = &locEdge->oNext();
205
0
    }
206
0
    while(locEdge != base);
207
0
    return nullptr;
208
0
}
209
210
QuadEdge&
211
QuadEdgeSubdivision::insertSite(const Vertex& v)
212
0
{
213
0
    QuadEdge* e = locate(v);
214
215
0
    if((v.equals(e->orig(), tolerance)) || (v.equals(e->dest(), tolerance))) {
216
0
        return *e; // point already in subdivision.
217
0
    }
218
219
    // Connect the new point to the vertices of the containing
220
    // triangle (or quadrilateral, if the new point fell on an
221
    // existing edge.)
222
0
    QuadEdge* base = &makeEdge(e->orig(), v);
223
0
    QuadEdge::splice(*base, *e);
224
0
    QuadEdge* startEdge = base;
225
0
    do {
226
0
        base = &connect(*e, base->sym());
227
0
        e = &base->oPrev();
228
0
    }
229
0
    while(&e->lNext() != startEdge);
230
231
0
    return *startEdge;
232
0
}
233
234
bool
235
QuadEdgeSubdivision::isFrameEdge(const QuadEdge& e) const
236
0
{
237
0
    if(isFrameVertex(e.orig()) || isFrameVertex(e.dest())) {
238
0
        return true;
239
0
    }
240
0
    return false;
241
0
}
242
243
bool
244
QuadEdgeSubdivision::isFrameBorderEdge(const QuadEdge& e) const
245
0
{
246
    // check other vertex of triangle to left of edge
247
0
    Vertex vLeftTriOther = e.lNext().dest();
248
0
    if(isFrameVertex(vLeftTriOther)) {
249
0
        return true;
250
0
    }
251
    // check other vertex of triangle to right of edge
252
0
    Vertex vRightTriOther = e.sym().lNext().dest();
253
0
    if(isFrameVertex(vRightTriOther)) {
254
0
        return true;
255
0
    }
256
257
0
    return false;
258
0
}
259
260
bool
261
QuadEdgeSubdivision::isFrameVertex(const Vertex& v) const
262
0
{
263
0
    if(v.equals(frameVertex[0])) {
264
0
        return true;
265
0
    }
266
0
    if(v.equals(frameVertex[1])) {
267
0
        return true;
268
0
    }
269
0
    if(v.equals(frameVertex[2])) {
270
0
        return true;
271
0
    }
272
0
    return false;
273
0
}
274
275
bool
276
QuadEdgeSubdivision::isOnEdge(const QuadEdge& e, const Coordinate& p) const
277
0
{
278
0
    geom::LineSegment seg;
279
0
    seg.setCoordinates(e.orig().getCoordinate(), e.dest().getCoordinate());
280
0
    double dist = seg.distance(p);
281
    // heuristic (hack?)
282
0
    return dist < edgeCoincidenceTolerance;
283
0
}
284
285
bool
286
QuadEdgeSubdivision::isVertexOfEdge(const QuadEdge& e, const Vertex& v) const
287
0
{
288
0
    if((v.equals(e.orig(), tolerance)) || (v.equals(e.dest(), tolerance))) {
289
0
        return true;
290
0
    }
291
0
    return false;
292
0
}
293
294
std::unique_ptr<QuadEdgeSubdivision::QuadEdgeList>
295
QuadEdgeSubdivision::getPrimaryEdges(bool includeFrame)
296
0
{
297
0
    QuadEdgeList* edges = new QuadEdgeList();
298
0
    QuadEdgeStack edgeStack;
299
300
0
    edgeStack.push(startingEdges[0]);
301
302
0
    prepareVisit();
303
304
0
    while(!edgeStack.empty()) {
305
0
        QuadEdge* edge = edgeStack.top();
306
0
        edgeStack.pop();
307
0
        if(!edge->isVisited()) {
308
0
            QuadEdge* priQE = (QuadEdge*)&edge->getPrimary();
309
310
0
            if(includeFrame || ! isFrameEdge(*priQE)) {
311
0
                edges->push_back(priQE);
312
0
            }
313
314
0
            edgeStack.push(&edge->oNext());
315
0
            edgeStack.push(&edge->sym().oNext());
316
317
0
            edge->setVisited(true);
318
0
            edge->sym().setVisited(true);
319
0
        }
320
0
    }
321
0
    return std::unique_ptr<QuadEdgeList>(edges);
322
0
}
323
324
std::array<QuadEdge*, 3>*
325
QuadEdgeSubdivision::fetchTriangleToVisit(QuadEdge* edge,
326
        QuadEdgeStack& edgeStack, bool includeFrame)
327
0
{
328
0
    QuadEdge* curr = edge;
329
0
    std::size_t edgeCount = 0;
330
0
    bool isFrame = false;
331
0
    do {
332
0
        m_triEdges[edgeCount] = curr;
333
334
0
        if(!includeFrame && isFrameEdge(*curr)) {
335
0
            isFrame = true;
336
0
        }
337
338
        // push sym edges to visit next
339
0
        QuadEdge* sym = &curr->sym();
340
0
        if (!sym->isVisited()) {
341
0
            edgeStack.push(sym);
342
0
        }
343
344
        // mark this edge as visited
345
0
        curr->setVisited(true);
346
347
0
        edgeCount++;
348
0
        curr = &curr->lNext();
349
0
    }
350
0
    while(curr != edge);
351
352
0
    if(!includeFrame && isFrame) {
353
0
        return nullptr;
354
0
    }
355
0
    return &m_triEdges;
356
0
}
357
358
class
359
    QuadEdgeSubdivision::TriangleCoordinatesVisitor : public TriangleVisitor {
360
private:
361
    QuadEdgeSubdivision::TriList* triCoords;
362
363
public:
364
0
    TriangleCoordinatesVisitor(QuadEdgeSubdivision::TriList* p_triCoords): triCoords(p_triCoords)
365
0
    {
366
0
    }
367
368
    void
369
    visit(std::array<QuadEdge*, 3>& triEdges) override
370
0
    {
371
0
        auto coordSeq = detail::make_unique<geom::CoordinateSequence>(4u, 0u);
372
0
        for(std::size_t i = 0; i < 3; i++) {
373
0
            Vertex v = triEdges[i]->orig();
374
0
            coordSeq->setAt(v.getCoordinate(), i);
375
0
        }
376
0
        coordSeq->setAt(triEdges[0]->orig().getCoordinate(), 3);
377
0
        triCoords->push_back(std::move(coordSeq));
378
0
    }
379
};
380
381
382
class
383
    QuadEdgeSubdivision::TriangleCircumcentreVisitor : public TriangleVisitor {
384
public:
385
    void
386
    visit(std::array<QuadEdge*, 3>& triEdges) override
387
0
    {
388
0
        Triangle triangle(triEdges[0]->orig().getCoordinate(),
389
0
                          triEdges[1]->orig().getCoordinate(), triEdges[2]->orig().getCoordinate());
390
0
        Coordinate cc;
391
392
        //TODO: identify heuristic to allow calling faster circumcentre() when possible
393
0
        triangle.circumcentreDD(cc);
394
395
0
        Vertex ccVertex(cc);
396
397
0
        for(uint8_t i = 0 ; i < 3 ; i++) {
398
0
            triEdges[i]->rot().setOrig(ccVertex);
399
0
        }
400
0
    }
401
};
402
403
404
void
405
QuadEdgeSubdivision::getTriangleCoordinates(QuadEdgeSubdivision::TriList* triList, bool includeFrame)
406
0
{
407
0
    TriangleCoordinatesVisitor visitor(triList);
408
0
    visitTriangles(&visitor, includeFrame);
409
0
}
410
411
void
412
0
QuadEdgeSubdivision::prepareVisit() {
413
0
    if (!visit_state_clean) {
414
0
        for (auto& qe : quadEdges) {
415
0
            qe.setVisited(false);
416
0
        }
417
0
    }
418
419
0
    visit_state_clean = false;
420
0
}
421
422
void
423
QuadEdgeSubdivision::visitTriangles(TriangleVisitor* triVisitor, bool includeFrame)
424
0
{
425
0
    QuadEdgeStack edgeStack;
426
0
    edgeStack.push(startingEdges[0]);
427
428
0
    prepareVisit();
429
430
0
    while(!edgeStack.empty()) {
431
0
        QuadEdge* edge = edgeStack.top();
432
0
        edgeStack.pop();
433
0
        if(!edge->isVisited()) {
434
0
            auto triEdges = fetchTriangleToVisit(edge, edgeStack, includeFrame);
435
0
            if(triEdges != nullptr) {
436
0
                triVisitor->visit(*triEdges);
437
0
            }
438
0
        }
439
0
    }
440
0
}
441
442
std::unique_ptr<geom::MultiLineString>
443
QuadEdgeSubdivision::getEdges(const geom::GeometryFactory& geomFact)
444
0
{
445
0
    std::unique_ptr<QuadEdgeList> p_quadEdges(getPrimaryEdges(false));
446
0
    std::vector<std::unique_ptr<Geometry>> edges;
447
448
0
    edges.reserve(p_quadEdges->size());
449
0
    for(const QuadEdge* qe : *p_quadEdges) {
450
0
        auto coordSeq = detail::make_unique<geom::CoordinateSequence>(2u);
451
452
0
        coordSeq->setAt(qe->orig().getCoordinate(), 0);
453
0
        coordSeq->setAt(qe->dest().getCoordinate(), 1);
454
455
0
        edges.emplace_back(geomFact.createLineString(std::move(coordSeq)));
456
0
    }
457
458
0
    return geomFact.createMultiLineString(std::move(edges));
459
0
}
460
461
std::unique_ptr<GeometryCollection>
462
QuadEdgeSubdivision::getTriangles(const GeometryFactory& geomFact)
463
0
{
464
0
    TriList triPtsList;
465
0
    getTriangleCoordinates(&triPtsList, false);
466
0
    std::vector<std::unique_ptr<Geometry>> tris;
467
0
    tris.reserve(triPtsList.size());
468
469
0
    for(auto& coordSeq : triPtsList) {
470
0
        tris.push_back(
471
0
                geomFact.createPolygon(geomFact.createLinearRing(std::move(coordSeq))));
472
0
    }
473
474
0
    return geomFact.createGeometryCollection(std::move(tris));
475
0
}
476
477
478
//Methods for VoronoiDiagram
479
std::unique_ptr<geom::GeometryCollection>
480
QuadEdgeSubdivision::getVoronoiDiagram(const geom::GeometryFactory& geomFact)
481
0
{
482
0
    return geomFact.createGeometryCollection(getVoronoiCellPolygons(geomFact));
483
0
}
484
485
std::unique_ptr<geom::MultiLineString>
486
QuadEdgeSubdivision::getVoronoiDiagramEdges(const geom::GeometryFactory& geomFact)
487
0
{
488
0
    return geomFact.createMultiLineString(getVoronoiCellEdges(geomFact));
489
0
}
490
491
std::vector<std::unique_ptr<geom::Geometry>>
492
QuadEdgeSubdivision::getVoronoiCellPolygons(const geom::GeometryFactory& geomFact)
493
0
{
494
0
    std::vector<std::unique_ptr<geom::Geometry>> cells;
495
0
    TriangleCircumcentreVisitor tricircumVisitor;
496
497
0
    visitTriangles(&tricircumVisitor, true);
498
499
0
    std::unique_ptr<QuadEdgeSubdivision::QuadEdgeList> edges = getVertexUniqueEdges(false);
500
501
0
    cells.reserve(edges->size());
502
0
    for(const QuadEdge* qe : *edges) {
503
0
        cells.push_back(getVoronoiCellPolygon(qe, geomFact));
504
0
    }
505
506
0
    return cells;
507
0
}
508
509
std::vector<std::unique_ptr<geom::Geometry>>
510
QuadEdgeSubdivision::getVoronoiCellEdges(const geom::GeometryFactory& geomFact)
511
0
{
512
0
    std::vector<std::unique_ptr<geom::Geometry>> cells;
513
0
    TriangleCircumcentreVisitor tricircumVisitor;
514
515
0
    visitTriangles((TriangleVisitor*) &tricircumVisitor, true);
516
517
0
    std::unique_ptr<QuadEdgeSubdivision::QuadEdgeList> edges = getVertexUniqueEdges(false);
518
0
    cells.reserve(edges->size());
519
520
0
    for(const QuadEdge* qe : *edges) {
521
0
        cells.push_back(getVoronoiCellEdge(qe, geomFact));
522
0
    }
523
524
0
    return cells;
525
0
}
526
527
std::unique_ptr<geom::Geometry>
528
QuadEdgeSubdivision::getVoronoiCellPolygon(const QuadEdge* qe, const geom::GeometryFactory& geomFact)
529
0
{
530
0
    auto cellPts = detail::make_unique<CoordinateSequence>();
531
532
0
    const QuadEdge* startQE = qe;
533
0
    do {
534
0
        const Coordinate& cc = qe->rot().orig().getCoordinate();
535
0
        if(cellPts->isEmpty() || cellPts->back() != cc) {  // no duplicates
536
0
            cellPts->add(cc);
537
0
        }
538
0
        qe = &qe->oPrev();
539
540
0
    }
541
0
    while(qe != startQE);
542
543
    // Close the ring
544
0
    if (cellPts->front() != cellPts->back()) {
545
0
        cellPts->closeRing();
546
0
    }
547
0
    if (cellPts->size() < 4) {
548
0
        cellPts->add(cellPts->back());
549
0
    }
550
551
0
    std::unique_ptr<Geometry> cellPoly = geomFact.createPolygon(geomFact.createLinearRing(std::move(cellPts)));
552
553
0
    const Vertex& v = startQE->orig();
554
0
    const Coordinate& c = v.getCoordinate();
555
0
    cellPoly->setUserData(const_cast<Coordinate*>(&c));
556
557
0
    return cellPoly;
558
0
}
559
560
std::unique_ptr<geom::Geometry>
561
QuadEdgeSubdivision::getVoronoiCellEdge(const QuadEdge* qe, const geom::GeometryFactory& geomFact)
562
0
{
563
0
    auto cellPts = detail::make_unique<CoordinateSequence>();
564
565
0
    const QuadEdge* startQE = qe;
566
0
    do {
567
0
        const Coordinate& cc = qe->rot().orig().getCoordinate();
568
0
        if(cellPts->isEmpty() || cellPts->back() != cc) {  // no duplicates
569
0
            cellPts->add(cc);
570
0
        }
571
0
        qe = &qe->oPrev();
572
573
0
    }
574
0
    while(qe != startQE);
575
576
    // Close the ring
577
0
    if (cellPts->front() != cellPts->back()) {
578
0
        cellPts->closeRing();
579
0
    }
580
581
0
    std::unique_ptr<geom::Geometry> cellEdge(
582
0
        geomFact.createLineString(std::move(cellPts)));
583
584
0
    return cellEdge;
585
0
}
586
587
std::unique_ptr<QuadEdgeSubdivision::QuadEdgeList>
588
QuadEdgeSubdivision::getVertexUniqueEdges(bool includeFrame)
589
0
{
590
0
    auto edges = detail::make_unique<QuadEdgeList>();
591
0
    std::set<Vertex> visitedVertices; // TODO unordered_set of Vertex* ?
592
593
0
    for(auto& quartet : quadEdges) {
594
0
        QuadEdge* qe = &quartet.base();
595
0
        const Vertex& v = qe->orig();
596
597
0
        if(visitedVertices.insert(v).second) {
598
            // v was not found and was newly inserted
599
0
            if(includeFrame || ! QuadEdgeSubdivision::isFrameVertex(v)) {
600
0
                edges->push_back(qe);
601
0
            }
602
0
        }
603
0
        QuadEdge* qd = &(qe->sym());
604
0
        const Vertex& vd = qd->orig();
605
606
0
        if (visitedVertices.insert(vd).second) {
607
            // vd was not found and was newly inserted
608
0
            if(includeFrame || ! QuadEdgeSubdivision::isFrameVertex(vd)) {
609
0
                edges->push_back(qd);
610
0
            }
611
0
        }
612
0
    }
613
0
    return edges;
614
0
}
615
616
} //namespace geos.triangulate.quadedge
617
} //namespace geos.triangulate
618
619
} //namespace goes