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
}