qwt_date_scale_engine.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_date_scale_engine.h"
11 #include "qwt_math.h"
12 #include "qwt_transform.h"
13 #include <qdatetime.h>
14 #include <limits.h>
15 
17 {
18  static const double msecs[] =
19  {
20  1.0,
21  1000.0,
22  60.0 * 1000.0,
23  3600.0 * 1000.0,
24  24.0 * 3600.0 * 1000.0,
25  7.0 * 24.0 * 3600.0 * 1000.0,
26  30.0 * 24.0 * 3600.0 * 1000.0,
27  365.0 * 24.0 * 3600.0 * 1000.0,
28  };
29 
30  if ( type < 0 || type >= static_cast<int>( sizeof( msecs ) / sizeof( msecs[0] ) ) )
31  return 1.0;
32 
33  return msecs[ type ];
34 }
35 
36 static inline int qwtAlignValue(
37  double value, double stepSize, bool up )
38 {
39  double d = value / stepSize;
40  d = up ? ::ceil( d ) : ::floor( d );
41 
42  return static_cast<int>( d * stepSize );
43 }
44 
45 static double qwtIntervalWidth( const QDateTime &minDate,
46  const QDateTime &maxDate, QwtDate::IntervalType intervalType )
47 {
48  switch( intervalType )
49  {
51  {
52  const double secsTo = minDate.secsTo( maxDate );
53  const double msecs = maxDate.time().msec() -
54  minDate.time().msec();
55 
56  return secsTo * 1000 + msecs;
57  }
58  case QwtDate::Second:
59  {
60  return minDate.secsTo( maxDate );
61  }
62  case QwtDate::Minute:
63  {
64  const double secsTo = minDate.secsTo( maxDate );
65  return ::floor( secsTo / 60 );
66  }
67  case QwtDate::Hour:
68  {
69  const double secsTo = minDate.secsTo( maxDate );
70  return ::floor( secsTo / 3600 );
71  }
72  case QwtDate::Day:
73  {
74  return minDate.daysTo( maxDate );
75  }
76  case QwtDate::Week:
77  {
78  return ::floor( minDate.daysTo( maxDate ) / 7.0 );
79  }
80  case QwtDate::Month:
81  {
82  const double years =
83  double( maxDate.date().year() ) - minDate.date().year();
84 
85  int months = maxDate.date().month() - minDate.date().month();
86  if ( maxDate.date().day() < minDate.date().day() )
87  months--;
88 
89  return years * 12 + months;
90  }
91  case QwtDate::Year:
92  {
93  double years =
94  double( maxDate.date().year() ) - minDate.date().year();
95 
96  if ( maxDate.date().month() < minDate.date().month() )
97  years -= 1.0;
98 
99  return years;
100  }
101  }
102 
103  return 0.0;
104 }
105 
106 static double qwtRoundedIntervalWidth(
107  const QDateTime &minDate, const QDateTime &maxDate,
108  QwtDate::IntervalType intervalType )
109 {
110  const QDateTime minD = QwtDate::floor( minDate, intervalType );
111  const QDateTime maxD = QwtDate::ceil( maxDate, intervalType );
112 
113  return qwtIntervalWidth( minD, maxD, intervalType );
114 }
115 
116 static inline int qwtStepCount( int intervalSize, int maxSteps,
117  const int limits[], size_t numLimits )
118 {
119  for ( uint i = 0; i < numLimits; i++ )
120  {
121  const int numSteps = intervalSize / limits[ i ];
122 
123  if ( numSteps > 1 && numSteps <= maxSteps &&
124  numSteps * limits[ i ] == intervalSize )
125  {
126  return numSteps;
127  }
128  }
129 
130  return 0;
131 }
132 
133 static int qwtStepSize( int intervalSize, int maxSteps, uint base )
134 {
135  if ( maxSteps <= 0 )
136  return 0;
137 
138  if ( maxSteps > 2 )
139  {
140  for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
141  {
142  const double stepSize = double( intervalSize ) / numSteps;
143 
144  const double p = ::floor( ::log( stepSize ) / ::log( double( base ) ) );
145  const double fraction = qPow( base, p );
146 
147  for ( uint n = base; n >= 1; n /= 2 )
148  {
149  if ( qFuzzyCompare( stepSize, n * fraction ) )
150  return qRound( stepSize );
151 
152  if ( n == 3 && ( base % 2 ) == 0 )
153  {
154  if ( qFuzzyCompare( stepSize, 2 * fraction ) )
155  return qRound( stepSize );
156  }
157  }
158  }
159  }
160 
161  return 0;
162 }
163 
164 static int qwtDivideInterval( double intervalSize, int numSteps,
165  const int limits[], size_t numLimits )
166 {
167  const int v = qCeil( intervalSize / double( numSteps ) );
168 
169  for ( uint i = 0; i < numLimits - 1; i++ )
170  {
171  if ( v <= limits[i] )
172  return limits[i];
173  }
174 
175  return limits[ numLimits - 1 ];
176 }
177 
178 static double qwtDivideScale( double intervalSize, int numSteps,
179  QwtDate::IntervalType intervalType )
180 {
181  if ( intervalType != QwtDate::Day )
182  {
183  if ( ( intervalSize > numSteps ) &&
184  ( intervalSize <= 2 * numSteps ) )
185  {
186  return 2.0;
187  }
188  }
189 
190  double stepSize;
191 
192  switch( intervalType )
193  {
194  case QwtDate::Second:
195  case QwtDate::Minute:
196  {
197  static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
198 
199  stepSize = qwtDivideInterval( intervalSize, numSteps,
200  limits, sizeof( limits ) / sizeof( int ) );
201 
202  break;
203  }
204  case QwtDate::Hour:
205  {
206  static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
207 
208  stepSize = qwtDivideInterval( intervalSize, numSteps,
209  limits, sizeof( limits ) / sizeof( int ) );
210 
211  break;
212  }
213  case QwtDate::Day:
214  {
215  const double v = intervalSize / double( numSteps );
216  if ( v <= 5.0 )
217  stepSize = qCeil( v );
218  else
219  stepSize = qCeil( v / 7 ) * 7;
220 
221  break;
222  }
223  case QwtDate::Week:
224  {
225  static int limits[] = { 1, 2, 4, 8, 12, 26, 52 };
226 
227  stepSize = qwtDivideInterval( intervalSize, numSteps,
228  limits, sizeof( limits ) / sizeof( int ) );
229 
230  break;
231  }
232  case QwtDate::Month:
233  {
234  static int limits[] = { 1, 2, 3, 4, 6, 12 };
235 
236  stepSize = qwtDivideInterval( intervalSize, numSteps,
237  limits, sizeof( limits ) / sizeof( int ) );
238 
239  break;
240  }
241  case QwtDate::Year:
243  default:
244  {
246  intervalSize, numSteps, 10 );
247  }
248  }
249 
250  return stepSize;
251 }
252 
253 static double qwtDivideMajorStep( double stepSize, int maxMinSteps,
254  QwtDate::IntervalType intervalType )
255 {
256  double minStepSize = 0.0;
257 
258  switch( intervalType )
259  {
260  case QwtDate::Second:
261  {
262  minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 );
263  if ( minStepSize == 0.0 )
264  minStepSize = 0.5 * stepSize;
265 
266  break;
267  }
268  case QwtDate::Minute:
269  {
270  static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
271 
272  int numSteps;
273 
274  if ( stepSize > maxMinSteps )
275  {
276  numSteps = qwtStepCount( stepSize, maxMinSteps,
277  limits, sizeof( limits ) / sizeof( int ) );
278 
279  }
280  else
281  {
282  numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
283  limits, sizeof( limits ) / sizeof( int ) );
284  }
285 
286  if ( numSteps > 0 )
287  minStepSize = double( stepSize ) / numSteps;
288 
289  break;
290  }
291  case QwtDate::Hour:
292  {
293  int numSteps = 0;
294 
295  if ( stepSize > maxMinSteps )
296  {
297  static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
298 
299  numSteps = qwtStepCount( stepSize, maxMinSteps,
300  limits, sizeof( limits ) / sizeof( int ) );
301  }
302  else
303  {
304  static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
305 
306  numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
307  limits, sizeof( limits ) / sizeof( int ) );
308  }
309 
310  if ( numSteps > 0 )
311  minStepSize = double( stepSize ) / numSteps;
312 
313  break;
314  }
315  case QwtDate::Day:
316  {
317  int numSteps = 0;
318 
319  if ( stepSize > maxMinSteps )
320  {
321  static int limits[] = { 1, 2, 3, 7, 14, 28 };
322 
323  numSteps = qwtStepCount( stepSize, maxMinSteps,
324  limits, sizeof( limits ) / sizeof( int ) );
325  }
326  else
327  {
328  static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
329 
330  numSteps = qwtStepCount( stepSize * 24, maxMinSteps,
331  limits, sizeof( limits ) / sizeof( int ) );
332  }
333 
334  if ( numSteps > 0 )
335  minStepSize = double( stepSize ) / numSteps;
336 
337  break;
338  }
339  case QwtDate::Week:
340  {
341  const int daysInStep = stepSize * 7;
342 
343  if ( maxMinSteps >= daysInStep )
344  {
345  // we want to have one tick per day
346  minStepSize = 1.0 / 7.0;
347  }
348  else
349  {
350  // when the stepSize is more than a week we want to
351  // have a tick for each week
352 
353  const int stepSizeInWeeks = stepSize;
354 
355  if ( stepSizeInWeeks <= maxMinSteps )
356  {
357  minStepSize = 1;
358  }
359  else
360  {
361  minStepSize = QwtScaleArithmetic::divideInterval(
362  stepSizeInWeeks, maxMinSteps, 10 );
363  }
364  }
365  break;
366  }
367  case QwtDate::Month:
368  {
369  // fractions of months doesn't make any sense
370 
371  if ( stepSize < maxMinSteps )
372  maxMinSteps = static_cast<int>( stepSize );
373 
374  static int limits[] = { 1, 2, 3, 4, 6, 12 };
375 
376  int numSteps = qwtStepCount( stepSize, maxMinSteps,
377  limits, sizeof( limits ) / sizeof( int ) );
378 
379  if ( numSteps > 0 )
380  minStepSize = double( stepSize ) / numSteps;
381 
382  break;
383  }
384  case QwtDate::Year:
385  {
386  if ( stepSize >= maxMinSteps )
387  {
389  stepSize, maxMinSteps, 10 );
390  }
391  else
392  {
393  // something in months
394 
395  static int limits[] = { 1, 2, 3, 4, 6, 12 };
396 
397  int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps,
398  limits, sizeof( limits ) / sizeof( int ) );
399 
400  if ( numSteps > 0 )
401  minStepSize = double( stepSize ) / numSteps;
402  }
403 
404  break;
405  }
406  default:
407  break;
408  }
409 
410  if ( intervalType != QwtDate::Month
411  && minStepSize == 0.0 )
412  {
413  minStepSize = 0.5 * stepSize;
414  }
415 
416  return minStepSize;
417 }
418 
419 static QList<double> qwtDstTicks( const QDateTime &dateTime,
420  int secondsMajor, int secondsMinor )
421 {
422  if ( secondsMinor <= 0 )
423  QList<double>();
424 
425  QDateTime minDate = dateTime.addSecs( -secondsMajor );
426  minDate = QwtDate::floor( minDate, QwtDate::Hour );
427 
428  const double utcOffset = QwtDate::utcOffset( dateTime );
429 
430  // find the hours where daylight saving time happens
431 
432  double dstMin = QwtDate::toDouble( minDate );
433  while ( minDate < dateTime &&
434  QwtDate::utcOffset( minDate ) != utcOffset )
435  {
436  minDate = minDate.addSecs( 3600 );
437  dstMin += 3600 * 1000.0;
438  }
439 
440  QList<double> ticks;
441 #if QT_VERSION >= 0x040700
442  ticks.reserve( 3600 / secondsMinor);
443 #endif
444 
445  for ( int i = 0; i < 3600; i += secondsMinor )
446  ticks += dstMin + i * 1000.0;
447 
448  return ticks;
449 }
450 
452  const QDateTime &minDate, const QDateTime &maxDate,
453  double stepSize, int maxMinSteps,
454  QwtDate::IntervalType intervalType )
455 {
456  // calculate the min step size
457  double minStepSize = 0;
458 
459  if ( maxMinSteps > 1 )
460  {
461  minStepSize = qwtDivideMajorStep( stepSize,
462  maxMinSteps, intervalType );
463  }
464 
465  bool daylightSaving = false;
466  if ( minDate.timeSpec() == Qt::LocalTime )
467  {
468  daylightSaving = intervalType > QwtDate::Hour;
469  if ( intervalType == QwtDate::Hour )
470  {
471  daylightSaving = stepSize > 1;
472  }
473  }
474 
475  const double s = qwtMsecsForType( intervalType ) / 1000;
476  const int secondsMajor = static_cast<int>( stepSize * s );
477  const double secondsMinor = minStepSize * s;
478 
479  // UTC excludes daylight savings. So from the difference
480  // of a date and its UTC counterpart we can find out
481  // the daylight saving hours
482 
483  const double utcOffset = QwtDate::utcOffset( minDate );
484  double dstOff = 0;
485 
486  QList<double> majorTicks;
487  QList<double> mediumTicks;
488  QList<double> minorTicks;
489 
490  for ( QDateTime dt = minDate; dt <= maxDate;
491  dt = dt.addSecs( secondsMajor ) )
492  {
493  if ( !dt.isValid() )
494  break;
495 
496  double majorValue = QwtDate::toDouble( dt );
497 
498  if ( daylightSaving )
499  {
500  const double offset = utcOffset - QwtDate::utcOffset( dt );
501  majorValue += offset * 1000.0;
502 
503  if ( offset > dstOff )
504  {
505  // we add some minor ticks for the DST hour,
506  // otherwise the ticks will be unaligned: 0, 2, 3, 5 ...
507  minorTicks += qwtDstTicks(
508  dt, secondsMajor, qRound( secondsMinor ) );
509  }
510 
511  dstOff = offset;
512  }
513 
514  if ( majorTicks.isEmpty() || majorTicks.last() != majorValue )
515  majorTicks += majorValue;
516 
517  if ( secondsMinor > 0.0 )
518  {
519  const int numMinorSteps = qFloor( secondsMajor / secondsMinor );
520 
521  for ( int i = 1; i < numMinorSteps; i++ )
522  {
523  const QDateTime mt = dt.addMSecs(
524  qRound64( i * secondsMinor * 1000 ) );
525 
526  double minorValue = QwtDate::toDouble( mt );
527  if ( daylightSaving )
528  {
529  const double offset = utcOffset - QwtDate::utcOffset( mt );
530  minorValue += offset * 1000.0;
531  }
532 
533  if ( minorTicks.isEmpty() || minorTicks.last() != minorValue )
534  {
535  const bool isMedium = ( numMinorSteps % 2 == 0 )
536  && ( i != 1 ) && ( i == numMinorSteps / 2 );
537 
538  if ( isMedium )
539  mediumTicks += minorValue;
540  else
541  minorTicks += minorValue;
542  }
543  }
544  }
545  }
546 
547  QwtScaleDiv scaleDiv;
548 
549  scaleDiv.setInterval( QwtDate::toDouble( minDate ),
550  QwtDate::toDouble( maxDate ) );
551 
552  scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
553  scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
554  scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
555 
556  return scaleDiv;
557 }
558 
560  QDateTime &minDate, const QDateTime &maxDate,
561  double stepSize, int maxMinSteps )
562 {
563  // months are intervals with non
564  // equidistant ( in ms ) steps: we have to build the
565  // scale division manually
566 
567  int minStepDays = 0;
568  int minStepSize = 0.0;
569 
570  if ( maxMinSteps > 1 )
571  {
572  if ( stepSize == 1 )
573  {
574  if ( maxMinSteps >= 30 )
575  minStepDays = 1;
576  else if ( maxMinSteps >= 6 )
577  minStepDays = 5;
578  else if ( maxMinSteps >= 3 )
579  minStepDays = 10;
580  else
581  minStepDays = 15;
582  }
583  else
584  {
585  minStepSize = qwtDivideMajorStep(
586  stepSize, maxMinSteps, QwtDate::Month );
587  }
588  }
589 
590  QList<double> majorTicks;
591  QList<double> mediumTicks;
592  QList<double> minorTicks;
593 
594  for ( QDateTime dt = minDate;
595  dt <= maxDate; dt = dt.addMonths( stepSize ) )
596  {
597  if ( !dt.isValid() )
598  break;
599 
600  majorTicks += QwtDate::toDouble( dt );
601 
602  if ( minStepDays > 0 )
603  {
604  for ( int days = minStepDays;
605  days < 30; days += minStepDays )
606  {
607  const double tick = QwtDate::toDouble( dt.addDays( days ) );
608 
609  if ( days == 15 && minStepDays != 15 )
610  mediumTicks += tick;
611  else
612  minorTicks += tick;
613  }
614  }
615  else if ( minStepSize > 0.0 )
616  {
617  const int numMinorSteps = qRound( stepSize / (double) minStepSize );
618 
619  for ( int i = 1; i < numMinorSteps; i++ )
620  {
621  const double minorValue =
622  QwtDate::toDouble( dt.addMonths( i * minStepSize ) );
623 
624  if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) )
625  mediumTicks += minorValue;
626  else
627  minorTicks += minorValue;
628  }
629  }
630  }
631 
632  QwtScaleDiv scaleDiv;
633  scaleDiv.setInterval( QwtDate::toDouble( minDate ),
634  QwtDate::toDouble( maxDate ) );
635 
636  scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
637  scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
638  scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
639 
640  return scaleDiv;
641 }
642 
644  const QDateTime &minDate, const QDateTime &maxDate,
645  double stepSize, int maxMinSteps )
646 {
647  QList<double> majorTicks;
648  QList<double> mediumTicks;
649  QList<double> minorTicks;
650 
651  double minStepSize = 0.0;
652 
653  if ( maxMinSteps > 1 )
654  {
655  minStepSize = qwtDivideMajorStep(
656  stepSize, maxMinSteps, QwtDate::Year );
657  }
658 
659  int numMinorSteps = 0;
660  if ( minStepSize > 0.0 )
661  numMinorSteps = qFloor( stepSize / minStepSize );
662 
663  bool dateBC = minDate.date().year() < -1;
664 
665  for ( QDateTime dt = minDate; dt <= maxDate;
666  dt = dt.addYears( stepSize ) )
667  {
668  if ( dateBC && dt.date().year() > 1 )
669  {
670  // there is no year 0 in the Julian calendar
671  dt = dt.addYears( -1 );
672  dateBC = false;
673  }
674 
675  if ( !dt.isValid() )
676  break;
677 
678  majorTicks += QwtDate::toDouble( dt );
679 
680  for ( int i = 1; i < numMinorSteps; i++ )
681  {
682  QDateTime tickDate;
683 
684  const double years = qRound( i * minStepSize );
685  if ( years >= INT_MAX / 12 )
686  {
687  tickDate = dt.addYears( years );
688  }
689  else
690  {
691  tickDate = dt.addMonths( qRound( years * 12 ) );
692  }
693 
694  const bool isMedium = ( numMinorSteps > 2 ) &&
695  ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 );
696 
697  const double minorValue = QwtDate::toDouble( tickDate );
698  if ( isMedium )
699  mediumTicks += minorValue;
700  else
701  minorTicks += minorValue;
702  }
703 
704  if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() )
705  {
706  break;
707  }
708  }
709 
710  QwtScaleDiv scaleDiv;
711  scaleDiv.setInterval( QwtDate::toDouble( minDate ),
712  QwtDate::toDouble( maxDate ) );
713 
714  scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
715  scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
716  scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
717 
718  return scaleDiv;
719 }
720 
722 {
723 public:
724  explicit PrivateData( Qt::TimeSpec spec ):
725  timeSpec( spec ),
726  utcOffset( 0 ),
727  week0Type( QwtDate::FirstThursday ),
728  maxWeeks( 4 )
729  {
730  }
731 
732  Qt::TimeSpec timeSpec;
735  int maxWeeks;
736 };
737 
738 
753 {
754  d_data = new PrivateData( timeSpec );
755 }
756 
759 {
760  delete d_data;
761 }
762 
770 {
772 }
773 
778 Qt::TimeSpec QwtDateScaleEngine::timeSpec() const
779 {
780  return d_data->timeSpec;
781 }
782 
794 {
795  d_data->utcOffset = seconds;
796 }
797 
806 {
807  return d_data->utcOffset;
808 }
809 
820 {
822 }
823 
829 {
830  return d_data->week0Type;
831 }
832 
846 {
847  d_data->maxWeeks = qMax( weeks, 0 );
848 }
849 
856 {
857  return d_data->maxWeeks;
858 }
859 
870  const QDateTime &minDate, const QDateTime &maxDate,
871  int maxSteps ) const
872 {
873  const double jdMin = minDate.date().toJulianDay();
874  const double jdMax = maxDate.date().toJulianDay();
875 
876  if ( ( jdMax - jdMin ) / 365 > maxSteps )
877  return QwtDate::Year;
878 
879  const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month );
880  if ( months > maxSteps * 6 )
881  return QwtDate::Year;
882 
883  const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day );
884  const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week );
885 
886  if ( weeks > d_data->maxWeeks )
887  {
888  if ( days > 4 * maxSteps * 7 )
889  return QwtDate::Month;
890  }
891 
892  if ( days > maxSteps * 7 )
893  return QwtDate::Week;
894 
895  const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour );
896  if ( hours > maxSteps * 24 )
897  return QwtDate::Day;
898 
899  const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second );
900 
901  if ( seconds >= maxSteps * 3600 )
902  return QwtDate::Hour;
903 
904  if ( seconds >= maxSteps * 60 )
905  return QwtDate::Minute;
906 
907  if ( seconds >= maxSteps )
908  return QwtDate::Second;
909 
910  return QwtDate::Millisecond;
911 }
912 
929 void QwtDateScaleEngine::autoScale( int maxNumSteps,
930  double &x1, double &x2, double &stepSize ) const
931 {
932  stepSize = 0.0;
933 
934  QwtInterval interval( x1, x2 );
935  interval = interval.normalized();
936 
937  interval.setMinValue( interval.minValue() - lowerMargin() );
938  interval.setMaxValue( interval.maxValue() + upperMargin() );
939 
941  interval = interval.symmetrize( reference() );
942 
944  interval = interval.extend( reference() );
945 
946  if ( interval.width() == 0.0 )
947  interval = buildInterval( interval.minValue() );
948 
949  const QDateTime from = toDateTime( interval.minValue() );
950  const QDateTime to = toDateTime( interval.maxValue() );
951 
952  if ( from.isValid() && to.isValid() )
953  {
954  if ( maxNumSteps < 1 )
955  maxNumSteps = 1;
956 
957  const QwtDate::IntervalType intvType =
958  intervalType( from, to, maxNumSteps );
959 
960  const double width = qwtIntervalWidth( from, to, intvType );
961 
962  const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType );
963  if ( stepWidth != 0.0 && !testAttribute( QwtScaleEngine::Floating ) )
964  {
965  const QDateTime d1 = alignDate( from, stepWidth, intvType, false );
966  const QDateTime d2 = alignDate( to, stepWidth, intvType, true );
967 
968  interval.setMinValue( QwtDate::toDouble( d1 ) );
969  interval.setMaxValue( QwtDate::toDouble( d2 ) );
970  }
971 
972  stepSize = stepWidth * qwtMsecsForType( intvType );
973  }
974 
975  x1 = interval.minValue();
976  x2 = interval.maxValue();
977 
979  {
980  qSwap( x1, x2 );
981  stepSize = -stepSize;
982  }
983 }
984 
997  int maxMajorSteps, int maxMinorSteps, double stepSize ) const
998 {
999  if ( maxMajorSteps < 1 )
1000  maxMajorSteps = 1;
1001 
1002  const double min = qMin( x1, x2 );
1003  const double max = qMax( x1, x2 );
1004 
1005  const QDateTime from = toDateTime( min );
1006  const QDateTime to = toDateTime( max );
1007 
1008  if ( from == to )
1009  return QwtScaleDiv();
1010 
1011  stepSize = qAbs( stepSize );
1012  if ( stepSize > 0.0 )
1013  {
1014  // as interval types above hours are not equidistant
1015  // ( even days might have 23/25 hours because of daylight saving )
1016  // the stepSize is used as a hint only
1017 
1018  maxMajorSteps = qCeil( ( max - min ) / stepSize );
1019  }
1020 
1021  const QwtDate::IntervalType intvType =
1022  intervalType( from, to, maxMajorSteps );
1023 
1024  QwtScaleDiv scaleDiv;
1025 
1026  if ( intvType == QwtDate::Millisecond )
1027  {
1028  // for milliseconds and below we can use the decimal system
1029  scaleDiv = QwtLinearScaleEngine::divideScale( min, max,
1030  maxMajorSteps, maxMinorSteps, stepSize );
1031  }
1032  else
1033  {
1034  const QDateTime minDate = QwtDate::floor( from, intvType );
1035  const QDateTime maxDate = QwtDate::ceil( to, intvType );
1036 
1037  scaleDiv = buildScaleDiv( minDate, maxDate,
1038  maxMajorSteps, maxMinorSteps, intvType );
1039 
1040  // scaleDiv has been calculated from an extended interval
1041  // adjusted to the step size. We have to shrink it again.
1042 
1043  scaleDiv = scaleDiv.bounded( min, max );
1044  }
1045 
1046  if ( x1 > x2 )
1047  scaleDiv.invert();
1048 
1049  return scaleDiv;
1050 }
1051 
1053  const QDateTime &minDate, const QDateTime &maxDate,
1054  int maxMajorSteps, int maxMinorSteps,
1056 {
1057  // calculate the step size
1058  const double stepSize = qwtDivideScale(
1059  qwtIntervalWidth( minDate, maxDate, intervalType ),
1060  maxMajorSteps, intervalType );
1061 
1062  // align minDate to the step size
1063  QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false );
1064  if ( !dt0.isValid() )
1065  {
1066  // the floored date is out of the range of a
1067  // QDateTime - we ceil instead.
1068  dt0 = alignDate( minDate, stepSize, intervalType, true );
1069  }
1070 
1071  QwtScaleDiv scaleDiv;
1072 
1073  if ( intervalType <= QwtDate::Week )
1074  {
1075  scaleDiv = qwtDivideToSeconds( dt0, maxDate,
1076  stepSize, maxMinorSteps, intervalType );
1077  }
1078  else
1079  {
1080  if( intervalType == QwtDate::Month )
1081  {
1082  scaleDiv = qwtDivideToMonths( dt0, maxDate,
1083  stepSize, maxMinorSteps );
1084  }
1085  else if ( intervalType == QwtDate::Year )
1086  {
1087  scaleDiv = qwtDivideToYears( dt0, maxDate,
1088  stepSize, maxMinorSteps );
1089  }
1090  }
1091 
1092 
1093  return scaleDiv;
1094 }
1095 
1113  const QDateTime &dateTime, double stepSize,
1114  QwtDate::IntervalType intervalType, bool up ) const
1115 {
1116  // what about: (year == 1582 && month == 10 && day > 4 && day < 15) ??
1117 
1118  QDateTime dt = dateTime;
1119 
1120  if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
1121  {
1122  dt.setUtcOffset( 0 );
1123  }
1124 
1125  switch( intervalType )
1126  {
1127  case QwtDate::Millisecond:
1128  {
1129  const int ms = qwtAlignValue(
1130  dt.time().msec(), stepSize, up ) ;
1131 
1132  dt = QwtDate::floor( dateTime, QwtDate::Second );
1133  dt = dt.addMSecs( ms );
1134 
1135  break;
1136  }
1137  case QwtDate::Second:
1138  {
1139  int second = dt.time().second();
1140  if ( up )
1141  {
1142  if ( dt.time().msec() > 0 )
1143  second++;
1144  }
1145 
1146  const int s = qwtAlignValue( second, stepSize, up );
1147 
1148  dt = QwtDate::floor( dt, QwtDate::Minute );
1149  dt = dt.addSecs( s );
1150 
1151  break;
1152  }
1153  case QwtDate::Minute:
1154  {
1155  int minute = dt.time().minute();
1156  if ( up )
1157  {
1158  if ( dt.time().msec() > 0 || dt.time().second() > 0 )
1159  minute++;
1160  }
1161 
1162  const int m = qwtAlignValue( minute, stepSize, up );
1163 
1164  dt = QwtDate::floor( dt, QwtDate::Hour );
1165  dt = dt.addSecs( m * 60 );
1166 
1167  break;
1168  }
1169  case QwtDate::Hour:
1170  {
1171  int hour = dt.time().hour();
1172  if ( up )
1173  {
1174  if ( dt.time().msec() > 0 || dt.time().second() > 0
1175  || dt.time().minute() > 0 )
1176  {
1177  hour++;
1178  }
1179  }
1180  const int h = qwtAlignValue( hour, stepSize, up );
1181 
1182  dt = QwtDate::floor( dt, QwtDate::Day );
1183  dt = dt.addSecs( h * 3600 );
1184 
1185  break;
1186  }
1187  case QwtDate::Day:
1188  {
1189  // What date do we expect f.e. from an alignment of 5 days ??
1190  // Aligning them to the beginning of the year avoids at least
1191  // jumping major ticks when panning
1192 
1193  int day = dt.date().dayOfYear();
1194  if ( up )
1195  {
1196  if ( dt.time() > QTime( 0, 0 ) )
1197  day++;
1198  }
1199 
1200  const int d = qwtAlignValue( day, stepSize, up );
1201 
1202  dt = QwtDate::floor( dt, QwtDate::Year );
1203  dt = dt.addDays( d - 1 );
1204 
1205  break;
1206  }
1207  case QwtDate::Week:
1208  {
1209  const QDate date = QwtDate::dateOfWeek0(
1210  dt.date().year(), d_data->week0Type );
1211 
1212  int numWeeks = date.daysTo( dt.date() ) / 7;
1213  if ( up )
1214  {
1215  if ( dt.time() > QTime( 0, 0 ) ||
1216  date.daysTo( dt.date() ) % 7 )
1217  {
1218  numWeeks++;
1219  }
1220  }
1221 
1222  const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
1223 
1224  dt = QwtDate::floor( dt, QwtDate::Day );
1225  dt.setDate( date );
1226  dt = dt.addDays( d );
1227 
1228  break;
1229  }
1230  case QwtDate::Month:
1231  {
1232  int month = dt.date().month();
1233  if ( up )
1234  {
1235  if ( dt.date().day() > 1 ||
1236  dt.time() > QTime( 0, 0 ) )
1237  {
1238  month++;
1239  }
1240  }
1241 
1242  const int m = qwtAlignValue( month - 1, stepSize, up );
1243 
1244  dt = QwtDate::floor( dt, QwtDate::Year );
1245  dt = dt.addMonths( m );
1246 
1247  break;
1248  }
1249  case QwtDate::Year:
1250  {
1251  int year = dateTime.date().year();
1252  if ( up )
1253  {
1254  if ( dateTime.date().dayOfYear() > 1 ||
1255  dt.time() > QTime( 0, 0 ) )
1256  {
1257  year++;
1258  }
1259  }
1260 
1261  const int y = qwtAlignValue( year, stepSize, up );
1262 
1263  dt = QwtDate::floor( dt, QwtDate::Day );
1264  if ( y == 0 )
1265  {
1266  // there is no year 0 in the Julian calendar
1267  dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
1268  }
1269  else
1270  {
1271  dt.setDate( QDate( y, 1, 1 ) );
1272  }
1273 
1274  break;
1275  }
1276  }
1277 
1278  if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
1279  {
1280  dt.setUtcOffset( dateTime.utcOffset() );
1281  }
1282 
1283  return dt;
1284 }
1285 
1294 QDateTime QwtDateScaleEngine::toDateTime( double value ) const
1295 {
1296  QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
1297  if ( !dt.isValid() )
1298  {
1299  const QDate date = ( value <= 0.0 )
1301 
1302  dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec );
1303  }
1304 
1305  if ( d_data->timeSpec == Qt::OffsetFromUTC )
1306  {
1307  dt = dt.addSecs( d_data->utcOffset );
1308  dt.setUtcOffset( d_data->utcOffset );
1309  }
1310 
1311  return dt;
1312 }
1313 
int v
virtual void autoScale(int maxNumSteps, double &x1, double &x2, double &stepSize) const
d
The interval is related to weeks.
Definition: qwt_date.h:93
The interval is related to years.
Definition: qwt_date.h:99
QwtInterval normalized() const
Normalize the limits of the interval.
void setTimeSpec(Qt::TimeSpec)
The interval is related to months.
Definition: qwt_date.h:96
static double qwtDivideScale(double intervalSize, int numSteps, QwtDate::IntervalType intervalType)
static double qwtDivideMajorStep(double stepSize, int maxMinSteps, QwtDate::IntervalType intervalType)
bool testAttribute(Attribute) const
QwtScaleDiv bounded(double lowerBound, double upperBound) const
static int qwtStepCount(int intervalSize, int maxSteps, const int limits[], size_t numLimits)
QDateTime toDateTime(double) const
A class representing an interval.
Definition: qwt_interval.h:26
void setWeek0Type(QwtDate::Week0Type)
static QwtScaleDiv qwtDivideToMonths(QDateTime &minDate, const QDateTime &maxDate, double stepSize, int maxMinSteps)
double minValue() const
Definition: qwt_interval.h:193
void setTicks(int tickType, const QList< double > &)
static QDate maxDate()
Definition: qwt_date.cpp:589
XmlRpcServer s
virtual QwtScaleDiv divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize=0.0) const
Calculate a scale division for a date/time interval.
QwtDate::Week0Type week0Type() const
virtual ~QwtDateScaleEngine()
Destructor.
static double qwtRoundedIntervalWidth(const QDateTime &minDate, const QDateTime &maxDate, QwtDate::IntervalType intervalType)
static QDateTime floor(const QDateTime &, IntervalType)
Definition: qwt_date.cpp:493
A class representing a scale division.
Definition: qwt_scale_div.h:36
static double toDouble(const QDateTime &)
Definition: qwt_date.cpp:366
static double divideInterval(double interval, int numSteps, uint base)
double upperMargin() const
TFSIMD_FORCE_INLINE const tfScalar & y() const
double lowerMargin() const
double maxValue() const
Definition: qwt_interval.h:199
void setInterval(double lowerBound, double upperBound)
std::chrono::duration< std::int_fast64_t > seconds
IntervalType
Definition: qwt_date.h:75
The interval is related to minutes.
Definition: qwt_date.h:84
Qt::TimeSpec timeSpec() const
QwtDateScaleEngine(Qt::TimeSpec=Qt::LocalTime)
Constructor.
The interval is related to hours.
Definition: qwt_date.h:87
double reference() const
static QList< double > qwtDstTicks(const QDateTime &dateTime, int secondsMajor, int secondsMinor)
static int utcOffset(const QDateTime &)
Definition: qwt_date.cpp:703
virtual QDateTime alignDate(const QDateTime &, double stepSize, QwtDate::IntervalType, bool up) const
static double qwtMsecsForType(QwtDate::IntervalType type)
size_t to
void setUtcOffset(int seconds)
static QwtScaleDiv qwtDivideToYears(const QDateTime &minDate, const QDateTime &maxDate, double stepSize, int maxMinSteps)
virtual QwtScaleDiv divideScale(double x1, double x2, int numMajorSteps, int numMinorSteps, double stepSize=0.0) const
Calculate a scale division for an interval.
static QwtScaleDiv qwtDivideToSeconds(const QDateTime &minDate, const QDateTime &maxDate, double stepSize, int maxMinSteps, QwtDate::IntervalType intervalType)
The interval is related to milliseconds.
Definition: qwt_date.h:78
Week0Type
Definition: qwt_date.h:49
static int qwtStepSize(int intervalSize, int maxSteps, uint base)
bool log
Build a scale which is symmetric to the reference() value.
QwtScaleDiv buildScaleDiv(const QDateTime &, const QDateTime &, int maxMajorSteps, int maxMinorSteps, QwtDate::IntervalType) const
static QDateTime ceil(const QDateTime &, IntervalType)
Definition: qwt_date.cpp:391
static int qwtAlignValue(double value, double stepSize, bool up)
void setMinValue(double)
Definition: qwt_interval.h:177
static QDate dateOfWeek0(int year, Week0Type)
Date of the first day of the first week for a year.
Definition: qwt_date.cpp:610
Turn the scale upside down.
static QDateTime toDateTime(double value, Qt::TimeSpec=Qt::UTC)
Definition: qwt_date.cpp:329
int min(int a, int b)
static QDate minDate()
Definition: qwt_date.cpp:567
A collection of methods around date/time values.
Definition: qwt_date.h:42
A scale engine for linear scales.
QwtInterval extend(double value) const
Extend the interval.
QwtInterval buildInterval(double v) const
Build an interval around a value.
int i
virtual QwtDate::IntervalType intervalType(const QDateTime &, const QDateTime &, int maxSteps) const
QwtInterval symmetrize(double value) const
double width() const
Return the width of an interval.
Definition: qwt_interval.h:228
Build a scale which includes the reference() value.
The interval is related to seconds.
Definition: qwt_date.h:81
size_t from
void setMaxValue(double)
Definition: qwt_interval.h:187
static double qwtIntervalWidth(const QDateTime &minDate, const QDateTime &maxDate, QwtDate::IntervalType intervalType)
static int qwtDivideInterval(double intervalSize, int numSteps, const int limits[], size_t numLimits)
int n
The interval is related to days.
Definition: qwt_date.h:90


plotjuggler
Author(s): Davide Faconti
autogenerated on Sat Jul 6 2019 03:44:17