00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_spline_local.h"
00011 #include "qwt_spline_parametrization.h"
00012 #include <qmath.h>
00013
00014 static inline bool qwtIsStrictlyMonotonic( double dy1, double dy2 )
00015 {
00016 if ( dy1 == 0.0 || dy2 == 0.0 )
00017 return false;
00018
00019 return ( dy1 > 0.0 ) == ( dy2 > 0.0 );
00020 }
00021
00022 static inline double qwtSlopeLine( const QPointF &p1, const QPointF &p2 )
00023 {
00024
00025 const double dx = p2.x() - p1.x();
00026 return dx ? ( p2.y() - p1.y() ) / dx : 0.0;
00027 }
00028
00029 static inline double qwtSlopeCardinal(
00030 double dx1, double dy1, double s1, double dx2, double dy2, double s2 )
00031 {
00032 Q_UNUSED(s1)
00033 Q_UNUSED(s2)
00034
00035 return ( dy1 + dy2 ) / ( dx1 + dx2 );
00036 }
00037
00038 static inline double qwtSlopeParabolicBlending(
00039 double dx1, double dy1, double s1, double dx2, double dy2, double s2 )
00040 {
00041 Q_UNUSED( dy1 )
00042 Q_UNUSED( dy2 )
00043
00044 return ( dx2 * s1 + dx1 * s2 ) / ( dx1 + dx2 );
00045 }
00046
00047 static inline double qwtSlopePChip(
00048 double dx1, double dy1, double s1, double dx2, double dy2, double s2 )
00049 {
00050 if ( qwtIsStrictlyMonotonic( dy1, dy2 ) )
00051 {
00052 #if 0
00053
00054 const double w1 = ( 3 * dx1 + 3 * dx2 ) / ( 2 * dx1 + 4 * dx2 );
00055 const double w2 = ( 3 * dx1 + 3 * dx2 ) / ( 4 * dx1 + 2 * dx2 );
00056
00057 s1 *= w1;
00058 s2 *= w2;
00059
00060
00061 return 2.0 / ( 1.0 / s1 + 1.0 / s2 );
00062 #endif
00063
00064
00065 const double s12 = ( dy1 + dy2 ) / ( dx1 + dx2 );
00066 return 3.0 * ( s1 * s2 ) / ( s1 + s2 + s12 );
00067 }
00068
00069 return 0.0;
00070 }
00071
00072 namespace QwtSplineLocalP
00073 {
00074 class PathStore
00075 {
00076 public:
00077 inline void init( const QVector<QPointF> & )
00078 {
00079 }
00080
00081 inline void start( const QPointF &p0, double )
00082 {
00083 path.moveTo( p0 );
00084 }
00085
00086 inline void addCubic( const QPointF &p1, double m1,
00087 const QPointF &p2, double m2 )
00088 {
00089 const double dx3 = ( p2.x() - p1.x() ) / 3.0;
00090
00091 path.cubicTo( p1.x() + dx3, p1.y() + m1 * dx3,
00092 p2.x() - dx3, p2.y() - m2 * dx3,
00093 p2.x(), p2.y() );
00094 }
00095
00096 QPainterPath path;
00097 };
00098
00099 class ControlPointsStore
00100 {
00101 public:
00102 inline void init( const QVector<QPointF> &points )
00103 {
00104 if ( points.size() > 0 )
00105 controlPoints.resize( points.size() - 1 );
00106 d_cp = controlPoints.data();
00107 }
00108
00109 inline void start( const QPointF &, double )
00110 {
00111 }
00112
00113 inline void addCubic( const QPointF &p1, double m1,
00114 const QPointF &p2, double m2 )
00115 {
00116 const double dx3 = ( p2.x() - p1.x() ) / 3.0;
00117
00118 QLineF &l = *d_cp++;
00119 l.setLine( p1.x() + dx3, p1.y() + m1 * dx3,
00120 p2.x() - dx3, p2.y() - m2 * dx3 );
00121 }
00122
00123 QVector<QLineF> controlPoints;
00124
00125 private:
00126 QLineF *d_cp;
00127 };
00128
00129 class SlopeStore
00130 {
00131 public:
00132 void init( const QVector<QPointF> &points )
00133 {
00134 slopes.resize( points.size() );
00135 d_m = slopes.data();
00136 }
00137
00138 inline void start( const QPointF &, double m0 )
00139 {
00140 *d_m++ = m0;
00141 }
00142
00143 inline void addCubic( const QPointF &, double,
00144 const QPointF &, double m2 )
00145 {
00146 *d_m++ = m2;
00147 }
00148
00149 QVector<double> slopes;
00150
00151 private:
00152 double *d_m;
00153 };
00154
00155 struct slopeCardinal
00156 {
00157 static inline double value( double dx1, double dy1, double s1,
00158 double dx2, double dy2, double s2 )
00159 {
00160 return qwtSlopeCardinal( dx1, dy1, s1, dx2, dy2, s2 );
00161 }
00162 };
00163
00164 struct slopeParabolicBlending
00165 {
00166 static inline double value( double dx1, double dy1, double s1,
00167 double dx2, double dy2, double s2 )
00168 {
00169 return qwtSlopeParabolicBlending( dx1, dy1, s1, dx2, dy2, s2 );
00170 }
00171 };
00172
00173 struct slopePChip
00174 {
00175 static inline double value( double dx1, double dy1, double s1,
00176 double dx2, double dy2, double s2 )
00177 {
00178 return qwtSlopePChip( dx1, dy1, s1, dx2, dy2, s2 );
00179 }
00180 };
00181 };
00182
00183 template< class Slope >
00184 static inline double qwtSlopeP3(
00185 const QPointF &p1, const QPointF &p2, const QPointF &p3 )
00186 {
00187 const double dx1 = p2.x() - p1.x();
00188 const double dy1 = p2.y() - p1.y();
00189 const double dx2 = p3.x() - p2.x();
00190 const double dy2 = p3.y() - p2.y();
00191
00192 return Slope::value( dx1, dy1, dy1 / dx1, dx2, dy2, dy2 / dx2 );
00193 }
00194
00195 static inline double qwtSlopeAkima( double s1, double s2, double s3, double s4 )
00196 {
00197 if ( ( s1 == s2 ) && ( s3 == s4 ) )
00198 {
00199 return 0.5 * ( s2 + s3 );
00200 }
00201
00202 const double ds12 = qAbs( s2 - s1 );
00203 const double ds34 = qAbs( s4 - s3 );
00204
00205 return ( s2 * ds34 + s3 * ds12 ) / ( ds12 + ds34 );
00206 }
00207
00208 static inline double qwtSlopeAkima( const QPointF &p1, const QPointF &p2,
00209 const QPointF &p3, const QPointF &p4, const QPointF &p5 )
00210 {
00211 const double s1 = qwtSlopeLine( p1, p2 );
00212 const double s2 = qwtSlopeLine( p2, p3 );
00213 const double s3 = qwtSlopeLine( p3, p4 );
00214 const double s4 = qwtSlopeLine( p4, p5 );
00215
00216 return qwtSlopeAkima( s1, s2, s3, s4 );
00217 }
00218
00219 template< class Slope >
00220 static void qwtSplineBoundariesL1(
00221 const QwtSplineLocal *spline, const QVector<QPointF> &points,
00222 double &slopeBegin, double &slopeEnd )
00223 {
00224 const int n = points.size();
00225 const QPointF *p = points.constData();
00226
00227 if ( ( spline->boundaryType() == QwtSpline::PeriodicPolygon )
00228 || ( spline->boundaryType() == QwtSpline::ClosedPolygon ) )
00229 {
00230 const QPointF pn = p[0] - ( p[n-1] - p[n-2] );
00231 slopeBegin = slopeEnd = qwtSlopeP3<Slope>( pn, p[0], p[1] );
00232 }
00233 else
00234 {
00235 const double m2 = qwtSlopeP3<Slope>( p[0], p[1], p[2] );
00236 slopeBegin = spline->slopeAtBeginning( points, m2 );
00237
00238 const double mn2 = qwtSlopeP3<Slope>( p[n-3], p[n-2], p[n-1] );
00239 slopeEnd = spline->slopeAtEnd( points, mn2 );
00240 }
00241 }
00242
00243 template< class SplineStore, class Slope >
00244 static inline SplineStore qwtSplineL1(
00245 const QwtSplineLocal *spline, const QVector<QPointF> &points )
00246 {
00247 const int size = points.size();
00248 const QPointF *p = points.constData();
00249
00250 double slopeBegin, slopeEnd;
00251 qwtSplineBoundariesL1<Slope>( spline, points, slopeBegin, slopeEnd );
00252
00253 double m1 = slopeBegin;
00254
00255 SplineStore store;
00256 store.init( points );
00257 store.start( p[0], m1 );
00258
00259 double dx1 = p[1].x() - p[0].x();
00260 double dy1 = p[1].y() - p[0].y();
00261 double s1 = dy1 / dx1;
00262
00263 for ( int i = 1; i < size - 1; i++ )
00264 {
00265 const double dx2 = p[i+1].x() - p[i].x();
00266 const double dy2 = p[i+1].y() - p[i].y() ;
00267
00268
00269
00270 const double s2 = dy2 / dx2;
00271
00272 const double m2 = Slope::value( dx1, dy1, s1, dx2, dy2, s2 );
00273
00274 store.addCubic( p[i-1], m1, p[i], m2 );
00275
00276 dx1 = dx2;
00277 dy1 = dy2;
00278 s1 = s2;
00279 m1 = m2;
00280 }
00281
00282 store.addCubic( p[size-2], m1, p[size-1], slopeEnd );
00283
00284 return store;
00285 }
00286
00287 static inline void qwtSplineAkimaBoundaries(
00288 const QwtSplineLocal *spline, const QVector<QPointF> &points,
00289 double &slopeBegin, double &slopeEnd )
00290 {
00291 const int n = points.size();
00292 const QPointF *p = points.constData();
00293
00294 if ( ( spline->boundaryType() == QwtSpline::PeriodicPolygon )
00295 || ( spline->boundaryType() == QwtSpline::ClosedPolygon ) )
00296 {
00297 const QPointF p2 = p[0] - ( p[n-1] - p[n-2] );
00298 const QPointF p1 = p2 - ( p[n-2] - p[n-3] );
00299
00300 slopeBegin = slopeEnd = qwtSlopeAkima( p1, p2, p[0], p[1], p[2] );
00301
00302 return;
00303 }
00304
00305 if ( spline->boundaryCondition( QwtSpline::AtBeginning ) == QwtSpline::Clamped1
00306 && spline->boundaryCondition( QwtSpline::AtEnd ) == QwtSpline::Clamped1 )
00307 {
00308 slopeBegin = spline->boundaryValue( QwtSpline::AtBeginning);
00309 slopeEnd = spline->boundaryValue( QwtSpline::AtEnd );
00310
00311 return;
00312 }
00313
00314 if ( n == 3 )
00315 {
00316 const double s1 = qwtSlopeLine( p[0], p[1] );
00317 const double s2 = qwtSlopeLine( p[1], p[2] );
00318 const double m = qwtSlopeAkima( 0.5 * s1, s1, s2, 0.5 * s2 );
00319
00320 slopeBegin = spline->slopeAtBeginning( points, m );
00321 slopeEnd = spline->slopeAtEnd( points, m );
00322 }
00323 else
00324 {
00325 double s[3];
00326
00327 s[0] = qwtSlopeLine( p[0], p[1] );
00328 s[1] = qwtSlopeLine( p[1], p[2] );
00329 s[2] = qwtSlopeLine( p[2], p[3] );
00330
00331 const double m2 = qwtSlopeAkima( 0.5 * s[0], s[0], s[1], s[2] );
00332
00333 slopeBegin = spline->slopeAtBeginning( points, m2 );
00334
00335 s[0] = qwtSlopeLine( p[n-4], p[n-3] );
00336 s[1] = qwtSlopeLine( p[n-3], p[n-2] );
00337 s[2] = qwtSlopeLine( p[n-2], p[n-1] );
00338
00339 const double mn2 = qwtSlopeAkima( s[0], s[1], s[2], 0.5 * s[2] );
00340
00341 slopeEnd = spline->slopeAtEnd( points, mn2 );
00342 }
00343 }
00344
00345 template< class SplineStore >
00346 static inline SplineStore qwtSplineAkima(
00347 const QwtSplineLocal *spline, const QVector<QPointF> &points )
00348 {
00349 const int size = points.size();
00350 const QPointF *p = points.constData();
00351
00352 double slopeBegin, slopeEnd;
00353 qwtSplineAkimaBoundaries( spline, points, slopeBegin, slopeEnd );
00354
00355 double m1 = slopeBegin;
00356
00357 SplineStore store;
00358 store.init( points );
00359 store.start( p[0], m1 );
00360
00361 double s2 = qwtSlopeLine( p[0], p[1] );
00362 double s3 = qwtSlopeLine( p[1], p[2] );
00363 double s1 = 0.5 * s2;
00364
00365 for ( int i = 0; i < size - 3; i++ )
00366 {
00367 const double s4 = qwtSlopeLine( p[i+2], p[i+3] );
00368
00369 const double m2 = qwtSlopeAkima( s1, s2, s3, s4 );
00370 store.addCubic( p[i], m1, p[i+1], m2 );
00371
00372 s1 = s2;
00373 s2 = s3;
00374 s3 = s4;
00375
00376 m1 = m2;
00377 }
00378
00379 const double m2 = qwtSlopeAkima( s1, s2, s3, 0.5 * s3 );
00380
00381 store.addCubic( p[size - 3], m1, p[size - 2], m2 );
00382 store.addCubic( p[size - 2], m2, p[size - 1], slopeEnd );
00383
00384 return store;
00385 }
00386
00387 template< class SplineStore >
00388 static inline SplineStore qwtSplineLocal(
00389 const QwtSplineLocal *spline, const QVector<QPointF> &points )
00390 {
00391 SplineStore store;
00392
00393 const int size = points.size();
00394 if ( size <= 1 )
00395 return store;
00396
00397 if ( size == 2 )
00398 {
00399 const double s0 = qwtSlopeLine( points[0], points[1] );
00400 const double m1 = spline->slopeAtBeginning( points, s0 );
00401 const double m2 = spline->slopeAtEnd( points, s0 );
00402
00403 store.init( points );
00404 store.start( points[0], m1 );
00405 store.addCubic( points[0], m1, points[1], m2 );
00406
00407 return store;
00408 }
00409
00410 switch( spline->type() )
00411 {
00412 case QwtSplineLocal::Cardinal:
00413 {
00414 using namespace QwtSplineLocalP;
00415 store = qwtSplineL1<SplineStore, slopeCardinal>( spline, points );
00416 break;
00417 }
00418 case QwtSplineLocal::ParabolicBlending:
00419 {
00420 using namespace QwtSplineLocalP;
00421 store = qwtSplineL1<SplineStore, slopeParabolicBlending>( spline, points );
00422 break;
00423 }
00424 case QwtSplineLocal::PChip:
00425 {
00426 using namespace QwtSplineLocalP;
00427 store = qwtSplineL1<SplineStore, slopePChip>( spline, points );
00428 break;
00429 }
00430 case QwtSplineLocal::Akima:
00431 {
00432 store = qwtSplineAkima<SplineStore>( spline, points );
00433 break;
00434 }
00435 default:
00436 break;
00437 }
00438
00439 return store;
00440 }
00441
00448 QwtSplineLocal::QwtSplineLocal( Type type ):
00449 d_type( type )
00450 {
00451 setBoundaryCondition( QwtSpline::AtBeginning, QwtSpline::LinearRunout );
00452 setBoundaryValue( QwtSpline::AtBeginning, 0.0 );
00453
00454 setBoundaryCondition( QwtSpline::AtEnd, QwtSpline::LinearRunout );
00455 setBoundaryValue( QwtSpline::AtEnd, 0.0 );
00456 }
00457
00459 QwtSplineLocal::~QwtSplineLocal()
00460 {
00461 }
00462
00466 QwtSplineLocal::Type QwtSplineLocal::type() const
00467 {
00468 return d_type;
00469 }
00470
00480 QPainterPath QwtSplineLocal::painterPath( const QPolygonF &points ) const
00481 {
00482 if ( parametrization()->type() == QwtSplineParametrization::ParameterX )
00483 {
00484 using namespace QwtSplineLocalP;
00485 return qwtSplineLocal<PathStore>( this, points).path;
00486 }
00487
00488 return QwtSplineC1::painterPath( points );
00489 }
00490
00500 QVector<QLineF> QwtSplineLocal::bezierControlLines( const QPolygonF &points ) const
00501 {
00502 if ( parametrization()->type() == QwtSplineParametrization::ParameterX )
00503 {
00504 using namespace QwtSplineLocalP;
00505 return qwtSplineLocal<ControlPointsStore>( this, points ).controlPoints;
00506 }
00507
00508 return QwtSplineC1::bezierControlLines( points );
00509 }
00510
00519 QVector<double> QwtSplineLocal::slopes( const QPolygonF &points ) const
00520 {
00521 using namespace QwtSplineLocalP;
00522 return qwtSplineLocal<SlopeStore>( this, points ).slopes;
00523 }
00524
00535 QVector<QwtSplinePolynomial> QwtSplineLocal::polynomials( const QPolygonF &points ) const
00536 {
00537
00538 return QwtSplineC1::polynomials( points );
00539 }
00540
00550 uint QwtSplineLocal::locality() const
00551 {
00552 switch ( d_type )
00553 {
00554 case Akima:
00555 {
00556
00557 return 2;
00558 }
00559 case Cardinal:
00560 case ParabolicBlending:
00561 case PChip:
00562 {
00563
00564 return 1;
00565 }
00566 }
00567
00568 return QwtSplineC1::locality();
00569 }