10#include "qwt_spline_cubic.h"
11#include "qwt_spline_polynomial.h"
14#include <qpainterpath.h>
16#define SLOPES_INCREMENTAL 0
19namespace QwtSplineCubicP
24 inline KahanSum(
double value = 0.0 ):
32 m_sum = m_carry = 0.0;
35 inline double value()
const
40 inline void add(
double value )
42 const double y = value - m_carry;
43 const double t = m_sum + y;
45 m_carry = ( t - m_sum ) - y;
49 static inline double sum3(
double d1,
double d2,
double d3 )
58 static inline double sum4(
double d1,
double d2,
double d3,
double d4 )
77 inline void setup(
int size )
79 m_curvatures.resize( size );
80 m_cv = m_curvatures.data();
83 inline void storeFirst(
double,
84 const QPointF&,
const QPointF&,
double b1,
double )
89 inline void storeNext(
int index,
double,
90 const QPointF&,
const QPointF&,
double,
double b2 )
92 m_cv[index] = 2.0 * b2;
95 inline void storeLast(
double,
96 const QPointF&,
const QPointF&,
double,
double b2 )
98 m_cv[m_curvatures.size() - 1] = 2.0 * b2;
101 inline void storePrevious(
int index,
double,
102 const QPointF&,
const QPointF&,
double b1,
double )
104 m_cv[index] = 2.0 * b1;
109 m_cv[0] = m_cv[m_curvatures.size() - 1];
122 inline void setup(
int size )
124 m_slopes.resize( size );
125 m_m = m_slopes.data();
128 inline void storeFirst(
double h,
129 const QPointF& p1,
const QPointF& p2,
double b1,
double b2 )
131 const double s = ( p2.y() - p1.y() ) / h;
132 m_m[0] = s - h * ( 2.0 * b1 + b2 ) / 3.0;
138 inline void storeNext(
int index,
double h,
139 const QPointF& p1,
const QPointF& p2,
double b1,
double b2 )
141#if SLOPES_INCREMENTAL
145 m_sum.add( ( b1 + b2 ) * h );
146 m_m[index] = m_sum.value();
148 m_m[index] = m_m[index - 1] + ( b1 + b2 ) * h;
151 const double s = ( p2.y() - p1.y() ) / h;
152 m_m[index] = s + h * ( b1 + 2.0 * b2 ) / 3.0;
156 inline void storeLast(
double h,
157 const QPointF& p1,
const QPointF& p2,
double b1,
double b2 )
159 const double s = ( p2.y() - p1.y() ) / h;
160 m_m[m_slopes.size() - 1] = s + h * ( b1 + 2.0 * b2 ) / 3.0;
162 m_sum.add( m_m[m_slopes.size() - 1] );
166 inline void storePrevious(
int index,
double h,
167 const QPointF& p1,
const QPointF& p2,
double b1,
double b2 )
169#if SLOPES_INCREMENTAL
173 m_sum.add( -( b1 + b2 ) * h );
174 m_m[index] = m_sum.value();
176 m_m[index] = m_m[index + 1] - ( b1 + b2 ) * h;
180 const double s = ( p2.y() - p1.y() ) / h;
181 m_m[index] = s - h * ( 2.0 * b1 + b2 ) / 3.0;
187 m_m[0] = m_m[m_slopes.size() - 1];
195#if SLOPES_INCREMENTAL
201namespace QwtSplineCubicP
210 inline Equation2(
double p0,
double q0,
double r0 ):
217 inline void setup(
double p0,
double q0,
double r0 )
224 inline Equation2 normalized()
const
234 inline double resolved1(
double x2 )
const
236 return ( r - q * x2 ) / p;
239 inline double resolved2(
double x1 )
const
241 return ( r - p * x1 ) / q;
244 inline double resolved1(
const Equation2& eq )
const
248 return ( r - k * eq.r ) / ( p - k * eq.p );
251 inline double resolved2(
const Equation2& eq )
const
254 const double k = p / eq.p;
255 return ( r - k * eq.r ) / ( q - k * eq.q );
269 inline Equation3(
const QPointF& p1,
const QPointF& p2,
const QPointF& p3 )
271 const double h1 = p2.x() - p1.x();
272 const double s1 = ( p2.y() - p1.y() ) / h1;
274 const double h2 = p3.x() - p2.x();
275 const double s2 = ( p3.y() - p2.y() ) / h2;
283 inline Equation3(
double cp,
double cq,
double du,
double dr ):
291 inline bool operator==(
const Equation3& c )
const
293 return ( p == c.p ) && ( q == c.q ) &&
294 ( u == c.u ) && ( r == c.r );
297 inline void setup(
double cp,
double cq,
double du,
double dr )
305 inline Equation3 normalized()
const
316 inline Equation2 substituted1(
const Equation3& eq )
const
319 const double k = p / eq.p;
320 return Equation2( q - k * eq.q, u - k * eq.u, r - k * eq.r );
323 inline Equation2 substituted2(
const Equation3& eq )
const
327 const double k = q / eq.q;
328 return Equation2( p - k * eq.p, u - k * eq.u, r - k * eq.r );
331 inline Equation2 substituted3(
const Equation3& eq )
const
335 const double k = u / eq.u;
336 return Equation2( p - k * eq.p, q - k * eq.q, r - k * eq.r );
339 inline Equation2 substituted1(
const Equation2& eq )
const
342 const double k = p / eq.p;
343 return Equation2( q - k * eq.q, u, r - k * eq.r );
346 inline Equation2 substituted3(
const Equation2& eq )
const
350 const double k = u / eq.q;
351 return Equation2( p, q - k * eq.p, r - k * eq.r );
355 inline double resolved1(
double x2,
double x3 )
const
357 return ( r - q * x2 - u * x3 ) / p;
360 inline double resolved2(
double x1,
double x3 )
const
362 return ( r - u * x3 - p * x1 ) / q;
365 inline double resolved3(
double x1,
double x2 )
const
367 return ( r - p * x1 - q * x2 ) / u;
376static QDebug operator<<( QDebug debug,
const QwtSplineCubicP::Equation2& eq )
378 debug.nospace() <<
"EQ2(" << eq.p <<
", " << eq.q <<
", " << eq.r <<
")";
379 return debug.space();
382static QDebug operator<<( QDebug debug,
const QwtSplineCubicP::Equation3& eq )
384 debug.nospace() <<
"EQ3(" << eq.p <<
", "
385 << eq.q <<
", " << eq.u <<
", " << eq.r <<
")";
386 return debug.space();
390namespace QwtSplineCubicP
396 void setStartCondition(
double p,
double q,
double u,
double r )
398 m_conditionsEQ[0].setup( p, q, u, r );
401 void setEndCondition(
double p,
double q,
double u,
double r )
403 m_conditionsEQ[1].setup( p, q, u, r );
406 const T& store()
const
411 void resolve(
const QPolygonF& p )
413 const int n = p.size();
417 if ( m_conditionsEQ[0].p == 0.0 ||
418 ( m_conditionsEQ[0].q == 0.0 && m_conditionsEQ[0].u != 0.0 ) )
423 if ( m_conditionsEQ[1].u == 0.0 ||
424 ( m_conditionsEQ[1].q == 0.0 && m_conditionsEQ[1].p != 0.0 ) )
429 const double h0 = p[1].x() - p[0].x();
430 const double h1 = p[2].x() - p[1].x();
431 const double hn = p[n - 1].x() - p[n - 2].x();
443 const Equation3 eqSpline0( p[0], p[1], p[2] );
444 const Equation2 eq0 = m_conditionsEQ[0].substituted1( eqSpline0 );
451 if ( m_conditionsEQ[0].normalized() == m_conditionsEQ[1].normalized() )
462 const Equation2 eq = m_conditionsEQ[1].substituted1( eqSpline0 );
463 b1 = eq0.resolved1( eq );
466 const double b2 = eq0.resolved2( b1 );
467 const double b0 = eqSpline0.resolved1( b1, b2 );
469 m_store.storeFirst( h0, p[0], p[1], b0, b1 );
470 m_store.storeNext( 1, h0, p[0], p[1], b0, b1 );
471 m_store.storeNext( 2, h1, p[1], p[2], b1, b2 );
476 const Equation3 eqSplineN( p[n - 3], p[n - 2], p[n - 1] );
477 const Equation2 eqN = m_conditionsEQ[1].substituted3( eqSplineN );
482 const Equation3 eqSplineR( p[n - 4], p[n - 3], p[n - 2] );
483 eq = eqSplineR.substituted3( eq );
484 eq = substituteSpline( p, eq );
487 const Equation3 eqSpline0( p[0], p[1], p[2] );
490 if ( m_conditionsEQ[0].u == 0.0 )
492 eq = eqSpline0.substituted3( eq );
494 const Equation3& eq0 = m_conditionsEQ[0];
495 b0 = Equation2( eq0.p, eq0.q, eq0.r ).resolved1( eq );
496 b1 = eq.resolved2( b0 );
500 const Equation2 eqX = m_conditionsEQ[0].substituted3( eq );
501 const Equation2 eqY = eqSpline0.substituted3( eq );
503 b0 = eqY.resolved1( eqX );
504 b1 = eqY.resolved2( b0 );
507 m_store.storeFirst( h0, p[0], p[1], b0, b1 );
508 m_store.storeNext( 1, h0, p[0], p[1], b0, b1 );
510 const double bn2 = resolveSpline( p, b1 );
512 const double bn1 = eqN.resolved2( bn2 );
513 const double bn0 = m_conditionsEQ[1].resolved3( bn2, bn1 );
515 const double hx = p[n - 2].x() - p[n - 3].x();
516 m_store.storeNext( n - 2, hx, p[n - 3], p[n - 2], bn2, bn1 );
517 m_store.storeNext( n - 1, hn, p[n - 2], p[n - 1], bn1, bn0 );
521 Equation2 substituteSpline(
const QPolygonF& points,
const Equation2& eq )
523 const int n = points.size();
525 m_eq.resize( n - 2 );
530 double slope2 = ( points[n - 3].y() - points[n - 4].y() ) / eq.p;
532 for (
int i = n - 4; i > 1; i-- )
534 const Equation2& eq2 = m_eq[i + 1];
535 Equation2& eq1 = m_eq[i];
537 eq1.p = points[i].x() - points[i - 1].x();
538 const double slope1 = ( points[i].y() - points[i - 1].y() ) / eq1.p;
540 const double v = eq2.p / eq2.q;
542 eq1.q = 2.0 * ( eq1.p + eq2.p ) - v * eq2.p;
543 eq1.r = 3.0 * ( slope2 - slope1 ) - v * eq2.r;
551 double resolveSpline(
const QPolygonF& points,
double b1 )
553 const int n = points.size();
554 const QPointF* p = points.constData();
556 for (
int i = 2; i < n - 2; i++ )
559 const double b2 = m_eq[i].resolved2( b1 );
560 m_store.storeNext( i, m_eq[i].p, p[i - 1], p[i], b1, b2 );
569 Equation3 m_conditionsEQ[2];
575 class EquationSystem2
578 const T& store()
const
583 void resolve(
const QPolygonF& p )
585 const int n = p.size();
587 if ( p[n - 1].y() != p[0].y() )
592 const double h0 = p[1].x() - p[0].x();
593 const double s0 = ( p[1].y() - p[0].y() ) / h0;
597 const double h1 = p[2].x() - p[1].x();
598 const double s1 = ( p[2].y() - p[1].y() ) / h1;
600 const double b = 3.0 * ( s0 - s1 ) / ( h0 + h1 );
603 m_store.storeLast( h1, p[1], p[2], -b, b );
604 m_store.storePrevious( 1, h1, p[1], p[2], -b, b );
610 const double hn = p[n - 1].x() - p[n - 2].x();
613 substitute( p, eqn, eqX );
615 const double b0 = eqn.resolved2( eqX );
616 const double bn = eqn.resolved1( b0 );
619 m_store.storeLast( hn, p[n - 2], p[n - 1], bn, b0 );
620 m_store.storePrevious( n - 2, hn, p[n - 2], p[n - 1], bn, b0 );
622 resolveSpline( p, b0, bn );
629 void substitute(
const QPolygonF& points, Equation2& eqn, Equation2& eqX )
631 const int n = points.size();
633 const double hn = points[n - 1].x() - points[n - 2].x();
635 const Equation3 eqSpline0( points[0], points[1], points[2] );
636 const Equation3 eqSplineN(
637 QPointF( points[0].x() - hn, points[n - 2].y() ), points[0], points[1] );
639 m_eq.resize( n - 1 );
646 double slope1 = ( points[2].y() - points[1].y() ) / m_eq[1].u;
660 for (
int i = 2; i < n - 1; i++ )
662 const Equation3& eq1 = m_eq[i - 1];
663 Equation3& eq2 = m_eq[i];
665 dq += eq1.p * eq1.p / eq1.q;
666 dr += eq1.p * eq1.r / eq1.q;
668 eq2.u = points[i + 1].x() - points[i].x();
669 const double slope2 = ( points[i + 1].y() - points[i].y() ) / eq2.u;
671 const double k = eq1.u / eq1.q;
674 eq2.q = 2.0 * ( eq1.u + eq2.u ) - eq1.u * k;
675 eq2.r = 3.0 * ( slope2 - slope1 ) - eq1.r * k;
682 eqn.setup( m_eq[n - 2].q, m_eq[n - 2].p + eqSplineN.p, m_eq[n - 2].r );
685 eqX.setup( m_eq[n - 2].p + eqSplineN.p, eqSplineN.q - dq, eqSplineN.r - dr );
688 void resolveSpline(
const QPolygonF& points,
double b0,
double bi )
690 const int n = points.size();
692 for (
int i = n - 3; i >= 1; i-- )
694 const Equation3& eq = m_eq[i];
696 const double b = eq.resolved2( b0, bi );
697 m_store.storePrevious( i, eq.u, points[i], points[i + 1], b, bi );
703 void resolveSpline2(
const QPolygonF& points,
706 const int n = points.size();
708 bi = m_eq[0].resolved3( b0, bi );
710 for (
int i = 1; i < n - 2; i++ )
712 const Equation3& eq = m_eq[i];
714 const double b = eq.resolved3( b0, bi );
715 m[i + 1] = m[i] + ( b + bi ) * m_eq[i].u;
721 void resolveSpline3(
const QPolygonF& points,
724 const int n = points.size();
726 double h0 = ( points[1].x() - points[0].x() );
727 double s0 = ( points[1].y() - points[0].y() ) / h0;
729 m[1] = m[0] + ( b0 + b1 ) * h0;
731 for (
int i = 1; i < n - 1; i++ )
733 const double h1 = ( points[i + 1].x() - points[i].x() );
734 const double s1 = ( points[i + 1].y() - points[i].y() ) / h1;
736 const double r = 3.0 * ( s1 - s0 );
738 const double b2 = ( r - h0 * b0 - 2.0 * ( h0 + h1 ) * b1 ) / h1;
739 m[i + 1] = m[i] + ( b1 + b2 ) * h1;
748 void resolveSpline4(
const QPolygonF& points,
751 const int n = points.size();
753 double h2 = ( points[n - 1].x() - points[n - 2].x() );
754 double s2 = ( points[n - 1].y() - points[n - 2].y() ) / h2;
756 for (
int i = n - 2; i > 1; i-- )
758 const double h1 = ( points[i].x() - points[i - 1].x() );
759 const double s1 = ( points[i].y() - points[i - 1].y() ) / h1;
761 const double r = 3.0 * ( s2 - s1 );
762 const double k = 2.0 * ( h1 + h2 );
764 const double b0 = ( r - h2 * b2 - k * b1 ) / h1;
766 m[i - 1] = m[i] - ( b0 + b1 ) * h1;
781static void qwtSetupEndEquations(
782 int conditionBegin,
double valueBegin,
int conditionEnd,
double valueEnd,
783 const QPolygonF& points, QwtSplineCubicP::Equation3 eq[2] )
785 const int n = points.size();
787 const double h0 = points[1].x() - points[0].x();
788 const double s0 = ( points[1].y() - points[0].y() ) / h0;
790 const double hn = ( points[n - 1].x() - points[n - 2].x() );
791 const double sn = ( points[n - 1].y() - points[n - 2].y() ) / hn;
793 switch( conditionBegin )
808 eq[0].setup( 2 * h0 / 3.0, h0 / 3.0, 0.0, s0 - valueBegin );
821 eq[0].setup( 1.0, 0.0, 0.0, 0.5 * valueBegin );
836 eq[0].setup( 1.0, -1.0, 0.0, -0.5 * valueBegin * h0 );
842 const double r0 = qBound( 0.0, valueBegin, 1.0 );
846 eq[0].setup( 2 * h0 / 3.0, h0 / 3.0, 0.0, 0.0 );
850 eq[0].setup( 1.0 + 2.0 / r0, 2.0 + 1.0 / r0, 0.0, 0.0 );
875 v0 = h0 / ( points[2].x() - points[1].x() );
878 eq[0].setup( 1.0, -( 1.0 + v0 ), v0, 0.0 );
885 eq[0].setup( 1.0, 0.0, 0.0, 0.0 );
890 switch( conditionEnd )
895 eq[1].setup( 0.0, 1.0 / 3.0 * hn, 2.0 / 3.0 * hn, valueEnd - sn );
901 eq[1].setup( 0.0, 0.0, 1.0, 0.5 * valueEnd );
907 eq[1].setup( 0.0, 1.0, -1.0, -0.5 * valueEnd * hn );
912 const double rn = qBound( 0.0, valueEnd, 1.0 );
916 eq[1].setup( 0.0, 1.0 / 3.0 * hn, 2.0 / 3.0 * hn, 0.0 );
920 eq[1].setup( 0.0, 2.0 + 1.0 / rn, 1.0 + 2.0 / rn, 0.0 );
942 vn = hn / ( points[n - 2].x() - points[n - 3].x() );
945 eq[1].setup( vn, -( 1.0 + vn ), 1.0, 0.0 );
952 eq[1].setup( 0.0, 0.0, 1.0, 0.0 );
958class QwtSplineCubic::PrivateData
1008 using namespace QwtSplineCubicP;
1010 if ( points.size() <= 2 )
1016 EquationSystem2< SlopeStore > eqs;
1017 eqs.resolve( points );
1019 return eqs.store().slopes();
1022 if ( points.size() == 3 )
1028 const double h0 = points[1].x() - points[0].x();
1029 const double h1 = points[2].x() - points[1].x();
1031 const double s0 = ( points[1].y() - points[0].y() ) / h0;
1032 const double s1 = ( points[2].y() - points[1].y() ) / h1;
1039 const double b = ( s1 - s0 ) / ( h0 + h1 );
1054 qwtSetupEndEquations(
1061 EquationSystem< SlopeStore > eqs;
1062 eqs.setStartCondition( eq[0].p, eq[0].q, eq[0].u, eq[0].r );
1063 eqs.setEndCondition( eq[1].p, eq[1].q, eq[1].u, eq[1].r );
1064 eqs.resolve( points );
1066 return eqs.store().slopes();
1080 using namespace QwtSplineCubicP;
1082 if ( points.size() <= 2 )
1088 EquationSystem2< CurvatureStore > eqs;
1089 eqs.resolve( points );
1091 return eqs.store().curvatures();
1094 if ( points.size() == 3 )
1104 qwtSetupEndEquations(
1111 EquationSystem< CurvatureStore > eqs;
1112 eqs.setStartCondition( eq[0].p, eq[0].q, eq[0].u, eq[0].r );
1113 eqs.setEndCondition( eq[1].p, eq[1].q, eq[1].u, eq[1].r );
1114 eqs.resolve( points );
1116 return eqs.store().curvatures();
virtual QPainterPath painterPath(const QPolygonF &) const override
Calculate an interpolated painter path.
virtual QVector< QLineF > bezierControlLines(const QPolygonF &) const override
Interpolate a curve with Bezier curves.
virtual QVector< QwtSplinePolynomial > polynomials(const QPolygonF &) const override
Calculate the interpolating polynomials for a non parametric spline.
QwtSplineCubic()
Constructor The default setting is a non closing natural spline with no parametrization.
virtual ~QwtSplineCubic()
Destructor.
virtual uint locality() const override
virtual QPainterPath painterPath(const QPolygonF &) const override
Interpolate a curve with Bezier curves.
virtual QVector< QLineF > bezierControlLines(const QPolygonF &points) const override
Interpolate a curve with Bezier curves.
virtual QVector< double > curvatures(const QPolygonF &) const override
Find the second derivative at the control points.
virtual QVector< double > slopes(const QPolygonF &) const override
Find the first derivative at the control points.
virtual QVector< QwtSplinePolynomial > polynomials(const QPolygonF &) const override
Calculate the interpolating polynomials for a non parametric spline.
@ AtBeginning
the condition is at the beginning of the polynomial
@ AtEnd
the condition is at the end of the polynomial
double boundaryValue(BoundaryPosition) const
void setBoundaryCondition(BoundaryPosition, int condition)
Define the condition for an endpoint of the spline.
int boundaryCondition(BoundaryPosition) const
BoundaryType boundaryType() const
void setBoundaryValue(BoundaryPosition, double value)
Define the boundary value.