qwt_clipper.cpp 11.8 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 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include <qrect.h>
#include "qwt_math.h"
#include "qwt_clipper.h"

static inline QwtDoubleRect boundingRect(const QwtPolygonF &polygon)
{
#if QT_VERSION < 0x040000
    if (polygon.isEmpty())
        return QwtDoubleRect(0, 0, 0, 0);

    register const QwtDoublePoint *pd = polygon.data();

    double minx, maxx, miny, maxy;
    minx = maxx = pd->x();
    miny = maxy = pd->y();
    pd++;

27
    for (uint i = 1; i < polygon.size(); i++, pd++) {
pixhawk's avatar
pixhawk committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
        if (pd->x() < minx)
            minx = pd->x();
        else if (pd->x() > maxx)
            maxx = pd->x();
        if (pd->y() < miny)
            miny = pd->y();
        else if (pd->y() > maxy)
            maxy = pd->y();
    }
    return QwtDoubleRect(minx, miny, maxx - minx, maxy - miny);
#else
    return polygon.boundingRect();
#endif
}

43 44 45 46 47 48
enum Edge {
    Left,
    Top,
    Right,
    Bottom,
    NEdges
pixhawk's avatar
pixhawk committed
49 50 51 52 53 54 55 56 57 58 59 60 61
};

class QwtPolygonClipper: public QRect
{
public:
    QwtPolygonClipper(const QRect &r);

    QwtPolygon clipPolygon(const QwtPolygon &) const;

private:
    void clipEdge(Edge, const QwtPolygon &, QwtPolygon &) const;
    bool insideEdge(const QPoint &, Edge edge) const;
    QPoint intersectEdge(const QPoint &p1,
62
                         const QPoint &p2, Edge edge) const;
pixhawk's avatar
pixhawk committed
63 64 65 66 67 68 69 70 71 72 73 74 75 76

    void addPoint(QwtPolygon &, uint pos, const QPoint &point) const;
};

class QwtPolygonClipperF: public QwtDoubleRect
{
public:
    QwtPolygonClipperF(const QwtDoubleRect &r);
    QwtPolygonF clipPolygon(const QwtPolygonF &) const;

private:
    void clipEdge(Edge, const QwtPolygonF &, QwtPolygonF &) const;
    bool insideEdge(const QwtDoublePoint &, Edge edge) const;
    QwtDoublePoint intersectEdge(const QwtDoublePoint &p1,
77
                                 const QwtDoublePoint &p2, Edge edge) const;
pixhawk's avatar
pixhawk committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

    void addPoint(QwtPolygonF &, uint pos, const QwtDoublePoint &point) const;
};

#if QT_VERSION >= 0x040000
class QwtCircleClipper: public QwtDoubleRect
{
public:
    QwtCircleClipper(const QwtDoubleRect &r);
    QwtArray<QwtDoubleInterval> clipCircle(
        const QwtDoublePoint &, double radius) const;

private:
    QList<QwtDoublePoint> cuttingPoints(
        Edge, const QwtDoublePoint &pos, double radius) const;
    double toAngle(const QwtDoublePoint &, const QwtDoublePoint &) const;
};
#endif

97 98
QwtPolygonClipper::QwtPolygonClipper(const QRect &r):
    QRect(r)
pixhawk's avatar
pixhawk committed
99 100 101 102 103 104
{
}

inline void QwtPolygonClipper::addPoint(
    QwtPolygon &pa, uint pos, const QPoint &point) const
{
105
    if ( uint(pa.size()) <= pos )
pixhawk's avatar
pixhawk committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
        pa.resize(pos + 5);

    pa.setPoint(pos, point);
}

//! Sutherland-Hodgman polygon clipping
QwtPolygon QwtPolygonClipper::clipPolygon(const QwtPolygon &pa) const
{
    if ( contains( pa.boundingRect() ) )
        return pa;

    QwtPolygon cpa(pa.size());

    clipEdge((Edge)0, pa, cpa);

121
    for ( uint edge = 1; edge < NEdges; edge++ ) {
pixhawk's avatar
pixhawk committed
122 123 124 125 126 127 128 129 130 131 132 133
        const QwtPolygon rpa = cpa;
#if QT_VERSION < 0x040000
        cpa.detach();
#endif
        clipEdge((Edge)edge, rpa, cpa);
    }

    return cpa;
}

