qwt_spline_cubic.cpp
Go to the documentation of this file.
1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
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  d_sum( value ),
26  d_carry( 0.0 )
27  {
28  }
29 
30  inline void reset()
31  {
32  d_sum = d_carry = 0.0;
33  }
34 
35  inline double value() const
36  {
37  return d_sum;
38  }
39 
40  inline void add( double value )
41  {
42  const double y = value - d_carry;
43  const double t = d_sum + y;
44 
45  d_carry = ( t - d_sum ) - y;
46  d_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 d_sum;
71  double d_carry; // The carry from the previous operation
72  };
73 
75  {
76  public:
77  inline void setup( int size )
78  {
79  d_curvatures.resize( size );
80  d_cv = d_curvatures.data();
81  }
82 
83  inline void storeFirst( double,
84  const QPointF &, const QPointF &, double b1, double )
85  {
86  d_cv[0] = 2.0 * b1;
87  }
88 
89  inline void storeNext( int index, double,
90  const QPointF &, const QPointF &, double, double b2 )
91  {
92  d_cv[index] = 2.0 * b2;
93  }
94 
95  inline void storeLast( double,
96  const QPointF &, const QPointF &, double, double b2 )
97  {
98  d_cv[d_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  d_cv[index] = 2.0 * b1;
105  }
106 
107  inline void closeR()
108  {
109  d_cv[0] = d_cv[d_curvatures.size() - 1];
110  }
111 
112  QVector<double> curvatures() const { return d_curvatures; }
113 
114  private:
116  double *d_cv;
117  };
118 
120  {
121  public:
122  inline void setup( int size )
123  {
124  d_slopes.resize( size );
125  d_m = d_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  d_m[0] = s - h * ( 2.0 * b1 + b2 ) / 3.0;
133 #if KAHAN
134  d_sum.add( d_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  d_sum.add( ( b1 + b2 ) * h );
146  d_m[index] = d_sum.value();
147 #else
148  d_m[index] = d_m[index-1] + ( b1 + b2 ) * h;
149 #endif
150 #else
151  const double s = ( p2.y() - p1.y() ) / h;
152  d_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  d_m[d_slopes.size() - 1] = s + h * ( b1 + 2.0 * b2 ) / 3.0;
161 #if KAHAN
162  d_sum.add( d_m[d_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  d_sum.add( -( b1 + b2 ) * h );
174  d_m[index] = d_sum.value();
175 #else
176  d_m[index] = d_m[index+1] - ( b1 + b2 ) * h;
177 #endif
178 
179 #else
180  const double s = ( p2.y() - p1.y() ) / h;
181  d_m[index] = s - h * ( 2.0 * b1 + b2 ) / 3.0;
182 #endif
183  }
184 
185  inline void closeR()
186  {
187  d_m[0] = d_m[d_slopes.size() - 1];
188  }
189 
190  QVector<double> slopes() const { return d_slopes; }
191 
192  private:
194  double *d_m;
195 #if SLOPES_INCREMENTAL
196  KahanSum d_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>
394  {
395  public:
396  void setStartCondition( double p, double q, double u, double r )
397  {
398  d_conditionsEQ[0].setup( p, q, u, r );
399  }
400 
401  void setEndCondition( double p, double q, double u, double r )
402  {
403  d_conditionsEQ[1].setup( p, q, u, r );
404  }
405 
406  const T &store() const
407  {
408  return d_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 ( d_conditionsEQ[0].p == 0.0 ||
418  ( d_conditionsEQ[0].q == 0.0 && d_conditionsEQ[0].u != 0.0 ) )
419  {
420  return;
421  }
422 
423  if ( d_conditionsEQ[1].u == 0.0 ||
424  ( d_conditionsEQ[1].q == 0.0 && d_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  d_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 = d_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 ( d_conditionsEQ[0].normalized() == d_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 = d_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  d_store.storeFirst( h0, p[0], p[1], b0, b1 );
470  d_store.storeNext( 1, h0, p[0], p[1], b0, b1 );
471  d_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 = d_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 ( d_conditionsEQ[0].u == 0.0 )
491  {
492  eq = eqSpline0.substituted3( eq );
493 
494  const Equation3 &eq0 = d_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 = d_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  d_store.storeFirst( h0, p[0], p[1], b0, b1 );
508  d_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 = d_conditionsEQ[1].resolved3( bn2, bn1 );
514 
515  const double hx = p[n-2].x() - p[n-3].x();
516  d_store.storeNext( n - 2, hx, p[n-3], p[n-2], bn2, bn1 );
517  d_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  d_eq.resize( n - 2 );
526  d_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 = d_eq[i+1];
535  Equation2 &eq1 = d_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 d_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 = d_eq[i].resolved2( b1 );
560  d_store.storeNext( i, d_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 d_conditionsEQ[2];
572  };
573 
574  template <class T>
576  {
577  public:
578  const T &store() const
579  {
580  return d_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  d_store.setup( 3 );
603  d_store.storeLast( h1, p[1], p[2], -b, b );
604  d_store.storePrevious( 1, h1, p[1], p[2], -b, b );
605  d_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  d_store.setup( n );
619  d_store.storeLast( hn, p[n-2], p[n-1], bn, b0 );
620  d_store.storePrevious( n - 2, hn, p[n-2], p[n-1], bn, b0 );
621 
622  resolveSpline( p, b0, bn );
623 
624  d_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  d_eq.resize( n - 1 );
640 
641  double dq = 0;
642  double dr = 0;
643 
644  d_eq[1] = eqSpline0;
645 
646  double slope1 = ( points[2].y() - points[1].y() ) / d_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 = d_eq[i-1];
663  Equation3 &eq2 = d_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] * d_p[n-2] + b[n-2] * d_q[n-2] + b[n-1] * pN = d_r[n-2]
682  eqn.setup( d_eq[n-2].q, d_eq[n-2].p + eqSplineN.p , d_eq[n-2].r );
683 
684  // b[n-2] * pN + b[0] * ( qN - dq ) + b[n-2] * d_p[n-2] = rN - dr
685  eqX.setup( d_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 = d_eq[i];
695 
696  const double b = eq.resolved2( b0, bi );
697  d_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 = d_eq[0].resolved3( b0, bi );
709 
710  for ( int i = 1; i < n - 2; i++ )
711  {
712  const Equation3 &eq = d_eq[i];
713 
714  const double b = eq.resolved3( b0, bi );
715  m[i+1] = m[i] + ( b + bi ) * d_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:
778  };
779 }
780 
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  }
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  {
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  }
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 
959 {
960 public:
962  {
963  }
964 };
965 
971 {
972  d_data = new PrivateData;
973 
974  // a natural spline
975 
976  setBoundaryCondition( QwtSpline::AtBeginning, QwtSpline::Clamped2 );
977  setBoundaryValue( QwtSpline::AtBeginning, 0.0 );
978 
979  setBoundaryCondition( QwtSpline::AtEnd, QwtSpline::Clamped2 );
980  setBoundaryValue( QwtSpline::AtEnd, 0.0 );
981 }
982 
985 {
986  delete d_data;
987 }
988 
996 {
997  return 0;
998 }
999 
1012 QVector<double> QwtSplineCubic::slopes( const QPolygonF &points ) const
1013 {
1014  using namespace QwtSplineCubicP;
1015 
1016  if ( points.size() <= 2 )
1017  return QVector<double>();
1018 
1019  if ( ( boundaryType() == QwtSpline::PeriodicPolygon )
1020  || ( boundaryType() == QwtSpline::ClosedPolygon ) )
1021  {
1023  eqs.resolve( points );
1024 
1025  return eqs.store().slopes();
1026  }
1027 
1028  if ( points.size() == 3 )
1029  {
1030  if ( boundaryCondition( QwtSpline::AtBeginning ) == QwtSplineCubic::NotAKnot
1031  || boundaryCondition( QwtSpline::AtEnd ) == QwtSplineCubic::NotAKnot )
1032  {
1033 #if 0
1034  const double h0 = points[1].x() - points[0].x();
1035  const double h1 = points[2].x() - points[1].x();
1036 
1037  const double s0 = ( points[1].y() - points[0].y() ) / h0;
1038  const double s1 = ( points[2].y() - points[1].y() ) / h1;
1039 
1040  /*
1041  the system is under-determined and we only
1042  compute a quadratic spline.
1043  */
1044 
1045  const double b = ( s1 - s0 ) / ( h0 + h1 );
1046 
1047  QVector<double> m( 3 );
1048  m[0] = s0 - h0 * b;
1049  m[1] = s1 - h1 * b;
1050  m[2] = s1 + h1 * b;
1051 
1052  return m;
1053 #else
1054  return QVector<double>();
1055 #endif
1056  }
1057  }
1058 
1059  Equation3 eq[2];
1061  boundaryCondition( QwtSpline::AtBeginning ),
1062  boundaryValue( QwtSpline::AtBeginning ),
1063  boundaryCondition( QwtSpline::AtEnd ),
1064  boundaryValue( QwtSpline::AtEnd ),
1065  points, eq );
1066 
1068  eqs.setStartCondition( eq[0].p, eq[0].q, eq[0].u, eq[0].r );
1069  eqs.setEndCondition( eq[1].p, eq[1].q, eq[1].u, eq[1].r );
1070  eqs.resolve( points );
1071 
1072  return eqs.store().slopes();
1073 }
1074 
1084 QVector<double> QwtSplineCubic::curvatures( const QPolygonF &points ) const
1085 {
1086  using namespace QwtSplineCubicP;
1087 
1088  if ( points.size() <= 2 )
1089  return QVector<double>();
1090 
1091  if ( ( boundaryType() == QwtSpline::PeriodicPolygon )
1092  || ( boundaryType() == QwtSpline::ClosedPolygon ) )
1093  {
1095  eqs.resolve( points );
1096 
1097  return eqs.store().curvatures();
1098  }
1099 
1100  if ( points.size() == 3 )
1101  {
1102  if ( boundaryCondition( QwtSpline::AtBeginning ) == QwtSplineC2::NotAKnot
1103  || boundaryCondition( QwtSpline::AtEnd ) == QwtSplineC2::NotAKnot )
1104  {
1105  return QVector<double>();
1106  }
1107  }
1108 
1109  Equation3 eq[2];
1111  boundaryCondition( QwtSpline::AtBeginning ),
1112  boundaryValue( QwtSpline::AtBeginning ),
1113  boundaryCondition( QwtSpline::AtEnd ),
1114  boundaryValue( QwtSpline::AtEnd ),
1115  points, eq );
1116 
1118  eqs.setStartCondition( eq[0].p, eq[0].q, eq[0].u, eq[0].r );
1119  eqs.setEndCondition( eq[1].p, eq[1].q, eq[1].u, eq[1].r );
1120  eqs.resolve( points );
1121 
1122  return eqs.store().curvatures();
1123 }
1124 
1136 QPainterPath QwtSplineCubic::painterPath( const QPolygonF &points ) const
1137 {
1138  // as QwtSplineCubic can calcuate slopes directly we can
1139  // use the implementation of QwtSplineC1 without any performance loss.
1140 
1141  return QwtSplineC1::painterPath( points );
1142 }
1143 
1155 QVector<QLineF> QwtSplineCubic::bezierControlLines( const QPolygonF &points ) const
1156 {
1157  // as QwtSplineCubic can calcuate slopes directly we can
1158  // use the implementation of QwtSplineC1 without any performance loss.
1159 
1160  return QwtSplineC1::bezierControlLines( points );
1161 }
1162 
1174 {
1175  return QwtSplineC2::polynomials( points );
1176 }
1177 
virtual ~QwtSplineCubic()
Destructor.
Equation2 substituted2(const Equation3 &eq) const
void storeLast(double h, const QPointF &p1, const QPointF &p2, double b1, double b2)
static double sum3(double d1, double d2, double d3)
static double sum4(double d1, double d2, double d3, double d4)
Equation2 substituteSpline(const QPolygonF &points, const Equation2 &eq)
Equation2 substituted3(const Equation2 &eq) const
static void qwtSetupEndEquations(int conditionBegin, double valueBegin, int conditionEnd, double valueEnd, const QPolygonF &points, QwtSplineCubicP::Equation3 eq[2])
virtual QVector< QwtSplinePolynomial > polynomials(const QPolygonF &) const QWT_OVERRIDE
Calculate the interpolating polynomials for a non parametric spline.
void storeLast(double, const QPointF &, const QPointF &, double, double b2)
Equation3(double cp, double cq, double du, double dr)
void resolveSpline2(const QPolygonF &points, double b0, double bi, QVector< double > &m)
void storeFirst(double h, const QPointF &p1, const QPointF &p2, double b1, double b2)
void setup(double p0, double q0, double r0)
void setup(double cp, double cq, double du, double dr)
void resolve(const QPolygonF &p)
void storeNext(int index, double, const QPointF &, const QPointF &, double, double b2)
QVector< double > slopes() const
the condiation is at the end of the polynomial
Definition: qwt_spline.h:102
void storeNext(int index, double h, const QPointF &p1, const QPointF &p2, double b1, double b2)
virtual QVector< QLineF > bezierControlLines(const QPolygonF &) const QWT_OVERRIDE
Interpolate a curve with Bezier curves.
QVector< double > curvatures() const
virtual QPainterPath painterPath(const QPolygonF &) const QWT_OVERRIDE
Calculate an interpolated painter path.
Equation2 substituted3(const Equation3 &eq) const
virtual QVector< QLineF > bezierControlLines(const QPolygonF &points) const QWT_OVERRIDE
Interpolate a curve with Bezier curves.
the condiation is at the beginning of the polynomial
Definition: qwt_spline.h:99
QDebug operator<<(QDebug debug, const QwtInterval &interval)
void resolve(const QPolygonF &p)
Equation2 substituted1(const Equation2 &eq) const
double resolved1(double x2) const
Equation2 substituted1(const Equation3 &eq) const
void storePrevious(int index, double h, const QPointF &p1, const QPointF &p2, double b1, double b2)
bool operator==(const Equation3 &c) const
virtual uint locality() const QWT_OVERRIDE
void setStartCondition(double p, double q, double u, double r)
void setEndCondition(double p, double q, double u, double r)
void resolveSpline3(const QPolygonF &points, double b0, double b1, QVector< double > &m)
virtual QVector< double > slopes(const QPolygonF &) const QWT_OVERRIDE
Find the first derivative at the control points.
MQTTClient c
Definition: test10.c:1656
Equation2(double p0, double q0, double r0)
double resolved1(double x2, double x3) const
void storeFirst(double, const QPointF &, const QPointF &, double b1, double)
double resolveSpline(const QPolygonF &points, double b1)
double resolved2(double x1, double x3) const
double resolved3(double x1, double x2) const
double resolved2(double x1) const
virtual QVector< QwtSplinePolynomial > polynomials(const QPolygonF &) const QWT_OVERRIDE
Calculate the interpolating polynomials for a non parametric spline.
virtual QPainterPath painterPath(const QPolygonF &) const QWT_OVERRIDE
Interpolate a curve with Bezier curves.
virtual QVector< double > curvatures(const QPolygonF &) const QWT_OVERRIDE
Find the second derivative at the control points.
double resolved1(const Equation2 &eq) const
QwtSplineCubic()
Constructor The default setting is a non closing natural spline with no parametrization.
Equation3(const QPointF &p1, const QPointF &p2, const QPointF &p3)
void resolveSpline4(const QPolygonF &points, double b2, double b1, QVector< double > &m)
void resolveSpline(const QPolygonF &points, double b0, double bi)
void substitute(const QPolygonF &points, Equation2 &eqn, Equation2 &eqX)
double resolved2(const Equation2 &eq) const
void storePrevious(int index, double, const QPointF &, const QPointF &, double b1, double)


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Dec 6 2020 03:48:10