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_interval.h"
13 
14 #include <qdatetime.h>
15 
16 #include <limits>
17 
18 static inline double qwtMsecsForType( int type )
19 {
20  static const double msecs[] =
21  {
22  1.0,
23  1000.0,
24  60.0 * 1000.0,
25  3600.0 * 1000.0,
26  24.0 * 3600.0 * 1000.0,
27  7.0 * 24.0 * 3600.0 * 1000.0,
28  30.0 * 24.0 * 3600.0 * 1000.0,
29  365.0 * 24.0 * 3600.0 * 1000.0,
30  };
31 
32  if ( type < 0 || type >= static_cast<int>( sizeof( msecs ) / sizeof( msecs[0] ) ) )
33  return 1.0;
34 
35  return msecs[ type ];
36 }
37 
38 static inline int qwtAlignValue(
39  double value, double stepSize, bool up )
40 {
41  double d = value / stepSize;
42  d = up ? std::ceil( d ) : std::floor( d );
43 
44  return static_cast<int>( d * stepSize );
45 }
46 
47 static double qwtIntervalWidth( const QDateTime &minDate,
48  const QDateTime &maxDate, QwtDate::IntervalType intervalType )
49 {
50  switch( intervalType )
51  {
53  {
54  const double secsTo = minDate.secsTo( maxDate );
55  const double msecs = maxDate.time().msec() -
56  minDate.time().msec();
57 
58  return secsTo * 1000 + msecs;
59  }
60  case QwtDate::Second:
61  {
62  return minDate.secsTo( maxDate );
63  }
64  case QwtDate::Minute:
65  {
66  const double secsTo = minDate.secsTo( maxDate );
67  return std::floor( secsTo / 60 );
68  }
69  case QwtDate::Hour:
70  {
71  const double secsTo = minDate.secsTo( maxDate );
72  return std::floor( secsTo / 3600 );
73  }
74  case QwtDate::Day:
75  {
76  return minDate.daysTo( maxDate );
77  }
78  case QwtDate::Week:
79  {
80  return std::floor( minDate.daysTo( maxDate ) / 7.0 );
81  }
82  case QwtDate::Month:
83  {
84  const double years =
85  double( maxDate.date().year() ) - minDate.date().year();
86 
87  int months = maxDate.date().month() - minDate.date().month();
88  if ( maxDate.date().day() < minDate.date().day() )
89  months--;
90 
91  return years * 12 + months;
92  }
93  case QwtDate::Year:
94  {
95  double years =
96  double( maxDate.date().year() ) - minDate.date().year();
97 
98  if ( maxDate.date().month() < minDate.date().month() )
99  years -= 1.0;
100 
101  return years;
102  }
103  }
104 
105  return 0.0;
106 }
107 
109  const QDateTime &minDate, const QDateTime &maxDate,
110  QwtDate::IntervalType intervalType )
111 {
112  const QDateTime minD = QwtDate::floor( minDate, intervalType );
113  const QDateTime maxD = QwtDate::ceil( maxDate, intervalType );
114 
115  return qwtIntervalWidth( minD, maxD, intervalType );
116 }
117 
118 static inline int qwtStepCount( int intervalSize, int maxSteps,
119  const int limits[], size_t numLimits )
120 {
121  for ( uint i = 0; i < numLimits; i++ )
122  {
123  const int numSteps = intervalSize / limits[ i ];
124 
125  if ( numSteps > 1 && numSteps <= maxSteps &&
126  numSteps * limits[ i ] == intervalSize )
127  {
128  return numSteps;
129  }
130  }
131 
132  return 0;
133 }
134 
135 static int qwtStepSize( int intervalSize, int maxSteps, uint base )
136 {
137  if ( maxSteps <= 0 )
138  return 0;
139 
140  if ( maxSteps > 2 )
141  {
142  for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
143  {
144  const double stepSize = double( intervalSize ) / numSteps;
145 
146  const double p = std::floor( std::log( stepSize ) / std::log( double( base ) ) );
147  const double fraction = std::pow( base, p );
148 
149  for ( uint n = base; n >= 1; n /= 2 )
150  {
151  if ( qFuzzyCompare( stepSize, n * fraction ) )
152  return qRound( stepSize );
153 
154  if ( n == 3 && ( base % 2 ) == 0 )
155  {
156  if ( qFuzzyCompare( stepSize, 2 * fraction ) )
157  return qRound( stepSize );
158  }
159  }
160  }
161  }
162 
163  return 0;
164 }
165 
166 static int qwtDivideInterval( double intervalSize, int numSteps,
167  const int limits[], size_t numLimits )
168 {
169  const int v = qwtCeil( intervalSize / double( numSteps ) );
170 
171  for ( uint i = 0; i < numLimits - 1; i++ )
172  {
173  if ( v <= limits[i] )
174  return limits[i];
175  }
176 
177  return limits[ numLimits - 1 ];
178 }
179 
180 static double qwtDivideScale( double intervalSize, int numSteps,
181  QwtDate::IntervalType intervalType )
182 {
183  if ( intervalType != QwtDate::Day )
184  {
185  if ( ( intervalSize > numSteps ) &&
186  ( intervalSize <= 2 * numSteps ) )
187  {
188  return 2.0;
189  }
190  }
191 
192  double stepSize;
193 
194  switch( intervalType )
195  {
196  case QwtDate::Second:
197  case QwtDate::Minute:
198  {
199  static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
200 
201  stepSize = qwtDivideInterval( intervalSize, numSteps,
202  limits, sizeof( limits ) / sizeof( int ) );
203 
204  break;
205  }
206  case QwtDate::Hour:
207  {
208  static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
209 
210  stepSize = qwtDivideInterval( intervalSize, numSteps,
211  limits, sizeof( limits ) / sizeof( int ) );
212 
213  break;
214  }
215  case QwtDate::Day:
216  {
217  const double v = intervalSize / double( numSteps );
218  if ( v <= 5.0 )
219  stepSize = std::ceil( v );
220  else
221  stepSize = std::ceil( v / 7 ) * 7;
222 
223  break;
224  }
225  case QwtDate::Week:
226  {
227  static int limits[] = { 1, 2, 4, 8, 12, 26, 52 };
228 
229  stepSize = qwtDivideInterval( intervalSize, numSteps,
230  limits, sizeof( limits ) / sizeof( int ) );
231 
232  break;
233  }
234  case QwtDate::Month:
235  {
236  static int limits[] = { 1, 2, 3, 4, 6, 12 };
237 
238  stepSize = qwtDivideInterval( intervalSize, numSteps,
239  limits, sizeof( limits ) / sizeof( int ) );
240 
241  break;
242  }
243  case QwtDate::Year:
245  default:
246  {
248  intervalSize, numSteps, 10 );
249  }
250  }
251 
252  return stepSize;
253 }
254 
255 static double qwtDivideMajorStep( double stepSize, int maxMinSteps,
256  QwtDate::IntervalType intervalType )
257 {
258  double minStepSize = 0.0;
259 
260  switch( intervalType )
261  {
262  case QwtDate::Second:
263  {
264  minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 );
265  if ( minStepSize == 0.0 )
266  minStepSize = 0.5 * stepSize;
267 
268  break;
269  }
270  case QwtDate::Minute:
271  {
272  static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
273 
274  int numSteps;
275 
276  if ( stepSize > maxMinSteps )
277  {
278  numSteps = qwtStepCount( stepSize, maxMinSteps,
279  limits, sizeof( limits ) / sizeof( int ) );
280 
281  }
282  else
283  {
284  numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
285  limits, sizeof( limits ) / sizeof( int ) );
286  }
287 
288  if ( numSteps > 0 )
289  minStepSize = stepSize / numSteps;
290 
291  break;
292  }
293  case QwtDate::Hour:
294  {
295  int numSteps = 0;
296 
297  if ( stepSize > maxMinSteps )
298  {
299  static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
300 
301  numSteps = qwtStepCount( stepSize, maxMinSteps,
302  limits, sizeof( limits ) / sizeof( int ) );
303  }
304  else
305  {
306  static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
307 
308  numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
309  limits, sizeof( limits ) / sizeof( int ) );
310  }
311 
312  if ( numSteps > 0 )
313  minStepSize = stepSize / numSteps;
314 
315  break;
316  }
317  case QwtDate::Day:
318  {
319  int numSteps = 0;
320 
321  if ( stepSize > maxMinSteps )
322  {
323  static int limits[] = { 1, 2, 3, 7, 14, 28 };
324 
325  numSteps = qwtStepCount( stepSize, maxMinSteps,
326  limits, sizeof( limits ) / sizeof( int ) );
327  }
328  else
329  {
330  static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
331 
332  numSteps = qwtStepCount( stepSize * 24, maxMinSteps,
333  limits, sizeof( limits ) / sizeof( int ) );
334  }
335 
336  if ( numSteps > 0 )
337  minStepSize = stepSize / numSteps;
338 
339  break;
340  }
341  case QwtDate::Week:
342  {
343  const int daysInStep = stepSize * 7;
344 
345  if ( maxMinSteps >= daysInStep )
346  {
347  // we want to have one tick per day
348  minStepSize = 1.0 / 7.0;
349  }
350  else
351  {
352  // when the stepSize is more than a week we want to
353  // have a tick for each week
354 
355  const int stepSizeInWeeks = stepSize;
356 
357  if ( stepSizeInWeeks <= maxMinSteps )
358  {
359  minStepSize = 1;
360  }
361  else
362  {
364  stepSizeInWeeks, maxMinSteps, 10 );
365  }
366  }
367  break;
368  }
369  case QwtDate::Month:
370  {
371  // fractions of months doesn't make any sense
372 
373  if ( stepSize < maxMinSteps )
374  maxMinSteps = static_cast<int>( stepSize );
375 
376  static int limits[] = { 1, 2, 3, 4, 6, 12 };
377 
378  int numSteps = qwtStepCount( stepSize, maxMinSteps,
379  limits, sizeof( limits ) / sizeof( int ) );
380 
381  if ( numSteps > 0 )
382  minStepSize = stepSize / numSteps;
383 
384  break;
385  }
386  case QwtDate::Year:
387  {
388  if ( stepSize >= maxMinSteps )
389  {
391  stepSize, maxMinSteps, 10 );
392  }
393  else
394  {
395  // something in months
396 
397  static int limits[] = { 1, 2, 3, 4, 6, 12 };
398 
399  int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps,
400  limits, sizeof( limits ) / sizeof( int ) );
401 
402  if ( numSteps > 0 )
403  minStepSize = stepSize / numSteps;
404  }
405 
406  break;
407  }
408  default:
409  break;
410  }
411 
412  if ( intervalType != QwtDate::Month
413  && minStepSize == 0.0 )
414  {
415  minStepSize = 0.5 * stepSize;
416  }
417 
418  return minStepSize;
419 }
420 
421 static QList<double> qwtDstTicks( const QDateTime &dateTime,
422  int secondsMajor, int secondsMinor )
423 {
424  if ( secondsMinor <= 0 )
425  QList<double>();
426 
427  QDateTime minDate = dateTime.addSecs( -secondsMajor );
428  minDate = QwtDate::floor( minDate, QwtDate::Hour );
429 
430  const double utcOffset = QwtDate::utcOffset( dateTime );
431 
432  // find the hours where daylight saving time happens
433 
434  double dstMin = QwtDate::toDouble( minDate );
435  while ( minDate < dateTime &&
436  QwtDate::utcOffset( minDate ) != utcOffset )
437  {
438  minDate = minDate.addSecs( 3600 );
439  dstMin += 3600 * 1000.0;
440  }
441 
442  QList<double> ticks;
443  ticks.reserve( 3600 / secondsMinor);
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 = qwtFloor( 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 = qwtFloor( 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 >= std::numeric_limits<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 = qwtMinF( x1, x2 );
1003  const double max = qwtMaxF( 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 = qwtCeil( ( 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 #if QT_VERSION >= 0x050200
1123  dt.setOffsetFromUtc( 0 );
1124 #else
1125  dt.setUtcOffset( 0 );
1126 #endif
1127  }
1128 
1129  switch( intervalType )
1130  {
1131  case QwtDate::Millisecond:
1132  {
1133  const int ms = qwtAlignValue(
1134  dt.time().msec(), stepSize, up ) ;
1135 
1136  dt = QwtDate::floor( dateTime, QwtDate::Second );
1137  dt = dt.addMSecs( ms );
1138 
1139  break;
1140  }
1141  case QwtDate::Second:
1142  {
1143  int second = dt.time().second();
1144  if ( up )
1145  {
1146  if ( dt.time().msec() > 0 )
1147  second++;
1148  }
1149 
1150  const int s = qwtAlignValue( second, stepSize, up );
1151 
1152  dt = QwtDate::floor( dt, QwtDate::Minute );
1153  dt = dt.addSecs( s );
1154 
1155  break;
1156  }
1157  case QwtDate::Minute:
1158  {
1159  int minute = dt.time().minute();
1160  if ( up )
1161  {
1162  if ( dt.time().msec() > 0 || dt.time().second() > 0 )
1163  minute++;
1164  }
1165 
1166  const int m = qwtAlignValue( minute, stepSize, up );
1167 
1168  dt = QwtDate::floor( dt, QwtDate::Hour );
1169  dt = dt.addSecs( m * 60 );
1170 
1171  break;
1172  }
1173  case QwtDate::Hour:
1174  {
1175  int hour = dt.time().hour();
1176  if ( up )
1177  {
1178  if ( dt.time().msec() > 0 || dt.time().second() > 0
1179  || dt.time().minute() > 0 )
1180  {
1181  hour++;
1182  }
1183  }
1184  const int h = qwtAlignValue( hour, stepSize, up );
1185 
1186  dt = QwtDate::floor( dt, QwtDate::Day );
1187  dt = dt.addSecs( h * 3600 );
1188 
1189  break;
1190  }
1191  case QwtDate::Day:
1192  {
1193  // What date do we expect f.e. from an alignment of 5 days ??
1194  // Aligning them to the beginning of the year avoids at least
1195  // jumping major ticks when panning
1196 
1197  int day = dt.date().dayOfYear();
1198  if ( up )
1199  {
1200  if ( dt.time() > QTime( 0, 0 ) )
1201  day++;
1202  }
1203 
1204  const int d = qwtAlignValue( day, stepSize, up );
1205 
1206  dt = QwtDate::floor( dt, QwtDate::Year );
1207  dt = dt.addDays( d - 1 );
1208 
1209  break;
1210  }
1211  case QwtDate::Week:
1212  {
1213  const QDate date = QwtDate::dateOfWeek0(
1214  dt.date().year(), d_data->week0Type );
1215 
1216  int numWeeks = date.daysTo( dt.date() ) / 7;
1217  if ( up )
1218  {
1219  if ( dt.time() > QTime( 0, 0 ) ||
1220  date.daysTo( dt.date() ) % 7 )
1221  {
1222  numWeeks++;
1223  }
1224  }
1225 
1226  const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
1227 
1228  dt = QwtDate::floor( dt, QwtDate::Day );
1229  dt.setDate( date );
1230  dt = dt.addDays( d );
1231 
1232  break;
1233  }
1234  case QwtDate::Month:
1235  {
1236  int month = dt.date().month();
1237  if ( up )
1238  {
1239  if ( dt.date().day() > 1 ||
1240  dt.time() > QTime( 0, 0 ) )
1241  {
1242  month++;
1243  }
1244  }
1245 
1246  const int m = qwtAlignValue( month - 1, stepSize, up );
1247 
1248  dt = QwtDate::floor( dt, QwtDate::Year );
1249  dt = dt.addMonths( m );
1250 
1251  break;
1252  }
1253  case QwtDate::Year:
1254  {
1255  int year = dateTime.date().year();
1256  if ( up )
1257  {
1258  if ( dateTime.date().dayOfYear() > 1 ||
1259  dt.time() > QTime( 0, 0 ) )
1260  {
1261  year++;
1262  }
1263  }
1264 
1265  const int y = qwtAlignValue( year, stepSize, up );
1266 
1267  dt = QwtDate::floor( dt, QwtDate::Day );
1268  if ( y == 0 )
1269  {
1270  // there is no year 0 in the Julian calendar
1271  dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
1272  }
1273  else
1274  {
1275  dt.setDate( QDate( y, 1, 1 ) );
1276  }
1277 
1278  break;
1279  }
1280  }
1281 
1282  if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
1283  {
1284 #if QT_VERSION >= 0x050200
1285  dt.setOffsetFromUtc( dateTime.offsetFromUtc() );
1286 #else
1287  dt.setUtcOffset( dateTime.utcOffset() );
1288 #endif
1289  }
1290 
1291  return dt;
1292 }
1293 
1302 QDateTime QwtDateScaleEngine::toDateTime( double value ) const
1303 {
1304  QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
1305  if ( !dt.isValid() )
1306  {
1307  const QDate date = ( value <= 0.0 )
1309 
1310  dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec );
1311  }
1312 
1313  if ( d_data->timeSpec == Qt::OffsetFromUTC )
1314  {
1315  dt = dt.addSecs( d_data->utcOffset );
1316 #if QT_VERSION >= 0x050200
1317  dt.setOffsetFromUtc( d_data->utcOffset );
1318 #else
1319  dt.setUtcOffset( d_data->utcOffset );
1320 #endif
1321  }
1322 
1323  return dt;
1324 }
1325 
The interval is related to weeks.
Definition: qwt_date.h:93
enum MQTTPropertyCodes value
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:22
void setWeek0Type(QwtDate::Week0Type)
MQTTClient d
Definition: test10.c:1656
static QwtScaleDiv qwtDivideToMonths(QDateTime &minDate, const QDateTime &maxDate, double stepSize, int maxMinSteps)
QWT_CONSTEXPR float qwtMaxF(float a, float b)
Definition: qwt_math.h:123
double minValue() const
Definition: qwt_interval.h:190
void setTicks(int tickType, const QList< double > &)
static QDate maxDate()
Definition: qwt_date.cpp:521
virtual QwtScaleDiv divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize=0.0) const QWT_OVERRIDE
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:425
QwtInterval buildInterval(double value) const
Build an interval around a value.
A class representing a scale division.
Definition: qwt_scale_div.h:33
static double toDouble(const QDateTime &)
Definition: qwt_date.cpp:298
double upperMargin() const
double lowerMargin() const
QWT_CONSTEXPR float qwtMinF(float a, float b)
Definition: qwt_math.h:99
double maxValue() const
Definition: qwt_interval.h:196
static double divideInterval(double intervalSize, int numSteps, uint base)
int qwtFloor(qreal value)
Definition: qwt_math.h:271
void setInterval(double lowerBound, double upperBound)
IntervalType
Definition: qwt_date.h:75
The interval is related to minutes.
Definition: qwt_date.h:84
virtual void autoScale(int maxNumSteps, double &x1, double &x2, double &stepSize) const QWT_OVERRIDE
Qt::TimeSpec timeSpec() const
#define min(A, B)
Definition: Log.c:64
#define max(A, B)
Definition: Socket.h:88
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:635
virtual QDateTime alignDate(const QDateTime &, double stepSize, QwtDate::IntervalType, bool up) const
void setUtcOffset(int seconds)
static QwtScaleDiv qwtDivideToYears(const QDateTime &minDate, const QDateTime &maxDate, double stepSize, int maxMinSteps)
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)
static double qwtMsecsForType(int type)
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:323
static int qwtAlignValue(double value, double stepSize, bool up)
void setMinValue(double)
Definition: qwt_interval.h:174
static QDate dateOfWeek0(int year, Week0Type)
Date of the first day of the first week for a year.
Definition: qwt_date.cpp:542
Turn the scale upside down.
static QDateTime toDateTime(double value, Qt::TimeSpec=Qt::UTC)
Definition: qwt_date.cpp:261
static QDate minDate()
Definition: qwt_date.cpp:499
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.
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:225
Build a scale which includes the reference() value.
The interval is related to seconds.
Definition: qwt_date.h:81
void setMaxValue(double)
Definition: qwt_interval.h:184
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 qwtCeil(qreal value)
Definition: qwt_math.h:262
virtual QwtScaleDiv divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize=0.0) const QWT_OVERRIDE
Calculate a scale division for an interval.
The interval is related to days.
Definition: qwt_date.h:90


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