qwt_clipper.cpp 12.5 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
5
 *
pixhawk's avatar
pixhawk committed
6 7 8 9 10
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include "qwt_clipper.h"
Bryant's avatar
Bryant committed
11 12 13 14
#include "qwt_point_polar.h"
#include <qrect.h>
#include <string.h>
#include <stdlib.h>
pixhawk's avatar
pixhawk committed
15

Bryant's avatar
Bryant committed
16 17
#if QT_VERSION < 0x040601
#define qAtan(x) ::atan(x)
pixhawk's avatar
pixhawk committed
18 19
#endif

Bryant's avatar
Bryant committed
20 21 22 23 24 25 26
namespace QwtClip
{
    // some templates used for inlining
    template <class Point, typename T> class LeftEdge;
    template <class Point, typename T> class RightEdge;
    template <class Point, typename T> class TopEdge;
    template <class Point, typename T> class BottomEdge;
pixhawk's avatar
pixhawk committed
27

Bryant's avatar
Bryant committed
28 29 30 31 32
    template <class Point> class PointBuffer;
}

template <class Point, typename Value>
class QwtClip::LeftEdge
pixhawk's avatar
pixhawk committed
33 34
{
public:
Bryant's avatar
Bryant committed
35 36 37 38
    inline LeftEdge( Value x1, Value, Value, Value ):
        d_x1( x1 )
    {
    }
pixhawk's avatar
pixhawk committed
39

Bryant's avatar
Bryant committed
40 41 42 43
    inline bool isInside( const Point &p  ) const
    {
        return p.x() >= d_x1;
    }
pixhawk's avatar
pixhawk committed
44

Bryant's avatar
Bryant committed
45 46 47 48 49
    inline Point intersection( const Point &p1, const Point &p2 ) const
    {
        double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() );
        return Point( d_x1, static_cast< Value >( p2.y() + ( d_x1 - p2.x() ) * dy ) );
    }
pixhawk's avatar
pixhawk committed
50
private:
Bryant's avatar
Bryant committed
51
    const Value d_x1;
pixhawk's avatar
pixhawk committed
52 53
};

Bryant's avatar
Bryant committed
54 55
template <class Point, typename Value>
class QwtClip::RightEdge
pixhawk's avatar
pixhawk committed
56 57
{
public:
Bryant's avatar
Bryant committed
58 59 60 61
    inline RightEdge( Value, Value x2, Value, Value ):
        d_x2( x2 )
    {
    }
pixhawk's avatar
pixhawk committed
62

Bryant's avatar
Bryant committed
63 64 65 66 67 68 69 70 71 72
    inline bool isInside( const Point &p  ) const
    {
        return p.x() <= d_x2;
    }

    inline Point intersection( const Point &p1, const Point &p2 ) const
    {
        double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() );
        return Point( d_x2, static_cast<Value>( p2.y() + ( d_x2 - p2.x() ) * dy ) );
    }
pixhawk's avatar
pixhawk committed
73

Bryant's avatar
Bryant committed
74 75
private:
    const Value d_x2;
pixhawk's avatar
pixhawk committed
76 77
};

Bryant's avatar
Bryant committed
78 79
template <class Point, typename Value>
class QwtClip::TopEdge
pixhawk's avatar
pixhawk committed
80 81
{
public:
Bryant's avatar
Bryant committed
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    inline TopEdge( Value, Value, Value y1, Value ):
        d_y1( y1 )
    {
    }

    inline bool isInside( const Point &p  ) const
    {
        return p.y() >= d_y1;
    }

    inline Point intersection( const Point &p1, const Point &p2 ) const
    {
        double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() );
        return Point( static_cast<Value>( p2.x() + ( d_y1 - p2.y() ) * dx ), d_y1 );
    }
pixhawk's avatar
pixhawk committed
97 98

private:
Bryant's avatar
Bryant committed
99
    const Value d_y1;
pixhawk's avatar
pixhawk committed
100 101
};

