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

Generated on Mon Feb 26 21:22:38 2007 for Qwt User's Guide by  doxygen 1.4.6