You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
460 lines
12 KiB
460 lines
12 KiB
15 years ago
|
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
|
||
|
* Qwt Widget Library
|
||
|
* Copyright (C) 1997 Josef Wilgen
|
||
|
* Copyright (C) 2002 Uwe Rathmann
|
||
14 years ago
|
*
|
||
15 years ago
|
* 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++;
|
||
|
|
||
14 years ago
|
for (uint i = 1; i < polygon.size(); i++, pd++) {
|
||
15 years ago
|
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
|
||
|
}
|
||
|
|
||
14 years ago
|
enum Edge {
|
||
|
Left,
|
||
|
Top,
|
||
|
Right,
|
||
|
Bottom,
|
||
|
NEdges
|
||
15 years ago
|
};
|
||
|
|
||
|
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,
|
||
14 years ago
|
const QPoint &p2, Edge edge) const;
|
||
15 years ago
|
|
||
|
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,
|
||
14 years ago
|
const QwtDoublePoint &p2, Edge edge) const;
|
||
15 years ago
|
|
||
|
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
|
||
|
|
||
14 years ago
|
QwtPolygonClipper::QwtPolygonClipper(const QRect &r):
|
||
|
QRect(r)
|
||
15 years ago
|
{
|
||
|
}
|
||
|
|
||
|
inline void QwtPolygonClipper::addPoint(
|
||
|
QwtPolygon &pa, uint pos, const QPoint &point) const
|
||
|
{
|
||
14 years ago
|
if ( uint(pa.size()) <= pos )
|
||
15 years ago
|
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);
|
||
|
|
||
14 years ago
|
for ( uint edge = 1; edge < NEdges; edge++ ) {
|
||
15 years ago
|
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
|
||
|
{
|
||
14 years ago
|
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;
|
||
15 years ago
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
14 years ago
|
QPoint QwtPolygonClipper::intersectEdge(const QPoint &p1,
|
||
|
const QPoint &p2, Edge edge ) const
|
||
15 years ago
|
{
|
||
|
int x=0, y=0;
|
||
|
double m = 0;
|
||
|
|
||
|
const double dy = p2.y() - p1.y();
|
||
|
const double dx = p2.x() - p1.x();
|
||
|
|
||
14 years ago
|
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;
|
||
15 years ago
|
}
|
||
|
|
||
|
return QPoint(x,y);
|
||
|
}
|
||
|
|
||
14 years ago
|
void QwtPolygonClipper::clipEdge(Edge edge,
|
||
|
const QwtPolygon &pa, QwtPolygon &cpa) const
|
||
15 years ago
|
{
|
||
14 years ago
|
if ( pa.count() == 0 ) {
|
||
15 years ago
|
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();
|
||
14 years ago
|
for ( uint i = 1; i < nPoints; i++ ) {
|
||
15 years ago
|
const QPoint p2 = pa.point(i);
|
||
14 years ago
|
if ( insideEdge(p2, edge) ) {
|
||
15 years ago
|
if ( insideEdge(p1, edge) )
|
||
|
addPoint(cpa, count++, p2);
|
||
14 years ago
|
else {
|
||
15 years ago
|
addPoint(cpa, count++, intersectEdge(p1, p2, edge));
|
||
|
addPoint(cpa, count++, p2);
|
||
|
}
|
||
14 years ago
|
} else {
|
||
15 years ago
|
if ( insideEdge(p1, edge) )
|
||
|
addPoint(cpa, count++, intersectEdge(p1, p2, edge));
|
||
|
}
|
||
|
p1 = p2;
|
||
|
}
|
||
|
cpa.resize(count);
|
||
|
}
|
||
|
|
||
14 years ago
|
QwtPolygonClipperF::QwtPolygonClipperF(const QwtDoubleRect &r):
|
||
|
QwtDoubleRect(r)
|
||
15 years ago
|
{
|
||
|
}
|
||
|
|
||
|
inline void QwtPolygonClipperF::addPoint(QwtPolygonF &pa, uint pos, const QwtDoublePoint &point) const
|
||
|
{
|
||
14 years ago
|
if ( uint(pa.size()) <= pos )
|
||
15 years ago
|
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);
|
||
|
|
||
14 years ago
|
for ( uint edge = 1; edge < NEdges; edge++ ) {
|
||
15 years ago
|
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
|
||
|
{
|
||
14 years ago
|
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;
|
||
15 years ago
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
14 years ago
|
QwtDoublePoint QwtPolygonClipperF::intersectEdge(const QwtDoublePoint &p1,
|
||
|
const QwtDoublePoint &p2, Edge edge ) const
|
||
15 years ago
|
{
|
||
|
double x=0.0, y=0.0;
|
||
|
double m = 0;
|
||
|
|
||
|
const double dy = p2.y() - p1.y();
|
||
|
const double dx = p2.x() - p1.x();
|
||
|
|
||
14 years ago
|
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;
|
||
15 years ago
|
}
|
||
|
|
||
|
return QwtDoublePoint(x,y);
|
||
|
}
|
||
|
|
||
14 years ago
|
void QwtPolygonClipperF::clipEdge(Edge edge,
|
||
|
const QwtPolygonF &pa, QwtPolygonF &cpa) const
|
||
15 years ago
|
{
|
||
14 years ago
|
if ( pa.count() == 0 ) {
|
||
15 years ago
|
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();
|
||
14 years ago
|
for ( uint i = 1; i < nPoints; i++ ) {
|
||
15 years ago
|
const QwtDoublePoint p2 = pa[(int)i];
|
||
14 years ago
|
if ( insideEdge(p2, edge) ) {
|
||
15 years ago
|
if ( insideEdge(p1, edge) )
|
||
|
addPoint(cpa, count++, p2);
|
||
14 years ago
|
else {
|
||
15 years ago
|
addPoint(cpa, count++, intersectEdge(p1, p2, edge));
|
||
|
addPoint(cpa, count++, p2);
|
||
|
}
|
||
14 years ago
|
} else {
|
||
15 years ago
|
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;
|
||
14 years ago
|
if ( points.size() <= 0 ) {
|
||
15 years ago
|
QwtDoubleRect cRect(0, 0, 2 * radius, 2* radius);
|
||
|
cRect.moveCenter(pos);
|
||
|
if ( contains(cRect) )
|
||
|
intv += QwtDoubleInterval(0.0, 2 * M_PI);
|
||
14 years ago
|
} else {
|
||
15 years ago
|
QList<double> angles;
|
||
|
for ( int i = 0; i < points.size(); i++ )
|
||
|
angles += toAngle(pos, points[i]);
|
||
|
qSort(angles);
|
||
|
|
||
14 years ago
|
const int in = contains(qwtPolar2Pos(pos, radius,
|
||
|
angles[0] + (angles[1] - angles[0]) / 2));
|
||
|
if ( in ) {
|
||
15 years ago
|
for ( int i = 0; i < angles.size() - 1; i += 2)
|
||
|
intv += QwtDoubleInterval(angles[i], angles[i+1]);
|
||
14 years ago
|
} else {
|
||
15 years ago
|
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);
|
||
14 years ago
|
if ( to.x() > from.x() ) {
|
||
15 years ago
|
if ( to.y() > from.y() )
|
||
|
angle = 2 * M_PI - angle;
|
||
14 years ago
|
} else {
|
||
15 years ago
|
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;
|
||
|
|
||
14 years ago
|
if ( edge == Left || edge == Right ) {
|
||
15 years ago
|
const double x = (edge == Left) ? left() : right();
|
||
14 years ago
|
if ( qwtAbs(pos.x() - x) < radius ) {
|
||
15 years ago
|
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);
|
||
|
}
|
||
14 years ago
|
} else {
|
||
15 years ago
|
const double y = (edge == Top) ? top() : bottom();
|
||
14 years ago
|
if ( qwtAbs(pos.y() - y) < radius ) {
|
||
15 years ago
|
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
|
||
14 years ago
|
|
||
15 years ago
|
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(
|
||
14 years ago
|
const QwtDoubleRect &clipRect,
|
||
15 years ago
|
const QwtDoublePoint ¢er, double radius)
|
||
|
{
|
||
|
QwtCircleClipper clipper(clipRect);
|
||
|
return clipper.clipCircle(center, radius);
|
||
|
}
|
||
|
#endif
|