Bryant's avatar
Bryant committed
102 103
template <class Point, typename Value>
class QwtClip::BottomEdge
pixhawk's avatar
pixhawk committed
104
{
Bryant's avatar
Bryant committed
105 106 107 108 109
public:
    inline BottomEdge( Value, Value, Value, Value y2 ):
        d_y2( y2 )
    {
    }
pixhawk's avatar
pixhawk committed
110

Bryant's avatar
Bryant committed
111 112 113 114
    inline bool isInside( const Point &p ) const
    {
        return p.y() <= d_y2;
    }
pixhawk's avatar
pixhawk committed
115

Bryant's avatar
Bryant committed
116 117 118 119 120 121 122 123 124
    inline Point intersection( const Point &p1, const Point &p2 ) const
    {
        double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() );
        return Point( static_cast<Value>( p2.x() + ( d_y2 - p2.y() ) * dx ), d_y2 );
    }

private:
    const Value d_y2;
};
pixhawk's avatar
pixhawk committed
125

Bryant's avatar
Bryant committed
126 127
template<class Point>
class QwtClip::PointBuffer
pixhawk's avatar
pixhawk committed
128
{
Bryant's avatar
Bryant committed
129 130 131 132 133 134 135 136 137
public:
    PointBuffer( int capacity = 0 ):
        m_capacity( 0 ),
        m_size( 0 ),
        m_buffer( NULL )
    {
        if ( capacity > 0 )
            reserve( capacity );
    }
pixhawk's avatar
pixhawk committed
138

Bryant's avatar
Bryant committed
139 140 141 142 143
    ~PointBuffer()
    {
        if ( m_buffer )
            ::free( m_buffer );
    }
pixhawk's avatar
pixhawk committed
144

Bryant's avatar
Bryant committed
145 146 147
    inline void setPoints( int numPoints, const Point *points )
    {
        reserve( numPoints );
pixhawk's avatar
pixhawk committed
148

Bryant's avatar
Bryant committed
149 150
        m_size = numPoints;
        ::memcpy( m_buffer, points, m_size * sizeof( Point ) );
pixhawk's avatar
pixhawk committed
151 152
    }

Bryant's avatar
Bryant committed
153 154 155
    inline void reset() 
    { 
        m_size = 0; 
pixhawk's avatar
pixhawk committed
156 157
    }

Bryant's avatar
Bryant committed
158 159 160 161
    inline int size() const 
    { 
        return m_size; 
    }
pixhawk's avatar
pixhawk committed
162

Bryant's avatar
Bryant committed
163 164 165
    inline Point *data() const 
    { 
        return m_buffer; 
pixhawk's avatar
pixhawk committed
166 167
    }

Bryant's avatar
Bryant committed
168 169 170 171
    inline Point &operator[]( int i ) 
    { 
        return m_buffer[i]; 
    }
pixhawk's avatar
pixhawk committed
172

Bryant's avatar
Bryant committed
173 174 175
    inline const Point &operator[]( int i ) const 
    { 
        return m_buffer[i]; 
pixhawk's avatar
pixhawk committed
176 177
    }

Bryant's avatar
Bryant committed
178 179 180 181 182 183
    inline void add( const Point &point )
    {
        if ( m_capacity <= m_size )
            reserve( m_size + 1 );

        m_buffer[m_size++] = point;
pixhawk's avatar
pixhawk committed
184 185
    }

Bryant's avatar
Bryant committed
186 187 188 189 190
private:
    inline void reserve( int size )
    {
        if ( m_capacity == 0 )
            m_capacity = 1;
pixhawk's avatar
pixhawk committed
191

Bryant's avatar
Bryant committed
192 193
        while ( m_capacity < size )
            m_capacity *= 2;
pixhawk's avatar
pixhawk committed
194

Bryant's avatar
Bryant committed
195 196 197
        m_buffer = static_cast<Point *>( 
            ::realloc( m_buffer, m_capacity * sizeof( Point ) ) );
    }
pixhawk's avatar
pixhawk committed
198

Bryant's avatar
Bryant committed
199 200 201 202
    int m_capacity;
    int m_size;
    Point *m_buffer;
};
pixhawk's avatar
pixhawk committed
203

Bryant's avatar
Bryant committed
204
using namespace QwtClip;
pixhawk's avatar
pixhawk committed
205

