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