qwt_clipper.cpp
Go to the documentation of this file.
00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
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     // some templates used for inlining
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 &center, double radius )
00507 {
00508     QwtCircleClipper clipper( clipRect );
00509     return clipper.clipCircle( center, radius );
00510 }


plotjuggler
Author(s): Davide Faconti
autogenerated on Fri Sep 1 2017 02:41:56