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.
1118 lines
28 KiB
1118 lines
28 KiB
/* -*- 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 "qwt_scale_engine.h" |
|
#include "qwt_math.h" |
|
#include "qwt_scale_map.h" |
|
#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() ) ); |
|
} |
|
|
|
|
|
#if 1 |
|
|
|
// 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; |
|
} |
|
|
|
#else |
|
|
|
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; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return intervalSize * 0.5; |
|
} |
|
|
|
#endif |
|
|
|
static const double _eps = 1.0e-6; |
|
|
|
/*! |
|
Ceil a value, relative to an interval |
|
|
|
\param value Value to be ceiled |
|
\param intervalSize Interval size |
|
|
|
\return Rounded value |
|
|
|
\sa floorEps() |
|
*/ |
|
double QwtScaleArithmetic::ceilEps( double value, |
|
double intervalSize ) |
|
{ |
|
const double eps = _eps * intervalSize; |
|
|
|
value = ( value - eps ) / intervalSize; |
|
return ::ceil( value ) * intervalSize; |
|
} |
|
|
|
/*! |
|
Floor a value, relative to an interval |
|
|
|
\param value Value to be floored |
|
\param intervalSize Interval size |
|
|
|
\return Rounded value |
|
\sa floorEps() |
|
*/ |
|
double QwtScaleArithmetic::floorEps( double value, double intervalSize ) |
|
{ |
|
const double eps = _eps * 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; |
|
} |
|
|
|
/*! |
|
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 ) |
|
{ |
|
if ( numSteps <= 0 ) |
|
return 0.0; |
|
|
|
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 ); |
|
|
|
const double fraction = qPow( base, lx - p ); |
|
|
|
uint n = base; |
|
while ( ( n > 1 ) && ( fraction <= n / 2 ) ) |
|
n /= 2; |
|
|
|
double stepSize = n * qPow( base, p ); |
|
if ( v < 0 ) |
|
stepSize = -stepSize; |
|
|
|
return 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; |
|
} |
|
|
|
QwtScaleEngine::Attributes attributes; |
|
|
|
double lowerMargin; |
|
double upperMargin; |
|
|
|
double referenceValue; |
|
|
|
uint base; |
|
|
|
QwtTransform* transform; |
|
}; |
|
|
|
/*! |
|
Constructor |
|
|
|
\param base Base of the scale engine |
|
\sa setBase() |
|
*/ |
|
QwtScaleEngine::QwtScaleEngine( uint base ) |
|
{ |
|
d_data = new PrivateData; |
|
setBase( base ); |
|
} |
|
|
|
|
|
//! Destructor |
|
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; |
|
} |
|
|
|
/*! |
|
\return the margin at the lower end of the scale |
|
The default margin is 0. |
|
|
|
\sa setMargins() |
|
*/ |
|
double QwtScaleEngine::lowerMargin() const |
|
{ |
|
return d_data->lowerMargin; |
|
} |
|
|
|
/*! |
|
\return the margin at the upper end of the scale |
|
The default margin is 0. |
|
|
|
\sa setMargins() |
|
*/ |
|
double QwtScaleEngine::upperMargin() const |
|
{ |
|
return d_data->upperMargin; |
|
} |
|
|
|
/*! |
|
\brief Specify margins at the scale's endpoints |
|
\param lower minimum distance between the scale's lower boundary and the |
|
smallest enclosed value |
|
\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 |
|
\li QwtLogScaleEngine measures the margins in decades. |
|
|
|
\sa upperMargin(), lowerMargin() |
|
*/ |
|
|
|
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( |
|
double intervalSize, int numSteps ) const |
|
{ |
|
return QwtScaleArithmetic::divideInterval( |
|
intervalSize, numSteps, d_data->base ); |
|
} |
|
|
|
/*! |
|
Check if an interval "contains" a value |
|
|
|
\param interval Interval |
|
\param value Value |
|
|
|
\return True, when the value is inside the interval |
|
*/ |
|
bool QwtScaleEngine::contains( |
|
const QwtInterval &interval, double value ) const |
|
{ |
|
if ( !interval.isValid() ) |
|
return false; |
|
|
|
if ( qwtFuzzyCompare( value, interval.minValue(), interval.width() ) < 0 ) |
|
return false; |
|
|
|
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 ( !interval.isValid() || ticks.count() == 0 ) |
|
return QList<double>(); |
|
|
|
if ( contains( interval, ticks.first() ) |
|
&& contains( interval, ticks.last() ) ) |
|
{ |
|
return ticks; |
|
} |
|
|
|
QList<double> strippedTicks; |
|
for ( int i = 0; i < ticks.count(); i++ ) |
|
{ |
|
if ( contains( interval, ticks[i] ) ) |
|
strippedTicks += ticks[i]; |
|
} |
|
return strippedTicks; |
|
} |
|
|
|
/*! |
|
\brief Build an interval around a value |
|
|
|
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 |
|
|
|
\sa Attribute, testAttribute() |
|
*/ |
|
void QwtScaleEngine::setAttribute( Attribute attribute, bool on ) |
|
{ |
|
if ( on ) |
|
d_data->attributes |= attribute; |
|
else |
|
d_data->attributes &= ~attribute; |
|
} |
|
|
|
/*! |
|
\return True, if attribute is enabled. |
|
|
|
\param attribute Attribute to be tested |
|
\sa Attribute, setAttribute() |
|
*/ |
|
bool QwtScaleEngine::testAttribute( Attribute attribute ) const |
|
{ |
|
return ( d_data->attributes & attribute ); |
|
} |
|
|
|
/*! |
|
Change the scale attribute |
|
|
|
\param attributes Set scale attributes |
|
\sa Attribute, attributes() |
|
*/ |
|
void QwtScaleEngine::setAttributes( Attributes attributes ) |
|
{ |
|
d_data->attributes = 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 |
|
Symmetric are active. Its default value is 0.0. |
|
|
|
\sa Attribute |
|
*/ |
|
void QwtScaleEngine::setReference( double r ) |
|
{ |
|
d_data->referenceValue = r; |
|
} |
|
|
|
/*! |
|
\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 |
|
{ |
|
return d_data->base; |
|
} |
|
|
|
/*! |
|
Constructor |
|
|
|
\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 |
|
{ |
|
QwtInterval interval( x1, x2 ); |
|
interval = interval.normalized(); |
|
|
|
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 ); |
|
stepSize = -stepSize; |
|
} |
|
} |
|
|
|
/*! |
|
\brief Calculate a scale division for an interval |
|
|
|
\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 |
|
calculates one. |
|
|
|
\return Calculated scale division |
|
*/ |
|
QwtScaleDiv QwtLinearScaleEngine::divideScale( double x1, double x2, |
|
int maxMajorSteps, int maxMinorSteps, double stepSize ) const |
|
{ |
|
QwtInterval interval = QwtInterval( x1, x2 ).normalized(); |
|
if ( interval.width() <= 0 ) |
|
return QwtScaleDiv(); |
|
|
|
stepSize = qAbs( stepSize ); |
|
if ( stepSize == 0.0 ) |
|
{ |
|
if ( maxMajorSteps < 1 ) |
|
maxMajorSteps = 1; |
|
|
|
stepSize = QwtScaleArithmetic::divideInterval( |
|
interval.width(), maxMajorSteps, base() ); |
|
} |
|
|
|
QwtScaleDiv scaleDiv; |
|
|
|
if ( stepSize != 0.0 ) |
|
{ |
|
QList<double> ticks[QwtScaleDiv::NTickTypes]; |
|
buildTicks( interval, stepSize, maxMinorSteps, ticks ); |
|
|
|
scaleDiv = QwtScaleDiv( interval, 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 |
|
*/ |
|
void QwtLinearScaleEngine::buildTicks( |
|
const QwtInterval& interval, double stepSize, int maxMinorSteps, |
|
QList<double> ticks[QwtScaleDiv::NTickTypes] ) const |
|
{ |
|
const QwtInterval boundingInterval = align( interval, stepSize ); |
|
|
|
ticks[QwtScaleDiv::MajorTick] = |
|
buildMajorTicks( boundingInterval, stepSize ); |
|
|
|
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 ); |
|
|
|
// ticks very close to 0.0 are |
|
// explicitely set to 0.0 |
|
|
|
for ( int j = 0; j < ticks[i].count(); j++ ) |
|
{ |
|
if ( qwtFuzzyCompare( ticks[i][j], 0.0, stepSize ) == 0 ) |
|
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 |
|
{ |
|
int numTicks = qRound( interval.width() / stepSize ) + 1; |
|
if ( numTicks > 10000 ) |
|
numTicks = 10000; |
|
|
|
QList<double> ticks; |
|
|
|
ticks += interval.minValue(); |
|
for ( int i = 1; i < numTicks - 1; i++ ) |
|
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 |
|
|
|
*/ |
|
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; |
|
|
|
// # ticks per interval |
|
const int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1; |
|
|
|
int medIndex = -1; |
|
if ( numTicks % 2 ) |
|
medIndex = numTicks / 2; |
|
|
|
// calculate minor ticks |
|
|
|
for ( int i = 0; i < majorTicks.count(); i++ ) |
|
{ |
|
double val = majorTicks[i]; |
|
for ( int k = 0; k < numTicks; k++ ) |
|
{ |
|
val += minStep; |
|
|
|
double alignedValue = val; |
|
if ( qwtFuzzyCompare( val, 0.0, stepSize ) == 0 ) |
|
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) |
|
|
|
\sa QwtScaleEngine::setAttribute() |
|
*/ |
|
void QwtLogScaleEngine::autoScale( int maxNumSteps, |
|
double &x1, double &x2, double &stepSize ) const |
|
{ |
|
if ( x1 > x2 ) |
|
qSwap( x1, x2 ); |
|
|
|
const double logBase = base(); |
|
|
|
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 ); |
|
|
|
interval = interval.limited( LOG_MIN, LOG_MAX ); |
|
|
|
if ( interval.width() == 0.0 ) |
|
interval = buildInterval( interval.minValue() ); |
|
|
|
stepSize = divideInterval( qwtLogInterval( logBase, interval ).width(), |
|
qMax( maxNumSteps, 1 ) ); |
|
if ( stepSize < 1.0 ) |
|
stepSize = 1.0; |
|
|
|
if ( !testAttribute( QwtScaleEngine::Floating ) ) |
|
interval = align( interval, stepSize ); |
|
|
|
x1 = interval.minValue(); |
|
x2 = interval.maxValue(); |
|
|
|
if ( testAttribute( QwtScaleEngine::Inverted ) ) |
|
{ |
|
qSwap( x1, x2 ); |
|
stepSize = -stepSize; |
|
} |
|
} |
|
|
|
/*! |
|
\brief Calculate a scale division for an interval |
|
|
|
\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 |
|
calculates one. |
|
|
|
\return Calculated scale division |
|
*/ |
|
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 ); |
|
|
|
if ( interval.width() <= 0 ) |
|
return QwtScaleDiv(); |
|
|
|
const double logBase = base(); |
|
|
|
if ( interval.maxValue() / interval.minValue() < logBase ) |
|
{ |
|
// scale width is less than one decade -> build linear scale |
|
|
|
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 ); |
|
} |
|
|
|
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 ); |
|
|
|
scaleDiv = QwtScaleDiv( interval, 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] = |
|
buildMajorTicks( boundingInterval, stepSize ); |
|
|
|
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 ); |
|
} |
|
|
|
/*! |
|
\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 |
|
{ |
|
double width = qwtLogInterval( base(), interval ).width(); |
|
|
|
int numTicks = qRound( width / stepSize ) + 1; |
|
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 ); |
|
|
|
QList<double> ticks; |
|
|
|
ticks += interval.minValue(); |
|
|
|
for ( int i = 1; i < numTicks - 1; i++ ) |
|
ticks += qExp( lxmin + double( i ) * lstep ); |
|
|
|
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 |
|
*/ |
|
void QwtLogScaleEngine::buildMinorTicks( |
|
const QList<double> &majorTicks, |
|
int maxMinorSteps, double stepSize, |
|
QList<double> &minorTicks, |
|
QList<double> &mediumTicks ) const |
|
{ |
|
const double logBase = base(); |
|
|
|
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++ ) |
|
{ |
|
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; |
|
} |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
double minStep = divideInterval( stepSize, maxMinorSteps ); |
|
if ( minStep == 0.0 ) |
|
return; |
|
|
|
if ( minStep < 1.0 ) |
|
minStep = 1.0; |
|
|
|
// # subticks per interval |
|
int numTicks = qRound( stepSize / minStep ) - 1; |
|
|
|
// Do the minor steps fit into the interval? |
|
if ( qwtFuzzyCompare( ( numTicks + 1 ) * minStep, |
|
stepSize, stepSize ) > 0 ) |
|
{ |
|
numTicks = 0; |
|
} |
|
|
|
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 ) ); |
|
|
|
for ( int i = 0; i < majorTicks.count(); i++ ) |
|
{ |
|
double tick = majorTicks[i]; |
|
for ( int j = 0; j < numTicks; j++ ) |
|
{ |
|
tick *= minFactor; |
|
|
|
if ( j == mediumTickIndex ) |
|
mediumTicks += tick; |
|
else |
|
minorTicks += tick; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/*! |
|
\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 |
|
{ |
|
const QwtInterval intv = qwtLogInterval( base(), interval ); |
|
|
|
double x1 = QwtScaleArithmetic::floorEps( intv.minValue(), stepSize ); |
|
if ( qwtFuzzyCompare( interval.minValue(), x1, stepSize ) == 0 ) |
|
x1 = interval.minValue(); |
|
|
|
double x2 = QwtScaleArithmetic::ceilEps( intv.maxValue(), stepSize ); |
|
if ( qwtFuzzyCompare( interval.maxValue(), x2, stepSize ) == 0 ) |
|
x2 = interval.maxValue(); |
|
|
|
return qwtPowInterval( base(), QwtInterval( x1, x2 ) ); |
|
}
|
|
|