/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 |