[QGIS Commit] r14092 - in branches/threading-branch/src/core: . symbology-ng

svn_qgis at osgeo.org svn_qgis at osgeo.org
Mon Aug 16 11:17:09 EDT 2010


Author: wonder
Date: 2010-08-16 15:17:09 +0000 (Mon, 16 Aug 2010)
New Revision: 14092

Modified:
   branches/threading-branch/src/core/qgsclipper.cpp
   branches/threading-branch/src/core/qgsclipper.h
   branches/threading-branch/src/core/symbology-ng/qgssymbollayerv2.cpp
Log:
Improved rendering speed of zoomed in polygons with holes.
Clipping is now done to get coordinates into range +/- 16000.
(This is to ensure that slow Qt clipping won't start with Qt >= 4.6)
Hopefully it will not affect printing from map composer.


Modified: branches/threading-branch/src/core/qgsclipper.cpp
===================================================================
--- branches/threading-branch/src/core/qgsclipper.cpp	2010-08-16 12:30:26 UTC (rev 14091)
+++ branches/threading-branch/src/core/qgsclipper.cpp	2010-08-16 15:17:09 UTC (rev 14092)
@@ -25,9 +25,14 @@
 
 // But the static members must be initialised outside the class! (or GCC 4 dies)
 
-const double QgsClipper::MAX_X =  30000;
-const double QgsClipper::MIN_X = -30000;
-const double QgsClipper::MAX_Y =  30000;
-const double QgsClipper::MIN_Y = -30000;
+// Qt also does clipping when the coordinates go over +/- 32767
+// moreover from Qt 4.6, Qt clips also when the width/height of a painter path
+// is more than 32767. Since we want to avoid clipping by Qt (because it is slow)
+// we set coordinate limit to less than 32767 / 2
+static const double COORD_LIMIT = 16000;
+const double QgsClipper::MAX_X =  COORD_LIMIT;
+const double QgsClipper::MIN_X = -COORD_LIMIT;
+const double QgsClipper::MAX_Y =  COORD_LIMIT;
+const double QgsClipper::MIN_Y = -COORD_LIMIT;
 
 const double QgsClipper::SMALL_NUM = 1e-12;

Modified: branches/threading-branch/src/core/qgsclipper.h
===================================================================
--- branches/threading-branch/src/core/qgsclipper.h	2010-08-16 12:30:26 UTC (rev 14091)
+++ branches/threading-branch/src/core/qgsclipper.h	2010-08-16 15:17:09 UTC (rev 14092)
@@ -27,6 +27,8 @@
 #include <cmath>
 #include <iostream>
 
+#include <QPolygonF>
+
 /** \ingroup core
  * A class to trim lines and polygons to within a rectangular region.
  * The functions in this class are likely to be called from within a
@@ -77,6 +79,8 @@
                              std::vector<double>& y,
                              bool shapeOpen );
 
+    static void trimFeature( QPolygonF& pts, bool shapeOpen );
+
   private:
 
     // Used when testing for equivalance to 0.0
@@ -91,16 +95,27 @@
                                        Boundary b,
                                        bool shapeOpen );
 
+    static void trimFeatureToBoundary( const QPolygonF& inPts,
+                                       QPolygonF& outPts,
+                                       Boundary b,
+                                       bool shapeOpen );
+
     // 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 );
+
     // 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 );
+  };
+
 // The inline functions
 
 // Trim the feature using Sutherland and Hodgman's
@@ -134,6 +149,36 @@
   trimFeatureToBoundary( tmpX, tmpY, x, y, YMin, shapeOpen );
 }
 
+inline void QgsClipper::trimFeature( QPolygonF& pts, bool shapeOpen )
+{
+  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 );
+
+  pts.clear();
+  trimFeatureToBoundary( tmpPts, pts, YMax, shapeOpen );
+
+  tmpPts.clear();
+  trimFeatureToBoundary( pts, tmpPts, XMin, shapeOpen );
+
+  pts.clear();
+  trimFeatureToBoundary( tmpPts, pts, YMin, shapeOpen );
+}
+
 // An auxilary function that is part of the polygon trimming
 // code. Will trim the given polygon to the given boundary and return
 // the trimmed polygon in the out pointer. Uses Sutherland and
@@ -194,6 +239,53 @@
   }
 }
 
+inline void QgsClipper::trimFeatureToBoundary(
+  const QPolygonF& inPts, QPolygonF& outPts,
+  Boundary b, bool shapeOpen )
+{
+  // 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 ( unsigned 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[i1], b ) )
+      {
+        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( 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 ( !( i2 == 0 && shapeOpen ) )
+        {
+          outPts.append( intersect( inPts[i1], inPts[i2], b ) );
+        }
+      }
+    }
+    i1 = i2;
+  }
+}
+
+
 // An auxilary function to trimPolygonToBoundarY() that returns
 // whether a point is inside or outside the given boundary.
 
@@ -221,7 +313,23 @@
   return false;
 }
 
+inline bool QgsClipper::inside( const QPointF& pt, Boundary b )
+{
+  switch ( b )
+  {
+    case XMax: // x < MAX_X is inside
+      return ( pt.x() < MAX_X );
+    case XMin: // x > MIN_X is inside
+      return ( pt.x() > MIN_X );
+    case YMax: // y < MAX_Y is inside
+      return ( pt.y() < MAX_Y );
+    case YMin: // y > MIN_Y is inside
+      return ( pt.y() > MIN_Y );
+  }
+  return false;
+}
 
+
 // An auxilary function to trimPolygonToBoundarY() that calculates and
 // returns the intersection of the line defined by the given points
 // and the given boundary.
@@ -273,5 +381,50 @@
   return p;
 }
 
+inline QPointF QgsClipper::intersect( const QPointF& pt1,
+                                       const QPointF& pt2,
+                                       Boundary b )
+{
+  // This function assumes that the two given points (x1, y1), and
+  // (x2, y2) cross the given boundary. Making this assumption allows
+  // some optimisations.
 
+  double r_n = SMALL_NUM, r_d = SMALL_NUM;
+  const double x1 = pt1.x(), x2 = pt2.x();
+  const double y1 = pt1.y(), y2 = pt2.y();
+
+  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 );
+      break;
+    case XMin: // x = MIN_X boundary
+      r_n = -( x1 - MIN_X ) * ( MAX_Y - MIN_Y );
+      r_d = ( x2 - x1 )   * ( MAX_Y - MIN_Y );
+      break;
+    case YMax: // y = MAX_Y boundary
+      r_n = ( y1 - MAX_Y ) * ( MAX_X - MIN_X );
+      r_d = -( y2 - y1 )   * ( MAX_X - MIN_X );
+      break;
+    case YMin: // y = MIN_Y boundary
+      r_n = ( y1 - MIN_Y ) * ( MAX_X - MIN_X );
+      r_d = -( y2 - y1 )   * ( MAX_X - MIN_X );
+      break;
+  }
+
+  if ( std::abs( r_d ) > SMALL_NUM && std::abs( r_n ) > SMALL_NUM )
+  { // they cross
+    double r = r_n / r_d;
+    return QPointF( x1 + r*( x2 - x1 ), y1 + r*( y2 - y1 ) );
+  }
+  else
+  {
+    // Should never get here, but if we do for some reason, cause a
+    // clunk because something else is wrong if we do.
+    Q_ASSERT( std::abs( r_d ) > SMALL_NUM && std::abs( r_n ) > SMALL_NUM );
+    return QPointF();
+  }
+}
+
 #endif

Modified: branches/threading-branch/src/core/symbology-ng/qgssymbollayerv2.cpp
===================================================================
--- branches/threading-branch/src/core/symbology-ng/qgssymbollayerv2.cpp	2010-08-16 12:30:26 UTC (rev 14091)
+++ branches/threading-branch/src/core/symbology-ng/qgssymbollayerv2.cpp	2010-08-16 15:17:09 UTC (rev 14092)
@@ -3,6 +3,7 @@
 
 #include "qgsrenderer.h"
 #include "qgsrendercontext.h"
+#include "qgsclipper.h"
 
 #include <QSize>
 #include <QPainter>
@@ -87,12 +88,20 @@
     QPainterPath path;
     QPolygonF outer_ring( numPoints );
     memcpy( outer_ring.data(), points, sizeof(QPointF) * numPoints );
+
+    // 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.
+    QgsClipper::trimFeature( outer_ring, false );
+
     path.addPolygon( outer_ring );
 
     QList<QPolygonF>::const_iterator it = rings->constBegin();
     for ( ; it != rings->constEnd(); ++it )
     {
-      path.addPolygon( *it );
+      QPolygonF ring = *it;
+      QgsClipper::trimFeature( ring, false );
+      path.addPolygon( ring );
     }
 
     p->drawPath( path );



More information about the QGIS-commit mailing list