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 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
00522
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
00562 int nMin = qwtAbs(qRound(stepSize / minStep)) - 1;
00563
00564
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
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
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;
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)
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
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
00845 int nMin = qRound(stepSize / minStep) - 1;
00846
00847
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();
00857
00858
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 }