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 #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
00521
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
00561 int nMin = qwtAbs(qRound(stepSize / minStep)) - 1;
00562
00563
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
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
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;
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)
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
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
00843 int nMin = qRound(stepSize / minStep) - 1;
00844
00845
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();
00855
00856
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 }