1 /**********************************************************************
2  *
3  * GEOS-Geometry Engine Open Source
4  * http://geos.osgeo.org
5  *
6  * Copyright (C) 2009-2011  Sandro Santilli <strk@kbt.io>
7  * Copyright (C) 2005 Refractions Research Inc.
8  * Copyright (C) 2001-2002 Vivid Solutions Inc.
9  *
10  * This is free software; you can redistribute and/or modify it under
11  * the terms of the GNU Lesser General Public Licence as published
12  * by the Free Software Foundation.
13  * See the COPYING file for more information.
14  *
15  **********************************************************************
16  *
17  * Last port: operation/buffer/OffsetCurveBuilder.java r378 (JTS-1.12)
18  *
19  **********************************************************************/
20 
21 #include <cassert>
22 #include <cmath>
23 #include <vector>
24 
25 #include <geos/algorithm/Angle.h>
26 #include <geos/operation/buffer/OffsetCurveBuilder.h>
27 #include <geos/operation/buffer/BufferInputLineSimplifier.h>
28 #include <geos/operation/buffer/BufferOp.h>
29 #include <geos/operation/buffer/BufferParameters.h>
30 #include <geos/geom/Position.h>
31 #include <geos/geom/CoordinateArraySequence.h>
32 #include <geos/geom/CoordinateSequence.h>
33 #include <geos/geom/Coordinate.h>
34 #include <geos/geom/PrecisionModel.h>
35 #include <geos/algorithm/NotRepresentableException.h>
36 #include <geos/algorithm/HCoordinate.h>
37 #include <geos/util.h>
38 #include <geos/util/IllegalArgumentException.h>
39 
40 #ifndef GEOS_DEBUG
41 #define GEOS_DEBUG 0
42 #endif
43 
44 using namespace std;
45 using namespace geos::algorithm;
46 using namespace geos::geom;
47 
48 namespace geos {
49 namespace operation { // geos.operation
50 namespace buffer { // geos.operation.buffer
51 
52 /*private data*/
53 const double OffsetCurveBuilder::SIMPLIFY_FACTOR = 100.0;
54 
55 /*public*/
56 void
getLineCurve(const CoordinateSequence * inputPts,double nDistance,vector<CoordinateSequence * > & lineList)57 OffsetCurveBuilder::getLineCurve(const CoordinateSequence* inputPts,
58                                  double nDistance, vector<CoordinateSequence*>& lineList)
59 {
60     distance = nDistance;
61 
62     if (isLineOffsetEmpty(distance)) {
63         return;
64     }
65 
66     double posDistance = std::abs(distance);
67 
68     std::unique_ptr<OffsetSegmentGenerator> segGen = getSegGen(posDistance);
69     if(inputPts->getSize() <= 1) {
70         computePointCurve(inputPts->getAt(0), *segGen);
71     }
72     else {
73         if(bufParams.isSingleSided()) {
74             bool isRightSide = distance < 0.0;
75             computeSingleSidedBufferCurve(*inputPts, isRightSide, *segGen);
76         }
77         else {
78             computeLineBufferCurve(*inputPts, *segGen);
79         }
80     }
81 
82     segGen->getCoordinates(lineList);
83 }
84 
85 /* private */
86 void
computePointCurve(const Coordinate & pt,OffsetSegmentGenerator & segGen)87 OffsetCurveBuilder::computePointCurve(const Coordinate& pt,
88                                       OffsetSegmentGenerator& segGen)
89 {
90     switch(bufParams.getEndCapStyle()) {
91     case BufferParameters::CAP_ROUND:
92         segGen.createCircle(pt, distance);
93         break;
94     case BufferParameters::CAP_SQUARE:
95         segGen.createSquare(pt, distance);
96         break;
97     default:
98         // otherwise curve is empty (e.g. for a butt cap);
99         break;
100     }
101 }
102 
103 /*public*/
104 void
getSingleSidedLineCurve(const CoordinateSequence * inputPts,double p_distance,vector<CoordinateSequence * > & lineList,bool leftSide,bool rightSide)105 OffsetCurveBuilder::getSingleSidedLineCurve(const CoordinateSequence* inputPts,
106         double p_distance, vector<CoordinateSequence*>& lineList, bool leftSide,
107         bool rightSide)
108 {
109     // A zero or negative width buffer of a line/point is empty.
110     if(p_distance <= 0.0) {
111         return ;
112     }
113 
114     if(inputPts->getSize() < 2) {
115         // No cap, so just return.
116         return ;
117     }
118 
119     double distTol = simplifyTolerance(p_distance);
120 
121     std::unique_ptr<OffsetSegmentGenerator> segGen = getSegGen(p_distance);
122 
123     if(leftSide) {
124         //--------- compute points for left side of line
125         // Simplify the appropriate side of the line before generating
126         std::unique_ptr<CoordinateSequence> simp1_ =
127             BufferInputLineSimplifier::simplify(*inputPts, distTol);
128         const CoordinateSequence& simp1 = *simp1_;
129 
130 
131         auto n1 = simp1.size() - 1;
132         if(! n1) {
133             throw util::IllegalArgumentException("Cannot get offset of single-vertex line");
134         }
135         segGen->initSideSegments(simp1[0], simp1[1], Position::LEFT);
136         segGen->addFirstSegment();
137         for(size_t i = 2; i <= n1; ++i) {
138             segGen->addNextSegment(simp1[i], true);
139         }
140         segGen->addLastSegment();
141     }
142 
143     if(rightSide) {
144 
145         //---------- compute points for right side of line
146         // Simplify the appropriate side of the line before generating
147         std::unique_ptr<CoordinateSequence> simp2_ =
148             BufferInputLineSimplifier::simplify(*inputPts, -distTol);
149         const CoordinateSequence& simp2 = *simp2_;
150 
151         auto n2 = simp2.size() - 1;
152         if(! n2) {
153             throw util::IllegalArgumentException("Cannot get offset of single-vertex line");
154         }
155         segGen->initSideSegments(simp2[n2], simp2[n2 - 1], Position::LEFT);
156         segGen->addFirstSegment();
157         for(size_t i = n2 - 1; i > 0; --i) {
158             segGen->addNextSegment(simp2[i - 1], true);
159         }
160         segGen->addLastSegment();
161     }
162 
163     segGen->getCoordinates(lineList);
164 }
165 
166 /*public*/
167 bool
isLineOffsetEmpty(double p_distance)168 OffsetCurveBuilder::isLineOffsetEmpty(double p_distance)
169 {
170     // a zero width buffer of a line or point is empty
171     if (p_distance == 0.0) return true;
172     // a negative width buffer of a line or point is empty,
173     // except for single-sided buffers, where the sign indicates the side
174     if (p_distance < 0.0 && ! bufParams.isSingleSided()) return true;
175     return false;
176 }
177 
178 
179 /*public*/
180 void
getRingCurve(const CoordinateSequence * inputPts,int side,double nDistance,vector<CoordinateSequence * > & lineList)181 OffsetCurveBuilder::getRingCurve(const CoordinateSequence* inputPts,
182                                  int side, double nDistance,
183                                  vector<CoordinateSequence*>& lineList)
184 {
185     distance = nDistance;
186 
187     // optimize creating ring for zero distance
188     if(distance == 0.0) {
189         lineList.push_back(inputPts->clone().release());
190         return;
191     }
192 
193     if(inputPts->getSize() <= 2) {
194         getLineCurve(inputPts, distance, lineList);
195         return;
196     }
197 
198     std::unique_ptr<OffsetSegmentGenerator> segGen = getSegGen(std::abs(distance));
199     computeRingBufferCurve(*inputPts, side, *segGen);
200     segGen->getCoordinates(lineList);
201 }
202 
203 /* private */
204 double
simplifyTolerance(double bufDistance)205 OffsetCurveBuilder::simplifyTolerance(double bufDistance)
206 {
207     return bufDistance / SIMPLIFY_FACTOR;
208 }
209 
210 /*private*/
211 void
computeLineBufferCurve(const CoordinateSequence & inputPts,OffsetSegmentGenerator & segGen)212 OffsetCurveBuilder::computeLineBufferCurve(const CoordinateSequence& inputPts,
213         OffsetSegmentGenerator& segGen)
214 {
215     double distTol = simplifyTolerance(distance);
216 
217     //--------- compute points for left side of line
218     // Simplify the appropriate side of the line before generating
219     std::unique_ptr<CoordinateSequence> simp1_ =
220         BufferInputLineSimplifier::simplify(inputPts, distTol);
221     const CoordinateSequence& simp1 = *simp1_;
222 
223 
224     auto n1 = simp1.size() - 1;
225     segGen.initSideSegments(simp1[0], simp1[1], Position::LEFT);
226     for(size_t i = 2; i <= n1; ++i) {
227         segGen.addNextSegment(simp1[i], true);
228     }
229     segGen.addLastSegment();
230     // add line cap for end of line
231     segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]);
232 
233     //---------- compute points for right side of line
234     // Simplify the appropriate side of the line before generating
235     std::unique_ptr<CoordinateSequence> simp2_ =
236         BufferInputLineSimplifier::simplify(inputPts, -distTol);
237     const CoordinateSequence& simp2 = *simp2_;
238 
239     auto n2 = simp2.size() - 1;
240     segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position::LEFT);
241     for(size_t i = n2 - 1; i > 0; --i) {
242         segGen.addNextSegment(simp2[i - 1], true);
243     }
244     segGen.addLastSegment();
245     // add line cap for start of line
246     segGen.addLineEndCap(simp2[1], simp2[0]);
247 
248     segGen.closeRing();
249 }
250 
251 /*private*/
252 void
computeRingBufferCurve(const CoordinateSequence & inputPts,int side,OffsetSegmentGenerator & segGen)253 OffsetCurveBuilder::computeRingBufferCurve(const CoordinateSequence& inputPts,
254         int side, OffsetSegmentGenerator& segGen)
255 {
256     // simplify input line to improve performance
257     double distTol = simplifyTolerance(distance);
258     // ensure that correct side is simplified
259     if(side == Position::RIGHT) {
260         distTol = -distTol;
261     }
262     std::unique_ptr<CoordinateSequence> simp_ =
263         BufferInputLineSimplifier::simplify(inputPts, distTol);
264     const CoordinateSequence& simp = *simp_;
265 
266     auto n = simp.size() - 1;
267     segGen.initSideSegments(simp[n - 1], simp[0], side);
268     for(size_t i = 1; i <= n; i++) {
269         bool addStartPoint = i != 1;
270         segGen.addNextSegment(simp[i], addStartPoint);
271     }
272     segGen.closeRing();
273 }
274 
275 /*private*/
276 void
computeSingleSidedBufferCurve(const CoordinateSequence & inputPts,bool isRightSide,OffsetSegmentGenerator & segGen)277 OffsetCurveBuilder::computeSingleSidedBufferCurve(
278     const CoordinateSequence& inputPts, bool isRightSide,
279     OffsetSegmentGenerator& segGen)
280 {
281     double distTol = simplifyTolerance(distance);
282 
283     if(isRightSide) {
284 
285         // add original line
286         segGen.addSegments(inputPts, true);
287 
288         //---------- compute points for right side of line
289         // Simplify the appropriate side of the line before generating
290         std::unique_ptr<CoordinateSequence> simp2_ =
291             BufferInputLineSimplifier::simplify(inputPts, -distTol);
292         const CoordinateSequence& simp2 = *simp2_;
293 
294         auto n2 = simp2.size() - 1;
295         segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position::LEFT);
296         segGen.addFirstSegment();
297         for(size_t  i = n2 - 1; i > 0; --i) {
298             segGen.addNextSegment(simp2[i - 1], true);
299         }
300 
301     }
302     else {
303 
304         // add original line
305         segGen.addSegments(inputPts, false);
306 
307         //--------- compute points for left side of line
308         // Simplify the appropriate side of the line before generating
309         std::unique_ptr<CoordinateSequence> simp1_ =
310             BufferInputLineSimplifier::simplify(inputPts, distTol);
311         const CoordinateSequence& simp1 = *simp1_;
312 
313         auto n1 = simp1.size() - 1;
314         segGen.initSideSegments(simp1[0], simp1[1], Position::LEFT);
315         segGen.addFirstSegment();
316         for(size_t i = 2; i <= n1; ++i) {
317             segGen.addNextSegment(simp1[i], true);
318         }
319 
320     }
321     segGen.addLastSegment();
322     segGen.closeRing();
323 }
324 
325 /*private*/
326 std::unique_ptr<OffsetSegmentGenerator>
getSegGen(double dist)327 OffsetCurveBuilder::getSegGen(double dist)
328 {
329     std::unique_ptr<OffsetSegmentGenerator> osg(
330         new OffsetSegmentGenerator(precisionModel, bufParams, dist)
331     );
332     return osg;
333 }
334 
335 } // namespace geos.operation.buffer
336 } // namespace geos.operation
337 } // namespace geos
338 
339