00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_date_scale_engine.h"
00011 #include "qwt_math.h"
00012 #include "qwt_transform.h"
00013 #include <qdatetime.h>
00014 #include <limits.h>
00015
00016 static inline double qwtMsecsForType( QwtDate::IntervalType type )
00017 {
00018 static const double msecs[] =
00019 {
00020 1.0,
00021 1000.0,
00022 60.0 * 1000.0,
00023 3600.0 * 1000.0,
00024 24.0 * 3600.0 * 1000.0,
00025 7.0 * 24.0 * 3600.0 * 1000.0,
00026 30.0 * 24.0 * 3600.0 * 1000.0,
00027 365.0 * 24.0 * 3600.0 * 1000.0,
00028 };
00029
00030 if ( type < 0 || type >= static_cast<int>( sizeof( msecs ) / sizeof( msecs[0] ) ) )
00031 return 1.0;
00032
00033 return msecs[ type ];
00034 }
00035
00036 static inline int qwtAlignValue(
00037 double value, double stepSize, bool up )
00038 {
00039 double d = value / stepSize;
00040 d = up ? ::ceil( d ) : ::floor( d );
00041
00042 return static_cast<int>( d * stepSize );
00043 }
00044
00045 static double qwtIntervalWidth( const QDateTime &minDate,
00046 const QDateTime &maxDate, QwtDate::IntervalType intervalType )
00047 {
00048 switch( intervalType )
00049 {
00050 case QwtDate::Millisecond:
00051 {
00052 const double secsTo = minDate.secsTo( maxDate );
00053 const double msecs = maxDate.time().msec() -
00054 minDate.time().msec();
00055
00056 return secsTo * 1000 + msecs;
00057 }
00058 case QwtDate::Second:
00059 {
00060 return minDate.secsTo( maxDate );
00061 }
00062 case QwtDate::Minute:
00063 {
00064 const double secsTo = minDate.secsTo( maxDate );
00065 return ::floor( secsTo / 60 );
00066 }
00067 case QwtDate::Hour:
00068 {
00069 const double secsTo = minDate.secsTo( maxDate );
00070 return ::floor( secsTo / 3600 );
00071 }
00072 case QwtDate::Day:
00073 {
00074 return minDate.daysTo( maxDate );
00075 }
00076 case QwtDate::Week:
00077 {
00078 return ::floor( minDate.daysTo( maxDate ) / 7.0 );
00079 }
00080 case QwtDate::Month:
00081 {
00082 const double years =
00083 double( maxDate.date().year() ) - minDate.date().year();
00084
00085 int months = maxDate.date().month() - minDate.date().month();
00086 if ( maxDate.date().day() < minDate.date().day() )
00087 months--;
00088
00089 return years * 12 + months;
00090 }
00091 case QwtDate::Year:
00092 {
00093 double years =
00094 double( maxDate.date().year() ) - minDate.date().year();
00095
00096 if ( maxDate.date().month() < minDate.date().month() )
00097 years -= 1.0;
00098
00099 return years;
00100 }
00101 }
00102
00103 return 0.0;
00104 }
00105
00106 static double qwtRoundedIntervalWidth(
00107 const QDateTime &minDate, const QDateTime &maxDate,
00108 QwtDate::IntervalType intervalType )
00109 {
00110 const QDateTime minD = QwtDate::floor( minDate, intervalType );
00111 const QDateTime maxD = QwtDate::ceil( maxDate, intervalType );
00112
00113 return qwtIntervalWidth( minD, maxD, intervalType );
00114 }
00115
00116 static inline int qwtStepCount( int intervalSize, int maxSteps,
00117 const int limits[], size_t numLimits )
00118 {
00119 for ( uint i = 0; i < numLimits; i++ )
00120 {
00121 const int numSteps = intervalSize / limits[ i ];
00122
00123 if ( numSteps > 1 && numSteps <= maxSteps &&
00124 numSteps * limits[ i ] == intervalSize )
00125 {
00126 return numSteps;
00127 }
00128 }
00129
00130 return 0;
00131 }
00132
00133 static int qwtStepSize( int intervalSize, int maxSteps, uint base )
00134 {
00135 if ( maxSteps <= 0 )
00136 return 0;
00137
00138 if ( maxSteps > 2 )
00139 {
00140 for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
00141 {
00142 const double stepSize = double( intervalSize ) / numSteps;
00143
00144 const double p = ::floor( ::log( stepSize ) / ::log( double( base ) ) );
00145 const double fraction = qPow( base, p );
00146
00147 for ( uint n = base; n >= 1; n /= 2 )
00148 {
00149 if ( qFuzzyCompare( stepSize, n * fraction ) )
00150 return qRound( stepSize );
00151
00152 if ( n == 3 && ( base % 2 ) == 0 )
00153 {
00154 if ( qFuzzyCompare( stepSize, 2 * fraction ) )
00155 return qRound( stepSize );
00156 }
00157 }
00158 }
00159 }
00160
00161 return 0;
00162 }
00163
00164 static int qwtDivideInterval( double intervalSize, int numSteps,
00165 const int limits[], size_t numLimits )
00166 {
00167 const int v = qCeil( intervalSize / double( numSteps ) );
00168
00169 for ( uint i = 0; i < numLimits - 1; i++ )
00170 {
00171 if ( v <= limits[i] )
00172 return limits[i];
00173 }
00174
00175 return limits[ numLimits - 1 ];
00176 }
00177
00178 static double qwtDivideScale( double intervalSize, int numSteps,
00179 QwtDate::IntervalType intervalType )
00180 {
00181 if ( intervalType != QwtDate::Day )
00182 {
00183 if ( ( intervalSize > numSteps ) &&
00184 ( intervalSize <= 2 * numSteps ) )
00185 {
00186 return 2.0;
00187 }
00188 }
00189
00190 double stepSize;
00191
00192 switch( intervalType )
00193 {
00194 case QwtDate::Second:
00195 case QwtDate::Minute:
00196 {
00197 static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
00198
00199 stepSize = qwtDivideInterval( intervalSize, numSteps,
00200 limits, sizeof( limits ) / sizeof( int ) );
00201
00202 break;
00203 }
00204 case QwtDate::Hour:
00205 {
00206 static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
00207
00208 stepSize = qwtDivideInterval( intervalSize, numSteps,
00209 limits, sizeof( limits ) / sizeof( int ) );
00210
00211 break;
00212 }
00213 case QwtDate::Day:
00214 {
00215 const double v = intervalSize / double( numSteps );
00216 if ( v <= 5.0 )
00217 stepSize = qCeil( v );
00218 else
00219 stepSize = qCeil( v / 7 ) * 7;
00220
00221 break;
00222 }
00223 case QwtDate::Week:
00224 {
00225 static int limits[] = { 1, 2, 4, 8, 12, 26, 52 };
00226
00227 stepSize = qwtDivideInterval( intervalSize, numSteps,
00228 limits, sizeof( limits ) / sizeof( int ) );
00229
00230 break;
00231 }
00232 case QwtDate::Month:
00233 {
00234 static int limits[] = { 1, 2, 3, 4, 6, 12 };
00235
00236 stepSize = qwtDivideInterval( intervalSize, numSteps,
00237 limits, sizeof( limits ) / sizeof( int ) );
00238
00239 break;
00240 }
00241 case QwtDate::Year:
00242 case QwtDate::Millisecond:
00243 default:
00244 {
00245 stepSize = QwtScaleArithmetic::divideInterval(
00246 intervalSize, numSteps, 10 );
00247 }
00248 }
00249
00250 return stepSize;
00251 }
00252
00253 static double qwtDivideMajorStep( double stepSize, int maxMinSteps,
00254 QwtDate::IntervalType intervalType )
00255 {
00256 double minStepSize = 0.0;
00257
00258 switch( intervalType )
00259 {
00260 case QwtDate::Second:
00261 {
00262 minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 );
00263 if ( minStepSize == 0.0 )
00264 minStepSize = 0.5 * stepSize;
00265
00266 break;
00267 }
00268 case QwtDate::Minute:
00269 {
00270 static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
00271
00272 int numSteps;
00273
00274 if ( stepSize > maxMinSteps )
00275 {
00276 numSteps = qwtStepCount( stepSize, maxMinSteps,
00277 limits, sizeof( limits ) / sizeof( int ) );
00278
00279 }
00280 else
00281 {
00282 numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
00283 limits, sizeof( limits ) / sizeof( int ) );
00284 }
00285
00286 if ( numSteps > 0 )
00287 minStepSize = double( stepSize ) / numSteps;
00288
00289 break;
00290 }
00291 case QwtDate::Hour:
00292 {
00293 int numSteps = 0;
00294
00295 if ( stepSize > maxMinSteps )
00296 {
00297 static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
00298
00299 numSteps = qwtStepCount( stepSize, maxMinSteps,
00300 limits, sizeof( limits ) / sizeof( int ) );
00301 }
00302 else
00303 {
00304 static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
00305
00306 numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
00307 limits, sizeof( limits ) / sizeof( int ) );
00308 }
00309
00310 if ( numSteps > 0 )
00311 minStepSize = double( stepSize ) / numSteps;
00312
00313 break;
00314 }
00315 case QwtDate::Day:
00316 {
00317 int numSteps = 0;
00318
00319 if ( stepSize > maxMinSteps )
00320 {
00321 static int limits[] = { 1, 2, 3, 7, 14, 28 };
00322
00323 numSteps = qwtStepCount( stepSize, maxMinSteps,
00324 limits, sizeof( limits ) / sizeof( int ) );
00325 }
00326 else
00327 {
00328 static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
00329
00330 numSteps = qwtStepCount( stepSize * 24, maxMinSteps,
00331 limits, sizeof( limits ) / sizeof( int ) );
00332 }
00333
00334 if ( numSteps > 0 )
00335 minStepSize = double( stepSize ) / numSteps;
00336
00337 break;
00338 }
00339 case QwtDate::Week:
00340 {
00341 const int daysInStep = stepSize * 7;
00342
00343 if ( maxMinSteps >= daysInStep )
00344 {
00345
00346 minStepSize = 1.0 / 7.0;
00347 }
00348 else
00349 {
00350
00351
00352
00353 const int stepSizeInWeeks = stepSize;
00354
00355 if ( stepSizeInWeeks <= maxMinSteps )
00356 {
00357 minStepSize = 1;
00358 }
00359 else
00360 {
00361 minStepSize = QwtScaleArithmetic::divideInterval(
00362 stepSizeInWeeks, maxMinSteps, 10 );
00363 }
00364 }
00365 break;
00366 }
00367 case QwtDate::Month:
00368 {
00369
00370
00371 if ( stepSize < maxMinSteps )
00372 maxMinSteps = static_cast<int>( stepSize );
00373
00374 static int limits[] = { 1, 2, 3, 4, 6, 12 };
00375
00376 int numSteps = qwtStepCount( stepSize, maxMinSteps,
00377 limits, sizeof( limits ) / sizeof( int ) );
00378
00379 if ( numSteps > 0 )
00380 minStepSize = double( stepSize ) / numSteps;
00381
00382 break;
00383 }
00384 case QwtDate::Year:
00385 {
00386 if ( stepSize >= maxMinSteps )
00387 {
00388 minStepSize = QwtScaleArithmetic::divideInterval(
00389 stepSize, maxMinSteps, 10 );
00390 }
00391 else
00392 {
00393
00394
00395 static int limits[] = { 1, 2, 3, 4, 6, 12 };
00396
00397 int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps,
00398 limits, sizeof( limits ) / sizeof( int ) );
00399
00400 if ( numSteps > 0 )
00401 minStepSize = double( stepSize ) / numSteps;
00402 }
00403
00404 break;
00405 }
00406 default:
00407 break;
00408 }
00409
00410 if ( intervalType != QwtDate::Month
00411 && minStepSize == 0.0 )
00412 {
00413 minStepSize = 0.5 * stepSize;
00414 }
00415
00416 return minStepSize;
00417 }
00418
00419 static QList<double> qwtDstTicks( const QDateTime &dateTime,
00420 int secondsMajor, int secondsMinor )
00421 {
00422 if ( secondsMinor <= 0 )
00423 QList<double>();
00424
00425 QDateTime minDate = dateTime.addSecs( -secondsMajor );
00426 minDate = QwtDate::floor( minDate, QwtDate::Hour );
00427
00428 const double utcOffset = QwtDate::utcOffset( dateTime );
00429
00430
00431
00432 double dstMin = QwtDate::toDouble( minDate );
00433 while ( minDate < dateTime &&
00434 QwtDate::utcOffset( minDate ) != utcOffset )
00435 {
00436 minDate = minDate.addSecs( 3600 );
00437 dstMin += 3600 * 1000.0;
00438 }
00439
00440 QList<double> ticks;
00441 #if QT_VERSION >= 0x040700
00442 ticks.reserve( 3600 / secondsMinor);
00443 #endif
00444
00445 for ( int i = 0; i < 3600; i += secondsMinor )
00446 ticks += dstMin + i * 1000.0;
00447
00448 return ticks;
00449 }
00450
00451 static QwtScaleDiv qwtDivideToSeconds(
00452 const QDateTime &minDate, const QDateTime &maxDate,
00453 double stepSize, int maxMinSteps,
00454 QwtDate::IntervalType intervalType )
00455 {
00456
00457 double minStepSize = 0;
00458
00459 if ( maxMinSteps > 1 )
00460 {
00461 minStepSize = qwtDivideMajorStep( stepSize,
00462 maxMinSteps, intervalType );
00463 }
00464
00465 bool daylightSaving = false;
00466 if ( minDate.timeSpec() == Qt::LocalTime )
00467 {
00468 daylightSaving = intervalType > QwtDate::Hour;
00469 if ( intervalType == QwtDate::Hour )
00470 {
00471 daylightSaving = stepSize > 1;
00472 }
00473 }
00474
00475 const double s = qwtMsecsForType( intervalType ) / 1000;
00476 const int secondsMajor = static_cast<int>( stepSize * s );
00477 const double secondsMinor = minStepSize * s;
00478
00479
00480
00481
00482
00483 const double utcOffset = QwtDate::utcOffset( minDate );
00484 double dstOff = 0;
00485
00486 QList<double> majorTicks;
00487 QList<double> mediumTicks;
00488 QList<double> minorTicks;
00489
00490 for ( QDateTime dt = minDate; dt <= maxDate;
00491 dt = dt.addSecs( secondsMajor ) )
00492 {
00493 if ( !dt.isValid() )
00494 break;
00495
00496 double majorValue = QwtDate::toDouble( dt );
00497
00498 if ( daylightSaving )
00499 {
00500 const double offset = utcOffset - QwtDate::utcOffset( dt );
00501 majorValue += offset * 1000.0;
00502
00503 if ( offset > dstOff )
00504 {
00505
00506
00507 minorTicks += qwtDstTicks(
00508 dt, secondsMajor, qRound( secondsMinor ) );
00509 }
00510
00511 dstOff = offset;
00512 }
00513
00514 if ( majorTicks.isEmpty() || majorTicks.last() != majorValue )
00515 majorTicks += majorValue;
00516
00517 if ( secondsMinor > 0.0 )
00518 {
00519 const int numMinorSteps = qFloor( secondsMajor / secondsMinor );
00520
00521 for ( int i = 1; i < numMinorSteps; i++ )
00522 {
00523 const QDateTime mt = dt.addMSecs(
00524 qRound64( i * secondsMinor * 1000 ) );
00525
00526 double minorValue = QwtDate::toDouble( mt );
00527 if ( daylightSaving )
00528 {
00529 const double offset = utcOffset - QwtDate::utcOffset( mt );
00530 minorValue += offset * 1000.0;
00531 }
00532
00533 if ( minorTicks.isEmpty() || minorTicks.last() != minorValue )
00534 {
00535 const bool isMedium = ( numMinorSteps % 2 == 0 )
00536 && ( i != 1 ) && ( i == numMinorSteps / 2 );
00537
00538 if ( isMedium )
00539 mediumTicks += minorValue;
00540 else
00541 minorTicks += minorValue;
00542 }
00543 }
00544 }
00545 }
00546
00547 QwtScaleDiv scaleDiv;
00548
00549 scaleDiv.setInterval( QwtDate::toDouble( minDate ),
00550 QwtDate::toDouble( maxDate ) );
00551
00552 scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
00553 scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
00554 scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
00555
00556 return scaleDiv;
00557 }
00558
00559 static QwtScaleDiv qwtDivideToMonths(
00560 QDateTime &minDate, const QDateTime &maxDate,
00561 double stepSize, int maxMinSteps )
00562 {
00563
00564
00565
00566
00567 int minStepDays = 0;
00568 int minStepSize = 0.0;
00569
00570 if ( maxMinSteps > 1 )
00571 {
00572 if ( stepSize == 1 )
00573 {
00574 if ( maxMinSteps >= 30 )
00575 minStepDays = 1;
00576 else if ( maxMinSteps >= 6 )
00577 minStepDays = 5;
00578 else if ( maxMinSteps >= 3 )
00579 minStepDays = 10;
00580 else
00581 minStepDays = 15;
00582 }
00583 else
00584 {
00585 minStepSize = qwtDivideMajorStep(
00586 stepSize, maxMinSteps, QwtDate::Month );
00587 }
00588 }
00589
00590 QList<double> majorTicks;
00591 QList<double> mediumTicks;
00592 QList<double> minorTicks;
00593
00594 for ( QDateTime dt = minDate;
00595 dt <= maxDate; dt = dt.addMonths( stepSize ) )
00596 {
00597 if ( !dt.isValid() )
00598 break;
00599
00600 majorTicks += QwtDate::toDouble( dt );
00601
00602 if ( minStepDays > 0 )
00603 {
00604 for ( int days = minStepDays;
00605 days < 30; days += minStepDays )
00606 {
00607 const double tick = QwtDate::toDouble( dt.addDays( days ) );
00608
00609 if ( days == 15 && minStepDays != 15 )
00610 mediumTicks += tick;
00611 else
00612 minorTicks += tick;
00613 }
00614 }
00615 else if ( minStepSize > 0.0 )
00616 {
00617 const int numMinorSteps = qRound( stepSize / (double) minStepSize );
00618
00619 for ( int i = 1; i < numMinorSteps; i++ )
00620 {
00621 const double minorValue =
00622 QwtDate::toDouble( dt.addMonths( i * minStepSize ) );
00623
00624 if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) )
00625 mediumTicks += minorValue;
00626 else
00627 minorTicks += minorValue;
00628 }
00629 }
00630 }
00631
00632 QwtScaleDiv scaleDiv;
00633 scaleDiv.setInterval( QwtDate::toDouble( minDate ),
00634 QwtDate::toDouble( maxDate ) );
00635
00636 scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
00637 scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
00638 scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
00639
00640 return scaleDiv;
00641 }
00642
00643 static QwtScaleDiv qwtDivideToYears(
00644 const QDateTime &minDate, const QDateTime &maxDate,
00645 double stepSize, int maxMinSteps )
00646 {
00647 QList<double> majorTicks;
00648 QList<double> mediumTicks;
00649 QList<double> minorTicks;
00650
00651 double minStepSize = 0.0;
00652
00653 if ( maxMinSteps > 1 )
00654 {
00655 minStepSize = qwtDivideMajorStep(
00656 stepSize, maxMinSteps, QwtDate::Year );
00657 }
00658
00659 int numMinorSteps = 0;
00660 if ( minStepSize > 0.0 )
00661 numMinorSteps = qFloor( stepSize / minStepSize );
00662
00663 bool dateBC = minDate.date().year() < -1;
00664
00665 for ( QDateTime dt = minDate; dt <= maxDate;
00666 dt = dt.addYears( stepSize ) )
00667 {
00668 if ( dateBC && dt.date().year() > 1 )
00669 {
00670
00671 dt = dt.addYears( -1 );
00672 dateBC = false;
00673 }
00674
00675 if ( !dt.isValid() )
00676 break;
00677
00678 majorTicks += QwtDate::toDouble( dt );
00679
00680 for ( int i = 1; i < numMinorSteps; i++ )
00681 {
00682 QDateTime tickDate;
00683
00684 const double years = qRound( i * minStepSize );
00685 if ( years >= INT_MAX / 12 )
00686 {
00687 tickDate = dt.addYears( years );
00688 }
00689 else
00690 {
00691 tickDate = dt.addMonths( qRound( years * 12 ) );
00692 }
00693
00694 const bool isMedium = ( numMinorSteps > 2 ) &&
00695 ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 );
00696
00697 const double minorValue = QwtDate::toDouble( tickDate );
00698 if ( isMedium )
00699 mediumTicks += minorValue;
00700 else
00701 minorTicks += minorValue;
00702 }
00703
00704 if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() )
00705 {
00706 break;
00707 }
00708 }
00709
00710 QwtScaleDiv scaleDiv;
00711 scaleDiv.setInterval( QwtDate::toDouble( minDate ),
00712 QwtDate::toDouble( maxDate ) );
00713
00714 scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
00715 scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
00716 scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
00717
00718 return scaleDiv;
00719 }
00720
00721 class QwtDateScaleEngine::PrivateData
00722 {
00723 public:
00724 explicit PrivateData( Qt::TimeSpec spec ):
00725 timeSpec( spec ),
00726 utcOffset( 0 ),
00727 week0Type( QwtDate::FirstThursday ),
00728 maxWeeks( 4 )
00729 {
00730 }
00731
00732 Qt::TimeSpec timeSpec;
00733 int utcOffset;
00734 QwtDate::Week0Type week0Type;
00735 int maxWeeks;
00736 };
00737
00738
00751 QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec ):
00752 QwtLinearScaleEngine( 10 )
00753 {
00754 d_data = new PrivateData( timeSpec );
00755 }
00756
00758 QwtDateScaleEngine::~QwtDateScaleEngine()
00759 {
00760 delete d_data;
00761 }
00762
00769 void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec )
00770 {
00771 d_data->timeSpec = timeSpec;
00772 }
00773
00778 Qt::TimeSpec QwtDateScaleEngine::timeSpec() const
00779 {
00780 return d_data->timeSpec;
00781 }
00782
00793 void QwtDateScaleEngine::setUtcOffset( int seconds )
00794 {
00795 d_data->utcOffset = seconds;
00796 }
00797
00805 int QwtDateScaleEngine::utcOffset() const
00806 {
00807 return d_data->utcOffset;
00808 }
00809
00819 void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type )
00820 {
00821 d_data->week0Type = week0Type;
00822 }
00823
00828 QwtDate::Week0Type QwtDateScaleEngine::week0Type() const
00829 {
00830 return d_data->week0Type;
00831 }
00832
00845 void QwtDateScaleEngine::setMaxWeeks( int weeks )
00846 {
00847 d_data->maxWeeks = qMax( weeks, 0 );
00848 }
00849
00855 int QwtDateScaleEngine::maxWeeks() const
00856 {
00857 return d_data->maxWeeks;
00858 }
00859
00869 QwtDate::IntervalType QwtDateScaleEngine::intervalType(
00870 const QDateTime &minDate, const QDateTime &maxDate,
00871 int maxSteps ) const
00872 {
00873 const double jdMin = minDate.date().toJulianDay();
00874 const double jdMax = maxDate.date().toJulianDay();
00875
00876 if ( ( jdMax - jdMin ) / 365 > maxSteps )
00877 return QwtDate::Year;
00878
00879 const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month );
00880 if ( months > maxSteps * 6 )
00881 return QwtDate::Year;
00882
00883 const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day );
00884 const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week );
00885
00886 if ( weeks > d_data->maxWeeks )
00887 {
00888 if ( days > 4 * maxSteps * 7 )
00889 return QwtDate::Month;
00890 }
00891
00892 if ( days > maxSteps * 7 )
00893 return QwtDate::Week;
00894
00895 const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour );
00896 if ( hours > maxSteps * 24 )
00897 return QwtDate::Day;
00898
00899 const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second );
00900
00901 if ( seconds >= maxSteps * 3600 )
00902 return QwtDate::Hour;
00903
00904 if ( seconds >= maxSteps * 60 )
00905 return QwtDate::Minute;
00906
00907 if ( seconds >= maxSteps )
00908 return QwtDate::Second;
00909
00910 return QwtDate::Millisecond;
00911 }
00912
00929 void QwtDateScaleEngine::autoScale( int maxNumSteps,
00930 double &x1, double &x2, double &stepSize ) const
00931 {
00932 stepSize = 0.0;
00933
00934 QwtInterval interval( x1, x2 );
00935 interval = interval.normalized();
00936
00937 interval.setMinValue( interval.minValue() - lowerMargin() );
00938 interval.setMaxValue( interval.maxValue() + upperMargin() );
00939
00940 if ( testAttribute( QwtScaleEngine::Symmetric ) )
00941 interval = interval.symmetrize( reference() );
00942
00943 if ( testAttribute( QwtScaleEngine::IncludeReference ) )
00944 interval = interval.extend( reference() );
00945
00946 if ( interval.width() == 0.0 )
00947 interval = buildInterval( interval.minValue() );
00948
00949 const QDateTime from = toDateTime( interval.minValue() );
00950 const QDateTime to = toDateTime( interval.maxValue() );
00951
00952 if ( from.isValid() && to.isValid() )
00953 {
00954 if ( maxNumSteps < 1 )
00955 maxNumSteps = 1;
00956
00957 const QwtDate::IntervalType intvType =
00958 intervalType( from, to, maxNumSteps );
00959
00960 const double width = qwtIntervalWidth( from, to, intvType );
00961
00962 const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType );
00963 if ( stepWidth != 0.0 && !testAttribute( QwtScaleEngine::Floating ) )
00964 {
00965 const QDateTime d1 = alignDate( from, stepWidth, intvType, false );
00966 const QDateTime d2 = alignDate( to, stepWidth, intvType, true );
00967
00968 interval.setMinValue( QwtDate::toDouble( d1 ) );
00969 interval.setMaxValue( QwtDate::toDouble( d2 ) );
00970 }
00971
00972 stepSize = stepWidth * qwtMsecsForType( intvType );
00973 }
00974
00975 x1 = interval.minValue();
00976 x2 = interval.maxValue();
00977
00978 if ( testAttribute( QwtScaleEngine::Inverted ) )
00979 {
00980 qSwap( x1, x2 );
00981 stepSize = -stepSize;
00982 }
00983 }
00984
00996 QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2,
00997 int maxMajorSteps, int maxMinorSteps, double stepSize ) const
00998 {
00999 if ( maxMajorSteps < 1 )
01000 maxMajorSteps = 1;
01001
01002 const double min = qMin( x1, x2 );
01003 const double max = qMax( x1, x2 );
01004
01005 const QDateTime from = toDateTime( min );
01006 const QDateTime to = toDateTime( max );
01007
01008 if ( from == to )
01009 return QwtScaleDiv();
01010
01011 stepSize = qAbs( stepSize );
01012 if ( stepSize > 0.0 )
01013 {
01014
01015
01016
01017
01018 maxMajorSteps = qCeil( ( max - min ) / stepSize );
01019 }
01020
01021 const QwtDate::IntervalType intvType =
01022 intervalType( from, to, maxMajorSteps );
01023
01024 QwtScaleDiv scaleDiv;
01025
01026 if ( intvType == QwtDate::Millisecond )
01027 {
01028
01029 scaleDiv = QwtLinearScaleEngine::divideScale( min, max,
01030 maxMajorSteps, maxMinorSteps, stepSize );
01031 }
01032 else
01033 {
01034 const QDateTime minDate = QwtDate::floor( from, intvType );
01035 const QDateTime maxDate = QwtDate::ceil( to, intvType );
01036
01037 scaleDiv = buildScaleDiv( minDate, maxDate,
01038 maxMajorSteps, maxMinorSteps, intvType );
01039
01040
01041
01042
01043 scaleDiv = scaleDiv.bounded( min, max );
01044 }
01045
01046 if ( x1 > x2 )
01047 scaleDiv.invert();
01048
01049 return scaleDiv;
01050 }
01051
01052 QwtScaleDiv QwtDateScaleEngine::buildScaleDiv(
01053 const QDateTime &minDate, const QDateTime &maxDate,
01054 int maxMajorSteps, int maxMinorSteps,
01055 QwtDate::IntervalType intervalType ) const
01056 {
01057
01058 const double stepSize = qwtDivideScale(
01059 qwtIntervalWidth( minDate, maxDate, intervalType ),
01060 maxMajorSteps, intervalType );
01061
01062
01063 QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false );
01064 if ( !dt0.isValid() )
01065 {
01066
01067
01068 dt0 = alignDate( minDate, stepSize, intervalType, true );
01069 }
01070
01071 QwtScaleDiv scaleDiv;
01072
01073 if ( intervalType <= QwtDate::Week )
01074 {
01075 scaleDiv = qwtDivideToSeconds( dt0, maxDate,
01076 stepSize, maxMinorSteps, intervalType );
01077 }
01078 else
01079 {
01080 if( intervalType == QwtDate::Month )
01081 {
01082 scaleDiv = qwtDivideToMonths( dt0, maxDate,
01083 stepSize, maxMinorSteps );
01084 }
01085 else if ( intervalType == QwtDate::Year )
01086 {
01087 scaleDiv = qwtDivideToYears( dt0, maxDate,
01088 stepSize, maxMinorSteps );
01089 }
01090 }
01091
01092
01093 return scaleDiv;
01094 }
01095
01112 QDateTime QwtDateScaleEngine::alignDate(
01113 const QDateTime &dateTime, double stepSize,
01114 QwtDate::IntervalType intervalType, bool up ) const
01115 {
01116
01117
01118 QDateTime dt = dateTime;
01119
01120 if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
01121 {
01122 dt.setUtcOffset( 0 );
01123 }
01124
01125 switch( intervalType )
01126 {
01127 case QwtDate::Millisecond:
01128 {
01129 const int ms = qwtAlignValue(
01130 dt.time().msec(), stepSize, up ) ;
01131
01132 dt = QwtDate::floor( dateTime, QwtDate::Second );
01133 dt = dt.addMSecs( ms );
01134
01135 break;
01136 }
01137 case QwtDate::Second:
01138 {
01139 int second = dt.time().second();
01140 if ( up )
01141 {
01142 if ( dt.time().msec() > 0 )
01143 second++;
01144 }
01145
01146 const int s = qwtAlignValue( second, stepSize, up );
01147
01148 dt = QwtDate::floor( dt, QwtDate::Minute );
01149 dt = dt.addSecs( s );
01150
01151 break;
01152 }
01153 case QwtDate::Minute:
01154 {
01155 int minute = dt.time().minute();
01156 if ( up )
01157 {
01158 if ( dt.time().msec() > 0 || dt.time().second() > 0 )
01159 minute++;
01160 }
01161
01162 const int m = qwtAlignValue( minute, stepSize, up );
01163
01164 dt = QwtDate::floor( dt, QwtDate::Hour );
01165 dt = dt.addSecs( m * 60 );
01166
01167 break;
01168 }
01169 case QwtDate::Hour:
01170 {
01171 int hour = dt.time().hour();
01172 if ( up )
01173 {
01174 if ( dt.time().msec() > 0 || dt.time().second() > 0
01175 || dt.time().minute() > 0 )
01176 {
01177 hour++;
01178 }
01179 }
01180 const int h = qwtAlignValue( hour, stepSize, up );
01181
01182 dt = QwtDate::floor( dt, QwtDate::Day );
01183 dt = dt.addSecs( h * 3600 );
01184
01185 break;
01186 }
01187 case QwtDate::Day:
01188 {
01189
01190
01191
01192
01193 int day = dt.date().dayOfYear();
01194 if ( up )
01195 {
01196 if ( dt.time() > QTime( 0, 0 ) )
01197 day++;
01198 }
01199
01200 const int d = qwtAlignValue( day, stepSize, up );
01201
01202 dt = QwtDate::floor( dt, QwtDate::Year );
01203 dt = dt.addDays( d - 1 );
01204
01205 break;
01206 }
01207 case QwtDate::Week:
01208 {
01209 const QDate date = QwtDate::dateOfWeek0(
01210 dt.date().year(), d_data->week0Type );
01211
01212 int numWeeks = date.daysTo( dt.date() ) / 7;
01213 if ( up )
01214 {
01215 if ( dt.time() > QTime( 0, 0 ) ||
01216 date.daysTo( dt.date() ) % 7 )
01217 {
01218 numWeeks++;
01219 }
01220 }
01221
01222 const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
01223
01224 dt = QwtDate::floor( dt, QwtDate::Day );
01225 dt.setDate( date );
01226 dt = dt.addDays( d );
01227
01228 break;
01229 }
01230 case QwtDate::Month:
01231 {
01232 int month = dt.date().month();
01233 if ( up )
01234 {
01235 if ( dt.date().day() > 1 ||
01236 dt.time() > QTime( 0, 0 ) )
01237 {
01238 month++;
01239 }
01240 }
01241
01242 const int m = qwtAlignValue( month - 1, stepSize, up );
01243
01244 dt = QwtDate::floor( dt, QwtDate::Year );
01245 dt = dt.addMonths( m );
01246
01247 break;
01248 }
01249 case QwtDate::Year:
01250 {
01251 int year = dateTime.date().year();
01252 if ( up )
01253 {
01254 if ( dateTime.date().dayOfYear() > 1 ||
01255 dt.time() > QTime( 0, 0 ) )
01256 {
01257 year++;
01258 }
01259 }
01260
01261 const int y = qwtAlignValue( year, stepSize, up );
01262
01263 dt = QwtDate::floor( dt, QwtDate::Day );
01264 if ( y == 0 )
01265 {
01266
01267 dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
01268 }
01269 else
01270 {
01271 dt.setDate( QDate( y, 1, 1 ) );
01272 }
01273
01274 break;
01275 }
01276 }
01277
01278 if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
01279 {
01280 dt.setUtcOffset( dateTime.utcOffset() );
01281 }
01282
01283 return dt;
01284 }
01285
01294 QDateTime QwtDateScaleEngine::toDateTime( double value ) const
01295 {
01296 QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
01297 if ( !dt.isValid() )
01298 {
01299 const QDate date = ( value <= 0.0 )
01300 ? QwtDate::minDate() : QwtDate::maxDate();
01301
01302 dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec );
01303 }
01304
01305 if ( d_data->timeSpec == Qt::OffsetFromUTC )
01306 {
01307 dt = dt.addSecs( d_data->utcOffset );
01308 dt.setUtcOffset( d_data->utcOffset );
01309 }
01310
01311 return dt;
01312 }
01313