Qwt User's Guide  6.2.0
qwt_weeding_curve_fitter.cpp
1 /******************************************************************************
2  * Qwt Widget Library
3  * Copyright (C) 1997 Josef Wilgen
4  * Copyright (C) 2002 Uwe Rathmann
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the Qwt License, Version 1.0
8  *****************************************************************************/
9 
10 #include "qwt_weeding_curve_fitter.h"
11 #include "qwt_math.h"
12 
13 #include <qpainterpath.h>
14 #include <qpolygon.h>
15 #include <qstack.h>
16 #include <qvector.h>
17 
18 class QwtWeedingCurveFitter::PrivateData
19 {
20  public:
21  PrivateData()
22  : tolerance( 1.0 )
23  , chunkSize( 0 )
24  {
25  }
26 
27  double tolerance;
28  uint chunkSize;
29 };
30 
31 class QwtWeedingCurveFitter::Line
32 {
33  public:
34  Line( int i1 = 0, int i2 = 0 )
35  : from( i1 )
36  , to( i2 )
37  {
38  }
39 
40  int from;
41  int to;
42 };
43 
51  : QwtCurveFitter( QwtCurveFitter::Polygon )
52 {
53  m_data = new PrivateData;
55 }
56 
59 {
60  delete m_data;
61 }
62 
76 void QwtWeedingCurveFitter::setTolerance( double tolerance )
77 {
78  m_data->tolerance = qwtMaxF( tolerance, 0.0 );
79 }
80 
86 {
87  return m_data->tolerance;
88 }
89 
102 {
103  if ( numPoints > 0 )
104  numPoints = qMax( numPoints, 3U );
105 
106  m_data->chunkSize = numPoints;
107 }
108 
115 {
116  return m_data->chunkSize;
117 }
118 
124 QPolygonF QwtWeedingCurveFitter::fitCurve( const QPolygonF& points ) const
125 {
126  if ( points.isEmpty() )
127  return points;
128 
129  QPolygonF fittedPoints;
130  if ( m_data->chunkSize == 0 )
131  {
132  fittedPoints = simplify( points );
133  }
134  else
135  {
136  for ( int i = 0; i < points.size(); i += m_data->chunkSize )
137  {
138  const QPolygonF p = points.mid( i, m_data->chunkSize );
139  fittedPoints += simplify( p );
140  }
141  }
142 
143  return fittedPoints;
144 }
145 
151 QPainterPath QwtWeedingCurveFitter::fitCurvePath( const QPolygonF& points ) const
152 {
153  QPainterPath path;
154  path.addPolygon( fitCurve( points ) );
155  return path;
156 }
157 
158 QPolygonF QwtWeedingCurveFitter::simplify( const QPolygonF& points ) const
159 {
160  const double toleranceSqr = m_data->tolerance * m_data->tolerance;
161 
162  QStack< Line > stack;
163  stack.reserve( 500 );
164 
165  const QPointF* p = points.data();
166  const int nPoints = points.size();
167 
168  QVector< bool > usePoint( nPoints, false );
169 
170  stack.push( Line( 0, nPoints - 1 ) );
171 
172  while ( !stack.isEmpty() )
173  {
174  const Line r = stack.pop();
175 
176  // initialize line segment
177  const double vecX = p[r.to].x() - p[r.from].x();
178  const double vecY = p[r.to].y() - p[r.from].y();
179 
180  const double vecLength = std::sqrt( vecX * vecX + vecY * vecY );
181 
182  const double unitVecX = ( vecLength != 0.0 ) ? vecX / vecLength : 0.0;
183  const double unitVecY = ( vecLength != 0.0 ) ? vecY / vecLength : 0.0;
184 
185  double maxDistSqr = 0.0;
186  int nVertexIndexMaxDistance = r.from + 1;
187  for ( int i = r.from + 1; i < r.to; i++ )
188  {
189  //compare to anchor
190  const double fromVecX = p[i].x() - p[r.from].x();
191  const double fromVecY = p[i].y() - p[r.from].y();
192 
193  double distToSegmentSqr;
194  if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 )
195  {
196  distToSegmentSqr = fromVecX * fromVecX + fromVecY * fromVecY;
197  }
198  else
199  {
200  const double toVecX = p[i].x() - p[r.to].x();
201  const double toVecY = p[i].y() - p[r.to].y();
202  const double toVecLength = toVecX * toVecX + toVecY * toVecY;
203 
204  const double s = toVecX * ( -unitVecX ) + toVecY * ( -unitVecY );
205  if ( s < 0.0 )
206  {
207  distToSegmentSqr = toVecLength;
208  }
209  else
210  {
211  distToSegmentSqr = std::fabs( toVecLength - s * s );
212  }
213  }
214 
215  if ( maxDistSqr < distToSegmentSqr )
216  {
217  maxDistSqr = distToSegmentSqr;
218  nVertexIndexMaxDistance = i;
219  }
220  }
221  if ( maxDistSqr <= toleranceSqr )
222  {
223  usePoint[r.from] = true;
224  usePoint[r.to] = true;
225  }
226  else
227  {
228  stack.push( Line( r.from, nVertexIndexMaxDistance ) );
229  stack.push( Line( nVertexIndexMaxDistance, r.to ) );
230  }
231  }
232 
233  QPolygonF stripped;
234  for ( int i = 0; i < nPoints; i++ )
235  {
236  if ( usePoint[i] )
237  stripped += p[i];
238  }
239 
240  return stripped;
241 }
Abstract base class for a curve fitter.
virtual QPolygonF fitCurve(const QPolygonF &) const override
QwtWeedingCurveFitter(double tolerance=1.0)
virtual ~QwtWeedingCurveFitter()
Destructor.
virtual QPainterPath fitCurvePath(const QPolygonF &) const override