地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1119 lines
28 KiB

15 years ago
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* Qwt Widget Library
* Copyright (C) 1997 Josef Wilgen
* Copyright (C) 2002 Uwe Rathmann
*
15 years ago
* This library is free software; you can redistribute it and/or
* modify it under the terms of the Qwt License, Version 1.0
*****************************************************************************/
#include "qwt_scale_engine.h"
15 years ago
#include "qwt_math.h"
#include "qwt_scale_map.h"
#include <qalgorithms.h>
#include <qmath.h>
#include <float.h>
15 years ago
#if QT_VERSION < 0x040601
#define qFabs(x) ::fabs(x)
#define qExp(x) ::exp(x)
#endif
15 years ago
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() ) );
}
15 years ago
#if 1
15 years ago
// this version often doesn't find the best ticks: f.e for 15: 5, 10
static double qwtStepSize( double intervalSize, int maxSteps, uint base )
15 years ago
{
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;
}
15 years ago
#else
15 years ago
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;
}
}
}
}
15 years ago
return intervalSize * 0.5;
15 years ago
}
#endif
static const double _eps = 1.0e-6;
15 years ago
/*!
Ceil a value, relative to an interval
\param value Value to be ceiled
15 years ago
\param intervalSize Interval size
\return Rounded value
\sa floorEps()
15 years ago
*/
double QwtScaleArithmetic::ceilEps( double value,
double intervalSize )
15 years ago
{
const double eps = _eps * intervalSize;
value = ( value - eps ) / intervalSize;
return ::ceil( value ) * intervalSize;
15 years ago
}
/*!
Floor a value, relative to an interval
\param value Value to be floored
15 years ago
\param intervalSize Interval size
\return Rounded value
\sa floorEps()
15 years ago
*/
double QwtScaleArithmetic::floorEps( double value, double intervalSize )
15 years ago
{
const double eps = _eps * intervalSize;
value = ( value + eps ) / intervalSize;
return ::floor( value ) * intervalSize;
15 years ago
}
/*!
15 years ago
\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
*/
double QwtScaleArithmetic::divideEps( double intervalSize, double numSteps )
15 years ago
{
if ( numSteps == 0.0 || intervalSize == 0.0 )
return 0.0;
return ( intervalSize - ( _eps * intervalSize ) ) / numSteps;
}
15 years ago
/*!
Calculate a step size for a given interval
\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 )
15 years ago
{
if ( numSteps <= 0 )
15 years ago
return 0.0;
const double v = QwtScaleArithmetic::divideEps( intervalSize, numSteps );
if ( v == 0.0 )
return 0.0;
15 years ago
const double lx = qwtLog( base, qFabs( v ) );
const double p = ::floor( lx );
15 years ago
const double fraction = qPow( base, lx - p );
15 years ago
uint n = base;
while ( ( n > 1 ) && ( fraction <= n / 2 ) )
n /= 2;
15 years ago
double stepSize = n * qPow( base, p );
if ( v < 0 )
stepSize = -stepSize;
15 years ago
return stepSize;
15 years ago
}
class QwtScaleEngine::PrivateData
{
public:
PrivateData():
attributes( QwtScaleEngine::NoAttribute ),
lowerMargin( 0.0 ),
upperMargin( 0.0 ),
referenceValue( 0.0 ),
base( 10 ),
transform( NULL )
{
}
~PrivateData()
{
delete transform;
15 years ago
}
QwtScaleEngine::Attributes attributes;
15 years ago
double lowerMargin;
double upperMargin;
15 years ago
double referenceValue;
15 years ago
uint base;
QwtTransform* transform;
15 years ago
};
/*!
Constructor
\param base Base of the scale engine
\sa setBase()
*/
QwtScaleEngine::QwtScaleEngine( uint base )
15 years ago
{
d_data = new PrivateData;
setBase( base );
15 years ago
}
//! Destructor
15 years ago
QwtScaleEngine::~QwtScaleEngine ()
{
delete d_data;
}
/*!
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;
}
/*!
15 years ago
\return the margin at the lower end of the scale
The default margin is 0.
\sa setMargins()
15 years ago
*/
double QwtScaleEngine::lowerMargin() const
{
return d_data->lowerMargin;
15 years ago
}
/*!
\return the margin at the upper end of the scale
The default margin is 0.
\sa setMargins()
15 years ago
*/
double QwtScaleEngine::upperMargin() const
{
return d_data->upperMargin;
15 years ago
}
/*!
\brief Specify margins at the scale's endpoints
\param lower minimum distance between the scale's lower boundary and the
15 years ago
smallest enclosed value
\param upper minimum distance between the scale's upper boundary and the
15 years ago
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
\li QwtLogScaleEngine measures the margins in decades.
15 years ago
\sa upperMargin(), lowerMargin()
15 years ago
*/
void QwtScaleEngine::setMargins( double lower, double upper )
15 years ago
{
d_data->lowerMargin = qMax( lower, 0.0 );
d_data->upperMargin = qMax( upper, 0.0 );
15 years ago
}
/*!
Calculate a step size for an interval size
\param intervalSize Interval size
\param numSteps Number of steps
15 years ago
\return Step size
*/
double QwtScaleEngine::divideInterval(
double intervalSize, int numSteps ) const
15 years ago
{
return QwtScaleArithmetic::divideInterval(
intervalSize, numSteps, d_data->base );
15 years ago
}
/*!
Check if an interval "contains" a value
\param interval Interval
\param value Value
\return True, when the value is inside the interval
15 years ago
*/
bool QwtScaleEngine::contains(
const QwtInterval &interval, double value ) const
15 years ago
{
if ( !interval.isValid() )
15 years ago
return false;
if ( qwtFuzzyCompare( value, interval.minValue(), interval.width() ) < 0 )
15 years ago
return false;
if ( qwtFuzzyCompare( value, interval.maxValue(), interval.width() ) > 0 )
15 years ago
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
*/
QList<double> QwtScaleEngine::strip( const QList<double>& ticks,
const QwtInterval &interval ) const
15 years ago
{
if ( !interval.isValid() || ticks.count() == 0 )
return QList<double>();
15 years ago
if ( contains( interval, ticks.first() )
&& contains( interval, ticks.last() ) )
{
15 years ago
return ticks;
}
QList<double> strippedTicks;
for ( int i = 0; i < ticks.count(); i++ )
{
if ( contains( interval, ticks[i] ) )
15 years ago
strippedTicks += ticks[i];
}
return strippedTicks;
}
/*!
\brief Build an interval around a value
15 years ago
In case of v == 0.0 the interval is [-0.5, 0.5],
otherwide it is [0.5 * v, 1.5 * v]
\param value Initial value
\return Calculated interval
15 years ago
*/
QwtInterval QwtScaleEngine::buildInterval( double value ) const
15 years ago
{
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 );
15 years ago
}
/*!
Change a scale attribute
\param attribute Attribute to change
\param on On/Off
\sa Attribute, testAttribute()
15 years ago
*/
void QwtScaleEngine::setAttribute( Attribute attribute, bool on )
15 years ago
{
if ( on )
d_data->attributes |= attribute;
15 years ago
else
d_data->attributes &= ~attribute;
15 years ago
}
/*!
\return True, if attribute is enabled.
15 years ago
\param attribute Attribute to be tested
\sa Attribute, setAttribute()
15 years ago
*/
bool QwtScaleEngine::testAttribute( Attribute attribute ) const
15 years ago
{
return ( d_data->attributes & attribute );
15 years ago
}
/*!
Change the scale attribute
\param attributes Set scale attributes
\sa Attribute, attributes()
15 years ago
*/
void QwtScaleEngine::setAttributes( Attributes attributes )
15 years ago
{
d_data->attributes = attributes;
}
/*!
\return Scale attributes
\sa Attribute, setAttributes(), testAttribute()
15 years ago
*/
QwtScaleEngine::Attributes QwtScaleEngine::attributes() const
15 years ago
{
return d_data->attributes;
}
/*!
\brief Specify a reference point
\param r new reference value
The reference point is needed if options IncludeReference or
15 years ago
Symmetric are active. Its default value is 0.0.
\sa Attribute
15 years ago
*/
void QwtScaleEngine::setReference( double r )
15 years ago
{
d_data->referenceValue = r;
}
/*!
\return the reference value
\sa setReference(), setAttribute()
15 years ago
*/
double QwtScaleEngine::reference() const
{
return d_data->referenceValue;
15 years ago
}
/*!
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
15 years ago
{
return d_data->base;
15 years ago
}
/*!
Constructor
15 years ago
\param base Base of the scale engine
\sa setBase()
*/
QwtLinearScaleEngine::QwtLinearScaleEngine( uint base ):
QwtScaleEngine( base )
{
}
//! Destructor
QwtLinearScaleEngine::~QwtLinearScaleEngine()
{
}
/*!
Align and divide an interval
15 years ago
\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()
15 years ago
*/
void QwtLinearScaleEngine::autoScale( int maxNumSteps,
double &x1, double &x2, double &stepSize ) const
15 years ago
{
QwtInterval interval( x1, x2 );
15 years ago
interval = interval.normalized();
interval.setMinValue( interval.minValue() - lowerMargin() );
interval.setMaxValue( interval.maxValue() + upperMargin() );
15 years ago
if ( testAttribute( QwtScaleEngine::Symmetric ) )
interval = interval.symmetrize( reference() );
if ( testAttribute( QwtScaleEngine::IncludeReference ) )
interval = interval.extend( reference() );
15 years ago
if ( interval.width() == 0.0 )
interval = buildInterval( interval.minValue() );
15 years ago
stepSize = QwtScaleArithmetic::divideInterval(
interval.width(), qMax( maxNumSteps, 1 ), base() );
15 years ago
if ( !testAttribute( QwtScaleEngine::Floating ) )
interval = align( interval, stepSize );
15 years ago
x1 = interval.minValue();
x2 = interval.maxValue();
if ( testAttribute( QwtScaleEngine::Inverted ) )
{
qSwap( x1, x2 );
15 years ago
stepSize = -stepSize;
}
}
/*!
\brief Calculate a scale division for an interval
15 years ago
\param x1 First interval limit
\param x2 Second interval limit
\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
15 years ago
calculates one.
\return Calculated scale division
15 years ago
*/
QwtScaleDiv QwtLinearScaleEngine::divideScale( double x1, double x2,
int maxMajorSteps, int maxMinorSteps, double stepSize ) const
15 years ago
{
QwtInterval interval = QwtInterval( x1, x2 ).normalized();
if ( interval.width() <= 0 )
15 years ago
return QwtScaleDiv();
stepSize = qAbs( stepSize );
if ( stepSize == 0.0 )
{
if ( maxMajorSteps < 1 )
maxMajorSteps = 1;
15 years ago
stepSize = QwtScaleArithmetic::divideInterval(
interval.width(), maxMajorSteps, base() );
15 years ago
}
QwtScaleDiv scaleDiv;
if ( stepSize != 0.0 )
{
QList<double> ticks[QwtScaleDiv::NTickTypes];
buildTicks( interval, stepSize, maxMinorSteps, ticks );
15 years ago
scaleDiv = QwtScaleDiv( interval, ticks );
15 years ago
}
if ( x1 > x2 )
scaleDiv.invert();
return scaleDiv;
}
/*!
\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
*/
15 years ago
void QwtLinearScaleEngine::buildTicks(
const QwtInterval& interval, double stepSize, int maxMinorSteps,
QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
15 years ago
{
const QwtInterval boundingInterval = align( interval, stepSize );
ticks[QwtScaleDiv::MajorTick] =
buildMajorTicks( boundingInterval, stepSize );
15 years ago
if ( maxMinorSteps > 0 )
{
buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
15 years ago
}
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
{
ticks[i] = strip( ticks[i], interval );
15 years ago
// ticks very close to 0.0 are
15 years ago
// explicitely set to 0.0
for ( int j = 0; j < ticks[i].count(); j++ )
{
if ( qwtFuzzyCompare( ticks[i][j], 0.0, stepSize ) == 0 )
15 years ago
ticks[i][j] = 0.0;
}
}
}
/*!
\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
15 years ago
{
int numTicks = qRound( interval.width() / stepSize ) + 1;
15 years ago
if ( numTicks > 10000 )
numTicks = 10000;
QList<double> ticks;
15 years ago
ticks += interval.minValue();
for ( int i = 1; i < numTicks - 1; i++ )
15 years ago
ticks += interval.minValue() + i * stepSize;
ticks += interval.maxValue();
return ticks;
}
/*!
\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
*/
15 years ago
void QwtLinearScaleEngine::buildMinorTicks(
const QList<double>& majorTicks,
int maxMinorSteps, double stepSize,
QList<double> &minorTicks,
QList<double> &mediumTicks ) const
{
double minStep = qwtStepSize( stepSize, maxMinorSteps, base() );
if ( minStep == 0.0 )
return;
15 years ago
// # ticks per interval
const int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1;
15 years ago
int medIndex = -1;
if ( numTicks % 2 )
medIndex = numTicks / 2;
// calculate minor ticks
for ( int i = 0; i < majorTicks.count(); i++ )
{
15 years ago
double val = majorTicks[i];
for ( int k = 0; k < numTicks; k++ )
{
15 years ago
val += minStep;
double alignedValue = val;
if ( qwtFuzzyCompare( val, 0.0, stepSize ) == 0 )
15 years ago
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
*/
QwtInterval QwtLinearScaleEngine::align(
const QwtInterval &interval, double stepSize ) const
15 years ago
{
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;
}
15 years ago
if ( DBL_MAX - stepSize >= x2 )
{
const double x = QwtScaleArithmetic::ceilEps( x2, stepSize );
if ( qwtFuzzyCompare( x2, x, stepSize ) != 0 )
x2 = x;
}
return QwtInterval( x1, x2 );
15 years ago
}
/*!
Constructor
\param base Base of the scale engine
\sa setBase()
*/
QwtLogScaleEngine::QwtLogScaleEngine( uint base ):
QwtScaleEngine( base )
{
setTransformation( new QwtLogTransform() );
}
//! Destructor
QwtLogScaleEngine::~QwtLogScaleEngine()
15 years ago
{
}
/*!
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)
\sa QwtScaleEngine::setAttribute()
15 years ago
*/
void QwtLogScaleEngine::autoScale( int maxNumSteps,
double &x1, double &x2, double &stepSize ) const
15 years ago
{
if ( x1 > x2 )
qSwap( x1, x2 );
15 years ago
const double logBase = base();
15 years ago
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 );
15 years ago
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 );
15 years ago
}
if ( testAttribute( QwtScaleEngine::IncludeReference ) )
interval = interval.extend( logRef );
15 years ago
interval = interval.limited( LOG_MIN, LOG_MAX );
15 years ago
if ( interval.width() == 0.0 )
interval = buildInterval( interval.minValue() );
15 years ago
stepSize = divideInterval( qwtLogInterval( logBase, interval ).width(),
qMax( maxNumSteps, 1 ) );
15 years ago
if ( stepSize < 1.0 )
stepSize = 1.0;
if ( !testAttribute( QwtScaleEngine::Floating ) )
interval = align( interval, stepSize );
15 years ago
x1 = interval.minValue();
x2 = interval.maxValue();
if ( testAttribute( QwtScaleEngine::Inverted ) )
{
qSwap( x1, x2 );
15 years ago
stepSize = -stepSize;
}
}
/*!
\brief Calculate a scale division for an interval
15 years ago
\param x1 First interval limit
\param x2 Second interval limit
\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
15 years ago
calculates one.
\return Calculated scale division
15 years ago
*/
QwtScaleDiv QwtLogScaleEngine::divideScale( double x1, double x2,
int maxMajorSteps, int maxMinorSteps, double stepSize ) const
15 years ago
{
QwtInterval interval = QwtInterval( x1, x2 ).normalized();
interval = interval.limited( LOG_MIN, LOG_MAX );
15 years ago
if ( interval.width() <= 0 )
15 years ago
return QwtScaleDiv();
const double logBase = base();
if ( interval.maxValue() / interval.minValue() < logBase )
{
15 years ago
// scale width is less than one decade -> build linear scale
15 years ago
QwtLinearScaleEngine linearScaler;
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 );
}
15 years ago
return linearScaler.divideScale( x1, x2,
maxMajorSteps, maxMinorSteps, stepSize );
15 years ago
}
stepSize = qAbs( stepSize );
if ( stepSize == 0.0 )
{
if ( maxMajorSteps < 1 )
maxMajorSteps = 1;
15 years ago
stepSize = divideInterval(
qwtLogInterval( logBase, interval ).width(), maxMajorSteps );
15 years ago
if ( stepSize < 1.0 )
stepSize = 1.0; // major step must be >= 1 decade
}
QwtScaleDiv scaleDiv;
if ( stepSize != 0.0 )
{
QList<double> ticks[QwtScaleDiv::NTickTypes];
buildTicks( interval, stepSize, maxMinorSteps, ticks );
15 years ago
scaleDiv = QwtScaleDiv( interval, ticks );
15 years ago
}
if ( x1 > x2 )
scaleDiv.invert();
return scaleDiv;
}
/*!
\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
15 years ago
{
const QwtInterval boundingInterval = align( interval, stepSize );
ticks[QwtScaleDiv::MajorTick] =
buildMajorTicks( boundingInterval, stepSize );
15 years ago
if ( maxMinorSteps > 0 )
{
buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
15 years ago
}
15 years ago
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
ticks[i] = strip( ticks[i], interval );
15 years ago
}
/*!
\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
15 years ago
{
double width = qwtLogInterval( base(), interval ).width();
15 years ago
int numTicks = qRound( width / stepSize ) + 1;
15 years ago
if ( numTicks > 10000 )
numTicks = 10000;
const double lxmin = ::log( interval.minValue() );
const double lxmax = ::log( interval.maxValue() );
const double lstep = ( lxmax - lxmin ) / double( numTicks - 1 );
15 years ago
QList<double> ticks;
15 years ago
ticks += interval.minValue();
for ( int i = 1; i < numTicks - 1; i++ )
ticks += qExp( lxmin + double( i ) * lstep );
15 years ago
ticks += interval.maxValue();
return ticks;
}
/*!
\brief Calculate minor/medium ticks for major ticks
15 years ago
\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
*/
void QwtLogScaleEngine::buildMinorTicks(
const QList<double> &majorTicks,
int maxMinorSteps, double stepSize,
QList<double> &minorTicks,
QList<double> &mediumTicks ) const
{
const double logBase = base();
15 years ago
if ( stepSize < 1.1 ) // major step width is one base
{
double minStep = divideInterval( stepSize, maxMinorSteps + 1 );
if ( minStep == 0.0 )
return;
const int numSteps = qRound( stepSize / minStep );
int mediumTickIndex = -1;
if ( ( numSteps > 2 ) && ( numSteps % 2 == 0 ) )
mediumTickIndex = numSteps / 2;
for ( int i = 0; i < majorTicks.count() - 1; i++ )
{
15 years ago
const double v = majorTicks[i];
const double s = logBase / numSteps;
if ( s >= 1.0 )
{
for ( int j = 2; j < numSteps; j++ )
{
minorTicks += v * j * s;
}
}
else
{
for ( int j = 1; j < numSteps; j++ )
{
const double tick = v + j * v * ( logBase - 1 ) / numSteps;
if ( j == mediumTickIndex )
mediumTicks += tick;
else
minorTicks += tick;
}
}
15 years ago
}
}
else
{
double minStep = divideInterval( stepSize, maxMinorSteps );
15 years ago
if ( minStep == 0.0 )
return;
15 years ago
if ( minStep < 1.0 )
minStep = 1.0;
// # subticks per interval
int numTicks = qRound( stepSize / minStep ) - 1;
15 years ago
// Do the minor steps fit into the interval?
if ( qwtFuzzyCompare( ( numTicks + 1 ) * minStep,
stepSize, stepSize ) > 0 )
{
numTicks = 0;
15 years ago
}
if ( numTicks < 1 )
return;
int mediumTickIndex = -1;
if ( ( numTicks > 2 ) && ( numTicks % 2 ) )
mediumTickIndex = numTicks / 2;
// substep factor = base^substeps
const qreal minFactor = qMax( qPow( logBase, minStep ), qreal( logBase ) );
15 years ago
for ( int i = 0; i < majorTicks.count(); i++ )
{
double tick = majorTicks[i];
for ( int j = 0; j < numTicks; j++ )
{
tick *= minFactor;
15 years ago
if ( j == mediumTickIndex )
mediumTicks += tick;
else
minorTicks += tick;
15 years ago
}
}
}
}
/*!
\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
*/
QwtInterval QwtLogScaleEngine::align(
const QwtInterval &interval, double stepSize ) const
15 years ago
{
const QwtInterval intv = qwtLogInterval( base(), interval );
15 years ago
double x1 = QwtScaleArithmetic::floorEps( intv.minValue(), stepSize );
if ( qwtFuzzyCompare( interval.minValue(), x1, stepSize ) == 0 )
x1 = interval.minValue();
15 years ago
double x2 = QwtScaleArithmetic::ceilEps( intv.maxValue(), stepSize );
if ( qwtFuzzyCompare( interval.maxValue(), x2, stepSize ) == 0 )
x2 = interval.maxValue();
15 years ago
return qwtPowInterval( base(), QwtInterval( x1, x2 ) );
15 years ago
}