| 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_spline_pleasing.h"
|
|---|
| 11 | #include "qwt_spline_parametrization.h"
|
|---|
| 12 |
|
|---|
| 13 | #include <qpainterpath.h>
|
|---|
| 14 |
|
|---|
| 15 | static inline double qwtChordalLength( const QPointF& point1, const QPointF& point2 )
|
|---|
| 16 | {
|
|---|
| 17 | const double dx = point2.x() - point1.x();
|
|---|
| 18 | const double dy = point2.y() - point1.y();
|
|---|
| 19 |
|
|---|
| 20 | return std::sqrt( dx * dx + dy * dy );
|
|---|
| 21 | }
|
|---|
| 22 |
|
|---|
| 23 | template< class Param >
|
|---|
| 24 | static QPointF qwtVector( Param param,
|
|---|
| 25 | const QPointF& p1, const QPointF& p2 )
|
|---|
| 26 | {
|
|---|
| 27 | return ( p2 - p1 ) / param( p1, p2 );
|
|---|
| 28 | }
|
|---|
| 29 |
|
|---|
| 30 | template< class Param >
|
|---|
| 31 | static QPointF qwtVectorCardinal( Param param,
|
|---|
| 32 | const QPointF& p1, const QPointF& p2, const QPointF& p3 )
|
|---|
| 33 | {
|
|---|
| 34 | const double t1 = param( p1, p2 );
|
|---|
| 35 | const double t2 = param( p2, p3 );
|
|---|
| 36 |
|
|---|
| 37 | return t2 * ( p3 - p1 ) / ( t1 + t2 );
|
|---|
| 38 | }
|
|---|
| 39 |
|
|---|
| 40 | namespace QwtSplinePleasingP
|
|---|
| 41 | {
|
|---|
| 42 | struct Tension
|
|---|
| 43 | {
|
|---|
| 44 | double t1;
|
|---|
| 45 | double t2;
|
|---|
| 46 | };
|
|---|
| 47 |
|
|---|
| 48 | struct param
|
|---|
| 49 | {
|
|---|
| 50 | param( const QwtSplineParametrization* p ):
|
|---|
| 51 | parameter( p )
|
|---|
| 52 | {
|
|---|
| 53 | }
|
|---|
| 54 |
|
|---|
| 55 | inline double operator()( const QPointF& p1, const QPointF& p2 ) const
|
|---|
| 56 | {
|
|---|
| 57 | return parameter->valueIncrement( p1, p2 );
|
|---|
| 58 | }
|
|---|
| 59 |
|
|---|
| 60 | const QwtSplineParametrization* parameter;
|
|---|
| 61 | };
|
|---|
| 62 |
|
|---|
| 63 | struct paramUniform
|
|---|
| 64 | {
|
|---|
| 65 | inline double operator()( const QPointF& p1, const QPointF& p2 ) const
|
|---|
| 66 | {
|
|---|
| 67 | return QwtSplineParametrization::valueIncrementUniform( p1, p2 );
|
|---|
| 68 | }
|
|---|
| 69 | };
|
|---|
| 70 |
|
|---|
| 71 | class PathStore
|
|---|
| 72 | {
|
|---|
| 73 | public:
|
|---|
| 74 | inline void init( int )
|
|---|
| 75 | {
|
|---|
| 76 | }
|
|---|
| 77 |
|
|---|
| 78 | inline void start( const QPointF& p0 )
|
|---|
| 79 | {
|
|---|
| 80 | path.moveTo( p0 );
|
|---|
| 81 | }
|
|---|
| 82 |
|
|---|
| 83 | inline void addCubic( const QPointF& cp1,
|
|---|
| 84 | const QPointF& cp2, const QPointF& p2 )
|
|---|
| 85 | {
|
|---|
| 86 | path.cubicTo( cp1, cp2, p2 );
|
|---|
| 87 | }
|
|---|
| 88 |
|
|---|
| 89 | QPainterPath path;
|
|---|
| 90 | };
|
|---|
| 91 |
|
|---|
| 92 | class ControlPointsStore
|
|---|
| 93 | {
|
|---|
| 94 | public:
|
|---|
| 95 | inline ControlPointsStore():
|
|---|
| 96 | m_cp( NULL )
|
|---|
| 97 | {
|
|---|
| 98 | }
|
|---|
| 99 |
|
|---|
| 100 | inline void init( int size )
|
|---|
| 101 | {
|
|---|
| 102 | controlPoints.resize( size );
|
|---|
| 103 | m_cp = controlPoints.data();
|
|---|
| 104 | }
|
|---|
| 105 |
|
|---|
| 106 | inline void start( const QPointF& )
|
|---|
| 107 | {
|
|---|
| 108 | }
|
|---|
| 109 |
|
|---|
| 110 | inline void addCubic( const QPointF& cp1,
|
|---|
| 111 | const QPointF& cp2, const QPointF& )
|
|---|
| 112 | {
|
|---|
| 113 | QLineF& l = *m_cp++;
|
|---|
| 114 | l.setPoints( cp1, cp2 );
|
|---|
| 115 | }
|
|---|
| 116 |
|
|---|
| 117 | QVector< QLineF > controlPoints;
|
|---|
| 118 |
|
|---|
| 119 | private:
|
|---|
| 120 | QLineF* m_cp;
|
|---|
| 121 | };
|
|---|
| 122 | }
|
|---|
| 123 |
|
|---|
| 124 | static inline QwtSplinePleasingP::Tension qwtTensionPleasing(
|
|---|
| 125 | double d13, double d23, double d24,
|
|---|
| 126 | const QPointF& p1, const QPointF& p2,
|
|---|
| 127 | const QPointF& p3, const QPointF& p4 )
|
|---|
| 128 | {
|
|---|
| 129 | QwtSplinePleasingP::Tension tension;
|
|---|
| 130 |
|
|---|
| 131 | const bool b1 = ( d13 / 3.0 ) < d23;
|
|---|
| 132 | const bool b2 = ( d24 / 3.0 ) < d23;
|
|---|
| 133 |
|
|---|
| 134 | if ( b1 )
|
|---|
| 135 | {
|
|---|
| 136 | if ( b2 )
|
|---|
| 137 | {
|
|---|
| 138 | tension.t1 = ( p1 != p2 ) ? ( 1.0 / 3.0 ) : ( 2.0 / 3.0 );
|
|---|
| 139 | tension.t2 = ( p3 != p4 ) ? ( 1.0 / 3.0 ) : ( 2.0 / 3.0 );
|
|---|
| 140 | }
|
|---|
| 141 | else
|
|---|
| 142 | {
|
|---|
| 143 | tension.t1 = tension.t2 = d23 / d24;
|
|---|
| 144 | }
|
|---|
| 145 | }
|
|---|
| 146 | else
|
|---|
| 147 | {
|
|---|
| 148 | if ( b2 )
|
|---|
| 149 | {
|
|---|
| 150 | tension.t1 = tension.t2 = d23 / d13;
|
|---|
| 151 | }
|
|---|
| 152 | else
|
|---|
| 153 | {
|
|---|
| 154 | tension.t1 = d23 / d13;
|
|---|
| 155 | tension.t2 = d23 / d24;
|
|---|
| 156 | }
|
|---|
| 157 | }
|
|---|
| 158 |
|
|---|
| 159 | return tension;
|
|---|
| 160 | }
|
|---|
| 161 |
|
|---|
| 162 | template< class SplineStore, class Param >
|
|---|
| 163 | static SplineStore qwtSplinePathPleasing( const QPolygonF& points,
|
|---|
| 164 | bool isClosed, Param param )
|
|---|
| 165 | {
|
|---|
| 166 | using namespace QwtSplinePleasingP;
|
|---|
| 167 |
|
|---|
| 168 | const int size = points.size();
|
|---|
| 169 |
|
|---|
| 170 | const QPointF* p = points.constData();
|
|---|
| 171 |
|
|---|
| 172 | SplineStore store;
|
|---|
| 173 | store.init( isClosed ? size : size - 1 );
|
|---|
| 174 | store.start( p[0] );
|
|---|
| 175 |
|
|---|
| 176 | double d13;
|
|---|
| 177 | QPointF vec1;
|
|---|
| 178 |
|
|---|
| 179 | if ( isClosed )
|
|---|
| 180 | {
|
|---|
| 181 | d13 = qwtChordalLength(p[0], p[2]);
|
|---|
| 182 |
|
|---|
| 183 | const Tension t0 = qwtTensionPleasing(
|
|---|
| 184 | qwtChordalLength( p[size - 1], p[1]), qwtChordalLength(p[0], p[1]),
|
|---|
| 185 | d13, p[size - 1], p[0], p[1], p[2] );
|
|---|
| 186 |
|
|---|
| 187 | const QPointF vec0 = qwtVectorCardinal< Param >( param, p[size - 1], p[0], p[1] );
|
|---|
| 188 | vec1 = qwtVectorCardinal< Param >( param, p[0], p[1], p[2] );
|
|---|
| 189 |
|
|---|
| 190 | store.addCubic( p[0] + vec0 * t0.t1, p[1] - vec1 * t0.t2, p[1] );
|
|---|
| 191 | }
|
|---|
| 192 | else
|
|---|
| 193 | {
|
|---|
| 194 | d13 = qwtChordalLength(p[0], p[2]);
|
|---|
| 195 |
|
|---|
| 196 | const Tension t0 = qwtTensionPleasing(
|
|---|
| 197 | qwtChordalLength( p[0], p[1]), qwtChordalLength(p[0], p[1]),
|
|---|
| 198 | d13, p[0], p[0], p[1], p[2] );
|
|---|
| 199 |
|
|---|
| 200 | const QPointF vec0 = 0.5 * qwtVector< Param >( param, p[0], p[1] );
|
|---|
| 201 | vec1 = qwtVectorCardinal< Param >( param, p[0], p[1], p[2] );
|
|---|
| 202 |
|
|---|
| 203 | store.addCubic( p[0] + vec0 * t0.t1, p[1] - vec1 * t0.t2, p[1] );
|
|---|
| 204 | }
|
|---|
| 205 |
|
|---|
| 206 | for ( int i = 1; i < size - 2; i++ )
|
|---|
| 207 | {
|
|---|
| 208 | const double d23 = qwtChordalLength( p[i], p[i + 1] );
|
|---|
| 209 | const double d24 = qwtChordalLength( p[i], p[i + 2] );
|
|---|
| 210 |
|
|---|
| 211 | const QPointF vec2 = qwtVectorCardinal< Param >( param, p[i], p[i + 1], p[i + 2] );
|
|---|
| 212 |
|
|---|
| 213 | const Tension t = qwtTensionPleasing(
|
|---|
| 214 | d13, d23, d24, p[i - 1], p[i], p[i + 1], p[i + 2] );
|
|---|
| 215 |
|
|---|
| 216 | store.addCubic( p[i] + vec1 * t.t1, p[i + 1] - vec2 * t.t2, p[i + 1] );
|
|---|
| 217 |
|
|---|
| 218 | d13 = d24;
|
|---|
| 219 | vec1 = vec2;
|
|---|
| 220 | }
|
|---|
| 221 |
|
|---|
| 222 | if ( isClosed )
|
|---|
| 223 | {
|
|---|
| 224 | const double d24 = qwtChordalLength( p[size - 2], p[0] );
|
|---|
| 225 |
|
|---|
| 226 | const Tension tn = qwtTensionPleasing(
|
|---|
| 227 | d13, qwtChordalLength( p[size - 2], p[size - 1] ), d24,
|
|---|
| 228 | p[size - 3], p[size - 2], p[size - 1], p[0] );
|
|---|
| 229 |
|
|---|
| 230 | const QPointF vec2 = qwtVectorCardinal< Param >( param, p[size - 2], p[size - 1], p[0] );
|
|---|
| 231 | store.addCubic( p[size - 2] + vec1 * tn.t1, p[size - 1] - vec2 * tn.t2, p[size - 1] );
|
|---|
| 232 |
|
|---|
| 233 | const double d34 = qwtChordalLength( p[size - 1], p[0] );
|
|---|
| 234 | const double d35 = qwtChordalLength( p[size - 1], p[1] );
|
|---|
| 235 |
|
|---|
| 236 | const Tension tc = qwtTensionPleasing( d24, d34, d35, p[size - 2], p[size - 1], p[0], p[1] );
|
|---|
| 237 |
|
|---|
| 238 | const QPointF vec3 = qwtVectorCardinal< Param >( param, p[size - 1], p[0], p[1] );
|
|---|
| 239 |
|
|---|
| 240 | store.addCubic( p[size - 1] + vec2 * tc.t1, p[0] - vec3 * tc.t2, p[0] );
|
|---|
| 241 | }
|
|---|
| 242 | else
|
|---|
| 243 | {
|
|---|
| 244 | const double d24 = qwtChordalLength( p[size - 2], p[size - 1] );
|
|---|
| 245 |
|
|---|
| 246 | const Tension tn = qwtTensionPleasing(
|
|---|
| 247 | d13, qwtChordalLength( p[size - 2], p[size - 1] ), d24,
|
|---|
| 248 | p[size - 3], p[size - 2], p[size - 1], p[size - 1] );
|
|---|
| 249 |
|
|---|
| 250 | const QPointF vec2 = 0.5 * qwtVector< Param >( param, p[size - 2], p[size - 1] );
|
|---|
| 251 | store.addCubic( p[size - 2] + vec1 * tn.t1, p[size - 1] - vec2 * tn.t2, p[size - 1] );
|
|---|
| 252 | }
|
|---|
| 253 |
|
|---|
| 254 | return store;
|
|---|
| 255 | }
|
|---|
| 256 |
|
|---|
| 257 | /*!
|
|---|
| 258 | \brief Constructor
|
|---|
| 259 |
|
|---|
| 260 | The default setting is a non closing spline with uniform parametrization.
|
|---|
| 261 | ( QwtSplineParametrization::ParameterUniform ).
|
|---|
| 262 |
|
|---|
| 263 | \sa QwtSpline::setParametrization(), QwtSpline::setBoundaryType()
|
|---|
| 264 | */
|
|---|
| 265 | QwtSplinePleasing::QwtSplinePleasing()
|
|---|
| 266 | {
|
|---|
| 267 | setParametrization( QwtSplineParametrization::ParameterUniform );
|
|---|
| 268 | }
|
|---|
| 269 |
|
|---|
| 270 | //! Destructor
|
|---|
| 271 | QwtSplinePleasing::~QwtSplinePleasing()
|
|---|
| 272 | {
|
|---|
| 273 | }
|
|---|
| 274 |
|
|---|
| 275 | //! \return 2
|
|---|
| 276 | uint QwtSplinePleasing::locality() const
|
|---|
| 277 | {
|
|---|
| 278 | return 2;
|
|---|
| 279 | }
|
|---|
| 280 |
|
|---|
| 281 | /*!
|
|---|
| 282 | \brief Interpolate a curve with Bezier curves
|
|---|
| 283 |
|
|---|
| 284 | Interpolates a polygon piecewise with cubic Bezier curves
|
|---|
| 285 | and returns them as QPainterPath.
|
|---|
| 286 |
|
|---|
| 287 | \param points Control points
|
|---|
| 288 | \return QPainterPath Painter path, that can be rendered by QPainter
|
|---|
| 289 | */
|
|---|
| 290 | QPainterPath QwtSplinePleasing::painterPath( const QPolygonF& points ) const
|
|---|
| 291 | {
|
|---|
| 292 | const int size = points.size();
|
|---|
| 293 | if ( size <= 2 )
|
|---|
| 294 | return QwtSplineG1::painterPath( points );
|
|---|
| 295 |
|
|---|
| 296 | const bool isClosing = ( boundaryType() == QwtSpline::ClosedPolygon );
|
|---|
| 297 |
|
|---|
| 298 | using namespace QwtSplinePleasingP;
|
|---|
| 299 |
|
|---|
| 300 | PathStore store;
|
|---|
| 301 | if ( parametrization()->type() == QwtSplineParametrization::ParameterUniform )
|
|---|
| 302 | {
|
|---|
| 303 | store = qwtSplinePathPleasing< PathStore >( points,
|
|---|
| 304 | isClosing, paramUniform() );
|
|---|
| 305 | }
|
|---|
| 306 | else
|
|---|
| 307 | {
|
|---|
| 308 | store = qwtSplinePathPleasing< PathStore >( points,
|
|---|
| 309 | isClosing, param( parametrization() ) );
|
|---|
| 310 | }
|
|---|
| 311 |
|
|---|
| 312 | if ( isClosing )
|
|---|
| 313 | store.path.closeSubpath();
|
|---|
| 314 |
|
|---|
| 315 | return store.path;
|
|---|
| 316 | }
|
|---|
| 317 |
|
|---|
| 318 | /*!
|
|---|
| 319 | \brief Interpolate a curve with Bezier curves
|
|---|
| 320 |
|
|---|
| 321 | Interpolates a polygon piecewise with cubic Bezier curves
|
|---|
| 322 | and returns the 2 control points of each curve as QLineF.
|
|---|
| 323 |
|
|---|
| 324 | \param points Control points
|
|---|
| 325 | \return Control points of the interpolating Bezier curves
|
|---|
| 326 | */
|
|---|
| 327 | QVector< QLineF > QwtSplinePleasing::bezierControlLines(
|
|---|
| 328 | const QPolygonF& points ) const
|
|---|
| 329 | {
|
|---|
| 330 | const int size = points.size();
|
|---|
| 331 | if ( size <= 2 )
|
|---|
| 332 | return QVector< QLineF >();
|
|---|
| 333 |
|
|---|
| 334 | const bool isClosing = ( boundaryType() == QwtSpline::ClosedPolygon );
|
|---|
| 335 |
|
|---|
| 336 | using namespace QwtSplinePleasingP;
|
|---|
| 337 |
|
|---|
| 338 | ControlPointsStore store;
|
|---|
| 339 | if ( parametrization()->type() == QwtSplineParametrization::ParameterUniform )
|
|---|
| 340 | {
|
|---|
| 341 | store = qwtSplinePathPleasing< ControlPointsStore >( points,
|
|---|
| 342 | isClosing, paramUniform() );
|
|---|
| 343 | }
|
|---|
| 344 | else
|
|---|
| 345 | {
|
|---|
| 346 | store = qwtSplinePathPleasing< ControlPointsStore >( points,
|
|---|
| 347 | isClosing, param( parametrization() ) );
|
|---|
| 348 | }
|
|---|
| 349 |
|
|---|
| 350 | return store.controlPoints;
|
|---|
| 351 | }
|
|---|