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 for ( int i = 0; i < 3600; i += secondsMinor )
00442 ticks += dstMin + i * 1000.0;
00443
00444 return ticks;
00445 }
00446
00447 static QwtScaleDiv qwtDivideToSeconds(
00448 const QDateTime &minDate, const QDateTime &maxDate,
00449 double stepSize, int maxMinSteps,
00450 QwtDate::IntervalType intervalType )
00451 {
00452
00453 double minStepSize = 0;
00454
00455 if ( maxMinSteps > 1 )
00456 {
00457 minStepSize = qwtDivideMajorStep( stepSize,
00458 maxMinSteps, intervalType );
00459 }
00460
00461 bool daylightSaving = false;
00462 if ( minDate.timeSpec() == Qt::LocalTime )
00463 {
00464 daylightSaving = intervalType > QwtDate::Hour;
00465 if ( intervalType == QwtDate::Hour )
00466 {
00467 daylightSaving = stepSize > 1;
00468 }
00469 }
00470
00471 const double s = qwtMsecsForType( intervalType ) / 1000;
00472 const int secondsMajor = static_cast<int>( stepSize * s );
00473 const double secondsMinor = minStepSize * s;
00474
00475
00476
00477
00478
00479 const double utcOffset = QwtDate::utcOffset( minDate );
00480 double dstOff = 0;
00481
00482 QList<double> majorTicks;
00483 QList<double> mediumTicks;
00484 QList<double> minorTicks;
00485
00486 for ( QDateTime dt = minDate; dt <= maxDate;
00487 dt = dt.addSecs( secondsMajor ) )
00488 {
00489 if ( !dt.isValid() )
00490 break;
00491
00492 double majorValue = QwtDate::toDouble( dt );
00493
00494 if ( daylightSaving )
00495 {
00496 const double offset = utcOffset - QwtDate::utcOffset( dt );
00497 majorValue += offset * 1000.0;
00498
00499 if ( offset > dstOff )
00500 {
00501
00502
00503 minorTicks += qwtDstTicks(
00504 dt, secondsMajor, qRound( secondsMinor ) );
00505 }
00506
00507 dstOff = offset;
00508 }
00509
00510 if ( majorTicks.isEmpty() || majorTicks.last() != majorValue )
00511 majorTicks += majorValue;
00512
00513 if ( secondsMinor > 0.0 )
00514 {
00515 const int numMinorSteps = qFloor( secondsMajor / secondsMinor );
00516
00517 for ( int i = 1; i < numMinorSteps; i++ )
00518 {
00519 const QDateTime mt = dt.addMSecs(
00520 qRound64( i * secondsMinor * 1000 ) );
00521
00522 double minorValue = QwtDate::toDouble( mt );
00523 if ( daylightSaving )
00524 {
00525 const double offset = utcOffset - QwtDate::utcOffset( mt );
00526 minorValue += offset * 1000.0;
00527 }
00528
00529 if ( minorTicks.isEmpty() || minorTicks.last() != minorValue )
00530 {
00531 const bool isMedium = ( numMinorSteps % 2 == 0 )
00532 && ( i != 1 ) && ( i == numMinorSteps / 2 );
00533
00534 if ( isMedium )
00535 mediumTicks += minorValue;
00536 else
00537 minorTicks += minorValue;
00538 }
00539 }
00540 }
00541 }
00542
00543 QwtScaleDiv scaleDiv;
00544
00545 scaleDiv.setInterval( QwtDate::toDouble( minDate ),
00546 QwtDate::toDouble( maxDate ) );
00547
00548 scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
00549 scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
00550 scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
00551
00552 return scaleDiv;
00553 }
00554
00555 static QwtScaleDiv qwtDivideToMonths(
00556 QDateTime &minDate, const QDateTime &maxDate,
00557 double stepSize, int maxMinSteps )
00558 {
00559
00560
00561
00562
00563 int minStepDays = 0;
00564 int minStepSize = 0.0;
00565
00566 if ( maxMinSteps > 1 )
00567 {
00568 if ( stepSize == 1 )
00569 {
00570 if ( maxMinSteps >= 30 )
00571 minStepDays = 1;
00572 else if ( maxMinSteps >= 6 )
00573 minStepDays = 5;
00574 else if ( maxMinSteps >= 3 )
00575 minStepDays = 10;
00576 else
00577 minStepDays = 15;
00578 }
00579 else
00580 {
00581 minStepSize = qwtDivideMajorStep(
00582 stepSize, maxMinSteps, QwtDate::Month );
00583 }
00584 }
00585
00586 QList<double> majorTicks;
00587 QList<double> mediumTicks;
00588 QList<double> minorTicks;
00589
00590 for ( QDateTime dt = minDate;
00591 dt <= maxDate; dt = dt.addMonths( stepSize ) )
00592 {
00593 if ( !dt.isValid() )
00594 break;
00595
00596 majorTicks += QwtDate::toDouble( dt );
00597
00598 if ( minStepDays > 0 )
00599 {
00600 for ( int days = minStepDays;
00601 days < 30; days += minStepDays )
00602 {
00603 const double tick = QwtDate::toDouble( dt.addDays( days ) );
00604
00605 if ( days == 15 && minStepDays != 15 )
00606 mediumTicks += tick;
00607 else
00608 minorTicks += tick;
00609 }
00610 }
00611 else if ( minStepSize > 0.0 )
00612 {
00613 const int numMinorSteps = qRound( stepSize / (double) minStepSize );
00614
00615 for ( int i = 1; i < numMinorSteps; i++ )
00616 {
00617 const double minorValue =
00618 QwtDate::toDouble( dt.addMonths( i * minStepSize ) );
00619
00620 if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) )
00621 mediumTicks += minorValue;
00622 else
00623 minorTicks += minorValue;
00624 }
00625 }
00626 }
00627
00628 QwtScaleDiv scaleDiv;
00629 scaleDiv.setInterval( QwtDate::toDouble( minDate ),
00630 QwtDate::toDouble( maxDate ) );
00631
00632 scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
00633 scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
00634 scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
00635
00636 return scaleDiv;
00637 }
00638
00639 static QwtScaleDiv qwtDivideToYears(
00640 const QDateTime &minDate, const QDateTime &maxDate,
00641 double stepSize, int maxMinSteps )
00642 {
00643 QList<double> majorTicks;
00644 QList<double> mediumTicks;
00645 QList<double> minorTicks;
00646
00647 double minStepSize = 0.0;
00648
00649 if ( maxMinSteps > 1 )
00650 {
00651 minStepSize = qwtDivideMajorStep(
00652 stepSize, maxMinSteps, QwtDate::Year );
00653 }
00654
00655 int numMinorSteps = 0;
00656 if ( minStepSize > 0.0 )
00657 numMinorSteps = qFloor( stepSize / minStepSize );
00658
00659 bool dateBC = minDate.date().year() < -1;
00660
00661 for ( QDateTime dt = minDate; dt <= maxDate;
00662 dt = dt.addYears( stepSize ) )
00663 {
00664 if ( dateBC && dt.date().year() > 1 )
00665 {
00666
00667 dt = dt.addYears( -1 );
00668 dateBC = false;
00669 }
00670
00671 if ( !dt.isValid() )
00672 break;
00673
00674 majorTicks += QwtDate::toDouble( dt );
00675
00676 for ( int i = 1; i < numMinorSteps; i++ )
00677 {
00678 QDateTime tickDate;
00679
00680 const double years = qRound( i * minStepSize );
00681 if ( years >= INT_MAX / 12 )
00682 {
00683 tickDate = dt.addYears( years );
00684 }
00685 else
00686 {
00687 tickDate = dt.addMonths( qRound( years * 12 ) );
00688 }
00689
00690 const bool isMedium = ( numMinorSteps > 2 ) &&
00691 ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 );
00692
00693 const double minorValue = QwtDate::toDouble( tickDate );
00694 if ( isMedium )
00695 mediumTicks += minorValue;
00696 else
00697 minorTicks += minorValue;
00698 }
00699
00700 if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() )
00701 {
00702 break;
00703 }
00704 }
00705
00706 QwtScaleDiv scaleDiv;
00707 scaleDiv.setInterval( QwtDate::toDouble( minDate ),
00708 QwtDate::toDouble( maxDate ) );
00709
00710 scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
00711 scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
00712 scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
00713
00714 return scaleDiv;
00715 }
00716
00717 class QwtDateScaleEngine::PrivateData
00718 {
00719 public:
00720 explicit PrivateData( Qt::TimeSpec spec ):
00721 timeSpec( spec ),
00722 utcOffset( 0 ),
00723 week0Type( QwtDate::FirstThursday ),
00724 maxWeeks( 4 )
00725 {
00726 }
00727
00728 Qt::TimeSpec timeSpec;
00729 int utcOffset;
00730 QwtDate::Week0Type week0Type;
00731 int maxWeeks;
00732 };
00733
00734
00747 QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec ):
00748 QwtLinearScaleEngine( 10 )
00749 {
00750 d_data = new PrivateData( timeSpec );
00751 }
00752
00754 QwtDateScaleEngine::~QwtDateScaleEngine()
00755 {
00756 delete d_data;
00757 }
00758
00765 void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec )
00766 {
00767 d_data->timeSpec = timeSpec;
00768 }
00769
00774 Qt::TimeSpec QwtDateScaleEngine::timeSpec() const
00775 {
00776 return d_data->timeSpec;
00777 }
00778
00789 void QwtDateScaleEngine::setUtcOffset( int seconds )
00790 {
00791 d_data->utcOffset = seconds;
00792 }
00793
00801 int QwtDateScaleEngine::utcOffset() const
00802 {
00803 return d_data->utcOffset;
00804 }
00805
00815 void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type )
00816 {
00817 d_data->week0Type = week0Type;
00818 }
00819
00824 QwtDate::Week0Type QwtDateScaleEngine::week0Type() const
00825 {
00826 return d_data->week0Type;
00827 }
00828
00841 void QwtDateScaleEngine::setMaxWeeks( int weeks )
00842 {
00843 d_data->maxWeeks = qMax( weeks, 0 );
00844 }
00845
00851 int QwtDateScaleEngine::maxWeeks() const
00852 {
00853 return d_data->maxWeeks;
00854 }
00855
00865 QwtDate::IntervalType QwtDateScaleEngine::intervalType(
00866 const QDateTime &minDate, const QDateTime &maxDate,
00867 int maxSteps ) const
00868 {
00869 const double jdMin = minDate.date().toJulianDay();
00870 const double jdMax = maxDate.date().toJulianDay();
00871
00872 if ( ( jdMax - jdMin ) / 365 > maxSteps )
00873 return QwtDate::Year;
00874
00875 const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month );
00876 if ( months > maxSteps * 6 )
00877 return QwtDate::Year;
00878
00879 const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day );
00880 const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week );
00881
00882 if ( weeks > d_data->maxWeeks )
00883 {
00884 if ( days > 4 * maxSteps * 7 )
00885 return QwtDate::Month;
00886 }
00887
00888 if ( days > maxSteps * 7 )
00889 return QwtDate::Week;
00890
00891 const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour );
00892 if ( hours > maxSteps * 24 )
00893 return QwtDate::Day;
00894
00895 const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second );
00896
00897 if ( seconds >= maxSteps * 3600 )
00898 return QwtDate::Hour;
00899
00900 if ( seconds >= maxSteps * 60 )
00901 return QwtDate::Minute;
00902
00903 if ( seconds >= maxSteps )
00904 return QwtDate::Second;
00905
00906 return QwtDate::Millisecond;
00907 }
00908
00925 void QwtDateScaleEngine::autoScale( int maxNumSteps,
00926 double &x1, double &x2, double &stepSize ) const
00927 {
00928 stepSize = 0.0;
00929
00930 QwtInterval interval( x1, x2 );
00931 interval = interval.normalized();
00932
00933 interval.setMinValue( interval.minValue() - lowerMargin() );
00934 interval.setMaxValue( interval.maxValue() + upperMargin() );
00935
00936 if ( testAttribute( QwtScaleEngine::Symmetric ) )
00937 interval = interval.symmetrize( reference() );
00938
00939 if ( testAttribute( QwtScaleEngine::IncludeReference ) )
00940 interval = interval.extend( reference() );
00941
00942 if ( interval.width() == 0.0 )
00943 interval = buildInterval( interval.minValue() );
00944
00945 const QDateTime from = toDateTime( interval.minValue() );
00946 const QDateTime to = toDateTime( interval.maxValue() );
00947
00948 if ( from.isValid() && to.isValid() )
00949 {
00950 if ( maxNumSteps < 1 )
00951 maxNumSteps = 1;
00952
00953 const QwtDate::IntervalType intvType =
00954 intervalType( from, to, maxNumSteps );
00955
00956 const double width = qwtIntervalWidth( from, to, intvType );
00957
00958 const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType );
00959 if ( stepWidth != 0.0 && !testAttribute( QwtScaleEngine::Floating ) )
00960 {
00961 const QDateTime d1 = alignDate( from, stepWidth, intvType, false );
00962 const QDateTime d2 = alignDate( to, stepWidth, intvType, true );
00963
00964 interval.setMinValue( QwtDate::toDouble( d1 ) );
00965 interval.setMaxValue( QwtDate::toDouble( d2 ) );
00966 }
00967
00968 stepSize = stepWidth * qwtMsecsForType( intvType );
00969 }
00970
00971 x1 = interval.minValue();
00972 x2 = interval.maxValue();
00973
00974 if ( testAttribute( QwtScaleEngine::Inverted ) )
00975 {
00976 qSwap( x1, x2 );
00977 stepSize = -stepSize;
00978 }
00979 }
00980
00992 QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2,
00993 int maxMajorSteps, int maxMinorSteps, double stepSize ) const
00994 {
00995 if ( maxMajorSteps < 1 )
00996 maxMajorSteps = 1;
00997
00998 const double min = qMin( x1, x2 );
00999 const double max = qMax( x1, x2 );
01000
01001 const QDateTime from = toDateTime( min );
01002 const QDateTime to = toDateTime( max );
01003
01004 if ( from == to )
01005 return QwtScaleDiv();
01006
01007 stepSize = qAbs( stepSize );
01008 if ( stepSize > 0.0 )
01009 {
01010
01011
01012
01013
01014 maxMajorSteps = qCeil( ( max - min ) / stepSize );
01015 }
01016
01017 const QwtDate::IntervalType intvType =
01018 intervalType( from, to, maxMajorSteps );
01019
01020 QwtScaleDiv scaleDiv;
01021
01022 if ( intvType == QwtDate::Millisecond )
01023 {
01024
01025 scaleDiv = QwtLinearScaleEngine::divideScale( min, max,
01026 maxMajorSteps, maxMinorSteps, stepSize );
01027 }
01028 else
01029 {
01030 const QDateTime minDate = QwtDate::floor( from, intvType );
01031 const QDateTime maxDate = QwtDate::ceil( to, intvType );
01032
01033 scaleDiv = buildScaleDiv( minDate, maxDate,
01034 maxMajorSteps, maxMinorSteps, intvType );
01035
01036
01037
01038
01039 scaleDiv = scaleDiv.bounded( min, max );
01040 }
01041
01042 if ( x1 > x2 )
01043 scaleDiv.invert();
01044
01045 return scaleDiv;
01046 }
01047
01048 QwtScaleDiv QwtDateScaleEngine::buildScaleDiv(
01049 const QDateTime &minDate, const QDateTime &maxDate,
01050 int maxMajorSteps, int maxMinorSteps,
01051 QwtDate::IntervalType intervalType ) const
01052 {
01053
01054 const double stepSize = qwtDivideScale(
01055 qwtIntervalWidth( minDate, maxDate, intervalType ),
01056 maxMajorSteps, intervalType );
01057
01058
01059 QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false );
01060 if ( !dt0.isValid() )
01061 {
01062
01063
01064 dt0 = alignDate( minDate, stepSize, intervalType, true );
01065 }
01066
01067 QwtScaleDiv scaleDiv;
01068
01069 if ( intervalType <= QwtDate::Week )
01070 {
01071 scaleDiv = qwtDivideToSeconds( dt0, maxDate,
01072 stepSize, maxMinorSteps, intervalType );
01073 }
01074 else
01075 {
01076 if( intervalType == QwtDate::Month )
01077 {
01078 scaleDiv = qwtDivideToMonths( dt0, maxDate,
01079 stepSize, maxMinorSteps );
01080 }
01081 else if ( intervalType == QwtDate::Year )
01082 {
01083 scaleDiv = qwtDivideToYears( dt0, maxDate,
01084 stepSize, maxMinorSteps );
01085 }
01086 }
01087
01088
01089 return scaleDiv;
01090 }
01091
01108 QDateTime QwtDateScaleEngine::alignDate(
01109 const QDateTime &dateTime, double stepSize,
01110 QwtDate::IntervalType intervalType, bool up ) const
01111 {
01112
01113
01114 QDateTime dt = dateTime;
01115
01116 if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
01117 {
01118 dt.setUtcOffset( 0 );
01119 }
01120
01121 switch( intervalType )
01122 {
01123 case QwtDate::Millisecond:
01124 {
01125 const int ms = qwtAlignValue(
01126 dt.time().msec(), stepSize, up ) ;
01127
01128 dt = QwtDate::floor( dateTime, QwtDate::Second );
01129 dt = dt.addMSecs( ms );
01130
01131 break;
01132 }
01133 case QwtDate::Second:
01134 {
01135 int second = dt.time().second();
01136 if ( up )
01137 {
01138 if ( dt.time().msec() > 0 )
01139 second++;
01140 }
01141
01142 const int s = qwtAlignValue( second, stepSize, up );
01143
01144 dt = QwtDate::floor( dt, QwtDate::Minute );
01145 dt = dt.addSecs( s );
01146
01147 break;
01148 }
01149 case QwtDate::Minute:
01150 {
01151 int minute = dt.time().minute();
01152 if ( up )
01153 {
01154 if ( dt.time().msec() > 0 || dt.time().second() > 0 )
01155 minute++;
01156 }
01157
01158 const int m = qwtAlignValue( minute, stepSize, up );
01159
01160 dt = QwtDate::floor( dt, QwtDate::Hour );
01161 dt = dt.addSecs( m * 60 );
01162
01163 break;
01164 }
01165 case QwtDate::Hour:
01166 {
01167 int hour = dt.time().hour();
01168 if ( up )
01169 {
01170 if ( dt.time().msec() > 0 || dt.time().second() > 0
01171 || dt.time().minute() > 0 )
01172 {
01173 hour++;
01174 }
01175 }
01176 const int h = qwtAlignValue( hour, stepSize, up );
01177
01178 dt = QwtDate::floor( dt, QwtDate::Day );
01179 dt = dt.addSecs( h * 3600 );
01180
01181 break;
01182 }
01183 case QwtDate::Day:
01184 {
01185
01186
01187
01188
01189 int day = dt.date().dayOfYear();
01190 if ( up )
01191 {
01192 if ( dt.time() > QTime( 0, 0 ) )
01193 day++;
01194 }
01195
01196 const int d = qwtAlignValue( day, stepSize, up );
01197
01198 dt = QwtDate::floor( dt, QwtDate::Year );
01199 dt = dt.addDays( d - 1 );
01200
01201 break;
01202 }
01203 case QwtDate::Week:
01204 {
01205 const QDate date = QwtDate::dateOfWeek0(
01206 dt.date().year(), d_data->week0Type );
01207
01208 int numWeeks = date.daysTo( dt.date() ) / 7;
01209 if ( up )
01210 {
01211 if ( dt.time() > QTime( 0, 0 ) ||
01212 date.daysTo( dt.date() ) % 7 )
01213 {
01214 numWeeks++;
01215 }
01216 }
01217
01218 const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
01219
01220 dt = QwtDate::floor( dt, QwtDate::Day );
01221 dt.setDate( date );
01222 dt = dt.addDays( d );
01223
01224 break;
01225 }
01226 case QwtDate::Month:
01227 {
01228 int month = dt.date().month();
01229 if ( up )
01230 {
01231 if ( dt.date().day() > 1 ||
01232 dt.time() > QTime( 0, 0 ) )
01233 {
01234 month++;
01235 }
01236 }
01237
01238 const int m = qwtAlignValue( month - 1, stepSize, up );
01239
01240 dt = QwtDate::floor( dt, QwtDate::Year );
01241 dt = dt.addMonths( m );
01242
01243 break;
01244 }
01245 case QwtDate::Year:
01246 {
01247 int year = dateTime.date().year();
01248 if ( up )
01249 {
01250 if ( dateTime.date().dayOfYear() > 1 ||
01251 dt.time() > QTime( 0, 0 ) )
01252 {
01253 year++;
01254 }
01255 }
01256
01257 const int y = qwtAlignValue( year, stepSize, up );
01258
01259 dt = QwtDate::floor( dt, QwtDate::Day );
01260 if ( y == 0 )
01261 {
01262
01263 dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
01264 }
01265 else
01266 {
01267 dt.setDate( QDate( y, 1, 1 ) );
01268 }
01269
01270 break;
01271 }
01272 }
01273
01274 if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
01275 {
01276 dt.setUtcOffset( dateTime.utcOffset() );
01277 }
01278
01279 return dt;
01280 }
01281
01290 QDateTime QwtDateScaleEngine::toDateTime( double value ) const
01291 {
01292 QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
01293 if ( !dt.isValid() )
01294 {
01295 const QDate date = ( value <= 0.0 )
01296 ? QwtDate::minDate() : QwtDate::maxDate();
01297
01298 dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec );
01299 }
01300
01301 if ( d_data->timeSpec == Qt::OffsetFromUTC )
01302 {
01303 dt = dt.addSecs( d_data->utcOffset );
01304 dt.setUtcOffset( d_data->utcOffset );
01305 }
01306
01307 return dt;
01308 }
01309