QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsclipper.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsclipper.cpp - a class that clips line
3 segments and polygons
4 -------------------
5 begin : March 2004
6 copyright : (C) 2005 by Gavin Macaulay
7 email :
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19#include "qgsclipper.h"
20
21#include "qgsgeometry.h"
22#include "qgslogger.h"
23
24// Where has all the code gone?
25
26// It's been inlined, so its in the qgsclipper.h file.
27
28// But the static members must be initialized outside the class! (or GCC 4 dies)
29
30// Qt also does clipping when the coordinates go over +/- 32767
31// moreover from Qt 4.6, Qt clips also when the width/height of a painter path
32// is more than 32767. Since we want to avoid clipping by Qt (because it is slow)
33// we set coordinate limit to less than 32767 / 2
34const double QgsClipper::MAX_X = 16000;
35const double QgsClipper::MIN_X = -16000;
36const double QgsClipper::MAX_Y = 16000;
37const double QgsClipper::MIN_Y = -16000;
38
39const double QgsClipper::SMALL_NUM = 1e-12;
40
41void QgsClipper::clipped3dLine( const QVector< double > &xIn, const QVector< double > &yIn, const QVector<double> &zIn, QVector<double> &x, QVector<double> &y, QVector<double> &z, const QgsBox3D &clipExtent )
42{
43 double p0x, p0y, p0z, p1x = 0.0, p1y = 0.0, p1z = 0.0; //original coordinates
44 double p1x_c, p1y_c, p1z_c; //clipped end coordinates
45 double lastClipX = 0.0, lastClipY = 0.0, lastClipZ = 0.0; // last successfully clipped coordinates
46
47 const int nPoints = xIn.size();
48
49 x.reserve( nPoints );
50 y.reserve( nPoints );
51 z.reserve( nPoints );
52
53 const double *sourceX = xIn.data();
54 const double *sourceY = yIn.data();
55 const double *sourceZ = zIn.data();
56
57 for ( int i = 0; i < nPoints; ++i )
58 {
59 if ( i == 0 )
60 {
61 p1x = *sourceX++;
62 p1y = *sourceY++;
63 p1z = *sourceZ++;
64 }
65 else
66 {
67 p0x = p1x;
68 p0y = p1y;
69 p0z = p1z;
70
71 p1x = *sourceX++;
72 p1y = *sourceY++;
73 p1z = *sourceZ++;
74
75 p1x_c = p1x;
76 p1y_c = p1y;
77 p1z_c = p1z;
78
79 // TODO: should be in 3D
80 if ( clipLineSegment( clipExtent, p0x, p0y, p0z, p1x_c, p1y_c, p1z_c ) )
81 {
82 bool newLine = !x.isEmpty() && ( !qgsDoubleNear( p0x, lastClipX )
83 || !qgsDoubleNear( p0y, lastClipY )
84 || !qgsDoubleNear( p0z, lastClipZ ) );
85 if ( newLine )
86 {
87 //add edge points to connect old and new line
88 // TODO: should be (really) in 3D
89 connectSeparatedLines( lastClipX, lastClipY, lastClipZ, p0x, p0y, p0z, clipExtent, x, y, z );
90 }
91 if ( x.isEmpty() || newLine )
92 {
93 //add first point
94 x << p0x;
95 y << p0y;
96 z << p0z;
97 }
98
99 //add second point
100 lastClipX = p1x_c;
101 lastClipY = p1y_c;
102 lastClipZ = p1z_c;
103 x << p1x_c;
104 y << p1y_c;
105 z << p1z_c;
106 }
107 }
108 }
109}
110
111QPolygonF QgsClipper::clippedLine( const QgsCurve &curve, const QgsRectangle &clipExtent )
112{
113 return clippedLine( curve.asQPolygonF(), clipExtent );
114}
115
116QPolygonF QgsClipper::clippedLine( const QPolygonF &curve, const QgsRectangle &clipExtent )
117{
118 const int nPoints = curve.size();
119
120 double p0x, p0y, p1x = 0.0, p1y = 0.0; //original coordinates
121 double p1x_c, p1y_c; //clipped end coordinates
122 double lastClipX = 0.0, lastClipY = 0.0; //last successfully clipped coords
123
124 QPolygonF line;
125 line.reserve( nPoints + 1 );
126
127 const QPointF *curveData = curve.data();
128
129 for ( int i = 0; i < nPoints; ++i )
130 {
131 if ( i == 0 )
132 {
133 p1x = curveData->x();
134 p1y = curveData->y();
135 }
136 else
137 {
138 p0x = p1x;
139 p0y = p1y;
140
141 p1x = curveData->x();
142 p1y = curveData->y();
143
144 p1x_c = p1x;
145 p1y_c = p1y;
146 if ( clipLineSegment( clipExtent.xMinimum(), clipExtent.xMaximum(), clipExtent.yMinimum(), clipExtent.yMaximum(),
147 p0x, p0y, p1x_c, p1y_c ) )
148 {
149 const bool newLine = !line.isEmpty() && ( !qgsDoubleNear( p0x, lastClipX ) || !qgsDoubleNear( p0y, lastClipY ) );
150 if ( newLine )
151 {
152 //add edge points to connect old and new line
153 connectSeparatedLines( lastClipX, lastClipY, p0x, p0y, clipExtent, line );
154 }
155 if ( line.empty() || newLine )
156 {
157 //add first point
158 line << QPointF( p0x, p0y );
159 }
160
161 //add second point
162 lastClipX = p1x_c;
163 lastClipY = p1y_c;
164 line << QPointF( p1x_c, p1y_c );
165 }
166 }
167 curveData++;
168 }
169 return line;
170}
171
172void QgsClipper::connectSeparatedLines( double x0, double y0, double x1, double y1,
173 const QgsRectangle &clipRect, QPolygonF &pts )
174{
175 //test the different edge combinations...
176 if ( qgsDoubleNear( x0, clipRect.xMinimum() ) )
177 {
178 if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
179 {
180 return;
181 }
182 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
183 {
184 pts << QPointF( clipRect.xMinimum(), clipRect.yMaximum() );
185 return;
186 }
187 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
188 {
189 pts << QPointF( clipRect.xMinimum(), clipRect.yMaximum() );
190 pts << QPointF( clipRect.xMaximum(), clipRect.yMaximum() );
191 return;
192 }
193 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
194 {
195 pts << QPointF( clipRect.xMinimum(), clipRect.yMinimum() );
196 return;
197 }
198 }
199 else if ( qgsDoubleNear( y0, clipRect.yMaximum() ) )
200 {
201 if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
202 {
203 return;
204 }
205 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
206 {
207 pts << QPointF( clipRect.xMaximum(), clipRect.yMaximum() );
208 return;
209 }
210 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
211 {
212 pts << QPointF( clipRect.xMaximum(), clipRect.yMaximum() );
213 pts << QPointF( clipRect.xMaximum(), clipRect.yMinimum() );
214 return;
215 }
216 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
217 {
218 pts << QPointF( clipRect.xMinimum(), clipRect.yMaximum() );
219 return;
220 }
221 }
222 else if ( qgsDoubleNear( x0, clipRect.xMaximum() ) )
223 {
224 if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
225 {
226 return;
227 }
228 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
229 {
230 pts << QPointF( clipRect.xMaximum(), clipRect.yMinimum() );
231 return;
232 }
233 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
234 {
235 pts << QPointF( clipRect.xMaximum(), clipRect.yMinimum() );
236 pts << QPointF( clipRect.xMinimum(), clipRect.yMinimum() );
237 return;
238 }
239 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
240 {
241 pts << QPointF( clipRect.xMaximum(), clipRect.yMaximum() );
242 return;
243 }
244 }
245 else if ( qgsDoubleNear( y0, clipRect.yMinimum() ) )
246 {
247 if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
248 {
249 return;
250 }
251 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
252 {
253 pts << QPointF( clipRect.xMinimum(), clipRect.yMinimum() );
254 return;
255 }
256 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
257 {
258 pts << QPointF( clipRect.xMinimum(), clipRect.yMinimum() );
259 pts << QPointF( clipRect.xMinimum(), clipRect.yMaximum() );
260 return;
261 }
262 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
263 {
264 pts << QPointF( clipRect.xMaximum(), clipRect.yMinimum() );
265 return;
266 }
267 }
268}
269
270void QgsClipper::connectSeparatedLines( double x0, double y0, double z0, double x1, double y1, double z1,
271 const QgsBox3D &clipRect, QVector< double > &ptsX, QVector< double > &ptsY, QVector<double> &ptsZ )
272{
273 // TODO: really relevant and sufficient?
274 double meanZ = ( z0 + z1 ) / 2.0;
275
276 //test the different edge combinations...
277 if ( qgsDoubleNear( x0, clipRect.xMinimum() ) )
278 {
279 if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
280 {
281 return;
282 }
283 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
284 {
285 ptsX << clipRect.xMinimum();
286 ptsY << clipRect.yMaximum();
287 ptsZ << meanZ;
288 return;
289 }
290 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
291 {
292 ptsX << clipRect.xMinimum();
293 ptsY << clipRect.yMaximum();
294 ptsZ << meanZ;
295 ptsX << clipRect.xMaximum();
296 ptsY << clipRect.yMaximum();
297 ptsZ << meanZ;
298 return;
299 }
300 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
301 {
302 ptsX << clipRect.xMinimum();
303 ptsY << clipRect.yMinimum();
304 ptsZ << meanZ;
305 return;
306 }
307 }
308 else if ( qgsDoubleNear( y0, clipRect.yMaximum() ) )
309 {
310 if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
311 {
312 return;
313 }
314 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
315 {
316 ptsX << clipRect.xMaximum();
317 ptsY << clipRect.yMaximum();
318 ptsZ << meanZ;
319 return;
320 }
321 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
322 {
323 ptsX << clipRect.xMaximum();
324 ptsY << clipRect.yMaximum();
325 ptsZ << meanZ;
326 ptsX << clipRect.xMaximum();
327 ptsY << clipRect.yMinimum();
328 ptsZ << meanZ;
329 return;
330 }
331 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
332 {
333 ptsX << clipRect.xMinimum();
334 ptsY << clipRect.yMaximum();
335 ptsZ << meanZ;
336 return;
337 }
338 }
339 else if ( qgsDoubleNear( x0, clipRect.xMaximum() ) )
340 {
341 if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
342 {
343 return;
344 }
345 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
346 {
347 ptsX << clipRect.xMaximum();
348 ptsY << clipRect.yMinimum();
349 ptsZ << meanZ;
350 return;
351 }
352 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
353 {
354 ptsX << clipRect.xMaximum();
355 ptsY << clipRect.yMinimum();
356 ptsZ << meanZ;
357 ptsX << clipRect.xMinimum();
358 ptsY << clipRect.yMinimum();
359 ptsZ << meanZ;
360 return;
361 }
362 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
363 {
364 ptsX << clipRect.xMaximum();
365 ptsY << clipRect.yMaximum();
366 ptsZ << meanZ;
367 return;
368 }
369 }
370 else if ( qgsDoubleNear( y0, clipRect.yMinimum() ) )
371 {
372 if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
373 {
374 return;
375 }
376 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
377 {
378 ptsX << clipRect.xMinimum();
379 ptsY << clipRect.yMinimum();
380 ptsZ << meanZ;
381 return;
382 }
383 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
384 {
385 ptsX << clipRect.xMinimum();
386 ptsY << clipRect.yMinimum();
387 ptsZ << meanZ;
388 ptsX << clipRect.xMinimum();
389 ptsY << clipRect.yMaximum();
390 ptsZ << meanZ;
391 return;
392 }
393 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
394 {
395 ptsX << clipRect.xMaximum();
396 ptsY << clipRect.yMinimum();
397 ptsZ << meanZ;
398 return;
399 }
400 }
401}
402
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:42
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:230
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:195
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:202
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:223
static bool clipLineSegment(double left, double right, double bottom, double top, double &x0, double &y0, double &x1, double &y1)
Clips a line segment to a rectangle.
Definition qgsclipper.h:776
static const double MAX_X
Maximum X-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:72
static const double MIN_Y
Minimum Y-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:78
static const double MAX_Y
Maximum Y-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:76
static QPolygonF clippedLine(const QgsCurve &curve, const QgsRectangle &clipExtent)
Takes a linestring and clips it to clipExtent.
static const double MIN_X
Minimum X-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:74
static void clipped3dLine(const QVector< double > &xIn, const QVector< double > &yIn, const QVector< double > &zIn, QVector< double > &x, QVector< double > &y, QVector< double > &z, const QgsBox3D &clipExtent)
Takes a line with 3D coordinates and clips it to clipExtent.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
virtual QPolygonF asQPolygonF() const
Returns a QPolygonF representing the points.
Definition qgscurve.cpp:267
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607