Bryant's avatar
Bryant committed
206 207 208 209 210 211 212 213
template <class Polygon, class Rect, class Point, typename T>
class QwtPolygonClipper
{
public:
    QwtPolygonClipper( const Rect &clipRect ):
        d_clipRect( clipRect )
    {
    }
pixhawk's avatar
pixhawk committed
214

Bryant's avatar
Bryant committed
215 216 217 218 219
    Polygon clipPolygon( const Polygon &polygon, bool closePolygon ) const
    {
#if 0
        if ( d_clipRect.contains( polygon.boundingRect() ) )
            return polygon;
pixhawk's avatar
pixhawk committed
220 221
#endif

Bryant's avatar
Bryant committed
222 223
        PointBuffer<Point> points1;
        PointBuffer<Point> points2( qMin( 256, polygon.size() ) );
pixhawk's avatar
pixhawk committed
224

Bryant's avatar
Bryant committed
225
        points1.setPoints( polygon.size(), polygon.data() );
pixhawk's avatar
pixhawk committed
226

Bryant's avatar
Bryant committed
227 228 229 230
        clipEdge< LeftEdge<Point, T> >( closePolygon, points1, points2 );
        clipEdge< RightEdge<Point, T> >( closePolygon, points2, points1 );
        clipEdge< TopEdge<Point, T> >( closePolygon, points1, points2 );
        clipEdge< BottomEdge<Point, T> >( closePolygon, points2, points1 );
pixhawk's avatar
pixhawk committed
231

Bryant's avatar
Bryant committed
232 233 234 235 236
        Polygon p;
        p.resize( points1.size() );
        ::memcpy( p.data(), points1.data(), points1.size() * sizeof( Point ) );

        return p;
pixhawk's avatar
pixhawk committed
237 238
    }

Bryant's avatar
Bryant committed
239 240 241 242 243 244 245 246 247 248 249 250 251
private:
    template <class Edge>
    inline void clipEdge( bool closePolygon,
        PointBuffer<Point> &points, PointBuffer<Point> &clippedPoints ) const
    {
        clippedPoints.reset();

        if ( points.size() < 2 )
        {
            if ( points.size() == 1 )
                clippedPoints.add( points[0] );
            return;
        }
pixhawk's avatar
pixhawk committed
252

Bryant's avatar
Bryant committed
253 254 255 256 257 258 259 260 261 262 263 264 265
        const Edge edge( d_clipRect.x(), d_clipRect.x() + d_clipRect.width(),
            d_clipRect.y(), d_clipRect.y() + d_clipRect.height() );

        int lastPos, start;
        if ( closePolygon )
        {
            start = 0;
            lastPos = points.size() - 1;
        }
        else
        {
            start = 1;
            lastPos = 0;
pixhawk's avatar
pixhawk committed
266

Bryant's avatar
Bryant committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
            if ( edge.isInside( points[0] ) )
                clippedPoints.add( points[0] );
        }

        const uint nPoints = points.size();
        for ( uint i = start; i < nPoints; i++ )
        {
            const Point &p1 = points[i];
            const Point &p2 = points[lastPos];

            if ( edge.isInside( p1 ) )
            {
                if ( edge.isInside( p2 ) )
                {
                    clippedPoints.add( p1 );
                }
                else
                {
                    clippedPoints.add( edge.intersection( p1, p2 ) );
                    clippedPoints.add( p1 );
                }
            }
            else
            {
                if ( edge.isInside( p2 ) )
                {
                    clippedPoints.add( edge.intersection( p1, p2 ) );
                }
pixhawk's avatar
pixhawk committed
295
            }
Bryant's avatar
Bryant committed
296
            lastPos = i;
pixhawk's avatar
pixhawk committed
297 298 299
        }
    }

Bryant's avatar
Bryant committed
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
    const Rect d_clipRect;
};

class QwtCircleClipper
{
public:
    QwtCircleClipper( const QRectF &r );
    QVector<QwtInterval> clipCircle( const QPointF &, double radius ) const;

private:
    enum Edge
    {
        Left,
        Top,
        Right,
        Bottom,

