/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include #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++; for (uint i = 1; i < polygon.size(); i++, pd++) { 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 } enum Edge { Left, Top, Right, Bottom, NEdges }; 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, const QPoint &p2, Edge edge) const; 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, const QwtDoublePoint &p2, Edge edge) const; void addPoint(QwtPolygonF &, uint pos, const QwtDoublePoint &point) const; }; #if QT_VERSION >= 0x040000 class QwtCircleClipper: public QwtDoubleRect { public: QwtCircleClipper(const QwtDoubleRect &r); QwtArray clipCircle( const QwtDoublePoint &, double radius) const; private: QList cuttingPoints( Edge, const QwtDoublePoint &pos, double radius) const; double toAngle(const QwtDoublePoint &, const QwtDoublePoint &) const; }; #endif QwtPolygonClipper::QwtPolygonClipper(const QRect &r): QRect(r) { } inline void QwtPolygonClipper::addPoint( QwtPolygon &pa, uint pos, const QPoint &point) const { if ( uint(pa.size()) <= pos ) 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); for ( uint edge = 1; edge < NEdges; edge++ ) { 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 { 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; } return false; } QPoint QwtPolygonClipper::intersectEdge(const QPoint &p1, const QPoint &p2, Edge edge ) const { int x=0, y=0; double m = 0; const double dy = p2.y() - p1.y(); const double dx = p2.x() - p1.x(); 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; } return QPoint(x,y); } void QwtPolygonClipper::clipEdge(Edge edge, const QwtPolygon &pa, QwtPolygon &cpa) const { if ( pa.count() == 0 ) { 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(); for ( uint i = 1; i < nPoints; i++ ) { const QPoint p2 = pa.point(i); if ( insideEdge(p2, edge) ) { if ( insideEdge(p1, edge) ) addPoint(cpa, count++, p2); else { addPoint(cpa, count++, intersectEdge(p1, p2, edge)); addPoint(cpa, count++, p2); } } else { if ( insideEdge(p1, edge) ) addPoint(cpa, count++, intersectEdge(p1, p2, edge)); } p1 = p2; } cpa.resize(count); } QwtPolygonClipperF::QwtPolygonClipperF(const QwtDoubleRect &r): QwtDoubleRect(r) { } inline void QwtPolygonClipperF::addPoint(QwtPolygonF &pa, uint pos, const QwtDoublePoint &point) const { if ( uint(pa.size()) <= pos ) 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); for ( uint edge = 1; edge < NEdges; edge++ ) { 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 { 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; } return false; } QwtDoublePoint QwtPolygonClipperF::intersectEdge(const QwtDoublePoint &p1, const QwtDoublePoint &p2, Edge edge ) const { double x=0.0, y=0.0; double m = 0; const double dy = p2.y() - p1.y(); const double dx = p2.x() - p1.x(); 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; } return QwtDoublePoint(x,y); } void QwtPolygonClipperF::clipEdge(Edge edge, const QwtPolygonF &pa, QwtPolygonF &cpa) const { if ( pa.count() == 0 ) { 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(); for ( uint i = 1; i < nPoints; i++ ) { const QwtDoublePoint p2 = pa[(int)i]; if ( insideEdge(p2, edge) ) { if ( insideEdge(p1, edge) ) addPoint(cpa, count++, p2); else { addPoint(cpa, count++, intersectEdge(p1, p2, edge)); addPoint(cpa, count++, p2); } } else { 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 QwtCircleClipper::clipCircle( const QwtDoublePoint &pos, double radius) const { QList points; for ( int edge = 0; edge < NEdges; edge++ ) points += cuttingPoints((Edge)edge, pos, radius); QwtArray intv; if ( points.size() <= 0 ) { QwtDoubleRect cRect(0, 0, 2 * radius, 2* radius); cRect.moveCenter(pos); if ( contains(cRect) ) intv += QwtDoubleInterval(0.0, 2 * M_PI); } else { QList angles; for ( int i = 0; i < points.size(); i++ ) angles += toAngle(pos, points[i]); qSort(angles); const int in = contains(qwtPolar2Pos(pos, radius, angles[0] + (angles[1] - angles[0]) / 2)); if ( in ) { for ( int i = 0; i < angles.size() - 1; i += 2) intv += QwtDoubleInterval(angles[i], angles[i+1]); } else { 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); if ( to.x() > from.x() ) { if ( to.y() > from.y() ) angle = 2 * M_PI - angle; } else { if ( to.y() > from.y() ) angle = M_PI + angle; else angle = M_PI - angle; } return angle; } QList QwtCircleClipper::cuttingPoints( Edge edge, const QwtDoublePoint &pos, double radius) const { QList points; if ( edge == Left || edge == Right ) { const double x = (edge == Left) ? left() : right(); if ( qwtAbs(pos.x() - x) < radius ) { 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); } } else { const double y = (edge == Top) ? top() : bottom(); if ( qwtAbs(pos.y() - y) < radius ) { 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 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 QwtClipper::clipCircle( const QwtDoubleRect &clipRect, const QwtDoublePoint ¢er, double radius) { QwtCircleClipper clipper(clipRect); return clipper.clipCircle(center, radius); } #endif