00001
00002
00003
00004
00005
00006
00007
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
00077
00078
00079
00080
00081
00082
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;
00160
00161 double loMargin;
00162 double hiMargin;
00163
00164 double referenceValue;
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
00514
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
00554 int nMin = qwtAbs(qRound(stepSize / minStep)) - 1;
00555
00556
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
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
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;
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)
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
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
00836 int nMin = qRound(stepSize / minStep) - 1;
00837
00838
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();
00848
00849
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 }