qwt_spline_local.cpp
Go to the documentation of this file.
00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
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         // weighting the slopes by the dx1/dx2
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         // harmonic mean ( see https://en.wikipedia.org/wiki/Pythagorean_means )
00061         return 2.0 / ( 1.0 / s1 + 1.0 / s2 );
00062 #endif
00063         // the same as above - but faster
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         // cardinal spline doesn't need the line slopes, but 
00269         // the compiler will eliminate pointless calculations
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     // Polynomial store -> TODO
00538     return QwtSplineC1::polynomials( points );
00539 }
00540 
00550 uint QwtSplineLocal::locality() const
00551 {
00552     switch ( d_type )
00553     {
00554         case Akima:
00555         {
00556             // polynoms: 2 left, 2 right
00557             return 2;
00558         }
00559         case Cardinal:
00560         case ParabolicBlending:
00561         case PChip:
00562         {
00563             // polynoms: 1 left, 1 right
00564             return 1;
00565         }
00566     }
00567 
00568     return QwtSplineC1::locality(); 
00569 }


plotjuggler
Author(s): Davide Faconti
autogenerated on Fri Sep 1 2017 02:41:56