Skip to content
qwt_date_scale_engine.cpp 34.7 KiB
Newer Older
Bryant's avatar
Bryant committed
#include "qwt_date_scale_engine.h"
#include "qwt_math.h"
#include "qwt_transform.h"
#include <qdatetime.h>
#include <limits.h>

static inline double qwtMsecsForType( QwtDate::IntervalType type )
{
    static const double msecs[] =
    {
        1.0,
        1000.0,
        60.0 * 1000.0,
        3600.0 * 1000.0,
        24.0 * 3600.0 * 1000.0,
        7.0 * 24.0 * 3600.0 * 1000.0,
        30.0 * 24.0 * 3600.0 * 1000.0,
        365.0 * 24.0 * 3600.0 * 1000.0,
    };

    if ( type < 0 || type >= static_cast<int>( sizeof( msecs ) / sizeof( msecs[0] ) ) )
        return 1.0;

    return msecs[ type ];
}

static inline int qwtAlignValue(
    double value, double stepSize, bool up )
{
    double d = value / stepSize;
    d = up ? ::ceil( d ) : ::floor( d );

    return static_cast<int>( d * stepSize );
}

static double qwtIntervalWidth( const QDateTime &minDate,
    const QDateTime &maxDate, QwtDate::IntervalType intervalType ) 
{
    switch( intervalType )
    {
        case QwtDate::Millisecond:
        {
            const double secsTo = minDate.secsTo( maxDate );
            const double msecs = maxDate.time().msec() -
                minDate.time().msec();

            return secsTo * 1000 + msecs;
        }
        case QwtDate::Second:
        {
            return minDate.secsTo( maxDate );
        }
        case QwtDate::Minute:
        {
            const double secsTo = minDate.secsTo( maxDate );
            return ::floor( secsTo / 60 );
        }
        case QwtDate::Hour:
        {
            const double secsTo = minDate.secsTo( maxDate );
            return ::floor( secsTo / 3600 );
        }
        case QwtDate::Day:
        {
            return minDate.daysTo( maxDate );
        }
        case QwtDate::Week:
        {
            return ::floor( minDate.daysTo( maxDate ) / 7.0 );
        }
        case QwtDate::Month:
        {
            const double years = 
                double( maxDate.date().year() ) - minDate.date().year();

            int months = maxDate.date().month() - minDate.date().month();
            if ( maxDate.date().day() < minDate.date().day() )
                months--;

            return years * 12 + months;
        }
        case QwtDate::Year:
        {
            double years = 
                double( maxDate.date().year() ) - minDate.date().year();

            if ( maxDate.date().month() < minDate.date().month() )
                years -= 1.0;

            return years;
        }
    }

    return 0.0;
}

static double qwtRoundedIntervalWidth( 
    const QDateTime &minDate, const QDateTime &maxDate, 
    QwtDate::IntervalType intervalType ) 
{
    const QDateTime minD = QwtDate::floor( minDate, intervalType );
    const QDateTime maxD = QwtDate::ceil( maxDate, intervalType );

    return qwtIntervalWidth( minD, maxD, intervalType );
}

static inline int qwtStepCount( int intervalSize, int maxSteps,
    const int limits[], size_t numLimits )
{
    for ( uint i = 0; i < numLimits; i++ )
    {
        const int numSteps = intervalSize / limits[ i ];

        if ( numSteps > 1 && numSteps <= maxSteps &&
            numSteps * limits[ i ] == intervalSize )
        {
            return numSteps;
        }
    }

    return 0;
}

static int qwtStepSize( int intervalSize, int maxSteps, uint base ) 
{
    if ( maxSteps <= 0 )
        return 0;

    if ( maxSteps > 2 )
    {
        for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
        {
            const double stepSize = double( intervalSize ) / numSteps;

            const double p = ::floor( ::log( stepSize ) / ::log( double( base ) ) );
            const double fraction = qPow( base, p );

            for ( uint n = base; n >= 1; n /= 2 )
            {
                if ( qFuzzyCompare( stepSize, n * fraction ) )
                    return qRound( stepSize );

                if ( n == 3 && ( base % 2 ) == 0 )
                {
                    if ( qFuzzyCompare( stepSize, 2 * fraction ) )
                        return qRound( stepSize );
                }
            }
        }
    }

    return 0;
}

static int qwtDivideInterval( double intervalSize, int numSteps, 
    const int limits[], size_t numLimits )
{
    const int v = qCeil( intervalSize / double( numSteps ) );

    for ( uint i = 0; i < numLimits - 1; i++ )
    {
        if ( v <= limits[i] )
            return limits[i];
    }

    return limits[ numLimits - 1 ];
}

static double qwtDivideScale( double intervalSize, int numSteps,
    QwtDate::IntervalType intervalType )
{
    if ( intervalType != QwtDate::Day )
    {
        if ( ( intervalSize > numSteps ) && 
            ( intervalSize <= 2 * numSteps ) )
        {
            return 2.0;
        }
    }

    double stepSize;

    switch( intervalType )
    {
        case QwtDate::Second:
        case QwtDate::Minute:
        {
            static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
    
            stepSize = qwtDivideInterval( intervalSize, numSteps,
                limits, sizeof( limits ) / sizeof( int ) );

            break;
        }
        case QwtDate::Hour:
        {
            static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
    
            stepSize = qwtDivideInterval( intervalSize, numSteps,
                limits, sizeof( limits ) / sizeof( int ) );
Loading
Loading full blame...