Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_weeding_curve_fitter.h"
00011 #include "qwt_math.h"
00012 #include <qstack.h>
00013 #include <qvector.h>
00014
00015 #if QT_VERSION < 0x040601
00016 #define qFabs(x) ::fabs(x)
00017 #endif
00018
00019 class QwtWeedingCurveFitter::PrivateData
00020 {
00021 public:
00022 PrivateData():
00023 tolerance( 1.0 ),
00024 chunkSize( 0 )
00025 {
00026 }
00027
00028 double tolerance;
00029 uint chunkSize;
00030 };
00031
00032 class QwtWeedingCurveFitter::Line
00033 {
00034 public:
00035 Line( int i1 = 0, int i2 = 0 ):
00036 from( i1 ),
00037 to( i2 )
00038 {
00039 }
00040
00041 int from;
00042 int to;
00043 };
00044
00051 QwtWeedingCurveFitter::QwtWeedingCurveFitter( double tolerance ):
00052 QwtCurveFitter( QwtCurveFitter::Polygon )
00053 {
00054 d_data = new PrivateData;
00055 setTolerance( tolerance );
00056 }
00057
00059 QwtWeedingCurveFitter::~QwtWeedingCurveFitter()
00060 {
00061 delete d_data;
00062 }
00063
00077 void QwtWeedingCurveFitter::setTolerance( double tolerance )
00078 {
00079 d_data->tolerance = qMax( tolerance, 0.0 );
00080 }
00081
00086 double QwtWeedingCurveFitter::tolerance() const
00087 {
00088 return d_data->tolerance;
00089 }
00090
00102 void QwtWeedingCurveFitter::setChunkSize( uint numPoints )
00103 {
00104 if ( numPoints > 0 )
00105 numPoints = qMax( numPoints, 3U );
00106
00107 d_data->chunkSize = numPoints;
00108 }
00109
00116 uint QwtWeedingCurveFitter::chunkSize() const
00117 {
00118 return d_data->chunkSize;
00119 }
00120
00126 QPolygonF QwtWeedingCurveFitter::fitCurve( const QPolygonF &points ) const
00127 {
00128 QPolygonF fittedPoints;
00129
00130 if ( d_data->chunkSize == 0 )
00131 {
00132 fittedPoints = simplify( points );
00133 }
00134 else
00135 {
00136 for ( int i = 0; i < points.size(); i += d_data->chunkSize )
00137 {
00138 const QPolygonF p = points.mid( i, d_data->chunkSize );
00139 fittedPoints += simplify( p );
00140 }
00141 }
00142
00143 return fittedPoints;
00144 }
00145
00151 QPainterPath QwtWeedingCurveFitter::fitCurvePath( const QPolygonF &points ) const
00152 {
00153 QPainterPath path;
00154 path.addPolygon( fitCurve( points ) );
00155 return path;
00156 }
00157
00158 QPolygonF QwtWeedingCurveFitter::simplify( const QPolygonF &points ) const
00159 {
00160 const double toleranceSqr = d_data->tolerance * d_data->tolerance;
00161
00162 QStack<Line> stack;
00163 stack.reserve( 500 );
00164
00165 const QPointF *p = points.data();
00166 const int nPoints = points.size();
00167
00168 QVector<bool> usePoint( nPoints, false );
00169
00170 stack.push( Line( 0, nPoints - 1 ) );
00171
00172 while ( !stack.isEmpty() )
00173 {
00174 const Line r = stack.pop();
00175
00176
00177 const double vecX = p[r.to].x() - p[r.from].x();
00178 const double vecY = p[r.to].y() - p[r.from].y();
00179
00180 const double vecLength = qSqrt( vecX * vecX + vecY * vecY );
00181
00182 const double unitVecX = ( vecLength != 0.0 ) ? vecX / vecLength : 0.0;
00183 const double unitVecY = ( vecLength != 0.0 ) ? vecY / vecLength : 0.0;
00184
00185 double maxDistSqr = 0.0;
00186 int nVertexIndexMaxDistance = r.from + 1;
00187 for ( int i = r.from + 1; i < r.to; i++ )
00188 {
00189
00190 const double fromVecX = p[i].x() - p[r.from].x();
00191 const double fromVecY = p[i].y() - p[r.from].y();
00192
00193 double distToSegmentSqr;
00194 if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 )
00195 {
00196 distToSegmentSqr = fromVecX * fromVecX + fromVecY * fromVecY;
00197 }
00198 else
00199 {
00200 const double toVecX = p[i].x() - p[r.to].x();
00201 const double toVecY = p[i].y() - p[r.to].y();
00202 const double toVecLength = toVecX * toVecX + toVecY * toVecY;
00203
00204 const double s = toVecX * ( -unitVecX ) + toVecY * ( -unitVecY );
00205 if ( s < 0.0 )
00206 {
00207 distToSegmentSqr = toVecLength;
00208 }
00209 else
00210 {
00211 distToSegmentSqr = qFabs( toVecLength - s * s );
00212 }
00213 }
00214
00215 if ( maxDistSqr < distToSegmentSqr )
00216 {
00217 maxDistSqr = distToSegmentSqr;
00218 nVertexIndexMaxDistance = i;
00219 }
00220 }
00221 if ( maxDistSqr <= toleranceSqr )
00222 {
00223 usePoint[r.from] = true;
00224 usePoint[r.to] = true;
00225 }
00226 else
00227 {
00228 stack.push( Line( r.from, nVertexIndexMaxDistance ) );
00229 stack.push( Line( nVertexIndexMaxDistance, r.to ) );
00230 }
00231 }
00232
00233 QPolygonF stripped;
00234 for ( int i = 0; i < nPoints; i++ )
00235 {
00236 if ( usePoint[i] )
00237 stripped += p[i];
00238 }
00239
00240 return stripped;
00241 }