Skip to content
qwt_scale_engine.cpp 27.5 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
pixhawk's avatar
pixhawk committed
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

Bryant's avatar
Bryant committed
#include "qwt_scale_engine.h"
pixhawk's avatar
pixhawk committed
#include "qwt_math.h"
#include "qwt_scale_map.h"
Bryant's avatar
Bryant committed
#include <qalgorithms.h>
#include <qmath.h>
#include <float.h>
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
#if QT_VERSION < 0x040601
#define qFabs(x) ::fabs(x)
#define qExp(x) ::exp(x)
#endif
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
static inline double qwtLog( double base, double value )
{
    return log( value ) / log( base );
}

static inline QwtInterval qwtLogInterval( double base, const QwtInterval &interval )
{
    return QwtInterval( qwtLog( base, interval.minValue() ),
            qwtLog( base, interval.maxValue() ) );
}

static inline QwtInterval qwtPowInterval( double base, const QwtInterval &interval ) 
{
    return QwtInterval( qPow( base, interval.minValue() ),
            qPow( base, interval.maxValue() ) );
}
pixhawk's avatar
pixhawk committed


Bryant's avatar
Bryant committed
#if 1
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
// this version often doesn't find the best ticks: f.e for 15: 5, 10
static double qwtStepSize( double intervalSize, int maxSteps, uint base )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const double minStep = 
        QwtScaleArithmetic::divideInterval( intervalSize, maxSteps, base );

    if ( minStep != 0.0 )
    {
        // # ticks per interval
        const int numTicks = qCeil( qAbs( intervalSize / minStep ) ) - 1;

        // Do the minor steps fit into the interval?
        if ( qwtFuzzyCompare( ( numTicks +  1 ) * qAbs( minStep ),
            qAbs( intervalSize ), intervalSize ) > 0 )
        {
            // The minor steps doesn't fit into the interval
            return 0.5 * intervalSize;
        }
    }

    return minStep;
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
#else
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
static double qwtStepSize( double intervalSize, int maxSteps, uint base )
{
    if ( maxSteps <= 0 )
        return 0.0;

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

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

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

                if ( n == 3 && ( base % 2 ) == 0 )
                {
                    if ( qFuzzyCompare( stepSize, 2 * fraction ) )
                        return stepSize;
                }
            }
        }
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    return intervalSize * 0.5;
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
#endif

static const double _eps = 1.0e-6;

pixhawk's avatar
pixhawk committed
/*!
  Ceil a value, relative to an interval

Bryant's avatar
Bryant committed
  \param value Value to be ceiled
pixhawk's avatar
pixhawk committed
  \param intervalSize Interval size
Bryant's avatar
Bryant committed
  \return Rounded value

  \sa floorEps()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
double QwtScaleArithmetic::ceilEps( double value,
    double intervalSize )
pixhawk's avatar
pixhawk committed
{
    const double eps = _eps * intervalSize;

Bryant's avatar
Bryant committed
    value = ( value - eps ) / intervalSize;
    return ::ceil( value ) * intervalSize;
pixhawk's avatar
pixhawk committed
}

/*!
  Floor a value, relative to an interval

Bryant's avatar
Bryant committed
  \param value Value to be floored
pixhawk's avatar
pixhawk committed
  \param intervalSize Interval size
Bryant's avatar
Bryant committed
  \return Rounded value
  \sa floorEps()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
double QwtScaleArithmetic::floorEps( double value, double intervalSize )
pixhawk's avatar
pixhawk committed
{
    const double eps = _eps * intervalSize;

Bryant's avatar
Bryant committed
    value = ( value + eps ) / intervalSize;
    return ::floor( value ) * intervalSize;
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
pixhawk's avatar
pixhawk committed
  \brief Divide an interval into steps

  \f$stepSize = (intervalSize - intervalSize * 10e^{-6}) / numSteps\f$

  \param intervalSize Interval size
  \param numSteps Number of steps
  \return Step size
*/
Bryant's avatar
Bryant committed
double QwtScaleArithmetic::divideEps( double intervalSize, double numSteps )
pixhawk's avatar
pixhawk committed
{
    if ( numSteps == 0.0 || intervalSize == 0.0 )
        return 0.0;

Bryant's avatar
Bryant committed
    return ( intervalSize - ( _eps * intervalSize ) ) / numSteps;
pixhawk's avatar
pixhawk committed

/*!
Bryant's avatar
Bryant committed
  Calculate a step size for a given interval
Bryant's avatar
Bryant committed
  \param intervalSize Interval size
  \param numSteps Number of steps
  \param base Base for the division ( usually 10 )

  \return Calculated step size
 */
double QwtScaleArithmetic::divideInterval( 
    double intervalSize, int numSteps, uint base ) 
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( numSteps <= 0 )
pixhawk's avatar
pixhawk committed
        return 0.0;

Bryant's avatar
Bryant committed
    const double v = QwtScaleArithmetic::divideEps( intervalSize, numSteps );
    if ( v == 0.0 )
        return 0.0;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const double lx = qwtLog( base, qFabs( v ) );
    const double p = ::floor( lx );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const double fraction = qPow( base, lx - p );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    uint n = base;
    while ( ( n > 1 ) && ( fraction <= n / 2 ) )
        n /= 2;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    double stepSize = n * qPow( base, p );
    if ( v < 0 )
        stepSize = -stepSize;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    return stepSize;
pixhawk's avatar
pixhawk committed
}

