Qwt User's Guide 6.3.0
Loading...
Searching...
No Matches
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
19namespace 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
201namespace 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
376static 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
382static 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
390namespace 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];
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:
777 T m_store;
778 };
779}
780
781static 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 {
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 }
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 }
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 }
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 }
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 {
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 }
899 {
900 // second derivative at end points given
901 eq[1].setup( 0.0, 0.0, 1.0, 0.5 * valueEnd );
902 break;
903 }
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 }
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 }
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
958class QwtSplineCubic::PrivateData
959{
960};
961
977
982
990{
991 return 0;
992}
993
1006QVector< double > QwtSplineCubic::slopes( const QPolygonF& points ) const
1007{
1008 using namespace QwtSplineCubicP;
1009
1010 if ( points.size() <= 2 )
1011 return QVector< double >();
1012
1015 {
1016 EquationSystem2< SlopeStore > eqs;
1017 eqs.resolve( points );
1018
1019 return eqs.store().slopes();
1020 }
1021
1022 if ( points.size() == 3 )
1023 {
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(
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
1078QVector< double > QwtSplineCubic::curvatures( const QPolygonF& points ) const
1079{
1080 using namespace QwtSplineCubicP;
1081
1082 if ( points.size() <= 2 )
1083 return QVector< double >();
1084
1087 {
1088 EquationSystem2< CurvatureStore > eqs;
1089 eqs.resolve( points );
1090
1091 return eqs.store().curvatures();
1092 }
1093
1094 if ( points.size() == 3 )
1095 {
1098 {
1099 return QVector< double >();
1100 }
1101 }
1102
1103 Equation3 eq[2];
1104 qwtSetupEndEquations(
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
1130QPainterPath 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
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
1168{
1169 return QwtSplineC2::polynomials( points );
1170}
1171
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
Definition qwt_spline.h:102
@ AtEnd
the condition is at the end of the polynomial
Definition qwt_spline.h:105
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.
@ ClosedPolygon
Definition qwt_spline.h:92
@ PeriodicPolygon
Definition qwt_spline.h:79