bool QwtPolygonClipper::insideEdge(const QPoint &p, Edge edge) const
{
134 135 136 137 138 139 140 141 142 143 144
    switch(edge) {
    case Left:
        return p.x() > left();
    case Top:
        return p.y() > top();
    case Right:
        return p.x() < right();
    case Bottom:
        return p.y() < bottom();
    default:
        break;
pixhawk's avatar
pixhawk committed
145 146 147 148 149
    }

    return false;
}

150 151
QPoint QwtPolygonClipper::intersectEdge(const QPoint &p1,
                                        const QPoint &p2, Edge edge ) const
pixhawk's avatar
pixhawk committed
152 153 154 155 156 157 158
{
    int x=0, y=0;
    double m = 0;

    const double dy = p2.y() - p1.y();
    const double dx = p2.x() - p1.x();

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    switch ( edge ) {
    case Left:
        x = left();
        m = double(qwtAbs(p1.x() - x)) / qwtAbs(dx);
        y = p1.y() + int(dy * m);
        break;
    case Top:
        y = top();
        m = double(qwtAbs(p1.y() - y)) / qwtAbs(dy);
        x = p1.x() + int(dx * m);
        break;
    case Right:
        x = right();
        m = double(qwtAbs(p1.x() - x)) / qwtAbs(dx);
        y = p1.y() + int(dy * m);
        break;
    case Bottom:
        y = bottom();
        m = double(qwtAbs(p1.y() - y)) / qwtAbs(dy);
        x = p1.x() + int(dx * m);
        break;
    default:
        break;
pixhawk's avatar
pixhawk committed
182 183 184 185 186
    }

    return QPoint(x,y);
}

187 188
void QwtPolygonClipper::clipEdge(Edge edge,
                                 const QwtPolygon &pa, QwtPolygon &cpa) const
pixhawk's avatar
pixhawk committed
189
{
190
    if ( pa.count() == 0 ) {
pixhawk's avatar
pixhawk committed
191 192 193 194 195 196 197 198 199 200 201
        cpa.resize(0);
        return;
    }

    unsigned int count = 0;

    QPoint p1 = pa.point(0);
    if ( insideEdge(p1, edge) )
        addPoint(cpa, count++, p1);

    const uint nPoints = pa.size();
202
    for ( uint i = 1; i < nPoints; i++ ) {
pixhawk's avatar
pixhawk committed
203
        const QPoint p2 = pa.point(i);
204
        if ( insideEdge(p2, edge) ) {
pixhawk's avatar
pixhawk committed
205 206
            if ( insideEdge(p1, edge) )
                addPoint(cpa, count++, p2);
207
            else {
pixhawk's avatar
pixhawk committed
208 209 210
                addPoint(cpa, count++, intersectEdge(p1, p2, edge));
                addPoint(cpa, count++, p2);
            }
211
        } else {
pixhawk's avatar
pixhawk committed
212 213 214 215 216 217 218 219
            if ( insideEdge(p1, edge) )
                addPoint(cpa, count++, intersectEdge(p1, p2, edge));
        }
        p1 = p2;
    }
    cpa.resize(count);
}

220 221
QwtPolygonClipperF::QwtPolygonClipperF(const QwtDoubleRect &r):
    QwtDoubleRect(r)
pixhawk's avatar
pixhawk committed
222 223 224 225 226
{
}

inline void QwtPolygonClipperF::addPoint(QwtPolygonF &pa, uint pos, const QwtDoublePoint &point) const
{
227
    if ( uint(pa.size()) <= pos )
pixhawk's avatar
pixhawk committed
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
        pa.resize(pos + 5);

    pa[(int)pos] = point;
}

//! Sutherland-Hodgman polygon clipping
QwtPolygonF QwtPolygonClipperF::clipPolygon(const QwtPolygonF &pa) const
{
    if ( contains( ::boundingRect(pa) ) )
        return pa;

    QwtPolygonF cpa(pa.size());

    clipEdge((Edge)0, pa, cpa);

243
    for ( uint edge = 1; edge < NEdges; edge++ ) {
pixhawk's avatar
pixhawk committed
244 245 246 247 248 249 250 251 252 253 254 255
        const QwtPolygonF rpa = cpa;
#if QT_VERSION < 0x040000
        cpa.detach();
#endif
        clipEdge((Edge)edge, rpa, cpa);
    }

    return cpa;
}

