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