Newer
Older
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* Qwt Widget Library
* Copyright (C) 1997 Josef Wilgen
* Copyright (C) 2002 Uwe Rathmann
* This library is free software; you can redistribute it and/or
* modify it under the terms of the Qwt License, Version 1.0
*****************************************************************************/
#include <qalgorithms.h>
#include <qmath.h>
#include <float.h>
#if QT_VERSION < 0x040601
#define qFabs(x) ::fabs(x)
#define qExp(x) ::exp(x)
#endif
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() ) );
}
// this version often doesn't find the best ticks: f.e for 15: 5, 10
static double qwtStepSize( double intervalSize, int maxSteps, uint base )
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;
}
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;
}
}
}
}
double QwtScaleArithmetic::ceilEps( double value,
double intervalSize )
value = ( value - eps ) / intervalSize;
return ::ceil( value ) * intervalSize;
double QwtScaleArithmetic::floorEps( double value, double intervalSize )
value = ( value + eps ) / intervalSize;
return ::floor( value ) * intervalSize;
\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 )
{
if ( numSteps == 0.0 || intervalSize == 0.0 )
return 0.0;
return ( intervalSize - ( _eps * intervalSize ) ) / numSteps;
\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 )
const double v = QwtScaleArithmetic::divideEps( intervalSize, numSteps );
if ( v == 0.0 )
return 0.0;
const double lx = qwtLog( base, qFabs( v ) );
const double p = ::floor( lx );
uint n = base;
while ( ( n > 1 ) && ( fraction <= n / 2 ) )
n /= 2;
double stepSize = n * qPow( base, p );
if ( v < 0 )
stepSize = -stepSize;
}
class QwtScaleEngine::PrivateData
{
public:
PrivateData():
attributes( QwtScaleEngine::NoAttribute ),
lowerMargin( 0.0 ),
upperMargin( 0.0 ),
referenceValue( 0.0 ),
base( 10 ),
transform( NULL )
{
}
~PrivateData()
{
delete transform;
/*!
Constructor
\param base Base of the scale engine
\sa setBase()
*/
QwtScaleEngine::QwtScaleEngine( uint base )
QwtScaleEngine::~QwtScaleEngine ()
{
delete d_data;
}
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*!
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;
}
/*!
\return the margin at the lower end of the scale
The default margin is 0.
}
/*!
\return the margin at the upper end of the scale
The default margin is 0.
}
/*!
\brief Specify margins at the scale's endpoints
\param lower minimum distance between the scale's lower boundary and the
\param upper minimum distance between the scale's upper boundary and the
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
void QwtScaleEngine::setMargins( double lower, double upper )
d_data->lowerMargin = qMax( lower, 0.0 );
d_data->upperMargin = qMax( upper, 0.0 );
}
/*!
Calculate a step size for an interval size
\param intervalSize Interval size
\param numSteps Number of steps
\return Step size
*/
double QwtScaleEngine::divideInterval(
return QwtScaleArithmetic::divideInterval(
intervalSize, numSteps, d_data->base );
}
/*!
Check if an interval "contains" a value
\param interval Interval
\param value Value
if ( qwtFuzzyCompare( value, interval.minValue(), interval.width() ) < 0 )
if ( qwtFuzzyCompare( value, interval.maxValue(), interval.width() ) > 0 )
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
if ( contains( interval, ticks.first() )
&& contains( interval, ticks.last() ) )
{
QList<double> strippedTicks;
for ( int i = 0; i < ticks.count(); i++ )
{
if ( contains( interval, ticks[i] ) )
strippedTicks += ticks[i];
}
return strippedTicks;
}
/*!
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
QwtInterval QwtScaleEngine::buildInterval( double value ) const
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 );
}
/*!
Change a scale attribute
\param attribute Attribute to change
\param on On/Off
void QwtScaleEngine::setAttribute( Attribute attribute, bool on )
bool QwtScaleEngine::testAttribute( Attribute attribute ) const
}
/*!
Change the scale attribute
\param attributes Set scale attributes
\return Scale attributes
\sa Attribute, setAttributes(), testAttribute()
QwtScaleEngine::Attributes QwtScaleEngine::attributes() const
{
return d_data->attributes;
}
/*!
\brief Specify a reference point
\param r new reference value
The reference point is needed if options IncludeReference or
\return the reference value
\sa setReference(), setAttribute()
double QwtScaleEngine::reference() const
{
return d_data->referenceValue;
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
\param base Base of the scale engine
\sa setBase()
*/
QwtLinearScaleEngine::QwtLinearScaleEngine( uint base ):
QwtScaleEngine( base )
{
}
//! Destructor
QwtLinearScaleEngine::~QwtLinearScaleEngine()
{
}
/*!
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 setAttribute()
void QwtLinearScaleEngine::autoScale( int maxNumSteps,
double &x1, double &x2, double &stepSize ) const
interval.setMinValue( interval.minValue() - lowerMargin() );
interval.setMaxValue( interval.maxValue() + upperMargin() );
if ( testAttribute( QwtScaleEngine::Symmetric ) )
interval = interval.symmetrize( reference() );
if ( testAttribute( QwtScaleEngine::IncludeReference ) )
interval = interval.extend( reference() );
if ( interval.width() == 0.0 )
interval = buildInterval( interval.minValue() );
stepSize = QwtScaleArithmetic::divideInterval(
interval.width(), qMax( maxNumSteps, 1 ), base() );
if ( !testAttribute( QwtScaleEngine::Floating ) )
interval = align( interval, stepSize );
x1 = interval.minValue();
x2 = interval.maxValue();
if ( testAttribute( QwtScaleEngine::Inverted ) )
{
qSwap( x1, x2 );
\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
QwtScaleDiv QwtLinearScaleEngine::divideScale( double x1, double x2,
int maxMajorSteps, int maxMinorSteps, double stepSize ) const
QwtInterval interval = QwtInterval( x1, x2 ).normalized();
if ( interval.width() <= 0 )
stepSize = qAbs( stepSize );
if ( stepSize == 0.0 )
{
if ( maxMajorSteps < 1 )
maxMajorSteps = 1;
stepSize = QwtScaleArithmetic::divideInterval(
interval.width(), maxMajorSteps, base() );
if ( stepSize != 0.0 )
{
QList<double> ticks[QwtScaleDiv::NTickTypes];
buildTicks( interval, stepSize, maxMinorSteps, ticks );
}
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
*/
const QwtInterval& interval, double stepSize, int maxMinorSteps,
QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
const QwtInterval boundingInterval = align( interval, stepSize );
ticks[QwtScaleDiv::MajorTick] =
if ( maxMinorSteps > 0 )
{
buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
{
ticks[i] = strip( ticks[i], interval );
for ( int j = 0; j < ticks[i].count(); j++ )
{
if ( qwtFuzzyCompare( ticks[i][j], 0.0, stepSize ) == 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
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
*/
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 )
const int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1;
int medIndex = -1;
if ( numTicks % 2 )
medIndex = numTicks / 2;
// calculate minor ticks
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
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;
}
if ( DBL_MAX - stepSize >= x2 )
{
const double x = QwtScaleArithmetic::ceilEps( x2, stepSize );
if ( qwtFuzzyCompare( x2, x, stepSize ) != 0 )
x2 = x;
}
return QwtInterval( x1, x2 );
Constructor
\param base Base of the scale engine
\sa setBase()
*/
QwtLogScaleEngine::QwtLogScaleEngine( uint base ):
QwtScaleEngine( base )
{
setTransformation( new QwtLogTransform() );
}
//! Destructor
QwtLogScaleEngine::~QwtLogScaleEngine()
{
}
/*!
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)
void QwtLogScaleEngine::autoScale( int maxNumSteps,
double &x1, double &x2, double &stepSize ) const
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 );
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 );
if ( testAttribute( QwtScaleEngine::IncludeReference ) )
interval = interval.extend( logRef );
if ( interval.width() == 0.0 )
interval = buildInterval( interval.minValue() );
stepSize = divideInterval( qwtLogInterval( logBase, interval ).width(),
qMax( maxNumSteps, 1 ) );
if ( !testAttribute( QwtScaleEngine::Floating ) )
interval = align( interval, stepSize );
x1 = interval.minValue();
x2 = interval.maxValue();
if ( testAttribute( QwtScaleEngine::Inverted ) )
{
qSwap( x1, x2 );
\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
QwtScaleDiv QwtLogScaleEngine::divideScale( double x1, double x2,
int maxMajorSteps, int maxMinorSteps, double stepSize ) const
QwtInterval interval = QwtInterval( x1, x2 ).normalized();
interval = interval.limited( LOG_MIN, LOG_MAX );
const double logBase = base();
if ( interval.maxValue() / interval.minValue() < logBase )
{
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 );
}
return linearScaler.divideScale( x1, x2,
maxMajorSteps, maxMinorSteps, stepSize );
stepSize = qAbs( stepSize );
if ( stepSize == 0.0 )
{
if ( maxMajorSteps < 1 )
maxMajorSteps = 1;
stepSize = divideInterval(
qwtLogInterval( logBase, interval ).width(), maxMajorSteps );
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 );
}
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
const QwtInterval boundingInterval = align( interval, stepSize );
ticks[QwtScaleDiv::MajorTick] =
if ( maxMinorSteps > 0 )
{
buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
/*!
\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
const double lxmin = ::log( interval.minValue() );
const double lxmax = ::log( interval.maxValue() );
const double lstep = ( lxmax - lxmin ) / double( numTicks - 1 );
for ( int i = 1; i < numTicks - 1; i++ )
ticks += qExp( lxmin + double( i ) * lstep );
ticks += interval.maxValue();
return ticks;
}