Skip to content

Commit

Permalink
Re #11422 Created files for a power (X^n) scale engine
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew D Jones authored and Matthew D Jones committed Aug 6, 2015
1 parent 373d50b commit 0d7ffb7
Show file tree
Hide file tree
Showing 2 changed files with 367 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#ifndef MANTIDQTAPI_MANTIDQWTPOWERSCALEENGINE_H
#define MANTIDQTAPI_MANTIDQWTPOWERSCALEENGINE_H
/**
Copyright © 2014 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://github.com/mantidproject/mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/


#include "qwt_global.h"
#include "qwt_scale_div.h"
#include "qwt_double_interval.h"
#include "qwt_scale_engine.h"

class QwtScaleTransformation;

class QWT_EXPORT MantidQwtPowerScaleEngine: public QwtScaleEngine
{
public:
virtual void autoScale(int maxSteps,
double &x1, double &x2, double &stepSize) const;

virtual QwtScaleDiv divideScale(double x1, double x2,
int numMajorSteps, int numMinorSteps,
double stepSize = 0.0) const;

virtual QwtScaleTransformation *transformation() const;

protected:
QwtDoubleInterval log10(const QwtDoubleInterval&) const;
QwtDoubleInterval pow10(const QwtDoubleInterval&) const;

private:
QwtDoubleInterval align(const QwtDoubleInterval&,
double stepSize) const;

void buildTicks(
const QwtDoubleInterval &, double stepSize, int maxMinSteps,
QwtValueList ticks[QwtScaleDiv::NTickTypes]) const;

QwtValueList buildMinorTicks(
const QwtValueList& majorTicks,
int maxMinMark, double step) const;

QwtValueList buildMajorTicks(
const QwtDoubleInterval &interval, double stepSize) const;
};

#endif
301 changes: 301 additions & 0 deletions Code/Mantid/MantidQt/API/src/MantidQwtPowerScaleEngine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
#include "qwt_math.h"
#include "qwt_scale_map.h"
#include "MantidQwtPowerScaleEngine.h"

/*!
Return a transformation, for logarithmic (base 10) scales
*/
QwtScaleTransformation *MantidQwtPowerScaleEngine::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()
*/
void MantidQwtPowerScaleEngine::autoScale(int maxNumSteps,
double &x1, double &x2, double &stepSize) const
{
if ( x1 > x2 )
qSwap(x1, x2);

QwtDoubleInterval interval(x1 / pow(10.0, lowerMargin()),
x2 * pow(10.0, upperMargin()) );

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

if (testAttribute(QwtScaleEngine::Symmetric))
{
const double delta = qwtMax(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(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();

if (testAttribute(QwtScaleEngine::Inverted))
{
qSwap(x1, x2);
stepSize = -stepSize;
}
}

/*!
\brief Calculate a scale division
\param x1 First interval limit
\param x2 Second interval limit
\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 MantidQwtPowerScaleEngine::divideScale(double x1, double x2,
int maxMajSteps, int maxMinSteps, double stepSize) const
{
QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
interval = interval.limited(LOG_MIN, LOG_MAX);

if (interval.width() <= 0 )
return QwtScaleDiv();

if (interval.maxValue() / interval.minValue() < 10.0)
{
// scale width is less than one decade -> build linear scale

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

return linearScaler.divideScale(x1, x2,
maxMajSteps, maxMinSteps, stepSize);
}

stepSize = qwtAbs(stepSize);
if ( stepSize == 0.0 )
{
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;
if ( stepSize != 0.0 )
{
QwtValueList ticks[QwtScaleDiv::NTickTypes];
buildTicks(interval, stepSize, maxMinSteps, ticks);

scaleDiv = QwtScaleDiv(interval, ticks);
}

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

return scaleDiv;
}

void MantidQwtPowerScaleEngine::buildTicks(
const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
{
const QwtDoubleInterval boundingInterval =
align(interval, stepSize);

ticks[QwtScaleDiv::MajorTick] =
buildMajorTicks(boundingInterval, stepSize);

if ( maxMinSteps > 0 )
{
ticks[QwtScaleDiv::MinorTick] = buildMinorTicks(
ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize);
}

for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
ticks[i] = strip(ticks[i], interval);
}

QwtValueList MantidQwtPowerScaleEngine::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++)
ticks += exp(lxmin + double(i) * lstep);

ticks += interval.maxValue();

return ticks;
}

QwtValueList MantidQwtPowerScaleEngine::buildMinorTicks(
const QwtValueList &majorTicks,
int maxMinSteps, double stepSize) const
{
if (stepSize < 1.1) // major step width is one decade
{
if ( maxMinSteps < 1 )
return QwtValueList();

int k0, kstep, kmax;

if (maxMinSteps >= 8)
{
k0 = 2;
kmax = 9;
kstep = 1;
}
else if (maxMinSteps >= 4)
{
k0 = 2;
kmax = 8;
kstep = 2;
}
else if (maxMinSteps >= 2)
{
k0 = 2;
kmax = 5;
kstep = 3;
}
else
{
k0 = 5;
kmax = 5;
kstep = 1;
}

QwtValueList minorTicks;

for (int i = 0; i < (int)majorTicks.count(); i++)
{
const double v = majorTicks[i];
for (int k = k0; k<= kmax; k+=kstep)
minorTicks += v * double(k);
}

return minorTicks;
}
else // major step > one decade
{
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?

if ( QwtScaleArithmetic::compareEps((nMin + 1) * minStep,
qwtAbs(stepSize), stepSize) > 0)
{
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;
for (int i = 0; i < (int)majorTicks.count(); i++)
{
double val = majorTicks[i];
for (int k=0; k< nMin; k++)
{
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 MantidQwtPowerScaleEngine::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 MantidQwtPowerScaleEngine::log10(
const QwtDoubleInterval &interval) const
{
return QwtDoubleInterval(::log10(interval.minValue()),
::log10(interval.maxValue()));
}

/*!
Return the interval [pow10(interval.minValue(), pow10(interval.maxValue]
*/
QwtDoubleInterval MantidQwtPowerScaleEngine::pow10(
const QwtDoubleInterval &interval) const
{
return QwtDoubleInterval(pow(10.0, interval.minValue()),
pow(10.0, interval.maxValue()));
}

0 comments on commit 0d7ffb7

Please sign in to comment.