class QwtScaleEngine::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
        attributes( QwtScaleEngine::NoAttribute ),
        lowerMargin( 0.0 ),
        upperMargin( 0.0 ),
        referenceValue( 0.0 ),
        base( 10 ),
        transform( NULL )
    {
    }

    ~PrivateData()
    {
        delete transform;
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    QwtScaleEngine::Attributes attributes;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    double lowerMargin;
    double upperMargin;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    double referenceValue;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    uint base;

    QwtTransform* transform;
pixhawk's avatar
pixhawk committed
};

Bryant's avatar
Bryant committed
/*!
  Constructor

  \param base Base of the scale engine
  \sa setBase()
 */
QwtScaleEngine::QwtScaleEngine( uint base )
pixhawk's avatar
pixhawk committed
{
    d_data = new PrivateData;
Bryant's avatar
Bryant committed
    setBase( base );
pixhawk's avatar
pixhawk committed
}


Bryant's avatar
Bryant committed
//! Destructor
pixhawk's avatar
pixhawk committed
QwtScaleEngine::~QwtScaleEngine ()
{
    delete d_data;
}

Bryant's avatar
Bryant committed
/*!
   Assign a transformation

   \param transform Transformation

   The transformation object is used as factory for clones
   that are returned by transformation()

   The scale engine takes ownership of the transformation.

   \sa QwtTransform::copy(), transformation()

 */
void QwtScaleEngine::setTransformation( QwtTransform *transform )
{
    if ( transform != d_data->transform )
    {
        delete d_data->transform;
        d_data->transform = transform;
    }
}

/*!
   Create and return a clone of the transformation 
   of the engine. When the engine has no special transformation
   NULL is returned, indicating no transformation.

   \return A clone of the transfomation
   \sa setTransformation()
 */
QwtTransform *QwtScaleEngine::transformation() const
{
    QwtTransform *transform = NULL;
    if ( d_data->transform )
        transform = d_data->transform->copy();

    return transform;
}

