Skip to content

Commit

Permalink
Modify polygon clipping to clip close to the drawing rectangle. Shoul…
Browse files Browse the repository at this point in the history
…d now solve the clipping for print composer too

git-svn-id: http://svn.osgeo.org/qgis/trunk@15547 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
mhugent committed Mar 21, 2011
1 parent 6bd3336 commit 4af2bf3
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 82 deletions.
98 changes: 33 additions & 65 deletions src/core/qgsclipper.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class CORE_EXPORT QgsClipper
std::vector<double>& y,
bool shapeOpen );

static void trimFeature( QPolygonF& pts, bool shapeOpen );
static void trimPolygon( QPolygonF& pts, const QgsRectangle& clipRect );

/**Reads a polyline from WKB and clips it to clipExtent
@param wkb pointer to the start of the line wkb
Expand All @@ -101,25 +101,22 @@ class CORE_EXPORT QgsClipper
Boundary b,
bool shapeOpen );

static void trimFeatureToBoundary( const QPolygonF& inPts,
QPolygonF& outPts,
Boundary b,
bool shapeOpen );
static void trimPolygonToBoundary( const QPolygonF& inPts, QPolygonF& outPts, const QgsRectangle& rect, Boundary b, double boundaryValue );

// Determines if a point is inside or outside the given boundary
static bool inside( const double x, const double y, Boundary b );

static bool inside( const QPointF& pt, Boundary b );
static bool inside( const QPointF& pt, Boundary b, double val );

// Calculates the intersection point between a line defined by a
// (x1, y1), and (x2, y2) and the given boundary
static QgsPoint intersect( const double x1, const double y1,
const double x2, const double y2,
Boundary b );

static QPointF intersect( const QPointF& pt1,
const QPointF& pt2,
Boundary b );
static QPointF intersectRect( const QPointF& pt1,
const QPointF& pt2,
Boundary b, const QgsRectangle& rect );

//Implementation of 'Fast clipping' algorithm (Sobkow et al. 1987, Computers & Graphics Vol.11, 4, p.459-467)
static bool clipLineSegment( double xLeft, double xRight, double yBottom, double yTop, double& x0, double& y0, double& x1, double& y1 );
Expand Down Expand Up @@ -179,34 +176,18 @@ inline void QgsClipper::trimFeature( std::vector<double>& x,
trimFeatureToBoundary( tmpX, tmpY, x, y, YMin, shapeOpen );
}

inline void QgsClipper::trimFeature( QPolygonF& pts, bool shapeOpen )
inline void QgsClipper::trimPolygon( QPolygonF& pts, const QgsRectangle& clipRect )
{
bool needs_clipping = false;
const QPointF* ptsData = pts.constData();
for ( int i = 0; i < pts.count(); i++, ptsData++ )
{
if ( ptsData->x() < MIN_X || ptsData->x() > MAX_X
|| ptsData->y() < MIN_Y || ptsData->y() > MAX_Y )
{
needs_clipping = true;
break;
}
}
if ( !needs_clipping )
return;

QPolygonF tmpPts;
tmpPts.reserve( pts.size() );
trimFeatureToBoundary( pts, tmpPts, XMax, shapeOpen );

trimPolygonToBoundary( pts, tmpPts, clipRect, XMax, clipRect.xMaximum() );
pts.clear();
trimFeatureToBoundary( tmpPts, pts, YMax, shapeOpen );

trimPolygonToBoundary( tmpPts, pts, clipRect, YMax, clipRect.yMaximum() );
tmpPts.clear();
trimFeatureToBoundary( pts, tmpPts, XMin, shapeOpen );

trimPolygonToBoundary( pts, tmpPts, clipRect, XMin, clipRect.xMinimum() );
pts.clear();
trimFeatureToBoundary( tmpPts, pts, YMin, shapeOpen );
trimPolygonToBoundary( tmpPts, pts, clipRect, YMin, clipRect.yMinimum() );
}

// An auxilary function that is part of the polygon trimming
Expand Down Expand Up @@ -280,46 +261,33 @@ inline void QgsClipper::trimFeatureToBoundary(
}
}

inline void QgsClipper::trimFeatureToBoundary(
const QPolygonF& inPts, QPolygonF& outPts,
Boundary b, bool shapeOpen )
inline void QgsClipper::trimPolygonToBoundary( const QPolygonF& inPts, QPolygonF& outPts, const QgsRectangle& rect, Boundary b, double boundaryValue )
{
// The shapeOpen parameter selects whether this function treats the
// shape as open or closed. False is appropriate for polygons and
// true for polylines.

unsigned int i1 = inPts.size() - 1; // start with last point

// and compare to the first point initially.
for ( int i2 = 0; i2 < inPts.size() ; ++i2 )
{ // look at each edge of the polygon in turn
if ( inside( inPts[i2], b ) ) // end point of edge is inside boundary
if ( inside( inPts[i2], b, boundaryValue ) ) // end point of edge is inside boundary
{
if ( inside( inPts[i1], b ) )
if ( inside( inPts[i1], b, boundaryValue ) )
{
outPts.append( inPts[i2] );
}
else
{
// edge crosses into the boundary, so trim back to the boundary, and
// store both ends of the new edge
if ( !( i2 == 0 && shapeOpen ) )
{
outPts.append( intersect( inPts[i1], inPts[i2], b ) );
}

outPts.append( intersectRect( inPts[i1], inPts[i2], b, rect ) );
outPts.append( inPts[i2] );
}
}
else // end point of edge is outside boundary
{
// start point is in boundary, so need to trim back
if ( inside( inPts[i1], b ) )
if ( inside( inPts[i1], b, boundaryValue ) )
{
if ( !( i2 == 0 && shapeOpen ) )
{
outPts.append( intersect( inPts[i1], inPts[i2], b ) );
}
outPts.append( intersectRect( inPts[i1], inPts[i2], b, rect ) );
}
}
i1 = i2;
Expand Down Expand Up @@ -353,18 +321,18 @@ inline bool QgsClipper::inside( const double x, const double y, Boundary b )
return false;
}

inline bool QgsClipper::inside( const QPointF& pt, Boundary b )
inline bool QgsClipper::inside( const QPointF& pt, Boundary b, double val )
{
switch ( b )
{
case XMax: // x < MAX_X is inside
return ( pt.x() < MAX_X );
return ( pt.x() < val );
case XMin: // x > MIN_X is inside
return ( pt.x() > MIN_X );
return ( pt.x() > val );
case YMax: // y < MAX_Y is inside
return ( pt.y() < MAX_Y );
return ( pt.y() < val );
case YMin: // y > MIN_Y is inside
return ( pt.y() > MIN_Y );
return ( pt.y() > val );
}
return false;
}
Expand Down Expand Up @@ -421,9 +389,9 @@ inline QgsPoint QgsClipper::intersect( const double x1, const double y1,
return p;
}

inline QPointF QgsClipper::intersect( const QPointF& pt1,
const QPointF& pt2,
Boundary b )
inline QPointF QgsClipper::intersectRect( const QPointF& pt1,
const QPointF& pt2,
Boundary b, const QgsRectangle& rect )
{
// This function assumes that the two given points (x1, y1), and
// (x2, y2) cross the given boundary. Making this assumption allows
Expand All @@ -436,20 +404,20 @@ inline QPointF QgsClipper::intersect( const QPointF& pt1,
switch ( b )
{
case XMax: // x = MAX_X boundary
r_n = -( x1 - MAX_X ) * ( MAX_Y - MIN_Y );
r_d = ( x2 - x1 ) * ( MAX_Y - MIN_Y );
r_n = -( x1 - rect.xMaximum() ) * ( rect.yMaximum() - rect.yMinimum() );
r_d = ( x2 - x1 ) * ( rect.yMaximum() - rect.yMinimum() );
break;
case XMin: // x = MIN_X boundary
r_n = -( x1 - MIN_X ) * ( MAX_Y - MIN_Y );
r_d = ( x2 - x1 ) * ( MAX_Y - MIN_Y );
r_n = -( x1 - rect.xMinimum() ) * ( rect.yMaximum() - rect.yMinimum() );
r_d = ( x2 - x1 ) * ( rect.yMaximum() - rect.yMinimum() );
break;
case YMax: // y = MAX_Y boundary
r_n = ( y1 - MAX_Y ) * ( MAX_X - MIN_X );
r_d = -( y2 - y1 ) * ( MAX_X - MIN_X );
r_n = ( y1 - rect.yMaximum() ) * ( rect.xMaximum() - rect.xMinimum() );
r_d = -( y2 - y1 ) * ( rect.xMaximum() - rect.xMinimum() );
break;
case YMin: // y = MIN_Y boundary
r_n = ( y1 - MIN_Y ) * ( MAX_X - MIN_X );
r_d = -( y2 - y1 ) * ( MAX_X - MIN_X );
r_n = ( y1 - rect.yMinimum() ) * ( rect.xMaximum() - rect.xMinimum() );
r_d = -( y2 - y1 ) * ( rect.xMaximum() - rect.xMinimum() );
break;
}

Expand Down
26 changes: 18 additions & 8 deletions src/core/symbology-ng/qgsrendererv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QPolygon
const QgsMapToPixel& mtp = context.mapToPixel();
double z = 0; // dummy variable for coordiante transform

const QgsRectangle& e = context.extent();
double cw = e.width() / 10; double ch = e.height() / 10;
QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );

for ( unsigned int idx = 0; idx < numRings; idx++ )
{
unsigned int nPoints = *(( int* )wkb );
Expand All @@ -131,14 +135,6 @@ unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QPolygon
x = *(( double * ) wkb ); wkb += sizeof( double );
y = *(( double * ) wkb ); wkb += sizeof( double );

// TODO: maybe to the transform at once (faster?)
if ( ct )
{
z = 0;
ct->transformInPlace( x, y, z );
}
mtp.transformInPlace( x, y );

poly[jdx] = QPointF( x, y );

if ( hasZValue )
Expand All @@ -148,6 +144,20 @@ unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QPolygon
if ( nPoints < 1 )
continue;

//clip close to view extent
QgsClipper::trimPolygon( poly, clipRect );

//transform the QPolygonF to screen coordinates
for ( int i = 0; i < poly.size(); ++i )
{
if ( ct )
{
z = 0;
ct->transformInPlace( poly[i].rx(), poly[i].ry(), z );
}
mtp.transformInPlace( poly[i].rx(), poly[i].ry() );
}

if ( idx == 0 )
pts = poly;
else
Expand Down
10 changes: 1 addition & 9 deletions src/core/symbology-ng/qgssymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,27 +74,19 @@ void QgsFillSymbolLayerV2::_renderPolygon( QPainter* p, const QPolygonF& points,
if ( rings == NULL )
{
// simple polygon without holes

// clip polygon rings! Qt (as of version 4.6) has a slow algorithm for
// clipping painter paths: we will clip the rings by ourselves, so
// the clipping in Qt will not be triggered.
QPolygonF ring = points;
QgsClipper::trimFeature( ring, false );
p->drawPolygon( ring );
p->drawPolygon( points );
}
else
{
// polygon with holes must be drawn using painter path
QPainterPath path;
QPolygonF outerRing = points;
QgsClipper::trimFeature( outerRing, false );
path.addPolygon( outerRing );

QList<QPolygonF>::const_iterator it = rings->constBegin();
for ( ; it != rings->constEnd(); ++it )
{
QPolygonF ring = *it;
QgsClipper::trimFeature( ring, false );
path.addPolygon( ring );
}

Expand Down

0 comments on commit 4af2bf3

Please sign in to comment.