bool QwtPolygonClipperF::insideEdge(const QwtDoublePoint &p, Edge edge) const
{
256 257 258 259 260 261 262 263 264 265 266
    switch(edge) {
    case Left:
        return p.x() > left();
    case Top:
        return p.y() > top();
    case Right:
        return p.x() < right();
    case Bottom:
        return p.y() < bottom();
    default:
        break;
pixhawk's avatar
pixhawk committed
267 268 269 270 271
    }

    return false;
}

272 273
QwtDoublePoint QwtPolygonClipperF::intersectEdge(const QwtDoublePoint &p1,
        const QwtDoublePoint &p2, Edge edge ) const
pixhawk's avatar
pixhawk committed
274 275 276 277 278 279 280
{
    double x=0.0, y=0.0;
    double m = 0;

    const double dy = p2.y() - p1.y();
    const double dx = p2.x() - p1.x();

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
    switch ( edge ) {
    case Left:
        x = left();
        m = double(qwtAbs(p1.x() - x)) / qwtAbs(dx);
        y = p1.y() + int(dy * m);
        break;
    case Top:
        y = top();
        m = double(qwtAbs(p1.y() - y)) / qwtAbs(dy);
        x = p1.x() + int(dx * m);
        break;
    case Right:
        x = right();
        m = double(qwtAbs(p1.x() - x)) / qwtAbs(dx);
        y = p1.y() + int(dy * m);
        break;
    case Bottom:
        y = bottom();
        m = double(qwtAbs(p1.y() - y)) / qwtAbs(dy);
        x = p1.x() + int(dx * m);
        break;
    default:
        break;
pixhawk's avatar
pixhawk committed
304 305 306 307 308
    }

    return QwtDoublePoint(x,y);
}

309 310
void QwtPolygonClipperF::clipEdge(Edge edge,
                                  const QwtPolygonF &pa, QwtPolygonF &cpa) const
pixhawk's avatar
pixhawk committed
311
{
312
    if ( pa.count() == 0 ) {
pixhawk's avatar
pixhawk committed
313 314 315 316 317 318 319 320 321 322 323
        cpa.resize(0);
        return;
    }

    unsigned int count = 0;

    QwtDoublePoint p1 = pa[0];
    if ( insideEdge(p1, edge) )
        addPoint(cpa, count++, p1);

    const uint nPoints = pa.size();
324
    for ( uint i = 1; i < nPoints; i++ ) {
pixhawk's avatar
pixhawk committed
325
        const QwtDoublePoint p2 = pa[(int)i];
326
        if ( insideEdge(p2, edge) ) {
pixhawk's avatar
pixhawk committed
327 328
            if ( insideEdge(p1, edge) )
                addPoint(cpa, count++, p2);
329
            else {
pixhawk's avatar
pixhawk committed
330 331 332
                addPoint(cpa, count++, intersectEdge(p1, p2, edge));
                addPoint(cpa, count++, p2);
            }
333
        } else {
pixhawk's avatar
pixhawk committed
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
            if ( insideEdge(p1, edge) )
                addPoint(cpa, count++, intersectEdge(p1, p2, edge));
        }
        p1 = p2;
    }
    cpa.resize(count);
}

#if QT_VERSION >= 0x040000

QwtCircleClipper::QwtCircleClipper(const QwtDoubleRect &r):
    QwtDoubleRect(r)
{
}

