00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_clipper.h"
00011 #include "qwt_point_polar.h"
00012 #include <qrect.h>
00013 #include <string.h>
00014 #include <stdlib.h>
00015
00016 #if QT_VERSION < 0x040601
00017 #define qAtan(x) ::atan(x)
00018 #endif
00019
00020 namespace QwtClip
00021 {
00022
00023 template <class Point, typename T> class LeftEdge;
00024 template <class Point, typename T> class RightEdge;
00025 template <class Point, typename T> class TopEdge;
00026 template <class Point, typename T> class BottomEdge;
00027
00028 template <class Point> class PointBuffer;
00029 }
00030
00031 template <class Point, typename Value>
00032 class QwtClip::LeftEdge
00033 {
00034 public:
00035 inline LeftEdge( Value x1, Value, Value, Value ):
00036 d_x1( x1 )
00037 {
00038 }
00039
00040 inline bool isInside( const Point &p ) const
00041 {
00042 return p.x() >= d_x1;
00043 }
00044
00045 inline Point intersection( const Point &p1, const Point &p2 ) const
00046 {
00047 double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() );
00048 return Point( d_x1, static_cast< Value >( p2.y() + ( d_x1 - p2.x() ) * dy ) );
00049 }
00050 private:
00051 const Value d_x1;
00052 };
00053
00054 template <class Point, typename Value>
00055 class QwtClip::RightEdge
00056 {
00057 public:
00058 inline RightEdge( Value, Value x2, Value, Value ):
00059 d_x2( x2 )
00060 {
00061 }
00062
00063 inline bool isInside( const Point &p ) const
00064 {
00065 return p.x() <= d_x2;
00066 }
00067
00068 inline Point intersection( const Point &p1, const Point &p2 ) const
00069 {
00070 double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() );
00071 return Point( d_x2, static_cast<Value>( p2.y() + ( d_x2 - p2.x() ) * dy ) );
00072 }
00073
00074 private:
00075 const Value d_x2;
00076 };
00077
00078 template <class Point, typename Value>
00079 class QwtClip::TopEdge
00080 {
00081 public:
00082 inline TopEdge( Value, Value, Value y1, Value ):
00083 d_y1( y1 )
00084 {
00085 }
00086
00087 inline bool isInside( const Point &p ) const
00088 {
00089 return p.y() >= d_y1;
00090 }
00091
00092 inline Point intersection( const Point &p1, const Point &p2 ) const
00093 {
00094 double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() );
00095 return Point( static_cast<Value>( p2.x() + ( d_y1 - p2.y() ) * dx ), d_y1 );
00096 }
00097
00098 private:
00099 const Value d_y1;
00100 };
00101
00102 template <class Point, typename Value>
00103 class QwtClip::BottomEdge
00104 {
00105 public:
00106 inline BottomEdge( Value, Value, Value, Value y2 ):
00107 d_y2( y2 )
00108 {
00109 }
00110
00111 inline bool isInside( const Point &p ) const
00112 {
00113 return p.y() <= d_y2;
00114 }
00115
00116 inline Point intersection( const Point &p1, const Point &p2 ) const
00117 {
00118 double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() );
00119 return Point( static_cast<Value>( p2.x() + ( d_y2 - p2.y() ) * dx ), d_y2 );
00120 }
00121
00122 private:
00123 const Value d_y2;
00124 };
00125
00126 template<class Point>
00127 class QwtClip::PointBuffer
00128 {
00129 public:
00130 explicit PointBuffer( int capacity = 0 ):
00131 m_capacity( 0 ),
00132 m_size( 0 ),
00133 m_buffer( NULL )
00134 {
00135 if ( capacity > 0 )
00136 reserve( capacity );
00137 }
00138
00139 ~PointBuffer()
00140 {
00141 if ( m_buffer )
00142 ::free( m_buffer );
00143 }
00144
00145 inline void setPoints( int numPoints, const Point *points )
00146 {
00147 reserve( numPoints );
00148
00149 m_size = numPoints;
00150 ::memcpy( m_buffer, points, m_size * sizeof( Point ) );
00151 }
00152
00153 inline void reset()
00154 {
00155 m_size = 0;
00156 }
00157
00158 inline int size() const
00159 {
00160 return m_size;
00161 }
00162
00163 inline Point *data() const
00164 {
00165 return m_buffer;
00166 }
00167
00168 inline Point &operator[]( int i )
00169 {
00170 return m_buffer[i];
00171 }
00172
00173 inline const Point &operator[]( int i ) const
00174 {
00175 return m_buffer[i];
00176 }
00177
00178 inline void add( const Point &point )
00179 {
00180 if ( m_capacity <= m_size )
00181 reserve( m_size + 1 );
00182
00183 m_buffer[m_size++] = point;
00184 }
00185
00186 private:
00187 inline void reserve( int size )
00188 {
00189 if ( m_capacity == 0 )
00190 m_capacity = 1;
00191
00192 while ( m_capacity < size )
00193 m_capacity *= 2;
00194
00195 m_buffer = static_cast<Point *>(
00196 ::realloc( m_buffer, m_capacity * sizeof( Point ) ) );
00197 }
00198
00199 int m_capacity;
00200 int m_size;
00201 Point *m_buffer;
00202 };
00203
00204 using namespace QwtClip;
00205
00206 template <class Polygon, class Rect, class Point, typename T>
00207 class QwtPolygonClipper
00208 {
00209 public:
00210 explicit QwtPolygonClipper( const Rect &clipRect ):
00211 d_clipRect( clipRect )
00212 {
00213 }
00214
00215 Polygon clipPolygon( const Polygon &polygon, bool closePolygon ) const
00216 {
00217 #if 0
00218 if ( d_clipRect.contains( polygon.boundingRect() ) )
00219 return polygon;
00220 #endif
00221
00222 PointBuffer<Point> points1;
00223 PointBuffer<Point> points2( qMin( 256, polygon.size() ) );
00224
00225 points1.setPoints( polygon.size(), polygon.data() );
00226
00227 clipEdge< LeftEdge<Point, T> >( closePolygon, points1, points2 );
00228 clipEdge< RightEdge<Point, T> >( closePolygon, points2, points1 );
00229 clipEdge< TopEdge<Point, T> >( closePolygon, points1, points2 );
00230 clipEdge< BottomEdge<Point, T> >( closePolygon, points2, points1 );
00231
00232 Polygon p;
00233 p.resize( points1.size() );
00234 ::memcpy( p.data(), points1.data(), points1.size() * sizeof( Point ) );
00235
00236 return p;
00237 }
00238
00239 private:
00240 template <class Edge>
00241 inline void clipEdge( bool closePolygon,
00242 PointBuffer<Point> &points, PointBuffer<Point> &clippedPoints ) const
00243 {
00244 clippedPoints.reset();
00245
00246 if ( points.size() < 2 )
00247 {
00248 if ( points.size() == 1 )
00249 clippedPoints.add( points[0] );
00250 return;
00251 }
00252
00253 const Edge edge( d_clipRect.x(), d_clipRect.x() + d_clipRect.width(),
00254 d_clipRect.y(), d_clipRect.y() + d_clipRect.height() );
00255
00256 int lastPos, start;
00257 if ( closePolygon )
00258 {
00259 start = 0;
00260 lastPos = points.size() - 1;
00261 }
00262 else
00263 {
00264 start = 1;
00265 lastPos = 0;
00266
00267 if ( edge.isInside( points[0] ) )
00268 clippedPoints.add( points[0] );
00269 }
00270
00271 const uint nPoints = points.size();
00272 for ( uint i = start; i < nPoints; i++ )
00273 {
00274 const Point &p1 = points[i];
00275 const Point &p2 = points[lastPos];
00276
00277 if ( edge.isInside( p1 ) )
00278 {
00279 if ( edge.isInside( p2 ) )
00280 {
00281 clippedPoints.add( p1 );
00282 }
00283 else
00284 {
00285 clippedPoints.add( edge.intersection( p1, p2 ) );
00286 clippedPoints.add( p1 );
00287 }
00288 }
00289 else
00290 {
00291 if ( edge.isInside( p2 ) )
00292 {
00293 clippedPoints.add( edge.intersection( p1, p2 ) );
00294 }
00295 }
00296 lastPos = i;
00297 }
00298 }
00299
00300 const Rect d_clipRect;
00301 };
00302
00303 class QwtCircleClipper
00304 {
00305 public:
00306 explicit QwtCircleClipper( const QRectF &r );
00307 QVector<QwtInterval> clipCircle( const QPointF &, double radius ) const;
00308
00309 private:
00310 enum Edge
00311 {
00312 Left,
00313 Top,
00314 Right,
00315 Bottom,
00316
00317 NEdges
00318 };
00319
00320 QList<QPointF> cuttingPoints(
00321 Edge, const QPointF &pos, double radius ) const;
00322
00323 double toAngle( const QPointF &, const QPointF & ) const;
00324
00325 const QRectF d_rect;
00326 };
00327
00328
00329 QwtCircleClipper::QwtCircleClipper( const QRectF &r ):
00330 d_rect( r )
00331 {
00332 }
00333
00334 QVector<QwtInterval> QwtCircleClipper::clipCircle(
00335 const QPointF &pos, double radius ) const
00336 {
00337 QList<QPointF> points;
00338 for ( int edge = 0; edge < NEdges; edge++ )
00339 points += cuttingPoints( static_cast<Edge>(edge), pos, radius );
00340
00341 QVector<QwtInterval> intv;
00342 if ( points.size() <= 0 )
00343 {
00344 QRectF cRect( 0, 0, 2 * radius, 2 * radius );
00345 cRect.moveCenter( pos );
00346 if ( d_rect.contains( cRect ) )
00347 intv += QwtInterval( 0.0, 2 * M_PI );
00348 }
00349 else
00350 {
00351 QList<double> angles;
00352 for ( int i = 0; i < points.size(); i++ )
00353 angles += toAngle( pos, points[i] );
00354 qSort( angles );
00355
00356 const int in = d_rect.contains( qwtPolar2Pos( pos, radius,
00357 angles[0] + ( angles[1] - angles[0] ) / 2 ) );
00358
00359 if ( in )
00360 {
00361 for ( int i = 0; i < angles.size() - 1; i += 2 )
00362 intv += QwtInterval( angles[i], angles[i+1] );
00363 }
00364 else
00365 {
00366 for ( int i = 1; i < angles.size() - 1; i += 2 )
00367 intv += QwtInterval( angles[i], angles[i+1] );
00368 intv += QwtInterval( angles.last(), angles.first() );
00369 }
00370 }
00371
00372 return intv;
00373 }
00374
00375 double QwtCircleClipper::toAngle(
00376 const QPointF &from, const QPointF &to ) const
00377 {
00378 if ( from.x() == to.x() )
00379 return from.y() <= to.y() ? M_PI / 2.0 : 3 * M_PI / 2.0;
00380
00381 const double m = qAbs( ( to.y() - from.y() ) / ( to.x() - from.x() ) );
00382
00383 double angle = qAtan( m );
00384 if ( to.x() > from.x() )
00385 {
00386 if ( to.y() > from.y() )
00387 angle = 2 * M_PI - angle;
00388 }
00389 else
00390 {
00391 if ( to.y() > from.y() )
00392 angle = M_PI + angle;
00393 else
00394 angle = M_PI - angle;
00395 }
00396
00397 return angle;
00398 }
00399
00400 QList<QPointF> QwtCircleClipper::cuttingPoints(
00401 Edge edge, const QPointF &pos, double radius ) const
00402 {
00403 QList<QPointF> points;
00404
00405 if ( edge == Left || edge == Right )
00406 {
00407 const double x = ( edge == Left ) ? d_rect.left() : d_rect.right();
00408 if ( qAbs( pos.x() - x ) < radius )
00409 {
00410 const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.x() - x ) );
00411 const double m_y1 = pos.y() + off;
00412 if ( m_y1 >= d_rect.top() && m_y1 <= d_rect.bottom() )
00413 points += QPointF( x, m_y1 );
00414
00415 const double m_y2 = pos.y() - off;
00416 if ( m_y2 >= d_rect.top() && m_y2 <= d_rect.bottom() )
00417 points += QPointF( x, m_y2 );
00418 }
00419 }
00420 else
00421 {
00422 const double y = ( edge == Top ) ? d_rect.top() : d_rect.bottom();
00423 if ( qAbs( pos.y() - y ) < radius )
00424 {
00425 const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.y() - y ) );
00426 const double x1 = pos.x() + off;
00427 if ( x1 >= d_rect.left() && x1 <= d_rect.right() )
00428 points += QPointF( x1, y );
00429
00430 const double m_x2 = pos.x() - off;
00431 if ( m_x2 >= d_rect.left() && m_x2 <= d_rect.right() )
00432 points += QPointF( m_x2, y );
00433 }
00434 }
00435 return points;
00436 }
00437
00447 QPolygon QwtClipper::clipPolygon(
00448 const QRectF &clipRect, const QPolygon &polygon, bool closePolygon )
00449 {
00450 const int minX = qCeil( clipRect.left() );
00451 const int maxX = qFloor( clipRect.right() );
00452 const int minY = qCeil( clipRect.top() );
00453 const int maxY = qFloor( clipRect.bottom() );
00454
00455 const QRect r( minX, minY, maxX - minX, maxY - minY );
00456
00457 QwtPolygonClipper<QPolygon, QRect, QPoint, int> clipper( r );
00458 return clipper.clipPolygon( polygon, closePolygon );
00459 }
00469 QPolygon QwtClipper::clipPolygon(
00470 const QRect &clipRect, const QPolygon &polygon, bool closePolygon )
00471 {
00472 QwtPolygonClipper<QPolygon, QRect, QPoint, int> clipper( clipRect );
00473 return clipper.clipPolygon( polygon, closePolygon );
00474 }
00475
00485 QPolygonF QwtClipper::clipPolygonF(
00486 const QRectF &clipRect, const QPolygonF &polygon, bool closePolygon )
00487 {
00488 QwtPolygonClipper<QPolygonF, QRectF, QPointF, double> clipper( clipRect );
00489 return clipper.clipPolygon( polygon, closePolygon );
00490 }
00491
00505 QVector<QwtInterval> QwtClipper::clipCircle( const QRectF &clipRect,
00506 const QPointF ¢er, double radius )
00507 {
00508 QwtCircleClipper clipper( clipRect );
00509 return clipper.clipCircle( center, radius );
00510 }