qwt_scale_engine.cpp

00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  * 
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
00008  *****************************************************************************/
00009 
00010 #include "qwt_math.h"
00011 #include "qwt_scale_map.h"
00012 #include "qwt_scale_engine.h"
00013 
00014 static const double _eps = 1.0e-6;
00015 
00028 int QwtScaleArithmetic::compareEps(double value1, double value2, 
00029     double intervalSize) 
00030 {
00031     const double eps = qwtAbs(_eps * intervalSize);
00032 
00033     if ( value2 - value1 > eps )
00034         return -1;
00035 
00036     if ( value1 - value2 > eps )
00037         return 1;
00038 
00039     return 0;
00040 }
00041 
00050 double QwtScaleArithmetic::ceilEps(double value, 
00051     double intervalSize) 
00052 {
00053     const double eps = _eps * intervalSize;
00054 
00055     value = (value - eps) / intervalSize;
00056     return ceil(value) * intervalSize;
00057 }
00058 
00067 double QwtScaleArithmetic::floorEps(double value, double intervalSize) 
00068 {
00069     const double eps = _eps * intervalSize;
00070 
00071     value = (value + eps) / intervalSize;
00072     return floor(value) * intervalSize;
00073 }
00074 
00075 /*
00076   \brief Divide an interval into steps
00077 
00078   \f$stepSize = (intervalSize - intervalSize * 10e^{-6}) / numSteps\f$
00079 
00080   \param intervalSize Interval size
00081   \param numSteps Number of steps
00082   \return Step size
00083 */
00084 double QwtScaleArithmetic::divideEps(double intervalSize, double numSteps) 
00085 {
00086     if ( numSteps == 0.0 || intervalSize == 0.0 )
00087         return 0.0;
00088 
00089     return (intervalSize - (_eps * intervalSize)) / numSteps;
00090 } 
00091 
00098 double QwtScaleArithmetic::ceil125(double x) 
00099 {
00100     if (x == 0.0) 
00101         return 0.0;
00102 
00103     const double sign = (x > 0) ? 1.0 : -1.0;
00104     const double lx = log10(fabs(x));
00105     const double p10 = floor(lx);
00106     
00107     double fr = pow(10.0, lx - p10);
00108     if (fr <=1.0)
00109        fr = 1.0; 
00110     else if (fr <= 2.0)
00111        fr = 2.0;
00112     else if (fr <= 5.0) 
00113        fr = 5.0;
00114     else
00115        fr = 10.0;
00116 
00117     return sign * fr * pow(10.0, p10);
00118 }
00119 
00126 double QwtScaleArithmetic::floor125(double x) 
00127 {
00128     if (x == 0.0)
00129         return 0.0;
00130 
00131     double sign = (x > 0) ? 1.0 : -1.0;
00132     const double lx = log10(fabs(x));
00133     const double p10 = floor(lx);
00134 
00135     double fr = pow(10.0, lx - p10);
00136     if (fr >= 10.0)
00137        fr = 10.0;
00138     else if (fr >= 5.0)
00139        fr = 5.0;
00140     else if (fr >= 2.0)
00141        fr = 2.0;
00142     else
00143        fr = 1.0;
00144 
00145     return sign * fr * pow(10.0, p10);
00146 }
00147 
00148 class QwtScaleEngine::PrivateData
00149 {
00150 public:
00151     PrivateData():
00152         attributes(QwtScaleEngine::NoAttribute),
00153         loMargin(0.0),
00154         hiMargin(0.0),
00155         referenceValue(0.0)
00156     {
00157     }
00158 
00159     int attributes;       // scale attributes
00160 
00161     double loMargin;      // margins
00162     double hiMargin;
00163 
00164     double referenceValue; // reference value
00165 
00166 };
00167 
00169 QwtScaleEngine::QwtScaleEngine()
00170 {
00171     d_data = new PrivateData;
00172 }
00173 
00174 
00176 QwtScaleEngine::~QwtScaleEngine ()
00177 {
00178     delete d_data;
00179 }
00180 
00187 double QwtScaleEngine::loMargin() const 
00188 { 
00189     return d_data->loMargin; 
00190 }
00191 
00198 double QwtScaleEngine::hiMargin() const 
00199 { 
00200     return d_data->hiMargin; 
00201 }
00202 
00219 void QwtScaleEngine::setMargins(double mlo, double mhi)
00220 {
00221     d_data->loMargin = qwtMax(mlo,0.0);
00222     d_data->hiMargin = qwtMax(mhi,0.0);
00223 }
00224 
00233 double QwtScaleEngine::divideInterval(
00234     double intervalSize, int numSteps) const
00235 {
00236     if ( numSteps <= 0 )
00237         return 0.0;
00238 
00239     double v = QwtScaleArithmetic::divideEps(intervalSize, numSteps);
00240     return QwtScaleArithmetic::ceil125(v);
00241 }
00242 
00251 bool QwtScaleEngine::contains(
00252     const QwtDoubleInterval &interval, double value) const
00253 {
00254     if (!interval.isValid() )
00255         return false;
00256     
00257     if ( QwtScaleArithmetic::compareEps(value, 
00258         interval.minValue(), interval.width()) < 0 )
00259     {
00260         return false;
00261     }
00262 
00263     if ( QwtScaleArithmetic::compareEps(value, 
00264         interval.maxValue(), interval.width()) > 0 )
00265     {
00266         return false;
00267     }
00268 
00269     return true;
00270 }
00271 
00280 QwtValueList QwtScaleEngine::strip( 
00281     const QwtValueList& ticks, 
00282     const QwtDoubleInterval &interval) const
00283 {
00284     if ( !interval.isValid() || ticks.count() == 0 )
00285         return QwtValueList();
00286 
00287     if ( contains(interval, ticks.first())
00288         && contains(interval, ticks.last()) )
00289     {
00290         return ticks;
00291     }
00292 
00293     QwtValueList strippedTicks;
00294     for ( int i = 0; i < (int)ticks.count(); i++ )
00295     {
00296         if ( contains(interval, ticks[i]) )
00297             strippedTicks += ticks[i];
00298     }
00299     return strippedTicks;
00300 }
00301 
00309 QwtDoubleInterval QwtScaleEngine::buildInterval(double v) const
00310 {
00311     const double delta = (v == 0.0) ? 0.5 : qwtAbs(0.5 * v);
00312     return QwtDoubleInterval(v - delta, v + delta);
00313 }
00314 
00339 void QwtScaleEngine::setAttribute(Attribute attribute, bool on)
00340 {
00341     if (on)
00342        d_data->attributes |= attribute;
00343     else
00344        d_data->attributes &= (~attribute);
00345 }
00346 
00353 bool QwtScaleEngine::testAttribute(Attribute attribute) const
00354 {
00355     return bool(d_data->attributes & attribute);
00356 }
00357 
00364 void QwtScaleEngine::setAttributes(int attributes)
00365 {
00366     d_data->attributes = attributes;
00367 }
00368 
00372 int QwtScaleEngine::attributes() const
00373 {
00374     return d_data->attributes;
00375 }
00376 
00384 void QwtScaleEngine::setReference(double r)
00385 {
00386     d_data->referenceValue = r;
00387 }
00388 
00393 double QwtScaleEngine::reference() const 
00394 { 
00395     return d_data->referenceValue; 
00396 }
00397 
00401 QwtScaleTransformation *QwtLinearScaleEngine::transformation() const
00402 {
00403     return new QwtScaleTransformation(QwtScaleTransformation::Linear);
00404 }
00405 
00416 void QwtLinearScaleEngine::autoScale(int maxNumSteps, 
00417     double &x1, double &x2, double &stepSize) const
00418 {
00419     QwtDoubleInterval interval(x1, x2);
00420     interval = interval.normalized();
00421 
00422     interval.setMinValue(interval.minValue() - loMargin());
00423     interval.setMaxValue(interval.maxValue() + hiMargin());
00424 
00425     if (testAttribute(QwtScaleEngine::Symmetric))
00426         interval = interval.symmetrize(reference());
00427  
00428     if (testAttribute(QwtScaleEngine::IncludeReference))
00429         interval = interval.extend(reference());
00430 
00431     if (interval.width() == 0.0)
00432         interval = buildInterval(interval.minValue());
00433 
00434     stepSize = divideInterval(interval.width(), qwtMax(maxNumSteps, 1));
00435 
00436     if ( !testAttribute(QwtScaleEngine::Floating) )
00437         interval = align(interval, stepSize);
00438 
00439     x1 = interval.minValue();
00440     x2 = interval.maxValue();
00441 
00442     if (testAttribute(QwtScaleEngine::Inverted))
00443     {
00444         qSwap(x1, x2);
00445         stepSize = -stepSize;
00446     }
00447 }
00448 
00461 QwtScaleDiv QwtLinearScaleEngine::divideScale(double x1, double x2,
00462     int maxMajSteps, int maxMinSteps, double stepSize) const
00463 {
00464     QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
00465     if (interval.width() <= 0 )
00466         return QwtScaleDiv();
00467 
00468     stepSize = qwtAbs(stepSize);
00469     if ( stepSize == 0.0 )
00470     {
00471         if ( maxMajSteps < 1 )
00472             maxMajSteps = 1;
00473 
00474         stepSize = divideInterval(interval.width(), maxMajSteps);
00475     }
00476 
00477     QwtScaleDiv scaleDiv;
00478 
00479     if ( stepSize != 0.0 )
00480     {
00481         QwtValueList ticks[QwtScaleDiv::NTickTypes];
00482         buildTicks(interval, stepSize, maxMinSteps, ticks);
00483 
00484         scaleDiv = QwtScaleDiv(interval, ticks);
00485     }
00486 
00487     if ( x1 > x2 )
00488         scaleDiv.invert();
00489 
00490     return scaleDiv;
00491 }
00492 
00493 void QwtLinearScaleEngine::buildTicks(
00494     const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
00495     QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
00496 {
00497     const QwtDoubleInterval boundingInterval =
00498         align(interval, stepSize);
00499     
00500     ticks[QwtScaleDiv::MajorTick] = 
00501         buildMajorTicks(boundingInterval, stepSize);
00502 
00503     if ( maxMinSteps > 0 )
00504     {
00505         buildMinorTicks(ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize,
00506             ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick]);
00507     }
00508     
00509     for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
00510     {
00511         ticks[i] = strip(ticks[i], interval);
00512 
00513         // ticks very close to 0.0 are 
00514         // explicitely set to 0.0
00515 
00516         for ( int j = 0; j < (int)ticks[i].count(); j++ )
00517         {
00518             if ( QwtScaleArithmetic::compareEps(ticks[i][j], 0.0, stepSize) == 0 )
00519                 ticks[i][j] = 0.0;
00520         }
00521     }
00522 }
00523 
00524 QwtValueList QwtLinearScaleEngine::buildMajorTicks(
00525     const QwtDoubleInterval &interval, double stepSize) const
00526 {
00527     int numTicks = qRound(interval.width() / stepSize) + 1;
00528 #if 1
00529     if ( numTicks > 10000 )
00530         numTicks = 10000;
00531 #endif
00532 
00533     QwtValueList ticks;
00534 
00535     ticks += interval.minValue();
00536     for (int i = 1; i < numTicks - 1; i++)
00537         ticks += interval.minValue() + i * stepSize;
00538     ticks += interval.maxValue();
00539 
00540     return ticks;
00541 }
00542 
00543 void QwtLinearScaleEngine::buildMinorTicks(
00544     const QwtValueList& majorTicks,
00545     int maxMinSteps, double stepSize,
00546     QwtValueList &minorTicks, 
00547     QwtValueList &mediumTicks) const
00548 {   
00549     double minStep = divideInterval(stepSize, maxMinSteps);
00550     if (minStep == 0.0)  
00551         return; 
00552         
00553     // # minor steps per interval
00554     int nMin = qwtAbs(qRound(stepSize / minStep)) - 1;
00555     
00556     // Do the minor steps fit into the interval?
00557     if ( QwtScaleArithmetic::compareEps((nMin +  1) * qwtAbs(minStep), 
00558         qwtAbs(stepSize), stepSize) > 0)
00559     {   
00560         nMin = 1;
00561         minStep = stepSize * 0.5;
00562     }
00563 
00564     int medIndex = -1;
00565     if ( nMin % 2 )
00566         medIndex = nMin / 2;
00567 
00568     // calculate minor ticks
00569 
00570     for (int i = 0; i < (int)majorTicks.count(); i++)
00571     {
00572         double val = majorTicks[i];
00573         for (int k=0; k< nMin; k++)
00574         {
00575             val += minStep;
00576 
00577             double alignedValue = val;
00578             if (QwtScaleArithmetic::compareEps(val, 0.0, stepSize) == 0) 
00579                 alignedValue = 0.0;
00580 
00581             if ( k == medIndex )
00582                 mediumTicks += alignedValue;
00583             else
00584                 minorTicks += alignedValue;
00585         }
00586     }
00587 }
00588 
00600 QwtDoubleInterval QwtLinearScaleEngine::align(
00601     const QwtDoubleInterval &interval, double stepSize) const
00602 {
00603     const double x1 = 
00604         QwtScaleArithmetic::floorEps(interval.minValue(), stepSize);
00605     const double x2 = 
00606         QwtScaleArithmetic::ceilEps(interval.maxValue(), stepSize);
00607 
00608     return QwtDoubleInterval(x1, x2);
00609 }
00610 
00614 QwtScaleTransformation *QwtLog10ScaleEngine::transformation() const
00615 {
00616     return new QwtScaleTransformation(QwtScaleTransformation::Log10);
00617 }
00618 
00629 void QwtLog10ScaleEngine::autoScale(int maxNumSteps, 
00630     double &x1, double &x2, double &stepSize) const
00631 {
00632     if ( x1 > x2 )
00633         qSwap(x1, x2);
00634 
00635     QwtDoubleInterval interval(x1 / pow(10.0, loMargin()), 
00636         x2 * pow(10.0, hiMargin()) );
00637 
00638     double logRef = 1.0;
00639     if (reference() > LOG_MIN / 2)
00640         logRef = qwtMin(reference(), LOG_MAX / 2);
00641 
00642     if (testAttribute(QwtScaleEngine::Symmetric))
00643     {
00644         const double delta = qwtMax(interval.maxValue() / logRef,  
00645             logRef / interval.minValue());
00646         interval.setInterval(logRef / delta, logRef * delta);
00647     }
00648 
00649     if (testAttribute(QwtScaleEngine::IncludeReference))
00650         interval = interval.extend(logRef);
00651 
00652     interval = interval.limited(LOG_MIN, LOG_MAX);
00653 
00654     if (interval.width() == 0.0)
00655         interval = buildInterval(interval.minValue());
00656 
00657     stepSize = divideInterval(log10(interval).width(), qwtMax(maxNumSteps, 1));
00658     if ( stepSize < 1.0 )
00659         stepSize = 1.0;
00660 
00661     if (!testAttribute(QwtScaleEngine::Floating))
00662         interval = align(interval, stepSize);
00663 
00664     x1 = interval.minValue();
00665     x2 = interval.maxValue();
00666 
00667     if (testAttribute(QwtScaleEngine::Inverted))
00668     {
00669         qSwap(x1, x2);
00670         stepSize = -stepSize;
00671     }
00672 }
00673 
00686 QwtScaleDiv QwtLog10ScaleEngine::divideScale(double x1, double x2,
00687     int maxMajSteps, int maxMinSteps, double stepSize) const
00688 {
00689     QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
00690     interval = interval.limited(LOG_MIN, LOG_MAX);
00691 
00692     if (interval.width() <= 0 )
00693         return QwtScaleDiv();
00694 
00695     if (interval.maxValue() / interval.minValue() < 10.0)
00696     {
00697         // scale width is less than one decade -> build linear scale
00698     
00699         QwtLinearScaleEngine linearScaler;
00700         linearScaler.setAttributes(attributes());
00701         linearScaler.setReference(reference());
00702         linearScaler.setMargins(loMargin(), hiMargin());
00703 
00704         return linearScaler.divideScale(x1, x2, 
00705             maxMajSteps, maxMinSteps, stepSize);
00706     }
00707 
00708     stepSize = qwtAbs(stepSize);
00709     if ( stepSize == 0.0 )
00710     {
00711         if ( maxMajSteps < 1 )
00712             maxMajSteps = 1;
00713 
00714         stepSize = divideInterval(log10(interval).width(), maxMajSteps);
00715         if ( stepSize < 1.0 )
00716             stepSize = 1.0; // major step must be >= 1 decade
00717     }
00718 
00719     QwtScaleDiv scaleDiv;
00720     if ( stepSize != 0.0 )
00721     {
00722         QwtValueList ticks[QwtScaleDiv::NTickTypes];
00723         buildTicks(interval, stepSize, maxMinSteps, ticks);
00724 
00725         scaleDiv = QwtScaleDiv(interval, ticks);
00726     }
00727 
00728     if ( x1 > x2 )
00729         scaleDiv.invert();
00730 
00731     return scaleDiv;
00732 }
00733 
00734 void QwtLog10ScaleEngine::buildTicks(
00735     const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
00736     QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
00737 {
00738     const QwtDoubleInterval boundingInterval =
00739         align(interval, stepSize);
00740     
00741     ticks[QwtScaleDiv::MajorTick] = 
00742         buildMajorTicks(boundingInterval, stepSize);
00743 
00744     if ( maxMinSteps > 0 )
00745     {
00746         ticks[QwtScaleDiv::MinorTick] = buildMinorTicks(
00747             ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize);
00748     }
00749     
00750     for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
00751         ticks[i] = strip(ticks[i], interval);
00752 }
00753 
00754 QwtValueList QwtLog10ScaleEngine::buildMajorTicks(
00755     const QwtDoubleInterval &interval, double stepSize) const
00756 {
00757     double width = log10(interval).width();
00758 
00759     int numTicks = qRound(width / stepSize) + 1;
00760     if ( numTicks > 10000 )
00761         numTicks = 10000;
00762 
00763     const double lxmin = log(interval.minValue());
00764     const double lxmax = log(interval.maxValue());
00765     const double lstep = (lxmax - lxmin) / double(numTicks - 1);
00766 
00767     QwtValueList ticks;
00768 
00769     ticks += interval.minValue();
00770 
00771     for (int i = 1; i < numTicks; i++)
00772        ticks += exp(lxmin + double(i) * lstep);
00773 
00774     ticks += interval.maxValue();
00775 
00776     return ticks;
00777 }
00778 
00779 QwtValueList QwtLog10ScaleEngine::buildMinorTicks(
00780     const QwtValueList &majorTicks, 
00781     int maxMinSteps, double stepSize) const
00782 {   
00783     if (stepSize < 1.1)            // major step width is one decade
00784     {
00785         if ( maxMinSteps < 1 )
00786             return QwtValueList();
00787             
00788         int k0, kstep, kmax;
00789         
00790         if (maxMinSteps >= 8)
00791         {
00792             k0 = 2;
00793             kmax = 9;
00794             kstep = 1;
00795         }   
00796         else if (maxMinSteps >= 4)
00797         {
00798             k0 = 2;
00799             kmax = 8;
00800             kstep = 2;
00801         }   
00802         else if (maxMinSteps >= 2)
00803         {
00804             k0 = 2;
00805             kmax = 5;
00806             kstep = 3;
00807         }
00808         else
00809         {
00810             k0 = 5;
00811             kmax = 5;
00812             kstep = 1;
00813         }
00814 
00815         QwtValueList minorTicks;
00816 
00817         for (int i = 0; i < (int)majorTicks.count(); i++)
00818         {
00819             const double v = majorTicks[i];
00820             for (int k = k0; k<= kmax; k+=kstep)
00821                 minorTicks += v * double(k);
00822         }
00823 
00824         return minorTicks;
00825     }
00826     else  // major step > one decade
00827     {
00828         double minStep = divideInterval(stepSize, maxMinSteps);
00829         if ( minStep == 0.0 )
00830             return QwtValueList();
00831 
00832         if ( minStep < 1.0 )
00833             minStep = 1.0;
00834 
00835         // # subticks per interval
00836         int nMin = qRound(stepSize / minStep) - 1;
00837 
00838         // Do the minor steps fit into the interval?
00839 
00840         if ( QwtScaleArithmetic::compareEps((nMin +  1) * minStep, 
00841             qwtAbs(stepSize), stepSize) > 0)
00842         {
00843             nMin = 0;
00844         }
00845 
00846         if (nMin < 1)
00847             return QwtValueList();      // no subticks
00848 
00849         // substep factor = 10^substeps
00850         const double minFactor = qwtMax(pow(10.0, minStep), 10.0);
00851 
00852         QwtValueList minorTicks;
00853         for (int i = 0; i < (int)majorTicks.count(); i++)
00854         {
00855             double val = majorTicks[i];
00856             for (int k=0; k< nMin; k++)
00857             {
00858                 val *= minFactor;
00859                 minorTicks += val;
00860             }
00861         }
00862         return minorTicks;
00863     }
00864 }
00865 
00877 QwtDoubleInterval QwtLog10ScaleEngine::align(
00878     const QwtDoubleInterval &interval, double stepSize) const
00879 {
00880     const QwtDoubleInterval intv = log10(interval);
00881 
00882     const double x1 = QwtScaleArithmetic::floorEps(intv.minValue(), stepSize);
00883     const double x2 = QwtScaleArithmetic::ceilEps(intv.maxValue(), stepSize);
00884 
00885     return pow10(QwtDoubleInterval(x1, x2));
00886 }
00887 
00892 QwtDoubleInterval QwtLog10ScaleEngine::log10(
00893     const QwtDoubleInterval &interval) const
00894 {
00895     return QwtDoubleInterval(::log10(interval.minValue()),
00896             ::log10(interval.maxValue()));
00897 }
00898 
00902 QwtDoubleInterval QwtLog10ScaleEngine::pow10(
00903     const QwtDoubleInterval &interval) const
00904 {
00905     return QwtDoubleInterval(pow(10.0, interval.minValue()),
00906             pow(10.0, interval.maxValue()));
00907 }

Generated on Mon Jun 11 07:41:39 2007 for Qwt User's Guide by  doxygen 1.4.6