Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

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 QwtTickList QwtScaleEngine::strip( 
00281     const QwtTickList& ticks, 
00282     const QwtDoubleInterval &interval) const
00283 {
00284     if ( !interval.isValid() || ticks.count() == 0 )
00285         return QwtTickList();
00286 
00287     if ( contains(interval, ticks.first())
00288         && contains(interval, ticks.last()) )
00289     {
00290         return ticks;
00291     }
00292 
00293     QwtTickList 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 #if 1
00312     const double delta = (v == 0.0) ? 0.5 : qwtAbs(0.5 * v);
00313     return QwtDoubleInterval(v - delta, v + delta);
00314 #else
00315     if ( v == 0.0 )
00316         return QwtDoubleInterval(-0.5, 0.5);
00317 
00318     return QwtDoubleInterval(0.5 * v, 1.5 * v);
00319 #endif
00320 }
00321 
00346 void QwtScaleEngine::setAttribute(Attribute attribute, bool on)
00347 {
00348     if (on)
00349        d_data->attributes |= attribute;
00350     else
00351        d_data->attributes &= (~attribute);
00352 }
00353 
00360 bool QwtScaleEngine::testAttribute(Attribute attribute) const
00361 {
00362     return bool(d_data->attributes & attribute);
00363 }
00364 
00371 void QwtScaleEngine::setAttributes(int attributes)
00372 {
00373     d_data->attributes = attributes;
00374 }
00375 
00379 int QwtScaleEngine::attributes() const
00380 {
00381     return d_data->attributes;
00382 }
00383 
00391 void QwtScaleEngine::setReference(double r)
00392 {
00393     d_data->referenceValue = r;
00394 }
00395 
00400 double QwtScaleEngine::reference() const 
00401 { 
00402     return d_data->referenceValue; 
00403 }
00404 
00408 QwtScaleTransformation QwtLinearScaleEngine::transformation() const
00409 {
00410     return QwtScaleTransformation(QwtScaleTransformation::linearXForm, 
00411         QwtScaleTransformation::linearXForm);
00412 }
00413 
00424 void QwtLinearScaleEngine::autoScale(int maxNumSteps, 
00425     double &x1, double &x2, double &stepSize) const
00426 {
00427     QwtDoubleInterval interval(x1, x2);
00428     interval = interval.normalized();
00429 
00430     interval.setMinValue(interval.minValue() - loMargin());
00431     interval.setMaxValue(interval.maxValue() + hiMargin());
00432 
00433     if (testAttribute(QwtScaleEngine::Symmetric))
00434         interval = interval.symmetrize(reference());
00435  
00436     if (testAttribute(QwtScaleEngine::IncludeReference))
00437         interval = interval.extend(reference());
00438 
00439     if (interval.width() == 0.0)
00440         interval = buildInterval(interval.minValue());
00441 
00442     stepSize = divideInterval(interval.width(), qwtMax(maxNumSteps, 1));
00443 
00444     if ( !testAttribute(QwtScaleEngine::Floating) )
00445         interval = align(interval, stepSize);
00446 
00447     x1 = interval.minValue();
00448     x2 = interval.maxValue();
00449 
00450     if (testAttribute(QwtScaleEngine::Inverted))
00451     {
00452         qSwap(x1, x2);
00453         stepSize = -stepSize;
00454     }
00455 }
00456 
00469 QwtScaleDiv QwtLinearScaleEngine::divideScale(double x1, double x2,
00470     int maxMajSteps, int maxMinSteps, double stepSize) const
00471 {
00472     QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
00473     if (interval.width() <= 0 )
00474         return QwtScaleDiv();
00475 
00476     stepSize = qwtAbs(stepSize);
00477     if ( stepSize == 0.0 )
00478     {
00479         if ( maxMajSteps < 1 )
00480             maxMajSteps = 1;
00481 
00482         stepSize = divideInterval(interval.width(), maxMajSteps);
00483     }
00484 
00485     QwtScaleDiv scaleDiv;
00486 
00487     if ( stepSize != 0.0 )
00488     {
00489         QwtTickList ticks[QwtScaleDiv::NTickTypes];
00490         buildTicks(interval, stepSize, maxMinSteps, ticks);
00491 
00492         scaleDiv = QwtScaleDiv(interval, ticks);
00493     }
00494 
00495     if ( x1 > x2 )
00496         scaleDiv.invert();
00497 
00498     return scaleDiv;
00499 }
00500 
00501 void QwtLinearScaleEngine::buildTicks(
00502     const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
00503     QwtTickList ticks[QwtScaleDiv::NTickTypes]) const
00504 {
00505     const QwtDoubleInterval boundingInterval =
00506         align(interval, stepSize);
00507     
00508     ticks[QwtScaleDiv::MajorTick] = 
00509         buildMajorTicks(boundingInterval, stepSize);
00510 
00511     if ( maxMinSteps > 0 )
00512     {
00513         buildMinorTicks(ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize,
00514             ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick]);
00515     }
00516     
00517     for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
00518     {
00519         ticks[i] = strip(ticks[i], interval);
00520 
00521         // ticks very close to 0.0 are 
00522         // explicitely set to 0.0
00523 
00524         for ( int j = 0; j < (int)ticks[i].count(); j++ )
00525         {
00526             if ( QwtScaleArithmetic::compareEps(ticks[i][j], 0.0, stepSize) == 0 )
00527                 ticks[i][j] = 0.0;
00528         }
00529     }
00530 }
00531 
00532 QwtTickList QwtLinearScaleEngine::buildMajorTicks(
00533     const QwtDoubleInterval &interval, double stepSize) const
00534 {
00535     int numTicks = qRound(interval.width() / stepSize) + 1;
00536 #if 1
00537     if ( numTicks > 10000 )
00538         numTicks = 10000;
00539 #endif
00540 
00541     QwtTickList ticks;
00542 
00543     ticks += interval.minValue();
00544     for (int i = 1; i < numTicks - 1; i++)
00545         ticks += interval.minValue() + i * stepSize;
00546     ticks += interval.maxValue();
00547 
00548     return ticks;
00549 }
00550 
00551 void QwtLinearScaleEngine::buildMinorTicks(
00552     const QwtTickList& majorTicks,
00553     int maxMinSteps, double stepSize,
00554     QwtTickList &minorTicks, 
00555     QwtTickList &mediumTicks) const
00556 {   
00557     double minStep = divideInterval(stepSize, maxMinSteps);
00558     if (minStep == 0.0)  
00559         return; 
00560         
00561     // # minor steps per interval
00562     int nMin = qwtAbs(qRound(stepSize / minStep)) - 1;
00563     
00564     // Do the minor steps fit into the interval?
00565     if ( QwtScaleArithmetic::compareEps((nMin +  1) * qwtAbs(minStep), 
00566         qwtAbs(stepSize), stepSize) > 0)
00567     {   
00568         nMin = 1;
00569         minStep = stepSize * 0.5;
00570     }
00571 
00572     int medIndex = -1;
00573     if ( nMin % 2 )
00574         medIndex = nMin / 2;
00575 
00576     // calculate minor ticks
00577 
00578     for (int i = 0; i < (int)majorTicks.count(); i++)
00579     {
00580         double val = majorTicks[i];
00581         for (int k=0; k< nMin; k++)
00582         {
00583             val += minStep;
00584 
00585             double alignedValue = val;
00586             if (QwtScaleArithmetic::compareEps(val, 0.0, stepSize) == 0) 
00587                 alignedValue = 0.0;
00588 
00589             if ( k == medIndex )
00590                 mediumTicks += alignedValue;
00591             else
00592                 minorTicks += alignedValue;
00593         }
00594     }
00595 }
00596 
00608 QwtDoubleInterval QwtLinearScaleEngine::align(
00609     const QwtDoubleInterval &interval, double stepSize) const
00610 {
00611     const double x1 = 
00612         QwtScaleArithmetic::floorEps(interval.minValue(), stepSize);
00613     const double x2 = 
00614         QwtScaleArithmetic::ceilEps(interval.maxValue(), stepSize);
00615 
00616     return QwtDoubleInterval(x1, x2);
00617 }
00618 
00622 QwtScaleTransformation QwtLog10ScaleEngine::transformation() const
00623 {
00624     return QwtScaleTransformation(QwtScaleTransformation::log10XForm, 
00625         QwtScaleTransformation::log10InvXForm);
00626 }
00627 
00638 void QwtLog10ScaleEngine::autoScale(int maxNumSteps, 
00639     double &x1, double &x2, double &stepSize) const
00640 {
00641     if ( x1 > x2 )
00642         qSwap(x1, x2);
00643 
00644     QwtDoubleInterval interval(x1 / pow(10.0, loMargin()), 
00645         x2 * pow(10.0, hiMargin()) );
00646 
00647     double logRef = 1.0;
00648     if (reference() > LOG_MIN / 2)
00649         logRef = qwtMin(reference(), LOG_MAX / 2);
00650 
00651     if (testAttribute(QwtScaleEngine::Symmetric))
00652     {
00653         const double delta = qwtMax(interval.maxValue() / logRef,  
00654             logRef / interval.minValue());
00655         interval.setInterval(logRef / delta, logRef * delta);
00656     }
00657 
00658     if (testAttribute(QwtScaleEngine::IncludeReference))
00659         interval = interval.extend(logRef);
00660 
00661     interval = interval.limit(LOG_MIN, LOG_MAX);
00662 
00663     if (interval.width() == 0.0)
00664         interval = buildInterval(interval.minValue());
00665 
00666     stepSize = divideInterval(log10(interval).width(), qwtMax(maxNumSteps, 1));
00667     if ( stepSize < 1.0 )
00668         stepSize = 1.0;
00669 
00670     if (!testAttribute(QwtScaleEngine::Floating))
00671         interval = align(interval, stepSize);
00672 
00673     x1 = interval.minValue();
00674     x2 = interval.maxValue();
00675 
00676     if (testAttribute(QwtScaleEngine::Inverted))
00677     {
00678         qSwap(x1, x2);
00679         stepSize = -stepSize;
00680     }
00681 }
00682 
00695 QwtScaleDiv QwtLog10ScaleEngine::divideScale(double x1, double x2,
00696     int maxMajSteps, int maxMinSteps, double stepSize) const
00697 {
00698     QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
00699     interval = interval.limit(LOG_MIN, LOG_MAX);
00700 
00701     if (interval.width() <= 0 )
00702         return QwtScaleDiv();
00703 
00704     if (interval.maxValue() / interval.minValue() < 10.0)
00705     {
00706         // scale width is less than one decade -> build linear scale
00707     
00708         QwtLinearScaleEngine linearScaler;
00709         linearScaler.setAttributes(attributes());
00710         linearScaler.setReference(reference());
00711         linearScaler.setMargins(loMargin(), hiMargin());
00712 
00713         return linearScaler.divideScale(x1, x2, 
00714             maxMajSteps, maxMinSteps, stepSize);
00715     }
00716 
00717     stepSize = qwtAbs(stepSize);
00718     if ( stepSize == 0.0 )
00719     {
00720         if ( maxMajSteps < 1 )
00721             maxMajSteps = 1;
00722 
00723         stepSize = divideInterval(log10(interval).width(), maxMajSteps);
00724         if ( stepSize < 1.0 )
00725             stepSize = 1.0; // major step must be >= 1 decade
00726     }
00727 
00728     QwtScaleDiv scaleDiv;
00729     if ( stepSize != 0.0 )
00730     {
00731         QwtTickList ticks[QwtScaleDiv::NTickTypes];
00732         buildTicks(interval, stepSize, maxMinSteps, ticks);
00733 
00734         scaleDiv = QwtScaleDiv(interval, ticks);
00735     }
00736 
00737     if ( x1 > x2 )
00738         scaleDiv.invert();
00739 
00740     return scaleDiv;
00741 }
00742 
00743 void QwtLog10ScaleEngine::buildTicks(
00744     const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
00745     QwtTickList ticks[QwtScaleDiv::NTickTypes]) const
00746 {
00747     const QwtDoubleInterval boundingInterval =
00748         align(interval, stepSize);
00749     
00750     ticks[QwtScaleDiv::MajorTick] = 
00751         buildMajorTicks(boundingInterval, stepSize);
00752 
00753     if ( maxMinSteps > 0 )
00754     {
00755         ticks[QwtScaleDiv::MinorTick] = buildMinorTicks(
00756             ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize);
00757     }
00758     
00759     for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
00760         ticks[i] = strip(ticks[i], interval);
00761 }
00762 
00763 QwtTickList QwtLog10ScaleEngine::buildMajorTicks(
00764     const QwtDoubleInterval &interval, double stepSize) const
00765 {
00766     double width = log10(interval).width();
00767 
00768     int numTicks = qRound(width / stepSize) + 1;
00769     if ( numTicks > 10000 )
00770         numTicks = 10000;
00771 
00772     const double lxmin = log(interval.minValue());
00773     const double lxmax = log(interval.maxValue());
00774     const double lstep = (lxmax - lxmin) / double(numTicks - 1);
00775 
00776     QwtTickList ticks;
00777 
00778     ticks += interval.minValue();
00779 
00780     for (int i = 1; i < numTicks; i++)
00781        ticks += exp(lxmin + double(i) * lstep);
00782 
00783     ticks += interval.maxValue();
00784 
00785     return ticks;
00786 }
00787 
00788 QwtTickList QwtLog10ScaleEngine::buildMinorTicks(
00789     const QwtTickList &majorTicks, 
00790     int maxMinSteps, double stepSize) const
00791 {   
00792     if (stepSize < 1.1)            // major step width is one decade
00793     {
00794         if ( maxMinSteps < 1 )
00795             return QwtTickList();
00796             
00797         int k0, kstep, kmax;
00798         
00799         if (maxMinSteps >= 8)
00800         {
00801             k0 = 2;
00802             kmax = 9;
00803             kstep = 1;
00804         }   
00805         else if (maxMinSteps >= 4)
00806         {
00807             k0 = 2;
00808             kmax = 8;
00809             kstep = 2;
00810         }   
00811         else if (maxMinSteps >= 2)
00812         {
00813             k0 = 2;
00814             kmax = 5;
00815             kstep = 3;
00816         }
00817         else
00818         {
00819             k0 = 5;
00820             kmax = 5;
00821             kstep = 1;
00822         }
00823 
00824         QwtTickList minorTicks;
00825 
00826         for (int i = 0; i < (int)majorTicks.count(); i++)
00827         {
00828             const double v = majorTicks[i];
00829             for (int k = k0; k<= kmax; k+=kstep)
00830                 minorTicks += v * double(k);
00831         }
00832 
00833         return minorTicks;
00834     }
00835     else  // major step > one decade
00836     {
00837         double minStep = divideInterval(stepSize, maxMinSteps);
00838         if ( minStep == 0.0 )
00839             return QwtTickList();
00840 
00841         if ( minStep < 1.0 )
00842             minStep = 1.0;
00843 
00844         // # subticks per interval
00845         int nMin = qRound(stepSize / minStep) - 1;
00846 
00847         // Do the minor steps fit into the interval?
00848 
00849         if ( QwtScaleArithmetic::compareEps((nMin +  1) * minStep, 
00850             qwtAbs(stepSize), stepSize) > 0)
00851         {
00852             nMin = 0;
00853         }
00854 
00855         if (nMin < 1)
00856             return QwtTickList();      // no subticks
00857 
00858         // substep factor = 10^substeps
00859         const double minFactor = qwtMax(pow(10.0, minStep), 10.0);
00860 
00861         QwtTickList minorTicks;
00862         for (int i = 0; i < (int)majorTicks.count(); i++)
00863         {
00864             double val = majorTicks[i];
00865             for (int k=0; k< nMin; k++)
00866             {
00867                 val *= minFactor;
00868                 minorTicks += val;
00869             }
00870         }
00871         return minorTicks;
00872     }
00873 }
00874 
00886 QwtDoubleInterval QwtLog10ScaleEngine::align(
00887     const QwtDoubleInterval &interval, double stepSize) const
00888 {
00889     const QwtDoubleInterval intv = log10(interval);
00890 
00891     const double x1 = QwtScaleArithmetic::floorEps(intv.minValue(), stepSize);
00892     const double x2 = QwtScaleArithmetic::ceilEps(intv.maxValue(), stepSize);
00893 
00894     return pow10(QwtDoubleInterval(x1, x2));
00895 }
00896 
00901 QwtDoubleInterval QwtLog10ScaleEngine::log10(
00902     const QwtDoubleInterval &interval) const
00903 {
00904     return QwtDoubleInterval(::log10(interval.minValue()),
00905             ::log10(interval.maxValue()));
00906 }
00907 
00911 QwtDoubleInterval QwtLog10ScaleEngine::pow10(
00912     const QwtDoubleInterval &interval) const
00913 {
00914     return QwtDoubleInterval(pow(10.0, interval.minValue()),
00915             pow(10.0, interval.maxValue()));
00916 }

Generated on Mon Jan 30 22:16:26 2006 for Qwt User's Guide by  doxygen 1.4.4