Qwt User's Guide: qwt_spline_cubic.cpp Source File

Qwt User's Guide 6.2.0

qwt_spline_cubic.cpp
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_cubic.h"
11 #include "qwt_spline_polynomial.h"
12
13 #include <qpolygon.h>
14 #include <qpainterpath.h>
15
16 #define SLOPES_INCREMENTAL 0
17 #define KAHAN 0
18
19 namespace QwtSplineCubicP
20 {
21 class KahanSum
22 {
23 public:
24 inline KahanSum( double value = 0.0 ):
25 m_sum( value ),
26 m_carry( 0.0 )
27 {
28 }
29
30 inline void reset()
31 {
32 m_sum = m_carry = 0.0;
33 }
34
35 inline double value() const
36 {
37 return m_sum;
38 }
39
40 inline void add( double value )
41 {
42 const double y = value - m_carry;
43 const double t = m_sum + y;
44
45 m_carry = ( t - m_sum ) - y;
46 m_sum = t;
47 }
48
49 static inline double sum3( double d1, double d2, double d3 )
50 {
51 KahanSum sum( d1 );
52 sum.add( d2 );
53 sum.add( d3 );
54
55 return sum.value();
56 }
57
58 static inline double sum4( double d1, double d2, double d3, double d4 )
59 {
60 KahanSum sum( d1 );
61 sum.add( d2 );
62 sum.add( d3 );
63 sum.add( d4 );
64
65 return sum.value();
66 }
67
68
69 private:
70 double m_sum;
71 double m_carry; // The carry from the previous operation
72 };
73
74 class CurvatureStore
75 {
76 public:
77 inline void setup( int size )
78 {
79 m_curvatures.resize( size );
80 m_cv = m_curvatures.data();
81 }
82
83 inline void storeFirst( double,
84 const QPointF&, const QPointF&, double b1, double )
85 {
86 m_cv[0] = 2.0 * b1;
87 }
88
89 inline void storeNext( int index, double,
90 const QPointF&, const QPointF&, double, double b2 )
91 {
92 m_cv[index] = 2.0 * b2;
93 }
94
95 inline void storeLast( double,
96 const QPointF&, const QPointF&, double, double b2 )
97 {
98 m_cv[m_curvatures.size() - 1] = 2.0 * b2;
99 }
100
101 inline void storePrevious( int index, double,
102 const QPointF&, const QPointF&, double b1, double )
103 {
104 m_cv[index] = 2.0 * b1;
105 }
106
107 inline void closeR()
108 {
109 m_cv[0] = m_cv[m_curvatures.size() - 1];
110 }
111
112 QVector< double > curvatures() const { return m_curvatures; }
113
114 private:
115 QVector< double > m_curvatures;
116 double* m_cv;
117 };
118
119 class SlopeStore
120 {
121 public:
122 inline void setup( int size )
123 {
124 m_slopes.resize( size );
125 m_m = m_slopes.data();
126 }
127
128 inline void storeFirst( double h,
129 const QPointF& p1, const QPointF& p2, double b1, double b2 )
130 {
131 const double s = ( p2.y() - p1.y() ) / h;
132 m_m[0] = s - h * ( 2.0 * b1 + b2 ) / 3.0;
133 #if KAHAN
134 m_sum.add( m_m[0] );
135 #endif
136 }
137
138 inline void storeNext( int index, double h,
139 const QPointF& p1, const QPointF& p2, double b1, double b2 )
140 {
141 #if SLOPES_INCREMENTAL
142 Q_UNUSED( p1 )
143 Q_UNUSED( p2 )
144 #if KAHAN
145 m_sum.add( ( b1 + b2 ) * h );
146 m_m[index] = m_sum.value();
147 #else
148 m_m[index] = m_m[index - 1] + ( b1 + b2 ) * h;
149 #endif
150 #else
151 const double s = ( p2.y() - p1.y() ) / h;
152 m_m[index] = s + h * ( b1 + 2.0 * b2 ) / 3.0;
153 #endif
154 }
155
156 inline void storeLast( double h,
157 const QPointF& p1, const QPointF& p2, double b1, double b2 )
158 {
159 const double s = ( p2.y() - p1.y() ) / h;
160 m_m[m_slopes.size() - 1] = s + h * ( b1 + 2.0 * b2 ) / 3.0;
161 #if KAHAN
162 m_sum.add( m_m[m_slopes.size() - 1] );
163 #endif
164 }
165
166 inline void storePrevious( int index, double h,
167 const QPointF& p1, const QPointF& p2, double b1, double b2 )
168 {
169 #if SLOPES_INCREMENTAL
170 Q_UNUSED( p1 )
171 Q_UNUSED( p2 )
172 #if KAHAN
173 m_sum.add( -( b1 + b2 ) * h );
174 m_m[index] = m_sum.value();
175 #else
176 m_m[index] = m_m[index + 1] - ( b1 + b2 ) * h;
177 #endif
178
179 #else
180 const double s = ( p2.y() - p1.y() ) / h;
181 m_m[index] = s - h * ( 2.0 * b1 + b2 ) / 3.0;
182 #endif
183 }
184
185 inline void closeR()
186 {
187 m_m[0] = m_m[m_slopes.size() - 1];
188 }
189
190 QVector< double > slopes() const { return m_slopes; }
191
192 private:
193 QVector< double > m_slopes;
194 double* m_m;
195 #if SLOPES_INCREMENTAL
196 KahanSum m_sum;
197 #endif
198 };
199 }
200
201 namespace QwtSplineCubicP
202 {
203 class Equation2
204 {
205 public:
206 inline Equation2()
207 {
208 }
209
210 inline Equation2( double p0, double q0, double r0 ):
211 p( p0 ),
212 q( q0 ),
213 r( r0 )
214 {
215 }
216
217 inline void setup( double p0, double q0, double r0 )
218 {
219 p = p0;
220 q = q0;
221 r = r0;
222 }
223
224 inline Equation2 normalized() const
225 {
226 Equation2 c;
227 c.p = 1.0;
228 c.q = q / p;
229 c.r = r / p;
230
231 return c;
232 }
233
234 inline double resolved1( double x2 ) const
235 {
236 return ( r - q * x2 ) / p;
237 }
238
239 inline double resolved2( double x1 ) const
240 {
241 return ( r - p * x1 ) / q;
242 }
243
244 inline double resolved1( const Equation2& eq ) const
245 {
246 // find x1
247 double k = q / eq.q;
248 return ( r - k * eq.r ) / ( p - k * eq.p );
249 }
250
251 inline double resolved2( const Equation2& eq ) const
252 {
253 // find x2
254 const double k = p / eq.p;
255 return ( r - k * eq.r ) / ( q - k * eq.q );
256 }
257
258 // p * x1 + q * x2 = r
259 double p, q, r;
260 };
261
262 class Equation3
263 {
264 public:
265 inline Equation3()
266 {
267 }
268
269 inline Equation3( const QPointF& p1, const QPointF& p2, const QPointF& p3 )
270 {
271 const double h1 = p2.x() - p1.x();
272 const double s1 = ( p2.y() - p1.y() ) / h1;
273
274 const double h2 = p3.x() - p2.x();
275 const double s2 = ( p3.y() - p2.y() ) / h2;
276
277 p = h1;
278 q = 2 * ( h1 + h2 );
279 u = h2;
280 r = 3 * ( s2 - s1 );
281 }
282
283 inline Equation3( double cp, double cq, double du, double dr ):
284 p( cp ),
285 q( cq ),
286 u( du ),
287 r( dr )
288 {
289 }
290
291 inline bool operator==( const Equation3& c ) const
292 {
293 return ( p == c.p ) && ( q == c.q ) &&
294 ( u == c.u ) && ( r == c.r );
295 }
296
297 inline void setup( double cp, double cq, double du, double dr )
298 {
299 p = cp;
300 q = cq;
301 u = du;
302 r = dr;
303 }
304
305 inline Equation3 normalized() const
306 {
307 Equation3 c;
308 c.p = 1.0;
309 c.q = q / p;
310 c.u = u / p;
311 c.r = r / p;
312
313 return c;
314 }
315
316 inline Equation2 substituted1( const Equation3& eq ) const
317 {
318 // eliminate x1
319 const double k = p / eq.p;
320 return Equation2( q - k * eq.q, u - k * eq.u, r - k * eq.r );
321 }
322
323 inline Equation2 substituted2( const Equation3& eq ) const
324 {
325 // eliminate x2
326
327 const double k = q / eq.q;
328 return Equation2( p - k * eq.p, u - k * eq.u, r - k * eq.r );
329 }
330
331 inline Equation2 substituted3( const Equation3& eq ) const
332 {
333 // eliminate x3
334
335 const double k = u / eq.u;
336 return Equation2( p - k * eq.p, q - k * eq.q, r - k * eq.r );
337 }
338
339 inline Equation2 substituted1( const Equation2& eq ) const
340 {
341 // eliminate x1
342 const double k = p / eq.p;
343 return Equation2( q - k * eq.q, u, r - k * eq.r );
344 }
345
346 inline Equation2 substituted3( const Equation2& eq ) const
347 {
348 // eliminate x3
349
350 const double k = u / eq.q;
351 return Equation2( p, q - k * eq.p, r - k * eq.r );
352 }
353
354
355 inline double resolved1( double x2, double x3 ) const
356 {
357 return ( r - q * x2 - u * x3 ) / p;
358 }
359
360 inline double resolved2( double x1, double x3 ) const
361 {
362 return ( r - u * x3 - p * x1 ) / q;
363 }
364
365 inline double resolved3( double x1, double x2 ) const
366 {
367 return ( r - p * x1 - q * x2 ) / u;
368 }
369
370 // p * x1 + q * x2 + u * x3 = r
371 double p, q, u, r;
372 };
373 }
374
375 #if 0
376 static QDebug operator<<( QDebug debug, const QwtSplineCubicP::Equation2& eq )
377 {
378 debug.nospace() << "EQ2(" << eq.p << ", " << eq.q << ", " << eq.r << ")";
379 return debug.space();
380 }
381
382 static QDebug operator<<( QDebug debug, const QwtSplineCubicP::Equation3& eq )
383 {
384 debug.nospace() << "EQ3(" << eq.p << ", "
385 << eq.q << ", " << eq.u << ", " << eq.r << ")";
386 return debug.space();
387 }
388 #endif
389
390 namespace QwtSplineCubicP
391 {
392 template< class T >
393 class EquationSystem
394 {
395 public:
396 void setStartCondition( double p, double q, double u, double r )
397 {
398 m_conditionsEQ[0].setup( p, q, u, r );
399 }
400
401 void setEndCondition( double p, double q, double u, double r )
402 {
403 m_conditionsEQ[1].setup( p, q, u, r );
404 }
405
406 const T& store() const
407 {
408 return m_store;
409 }
410
411 void resolve( const QPolygonF& p )
412 {
413 const int n = p.size();
414 if ( n < 3 )
415 return;
416
417 if ( m_conditionsEQ[0].p == 0.0 ||
418 ( m_conditionsEQ[0].q == 0.0 && m_conditionsEQ[0].u != 0.0 ) )
419 {
420 return;
421 }
422
423 if ( m_conditionsEQ[1].u == 0.0 ||
424 ( m_conditionsEQ[1].q == 0.0 && m_conditionsEQ[1].p != 0.0 ) )
425 {
426 return;
427 }
428
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();
432
433 m_store.setup( n );
434
435
436 if ( n == 3 )
437 {
438 // For certain conditions the first/last point does not
439 // necessarily meet the spline equation and we would
440 // have many solutions. In this case we resolve using
441 // the spline equation - as for all other conditions.
442
443 const Equation3 eqSpline0( p[0], p[1], p[2] ); // ???
444 const Equation2 eq0 = m_conditionsEQ[0].substituted1( eqSpline0 );
445
446 // The equation system can be solved without substitution
447 // from the start/end conditions and eqSpline0 ( = eqSplineN ).
448
449 double b1;
450
451 if ( m_conditionsEQ[0].normalized() == m_conditionsEQ[1].normalized() )
452 {
453 // When we have 3 points only and start/end conditions
454 // for 3 points mean the same condition the system
455 // is under-determined and has many solutions.
456 // We chose b1 = 0.0
457
458 b1 = 0.0;
459 }
460 else
461 {
462 const Equation2 eq = m_conditionsEQ[1].substituted1( eqSpline0 );
463 b1 = eq0.resolved1( eq );
464 }
465
466 const double b2 = eq0.resolved2( b1 );
467 const double b0 = eqSpline0.resolved1( b1, b2 );
468
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 );
472
473 return;
474 }
475
476 const Equation3 eqSplineN( p[n - 3], p[n - 2], p[n - 1] );
477 const Equation2 eqN = m_conditionsEQ[1].substituted3( eqSplineN );
478
479 Equation2 eq = eqN;
480 if ( n > 4 )
481 {
482 const Equation3 eqSplineR( p[n - 4], p[n - 3], p[n - 2] );
483 eq = eqSplineR.substituted3( eq );
484 eq = substituteSpline( p, eq );
485 }
486
487 const Equation3 eqSpline0( p[0], p[1], p[2] );
488
489 double b0, b1;
490 if ( m_conditionsEQ[0].u == 0.0 )
491 {
492 eq = eqSpline0.substituted3( eq );
493
494 const Equation3& eq0 = m_conditionsEQ[0];
495 b0 = Equation2( eq0.p, eq0.q, eq0.r ).resolved1( eq );
496 b1 = eq.resolved2( b0 );
497 }
498 else
499 {
500 const Equation2 eqX = m_conditionsEQ[0].substituted3( eq );
501 const Equation2 eqY = eqSpline0.substituted3( eq );
502
503 b0 = eqY.resolved1( eqX );
504 b1 = eqY.resolved2( b0 );
505 }
506
507 m_store.storeFirst( h0, p[0], p[1], b0, b1 );
508 m_store.storeNext( 1, h0, p[0], p[1], b0, b1 );
509
510 const double bn2 = resolveSpline( p, b1 );
511
512 const double bn1 = eqN.resolved2( bn2 );
513 const double bn0 = m_conditionsEQ[1].resolved3( bn2, bn1 );
514
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 );
518 }
519
520 private:
521 Equation2 substituteSpline( const QPolygonF& points, const Equation2& eq )
522 {
523 const int n = points.size();
524
525 m_eq.resize( n - 2 );
526 m_eq[n - 3] = eq;
527
528 // eq[i].resolved2( b[i-1] ) => b[i]
529
530 double slope2 = ( points[n - 3].y() - points[n - 4].y() ) / eq.p;
531
532 for ( int i = n - 4; i > 1; i-- )
533 {
534 const Equation2& eq2 = m_eq[i + 1];
535 Equation2& eq1 = m_eq[i];
536
537 eq1.p = points[i].x() - points[i - 1].x();
538 const double slope1 = ( points[i].y() - points[i - 1].y() ) / eq1.p;
539
540 const double v = eq2.p / eq2.q;
541
542 eq1.q = 2.0 * ( eq1.p + eq2.p ) - v * eq2.p;
543 eq1.r = 3.0 * ( slope2 - slope1 ) - v * eq2.r;
544
545 slope2 = slope1;
546 }
547
548 return m_eq[2];
549 }
550
551 double resolveSpline( const QPolygonF& points, double b1 )
552 {
553 const int n = points.size();
554 const QPointF* p = points.constData();
555
556 for ( int i = 2; i < n - 2; i++ )
557 {
558 // eq[i].resolved2( b[i-1] ) => b[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 );
561
562 b1 = b2;
563 }
564
565 return b1;
566 }
567
568 private:
569 Equation3 m_conditionsEQ[2];
570 QVector< Equation2 > m_eq;
571 T m_store;
572 };
573
574 template< class T >
575 class EquationSystem2
576 {
577 public:
578 const T& store() const
579 {
580 return m_store;
581 }
582
583 void resolve( const QPolygonF& p )
584 {
585 const int n = p.size();
586
587 if ( p[n - 1].y() != p[0].y() )
588 {
589 // TODO ???
590 }
591
592 const double h0 = p[1].x() - p[0].x();
593 const double s0 = ( p[1].y() - p[0].y() ) / h0;
594
595 if ( n == 3 )
596 {
597 const double h1 = p[2].x() - p[1].x();
598 const double s1 = ( p[2].y() - p[1].y() ) / h1;
599
600 const double b = 3.0 * ( s0 - s1 ) / ( h0 + h1 );
601
602 m_store.setup( 3 );
603 m_store.storeLast( h1, p[1], p[2], -b, b );
604 m_store.storePrevious( 1, h1, p[1], p[2], -b, b );
605 m_store.closeR();
606
607 return;
608 }
609
610 const double hn = p[n - 1].x() - p[n - 2].x();
611
612 Equation2 eqn, eqX;
613 substitute( p, eqn, eqX );
614
615 const double b0 = eqn.resolved2( eqX );
616 const double bn = eqn.resolved1( b0 );
617
618 m_store.setup( n );
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 );
621
622 resolveSpline( p, b0, bn );
623
624 m_store.closeR();
625 }
626
627 private:
628
629 void substitute( const QPolygonF& points, Equation2& eqn, Equation2& eqX )
630 {
631 const int n = points.size();
632
633 const double hn = points[n - 1].x() - points[n - 2].x();
634
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] );
638
639 m_eq.resize( n - 1 );
640
641 double dq = 0;
642 double dr = 0;
643
644 m_eq[1] = eqSpline0;
645
646 double slope1 = ( points[2].y() - points[1].y() ) / m_eq[1].u;
647
648 // a) p1 * b[0] + q1 * b[1] + u1 * b[2] = r1
649 // b) p2 * b[n-2] + q2 * b[0] + u2 * b[1] = r2
650 // c) pi * b[i-1] + qi * b[i] + ui * b[i+1] = ri
651 //
652 // Using c) we can substitute b[i] ( starting from 2 ) by b[i+1]
653 // until we reach n-1. As we know, that b[0] == b[n-1] we found
654 // an equation where only 2 coefficients ( for b[n-2], b[0] ) are left unknown.
655 // Each step we have an equation that depends on b[0], b[i] and b[i+1]
656 // that can also be used to substitute b[i] in b). Ding so we end up with another
657 // equation depending on b[n-2], b[0] only.
658 // Finally 2 equations with 2 coefficients can be solved.
659
660 for ( int i = 2; i < n - 1; i++ )
661 {
662 const Equation3& eq1 = m_eq[i - 1];
663 Equation3& eq2 = m_eq[i];
664
665 dq += eq1.p * eq1.p / eq1.q;
666 dr += eq1.p * eq1.r / eq1.q;
667
668 eq2.u = points[i + 1].x() - points[i].x();
669 const double slope2 = ( points[i + 1].y() - points[i].y() ) / eq2.u;
670
671 const double k = eq1.u / eq1.q;
672
673 eq2.p = -eq1.p * k;
674 eq2.q = 2.0 * ( eq1.u + eq2.u ) - eq1.u * k;
675 eq2.r = 3.0 * ( slope2 - slope1 ) - eq1.r * k;
676
677 slope1 = slope2;
678 }
679
680
681 // b[0] * m_p[n-2] + b[n-2] * m_q[n-2] + b[n-1] * pN = m_r[n-2]
682 eqn.setup( m_eq[n - 2].q, m_eq[n - 2].p + eqSplineN.p, m_eq[n - 2].r );
683
684 // b[n-2] * pN + b[0] * ( qN - dq ) + b[n-2] * m_p[n-2] = rN - dr
685 eqX.setup( m_eq[n - 2].p + eqSplineN.p, eqSplineN.q - dq, eqSplineN.r - dr );
686 }
687
688 void resolveSpline( const QPolygonF& points, double b0, double bi )
689 {
690 const int n = points.size();
691
692 for ( int i = n - 3; i >= 1; i-- )
693 {
694 const Equation3& eq = m_eq[i];
695
696 const double b = eq.resolved2( b0, bi );
697 m_store.storePrevious( i, eq.u, points[i], points[i + 1], b, bi );
698
699 bi = b;
700 }
701 }
702
703 void resolveSpline2( const QPolygonF& points,
704 double b0, double bi, QVector< double >& m )
705 {
706 const int n = points.size();
707
708 bi = m_eq[0].resolved3( b0, bi );
709
710 for ( int i = 1; i < n - 2; i++ )
711 {
712 const Equation3& eq = m_eq[i];
713
714 const double b = eq.resolved3( b0, bi );
715 m[i + 1] = m[i] + ( b + bi ) * m_eq[i].u;
716
717 bi = b;
718 }
719 }
720
721 void resolveSpline3( const QPolygonF& points,
722 double b0, double b1, QVector< double >& m )
723 {
724 const int n = points.size();
725
726 double h0 = ( points[1].x() - points[0].x() );
727 double s0 = ( points[1].y() - points[0].y() ) / h0;
728
729 m[1] = m[0] + ( b0 + b1 ) * h0;
730
731 for ( int i = 1; i < n - 1; i++ )
732 {
733 const double h1 = ( points[i + 1].x() - points[i].x() );
734 const double s1 = ( points[i + 1].y() - points[i].y() ) / h1;
735
736 const double r = 3.0 * ( s1 - s0 );
737
738 const double b2 = ( r - h0 * b0 - 2.0 * ( h0 + h1 ) * b1 ) / h1;
739 m[i + 1] = m[i] + ( b1 + b2 ) * h1;
740
741 h0 = h1;
742 s0 = s1;
743 b0 = b1;
744 b1 = b2;
745 }
746 }
747
748 void resolveSpline4( const QPolygonF& points,
749 double b2, double b1, QVector< double >& m )
750 {
751 const int n = points.size();
752
753 double h2 = ( points[n - 1].x() - points[n - 2].x() );
754 double s2 = ( points[n - 1].y() - points[n - 2].y() ) / h2;
755
756 for ( int i = n - 2; i > 1; i-- )
757 {
758 const double h1 = ( points[i].x() - points[i - 1].x() );
759 const double s1 = ( points[i].y() - points[i - 1].y() ) / h1;
760
761 const double r = 3.0 * ( s2 - s1 );
762 const double k = 2.0 * ( h1 + h2 );
763
764 const double b0 = ( r - h2 * b2 - k * b1 ) / h1;
765
766 m[i - 1] = m[i] - ( b0 + b1 ) * h1;
767
768 h2 = h1;
769 s2 = s1;
770 b2 = b1;
771 b1 = b0;
772 }
773 }
774
775 public:
776 QVector< Equation3 > m_eq;
777 T m_store;
778 };
779 }
780
781 static void qwtSetupEndEquations(
782 int conditionBegin, double valueBegin, int conditionEnd, double valueEnd,
783 const QPolygonF& points, QwtSplineCubicP::Equation3 eq[2] )
784 {
785 const int n = points.size();
786
787 const double h0 = points[1].x() - points[0].x();
788 const double s0 = ( points[1].y() - points[0].y() ) / h0;
789
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;
792
793 switch( conditionBegin )
794 {
795 case QwtSpline::Clamped1:
796 {
797 // first derivative at end points given
798
799 // 3 * a1 * h + b1 = b2
800 // a1 * h * h + b1 * h + c1 = s
801
802 // c1 = slopeBegin
803 // => b1 * ( 2 * h / 3.0 ) + b2 * ( h / 3.0 ) = s - slopeBegin
804
805 // c2 = slopeEnd
806 // => b1 * ( 1.0 / 3.0 ) + b2 * ( 2.0 / 3.0 ) = ( slopeEnd - s ) / h;
807
808 eq[0].setup( 2 * h0 / 3.0, h0 / 3.0, 0.0, s0 - valueBegin );
809 break;
810 }
811 case QwtSpline::Clamped2:
812 {
813 // second derivative at end points given
814
815 // b0 = 0.5 * cvStart
816 // => b0 * 1.0 + b1 * 0.0 = 0.5 * cvStart
817
818 // b1 = 0.5 * cvEnd
819 // => b0 * 0.0 + b1 * 1.0 = 0.5 * cvEnd
820
821 eq[0].setup( 1.0, 0.0, 0.0, 0.5 * valueBegin );
822 break;
823 }
824 case QwtSpline::Clamped3:
825 {
826 // third derivative at end point given
827
828 // 3 * a * h0 + b[0] = b[1]
829
830 // a = marg_0 / 6.0
831 // => b[0] * 1.0 + b[1] * ( -1.0 ) = -0.5 * v0 * h0
832
833 // a = marg_n / 6.0
834 // => b[n-2] * 1.0 + b[n-1] * ( -1.0 ) = -0.5 * v1 * h5
835
836 eq[0].setup( 1.0, -1.0, 0.0, -0.5 * valueBegin * h0 );
837
838 break;
839 }
840 case QwtSpline::LinearRunout:
841 {
842 const double r0 = qBound( 0.0, valueBegin, 1.0 );
843 if ( r0 == 0.0 )
844 {
845 // clamping s0
846 eq[0].setup( 2 * h0 / 3.0, h0 / 3.0, 0.0, 0.0 );
847 }
848 else
849 {
850 eq[0].setup( 1.0 + 2.0 / r0, 2.0 + 1.0 / r0, 0.0, 0.0 );
851 }
852 break;
853 }
854 case QwtSplineC2::NotAKnot:
855 case QwtSplineC2::CubicRunout:
856 {
857 // building one cubic curve from 3 points
858
859 double v0;
860
861 if ( conditionBegin == QwtSplineC2::CubicRunout )
862 {
863 // first/last point are the endpoints of the curve
864
865 // b0 = 2 * b1 - b2
866 // => 1.0 * b0 - 2 * b1 + 1.0 * b2 = 0.0
867
868 v0 = 1.0;
869 }
870 else
871 {
872 // first/last points are on the curve,
873 // the imaginary endpoints have the same distance as h0/hn
874
875 v0 = h0 / ( points[2].x() - points[1].x() );
876 }
877
878 eq[0].setup( 1.0, -( 1.0 + v0 ), v0, 0.0 );
879 break;
880 }
881 default:
882 {
883 // a natural spline, where the
884 // second derivative at end points set to 0.0
885 eq[0].setup( 1.0, 0.0, 0.0, 0.0 );
886 break;
887 }
888 }
889
890 switch( conditionEnd )
891 {
892 case QwtSpline::Clamped1:
893 {
894 // first derivative at end points given
895 eq[1].setup( 0.0, 1.0 / 3.0 * hn, 2.0 / 3.0 * hn, valueEnd - sn );
896 break;
897 }
898 case QwtSpline::Clamped2:
899 {
900 // second derivative at end points given
901 eq[1].setup( 0.0, 0.0, 1.0, 0.5 * valueEnd );
902 break;
903 }
904 case QwtSpline::Clamped3:
905 {
906 // third derivative at end point given
907 eq[1].setup( 0.0, 1.0, -1.0, -0.5 * valueEnd * hn );
908 break;
909 }
910 case QwtSpline::LinearRunout:
911 {
912 const double rn = qBound( 0.0, valueEnd, 1.0 );
913 if ( rn == 0.0 )
914 {
915 // clamping sn
916 eq[1].setup( 0.0, 1.0 / 3.0 * hn, 2.0 / 3.0 * hn, 0.0 );
917 }
918 else
919 {
920 eq[1].setup( 0.0, 2.0 + 1.0 / rn, 1.0 + 2.0 / rn, 0.0 );
921 }
922
923 break;
924 }
925 case QwtSplineC2::NotAKnot:
926 case QwtSplineC2::CubicRunout:
927 {
928 // building one cubic curve from 3 points
929
930 double vn;
931
932 if ( conditionEnd == QwtSplineC2::CubicRunout )
933 {
934 // last point is the endpoints of the curve
935 vn = 1.0;
936 }
937 else
938 {
939 // last points on the curve,
940 // the imaginary endpoints have the same distance as hn
941
942 vn = hn / ( points[n - 2].x() - points[n - 3].x() );
943 }
944
945 eq[1].setup( vn, -( 1.0 + vn ), 1.0, 0.0 );
946 break;
947 }
948 default:
949 {
950 // a natural spline, where the
951 // second derivative at end points set to 0.0
952 eq[1].setup( 0.0, 0.0, 1.0, 0.0 );
953 break;
954 }
955 }
956 }
957
958 class QwtSplineCubic::PrivateData
959 {
960 };
961
966 QwtSplineCubic::QwtSplineCubic()
967 : m_data( nullptr )
968 {
969 // a natural spline
970
971 setBoundaryCondition( QwtSpline::AtBeginning, QwtSpline::Clamped2 );
972 setBoundaryValue( QwtSpline::AtBeginning, 0.0 );
973
974 setBoundaryCondition( QwtSpline::AtEnd, QwtSpline::Clamped2 );
975 setBoundaryValue( QwtSpline::AtEnd, 0.0 );
976 }
977
979 QwtSplineCubic::~QwtSplineCubic()
980 {
981 }
982
989 uint QwtSplineCubic::locality() const
990 {
991 return 0;
992 }
993
1006 QVector< double > QwtSplineCubic::slopes( const QPolygonF& points ) const
1007 {
1008 using namespace QwtSplineCubicP;
1009
1010 if ( points.size() <= 2 )
1011 return QVector< double >();
1012
1013 if ( ( boundaryType() == QwtSpline::PeriodicPolygon )
1014 || ( boundaryType() == QwtSpline::ClosedPolygon ) )
1015 {
1016 EquationSystem2< SlopeStore > eqs;
1017 eqs.resolve( points );
1018
1019 return eqs.store().slopes();
1020 }
1021
1022 if ( points.size() == 3 )
1023 {
1024 if ( boundaryCondition( QwtSpline::AtBeginning ) == QwtSplineCubic::NotAKnot
1025 || boundaryCondition( QwtSpline::AtEnd ) == QwtSplineCubic::NotAKnot )
1026 {
1027 #if 0
1028 const double h0 = points[1].x() - points[0].x();
1029 const double h1 = points[2].x() - points[1].x();
1030
1031 const double s0 = ( points[1].y() - points[0].y() ) / h0;
1032 const double s1 = ( points[2].y() - points[1].y() ) / h1;
1033
1034 /*
1035 the system is under-determined and we only
1036 compute a quadratic spline.
1037 */
1038
1039 const double b = ( s1 - s0 ) / ( h0 + h1 );
1040
1041 QVector< double > m( 3 );
1042 m[0] = s0 - h0 * b;
1043 m[1] = s1 - h1 * b;
1044 m[2] = s1 + h1 * b;
1045
1046 return m;
1047 #else
1048 return QVector< double >();
1049 #endif
1050 }
1051 }
1052
1053 Equation3 eq[2];
1054 qwtSetupEndEquations(
1055 boundaryCondition( QwtSpline::AtBeginning ),
1056 boundaryValue( QwtSpline::AtBeginning ),
1057 boundaryCondition( QwtSpline::AtEnd ),
1058 boundaryValue( QwtSpline::AtEnd ),
1059 points, eq );
1060
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 );
1065
1066 return eqs.store().slopes();
1067 }
1068
1078 QVector< double > QwtSplineCubic::curvatures( const QPolygonF& points ) const
1079 {
1080 using namespace QwtSplineCubicP;
1081
1082 if ( points.size() <= 2 )
1083 return QVector< double >();
1084
1085 if ( ( boundaryType() == QwtSpline::PeriodicPolygon )
1086 || ( boundaryType() == QwtSpline::ClosedPolygon ) )
1087 {
1088 EquationSystem2< CurvatureStore > eqs;
1089 eqs.resolve( points );
1090
1091 return eqs.store().curvatures();
1092 }
1093
1094 if ( points.size() == 3 )
1095 {
1096 if ( boundaryCondition( QwtSpline::AtBeginning ) == QwtSplineC2::NotAKnot
1097 || boundaryCondition( QwtSpline::AtEnd ) == QwtSplineC2::NotAKnot )
1098 {
1099 return QVector< double >();
1100 }
1101 }
1102
1103 Equation3 eq[2];
1104 qwtSetupEndEquations(
1105 boundaryCondition( QwtSpline::AtBeginning ),
1106 boundaryValue( QwtSpline::AtBeginning ),
1107 boundaryCondition( QwtSpline::AtEnd ),
1108 boundaryValue( QwtSpline::AtEnd ),
1109 points, eq );
1110
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 );
1115
1116 return eqs.store().curvatures();
1117 }
1118
1130 QPainterPath QwtSplineCubic::painterPath( const QPolygonF& points ) const
1131 {
1132 // as QwtSplineCubic can calculate slopes directly we can
1133 // use the implementation of QwtSplineC1 without any performance loss.
1134
1135 return QwtSplineC1::painterPath( points );
1136 }
1137
1149 QVector< QLineF > QwtSplineCubic::bezierControlLines( const QPolygonF& points ) const
1150 {
1151 // as QwtSplineCubic can calculate slopes directly we can
1152 // use the implementation of QwtSplineC1 without any performance loss.
1153
1154 return QwtSplineC1::bezierControlLines( points );
1155 }
1156
1167 QVector< QwtSplinePolynomial > QwtSplineCubic::polynomials( const QPolygonF& points ) const
1168 {
1169 return QwtSplineC2::polynomials( points );
1170 }
1171
QVector< double >
QwtSplineC1::painterPath
virtual QPainterPath painterPath(const QPolygonF &) const override
Calculate an interpolated painter path.
Definition: qwt_spline.cpp:1043
QwtSplineC1::bezierControlLines
virtual QVector< QLineF > bezierControlLines(const QPolygonF &) const override
Interpolate a curve with Bezier curves.
Definition: qwt_spline.cpp:1101
QwtSplineC2::NotAKnot
@ NotAKnot
Definition: qwt_spline.h:292
QwtSplineC2::CubicRunout
@ CubicRunout
Definition: qwt_spline.h:283
QwtSplineC2::polynomials
virtual QVector< QwtSplinePolynomial > polynomials(const QPolygonF &) const override
Calculate the interpolating polynomials for a non parametric spline.
Definition: qwt_spline.cpp:1381
QwtSplineCubic::QwtSplineCubic
QwtSplineCubic()
Constructor The default setting is a non closing natural spline with no parametrization.
Definition: qwt_spline_cubic.cpp:966
QwtSplineCubic::~QwtSplineCubic
virtual ~QwtSplineCubic()
Destructor.
Definition: qwt_spline_cubic.cpp:979
QwtSplineCubic::locality
virtual uint locality() const override
Definition: qwt_spline_cubic.cpp:989
QwtSplineCubic::painterPath
virtual QPainterPath painterPath(const QPolygonF &) const override
Interpolate a curve with Bezier curves.
Definition: qwt_spline_cubic.cpp:1130
QwtSplineCubic::bezierControlLines
virtual QVector< QLineF > bezierControlLines(const QPolygonF &points) const override
Interpolate a curve with Bezier curves.
Definition: qwt_spline_cubic.cpp:1149
QwtSplineCubic::curvatures
virtual QVector< double > curvatures(const QPolygonF &) const override
Find the second derivative at the control points.
Definition: qwt_spline_cubic.cpp:1078
QwtSplineCubic::slopes
virtual QVector< double > slopes(const QPolygonF &) const override
Find the first derivative at the control points.
Definition: qwt_spline_cubic.cpp:1006
QwtSplineCubic::polynomials
virtual QVector< QwtSplinePolynomial > polynomials(const QPolygonF &) const override
Calculate the interpolating polynomials for a non parametric spline.
Definition: qwt_spline_cubic.cpp:1167
QwtSpline::AtBeginning
@ AtBeginning
the condition is at the beginning of the polynomial
Definition: qwt_spline.h:102
QwtSpline::AtEnd
@ AtEnd
the condition is at the end of the polynomial
Definition: qwt_spline.h:105
QwtSpline::boundaryValue
double boundaryValue(BoundaryPosition) const
Definition: qwt_spline.cpp:682
QwtSpline::setBoundaryCondition
void setBoundaryCondition(BoundaryPosition, int condition)
Define the condition for an endpoint of the spline.
Definition: qwt_spline.cpp:639
QwtSpline::boundaryCondition
int boundaryCondition(BoundaryPosition) const
Definition: qwt_spline.cpp:651
QwtSpline::boundaryType
BoundaryType boundaryType() const
Definition: qwt_spline.cpp:626
QwtSpline::setBoundaryValue
void setBoundaryValue(BoundaryPosition, double value)
Define the boundary value.
Definition: qwt_spline.cpp:670
QwtSpline::ClosedPolygon
@ ClosedPolygon
Definition: qwt_spline.h:92
QwtSpline::PeriodicPolygon
@ PeriodicPolygon
Definition: qwt_spline.h:79
QwtSpline::Clamped2
@ Clamped2
Definition: qwt_spline.h:134
QwtSpline::LinearRunout
@ LinearRunout
Definition: qwt_spline.h:153
QwtSpline::Clamped3
@ Clamped3
Definition: qwt_spline.h:143
QwtSpline::Clamped1
@ Clamped1
Definition: qwt_spline.h:125