Coverage Report

Created: 2026-09-01 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/operation/buffer/RightmostEdgeFinder.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2005-2006 Refractions Research Inc.
7
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
8
 *
9
 * This is free software; you can redistribute and/or modify it under
10
 * the terms of the GNU Lesser General Public Licence as published
11
 * by the Free Software Foundation.
12
 * See the COPYING file for more information.
13
 *
14
 **********************************************************************
15
 *
16
 * Last port: operation/buffer/RightmostEdgeFinder.java r320 (JTS-1.12)
17
 *
18
 **********************************************************************/
19
20
#include <geos/algorithm/Orientation.h>
21
#include <geos/operation/buffer/RightmostEdgeFinder.h>
22
#include <geos/geomgraph/DirectedEdge.h>
23
#include <geos/geomgraph/DirectedEdgeStar.h>
24
#include <geos/geom/Position.h>
25
#include <geos/geomgraph/Node.h>
26
#include <geos/geomgraph/Edge.h>
27
#include <geos/util/TopologyException.h>
28
#include <geos/util.h>
29
30
#include <vector>
31
#include <cassert>
32
33
using namespace geos::algorithm; // Orientation
34
using namespace geos::geom;
35
using namespace geos::geomgraph; // DirectedEdge, Position
36
37
namespace geos {
38
namespace operation { // geos.operation
39
namespace buffer { // geos.operation.buffer
40
41
/*public*/
42
RightmostEdgeFinder::RightmostEdgeFinder()
43
    :
44
0
    minIndex(-1), // FIXME: don't use -1 as a sentinel, or we won't be
45
    // able to use an unsigned int here
46
0
    minCoord(Coordinate::getNull()),
47
0
    minDe(nullptr),
48
0
    orientedDe(nullptr)
49
0
{
50
0
}
51
52
/*public*/
53
void
54
RightmostEdgeFinder::findEdge(std::vector<DirectedEdge*>* dirEdgeList)
55
0
{
56
57
#ifndef NDEBUG
58
    std::size_t checked = 0;
59
#endif
60
61
    /*
62
     * Check all forward DirectedEdges only.  This is still general,
63
     * because each edge has a forward DirectedEdge.
64
     */
65
0
    std::size_t dirEdgeListSize = dirEdgeList->size();
66
0
    for(std::size_t i = 0; i < dirEdgeListSize; ++i) {
67
0
        DirectedEdge* de = (*dirEdgeList)[i];
68
0
        assert(de);
69
0
        if(!de->isForward()) {
70
0
            continue;
71
0
        }
72
0
        checkForRightmostCoordinate(de);
73
#ifndef NDEBUG
74
        ++checked;
75
#endif
76
0
    }
77
78
0
    if(! minDe) {
79
        // I don't know why, but it looks like this can happen
80
        // (invalid PlanarGraph, I think)
81
        // See http://trac.osgeo.org/geos/ticket/605#comment:17
82
        //
83
0
        throw util::TopologyException("No forward edges found in buffer subgraph");
84
0
    }
85
86
#ifndef NDEBUG
87
    assert(checked > 0);
88
    assert(minIndex >= 0);
89
    assert(minDe);
90
#endif
91
92
    /*
93
     * If the rightmost point is a node, we need to identify which of
94
     * the incident edges is rightmost.
95
     */
96
0
    assert(minIndex != 0 || minCoord == minDe->getCoordinate());
97
    // inconsistency in rightmost processing
98
99
0
    if(minIndex == 0) {
100
0
        findRightmostEdgeAtNode();
101
0
    }
102
0
    else {
103
0
        findRightmostEdgeAtVertex();
104
0
    }
105
106
    /*
107
     * now check that the extreme side is the R side.
108
     * If not, use the sym instead.
109
     */
110
0
    orientedDe = minDe;
111
0
    int rightmostSide = getRightmostSide(minDe, minIndex);
112
0
    if(rightmostSide == Position::LEFT) {
113
0
        orientedDe = minDe->getSym();
114
0
    }
115
0
}
116
117
/*private*/
118
void
119
RightmostEdgeFinder::findRightmostEdgeAtNode()
120
0
{
121
0
    Node* node = minDe->getNode();
122
0
    assert(node);
123
124
0
    DirectedEdgeStar* star = detail::down_cast<DirectedEdgeStar*>(node->getEdges());
125
126
    // Warning! NULL could be returned if the star is empty!
127
0
    minDe = star->getRightmostEdge();
128
0
    assert(minDe);
129
130
    // the DirectedEdge returned by the previous call is not
131
    // necessarily in the forward direction. Use the sym edge if it isn't.
132
0
    if(!minDe->isForward()) {
133
0
        minDe = minDe->getSym();
134
135
0
        const Edge* minEdge = minDe->getEdge();
136
0
        assert(minEdge);
137
138
0
        const CoordinateSequence* minEdgeCoords =
139
0
            minEdge->getCoordinates();
140
0
        assert(minEdgeCoords);
141
142
0
        minIndex = (int)(minEdgeCoords->getSize()) - 1;
143
0
        assert(minIndex >= 0);
144
0
    }
145
0
}
146
147
/*private*/
148
void
149
RightmostEdgeFinder::findRightmostEdgeAtVertex()
150
0
{
151
    /*
152
     * The rightmost point is an interior vertex, so it has
153
     * a segment on either side of it.
154
     * If these segments are both above or below the rightmost
155
     * point, we need to determine their relative orientation
156
     * to decide which is rightmost.
157
     */
158
159
0
    Edge* minEdge = minDe->getEdge();
160
0
    assert(minEdge);
161
0
    const CoordinateSequence* pts = minEdge->getCoordinates();
162
0
    assert(pts);
163
164
    // rightmost point expected to be interior vertex of edge
165
0
    assert(minIndex > 0);
166
0
    assert((std::size_t)minIndex < pts->getSize());
167
168
0
    const Coordinate& pPrev = pts->getAt(static_cast<std::size_t>(minIndex) - 1);
169
0
    const Coordinate& pNext = pts->getAt(static_cast<std::size_t>(minIndex) + 1);
170
0
    int orientation = Orientation::index(
171
0
                          minCoord,
172
0
                          pNext,
173
0
                          pPrev);
174
0
    bool usePrev = false;
175
176
    // both segments are below min point
177
0
    if(pPrev.y < minCoord.y && pNext.y < minCoord.y
178
0
            && orientation == Orientation::COUNTERCLOCKWISE) {
179
0
        usePrev = true;
180
0
    }
181
0
    else if(pPrev.y > minCoord.y && pNext.y > minCoord.y
182
0
            && orientation == Orientation::CLOCKWISE) {
183
0
        usePrev = true;
184
0
    }
185
186
    // if both segments are on the same side, do nothing - either is safe
187
    // to select as a rightmost segment
188
0
    if(usePrev) {
189
0
        minIndex = minIndex - 1;
190
0
    }
191
0
}
192
193
/*private*/
194
void
195
RightmostEdgeFinder::checkForRightmostCoordinate(DirectedEdge* de)
196
0
{
197
0
    const Edge* deEdge = de->getEdge();
198
0
    assert(deEdge);
199
200
0
    const CoordinateSequence* coord = deEdge->getCoordinates();
201
0
    assert(coord);
202
203
    // only check vertices which are the starting point of
204
    // a non-horizontal segment
205
0
    std::size_t n = coord->getSize() - 1;
206
0
    for(std::size_t i = 0; i < n; i++) {
207
        // only check vertices which are the start or end point
208
        // of a non-horizontal segment
209
        // <FIX> MD 19 Sep 03 - NO!  we can test all vertices,
210
        // since the rightmost must have a non-horiz segment adjacent to it
211
0
        if(minCoord.isNull() ||
212
0
                coord->getAt(i).x > minCoord.x) {
213
0
            minDe = de;
214
0
            minIndex = (int)i;
215
0
            minCoord = coord->getAt(i);
216
0
        }
217
0
    }
218
0
}
219
220
/*private*/
221
int
222
RightmostEdgeFinder::getRightmostSide(DirectedEdge* de, int index)
223
0
{
224
0
    int side = getRightmostSideOfSegment(de, index);
225
226
0
    if(side < 0) {
227
0
        side = getRightmostSideOfSegment(de, index - 1);
228
0
    }
229
230
0
    if(side < 0) {
231
        // reaching here can indicate that segment is horizontal
232
        // Assert::shouldNeverReachHere(
233
        //  "problem with finding rightmost side of segment");
234
235
0
        minCoord = Coordinate::getNull();
236
0
        checkForRightmostCoordinate(de);
237
0
    }
238
239
0
    return side;
240
0
}
241
242
/*private*/
243
int
244
RightmostEdgeFinder::getRightmostSideOfSegment(DirectedEdge* de, int i)
245
0
{
246
0
    assert(de);
247
248
0
    const Edge* e = de->getEdge();
249
0
    assert(e);
250
251
0
    const CoordinateSequence* coord = e->getCoordinates();
252
0
    assert(coord);
253
254
0
    if(i < 0 || i + 1 >= (int)coord->getSize()) {
255
0
        return -1;
256
0
    }
257
258
    // indicates edge is parallel to x-axis
259
0
    const Coordinate& p0 = coord->getAt(static_cast<std::size_t>(i));
260
0
    const Coordinate& p1 = coord->getAt(static_cast<std::size_t>(i) + 1);
261
0
    if(p0.y == p1.y) {
262
0
        return -1;
263
0
    }
264
265
0
    int pos = Position::LEFT;
266
0
    if(p0.y < p1.y) {
267
0
        pos = Position::RIGHT;
268
0
    }
269
0
    return pos;
270
0
}
271
272
} // namespace geos.operation.buffer
273
} // namespace geos.operation
274
} // namespace geos