00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_spline_pleasing.h"
00011 #include "qwt_spline_parametrization.h"
00012
00013 static inline double qwtChordalLength( const QPointF &point1, const QPointF &point2 )
00014 {
00015 const double dx = point2.x() - point1.x();
00016 const double dy = point2.y() - point1.y();
00017
00018 return qSqrt( dx * dx + dy * dy );
00019 }
00020
00021 template< class Param >
00022 static QPointF qwtVector( Param param,
00023 const QPointF &p1, const QPointF &p2 )
00024 {
00025 return ( p2 - p1 ) / param( p1, p2 );
00026 }
00027
00028 template< class Param >
00029 static QPointF qwtVectorCardinal( Param param,
00030 const QPointF &p1, const QPointF &p2, const QPointF &p3 )
00031 {
00032 const double t1 = param( p1, p2 );
00033 const double t2 = param( p2, p3 );
00034
00035 return t2 * ( p3 - p1 ) / ( t1 + t2 );
00036 }
00037
00038 namespace QwtSplinePleasingP
00039 {
00040 struct Tension
00041 {
00042 double t1;
00043 double t2;
00044 };
00045
00046 struct param
00047 {
00048 param( const QwtSplineParametrization *p ):
00049 parameter( p )
00050 {
00051 }
00052
00053 inline double operator()( const QPointF &p1, const QPointF &p2 ) const
00054 {
00055 return parameter->valueIncrement( p1, p2 );
00056 }
00057
00058 const QwtSplineParametrization *parameter;
00059 };
00060
00061 struct paramUniform
00062 {
00063 inline double operator()( const QPointF &p1, const QPointF &p2 ) const
00064 {
00065 return QwtSplineParametrization::valueIncrementUniform( p1, p2 );
00066 }
00067 };
00068
00069 class PathStore
00070 {
00071 public:
00072 inline void init( int )
00073 {
00074 }
00075
00076 inline void start( const QPointF &p0 )
00077 {
00078 path.moveTo( p0 );
00079 }
00080
00081 inline void addCubic( const QPointF &cp1,
00082 const QPointF &cp2, const QPointF &p2 )
00083 {
00084 path.cubicTo( cp1, cp2, p2 );
00085 }
00086
00087 QPainterPath path;
00088 };
00089
00090 class ControlPointsStore
00091 {
00092 public:
00093 inline ControlPointsStore():
00094 d_cp( NULL )
00095 {
00096 }
00097
00098 inline void init( int size )
00099 {
00100 controlPoints.resize( size );
00101 d_cp = controlPoints.data();
00102 }
00103
00104 inline void start( const QPointF & )
00105 {
00106 }
00107
00108 inline void addCubic( const QPointF &cp1,
00109 const QPointF &cp2, const QPointF & )
00110 {
00111 QLineF &l = *d_cp++;
00112 l.setPoints( cp1, cp2 );
00113 }
00114
00115 QVector<QLineF> controlPoints;
00116
00117 private:
00118 QLineF* d_cp;
00119 };
00120 }
00121
00122 static inline QwtSplinePleasingP::Tension qwtTensionPleasing(
00123 double d13, double d23, double d24,
00124 const QPointF &p1, const QPointF &p2,
00125 const QPointF &p3, const QPointF &p4 )
00126 {
00127 QwtSplinePleasingP::Tension tension;
00128
00129 const bool b1 = ( d13 / 3.0 ) < d23;
00130 const bool b2 = ( d24 / 3.0 ) < d23;
00131
00132 if ( b1 )
00133 {
00134 if ( b2 )
00135 {
00136 tension.t1 = ( p1 != p2 ) ? ( 1.0 / 3.0 ) : ( 2.0 / 3.0 );
00137 tension.t2 = ( p3 != p4 ) ? ( 1.0 / 3.0 ) : ( 2.0 / 3.0 );
00138 }
00139 else
00140 {
00141 tension.t1 = tension.t2 = d23 / d24;
00142 }
00143 }
00144 else
00145 {
00146 if ( b2 )
00147 {
00148 tension.t1 = tension.t2 = d23 / d13;
00149 }
00150 else
00151 {
00152 tension.t1 = d23 / d13;
00153 tension.t2 = d23 / d24;
00154 }
00155 }
00156
00157 return tension;
00158 }
00159
00160 template< class SplineStore, class Param >
00161 static SplineStore qwtSplinePathPleasing( const QPolygonF &points,
00162 bool isClosed, Param param )
00163 {
00164 using namespace QwtSplinePleasingP;
00165
00166 const int size = points.size();
00167
00168 const QPointF *p = points.constData();
00169
00170 SplineStore store;
00171 store.init( isClosed ? size : size - 1 );
00172 store.start( p[0] );
00173
00174 double d13;
00175 QPointF vec1;
00176
00177 if ( isClosed )
00178 {
00179 d13 = qwtChordalLength(p[0], p[2]);
00180
00181 const Tension t0 = qwtTensionPleasing(
00182 qwtChordalLength( p[size-1], p[1]), qwtChordalLength(p[0], p[1]),
00183 d13, p[size-1], p[0], p[1], p[2] );
00184
00185 const QPointF vec0 = qwtVectorCardinal<Param>( param, p[size-1], p[0], p[1] );
00186 vec1 = qwtVectorCardinal<Param>( param, p[0], p[1], p[2] );
00187
00188 store.addCubic( p[0] + vec0 * t0.t1, p[1] - vec1 * t0.t2, p[1] );
00189 }
00190 else
00191 {
00192 d13 = qwtChordalLength(p[0], p[2]);
00193
00194 const Tension t0 = qwtTensionPleasing(
00195 qwtChordalLength( p[0], p[1]), qwtChordalLength(p[0], p[1]),
00196 d13, p[0], p[0], p[1], p[2] );
00197
00198 const QPointF vec0 = 0.5 * qwtVector<Param>( param, p[0], p[1] );
00199 vec1 = qwtVectorCardinal<Param>( param, p[0], p[1], p[2] );
00200
00201 store.addCubic( p[0] + vec0 * t0.t1, p[1] - vec1 * t0.t2, p[1] );
00202 }
00203
00204 for ( int i = 1; i < size - 2; i++ )
00205 {
00206 const double d23 = qwtChordalLength( p[i], p[i+1] );
00207 const double d24 = qwtChordalLength( p[i], p[i+2] );
00208
00209 const QPointF vec2 = qwtVectorCardinal<Param>( param, p[i], p[i+1], p[i+2] );
00210
00211 const Tension t = qwtTensionPleasing(
00212 d13, d23, d24, p[i-1], p[i], p[i+1], p[i+2] );
00213
00214 store.addCubic( p[i] + vec1 * t.t1, p[i+1] - vec2 * t.t2, p[i+1] );
00215
00216 d13 = d24;
00217 vec1 = vec2;
00218 }
00219
00220 if ( isClosed )
00221 {
00222 const double d24 = qwtChordalLength( p[size-2], p[0] );
00223
00224 const Tension tn = qwtTensionPleasing(
00225 d13, qwtChordalLength( p[size-2], p[size-1] ), d24,
00226 p[size-3], p[size-2], p[size-1], p[0] );
00227
00228 const QPointF vec2 = qwtVectorCardinal<Param>( param, p[size-2], p[size-1], p[0] );
00229 store.addCubic( p[size-2] + vec1 * tn.t1, p[size-1] - vec2 * tn.t2, p[size-1] );
00230
00231 const double d34 = qwtChordalLength( p[size-1], p[0] );
00232 const double d35 = qwtChordalLength( p[size-1], p[1] );
00233
00234 const Tension tc = qwtTensionPleasing( d24, d34, d35, p[size-2], p[size-1], p[0], p[1] );
00235
00236 const QPointF vec3 = qwtVectorCardinal<Param>( param, p[size-1], p[0], p[1] );
00237
00238 store.addCubic( p[size-1] + vec2 * tc.t1, p[0] - vec3 * tc.t2, p[0] );
00239 }
00240 else
00241 {
00242 const double d24 = qwtChordalLength( p[size-2], p[size-1] );
00243
00244 const Tension tn = qwtTensionPleasing(
00245 d13, qwtChordalLength( p[size-2], p[size-1] ), d24,
00246 p[size-3], p[size-2], p[size-1], p[size-1] );
00247
00248 const QPointF vec2 = 0.5 * qwtVector<Param>( param, p[size-2], p[size-1] );
00249 store.addCubic( p[size-2] + vec1 * tn.t1, p[size-1] - vec2 * tn.t2, p[size-1] );
00250 }
00251
00252 return store;
00253 }
00254
00263 QwtSplinePleasing::QwtSplinePleasing()
00264 {
00265 setParametrization( QwtSplineParametrization::ParameterUniform );
00266 }
00267
00269 QwtSplinePleasing::~QwtSplinePleasing()
00270 {
00271 }
00272
00274 uint QwtSplinePleasing::locality() const
00275 {
00276 return 2;
00277 }
00278
00288 QPainterPath QwtSplinePleasing::painterPath( const QPolygonF &points ) const
00289 {
00290 const int size = points.size();
00291 if ( size <= 2 )
00292 return QwtSplineG1::painterPath( points );
00293
00294 const bool isClosing = ( boundaryType() == QwtSpline::ClosedPolygon );
00295
00296 using namespace QwtSplinePleasingP;
00297
00298 PathStore store;
00299 if ( parametrization()->type() == QwtSplineParametrization::ParameterUniform )
00300 {
00301 store = qwtSplinePathPleasing<PathStore>( points,
00302 isClosing, paramUniform() );
00303 }
00304 else
00305 {
00306 store = qwtSplinePathPleasing<PathStore>( points,
00307 isClosing, param( parametrization() ) );
00308 }
00309
00310 if ( isClosing )
00311 store.path.closeSubpath();
00312
00313 return store.path;
00314 }
00315
00325 QVector<QLineF> QwtSplinePleasing::bezierControlLines(
00326 const QPolygonF &points ) const
00327 {
00328 const int size = points.size();
00329 if ( size <= 2 )
00330 return QVector<QLineF>();
00331
00332 const bool isClosing = ( boundaryType() == QwtSpline::ClosedPolygon );
00333
00334 using namespace QwtSplinePleasingP;
00335
00336 ControlPointsStore store;
00337 if ( parametrization()->type() == QwtSplineParametrization::ParameterUniform )
00338 {
00339 store = qwtSplinePathPleasing<ControlPointsStore>( points,
00340 isClosing, paramUniform() );
00341 }
00342 else
00343 {
00344 store = qwtSplinePathPleasing<ControlPointsStore>( points,
00345 isClosing, param( parametrization() ) );
00346 }
00347
00348 return store.controlPoints;
00349 }