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.
874 lines
0 B
874 lines
0 B
15 years ago
|
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
|
||
|
* Qwt Widget Library
|
||
|
* Copyright (C) 1997 Josef Wilgen
|
||
|
* Copyright (C) 2002 Uwe Rathmann
|
||
14 years ago
|
*
|
||
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_math.h"
|
||
|
#include "qwt_scale_map.h"
|
||
|
#include "qwt_scale_engine.h"
|
||
|
|
||
|
static const double _eps = 1.0e-6;
|
||
|
|
||
|
/*!
|
||
|
\brief Compare 2 values, relative to an interval
|
||
|
|
||
|
Values are "equal", when :
|
||
|
\f$\cdot value2 - value1 <= abs(intervalSize * 10e^{-6})\f$
|
||
|
|
||
|
\param value1 First value to compare
|
||
|
\param value2 Second value to compare
|
||
|
\param intervalSize interval size
|
||
|
|
||
|
\return 0: if equal, -1: if value2 > value1, 1: if value1 > value2
|
||
|
*/
|
||
14 years ago
|
int QwtScaleArithmetic::compareEps(double value1, double value2,
|
||
|
double intervalSize)
|
||
15 years ago
|
{
|
||
|
const double eps = qwtAbs(_eps * intervalSize);
|
||
|
|
||
|
if ( value2 - value1 > eps )
|
||
|
return -1;
|
||
|
|
||
|
if ( value1 - value2 > eps )
|
||
|
return 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Ceil a value, relative to an interval
|
||
|
|
||
|
\param value Value to ceil
|
||
|
\param intervalSize Interval size
|
||
14 years ago
|
|
||
15 years ago
|
\sa floorEps
|
||
|
*/
|
||
14 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;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Floor a value, relative to an interval
|
||
|
|
||
|
\param value Value to floor
|
||
|
\param intervalSize Interval size
|
||
14 years ago
|
|
||
15 years ago
|
\sa floorEps
|
||
|
*/
|
||
14 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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
\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
|
||
|
*/
|
||
14 years ago
|
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;
|
||
14 years ago
|
}
|
||
15 years ago
|
|
||
|
/*!
|
||
|
Find the smallest value out of {1,2,5}*10^n with an integer number n
|
||
|
which is greater than or equal to x
|
||
14 years ago
|
|
||
15 years ago
|
\param x Input value
|
||
|
*/
|
||
14 years ago
|
double QwtScaleArithmetic::ceil125(double x)
|
||
15 years ago
|
{
|
||
14 years ago
|
if (x == 0.0)
|
||
15 years ago
|
return 0.0;
|
||
|
|
||
|
const double sign = (x > 0) ? 1.0 : -1.0;
|
||
|
const double lx = log10(fabs(x));
|
||
|
const double p10 = floor(lx);
|
||
14 years ago
|
|
||
15 years ago
|
double fr = pow(10.0, lx - p10);
|
||
|
if (fr <=1.0)
|
||
14 years ago
|
fr = 1.0;
|
||
15 years ago
|
else if (fr <= 2.0)
|
||
14 years ago
|
fr = 2.0;
|
||
|
else if (fr <= 5.0)
|
||
|
fr = 5.0;
|
||
15 years ago
|
else
|
||
14 years ago
|
fr = 10.0;
|
||
15 years ago
|
|
||
|
return sign * fr * pow(10.0, p10);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\brief Find the largest value out of {1,2,5}*10^n with an integer number n
|
||
|
which is smaller than or equal to x
|
||
|
|
||
|
\param x Input value
|
||
|
*/
|
||
14 years ago
|
double QwtScaleArithmetic::floor125(double x)
|
||
15 years ago
|
{
|
||
|
if (x == 0.0)
|
||
|
return 0.0;
|
||
|
|
||
|
double sign = (x > 0) ? 1.0 : -1.0;
|
||
|
const double lx = log10(fabs(x));
|
||
|
const double p10 = floor(lx);
|
||
|
|
||
|
double fr = pow(10.0, lx - p10);
|
||
|
if (fr >= 10.0)
|
||
14 years ago
|
fr = 10.0;
|
||
15 years ago
|
else if (fr >= 5.0)
|
||
14 years ago
|
fr = 5.0;
|
||
15 years ago
|
else if (fr >= 2.0)
|
||
14 years ago
|
fr = 2.0;
|
||
15 years ago
|
else
|
||
14 years ago
|
fr = 1.0;
|
||
15 years ago
|
|
||
|
return sign * fr * pow(10.0, p10);
|
||
|
}
|
||
|
|
||
|
class QwtScaleEngine::PrivateData
|
||
|
{
|
||
|
public:
|
||
|
PrivateData():
|
||
|
attributes(QwtScaleEngine::NoAttribute),
|
||
|
loMargin(0.0),
|
||
|
hiMargin(0.0),
|
||
14 years ago
|
referenceValue(0.0) {
|
||
15 years ago
|
}
|
||
|
|
||
|
int attributes; // scale attributes
|
||
|
|
||
|
double loMargin; // margins
|
||
|
double hiMargin;
|
||
|
|
||
|
double referenceValue; // reference value
|
||
|
|
||
|
};
|
||
|
|
||
|
//! Ctor
|
||
|
QwtScaleEngine::QwtScaleEngine()
|
||
|
{
|
||
|
d_data = new PrivateData;
|
||
|
}
|
||
|
|
||
|
|
||
|
//! Dtor
|
||
|
QwtScaleEngine::~QwtScaleEngine ()
|
||
|
{
|
||
|
delete d_data;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\return the margin at the lower end of the scale
|
||
|
The default margin is 0.
|
||
|
|
||
|
\sa QwtScaleEngine::setMargins()
|
||
|
*/
|
||
14 years ago
|
double QwtScaleEngine::loMargin() const
|
||
|
{
|
||
|
return d_data->loMargin;
|
||
15 years ago
|
}
|
||
|
|
||
|
/*!
|
||
|
\return the margin at the upper end of the scale
|
||
|
The default margin is 0.
|
||
|
|
||
|
\sa QwtScaleEngine::setMargins()
|
||
|
*/
|
||
14 years ago
|
double QwtScaleEngine::hiMargin() const
|
||
|
{
|
||
|
return d_data->hiMargin;
|
||
15 years ago
|
}
|
||
|
|
||
|
/*!
|
||
|
\brief Specify margins at the scale's endpoints
|
||
|
\param mlo minimum distance between the scale's lower boundary and the
|
||
|
smallest enclosed value
|
||
|
\param mhi 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 QwtLog10ScaleEngine measures the margins in decades.
|
||
|
|
||
|
\sa QwtScaleEngine::hiMargin, QwtScaleEngine::loMargin
|
||
|
*/
|
||
|
|
||
|
void QwtScaleEngine::setMargins(double mlo, double mhi)
|
||
|
{
|
||
|
d_data->loMargin = qwtMax(mlo,0.0);
|
||
|
d_data->hiMargin = qwtMax(mhi,0.0);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Calculate a step size for an interval size
|
||
|
|
||
|
\param intervalSize Interval size
|
||
|
\param numSteps Number of steps
|
||
14 years ago
|
|
||
15 years ago
|
\return Step size
|
||
|
*/
|
||
|
double QwtScaleEngine::divideInterval(
|
||
|
double intervalSize, int numSteps) const
|
||
|
{
|
||
|
if ( numSteps <= 0 )
|
||
|
return 0.0;
|
||
|
|
||
|
double v = QwtScaleArithmetic::divideEps(intervalSize, numSteps);
|
||
|
return QwtScaleArithmetic::ceil125(v);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Check if an interval "contains" a value
|
||
|
|
||
|
\param interval Interval
|
||
|
\param value Value
|
||
|
|
||
|
\sa QwtScaleArithmetic::compareEps
|
||
|
*/
|
||
|
bool QwtScaleEngine::contains(
|
||
|
const QwtDoubleInterval &interval, double value) const
|
||
|
{
|
||
|
if (!interval.isValid() )
|
||
|
return false;
|
||
14 years ago
|
|
||
|
if ( QwtScaleArithmetic::compareEps(value,
|
||
|
interval.minValue(), interval.width()) < 0 ) {
|
||
15 years ago
|
return false;
|
||
|
}
|
||
|
|
||
14 years ago
|
if ( QwtScaleArithmetic::compareEps(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
|
||
|
*/
|
||
14 years ago
|
QwtValueList QwtScaleEngine::strip(
|
||
|
const QwtValueList& ticks,
|
||
15 years ago
|
const QwtDoubleInterval &interval) const
|
||
|
{
|
||
|
if ( !interval.isValid() || ticks.count() == 0 )
|
||
|
return QwtValueList();
|
||
|
|
||
|
if ( contains(interval, ticks.first())
|
||
14 years ago
|
&& contains(interval, ticks.last()) ) {
|
||
15 years ago
|
return ticks;
|
||
|
}
|
||
|
|
||
|
QwtValueList strippedTicks;
|
||
14 years ago
|
for ( int i = 0; i < (int)ticks.count(); i++ ) {
|
||
15 years ago
|
if ( contains(interval, ticks[i]) )
|
||
|
strippedTicks += ticks[i];
|
||
|
}
|
||
|
return strippedTicks;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\brief Build an interval for a value
|
||
|
|
||
|
In case of v == 0.0 the interval is [-0.5, 0.5],
|
||
|
otherwide it is [0.5 * v, 1.5 * v]
|
||
|
*/
|
||
|
|
||
|
QwtDoubleInterval QwtScaleEngine::buildInterval(double v) const
|
||
|
{
|
||
|
const double delta = (v == 0.0) ? 0.5 : qwtAbs(0.5 * v);
|
||
|
return QwtDoubleInterval(v - delta, v + delta);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Change a scale attribute
|
||
|
|
||
|
\param attribute Attribute to change
|
||
|
\param on On/Off
|
||
|
|
||
|
The behaviour of the scale engine can be changed
|
||
|
with the following attributes:
|
||
|
<dl>
|
||
|
<dt>QwtScaleEngine::IncludeReference
|
||
|
<dd>Build a scale which includes the reference value.
|
||
|
<dt>QwtScaleEngine::Symmetric
|
||
|
<dd>Build a scale which is symmetric to the reference value.
|
||
|
<dt>QwtScaleEngine::Floating
|
||
|
<dd>The endpoints of the scale are supposed to be equal the outmost included
|
||
|
values plus the specified margins (see setMargins()). If this attribute is
|
||
|
*not* set, the endpoints of the scale will be integer multiples of the step
|
||
|
size.
|
||
|
<dt>QwtScaleEngine::Inverted
|
||
|
<dd>Turn the scale upside down.
|
||
|
</dl>
|
||
|
|
||
|
\sa QwtScaleEngine::testAttribute()
|
||
|
*/
|
||
|
void QwtScaleEngine::setAttribute(Attribute attribute, bool on)
|
||
|
{
|
||
|
if (on)
|
||
14 years ago
|
d_data->attributes |= attribute;
|
||
15 years ago
|
else
|
||
14 years ago
|
d_data->attributes &= (~attribute);
|
||
15 years ago
|
}
|
||
|
|
||
|
/*!
|
||
|
Check if a attribute is set.
|
||
|
|
||
|
\param attribute Attribute to be tested
|
||
|
\sa QwtScaleEngine::setAttribute() for a description of the possible options.
|
||
|
*/
|
||
|
bool QwtScaleEngine::testAttribute(Attribute attribute) const
|
||
|
{
|
||
|
return bool(d_data->attributes & attribute);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Change the scale attribute
|
||
|
|
||
|
\param attributes Set scale attributes
|
||
|
\sa QwtScaleEngine::attributes()
|
||
|
*/
|
||
|
void QwtScaleEngine::setAttributes(int attributes)
|
||
|
{
|
||
|
d_data->attributes = attributes;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Return the scale attributes
|
||
|
*/
|
||
|
int QwtScaleEngine::attributes() const
|
||
|
{
|
||
|
return d_data->attributes;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\brief Specify a reference point
|
||
|
\param r new reference value
|
||
|
|
||
|
The reference point is needed if options IncludeRef or
|
||
|
Symmetric are active. Its default value is 0.0.
|
||
|
*/
|
||
|
void QwtScaleEngine::setReference(double r)
|
||
|
{
|
||
|
d_data->referenceValue = r;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\return the reference value
|
||
|
\sa QwtScaleEngine::setReference(), QwtScaleEngine::setAttribute()
|
||
|
*/
|
||
14 years ago
|
double QwtScaleEngine::reference() const
|
||
|
{
|
||
|
return d_data->referenceValue;
|
||
15 years ago
|
}
|
||
|
|
||
|
/*!
|
||
|
Return a transformation, for linear scales
|
||
|
*/
|
||
|
QwtScaleTransformation *QwtLinearScaleEngine::transformation() const
|
||
|
{
|
||
|
return new QwtScaleTransformation(QwtScaleTransformation::Linear);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
14 years ago
|
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 QwtLinearScaleEngine::setAttribute
|
||
|
*/
|
||
14 years ago
|
void QwtLinearScaleEngine::autoScale(int maxNumSteps,
|
||
|
double &x1, double &x2, double &stepSize) const
|
||
15 years ago
|
{
|
||
|
QwtDoubleInterval interval(x1, x2);
|
||
|
interval = interval.normalized();
|
||
|
|
||
|
interval.setMinValue(interval.minValue() - loMargin());
|
||
|
interval.setMaxValue(interval.maxValue() + hiMargin());
|
||
|
|
||
|
if (testAttribute(QwtScaleEngine::Symmetric))
|
||
|
interval = interval.symmetrize(reference());
|
||
14 years ago
|
|
||
15 years ago
|
if (testAttribute(QwtScaleEngine::IncludeReference))
|
||
|
interval = interval.extend(reference());
|
||
|
|
||
|
if (interval.width() == 0.0)
|
||
|
interval = buildInterval(interval.minValue());
|
||
|
|
||
|
stepSize = divideInterval(interval.width(), qwtMax(maxNumSteps, 1));
|
||
|
|
||
|
if ( !testAttribute(QwtScaleEngine::Floating) )
|
||
|
interval = align(interval, stepSize);
|
||
|
|
||
|
x1 = interval.minValue();
|
||
|
x2 = interval.maxValue();
|
||
|
|
||
14 years ago
|
if (testAttribute(QwtScaleEngine::Inverted)) {
|
||
15 years ago
|
qSwap(x1, x2);
|
||
|
stepSize = -stepSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\brief Calculate a scale division
|
||
|
|
||
14 years ago
|
\param x1 First interval limit
|
||
|
\param x2 Second interval limit
|
||
15 years ago
|
\param maxMajSteps Maximum for the number of major steps
|
||
|
\param maxMinSteps Maximum number of minor steps
|
||
|
\param stepSize Step size. If stepSize == 0, the scaleEngine
|
||
|
calculates one.
|
||
|
|
||
|
\sa QwtScaleEngine::stepSize, QwtScaleEngine::subDivide
|
||
|
*/
|
||
|
QwtScaleDiv QwtLinearScaleEngine::divideScale(double x1, double x2,
|
||
14 years ago
|
int maxMajSteps, int maxMinSteps, double stepSize) const
|
||
15 years ago
|
{
|
||
|
QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
|
||
|
if (interval.width() <= 0 )
|
||
|
return QwtScaleDiv();
|
||
|
|
||
|
stepSize = qwtAbs(stepSize);
|
||
14 years ago
|
if ( stepSize == 0.0 ) {
|
||
15 years ago
|
if ( maxMajSteps < 1 )
|
||
|
maxMajSteps = 1;
|
||
|
|
||
|
stepSize = divideInterval(interval.width(), maxMajSteps);
|
||
|
}
|
||
|
|
||
|
QwtScaleDiv scaleDiv;
|
||
|
|
||
14 years ago
|
if ( stepSize != 0.0 ) {
|
||
15 years ago
|
QwtValueList ticks[QwtScaleDiv::NTickTypes];
|
||
|
buildTicks(interval, stepSize, maxMinSteps, ticks);
|
||
|
|
||
|
scaleDiv = QwtScaleDiv(interval, ticks);
|
||
|
}
|
||
|
|
||
|
if ( x1 > x2 )
|
||
|
scaleDiv.invert();
|
||
|
|
||
|
return scaleDiv;
|
||
|
}
|
||
|
|
||
|
void QwtLinearScaleEngine::buildTicks(
|
||
|
const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
|
||
|
QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
|
||
|
{
|
||
|
const QwtDoubleInterval boundingInterval =
|
||
|
align(interval, stepSize);
|
||
14 years ago
|
|
||
|
ticks[QwtScaleDiv::MajorTick] =
|
||
15 years ago
|
buildMajorTicks(boundingInterval, stepSize);
|
||
|
|
||
14 years ago
|
if ( maxMinSteps > 0 ) {
|
||
15 years ago
|
buildMinorTicks(ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize,
|
||
14 years ago
|
ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick]);
|
||
15 years ago
|
}
|
||
14 years ago
|
|
||
|
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) {
|
||
15 years ago
|
ticks[i] = strip(ticks[i], interval);
|
||
|
|
||
14 years ago
|
// ticks very close to 0.0 are
|
||
15 years ago
|
// explicitely set to 0.0
|
||
|
|
||
14 years ago
|
for ( int j = 0; j < (int)ticks[i].count(); j++ ) {
|
||
15 years ago
|
if ( QwtScaleArithmetic::compareEps(ticks[i][j], 0.0, stepSize) == 0 )
|
||
|
ticks[i][j] = 0.0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QwtValueList QwtLinearScaleEngine::buildMajorTicks(
|
||
|
const QwtDoubleInterval &interval, double stepSize) const
|
||
|
{
|
||
|
int numTicks = qRound(interval.width() / stepSize) + 1;
|
||
|
#if 1
|
||
|
if ( numTicks > 10000 )
|
||
|
numTicks = 10000;
|
||
|
#endif
|
||
|
|
||
|
QwtValueList ticks;
|
||
|
|
||
|
ticks += interval.minValue();
|
||
|
for (int i = 1; i < numTicks - 1; i++)
|
||
|
ticks += interval.minValue() + i * stepSize;
|
||
|
ticks += interval.maxValue();
|
||
|
|
||
|
return ticks;
|
||
|
}
|
||
|
|
||
|
void QwtLinearScaleEngine::buildMinorTicks(
|
||
|
const QwtValueList& majorTicks,
|
||
|
int maxMinSteps, double stepSize,
|
||
14 years ago
|
QwtValueList &minorTicks,
|
||
15 years ago
|
QwtValueList &mediumTicks) const
|
||
14 years ago
|
{
|
||
15 years ago
|
double minStep = divideInterval(stepSize, maxMinSteps);
|
||
14 years ago
|
if (minStep == 0.0)
|
||
|
return;
|
||
|
|
||
15 years ago
|
// # ticks per interval
|
||
|
int numTicks = (int)::ceil(qwtAbs(stepSize / minStep)) - 1;
|
||
14 years ago
|
|
||
15 years ago
|
// Do the minor steps fit into the interval?
|
||
14 years ago
|
if ( QwtScaleArithmetic::compareEps((numTicks + 1) * qwtAbs(minStep),
|
||
|
qwtAbs(stepSize), stepSize) > 0) {
|
||
15 years ago
|
numTicks = 1;
|
||
|
minStep = stepSize * 0.5;
|
||
|
}
|
||
|
|
||
|
int medIndex = -1;
|
||
|
if ( numTicks % 2 )
|
||
|
medIndex = numTicks / 2;
|
||
|
|
||
|
// calculate minor ticks
|
||
|
|
||
14 years ago
|
for (int i = 0; i < (int)majorTicks.count(); i++) {
|
||
15 years ago
|
double val = majorTicks[i];
|
||
14 years ago
|
for (int k = 0; k < numTicks; k++) {
|
||
15 years ago
|
val += minStep;
|
||
|
|
||
|
double alignedValue = val;
|
||
14 years ago
|
if (QwtScaleArithmetic::compareEps(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
|
||
|
*/
|
||
|
QwtDoubleInterval QwtLinearScaleEngine::align(
|
||
|
const QwtDoubleInterval &interval, double stepSize) const
|
||
|
{
|
||
14 years ago
|
const double x1 =
|
||
15 years ago
|
QwtScaleArithmetic::floorEps(interval.minValue(), stepSize);
|
||
14 years ago
|
const double x2 =
|
||
15 years ago
|
QwtScaleArithmetic::ceilEps(interval.maxValue(), stepSize);
|
||
|
|
||
|
return QwtDoubleInterval(x1, x2);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Return a transformation, for logarithmic (base 10) scales
|
||
|
*/
|
||
|
QwtScaleTransformation *QwtLog10ScaleEngine::transformation() const
|
||
|
{
|
||
|
return new QwtScaleTransformation(QwtScaleTransformation::Log10);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
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
|
||
|
*/
|
||
14 years ago
|
void QwtLog10ScaleEngine::autoScale(int maxNumSteps,
|
||
|
double &x1, double &x2, double &stepSize) const
|
||
15 years ago
|
{
|
||
|
if ( x1 > x2 )
|
||
|
qSwap(x1, x2);
|
||
|
|
||
14 years ago
|
QwtDoubleInterval interval(x1 / pow(10.0, loMargin()),
|
||
|
x2 * pow(10.0, hiMargin()) );
|
||
15 years ago
|
|
||
|
double logRef = 1.0;
|
||
|
if (reference() > LOG_MIN / 2)
|
||
|
logRef = qwtMin(reference(), LOG_MAX / 2);
|
||
|
|
||
14 years ago
|
if (testAttribute(QwtScaleEngine::Symmetric)) {
|
||
|
const double delta = qwtMax(interval.maxValue() / logRef,
|
||
|
logRef / interval.minValue());
|
||
15 years ago
|
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(log10(interval).width(), qwtMax(maxNumSteps, 1));
|
||
|
if ( stepSize < 1.0 )
|
||
|
stepSize = 1.0;
|
||
|
|
||
|
if (!testAttribute(QwtScaleEngine::Floating))
|
||
|
interval = align(interval, stepSize);
|
||
|
|
||
|
x1 = interval.minValue();
|
||
|
x2 = interval.maxValue();
|
||
|
|
||
14 years ago
|
if (testAttribute(QwtScaleEngine::Inverted)) {
|
||
15 years ago
|
qSwap(x1, x2);
|
||
|
stepSize = -stepSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\brief Calculate a scale division
|
||
|
|
||
14 years ago
|
\param x1 First interval limit
|
||
|
\param x2 Second interval limit
|
||
15 years ago
|
\param maxMajSteps Maximum for the number of major steps
|
||
|
\param maxMinSteps Maximum number of minor steps
|
||
|
\param stepSize Step size. If stepSize == 0, the scaleEngine
|
||
|
calculates one.
|
||
|
|
||
|
\sa QwtScaleEngine::stepSize, QwtLog10ScaleEngine::subDivide
|
||
|
*/
|
||
|
QwtScaleDiv QwtLog10ScaleEngine::divideScale(double x1, double x2,
|
||
14 years ago
|
int maxMajSteps, int maxMinSteps, double stepSize) const
|
||
15 years ago
|
{
|
||
|
QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
|
||
|
interval = interval.limited(LOG_MIN, LOG_MAX);
|
||
|
|
||
|
if (interval.width() <= 0 )
|
||
|
return QwtScaleDiv();
|
||
|
|
||
14 years ago
|
if (interval.maxValue() / interval.minValue() < 10.0) {
|
||
15 years ago
|
// scale width is less than one decade -> build linear scale
|
||
14 years ago
|
|
||
15 years ago
|
QwtLinearScaleEngine linearScaler;
|
||
|
linearScaler.setAttributes(attributes());
|
||
|
linearScaler.setReference(reference());
|
||
|
linearScaler.setMargins(loMargin(), hiMargin());
|
||
|
|
||
14 years ago
|
return linearScaler.divideScale(x1, x2,
|
||
|
maxMajSteps, maxMinSteps, stepSize);
|
||
15 years ago
|
}
|
||
|
|
||
|
stepSize = qwtAbs(stepSize);
|
||
14 years ago
|
if ( stepSize == 0.0 ) {
|
||
15 years ago
|
if ( maxMajSteps < 1 )
|
||
|
maxMajSteps = 1;
|
||
|
|
||
|
stepSize = divideInterval(log10(interval).width(), maxMajSteps);
|
||
|
if ( stepSize < 1.0 )
|
||
|
stepSize = 1.0; // major step must be >= 1 decade
|
||
|
}
|
||
|
|
||
|
QwtScaleDiv scaleDiv;
|
||
14 years ago
|
if ( stepSize != 0.0 ) {
|
||
15 years ago
|
QwtValueList ticks[QwtScaleDiv::NTickTypes];
|
||
|
buildTicks(interval, stepSize, maxMinSteps, ticks);
|
||
|
|
||
|
scaleDiv = QwtScaleDiv(interval, ticks);
|
||
|
}
|
||
|
|
||
|
if ( x1 > x2 )
|
||
|
scaleDiv.invert();
|
||
|
|
||
|
return scaleDiv;
|
||
|
}
|
||
|
|
||
|
void QwtLog10ScaleEngine::buildTicks(
|
||
|
const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
|
||
|
QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
|
||
|
{
|
||
|
const QwtDoubleInterval boundingInterval =
|
||
|
align(interval, stepSize);
|
||
14 years ago
|
|
||
|
ticks[QwtScaleDiv::MajorTick] =
|
||
15 years ago
|
buildMajorTicks(boundingInterval, stepSize);
|
||
|
|
||
14 years ago
|
if ( maxMinSteps > 0 ) {
|
||
15 years ago
|
ticks[QwtScaleDiv::MinorTick] = buildMinorTicks(
|
||
14 years ago
|
ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize);
|
||
15 years ago
|
}
|
||
14 years ago
|
|
||
15 years ago
|
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
|
||
|
ticks[i] = strip(ticks[i], interval);
|
||
|
}
|
||
|
|
||
|
QwtValueList QwtLog10ScaleEngine::buildMajorTicks(
|
||
|
const QwtDoubleInterval &interval, double stepSize) const
|
||
|
{
|
||
|
double width = log10(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);
|
||
|
|
||
|
QwtValueList ticks;
|
||
|
|
||
|
ticks += interval.minValue();
|
||
|
|
||
|
for (int i = 1; i < numTicks; i++)
|
||
14 years ago
|
ticks += exp(lxmin + double(i) * lstep);
|
||
15 years ago
|
|
||
|
ticks += interval.maxValue();
|
||
|
|
||
|
return ticks;
|
||
|
}
|
||
|
|
||
|
QwtValueList QwtLog10ScaleEngine::buildMinorTicks(
|
||
14 years ago
|
const QwtValueList &majorTicks,
|
||
15 years ago
|
int maxMinSteps, double stepSize) const
|
||
14 years ago
|
{
|
||
|
if (stepSize < 1.1) { // major step width is one decade
|
||
15 years ago
|
if ( maxMinSteps < 1 )
|
||
|
return QwtValueList();
|
||
14 years ago
|
|
||
15 years ago
|
int k0, kstep, kmax;
|
||
14 years ago
|
|
||
|
if (maxMinSteps >= 8) {
|
||
15 years ago
|
k0 = 2;
|
||
|
kmax = 9;
|
||
|
kstep = 1;
|
||
14 years ago
|
} else if (maxMinSteps >= 4) {
|
||
15 years ago
|
k0 = 2;
|
||
|
kmax = 8;
|
||
|
kstep = 2;
|
||
14 years ago
|
} else if (maxMinSteps >= 2) {
|
||
15 years ago
|
k0 = 2;
|
||
|
kmax = 5;
|
||
|
kstep = 3;
|
||
14 years ago
|
} else {
|
||
15 years ago
|
k0 = 5;
|
||
|
kmax = 5;
|
||
|
kstep = 1;
|
||
|
}
|
||
|
|
||
|
QwtValueList minorTicks;
|
||
|
|
||
14 years ago
|
for (int i = 0; i < (int)majorTicks.count(); i++) {
|
||
15 years ago
|
const double v = majorTicks[i];
|
||
|
for (int k = k0; k<= kmax; k+=kstep)
|
||
|
minorTicks += v * double(k);
|
||
|
}
|
||
|
|
||
|
return minorTicks;
|
||
14 years ago
|
} else { // major step > one decade
|
||
15 years ago
|
double minStep = divideInterval(stepSize, maxMinSteps);
|
||
|
if ( minStep == 0.0 )
|
||
|
return QwtValueList();
|
||
|
|
||
|
if ( minStep < 1.0 )
|
||
|
minStep = 1.0;
|
||
|
|
||
|
// # subticks per interval
|
||
|
int nMin = qRound(stepSize / minStep) - 1;
|
||
|
|
||
|
// Do the minor steps fit into the interval?
|
||
|
|
||
14 years ago
|
if ( QwtScaleArithmetic::compareEps((nMin + 1) * minStep,
|
||
|
qwtAbs(stepSize), stepSize) > 0) {
|
||
15 years ago
|
nMin = 0;
|
||
|
}
|
||
|
|
||
|
if (nMin < 1)
|
||
|
return QwtValueList(); // no subticks
|
||
|
|
||
|
// substep factor = 10^substeps
|
||
|
const double minFactor = qwtMax(pow(10.0, minStep), 10.0);
|
||
|
|
||
|
QwtValueList minorTicks;
|
||
14 years ago
|
for (int i = 0; i < (int)majorTicks.count(); i++) {
|
||
15 years ago
|
double val = majorTicks[i];
|
||
14 years ago
|
for (int k=0; k< nMin; k++) {
|
||
15 years ago
|
val *= minFactor;
|
||
|
minorTicks += val;
|
||
|
}
|
||
|
}
|
||
|
return minorTicks;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\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
|
||
|
*/
|
||
|
QwtDoubleInterval QwtLog10ScaleEngine::align(
|
||
|
const QwtDoubleInterval &interval, double stepSize) const
|
||
|
{
|
||
|
const QwtDoubleInterval intv = log10(interval);
|
||
|
|
||
|
const double x1 = QwtScaleArithmetic::floorEps(intv.minValue(), stepSize);
|
||
|
const double x2 = QwtScaleArithmetic::ceilEps(intv.maxValue(), stepSize);
|
||
|
|
||
|
return pow10(QwtDoubleInterval(x1, x2));
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Return the interval [log10(interval.minValue(), log10(interval.maxValue]
|
||
|
*/
|
||
|
|
||
|
QwtDoubleInterval QwtLog10ScaleEngine::log10(
|
||
|
const QwtDoubleInterval &interval) const
|
||
|
{
|
||
|
return QwtDoubleInterval(::log10(interval.minValue()),
|
||
14 years ago
|
::log10(interval.maxValue()));
|
||
15 years ago
|
}
|
||
|
|
||
|
/*!
|
||
|
Return the interval [pow10(interval.minValue(), pow10(interval.maxValue]
|
||
|
*/
|
||
|
QwtDoubleInterval QwtLog10ScaleEngine::pow10(
|
||
|
const QwtDoubleInterval &interval) const
|
||
|
{
|
||
|
return QwtDoubleInterval(pow(10.0, interval.minValue()),
|
||
14 years ago
|
pow(10.0, interval.maxValue()));
|
||
15 years ago
|
}
|