10#include "qwt_date_scale_engine.h" 
   12#include "qwt_interval.h" 
   18static inline double qwtMsecsForType( 
int type )
 
   20    static const double msecs[] =
 
   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,
 
   32    if ( type < 0 || type >= 
static_cast< int >( 
sizeof( msecs ) / 
sizeof( msecs[0] ) ) )
 
   38static inline int qwtAlignValue(
 
   39    double value, 
double stepSize, 
bool up )
 
   41    double d = value / stepSize;
 
   42    d = up ? std::ceil( d ) : std::floor( d );
 
   44    return static_cast< int >( d * stepSize );
 
   47static double qwtIntervalWidth( 
const QDateTime& minDate,
 
   50    switch( intervalType )
 
   54            return minDate.msecsTo( maxDate );
 
   58            return minDate.secsTo( maxDate );
 
   62            const double secsTo = minDate.secsTo( maxDate );
 
   63            return std::floor( secsTo / 60 );
 
   67            const double secsTo = minDate.secsTo( maxDate );
 
   68            return std::floor( secsTo / 3600 );
 
   72            return minDate.daysTo( maxDate );
 
   76            return std::floor( minDate.daysTo( maxDate ) / 7.0 );
 
   81                double( maxDate.date().year() ) - minDate.date().year();
 
   83            int months = maxDate.date().month() - minDate.date().month();
 
   84            if ( maxDate.date().day() < minDate.date().day() )
 
   87            return years * 12 + months;
 
   92                double( maxDate.date().year() ) - minDate.date().year();
 
   94            if ( maxDate.date().month() < minDate.date().month() )
 
  104static double qwtRoundedIntervalWidth(
 
  105    const QDateTime& minDate, 
const QDateTime& maxDate,
 
  109    const QDateTime maxD = 
QwtDate::ceil( maxDate, intervalType );
 
  111    return qwtIntervalWidth( minD, maxD, intervalType );
 
  114static inline int qwtStepCount( 
int intervalSize, 
int maxSteps,
 
  115    const int limits[], 
size_t numLimits )
 
  117    for ( uint i = 0; i < numLimits; i++ )
 
  119        const int numSteps = intervalSize / limits[ i ];
 
  121        if ( numSteps > 1 && numSteps <= maxSteps &&
 
  122            numSteps * limits[ i ] == intervalSize )
 
  131static int qwtStepSize( 
int intervalSize, 
int maxSteps, uint base )
 
  138        for ( 
int numSteps = maxSteps; numSteps > 1; numSteps-- )
 
  140            const double stepSize = double( intervalSize ) / numSteps;
 
  142            const double p = std::floor( std::log( stepSize ) / std::log( 
double( base ) ) );
 
  143            const double fraction = std::pow( base, p );
 
  145            for ( uint n = base; n >= 1; n /= 2 )
 
  147                if ( qFuzzyCompare( stepSize, n * fraction ) )
 
  148                    return qRound( stepSize );
 
  150                if ( n == 3 && ( base % 2 ) == 0 )
 
  152                    if ( qFuzzyCompare( stepSize, 2 * fraction ) )
 
  153                        return qRound( stepSize );
 
  162static int qwtDivideInterval( 
double intervalSize, 
int numSteps,
 
  163    const int limits[], 
size_t numLimits )
 
  165    const int v = qwtCeil( intervalSize / 
double( numSteps ) );
 
  167    for ( uint i = 0; i < numLimits - 1; i++ )
 
  169        if ( v <= limits[i] )
 
  173    return limits[ numLimits - 1 ];
 
  176static double qwtDivideScale( 
double intervalSize, 
int numSteps,
 
  181        if ( ( intervalSize > numSteps ) &&
 
  182            ( intervalSize <= 2 * numSteps ) )
 
  190    switch( intervalType )
 
  195            static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
 
  197            stepSize = qwtDivideInterval( intervalSize, numSteps,
 
  198                limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  204            static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
 
  206            stepSize = qwtDivideInterval( intervalSize, numSteps,
 
  207                limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  213            const double v = intervalSize / double( numSteps );
 
  215                stepSize = std::ceil( v );
 
  217                stepSize = std::ceil( v / 7 ) * 7;
 
  223            static int limits[] = { 1, 2, 4, 8, 12, 26, 52 };
 
  225            stepSize = qwtDivideInterval( intervalSize, numSteps,
 
  226                limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  232            static int limits[] = { 1, 2, 3, 4, 6, 12 };
 
  234            stepSize = qwtDivideInterval( intervalSize, numSteps,
 
  235                limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  244                intervalSize, numSteps, 10 );
 
  251static double qwtDivideMajorStep( 
double stepSize, 
int maxMinSteps,
 
  254    double minStepSize = 0.0;
 
  256    switch( intervalType )
 
  260            minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 );
 
  261            if ( minStepSize == 0.0 )
 
  262                minStepSize = 0.5 * stepSize;
 
  268            static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
 
  272            if ( stepSize > maxMinSteps )
 
  274                numSteps = qwtStepCount( stepSize, maxMinSteps,
 
  275                    limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  280                numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
 
  281                    limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  285                minStepSize = stepSize / numSteps;
 
  293            if ( stepSize > maxMinSteps )
 
  295                static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
 
  297                numSteps = qwtStepCount( stepSize, maxMinSteps,
 
  298                    limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  302                static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
 
  304                numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
 
  305                    limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  309                minStepSize = stepSize / numSteps;
 
  317            if ( stepSize > maxMinSteps )
 
  319                static int limits[] = { 1, 2, 3, 7, 14, 28 };
 
  321                numSteps = qwtStepCount( stepSize, maxMinSteps,
 
  322                    limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  326                static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
 
  328                numSteps = qwtStepCount( stepSize * 24, maxMinSteps,
 
  329                    limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  333                minStepSize = stepSize / numSteps;
 
  339            const int daysInStep = stepSize * 7;
 
  341            if ( maxMinSteps >= daysInStep )
 
  344                minStepSize = 1.0 / 7.0;
 
  351                const int stepSizeInWeeks = stepSize;
 
  353                if ( stepSizeInWeeks <= maxMinSteps )
 
  360                        stepSizeInWeeks, maxMinSteps, 10 );
 
  369            if ( stepSize < maxMinSteps )
 
  370                maxMinSteps = 
static_cast< int >( stepSize );
 
  372            static int limits[] = { 1, 2, 3, 4, 6, 12 };
 
  374            int numSteps = qwtStepCount( stepSize, maxMinSteps,
 
  375                limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  378                minStepSize = stepSize / numSteps;
 
  384            if ( stepSize >= maxMinSteps )
 
  387                    stepSize, maxMinSteps, 10 );
 
  393                static int limits[] = { 1, 2, 3, 4, 6, 12 };
 
  395                int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps,
 
  396                    limits, 
sizeof( limits ) / 
sizeof( 
int ) );
 
  399                    minStepSize = stepSize / numSteps;
 
  409        && minStepSize == 0.0 )
 
  411        minStepSize = 0.5 * stepSize;
 
  418    int secondsMajor, 
int secondsMinor )
 
  420    if ( secondsMinor <= 0 )
 
  423    QDateTime minDate = dateTime.addSecs( -secondsMajor );
 
  431    while ( minDate < dateTime &&
 
  434        minDate = minDate.addSecs( 3600 );
 
  435        dstMin += 3600 * 1000.0;
 
  439    ticks.reserve( 3600 / secondsMinor);
 
  441    for ( 
int i = 0; i < 3600; i += secondsMinor )
 
  442        ticks += dstMin + i * 1000.0;
 
  448    const QDateTime& minDate, 
const QDateTime& maxDate,
 
  449    double stepSize, 
int maxMinSteps,
 
  453    double minStepSize = 0;
 
  455    if ( maxMinSteps > 1 )
 
  457        minStepSize = qwtDivideMajorStep( stepSize,
 
  458            maxMinSteps, intervalType );
 
  461    bool daylightSaving = 
false;
 
  462    if ( minDate.timeSpec() == Qt::LocalTime )
 
  467            daylightSaving = stepSize > 1;
 
  471    const double s = qwtMsecsForType( intervalType ) / 1000;
 
  472    const int secondsMajor = 
static_cast< int >( stepSize * s );
 
  473    const double secondsMinor = minStepSize * s;
 
  486    for ( QDateTime dt = minDate; dt <= maxDate;
 
  487        dt = dt.addSecs( secondsMajor ) )
 
  494        if ( daylightSaving )
 
  497            majorValue += offset * 1000.0;
 
  499            if ( offset > dstOff )
 
  503                minorTicks += qwtDstTicks(
 
  504                    dt, secondsMajor, qRound( secondsMinor ) );
 
  510        if ( majorTicks.isEmpty() || majorTicks.last() != majorValue )
 
  511            majorTicks += majorValue;
 
  513        if ( secondsMinor > 0.0 )
 
  515            const int numMinorSteps = qwtFloor( secondsMajor / secondsMinor );
 
  517            for ( 
int i = 1; i < numMinorSteps; i++ )
 
  519                const QDateTime mt = dt.addMSecs(
 
  520                    qRound64( i * secondsMinor * 1000 ) );
 
  523                if ( daylightSaving )
 
  526                    minorValue += offset * 1000.0;
 
  529                if ( minorTicks.isEmpty() || minorTicks.last() != minorValue )
 
  531                    const bool isMedium = ( numMinorSteps % 2 == 0 )
 
  532                        && ( i != 1 ) && ( i == numMinorSteps / 2 );
 
  535                        mediumTicks += minorValue;
 
  537                        minorTicks += minorValue;
 
  556    QDateTime& minDate, 
const QDateTime& maxDate,
 
  557    double stepSize, 
int maxMinSteps )
 
  564    int minStepSize = 0.0;
 
  566    if ( maxMinSteps > 1 )
 
  570            if ( maxMinSteps >= 30 )
 
  572            else if ( maxMinSteps >= 6 )
 
  574            else if ( maxMinSteps >= 3 )
 
  581            minStepSize = qwtDivideMajorStep(
 
  590    for ( QDateTime dt = minDate;
 
  591        dt <= maxDate; dt = dt.addMonths( stepSize ) )
 
  598        if ( minStepDays > 0 )
 
  600            for ( 
int days = minStepDays;
 
  601                days < 30; days += minStepDays )
 
  605                if ( days == 15 && minStepDays != 15 )
 
  611        else if ( minStepSize > 0.0 )
 
  613            const int numMinorSteps = qRound( stepSize / (
double) minStepSize );
 
  615            for ( 
int i = 1; i < numMinorSteps; i++ )
 
  617                const double minorValue =
 
  620                if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) )
 
  621                    mediumTicks += minorValue;
 
  623                    minorTicks += minorValue;
 
  640    const QDateTime& minDate, 
const QDateTime& maxDate,
 
  641    double stepSize, 
int maxMinSteps )
 
  647    double minStepSize = 0.0;
 
  649    if ( maxMinSteps > 1 )
 
  651        minStepSize = qwtDivideMajorStep(
 
  655    int numMinorSteps = 0;
 
  656    if ( minStepSize > 0.0 )
 
  657        numMinorSteps = qwtFloor( stepSize / minStepSize );
 
  659    bool dateBC = minDate.date().year() < -1;
 
  661    for ( QDateTime dt = minDate; dt <= maxDate;
 
  662        dt = dt.addYears( stepSize ) )
 
  664        if ( dateBC && dt.date().year() > 1 )
 
  667            dt = dt.addYears( -1 );
 
  676        for ( 
int i = 1; i < numMinorSteps; i++ )
 
  680            const double years = qRound( i * minStepSize );
 
  681            if ( years >= std::numeric_limits< int >::max() / 12 )
 
  683                tickDate = dt.addYears( years );
 
  687                tickDate = dt.addMonths( qRound( years * 12 ) );
 
  690            const bool isMedium = ( numMinorSteps > 2 ) &&
 
  691                ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 );
 
  695                mediumTicks += minorValue;
 
  697                minorTicks += minorValue;
 
  717class QwtDateScaleEngine::PrivateData
 
  720    explicit PrivateData( Qt::TimeSpec spec )
 
  723        , week0Type( 
QwtDate::FirstThursday )
 
  728    Qt::TimeSpec timeSpec;
 
  750    m_data = 
new PrivateData( 
timeSpec );
 
 
  776    return m_data->timeSpec;
 
 
  791    m_data->utcOffset = seconds;
 
 
  803    return m_data->utcOffset;
 
 
  826    return m_data->week0Type;
 
 
  843    m_data->maxWeeks = qMax( weeks, 0 );
 
 
  853    return m_data->maxWeeks;
 
 
  866    const QDateTime& minDate, 
const QDateTime& maxDate,
 
  869    const double jdMin = minDate.date().toJulianDay();
 
  870    const double jdMax = maxDate.date().toJulianDay();
 
  872    if ( ( jdMax - jdMin ) / 365 > maxSteps )
 
  875    const int months = qwtRoundedIntervalWidth( minDate, maxDate, 
QwtDate::Month );
 
  876    if ( months > maxSteps * 6 )
 
  879    const int days = qwtRoundedIntervalWidth( minDate, maxDate, 
QwtDate::Day );
 
  880    const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, 
QwtDate::Week );
 
  882    if ( weeks > m_data->maxWeeks )
 
  884        if ( days > 4 * maxSteps * 7 )
 
  888    if ( days > maxSteps * 7 )
 
  891    const int hours = qwtRoundedIntervalWidth( minDate, maxDate, 
QwtDate::Hour );
 
  892    if ( hours > maxSteps * 24 )
 
  895    const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, 
QwtDate::Second );
 
  897    if ( seconds >= maxSteps * 3600 )
 
  900    if ( seconds >= maxSteps * 60 )
 
  903    if ( seconds >= maxSteps )
 
 
  926    double& x1, 
double& x2, 
double& stepSize )
 const 
  942    if ( interval.
width() == 0.0 )
 
  948    if ( from.isValid() && to.isValid() )
 
  950        if ( maxNumSteps < 1 )
 
  956        const double width = qwtIntervalWidth( from, to, intvType );
 
  958        const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType );
 
  961            const QDateTime d1 = 