        NEdges
    };

    QList<QPointF> cuttingPoints(
        Edge, const QPointF &pos, double radius ) const;

    double toAngle( const QPointF &, const QPointF & ) const;

    const QRectF d_rect;
};

pixhawk's avatar
pixhawk committed
328

Bryant's avatar
Bryant committed
329 330
QwtCircleClipper::QwtCircleClipper( const QRectF &r ):
    d_rect( r )
pixhawk's avatar
pixhawk committed
331 332 333
{
}

Bryant's avatar
Bryant committed
334 335
QVector<QwtInterval> QwtCircleClipper::clipCircle(
    const QPointF &pos, double radius ) const
pixhawk's avatar
pixhawk committed
336
{
Bryant's avatar
Bryant committed
337
    QList<QPointF> points;
pixhawk's avatar
pixhawk committed
338
    for ( int edge = 0; edge < NEdges; edge++ )
Bryant's avatar
Bryant committed
339 340 341 342 343 344 345 346 347 348 349 350
        points += cuttingPoints( static_cast<Edge>(edge), pos, radius );

    QVector<QwtInterval> intv;
    if ( points.size() <= 0 )
    {
        QRectF cRect( 0, 0, 2 * radius, 2 * radius );
        cRect.moveCenter( pos );
        if ( d_rect.contains( cRect ) )
            intv += QwtInterval( 0.0, 2 * M_PI );
    }
    else
    {
pixhawk's avatar
pixhawk committed
351 352
        QList<double> angles;
        for ( int i = 0; i < points.size(); i++ )
Bryant's avatar
Bryant committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
            angles += toAngle( pos, points[i] );
        qSort( angles );

        const int in = d_rect.contains( qwtPolar2Pos( pos, radius,
            angles[0] + ( angles[1] - angles[0] ) / 2 ) );

        if ( in )
        {
            for ( int i = 0; i < angles.size() - 1; i += 2 )
                intv += QwtInterval( angles[i], angles[i+1] );
        }
        else
        {
            for ( int i = 1; i < angles.size() - 1; i += 2 )
                intv += QwtInterval( angles[i], angles[i+1] );
            intv += QwtInterval( angles.last(), angles.first() );
pixhawk's avatar
pixhawk committed
369 370 371 372 373 374 375
        }
    }

    return intv;
}

double QwtCircleClipper::toAngle(
Bryant's avatar
Bryant committed
376
    const QPointF &from, const QPointF &to ) const
pixhawk's avatar
pixhawk committed
377 378 379 380
{
    if ( from.x() == to.x() )
        return from.y() <= to.y() ? M_PI / 2.0 : 3 * M_PI / 2.0;

Bryant's avatar
Bryant committed
381
    const double m = qAbs( ( to.y() - from.y() ) / ( to.x() - from.x() ) );
pixhawk's avatar
pixhawk committed
382

Bryant's avatar
Bryant committed
383 384 385
    double angle = qAtan( m );
    if ( to.x() > from.x() )
    {
pixhawk's avatar
pixhawk committed
386 387
        if ( to.y() > from.y() )
            angle = 2 * M_PI - angle;
Bryant's avatar
Bryant committed
388 389 390
    }
    else
    {
pixhawk's avatar
pixhawk committed
391 392 393 394 395 396 397 398 399
        if ( to.y() > from.y() )
            angle = M_PI + angle;
        else
            angle = M_PI - angle;
    }

    return angle;
}

Bryant's avatar
Bryant committed
400 401
QList<QPointF> QwtCircleClipper::cuttingPoints(
    Edge edge, const QPointF &pos, double radius ) const
pixhawk's avatar
pixhawk committed
402
{
Bryant's avatar
Bryant committed
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
    QList<QPointF> points;

    if ( edge == Left || edge == Right )
    {
        const double x = ( edge == Left ) ? d_rect.left() : d_rect.right();
        if ( qAbs( pos.x() - x ) < radius )
        {
            const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.x() - x ) );
            const double m_y1 = pos.y() + off;
            if ( m_y1 >= d_rect.top() && m_y1 <= d_rect.bottom() )
                points += QPointF( x, m_y1 );

            const double m_y2 = pos.y() - off;
            if ( m_y2 >= d_rect.top() && m_y2 <= d_rect.bottom() )
                points += QPointF( x, m_y2 );
pixhawk's avatar
pixhawk committed
418
        }
