QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgsclipper.h
Go to the documentation of this file.
1/***************************************************************************
2 qgsclipper.h - 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#ifndef QGSCLIPPER_H
20#define QGSCLIPPER_H
21
22#include "qgis_core.h"
23#include "qgis_sip.h"
24#include "qgsabstractgeometry.h"
25#include "qgsbox3d.h"
26#include "qgspointxy.h"
27#include "qgsrectangle.h"
28
29#include <QPolygonF>
30#include <QVector>
31
32class QgsCurve;
33class QgsLineString;
34
35SIP_FEATURE( ARM ) // Some parts are not available in sip bindings on ARM because of qreal double vs. float issues
36
37
38
50class CORE_EXPORT QgsClipper
51{
52 public:
53 // These are the limits for X11 screen coordinates. The actual
54 // values are +/-32767, but we allow a little bit of space for
55 // rounding errors.
56
57 // You may wonder why the clipping is done to these coordinates
58 // rather than the boundaries of the qgis canvas. Reasons include:
59 // - making the boundaries static const allows the compiler to
60 // optimise the code that uses these values more than if they changed
61 // for every call to the trim code.
62 // - clipping takes quite a bit of CPU effort, and the less that this is
63 // done the better. More stuff would have to be clipped if the
64 // boundaries were the qgis canvas (but this may be offset by
65 // having less to draw).
66 //
67 // The limit is set to 30,000 instead of 32768 because that things
68 // still go wrong.
69
71 static const double MAX_X;
73 static const double MIN_X;
75 static const double MAX_Y;
77 static const double MIN_Y;
78
79
90
99 static void trimFeature( QVector<double> &x, QVector<double> &y, bool shapeOpen );
100
107 static void trimPolygon( QPolygonF &pts, const QgsRectangle &clipRect );
108
115 static void trimPolygon( QVector< double > &x, QVector< double > &y, QVector< double> &z, const QgsBox3D &clipRect ) SIP_SKIP;
116
122 static void clipped3dLine(
123 const QVector< double > &xIn, const QVector< double > &yIn, const QVector<double> &zIn, QVector< double > &x, QVector< double > &y, QVector< double > &z, const QgsBox3D &clipExtent
124 ) SIP_SKIP;
125
132 static QPolygonF clippedLine( const QgsCurve &curve, const QgsRectangle &clipExtent );
133
141 static QPolygonF clippedLine( const QPolygonF &curve, const QgsRectangle &clipExtent );
142
160 static bool clipLineSegment( double left, double right, double bottom, double top, double &x0 SIP_INOUT, double &y0 SIP_INOUT, double &x1 SIP_INOUT, double &y1 SIP_INOUT );
161
162 private:
163 // Used when testing for equivalence to 0.0
164 static const double SMALL_NUM;
165
166 // Trims the given feature to the given boundary. Returns the
167 // trimmed feature in the outX and outY vectors.
168 static inline void trimFeatureToBoundary( const QVector<double> &inX, const QVector<double> &inY, QVector<double> &outX, QVector<double> &outY, Boundary b, bool shapeOpen );
169
170 static inline void trimPolygonToBoundary( const QPolygonF &inPts, QPolygonF &outPts, const QgsRectangle &rect, Boundary b, double boundaryValue );
171
172 static inline void trimPolygonToBoundary(
173 const QVector<double> &inX, const QVector<double> &inY, const QVector< double > &inZ, QVector<double> &outX, QVector<double> &outY, QVector<double> &outZ, const QgsBox3D &rect, Boundary boundary, double boundaryValue
174 );
175
176 // Determines if a point is inside or outside the given boundary
177 static inline bool inside( double x, double y, Boundary b );
178
179 static inline bool inside( QPointF pt, Boundary b, double val );
180
181 static inline bool inside( double x, double y, double z, Boundary boundary, double boundaryValue );
182
183 // Calculates the intersection point between a line defined by a
184 // (x1, y1), and (x2, y2) and the given boundary
185 static inline QgsPointXY intersect( double x1, double y1, double x2, double y2, Boundary b );
186
187 static inline QPointF intersectRect( QPointF pt1, QPointF pt2, Boundary b, const QgsRectangle &rect );
188
189 static inline void intersectRect( double x1, double y1, double z1, double x2, double y2, double z2, double &xOut, double &yOut, double &zOut, Boundary boundary, const QgsBox3D &rect );
190
191 static bool clipLineSegment( const QgsBox3D &extent, double &x0, double &y0, double &z0, double &x1, double &y1, double &z1 );
192
202 static void connectSeparatedLines( double x0, double y0, double x1, double y1, const QgsRectangle &clipRect, QPolygonF &pts );
203
217 static void connectSeparatedLines( double x0, double y0, double z0, double x1, double y1, double z1, const QgsBox3D &clipRect, QVector< double > &ptsX, QVector< double > &ptsY, QVector<double> &ptsZ );
218
219 //low level clip methods for fast clip algorithm
220 static void clipStartTop( double &x0, double &y0, double x1, double y1, double yMax );
221 static void clipStartBottom( double &x0, double &y0, double x1, double y1, double yMin );
222 static void clipStartRight( double &x0, double &y0, double x1, double y1, double xMax );
223 static void clipStartLeft( double &x0, double &y0, double &x1, double &y1, double xMin );
224 static void clipEndTop( double x0, double y0, double &x1, double &y1, double yMax );
225 static void clipEndBottom( double x0, double y0, double &x1, double &y1, double yMin );
226 static void clipEndRight( double x0, double y0, double &x1, double &y1, double xMax );
227 static void clipEndLeft( double x0, double y0, double &x1, double &y1, double xMin );
228};
229
230#include "qgslinestring.h"
231#include "qgspoint.h"
232
233#ifndef SIP_RUN
234// The inline functions
235
236// Trim the feature using Sutherland and Hodgman's
237// polygon-clipping algorithm. See J. D. Foley, A. van Dam,
238// S. K. Feiner, and J. F. Hughes, Computer Graphics, Principles and
239// Practice. Addison-Wesley Systems Programming Series,
240// Addison-Wesley, 2nd ed., 1991.
241
242// I understand that this is not the most efficient algorithm, but is
243// one (the only?) that is guaranteed to always give the correct
244// result.
245
246inline void QgsClipper::trimFeature( QVector<double> &x, QVector<double> &y, bool shapeOpen )
247{
248 QVector<double> tmpX;
249 QVector<double> tmpY;
250 trimFeatureToBoundary( x, y, tmpX, tmpY, XMax, shapeOpen );
251
252 x.clear();
253 y.clear();
254 trimFeatureToBoundary( tmpX, tmpY, x, y, YMax, shapeOpen );
255
256 tmpX.clear();
257 tmpY.clear();
258 trimFeatureToBoundary( x, y, tmpX, tmpY, XMin, shapeOpen );
259
260 x.clear();
261 y.clear();
262 trimFeatureToBoundary( tmpX, tmpY, x, y, YMin, shapeOpen );
263}
264
265inline void QgsClipper::trimPolygon( QPolygonF &pts, const QgsRectangle &clipRect )
266{
267 QPolygonF tmpPts;
268 tmpPts.reserve( pts.size() );
269
270 trimPolygonToBoundary( pts, tmpPts, clipRect, XMax, clipRect.xMaximum() );
271 pts.resize( 0 );
272 trimPolygonToBoundary( tmpPts, pts, clipRect, YMax, clipRect.yMaximum() );
273 tmpPts.resize( 0 );
274 trimPolygonToBoundary( pts, tmpPts, clipRect, XMin, clipRect.xMinimum() );
275 pts.resize( 0 );
276 trimPolygonToBoundary( tmpPts, pts, clipRect, YMin, clipRect.yMinimum() );
277}
278
279inline void QgsClipper::trimPolygon( QVector<double> &x, QVector<double> &y, QVector<double> &z, const QgsBox3D &clipRect )
280{
281 QVector< double > tempX;
282 QVector< double > tempY;
283 QVector< double > tempZ;
284 const int size = x.size();
285 tempX.reserve( size );
286 tempY.reserve( size );
287 tempZ.reserve( size );
288
289 trimPolygonToBoundary( x, y, z, tempX, tempY, tempZ, clipRect, XMax, clipRect.xMaximum() );
290 x.resize( 0 );
291 y.resize( 0 );
292 z.resize( 0 );
293 trimPolygonToBoundary( tempX, tempY, tempZ, x, y, z, clipRect, YMax, clipRect.yMaximum() );
294 tempX.resize( 0 );
295 tempY.resize( 0 );
296 tempZ.resize( 0 );
297 trimPolygonToBoundary( x, y, z, tempX, tempY, tempZ, clipRect, XMin, clipRect.xMinimum() );
298 x.resize( 0 );
299 y.resize( 0 );
300 z.resize( 0 );
301 trimPolygonToBoundary( tempX, tempY, tempZ, x, y, z, clipRect, YMin, clipRect.yMinimum() );
302
303 if ( !clipRect.is2d() )
304 {
305 tempX.resize( 0 );
306 tempY.resize( 0 );
307 tempZ.resize( 0 );
308 trimPolygonToBoundary( x, y, z, tempX, tempY, tempZ, clipRect, ZMax, clipRect.zMaximum() );
309 x.resize( 0 );
310 y.resize( 0 );
311 z.resize( 0 );
312 trimPolygonToBoundary( tempX, tempY, tempZ, x, y, z, clipRect, ZMin, clipRect.zMinimum() );
313 }
314}
315
316// An auxiliary function that is part of the polygon trimming
317// code. Will trim the given polygon to the given boundary and return
318// the trimmed polygon in the out pointer. Uses Sutherland and
319// Hodgman's polygon-clipping algorithm.
320
321inline void QgsClipper::trimFeatureToBoundary( const QVector<double> &inX, const QVector<double> &inY, QVector<double> &outX, QVector<double> &outY, Boundary b, bool shapeOpen )
322{
323 // The shapeOpen parameter selects whether this function treats the
324 // shape as open or closed. False is appropriate for polygons and
325 // true for polylines.
326
327 int i1 = inX.size() - 1; // start with last point
328
329 // and compare to the first point initially.
330 for ( int i2 = 0; i2 < inX.size(); ++i2 )
331 {
332 // look at each edge of the polygon in turn
333
334 //ignore segments with nan or inf coordinates
335 if ( std::isnan( inX[i2] ) || std::isnan( inY[i2] ) || std::isinf( inX[i2] ) || std::isinf( inY[i2] ) || std::isnan( inX[i1] ) || std::isnan( inY[i1] ) || std::isinf( inX[i1] ) || std::isinf( inY[i1] ) )
336 {
337 i1 = i2;
338 continue;
339 }
340
341
342 if ( inside( inX[i2], inY[i2], b ) ) // end point of edge is inside boundary
343 {
344 if ( inside( inX[i1], inY[i1], b ) )
345 {
346 outX.push_back( inX[i2] );
347 outY.push_back( inY[i2] );
348 }
349 else
350 {
351 // edge crosses into the boundary, so trim back to the boundary, and
352 // store both ends of the new edge
353 if ( !( i2 == 0 && shapeOpen ) )
354 {
355 const QgsPointXY p = intersect( inX[i1], inY[i1], inX[i2], inY[i2], b );
356 outX.push_back( p.x() );
357 outY.push_back( p.y() );
358 }
359
360 outX.push_back( inX[i2] );
361 outY.push_back( inY[i2] );
362 }
363 }
364 else // end point of edge is outside boundary
365 {
366 // start point is in boundary, so need to trim back
367 if ( inside( inX[i1], inY[i1], b ) )
368 {
369 if ( !( i2 == 0 && shapeOpen ) )
370 {
371 const QgsPointXY p = intersect( inX[i1], inY[i1], inX[i2], inY[i2], b );
372 outX.push_back( p.x() );
373 outY.push_back( p.y() );
374 }
375 }
376 }
377 i1 = i2;
378 }
379}
380
381inline void QgsClipper::trimPolygonToBoundary( const QPolygonF &inPts, QPolygonF &outPts, const QgsRectangle &rect, Boundary b, double boundaryValue )
382{
383 int i1 = inPts.size() - 1; // start with last point
384
385 // and compare to the first point initially.
386 for ( int i2 = 0; i2 < inPts.size(); ++i2 )
387 {
388 // look at each edge of the polygon in turn
389 if ( inside( inPts[i2], b, boundaryValue ) ) // end point of edge is inside boundary
390 {
391 if ( inside( inPts[i1], b, boundaryValue ) )
392 {
393 outPts.append( inPts[i2] );
394 }
395 else
396 {
397 // edge crosses into the boundary, so trim back to the boundary, and
398 // store both ends of the new edge
399 outPts.append( intersectRect( inPts[i1], inPts[i2], b, rect ) );
400 outPts.append( inPts[i2] );
401 }
402 }
403 else // end point of edge is outside boundary
404 {
405 // start point is in boundary, so need to trim back
406 if ( inside( inPts[i1], b, boundaryValue ) )
407 {
408 outPts.append( intersectRect( inPts[i1], inPts[i2], b, rect ) );
409 }
410 }
411 i1 = i2;
412 }
413}
414
415inline void QgsClipper::trimPolygonToBoundary(
416 const QVector<double> &inX, const QVector<double> &inY, const QVector< double > &inZ, QVector<double> &outX, QVector<double> &outY, QVector<double> &outZ, const QgsBox3D &rect, Boundary boundary, double boundaryValue
417)
418{
419 const double len = inX.length();
420 if ( len == 0 )
421 {
422 outX.clear();
423 outY.clear();
424 outZ.clear();
425 return;
426 }
427
428 double inI1X = inX.at( len - 1 ); // start with last point
429 double inI1Y = inY.at( len - 1 );
430 double inI1Z = inZ.at( len - 1 );
431
432 double xTemp;
433 double yTemp;
434 double zTemp;
435
436 // and compare to the first point initially.
437 for ( int i2 = 0; i2 < len; ++i2 )
438 {
439 double inI2X = inX.at( i2 );
440 double inI2Y = inY.at( i2 );
441 double inI2Z = inZ.at( i2 );
442
443 // look at each edge of the polygon in turn
444 if ( inside( inI2X, inI2Y, inI2Z, boundary, boundaryValue ) ) // end point of edge is inside boundary
445 {
446 if ( inside( inI1X, inI1Y, inI1Z, boundary, boundaryValue ) )
447 {
448 outX << inI2X;
449 outY << inI2Y;
450 outZ << inI2Z;
451 }
452 else
453 {
454 // edge crosses into the boundary, so trim back to the boundary, and
455 // store both ends of the new edge
456 intersectRect( inI1X, inI1Y, inI1Z, inI2X, inI2Y, inI2Z, xTemp, yTemp, zTemp, boundary, rect );
457 outX << xTemp;
458 outY << yTemp;
459 outZ << zTemp;
460 outX << inI2X;
461 outY << inI2Y;
462 outZ << inI2Z;
463 }
464 }
465 else // end point of edge is outside boundary
466 {
467 // start point is in boundary, so need to trim back
468 if ( inside( inI1X, inI1Y, inI1Z, boundary, boundaryValue ) )
469 {
470 intersectRect( inI1X, inI1Y, inI1Z, inI2X, inI2Y, inI2Z, xTemp, yTemp, zTemp, boundary, rect );
471 outX << xTemp;
472 outY << yTemp;
473 outZ << zTemp;
474 }
475 }
476 inI1X = inI2X;
477 inI1Y = inI2Y;
478 inI1Z = inI2Z;
479 }
480}
481
482// An auxiliary function to trimPolygonToBoundarY() that returns
483// whether a point is inside or outside the given boundary.
484
485inline bool QgsClipper::inside( const double x, const double y, Boundary b )
486{
487 switch ( b )
488 {
489 case XMax: // x < MAX_X is inside
490 if ( x < MAX_X )
491 return true;
492 break;
493 case XMin: // x > MIN_X is inside
494 if ( x > MIN_X )
495 return true;
496 break;
497 case YMax: // y < MAX_Y is inside
498 if ( y < MAX_Y )
499 return true;
500 break;
501 case YMin: // y > MIN_Y is inside
502 if ( y > MIN_Y )
503 return true;
504 break;
505 case ZMax: // z < MAX_Z is inside
506 case ZMin: // z > MIN_Z is inside
507 return false;
508 }
509 return false;
510}
511
512inline bool QgsClipper::inside( QPointF pt, Boundary b, double val )
513{
514 switch ( b )
515 {
516 case XMax: // x < MAX_X is inside
517 return ( pt.x() < val );
518 case XMin: // x > MIN_X is inside
519 return ( pt.x() > val );
520 case YMax: // y < MAX_Y is inside
521 return ( pt.y() < val );
522 case YMin: // y > MIN_Y is inside
523 return ( pt.y() > val );
524 case ZMax: // z < MAX_Z is inside
525 case ZMin: // z > MIN_Z is inside
526 return false;
527 }
528 return false;
529}
530
531inline bool QgsClipper::inside( double x, double y, double z, Boundary boundary, double boundaryValue )
532{
533 switch ( boundary )
534 {
535 case XMax: // x < MAX_X is inside
536 return ( x < boundaryValue );
537 case XMin: // x > MIN_X is inside
538 return ( x > boundaryValue );
539 case YMax: // y < MAX_Y is inside
540 return ( y < boundaryValue );
541 case YMin: // y > MIN_Y is inside
542 return ( y > boundaryValue );
543 case ZMax: // z < MAX_Z is inside
544 return z < boundaryValue || std::isnan( z );
545 case ZMin: // z > MIN_Z is inside
546 return z > boundaryValue || std::isnan( z );
547 }
548 return false;
549}
550
551
552// An auxiliary function to trimPolygonToBoundarY() that calculates and
553// returns the intersection of the line defined by the given points
554// and the given boundary.
555
556inline QgsPointXY QgsClipper::intersect( const double x1, const double y1, const double x2, const double y2, Boundary b )
557{
558 // This function assumes that the two given points (x1, y1), and
559 // (x2, y2) cross the given boundary. Making this assumption allows
560 // some optimisations.
561
562 double r_n = SMALL_NUM, r_d = SMALL_NUM;
563
564 switch ( b )
565 {
566 case XMax: // x = MAX_X boundary
567 r_n = -( x1 - MAX_X ) * ( MAX_Y - MIN_Y );
568 r_d = ( x2 - x1 ) * ( MAX_Y - MIN_Y );
569 break;
570 case XMin: // x = MIN_X boundary
571 r_n = -( x1 - MIN_X ) * ( MAX_Y - MIN_Y );
572 r_d = ( x2 - x1 ) * ( MAX_Y - MIN_Y );
573 break;
574 case YMax: // y = MAX_Y boundary
575 r_n = ( y1 - MAX_Y ) * ( MAX_X - MIN_X );
576 r_d = -( y2 - y1 ) * ( MAX_X - MIN_X );
577 break;
578 case YMin: // y = MIN_Y boundary
579 r_n = ( y1 - MIN_Y ) * ( MAX_X - MIN_X );
580 r_d = -( y2 - y1 ) * ( MAX_X - MIN_X );
581 break;
582 case ZMax: // z < MAX_Z is inside
583 case ZMin: // z > MIN_Z is inside
584 break;
585 }
586
587 QgsPointXY p;
588
589 if ( std::fabs( r_d ) > SMALL_NUM && std::fabs( r_n ) > SMALL_NUM )
590 {
591 // they cross
592 const double r = r_n / r_d;
593 p.set( x1 + r * ( x2 - x1 ), y1 + r * ( y2 - y1 ) );
594 }
595 else
596 {
597 // Should never get here, but if we do for some reason, cause a
598 // clunk because something else is wrong if we do.
599 Q_ASSERT( std::fabs( r_d ) > SMALL_NUM && std::fabs( r_n ) > SMALL_NUM );
600 }
601
602 return p;
603}
604
605inline QPointF QgsClipper::intersectRect( QPointF pt1, QPointF pt2, Boundary b, const QgsRectangle &rect )
606{
607 // This function assumes that the two given points (x1, y1), and
608 // (x2, y2) cross the given boundary. Making this assumption allows
609 // some optimisations.
610
611 double r_n = SMALL_NUM, r_d = SMALL_NUM;
612 const double x1 = pt1.x(), x2 = pt2.x();
613 const double y1 = pt1.y(), y2 = pt2.y();
614
615 switch ( b )
616 {
617 case XMax: // x = MAX_X boundary
618 r_n = -( x1 - rect.xMaximum() ) * ( rect.yMaximum() - rect.yMinimum() );
619 r_d = ( x2 - x1 ) * ( rect.yMaximum() - rect.yMinimum() );
620 break;
621 case XMin: // x = MIN_X boundary
622 r_n = -( x1 - rect.xMinimum() ) * ( rect.yMaximum() - rect.yMinimum() );
623 r_d = ( x2 - x1 ) * ( rect.yMaximum() - rect.yMinimum() );
624 break;
625 case YMax: // y = MAX_Y boundary
626 r_n = ( y1 - rect.yMaximum() ) * ( rect.xMaximum() - rect.xMinimum() );
627 r_d = -( y2 - y1 ) * ( rect.xMaximum() - rect.xMinimum() );
628 break;
629 case YMin: // y = MIN_Y boundary
630 r_n = ( y1 - rect.yMinimum() ) * ( rect.xMaximum() - rect.xMinimum() );
631 r_d = -( y2 - y1 ) * ( rect.xMaximum() - rect.xMinimum() );
632 break;
633 case ZMax: // z < MAX_Z is inside
634 case ZMin: // z > MIN_Z is inside
635 break;
636 }
637
638 double r = 0;
639 if ( !qgsDoubleNear( r_d, 0.0 ) )
640 {
641 r = r_n / r_d;
642 }
643 return QPointF( x1 + r * ( x2 - x1 ), y1 + r * ( y2 - y1 ) );
644}
645
646inline void QgsClipper::intersectRect(
647 const double x1, const double y1, const double z1, const double x2, const double y2, const double z2, double &xOut, double &yOut, double &zOut, Boundary boundary, const QgsBox3D &rect
648)
649{
650 // This function assumes that the two given points (x1, y1, z1), and
651 // (x2, y2, z2) cross the given boundary. Making this assumption allows
652 // some optimisations.
653
654 double r_n = SMALL_NUM, r_d = SMALL_NUM;
655
656 switch ( boundary )
657 {
658 case XMax: // x = MAX_X boundary
659 r_n = -( x1 - rect.xMaximum() ) * ( rect.yMaximum() - rect.yMinimum() );
660 r_d = ( x2 - x1 ) * ( rect.yMaximum() - rect.yMinimum() );
661 break;
662 case XMin: // x = MIN_X boundary
663 r_n = -( x1 - rect.xMinimum() ) * ( rect.yMaximum() - rect.yMinimum() );
664 r_d = ( x2 - x1 ) * ( rect.yMaximum() - rect.yMinimum() );
665 break;
666 case YMax: // y = MAX_Y boundary
667 r_n = ( y1 - rect.yMaximum() ) * ( rect.xMaximum() - rect.xMinimum() );
668 r_d = -( y2 - y1 ) * ( rect.xMaximum() - rect.xMinimum() );
669 break;
670 case YMin: // y = MIN_Y boundary
671 r_n = ( y1 - rect.yMinimum() ) * ( rect.xMaximum() - rect.xMinimum() );
672 r_d = -( y2 - y1 ) * ( rect.xMaximum() - rect.xMinimum() );
673 break;
674 case ZMax: // z = MAX_Z boundary
675 r_n = ( z1 - rect.zMaximum() );
676 r_d = -( z2 - z1 );
677 break;
678 case ZMin: // z = MIN_Z boundary
679 r_n = ( z1 - rect.zMinimum() );
680 r_d = -( z2 - z1 );
681 break;
682 }
683
684 double r = 0;
685 if ( !qgsDoubleNear( r_d, 0.0 ) )
686 {
687 r = r_n / r_d;
688 }
689 xOut = x1 + r * ( x2 - x1 );
690 yOut = y1 + r * ( y2 - y1 );
691 zOut = z1 + r * ( z2 - z1 );
692}
693
694inline void QgsClipper::clipStartTop( double &x0, double &y0, double x1, double y1, double yMax )
695{
696 x0 += ( x1 - x0 ) * ( yMax - y0 ) / ( y1 - y0 );
697 y0 = yMax;
698}
699
700inline void QgsClipper::clipStartBottom( double &x0, double &y0, double x1, double y1, double yMin )
701{
702 x0 += ( x1 - x0 ) * ( yMin - y0 ) / ( y1 - y0 );
703 y0 = yMin;
704}
705
706inline void QgsClipper::clipStartRight( double &x0, double &y0, double x1, double y1, double xMax )
707{
708 y0 += ( y1 - y0 ) * ( xMax - x0 ) / ( x1 - x0 );
709 x0 = xMax;
710}
711
712inline void QgsClipper::clipStartLeft( double &x0, double &y0, double &x1, double &y1, double xMin )
713{
714 y0 += ( y1 - y0 ) * ( xMin - x0 ) / ( x1 - x0 );
715 x0 = xMin;
716}
717
718inline void QgsClipper::clipEndTop( double x0, double y0, double &x1, double &y1, double yMax )
719{
720 x1 += ( x1 - x0 ) * ( yMax - y1 ) / ( y1 - y0 );
721 y1 = yMax;
722}
723
724inline void QgsClipper::clipEndBottom( double x0, double y0, double &x1, double &y1, double yMin )
725{
726 x1 += ( x1 - x0 ) * ( yMin - y1 ) / ( y1 - y0 );
727 y1 = yMin;
728}
729
730inline void QgsClipper::clipEndRight( double x0, double y0, double &x1, double &y1, double xMax )
731{
732 y1 += ( y1 - y0 ) * ( xMax - x1 ) / ( x1 - x0 );
733 x1 = xMax;
734}
735
736inline void QgsClipper::clipEndLeft( double x0, double y0, double &x1, double &y1, double xMin )
737{
738 y1 += ( y1 - y0 ) * ( xMin - x1 ) / ( x1 - x0 );
739 x1 = xMin;
740}
741
742inline bool QgsClipper::clipLineSegment( const QgsBox3D &clipExtent, double &x0, double &y0, double &, double &x1, double &y1, double & )
743{
744 // TODO: while waiting for a valid 3d solution, using the 2d one:
745 return clipLineSegment( clipExtent.xMinimum(), clipExtent.xMaximum(), clipExtent.yMinimum(), clipExtent.yMaximum(), x0, y0, x1, y1 );
746}
747
748//'Fast clipping' algorithm (Sobkow et al. 1987, Computers & Graphics Vol.11, 4, p.459-467)
749inline bool QgsClipper::clipLineSegment( double xLeft, double xRight, double yBottom, double yTop, double &x0, double &y0, double &x1, double &y1 )
750{
751 int lineCode = 0;
752
753 if ( y1 < yBottom )
754 lineCode |= 4;
755 else if ( y1 > yTop )
756 lineCode |= 8;
757
758 if ( x1 > xRight )
759 lineCode |= 2;
760 else if ( x1 < xLeft )
761 lineCode |= 1;
762
763 if ( y0 < yBottom )
764 lineCode |= 64;
765 else if ( y0 > yTop )
766 lineCode |= 128;
767
768 if ( x0 > xRight )
769 lineCode |= 32;
770 else if ( x0 < xLeft )
771 lineCode |= 16;
772
773 switch ( lineCode )
774 {
775 case 0: //completely inside
776 return true;
777
778 case 1:
779 clipEndLeft( x0, y0, x1, y1, xLeft );
780 return true;
781
782 case 2:
783 clipEndRight( x0, y0, x1, y1, xRight );
784 return true;
785
786 case 4:
787 clipEndBottom( x0, y0, x1, y1, yBottom );
788 return true;
789
790 case 5:
791 clipEndLeft( x0, y0, x1, y1, xLeft );
792 if ( y1 < yBottom )
793 clipEndBottom( x0, y0, x1, y1, yBottom );
794 return true;
795
796 case 6:
797 clipEndRight( x0, y0, x1, y1, xRight );
798 if ( y1 < yBottom )
799 clipEndBottom( x0, y0, x1, y1, yBottom );
800 return true;
801
802 case 8:
803 clipEndTop( x0, y0, x1, y1, yTop );
804 return true;
805
806 case 9:
807 clipEndLeft( x0, y0, x1, y1, xLeft );
808 if ( y1 > yTop )
809 clipEndTop( x0, y0, x1, y1, yTop );
810 return true;
811
812 case 10:
813 clipEndRight( x0, y0, x1, y1, xRight );
814 if ( y1 > yTop )
815 clipEndTop( x0, y0, x1, y1, yTop );
816 return true;
817
818 case 16:
819 clipStartLeft( x0, y0, x1, y1, xLeft );
820 return true;
821
822 case 18:
823 clipStartLeft( x0, y0, x1, y1, xLeft );
824 clipEndRight( x0, y0, x1, y1, xRight );
825 return true;
826
827 case 20:
828 clipStartLeft( x0, y0, x1, y1, xLeft );
829 if ( y0 < yBottom )
830 return false;
831 clipEndBottom( x0, y0, x1, y1, yBottom );
832 return true;
833
834 case 22:
835 clipStartLeft( x0, y0, x1, y1, xLeft );
836 if ( y0 < yBottom )
837 return false;
838 clipEndBottom( x0, y0, x1, y1, yBottom );
839 if ( x1 > xRight )
840 clipEndRight( x0, y0, x1, y1, xRight );
841 return true;
842
843 case 24:
844 clipStartLeft( x0, y0, x1, y1, xLeft );
845 if ( y0 > yTop )
846 return false;
847 clipEndTop( x0, y0, x1, y1, yTop );
848 return true;
849
850 case 26:
851 clipStartLeft( x0, y0, x1, y1, xLeft );
852 if ( y0 > yTop )
853 return false;
854 clipEndTop( x0, y0, x1, y1, yTop );
855 if ( x1 > xRight )
856 clipEndRight( x0, y0, x1, y1, xRight );
857 return true;
858
859 case 32:
860 clipStartRight( x0, y0, x1, y1, xRight );
861 return true;
862
863 case 33:
864 clipStartRight( x0, y0, x1, y1, xRight );
865 clipEndLeft( x0, y0, x1, y1, xLeft );
866 return true;
867
868 case 36:
869 clipStartRight( x0, y0, x1, y1, xRight );
870 if ( y0 < yBottom )
871 return false;
872 clipEndBottom( x0, y0, x1, y1, yBottom );
873 return true;
874
875 case 37:
876 clipStartRight( x0, y0, x1, y1, xRight );
877 if ( y0 < yBottom )
878 return false;
879 clipEndBottom( x0, y0, x1, y1, yBottom );
880 if ( x1 < xLeft )
881 clipEndLeft( x0, y0, x1, y1, xLeft );
882 return true;
883
884 case 40:
885 clipStartRight( x0, y0, x1, y1, xRight );
886 if ( y0 > yTop )
887 return false;
888 clipEndTop( x0, y0, x1, y1, yTop );
889 return true;
890
891 case 41:
892 clipStartRight( x0, y0, x1, y1, xRight );
893 if ( y0 > yTop )
894 return false;
895 clipEndTop( x0, y0, x1, y1, yTop );
896 if ( x1 < xLeft )
897 clipEndLeft( x0, y0, x1, y1, xLeft );
898 return true;
899
900 case 64:
901 clipStartBottom( x0, y0, x1, y1, yBottom );
902 return true;
903
904 case 65:
905 clipStartBottom( x0, y0, x1, y1, yBottom );
906 if ( x0 < xLeft )
907 return false;
908 clipEndLeft( x0, y0, x1, y1, xLeft );
909 if ( y1 < yBottom )
910 clipEndBottom( x0, y0, x1, y1, yBottom );
911 return true;
912
913 case 66:
914 clipStartBottom( x0, y0, x1, y1, yBottom );
915 if ( x0 > xRight )
916 return false;
917 clipEndRight( x0, y0, x1, y1, xRight );
918 return true;
919
920 case 72:
921 clipStartBottom( x0, y0, x1, y1, yBottom );
922 clipEndTop( x0, y0, x1, y1, yTop );
923 return true;
924
925 case 73:
926 clipStartBottom( x0, y0, x1, y1, yBottom );
927 if ( x0 < xLeft )
928 return false;
929 clipEndLeft( x0, y0, x1, y1, xLeft );
930 if ( y1 > yTop )
931 clipEndTop( x0, y0, x1, y1, yTop );
932 return true;
933
934 case 74:
935 clipStartBottom( x0, y0, x1, y1, yBottom );
936 if ( x0 > xRight )
937 return false;
938 clipEndRight( x0, y0, x1, y1, xRight );
939 if ( y1 > yTop )
940 clipEndTop( x0, y0, x1, y1, yTop );
941 return true;
942
943 case 80:
944 clipStartLeft( x0, y0, x1, y1, xLeft );
945 if ( y0 < yBottom )
946 clipStartBottom( x0, y0, x1, y1, yBottom );
947 return true;
948
949 case 82:
950 clipEndRight( x0, y0, x1, y1, xRight );
951 if ( y1 < yBottom )
952 return false;
953 clipStartBottom( x0, y0, x1, y1, yBottom );
954 if ( x0 < xLeft )
955 clipStartLeft( x0, y0, x1, y1, xLeft );
956 return true;
957
958 case 88:
959 clipEndTop( x0, y0, x1, y1, yTop );
960 if ( x1 < xLeft )
961 return false;
962 clipStartBottom( x0, y0, x1, y1, yBottom );
963 if ( x0 < xLeft )
964 clipStartLeft( x0, y0, x1, y1, xLeft );
965 return true;
966
967 case 90:
968 clipStartLeft( x0, y0, x1, y1, xLeft );
969 if ( y0 > yTop )
970 return false;
971 clipEndRight( x0, y0, x1, y1, xRight );
972 if ( y1 < yBottom )
973 return false;
974 if ( y0 < yBottom )
975 clipStartBottom( x0, y0, x1, y1, yBottom );
976 if ( y1 > yTop )
977 clipEndTop( x0, y0, x1, y1, yTop );
978 return true;
979
980 case 96:
981 clipStartRight( x0, y0, x1, y1, xRight );
982 if ( y0 < yBottom )
983 clipStartBottom( x0, y0, x1, y1, yBottom );
984 return true;
985
986 case 97:
987 clipEndLeft( x0, y0, x1, y1, xLeft );
988 if ( y1 < yBottom )
989 return false;
990 clipStartBottom( x0, y0, x1, y1, yBottom );
991 if ( x0 > xRight )
992 clipStartRight( x0, y0, x1, y1, xRight );
993 return true;
994
995 case 104:
996 clipEndTop( x0, y0, x1, y1, yTop );
997 if ( x1 > xRight )
998 return false;
999 clipStartRight( x0, y0, x1, y1, xRight );
1000 if ( y0 < yBottom )
1001 clipStartBottom( x0, y0, x1, y1, yBottom );
1002 return true;
1003
1004 case 105:
1005 clipEndLeft( x0, y0, x1, y1, xLeft );
1006 if ( y1 < yBottom )
1007 return false;
1008 clipStartRight( x0, y0, x1, y1, xRight );
1009 if ( y0 > yTop )
1010 return false;
1011 if ( y1 > yTop )
1012 clipEndTop( x0, y0, x1, y1, yTop );
1013 if ( y0 < yBottom )
1014 clipStartBottom( x0, y0, x1, y1, yBottom );
1015 return true;
1016
1017 case 128:
1018 clipStartTop( x0, y0, x1, y1, yTop );
1019 return true;
1020
1021 case 129:
1022 clipStartTop( x0, y0, x1, y1, yTop );
1023 if ( x0 < xLeft )
1024 return false;
1025 clipEndLeft( x0, y0, x1, y1, xLeft );
1026 return true;
1027
1028 case 130:
1029 clipStartTop( x0, y0, x1, y1, yTop );
1030 if ( x0 > xRight )
1031 return false;
1032 clipEndRight( x0, y0, x1, y1, xRight );
1033 return true;
1034
1035 case 132:
1036 clipStartTop( x0, y0, x1, y1, yTop );
1037 clipEndBottom( x0, y0, x1, y1, yBottom );
1038 return true;
1039
1040 case 133:
1041 clipStartTop( x0, y0, x1, y1, yTop );
1042 if ( x0 < xLeft )
1043 return false;
1044 clipEndLeft( x0, y0, x1, y1, xLeft );
1045 if ( y1 < yBottom )
1046 clipEndBottom( x0, y0, x1, y1, yBottom );
1047 return true;
1048
1049 case 134:
1050 clipStartTop( x0, y0, x1, y1, yTop );
1051 if ( x0 > xRight )
1052 return false;
1053 clipEndRight( x0, y0, x1, y1, xRight );
1054 if ( y1 < yBottom )
1055 clipEndBottom( x0, y0, x1, y1, yBottom );
1056 return true;
1057
1058 case 144:
1059 clipStartLeft( x0, y0, x1, y1, xLeft );
1060 if ( y0 > yTop )
1061 clipStartTop( x0, y0, x1, y1, yTop );
1062 return true;
1063
1064 case 146:
1065 clipEndRight( x0, y0, x1, y1, xRight );
1066 if ( y1 > yTop )
1067 return false;
1068 clipStartTop( x0, y0, x1, y1, yTop );
1069 if ( x0 < xLeft )
1070 clipStartLeft( x0, y0, x1, y1, xLeft );
1071 return true;
1072
1073 case 148:
1074 clipEndBottom( x0, y0, x1, y1, yBottom );
1075 if ( x1 < xLeft )
1076 return false;
1077 clipStartLeft( x0, y0, x1, y1, xLeft );
1078 if ( y0 > yTop )
1079 clipStartTop( x0, y0, x1, y1, yTop );
1080 return true;
1081
1082 case 150:
1083 clipStartLeft( x0, y0, x1, y1, xLeft );
1084 if ( y0 < yBottom )
1085 return false;
1086 clipEndRight( x0, y0, x1, y1, xRight );
1087 if ( y1 > yTop )
1088 return false;
1089 if ( y0 > yTop )
1090 clipStartTop( x0, y0, x1, y1, yTop );
1091 if ( y1 < yBottom )
1092 clipEndBottom( x0, y0, x1, y1, yBottom );
1093 return true;
1094
1095 case 160:
1096 clipStartRight( x0, y0, x1, y1, xRight );
1097 if ( y0 > yTop )
1098 clipStartTop( x0, y0, x1, y1, yTop );
1099 return true;
1100
1101 case 161:
1102 clipEndLeft( x0, y0, x1, y1, xLeft );
1103 if ( y1 > yTop )
1104 return false;
1105 clipStartTop( x0, y0, x1, y1, yTop );
1106 if ( x0 > xRight )
1107 clipStartRight( x0, y0, x1, y1, xRight );
1108 return true;
1109
1110 case 164:
1111 clipEndBottom( x0, y0, x1, y1, yBottom );
1112 if ( x1 > xRight )
1113 return false;
1114 clipStartRight( x0, y0, x1, y1, xRight );
1115 if ( y0 > yTop )
1116 clipStartTop( x0, y0, x1, y1, yTop );
1117 return true;
1118
1119 case 165:
1120 clipEndLeft( x0, y0, x1, y1, xLeft );
1121 if ( y1 > yTop )
1122 return false;
1123 clipStartRight( x0, y0, x1, y1, xRight );
1124 if ( y0 < yBottom )
1125 return false;
1126 if ( y1 < yBottom )
1127 clipEndBottom( x0, y0, x1, y1, yBottom );
1128 if ( y0 > yTop )
1129 clipStartTop( x0, y0, x1, y1, yTop );
1130 return true;
1131 }
1132
1133 return false;
1134}
1135#endif // SIP_RUN
1136
1137
1138#endif
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:240
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:205
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:268
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:212
bool is2d() const
Returns true if the box can be considered a 2-dimensional box, i.e.
Definition qgsbox3d.cpp:137
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:261
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:233
Trims lines and polygons to within a rectangular region.
Definition qgsclipper.h:51
static void trimPolygon(QPolygonF &pts, const QgsRectangle &clipRect)
Trims the given polygon to a rectangular box, by modifying the given polygon in place.
Definition qgsclipper.h:265
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:749
Boundary
A handy way to refer to the four boundaries.
Definition qgsclipper.h:82
@ ZMin
Minimum Z.
Definition qgsclipper.h:88
@ ZMax
Maximum Z.
Definition qgsclipper.h:87
static const double MAX_X
Maximum X-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:71
static const double MIN_Y
Minimum Y-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:77
static const double MAX_Y
Maximum Y-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:75
static const double MIN_X
Minimum X-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:73
static void trimFeature(QVector< double > &x, QVector< double > &y, bool shapeOpen)
Trims the given feature to a rectangular box.
Definition qgsclipper.h:246
Abstract base class for curved geometry type.
Definition qgscurve.h:36
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
Definition qgscurve.cpp:149
Line string geometry type, with support for z-dimension and m-values.
Represents a 2D point.
Definition qgspointxy.h:62
void set(double x, double y)
Sets the x and y value of the point.
Definition qgspointxy.h:139
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
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:6975
#define SIP_SKIP
Definition qgis_sip.h:133
#define SIP_FEATURE(feature)
Definition qgis_sip.h:183
#define SIP_INOUT
Definition qgis_sip.h:78