QwtArray<QwtDoubleInterval> QwtCircleClipper::clipCircle(
    const QwtDoublePoint &pos, double radius) const
{
    QList<QwtDoublePoint> points;
    for ( int edge = 0; edge < NEdges; edge++ )
        points += cuttingPoints((Edge)edge, pos, radius);

    QwtArray<QwtDoubleInterval> intv;
357
    if ( points.size() <= 0 ) {
pixhawk's avatar
pixhawk committed
358 359 360 361
        QwtDoubleRect cRect(0, 0, 2 * radius, 2* radius);
        cRect.moveCenter(pos);
        if ( contains(cRect) )
            intv += QwtDoubleInterval(0.0, 2 * M_PI);
362
    } else {
pixhawk's avatar
pixhawk committed
363 364 365 366 367
        QList<double> angles;
        for ( int i = 0; i < points.size(); i++ )
            angles += toAngle(pos, points[i]);
        qSort(angles);

368 369 370
        const int in = contains(qwtPolar2Pos(pos, radius,
                                             angles[0] + (angles[1] - angles[0]) / 2));
        if ( in ) {
pixhawk's avatar
pixhawk committed
371 372
            for ( int i = 0; i < angles.size() - 1; i += 2)
                intv += QwtDoubleInterval(angles[i], angles[i+1]);
373
        } else {
pixhawk's avatar
pixhawk committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
            for ( int i = 1; i < angles.size() - 1; i += 2)
                intv += QwtDoubleInterval(angles[i], angles[i+1]);
            intv += QwtDoubleInterval(angles.last(), angles.first());
        }
    }

    return intv;
}

double QwtCircleClipper::toAngle(
    const QwtDoublePoint &from, const QwtDoublePoint &to) const
{
    if ( from.x() == to.x() )
        return from.y() <= to.y() ? M_PI / 2.0 : 3 * M_PI / 2.0;

    const double m = qwtAbs((to.y() - from.y()) / (to.x() - from.x()) );

    double angle = ::atan(m);
392
    if ( to.x() > from.x() ) {
pixhawk's avatar
pixhawk committed
393 394
        if ( to.y() > from.y() )
            angle = 2 * M_PI - angle;
395
    } else {
pixhawk's avatar
pixhawk committed
396 397 398 399 400 401 402 403 404 405 406 407 408 409
        if ( to.y() > from.y() )
            angle = M_PI + angle;
        else
            angle = M_PI - angle;
    }

    return angle;
}

QList<QwtDoublePoint> QwtCircleClipper::cuttingPoints(
    Edge edge, const QwtDoublePoint &pos, double radius) const
{
    QList<QwtDoublePoint> points;

410
    if ( edge == Left || edge == Right ) {
pixhawk's avatar
pixhawk committed
411
        const double x = (edge == Left) ? left() : right();
412
        if ( qwtAbs(pos.x() - x) < radius ) {
pixhawk's avatar
pixhawk committed
413 414 415 416 417 418 419 420
            const double off = ::sqrt(qwtSqr(radius) - qwtSqr(pos.x() - x));
            const double y1 = pos.y() + off;
            if ( y1 >= top() && y1 <= bottom() )
                points += QwtDoublePoint(x, y1);
            const double y2 = pos.y() - off;
            if ( y2 >= top() && y2 <= bottom() )
                points += QwtDoublePoint(x, y2);
        }
421
    } else {
pixhawk's avatar
pixhawk committed
422
        const double y = (edge == Top) ? top() : bottom();
423
        if ( qwtAbs(pos.y() - y) < radius ) {
pixhawk's avatar
pixhawk committed
424 425 426 427 428 429 430 431 432 433 434 435
            const double off = ::sqrt(qwtSqr(radius) - qwtSqr(pos.y() - y));
            const double x1 = pos.x() + off;
            if ( x1 >= left() && x1 <= right() )
                points += QwtDoublePoint(x1, y);
            const double x2 = pos.x() - off;
            if ( x2 >= left() && x2 <= right() )
                points += QwtDoublePoint(x2, y);
        }
    }
    return points;
}
#endif
436

pixhawk's avatar
pixhawk committed
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
QwtPolygon QwtClipper::clipPolygon(
    const QRect &clipRect, const QwtPolygon &polygon)
{
    QwtPolygonClipper clipper(clipRect);
    return clipper.clipPolygon(polygon);
}

QwtPolygonF QwtClipper::clipPolygonF(
    const QwtDoubleRect &clipRect, const QwtPolygonF &polygon)
{
    QwtPolygonClipperF clipper(clipRect);
    return clipper.clipPolygon(polygon);
}

#if QT_VERSION >= 0x040000
QwtArray<QwtDoubleInterval> QwtClipper::clipCircle(
453
    const QwtDoubleRect &clipRect,
pixhawk's avatar
pixhawk committed
454 455 456 457 458 459
    const QwtDoublePoint &center, double radius)
{
    QwtCircleClipper clipper(clipRect);
    return clipper.clipCircle(center, radius);
}
#endif