alignDate( from, stepWidth, intvType, 
false );
 
  962            const QDateTime d2 = 
alignDate( to, stepWidth, intvType, 
true );
 
  968        stepSize = stepWidth * qwtMsecsForType( intvType );
 
  977        stepSize = -stepSize;
 
 
  993    int maxMajorSteps, 
int maxMinorSteps, 
double stepSize )
 const 
  995    if ( maxMajorSteps < 1 )
 
  998    const double min = qwtMinF( x1, x2 );
 
  999    const double max = qwtMaxF( x1, x2 );
 
 1007    stepSize = qAbs( stepSize );
 
 1008    if ( stepSize > 0.0 )
 
 1014        maxMajorSteps = qwtCeil( ( max - min ) / stepSize );
 
 1026            maxMajorSteps, maxMinorSteps, stepSize );
 
 1033        scaleDiv = buildScaleDiv( minDate, maxDate,
 
 1034            maxMajorSteps, maxMinorSteps, intvType );
 
 1039        scaleDiv = scaleDiv.
bounded( min, max );
 
 
 1049    const QDateTime& minDate, 
const QDateTime& maxDate,
 
 1050    int maxMajorSteps, 
int maxMinorSteps,
 
 1054    const double stepSize = qwtDivideScale(
 
 1060    if ( !dt0.isValid() )
 
 1071        scaleDiv = qwtDivideToSeconds( dt0, maxDate,
 
 1078            scaleDiv = qwtDivideToMonths( dt0, maxDate,
 
 1079                stepSize, maxMinorSteps );
 
 1083            scaleDiv = qwtDivideToYears( dt0, maxDate,
 
 1084                stepSize, maxMinorSteps );
 
 1109    const QDateTime& dateTime, 
double stepSize,
 
 1114    QDateTime dt = dateTime;
 
 1116    if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
 
 1118#if QT_VERSION >= 0x050200 
 1119        dt.setOffsetFromUtc( 0 );
 
 1121        dt.setUtcOffset( 0 );
 
 1129            const int ms = qwtAlignValue(
 
 1130                dt.time().msec(), stepSize, up );
 
 1133            dt = dt.addMSecs( ms );
 
 1139            int second = dt.time().second();
 
 1142                if ( dt.time().msec() > 0 )
 
 1146            const int s = qwtAlignValue( second, stepSize, up );
 
 1149            dt = dt.addSecs( s );
 
 1155            int minute = dt.time().minute();
 
 1158                if ( dt.time().msec() > 0 || dt.time().second() > 0 )
 
 1162            const int m = qwtAlignValue( minute, stepSize, up );
 
 1165            dt = dt.addSecs( m * 60 );
 
 1171            int hour = dt.time().hour();
 
 1174                if ( dt.time().msec() > 0 || dt.time().second() > 0
 
 1175                    || dt.time().minute() > 0 )
 
 1180            const int h = qwtAlignValue( hour, stepSize, up );
 
 1183            dt = dt.addSecs( h * 3600 );
 
 1193            int day = dt.date().dayOfYear();
 
 1196                if ( dt.time() > QTime( 0, 0 ) )
 
 1200            const int d = qwtAlignValue( day, stepSize, up );
 
 1203            dt = dt.addDays( d - 1 );
 
 1210                dt.date().year(), m_data->week0Type );
 
 1212            int numWeeks = date.daysTo( dt.date() ) / 7;
 
 1215                if ( dt.time() > QTime( 0, 0 ) ||
 
 1216                    date.daysTo( dt.date() ) % 7 )
 
 1222            const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
 
 1226            dt = dt.addDays( d );
 
 1232            int month = dt.date().month();
 
 1235                if ( dt.date().day() > 1 ||
 
 1236                    dt.time() > QTime( 0, 0 ) )
 
 1242            const int m = qwtAlignValue( month - 1, stepSize, up );
 
 1245            dt = dt.addMonths( m );
 
 1251            int year = dateTime.date().year();
 
 1254                if ( dateTime.date().dayOfYear() > 1 ||
 
 1255                    dt.time() > QTime( 0, 0 ) )
 
 1261            const int y = qwtAlignValue( year, stepSize, up );
 
 1267                dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
 
 1271                dt.setDate( QDate( y, 1, 1 ) );
 
 1278    if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
 
 1280#if QT_VERSION >= 0x050200 
 1281        dt.setOffsetFromUtc( dateTime.offsetFromUtc() );
 
 1283        dt.setUtcOffset( dateTime.utcOffset() );
 
 
 1301    if ( !dt.isValid() )
 
 1303        const QDate date = ( value <= 0.0 )
 
 1306        dt = QDateTime( date, QTime( 0, 0 ), m_data->timeSpec );
 
 1309    if ( m_data->timeSpec == Qt::OffsetFromUTC )
 
 1311        dt = dt.addSecs( m_data->utcOffset );
 
 1312#if QT_VERSION >= 0x050200 
 1313        dt.setOffsetFromUtc( m_data->utcOffset );
 
 1315        dt.setUtcOffset( m_data->utcOffset );
 
 
A collection of methods around date/time values.
static QDateTime floor(const QDateTime &, IntervalType)
static QDateTime toDateTime(double value, Qt::TimeSpec=Qt::UTC)
static QDateTime ceil(const QDateTime &, IntervalType)
static QDate dateOfWeek0(int year, Week0Type)
Date of the first day of the first week for a year.
@ Month
The interval is related to months.
@ Day
The interval is related to days.
@ Millisecond
The interval is related to milliseconds.
@ Minute
The interval is related to minutes.
@ Hour
The interval is related to hours.
@ Second
The interval is related to seconds.
@ Week
The interval is related to weeks.
@ Year
The interval is related to years.
static int utcOffset(const QDateTime &)
static double toDouble(const QDateTime &)
QwtDate::Week0Type week0Type() const
virtual ~QwtDateScaleEngine()
Destructor.
virtual QDateTime alignDate(const QDateTime &, double stepSize, QwtDate::IntervalType, bool up) const
QDateTime toDateTime(double) const
virtual QwtScaleDiv divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize=0.0) const override
Calculate a scale division for a date/time interval.
void setUtcOffset(int seconds)
void setWeek0Type(QwtDate::Week0Type)
virtual QwtDate::IntervalType intervalType(const QDateTime &, const QDateTime &, int maxSteps) const
Qt::TimeSpec timeSpec() const
QwtDateScaleEngine(Qt::TimeSpec=Qt::LocalTime)
Constructor.
virtual void autoScale(int maxNumSteps, double &x1, double &x2, double &stepSize) const override
void setTimeSpec(Qt::TimeSpec)
A class representing an interval.
QwtInterval normalized() const
Normalize the limits of the interval.
double width() const
Return the width of an interval.
QwtInterval extend(double value) const
Extend the interval.
QwtInterval symmetrize(double value) const
A scale engine for linear scales.
virtual QwtScaleDiv divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize=0.0) const override
Calculate a scale division for an interval.
static double divideInterval(double intervalSize, int numSteps, uint base)
A class representing a scale division.
QwtScaleDiv bounded(double lowerBound, double upperBound) const
void setInterval(double lowerBound, double upperBound)
@ MediumTick
Medium ticks.
void setTicks(int tickType, const QList< double > &)
@ Inverted
Turn the scale upside down.
@ Symmetric
Build a scale which is symmetric to the reference() value.
@ IncludeReference
Build a scale which includes the reference() value.
double upperMargin() const
bool testAttribute(Attribute) const
QwtInterval buildInterval(double value) const
Build an interval around a value.
double lowerMargin() const