pixhawk's avatar
pixhawk committed
/*!
    \return the margin at the lower end of the scale
    The default margin is 0.

Bryant's avatar
Bryant committed
    \sa setMargins()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
double QwtScaleEngine::lowerMargin() const
Bryant's avatar
Bryant committed
    return d_data->lowerMargin;
pixhawk's avatar
pixhawk committed
}

/*!
    \return the margin at the upper end of the scale
    The default margin is 0.

Bryant's avatar
Bryant committed
    \sa setMargins()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
double QwtScaleEngine::upperMargin() const
Bryant's avatar
Bryant committed
    return d_data->upperMargin;
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Specify margins at the scale's endpoints
Bryant's avatar
Bryant committed
  \param lower minimum distance between the scale's lower boundary and the
pixhawk's avatar
pixhawk committed
             smallest enclosed value
Bryant's avatar
Bryant committed
  \param upper minimum distance between the scale's upper boundary and the
pixhawk's avatar
pixhawk committed
             greatest enclosed value

  Margins can be used to leave a minimum amount of space between
  the enclosed intervals and the boundaries of the scale.

  \warning
Bryant's avatar
Bryant committed
  \li QwtLogScaleEngine measures the margins in decades.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa upperMargin(), lowerMargin()
pixhawk's avatar
pixhawk committed
*/

Bryant's avatar
Bryant committed
void QwtScaleEngine::setMargins( double lower, double upper )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    d_data->lowerMargin = qMax( lower, 0.0 );
    d_data->upperMargin = qMax( upper, 0.0 );
pixhawk's avatar
pixhawk committed
}

/*!
  Calculate a step size for an interval size

  \param intervalSize Interval size
  \param numSteps Number of steps
pixhawk's avatar
pixhawk committed
  \return Step size
*/
double QwtScaleEngine::divideInterval(
Bryant's avatar
Bryant committed
    double intervalSize, int numSteps ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return QwtScaleArithmetic::divideInterval( 
        intervalSize, numSteps, d_data->base );
pixhawk's avatar
pixhawk committed
}

/*!
  Check if an interval "contains" a value

  \param interval Interval
  \param value Value

Bryant's avatar
Bryant committed
  \return True, when the value is inside the interval
pixhawk's avatar
pixhawk committed
*/
bool QwtScaleEngine::contains(
Bryant's avatar
Bryant committed
    const QwtInterval &interval, double value ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( !interval.isValid() )
pixhawk's avatar
pixhawk committed
        return false;
Bryant's avatar
Bryant committed
    if ( qwtFuzzyCompare( value, interval.minValue(), interval.width() ) < 0 )
pixhawk's avatar
pixhawk committed
        return false;

Bryant's avatar
Bryant committed
    if ( qwtFuzzyCompare( value, interval.maxValue(), interval.width() ) > 0 )
pixhawk's avatar
pixhawk committed
        return false;

    return true;
}

/*!
  Remove ticks from a list, that are not inside an interval

  \param ticks Tick list
  \param interval Interval

  \return Stripped tick list
*/
Bryant's avatar
Bryant committed
QList<double> QwtScaleEngine::strip( const QList<double>& ticks,
    const QwtInterval &interval ) const
pixhawk's avatar
pixhawk committed
{
    if ( !interval.isValid() || ticks.count() == 0 )
Bryant's avatar
Bryant committed
        return QList<double>();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( contains( interval, ticks.first() )
        && contains( interval, ticks.last() ) )
    {
pixhawk's avatar
pixhawk committed
        return ticks;
    }

Bryant's avatar
Bryant committed
    QList<double> strippedTicks;
    for ( int i = 0; i < ticks.count(); i++ )
    {
        if ( contains( interval, ticks[i] ) )
pixhawk's avatar
pixhawk committed
            strippedTicks += ticks[i];
    }
    return strippedTicks;
}

/*!
Bryant's avatar
Bryant committed
  \brief Build an interval around a value
pixhawk's avatar
pixhawk committed

  In case of v == 0.0 the interval is [-0.5, 0.5],
  otherwide it is [0.5 * v, 1.5 * v]
Bryant's avatar
Bryant committed

  \param value Initial value
  \return Calculated interval
pixhawk's avatar
pixhawk committed
*/

Bryant's avatar
Bryant committed
QwtInterval QwtScaleEngine::buildInterval( double value ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const double delta = ( value == 0.0 ) ? 0.5 : qAbs( 0.5 * value );

    if ( DBL_MAX - delta < value )
        return QwtInterval( DBL_MAX - delta, DBL_MAX );

    if ( -DBL_MAX + delta > value )
        return QwtInterval( -DBL_MAX, -DBL_MAX + delta );

    return QwtInterval( value - delta, value + delta );
pixhawk's avatar
pixhawk committed
}