Bryant's avatar
Bryant committed
419 420 421 422 423 424 425
    }
    else
    {
        const double y = ( edge == Top ) ? d_rect.top() : d_rect.bottom();
        if ( qAbs( pos.y() - y ) < radius )
        {
            const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.y() - y ) );
pixhawk's avatar
pixhawk committed
426
            const double x1 = pos.x() + off;
Bryant's avatar
Bryant committed
427 428 429 430 431 432
            if ( x1 >= d_rect.left() && x1 <= d_rect.right() )
                points += QPointF( x1, y );

            const double m_x2 = pos.x() - off;
            if ( m_x2 >= d_rect.left() && m_x2 <= d_rect.right() )
                points += QPointF( m_x2, y );
pixhawk's avatar
pixhawk committed
433 434 435 436
        }
    }
    return points;
}
437

Bryant's avatar
Bryant committed
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
/*!
   Sutherland-Hodgman polygon clipping

   \param clipRect Clip rectangle
   \param polygon Polygon
   \param closePolygon True, when the polygon is closed

   \return Clipped polygon
*/
QPolygon QwtClipper::clipPolygon(
    const QRectF &clipRect, const QPolygon &polygon, bool closePolygon )
{
    const int minX = qCeil( clipRect.left() );
    const int maxX = qFloor( clipRect.right() );
    const int minY = qCeil( clipRect.top() );
    const int maxY = qFloor( clipRect.bottom() );

    const QRect r( minX, minY, maxX - minX, maxY - minY );

    QwtPolygonClipper<QPolygon, QRect, QPoint, int> clipper( r );
    return clipper.clipPolygon( polygon, closePolygon );
}
/*!
   Sutherland-Hodgman polygon clipping

   \param clipRect Clip rectangle
   \param polygon Polygon
   \param closePolygon True, when the polygon is closed

   \return Clipped polygon
*/
QPolygon QwtClipper::clipPolygon(
    const QRect &clipRect, const QPolygon &polygon, bool closePolygon )
pixhawk's avatar
pixhawk committed
471
{
Bryant's avatar
Bryant committed
472 473
    QwtPolygonClipper<QPolygon, QRect, QPoint, int> clipper( clipRect );
    return clipper.clipPolygon( polygon, closePolygon );
pixhawk's avatar
pixhawk committed
474 475
}

Bryant's avatar
Bryant committed
476 477 478 479 480 481 482 483 484 485 486
/*!
   Sutherland-Hodgman polygon clipping

   \param clipRect Clip rectangle
   \param polygon Polygon
   \param closePolygon True, when the polygon is closed

   \return Clipped polygon
*/
QPolygonF QwtClipper::clipPolygonF(
    const QRectF &clipRect, const QPolygonF &polygon, bool closePolygon )
pixhawk's avatar
pixhawk committed
487
{
Bryant's avatar
Bryant committed
488 489
    QwtPolygonClipper<QPolygonF, QRectF, QPointF, double> clipper( clipRect );
    return clipper.clipPolygon( polygon, closePolygon );
pixhawk's avatar
pixhawk committed
490 491
}

Bryant's avatar
Bryant committed
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
/*!
   Circle clipping

   clipCircle() divides a circle into intervals of angles representing arcs
   of the circle. When the circle is completely inside the clip rectangle
   an interval [0.0, 2 * M_PI] is returned.

   \param clipRect Clip rectangle
   \param center Center of the circle
   \param radius Radius of the circle

   \return Arcs of the circle
*/
QVector<QwtInterval> QwtClipper::clipCircle( const QRectF &clipRect,
    const QPointF &center, double radius )
pixhawk's avatar
pixhawk committed
507
{
Bryant's avatar
Bryant committed
508 509
    QwtCircleClipper clipper( clipRect );
    return clipper.clipCircle( center, radius );
pixhawk's avatar
pixhawk committed
510
}