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
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 lowerMargin(0.0),
00154 upperMargin(0.0),
00155 referenceValue(0.0)
00156 {
00157 }
00158
00159 int attributes;
00160
00161 double lowerMargin;
00162 double upperMargin;
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::lowerMargin() const
00188 {
00189 return d_data->lowerMargin;
00190 }
00191
00198 double QwtScaleEngine::upperMargin() const
00199 {
00200 return d_data->upperMargin;
00201 }
00202
00219 void QwtScaleEngine::setMargins(double lower, double upper)
00220 {
00221 d_data->lowerMargin = qwtMax(lower, 0.0);
00222 d_data->upperMargin = qwtMax(upper, 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
00323 void QwtScaleEngine::setAttribute(Attribute attribute, bool on)
00324 {
00325 if (on)
00326 d_data->attributes |= attribute;
00327 else
00328 d_data->attributes &= (~attribute);
00329 }
00330
00337 bool QwtScaleEngine::testAttribute(Attribute attribute) const
00338 {
00339 return bool(d_data->attributes & attribute);
00340 }
00341
00348 void QwtScaleEngine::setAttributes(int attributes)
00349 {
00350 d_data->attributes = attributes;
00351 }
00352
00357 int QwtScaleEngine::attributes() const
00358 {
00359 return d_data->attributes;
00360 }
00361
00371 void QwtScaleEngine::setReference(double r)
00372 {
00373 d_data->referenceValue = r;
00374 }
00375
00380 double QwtScaleEngine::reference() const
00381 {
00382 return d_data->referenceValue;
00383 }
00384
00388 QwtScaleTransformation *QwtLinearScaleEngine::transformation() const
00389 {
00390 return new QwtScaleTransformation(QwtScaleTransformation::Linear);
00391 }
00392
00403 void QwtLinearScaleEngine::autoScale(int maxNumSteps,
00404 double &x1, double &x2, double &stepSize) const
00405 {
00406 QwtDoubleInterval interval(x1, x2);
00407 interval = interval.normalized();
00408
00409 interval.setMinValue(interval.minValue() - lowerMargin());
00410 interval.setMaxValue(interval.maxValue() + upperMargin());
00411
00412 if (testAttribute(QwtScaleEngine::Symmetric))
00413 interval = interval.symmetrize(reference());
00414
00415 if (testAttribute(QwtScaleEngine::IncludeReference))
00416 interval = interval.extend(reference());
00417
00418 if (interval.width() == 0.0)
00419 interval = buildInterval(interval.minValue());
00420
00421 stepSize = divideInterval(interval.width(), qwtMax(maxNumSteps, 1));
00422
00423 if ( !testAttribute(QwtScaleEngine::Floating) )
00424 interval = align(interval, stepSize);
00425
00426 x1 = interval.minValue();
00427 x2 = interval.maxValue();
00428
00429 if (testAttribute(QwtScaleEngine::Inverted))
00430 {
00431 qSwap(x1, x2);
00432 stepSize = -stepSize;
00433 }
00434 }
00435
00448 QwtScaleDiv QwtLinearScaleEngine::divideScale(double x1, double x2,
00449 int maxMajSteps, int maxMinSteps, double stepSize) const
00450 {
00451 QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
00452 if (interval.width() <= 0 )
00453 return QwtScaleDiv();
00454
00455 stepSize = qwtAbs(stepSize);
00456 if ( stepSize == 0.0 )
00457 {
00458 if ( maxMajSteps < 1 )
00459 maxMajSteps = 1;
00460
00461 stepSize = divideInterval(interval.width(), maxMajSteps);
00462 }
00463
00464 QwtScaleDiv scaleDiv;
00465
00466 if ( stepSize != 0.0 )
00467 {
00468 QwtValueList ticks[QwtScaleDiv::NTickTypes];
00469 buildTicks(interval, stepSize, maxMinSteps, ticks);
00470
00471 scaleDiv = QwtScaleDiv(interval, ticks);
00472 }
00473
00474 if ( x1 > x2 )
00475 scaleDiv.invert();
00476
00477 return scaleDiv;
00478 }
00479
00480 void QwtLinearScaleEngine::buildTicks(
00481 const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
00482 QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
00483 {
00484 const QwtDoubleInterval boundingInterval =
00485 align(interval, stepSize);
00486
00487 ticks[QwtScaleDiv::MajorTick] =
00488 buildMajorTicks(boundingInterval, stepSize);
00489
00490 if ( maxMinSteps > 0 )
00491 {
00492 buildMinorTicks(ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize,
00493 ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick]);
00494 }
00495
00496 for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
00497 {
00498 ticks[i] = strip(ticks[i], interval);
00499
00500
00501
00502
00503 for ( int j = 0; j < (int)ticks[i].count(); j++ )
00504 {
00505 if ( QwtScaleArithmetic::compareEps(ticks[i][j], 0.0, stepSize) == 0 )
00506 ticks[i][j] = 0.0;
00507 }
00508 }
00509 }
00510
00511 QwtValueList QwtLinearScaleEngine::buildMajorTicks(
00512 const QwtDoubleInterval &interval, double stepSize) const
00513 {
00514 int numTicks = qRound(interval.width() / stepSize) + 1;
00515 if ( numTicks > 10000 )
00516 numTicks = 10000;
00517
00518 QwtValueList ticks;
00519
00520 ticks += interval.minValue();
00521 for (int i = 1; i < numTicks - 1; i++)
00522 ticks += interval.minValue() + i * stepSize;
00523 ticks += interval.maxValue();
00524
00525 return ticks;
00526 }
00527
00528 void QwtLinearScaleEngine::buildMinorTicks(
00529 const QwtValueList& majorTicks,
00530 int maxMinSteps, double stepSize,
00531 QwtValueList &minorTicks,
00532 QwtValueList &mediumTicks) const
00533 {
00534 double minStep = divideInterval(stepSize, maxMinSteps);
00535 if (minStep == 0.0)
00536 return;
00537
00538
00539 int numTicks = (int)::ceil(qwtAbs(stepSize / minStep)) - 1;
00540
00541
00542 if ( QwtScaleArithmetic::compareEps((numTicks + 1) * qwtAbs(minStep),
00543 qwtAbs(stepSize), stepSize) > 0)
00544 {
00545 numTicks = 1;
00546 minStep = stepSize * 0.5;
00547 }
00548
00549 int medIndex = -1;
00550 if ( numTicks % 2 )
00551 medIndex = numTicks / 2;
00552
00553
00554
00555 for (int i = 0; i < (int)majorTicks.count(); i++)
00556 {
00557 double val = majorTicks[i];
00558 for (int k = 0; k < numTicks; k++)
00559 {
00560 val += minStep;
00561
00562 double alignedValue = val;
00563 if (QwtScaleArithmetic::compareEps(val, 0.0, stepSize) == 0)
00564 alignedValue = 0.0;
00565
00566 if ( k == medIndex )
00567 mediumTicks += alignedValue;
00568 else
00569 minorTicks += alignedValue;
00570 }
00571 }
00572 }
00573
00585 QwtDoubleInterval QwtLinearScaleEngine::align(
00586 const QwtDoubleInterval &interval, double stepSize) const
00587 {
00588 const double x1 =
00589 QwtScaleArithmetic::floorEps(interval.minValue(), stepSize);
00590 const double x2 =
00591 QwtScaleArithmetic::ceilEps(interval.maxValue(), stepSize);
00592
00593 return QwtDoubleInterval(x1, x2);
00594 }
00595
00599 QwtScaleTransformation *QwtLog10ScaleEngine::transformation() const
00600 {
00601 return new QwtScaleTransformation(QwtScaleTransformation::Log10);
00602 }
00603
00614 void QwtLog10ScaleEngine::autoScale(int maxNumSteps,
00615 double &x1, double &x2, double &stepSize) const
00616 {
00617 if ( x1 > x2 )
00618 qSwap(x1, x2);
00619
00620 QwtDoubleInterval interval(x1 / pow(10.0, lowerMargin()),
00621 x2 * pow(10.0, upperMargin()) );
00622
00623 double logRef = 1.0;
00624 if (reference() > LOG_MIN / 2)
00625 logRef = qwtMin(reference(), LOG_MAX / 2);
00626
00627 if (testAttribute(QwtScaleEngine::Symmetric))
00628 {
00629 const double delta = qwtMax(interval.maxValue() / logRef,
00630 logRef / interval.minValue());
00631 interval.setInterval(logRef / delta, logRef * delta);
00632 }
00633
00634 if (testAttribute(QwtScaleEngine::IncludeReference))
00635 interval = interval.extend(logRef);
00636
00637 interval = interval.limited(LOG_MIN, LOG_MAX);
00638
00639 if (interval.width() == 0.0)
00640 interval = buildInterval(interval.minValue());
00641
00642 stepSize = divideInterval(log10(interval).width(), qwtMax(maxNumSteps, 1));
00643 if ( stepSize < 1.0 )
00644 stepSize = 1.0;
00645
00646 if (!testAttribute(QwtScaleEngine::Floating))
00647 interval = align(interval, stepSize);
00648
00649 x1 = interval.minValue();
00650 x2 = interval.maxValue();
00651
00652 if (testAttribute(QwtScaleEngine::Inverted))
00653 {
00654 qSwap(x1, x2);
00655 stepSize = -stepSize;
00656 }
00657 }
00658
00671 QwtScaleDiv QwtLog10ScaleEngine::divideScale(double x1, double x2,
00672 int maxMajSteps, int maxMinSteps, double stepSize) const
00673 {
00674 QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
00675 interval = interval.limited(LOG_MIN, LOG_MAX);
00676
00677 if (interval.width() <= 0 )
00678 return QwtScaleDiv();
00679
00680 if (interval.maxValue() / interval.minValue() < 10.0)
00681 {
00682
00683
00684 QwtLinearScaleEngine linearScaler;
00685 linearScaler.setAttributes(attributes());
00686 linearScaler.setReference(reference());
00687 linearScaler.setMargins(lowerMargin(), upperMargin());
00688
00689 return linearScaler.divideScale(x1, x2,
00690 maxMajSteps, maxMinSteps, stepSize);
00691 }
00692
00693 stepSize = qwtAbs(stepSize);
00694 if ( stepSize == 0.0 )
00695 {
00696 if ( maxMajSteps < 1 )
00697 maxMajSteps = 1;
00698
00699 stepSize = divideInterval(log10(interval).width(), maxMajSteps);
00700 if ( stepSize < 1.0 )
00701 stepSize = 1.0;
00702 }
00703
00704 QwtScaleDiv scaleDiv;
00705 if ( stepSize != 0.0 )
00706 {
00707 QwtValueList ticks[QwtScaleDiv::NTickTypes];
00708 buildTicks(interval, stepSize, maxMinSteps, ticks);
00709
00710 scaleDiv = QwtScaleDiv(interval, ticks);
00711 }
00712
00713 if ( x1 > x2 )
00714 scaleDiv.invert();
00715
00716 return scaleDiv;
00717 }
00718
00719 void QwtLog10ScaleEngine::buildTicks(
00720 const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
00721 QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
00722 {
00723 const QwtDoubleInterval boundingInterval =
00724 align(interval, stepSize);
00725
00726 ticks[QwtScaleDiv::MajorTick] =
00727 buildMajorTicks(boundingInterval, stepSize);
00728
00729 if ( maxMinSteps > 0 )
00730 {
00731 ticks[QwtScaleDiv::MinorTick] = buildMinorTicks(
00732 ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize);
00733 }
00734
00735 for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
00736 ticks[i] = strip(ticks[i], interval);
00737 }
00738
00739 QwtValueList QwtLog10ScaleEngine::buildMajorTicks(
00740 const QwtDoubleInterval &interval, double stepSize) const
00741 {
00742 double width = log10(interval).width();
00743
00744 int numTicks = qRound(width / stepSize) + 1;
00745 if ( numTicks > 10000 )
00746 numTicks = 10000;
00747
00748 const double lxmin = log(interval.minValue());
00749 const double lxmax = log(interval.maxValue());
00750 const double lstep = (lxmax - lxmin) / double(numTicks - 1);
00751
00752 QwtValueList ticks;
00753
00754 ticks += interval.minValue();
00755
00756 for (int i = 1; i < numTicks; i++)
00757 ticks += exp(lxmin + double(i) * lstep);
00758
00759 ticks += interval.maxValue();
00760
00761 return ticks;
00762 }
00763
00764 QwtValueList QwtLog10ScaleEngine::buildMinorTicks(
00765 const QwtValueList &majorTicks,
00766 int maxMinSteps, double stepSize) const
00767 {
00768 if (stepSize < 1.1)
00769 {
00770 if ( maxMinSteps < 1 )
00771 return QwtValueList();
00772
00773 int k0, kstep, kmax;
00774
00775 if (maxMinSteps >= 8)
00776 {
00777 k0 = 2;
00778 kmax = 9;
00779 kstep = 1;
00780 }
00781 else if (maxMinSteps >= 4)
00782 {
00783 k0 = 2;
00784 kmax = 8;
00785 kstep = 2;
00786 }
00787 else if (maxMinSteps >= 2)
00788 {
00789 k0 = 2;
00790 kmax = 5;
00791 kstep = 3;
00792 }
00793 else
00794 {
00795 k0 = 5;
00796 kmax = 5;
00797 kstep = 1;
00798 }
00799
00800 QwtValueList minorTicks;
00801
00802 for (int i = 0; i < (int)majorTicks.count(); i++)
00803 {
00804 const double v = majorTicks[i];
00805 for (int k = k0; k<= kmax; k+=kstep)
00806 minorTicks += v * double(k);
00807 }
00808
00809 return minorTicks;
00810 }
00811 else
00812 {
00813 double minStep = divideInterval(stepSize, maxMinSteps);
00814 if ( minStep == 0.0 )
00815 return QwtValueList();
00816
00817 if ( minStep < 1.0 )
00818 minStep = 1.0;
00819
00820
00821 int nMin = qRound(stepSize / minStep) - 1;
00822
00823
00824
00825 if ( QwtScaleArithmetic::compareEps((nMin + 1) * minStep,
00826 qwtAbs(stepSize), stepSize) > 0)
00827 {
00828 nMin = 0;
00829 }
00830
00831 if (nMin < 1)
00832 return QwtValueList();
00833
00834
00835 const double minFactor = qwtMax(pow(10.0, minStep), 10.0);
00836
00837 QwtValueList minorTicks;
00838 for (int i = 0; i < (int)majorTicks.count(); i++)
00839 {
00840 double val = majorTicks[i];
00841 for (int k=0; k< nMin; k++)
00842 {
00843 val *= minFactor;
00844 minorTicks += val;
00845 }
00846 }
00847 return minorTicks;
00848 }
00849 }
00850
00862 QwtDoubleInterval QwtLog10ScaleEngine::align(
00863 const QwtDoubleInterval &interval, double stepSize) const
00864 {
00865 const QwtDoubleInterval intv = log10(interval);
00866
00867 const double x1 = QwtScaleArithmetic::floorEps(intv.minValue(), stepSize);
00868 const double x2 = QwtScaleArithmetic::ceilEps(intv.maxValue(), stepSize);
00869
00870 return pow10(QwtDoubleInterval(x1, x2));
00871 }
00872
00877 QwtDoubleInterval QwtLog10ScaleEngine::log10(
00878 const QwtDoubleInterval &interval) const
00879 {
00880 return QwtDoubleInterval(::log10(interval.minValue()),
00881 ::log10(interval.maxValue()));
00882 }
00883
00887 QwtDoubleInterval QwtLog10ScaleEngine::pow10(
00888 const QwtDoubleInterval &interval) const
00889 {
00890 return QwtDoubleInterval(pow(10.0, interval.minValue()),
00891 pow(10.0, interval.maxValue()));
00892 }