/*!
  Change a scale attribute

  \param attribute Attribute to change
  \param on On/Off

Bryant's avatar
Bryant committed
  \sa Attribute, testAttribute()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtScaleEngine::setAttribute( Attribute attribute, bool on )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( on )
        d_data->attributes |= attribute;
pixhawk's avatar
pixhawk committed
    else
Bryant's avatar
Bryant committed
        d_data->attributes &= ~attribute;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return True, if attribute is enabled.
pixhawk's avatar
pixhawk committed

  \param attribute Attribute to be tested
Bryant's avatar
Bryant committed
  \sa Attribute, setAttribute()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
bool QwtScaleEngine::testAttribute( Attribute attribute ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return ( d_data->attributes & attribute );
pixhawk's avatar
pixhawk committed
}

/*!
  Change the scale attribute

  \param attributes Set scale attributes
Bryant's avatar
Bryant committed
  \sa Attribute, attributes()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtScaleEngine::setAttributes( Attributes attributes )
pixhawk's avatar
pixhawk committed
{
    d_data->attributes = attributes;
}

/*!
Bryant's avatar
Bryant committed
  \return Scale attributes
  \sa Attribute, setAttributes(), testAttribute()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtScaleEngine::Attributes QwtScaleEngine::attributes() const
pixhawk's avatar
pixhawk committed
{
    return d_data->attributes;
}

/*!
  \brief Specify a reference point
  \param r new reference value

Bryant's avatar
Bryant committed
  The reference point is needed if options IncludeReference or
pixhawk's avatar
pixhawk committed
  Symmetric are active. Its default value is 0.0.
Bryant's avatar
Bryant committed

  \sa Attribute
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtScaleEngine::setReference( double r )
pixhawk's avatar
pixhawk committed
{
    d_data->referenceValue = r;
}

/*!
Bryant's avatar
Bryant committed
  \return the reference value
  \sa setReference(), setAttribute()
pixhawk's avatar
pixhawk committed
*/
double QwtScaleEngine::reference() const
{
    return d_data->referenceValue;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Set the base of the scale engine

  While a base of 10 is what 99.9% of all applications need
  certain scales might need a different base: f.e 2

  The default setting is 10

  \param base Base of the engine

  \sa base()
 */
void QwtScaleEngine::setBase( uint base )
{ 
    d_data->base = qMax( base, 2U );
}

/*!
  \return base Base of the scale engine
  \sa setBase()
 */
uint QwtScaleEngine::base() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->base;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Constructor
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param base Base of the scale engine
  \sa setBase()
 */
QwtLinearScaleEngine::QwtLinearScaleEngine( uint base ):
    QwtScaleEngine( base )
{
}

//! Destructor
QwtLinearScaleEngine::~QwtLinearScaleEngine()
{
}

/*!
  Align and divide an interval
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param maxNumSteps Max. number of steps
  \param x1 First limit of the interval (In/Out)
  \param x2 Second limit of the interval (In/Out)
  \param stepSize Step size (Out)

  \sa setAttribute()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtLinearScaleEngine::autoScale( int maxNumSteps,
    double &x1, double &x2, double &stepSize ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtInterval interval( x1, x2 );
pixhawk's avatar
pixhawk committed
    interval = interval.normalized();

Bryant's avatar
Bryant committed
    interval.setMinValue( interval.minValue() - lowerMargin() );
    interval.setMaxValue( interval.maxValue() + upperMargin() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( testAttribute( QwtScaleEngine::Symmetric ) )
        interval = interval.symmetrize( reference() );
Bryant's avatar
Bryant committed
    if ( testAttribute( QwtScaleEngine::IncludeReference ) )
        interval = interval.extend( reference() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( interval.width() == 0.0 )
        interval = buildInterval( interval.minValue() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    stepSize = QwtScaleArithmetic::divideInterval( 
        interval.width(), qMax( maxNumSteps, 1 ), base() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( !testAttribute( QwtScaleEngine::Floating ) )
        interval = align( interval, stepSize );
pixhawk's avatar
pixhawk committed

    x1 = interval.minValue();
    x2 = interval.maxValue();

Bryant's avatar
Bryant committed
    if ( testAttribute( QwtScaleEngine::Inverted ) )
    {
        qSwap( x1, x2 );
pixhawk's avatar
pixhawk committed
        stepSize = -stepSize;
    }
}

/*!
Bryant's avatar
Bryant committed
   \brief Calculate a scale division for an interval
pixhawk's avatar
pixhawk committed

   \param x1 First interval limit
   \param x2 Second interval limit
Bryant's avatar
Bryant committed
   \param maxMajorSteps Maximum for the number of major steps
   \param maxMinorSteps Maximum number of minor steps
   \param stepSize Step size. If stepSize == 0, the engine
pixhawk's avatar
pixhawk committed
                   calculates one.

Bryant's avatar
Bryant committed
   \return Calculated scale division
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtScaleDiv QwtLinearScaleEngine::divideScale( double x1, double x2,
    int maxMajorSteps, int maxMinorSteps, double stepSize ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtInterval interval = QwtInterval( x1, x2 ).normalized();
    if ( interval.width() <= 0 )
pixhawk's avatar
pixhawk committed
        return QwtScaleDiv();

Bryant's avatar
Bryant committed
    stepSize = qAbs( stepSize );
    if ( stepSize == 0.0 )
    {
        if ( maxMajorSteps < 1 )
            maxMajorSteps = 1;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        stepSize = QwtScaleArithmetic::divideInterval( 
            interval.width(), maxMajorSteps, base() );
pixhawk's avatar
pixhawk committed
    }

    QwtScaleDiv scaleDiv;

Bryant's avatar
Bryant committed
    if ( stepSize != 0.0 )
    {
        QList<double> ticks[QwtScaleDiv::NTickTypes];
        buildTicks( interval, stepSize, maxMinorSteps, ticks );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        scaleDiv = QwtScaleDiv( interval, ticks );
pixhawk's avatar
pixhawk committed
    }

    if ( x1 > x2 )
        scaleDiv.invert();

    return scaleDiv;
}

Bryant's avatar
Bryant committed
/*!
   \brief Calculate ticks for an interval

   \param interval Interval
   \param stepSize Step size
   \param maxMinorSteps Maximum number of minor steps
   \param ticks Arrays to be filled with the calculated ticks

   \sa buildMajorTicks(), buildMinorTicks
*/
pixhawk's avatar
pixhawk committed
void QwtLinearScaleEngine::buildTicks(
Bryant's avatar
Bryant committed
    const QwtInterval& interval, double stepSize, int maxMinorSteps,
    QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const QwtInterval boundingInterval = align( interval, stepSize );

    ticks[QwtScaleDiv::MajorTick] =
Bryant's avatar
Bryant committed
        buildMajorTicks( boundingInterval, stepSize );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( maxMinorSteps > 0 )
    {
        buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
            ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed
    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
    {
        ticks[i] = strip( ticks[i], interval );
pixhawk's avatar
pixhawk committed

        // ticks very close to 0.0 are
pixhawk's avatar
pixhawk committed
        // explicitely set to 0.0

Bryant's avatar
Bryant committed
        for ( int j = 0; j < ticks[i].count(); j++ )
        {
            if ( qwtFuzzyCompare( ticks[i][j], 0.0, stepSize ) == 0 )
pixhawk's avatar
pixhawk committed
                ticks[i][j] = 0.0;
        }
    }
}

Bryant's avatar
Bryant committed
/*!
   \brief Calculate major ticks for an interval

   \param interval Interval
   \param stepSize Step size

   \return Calculated ticks
*/
QList<double> QwtLinearScaleEngine::buildMajorTicks(
    const QwtInterval &interval, double stepSize ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    int numTicks = qRound( interval.width() / stepSize ) + 1;
pixhawk's avatar
pixhawk committed
    if ( numTicks > 10000 )
        numTicks = 10000;

Bryant's avatar
Bryant committed
    QList<double> ticks;
pixhawk's avatar
pixhawk committed

    ticks += interval.minValue();
Bryant's avatar
Bryant committed
    for ( int i = 1; i < numTicks - 1; i++ )
pixhawk's avatar
pixhawk committed
        ticks += interval.minValue() + i * stepSize;
    ticks += interval.maxValue();

    return ticks;
}

Bryant's avatar
Bryant committed
/*!
   \brief Calculate minor/medium ticks for major ticks

   \param majorTicks Major ticks
   \param maxMinorSteps Maximum number of minor steps
   \param stepSize Step size
   \param minorTicks Array to be filled with the calculated minor ticks
   \param mediumTicks Array to be filled with the calculated medium ticks

*/
pixhawk's avatar
pixhawk committed
void QwtLinearScaleEngine::buildMinorTicks(
Bryant's avatar
Bryant committed
    const QList<double>& majorTicks,
    int maxMinorSteps, double stepSize,
    QList<double> &minorTicks,
    QList<double> &mediumTicks ) const
Bryant's avatar
Bryant committed
    double minStep = qwtStepSize( stepSize, maxMinorSteps, base() );
    if ( minStep == 0.0 )
pixhawk's avatar
pixhawk committed
    // # ticks per interval
Bryant's avatar
Bryant committed
    const int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1;
pixhawk's avatar
pixhawk committed

    int medIndex = -1;
    if ( numTicks % 2 )
        medIndex = numTicks / 2;

    // calculate minor ticks

Bryant's avatar
Bryant committed
    for ( int i = 0; i < majorTicks.count(); i++ )
    {
pixhawk's avatar
pixhawk committed
        double val = majorTicks[i];
Bryant's avatar
Bryant committed
        for ( int k = 0; k < numTicks; k++ )
        {
pixhawk's avatar
pixhawk committed
            val += minStep;

            double alignedValue = val;
Bryant's avatar
Bryant committed
            if ( qwtFuzzyCompare( val, 0.0, stepSize ) == 0 )
pixhawk's avatar
pixhawk committed
                alignedValue = 0.0;

            if ( k == medIndex )
                mediumTicks += alignedValue;
            else
                minorTicks += alignedValue;
        }
    }
}

/*!
  \brief Align an interval to a step size

  The limits of an interval are aligned that both are integer
  multiples of the step size.

  \param interval Interval
  \param stepSize Step size

  \return Aligned interval
*/
Bryant's avatar
Bryant committed
QwtInterval QwtLinearScaleEngine::align(
    const QwtInterval &interval, double stepSize ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    double x1 = interval.minValue();
    double x2 = interval.maxValue();

    if ( -DBL_MAX + stepSize <= x1 )
    {
        const double x = QwtScaleArithmetic::floorEps( x1, stepSize );
        if ( qwtFuzzyCompare( x1, x, stepSize ) != 0 )
            x1 = x;
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( DBL_MAX - stepSize >= x2 )
    {
        const double x = QwtScaleArithmetic::ceilEps( x2, stepSize );
        if ( qwtFuzzyCompare( x2, x, stepSize ) != 0 )
            x2 = x;
    }

    return QwtInterval( x1, x2 );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Constructor

  \param base Base of the scale engine
  \sa setBase()
 */
QwtLogScaleEngine::QwtLogScaleEngine( uint base ):
    QwtScaleEngine( base )
{
    setTransformation( new QwtLogTransform() );
}

//! Destructor
QwtLogScaleEngine::~QwtLogScaleEngine()
pixhawk's avatar
pixhawk committed
{
}

/*!
    Align and divide an interval

   \param maxNumSteps Max. number of steps
   \param x1 First limit of the interval (In/Out)
   \param x2 Second limit of the interval (In/Out)
   \param stepSize Step size (Out)

Bryant's avatar
Bryant committed
   \sa QwtScaleEngine::setAttribute()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtLogScaleEngine::autoScale( int maxNumSteps,
    double &x1, double &x2, double &stepSize ) const
pixhawk's avatar
pixhawk committed
{
    if ( x1 > x2 )
Bryant's avatar
Bryant committed
        qSwap( x1, x2 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const double logBase = base();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtInterval interval( x1 / qPow( logBase, lowerMargin() ),
        x2 * qPow( logBase, upperMargin() ) );

    if ( interval.maxValue() / interval.minValue() < logBase )
    {
        // scale width is less than one step -> try to build a linear scale

        QwtLinearScaleEngine linearScaler;
        linearScaler.setAttributes( attributes() );
        linearScaler.setReference( reference() );
        linearScaler.setMargins( lowerMargin(), upperMargin() );

        linearScaler.autoScale( maxNumSteps, x1, x2, stepSize );

        QwtInterval linearInterval = QwtInterval( x1, x2 ).normalized();
        linearInterval = linearInterval.limited( LOG_MIN, LOG_MAX );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( linearInterval.maxValue() / linearInterval.minValue() < logBase )
        {
            // the aligned scale is still less than one step
            if ( stepSize < 0.0 )
                stepSize = -qwtLog( logBase, qAbs( stepSize ) );
            else
                stepSize = qwtLog( logBase, stepSize );

            return;
        }
    }

    double logRef = 1.0;
    if ( reference() > LOG_MIN / 2 )
        logRef = qMin( reference(), LOG_MAX / 2 );

    if ( testAttribute( QwtScaleEngine::Symmetric ) )
    {
        const double delta = qMax( interval.maxValue() / logRef,
            logRef / interval.minValue() );
        interval.setInterval( logRef / delta, logRef * delta );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    if ( testAttribute( QwtScaleEngine::IncludeReference ) )
        interval = interval.extend( logRef );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    interval = interval.limited( LOG_MIN, LOG_MAX );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( interval.width() == 0.0 )
        interval = buildInterval( interval.minValue() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    stepSize = divideInterval( qwtLogInterval( logBase, interval ).width(), 
        qMax( maxNumSteps, 1 ) );
pixhawk's avatar
pixhawk committed
    if ( stepSize < 1.0 )
        stepSize = 1.0;

Bryant's avatar
Bryant committed
    if ( !testAttribute( QwtScaleEngine::Floating ) )
        interval = align( interval, stepSize );
pixhawk's avatar
pixhawk committed

    x1 = interval.minValue();
    x2 = interval.maxValue();

Bryant's avatar
Bryant committed
    if ( testAttribute( QwtScaleEngine::Inverted ) )
    {
        qSwap( x1, x2 );
pixhawk's avatar
pixhawk committed
        stepSize = -stepSize;
    }
}

/*!
Bryant's avatar
Bryant committed
   \brief Calculate a scale division for an interval
pixhawk's avatar
pixhawk committed

   \param x1 First interval limit
   \param x2 Second interval limit
Bryant's avatar
Bryant committed
   \param maxMajorSteps Maximum for the number of major steps
   \param maxMinorSteps Maximum number of minor steps
   \param stepSize Step size. If stepSize == 0, the engine
pixhawk's avatar
pixhawk committed
                   calculates one.

Bryant's avatar
Bryant committed
   \return Calculated scale division
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtScaleDiv QwtLogScaleEngine::divideScale( double x1, double x2,
    int maxMajorSteps, int maxMinorSteps, double stepSize ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtInterval interval = QwtInterval( x1, x2 ).normalized();
    interval = interval.limited( LOG_MIN, LOG_MAX );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( interval.width() <= 0 )
pixhawk's avatar
pixhawk committed
        return QwtScaleDiv();

Bryant's avatar
Bryant committed
    const double logBase = base();

    if ( interval.maxValue() / interval.minValue() < logBase )
    {
pixhawk's avatar
pixhawk committed
        // scale width is less than one decade -> build linear scale
pixhawk's avatar
pixhawk committed
        QwtLinearScaleEngine linearScaler;
Bryant's avatar
Bryant committed
        linearScaler.setAttributes( attributes() );
        linearScaler.setReference( reference() );
        linearScaler.setMargins( lowerMargin(), upperMargin() );

        if ( stepSize != 0.0 )
        {
            if ( stepSize < 0.0 )
                stepSize = -qPow( logBase, -stepSize );
            else
                stepSize = qPow( logBase, stepSize );
        }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        return linearScaler.divideScale( x1, x2,
            maxMajorSteps, maxMinorSteps, stepSize );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    stepSize = qAbs( stepSize );
    if ( stepSize == 0.0 )
    {
        if ( maxMajorSteps < 1 )
            maxMajorSteps = 1;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        stepSize = divideInterval( 
            qwtLogInterval( logBase, interval ).width(), maxMajorSteps );
pixhawk's avatar
pixhawk committed
        if ( stepSize < 1.0 )
            stepSize = 1.0; // major step must be >= 1 decade
    }

    QwtScaleDiv scaleDiv;
Bryant's avatar
Bryant committed
    if ( stepSize != 0.0 )
    {
        QList<double> ticks[QwtScaleDiv::NTickTypes];
        buildTicks( interval, stepSize, maxMinorSteps, ticks );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        scaleDiv = QwtScaleDiv( interval, ticks );
pixhawk's avatar
pixhawk committed
    }

    if ( x1 > x2 )
        scaleDiv.invert();

    return scaleDiv;
}

Bryant's avatar
Bryant committed
/*!
   \brief Calculate ticks for an interval

   \param interval Interval
   \param maxMinorSteps Maximum number of minor steps
   \param stepSize Step size
   \param ticks Arrays to be filled with the calculated ticks

   \sa buildMajorTicks(), buildMinorTicks
*/
void QwtLogScaleEngine::buildTicks(
    const QwtInterval& interval, double stepSize, int maxMinorSteps,
    QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const QwtInterval boundingInterval = align( interval, stepSize );

    ticks[QwtScaleDiv::MajorTick] =
Bryant's avatar
Bryant committed
        buildMajorTicks( boundingInterval, stepSize );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( maxMinorSteps > 0 )
    {
        buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
            ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
pixhawk's avatar
pixhawk committed
    }
pixhawk's avatar
pixhawk committed
    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
Bryant's avatar
Bryant committed
        ticks[i] = strip( ticks[i], interval );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
   \brief Calculate major ticks for an interval

   \param interval Interval
   \param stepSize Step size

   \return Calculated ticks
*/
QList<double> QwtLogScaleEngine::buildMajorTicks(
    const QwtInterval &interval, double stepSize ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    double width = qwtLogInterval( base(), interval ).width();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    int numTicks = qRound( width / stepSize ) + 1;
pixhawk's avatar
pixhawk committed
    if ( numTicks > 10000 )
        numTicks = 10000;

Bryant's avatar
Bryant committed
    const double lxmin = ::log( interval.minValue() );
    const double lxmax = ::log( interval.maxValue() );
    const double lstep = ( lxmax - lxmin ) / double( numTicks - 1 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QList<double> ticks;
pixhawk's avatar
pixhawk committed

    ticks += interval.minValue();

Bryant's avatar
Bryant committed
    for ( int i = 1; i < numTicks - 1; i++ )
        ticks += qExp( lxmin + double( i ) * lstep );
pixhawk's avatar
pixhawk committed

    ticks += interval.maxValue();

    return ticks;
}

Bryant's avatar
Bryant committed
/*!
   \brief Calculate minor/medium ticks for major ticks
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
   \param majorTicks Major ticks
   \param maxMinorSteps Maximum number of minor steps
   \param stepSize Step size
   \param minorTicks Array to be filled with the calculated minor ticks