1
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
3
* Copyright (C) 1997 Josef Wilgen
4
* Copyright (C) 2002 Uwe Rathmann
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the Qwt License, Version 1.0
8
*****************************************************************************/
11
#include "qwt_scale_map.h"
12
#include "qwt_scale_engine.h"
14
static const double _eps = 1.0e-6;
17
\brief Compare 2 values, relative to an interval
19
Values are "equal", when :
20
\f$\cdot value2 - value1 <= abs(intervalSize * 10e^{-6})\f$
22
\param value1 First value to compare
23
\param value2 Second value to compare
24
\param intervalSize interval size
26
\return 0: if equal, -1: if value2 > value1, 1: if value1 > value2
28
int QwtScaleArithmetic::compareEps(double value1, double value2,
31
const double eps = qwtAbs(_eps * intervalSize);
33
if ( value2 - value1 > eps )
36
if ( value1 - value2 > eps )
43
Ceil a value, relative to an interval
45
\param value Value to ceil
46
\param intervalSize Interval size
50
double QwtScaleArithmetic::ceilEps(double value,
53
const double eps = _eps * intervalSize;
55
value = (value - eps) / intervalSize;
56
return ceil(value) * intervalSize;
60
Floor a value, relative to an interval
62
\param value Value to floor
63
\param intervalSize Interval size
67
double QwtScaleArithmetic::floorEps(double value, double intervalSize)
69
const double eps = _eps * intervalSize;
71
value = (value + eps) / intervalSize;
72
return floor(value) * intervalSize;
76
\brief Divide an interval into steps
78
\f$stepSize = (intervalSize - intervalSize * 10e^{-6}) / numSteps\f$
80
\param intervalSize Interval size
81
\param numSteps Number of steps
84
double QwtScaleArithmetic::divideEps(double intervalSize, double numSteps)
86
if ( numSteps == 0.0 || intervalSize == 0.0 )
89
return (intervalSize - (_eps * intervalSize)) / numSteps;
93
Find the smallest value out of {1,2,5}*10^n with an integer number n
94
which is greater than or equal to x
98
double QwtScaleArithmetic::ceil125(double x)
103
const double sign = (x > 0) ? 1.0 : -1.0;
104
const double lx = log10(fabs(x));
105
const double p10 = floor(lx);
107
double fr = pow(10.0, lx - p10);
117
return sign * fr * pow(10.0, p10);
121
\brief Find the largest value out of {1,2,5}*10^n with an integer number n
122
which is smaller than or equal to x
126
double QwtScaleArithmetic::floor125(double x)
131
double sign = (x > 0) ? 1.0 : -1.0;
132
const double lx = log10(fabs(x));
133
const double p10 = floor(lx);
135
double fr = pow(10.0, lx - p10);
145
return sign * fr * pow(10.0, p10);
148
class QwtScaleEngine::PrivateData
152
attributes(QwtScaleEngine::NoAttribute),
159
int attributes; // scale attributes
161
double loMargin; // margins
164
double referenceValue; // reference value
169
QwtScaleEngine::QwtScaleEngine()
171
d_data = new PrivateData;
176
QwtScaleEngine::~QwtScaleEngine ()
182
\return the margin at the lower end of the scale
183
The default margin is 0.
185
\sa QwtScaleEngine::setMargins()
187
double QwtScaleEngine::loMargin() const
189
return d_data->loMargin;
193
\return the margin at the upper end of the scale
194
The default margin is 0.
196
\sa QwtScaleEngine::setMargins()
198
double QwtScaleEngine::hiMargin() const
200
return d_data->hiMargin;
204
\brief Specify margins at the scale's endpoints
205
\param mlo minimum distance between the scale's lower boundary and the
206
smallest enclosed value
207
\param mhi minimum distance between the scale's upper boundary and the
208
greatest enclosed value
210
Margins can be used to leave a minimum amount of space between
211
the enclosed intervals and the boundaries of the scale.
214
\li QwtLog10ScaleEngine measures the margins in decades.
216
\sa QwtScaleEngine::hiMargin, QwtScaleEngine::loMargin
219
void QwtScaleEngine::setMargins(double mlo, double mhi)
221
d_data->loMargin = qwtMax(mlo,0.0);
222
d_data->hiMargin = qwtMax(mhi,0.0);
226
Calculate a step size for an interval size
228
\param intervalSize Interval size
229
\param numSteps Number of steps
233
double QwtScaleEngine::divideInterval(
234
double intervalSize, int numSteps) const
239
double v = QwtScaleArithmetic::divideEps(intervalSize, numSteps);
240
return QwtScaleArithmetic::ceil125(v);
244
Check if an interval "contains" a value
246
\param interval Interval
249
\sa QwtScaleArithmetic::compareEps
251
bool QwtScaleEngine::contains(
252
const QwtDoubleInterval &interval, double value) const
254
if (!interval.isValid() )
257
if ( QwtScaleArithmetic::compareEps(value,
258
interval.minValue(), interval.width()) < 0 )
263
if ( QwtScaleArithmetic::compareEps(value,
264
interval.maxValue(), interval.width()) > 0 )
273
Remove ticks from a list, that are not inside an interval
275
\param ticks Tick list
276
\param interval Interval
278
\return Stripped tick list
280
QwtValueList QwtScaleEngine::strip(
281
const QwtValueList& ticks,
282
const QwtDoubleInterval &interval) const
284
if ( !interval.isValid() || ticks.count() == 0 )
285
return QwtValueList();
287
if ( contains(interval, ticks.first())
288
&& contains(interval, ticks.last()) )
293
QwtValueList strippedTicks;
294
for ( int i = 0; i < (int)ticks.count(); i++ )
296
if ( contains(interval, ticks[i]) )
297
strippedTicks += ticks[i];
299
return strippedTicks;
303
\brief Build an interval for a value
305
In case of v == 0.0 the interval is [-0.5, 0.5],
306
otherwide it is [0.5 * v, 1.5 * v]
309
QwtDoubleInterval QwtScaleEngine::buildInterval(double v) const
312
const double delta = (v == 0.0) ? 0.5 : qwtAbs(0.5 * v);
313
return QwtDoubleInterval(v - delta, v + delta);
316
return QwtDoubleInterval(-0.5, 0.5);
318
return QwtDoubleInterval(0.5 * v, 1.5 * v);
323
Change a scale attribute
325
\param attribute Attribute to change
328
The behaviour of the scale engine can be changed
329
with the following attributes:
331
<dt>QwtAutoscale::IncludeReference
332
<dd>Build a scale which includes the reference value.
333
<dt>QwtScaleEngine::Symmetric
334
<dd>Build a scale which is symmetric to the reference value.
335
<dt>QwtScaleEngine::Floating
336
<dd>The endpoints of the scale are supposed to be equal the outmost included
337
values plus the specified margins (see setMargins()). If this attribute is
338
*not* set, the endpoints of the scale will be integer multiples of the step
340
<dt>QwtScaleEngine::Inverted
341
<dd>Turn the scale upside down.
344
\sa QwtScaleEngine::testAttribute()
346
void QwtScaleEngine::setAttribute(Attribute attribute, bool on)
349
d_data->attributes |= attribute;
351
d_data->attributes &= (~attribute);
355
Check if a attribute is set.
357
\param attribute Attribute to be tested
358
\sa QwtScaleEngine::setAttribute() for a description of the possible options.
360
bool QwtScaleEngine::testAttribute(Attribute attribute) const
362
return bool(d_data->attributes & attribute);
366
Change the scale attribute
368
\param attributes Set scale attributes
369
\sa QwtScaleEngine::attributes()
371
void QwtScaleEngine::setAttributes(int attributes)
373
d_data->attributes = attributes;
377
Return the scale attributes
379
int QwtScaleEngine::attributes() const
381
return d_data->attributes;
385
\brief Specify a reference point
386
\param r new reference value
388
The reference point is needed if options IncludeRef or
389
Symmetric are active. Its default value is 0.0.
391
void QwtScaleEngine::setReference(double r)
393
d_data->referenceValue = r;
397
\return the reference value
398
\sa QwtScaleEngine::setReference(), QwtScaleEngine::setOptions()
400
double QwtScaleEngine::reference() const
402
return d_data->referenceValue;
406
Return a transformation, for linear scales
408
QwtScaleTransformation *QwtLinearScaleEngine::transformation() const
410
return new QwtScaleTransformation(QwtScaleTransformation::Linear);
414
Align and divide an interval
416
\param maxNumSteps Max. number of steps
417
\param x1 First limit of the interval (In/Out)
418
\param x2 Second limit of the interval (In/Out)
419
\param stepSize Step size (Out)
421
\sa QwtLinearScaleEngine::setAttribute
423
void QwtLinearScaleEngine::autoScale(int maxNumSteps,
424
double &x1, double &x2, double &stepSize) const
426
QwtDoubleInterval interval(x1, x2);
427
interval = interval.normalized();
429
interval.setMinValue(interval.minValue() - loMargin());
430
interval.setMaxValue(interval.maxValue() + hiMargin());
432
if (testAttribute(QwtScaleEngine::Symmetric))
433
interval = interval.symmetrize(reference());
435
if (testAttribute(QwtScaleEngine::IncludeReference))
436
interval = interval.extend(reference());
438
if (interval.width() == 0.0)
439
interval = buildInterval(interval.minValue());
441
stepSize = divideInterval(interval.width(), qwtMax(maxNumSteps, 1));
443
if ( !testAttribute(QwtScaleEngine::Floating) )
444
interval = align(interval, stepSize);
446
x1 = interval.minValue();
447
x2 = interval.maxValue();
449
if (testAttribute(QwtScaleEngine::Inverted))
452
stepSize = -stepSize;
457
\brief Calculate a scale division
459
\param x1 First interval limit
460
\param x2 Second interval limit
461
\param maxMajSteps Maximum for the number of major steps
462
\param maxMinSteps Maximum number of minor steps
463
\param stepSize Step size. If stepSize == 0, the scaleEngine
466
\sa QwtScaleEngine::stepSize, QwtScaleEngine::subDivide
468
QwtScaleDiv QwtLinearScaleEngine::divideScale(double x1, double x2,
469
int maxMajSteps, int maxMinSteps, double stepSize) const
471
QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
472
if (interval.width() <= 0 )
473
return QwtScaleDiv();
475
stepSize = qwtAbs(stepSize);
476
if ( stepSize == 0.0 )
478
if ( maxMajSteps < 1 )
481
stepSize = divideInterval(interval.width(), maxMajSteps);
484
QwtScaleDiv scaleDiv;
486
if ( stepSize != 0.0 )
488
QwtValueList ticks[QwtScaleDiv::NTickTypes];
489
buildTicks(interval, stepSize, maxMinSteps, ticks);
491
scaleDiv = QwtScaleDiv(interval, ticks);
500
void QwtLinearScaleEngine::buildTicks(
501
const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
502
QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
504
const QwtDoubleInterval boundingInterval =
505
align(interval, stepSize);
507
ticks[QwtScaleDiv::MajorTick] =
508
buildMajorTicks(boundingInterval, stepSize);
510
if ( maxMinSteps > 0 )
512
buildMinorTicks(ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize,
513
ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick]);
516
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
518
ticks[i] = strip(ticks[i], interval);
520
// ticks very close to 0.0 are
521
// explicitely set to 0.0
523
for ( int j = 0; j < (int)ticks[i].count(); j++ )
525
if ( QwtScaleArithmetic::compareEps(ticks[i][j], 0.0, stepSize) == 0 )
531
QwtValueList QwtLinearScaleEngine::buildMajorTicks(
532
const QwtDoubleInterval &interval, double stepSize) const
534
int numTicks = qRound(interval.width() / stepSize) + 1;
536
if ( numTicks > 10000 )
542
ticks += interval.minValue();
543
for (int i = 1; i < numTicks - 1; i++)
544
ticks += interval.minValue() + i * stepSize;
545
ticks += interval.maxValue();
550
void QwtLinearScaleEngine::buildMinorTicks(
551
const QwtValueList& majorTicks,
552
int maxMinSteps, double stepSize,
553
QwtValueList &minorTicks,
554
QwtValueList &mediumTicks) const
556
double minStep = divideInterval(stepSize, maxMinSteps);
560
// # minor steps per interval
561
int nMin = qwtAbs(qRound(stepSize / minStep)) - 1;
563
// Do the minor steps fit into the interval?
564
if ( QwtScaleArithmetic::compareEps((nMin + 1) * qwtAbs(minStep),
565
qwtAbs(stepSize), stepSize) > 0)
568
minStep = stepSize * 0.5;
575
// calculate minor ticks
577
for (int i = 0; i < (int)majorTicks.count(); i++)
579
double val = majorTicks[i];
580
for (int k=0; k< nMin; k++)
584
double alignedValue = val;
585
if (QwtScaleArithmetic::compareEps(val, 0.0, stepSize) == 0)
589
mediumTicks += alignedValue;
591
minorTicks += alignedValue;
597
\brief Align an interval to a step size
599
The limits of an interval are aligned that both are integer
600
multiples of the step size.
602
\param interval Interval
603
\param stepSize Step size
605
\return Aligned interval
607
QwtDoubleInterval QwtLinearScaleEngine::align(
608
const QwtDoubleInterval &interval, double stepSize) const
611
QwtScaleArithmetic::floorEps(interval.minValue(), stepSize);
613
QwtScaleArithmetic::ceilEps(interval.maxValue(), stepSize);
615
return QwtDoubleInterval(x1, x2);
619
Return a transformation, for logarithmic (base 10) scales
621
QwtScaleTransformation *QwtLog10ScaleEngine::transformation() const
623
return new QwtScaleTransformation(QwtScaleTransformation::Log10);
627
Align and divide an interval
629
\param maxNumSteps Max. number of steps
630
\param x1 First limit of the interval (In/Out)
631
\param x2 Second limit of the interval (In/Out)
632
\param stepSize Step size (Out)
634
\sa QwtScaleEngine::setAttribute
636
void QwtLog10ScaleEngine::autoScale(int maxNumSteps,
637
double &x1, double &x2, double &stepSize) const
642
QwtDoubleInterval interval(x1 / pow(10.0, loMargin()),
643
x2 * pow(10.0, hiMargin()) );
646
if (reference() > LOG_MIN / 2)
647
logRef = qwtMin(reference(), LOG_MAX / 2);
649
if (testAttribute(QwtScaleEngine::Symmetric))
651
const double delta = qwtMax(interval.maxValue() / logRef,
652
logRef / interval.minValue());
653
interval.setInterval(logRef / delta, logRef * delta);
656
if (testAttribute(QwtScaleEngine::IncludeReference))
657
interval = interval.extend(logRef);
659
interval = interval.limited(LOG_MIN, LOG_MAX);
661
if (interval.width() == 0.0)
662
interval = buildInterval(interval.minValue());
664
stepSize = divideInterval(log10(interval).width(), qwtMax(maxNumSteps, 1));
665
if ( stepSize < 1.0 )
668
if (!testAttribute(QwtScaleEngine::Floating))
669
interval = align(interval, stepSize);
671
x1 = interval.minValue();
672
x2 = interval.maxValue();
674
if (testAttribute(QwtScaleEngine::Inverted))
677
stepSize = -stepSize;
682
\brief Calculate a scale division
684
\param x1 First interval limit
685
\param x2 Second interval limit
686
\param maxMajSteps Maximum for the number of major steps
687
\param maxMinSteps Maximum number of minor steps
688
\param stepSize Step size. If stepSize == 0, the scaleEngine
691
\sa QwtScaleEngine::stepSize, QwtLog10ScaleEngine::subDivide
693
QwtScaleDiv QwtLog10ScaleEngine::divideScale(double x1, double x2,
694
int maxMajSteps, int maxMinSteps, double stepSize) const
696
QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
697
interval = interval.limited(LOG_MIN, LOG_MAX);
699
if (interval.width() <= 0 )
700
return QwtScaleDiv();
702
if (interval.maxValue() / interval.minValue() < 10.0)
704
// scale width is less than one decade -> build linear scale
706
QwtLinearScaleEngine linearScaler;
707
linearScaler.setAttributes(attributes());
708
linearScaler.setReference(reference());
709
linearScaler.setMargins(loMargin(), hiMargin());
711
return linearScaler.divideScale(x1, x2,
712
maxMajSteps, maxMinSteps, stepSize);
715
stepSize = qwtAbs(stepSize);
716
if ( stepSize == 0.0 )
718
if ( maxMajSteps < 1 )
721
stepSize = divideInterval(log10(interval).width(), maxMajSteps);
722
if ( stepSize < 1.0 )
723
stepSize = 1.0; // major step must be >= 1 decade
726
QwtScaleDiv scaleDiv;
727
if ( stepSize != 0.0 )
729
QwtValueList ticks[QwtScaleDiv::NTickTypes];
730
buildTicks(interval, stepSize, maxMinSteps, ticks);
732
scaleDiv = QwtScaleDiv(interval, ticks);
741
void QwtLog10ScaleEngine::buildTicks(
742
const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
743
QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
745
const QwtDoubleInterval boundingInterval =
746
align(interval, stepSize);
748
ticks[QwtScaleDiv::MajorTick] =
749
buildMajorTicks(boundingInterval, stepSize);
751
if ( maxMinSteps > 0 )
753
ticks[QwtScaleDiv::MinorTick] = buildMinorTicks(
754
ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize);
757
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
758
ticks[i] = strip(ticks[i], interval);
761
QwtValueList QwtLog10ScaleEngine::buildMajorTicks(
762
const QwtDoubleInterval &interval, double stepSize) const
764
double width = log10(interval).width();
766
int numTicks = qRound(width / stepSize) + 1;
767
if ( numTicks > 10000 )
770
const double lxmin = log(interval.minValue());
771
const double lxmax = log(interval.maxValue());
772
const double lstep = (lxmax - lxmin) / double(numTicks - 1);
776
ticks += interval.minValue();
778
for (int i = 1; i < numTicks; i++)
779
ticks += exp(lxmin + double(i) * lstep);
781
ticks += interval.maxValue();
786
QwtValueList QwtLog10ScaleEngine::buildMinorTicks(
787
const QwtValueList &majorTicks,
788
int maxMinSteps, double stepSize) const
790
if (stepSize < 1.1) // major step width is one decade
792
if ( maxMinSteps < 1 )
793
return QwtValueList();
797
if (maxMinSteps >= 8)
803
else if (maxMinSteps >= 4)
809
else if (maxMinSteps >= 2)
822
QwtValueList minorTicks;
824
for (int i = 0; i < (int)majorTicks.count(); i++)
826
const double v = majorTicks[i];
827
for (int k = k0; k<= kmax; k+=kstep)
828
minorTicks += v * double(k);
833
else // major step > one decade
835
double minStep = divideInterval(stepSize, maxMinSteps);
836
if ( minStep == 0.0 )
837
return QwtValueList();
842
// # subticks per interval
843
int nMin = qRound(stepSize / minStep) - 1;
845
// Do the minor steps fit into the interval?
847
if ( QwtScaleArithmetic::compareEps((nMin + 1) * minStep,
848
qwtAbs(stepSize), stepSize) > 0)
854
return QwtValueList(); // no subticks
856
// substep factor = 10^substeps
857
const double minFactor = qwtMax(pow(10.0, minStep), 10.0);
859
QwtValueList minorTicks;
860
for (int i = 0; i < (int)majorTicks.count(); i++)
862
double val = majorTicks[i];
863
for (int k=0; k< nMin; k++)
874
\brief Align an interval to a step size
876
The limits of an interval are aligned that both are integer
877
multiples of the step size.
879
\param interval Interval
880
\param stepSize Step size
882
\return Aligned interval
884
QwtDoubleInterval QwtLog10ScaleEngine::align(
885
const QwtDoubleInterval &interval, double stepSize) const
887
const QwtDoubleInterval intv = log10(interval);
889
const double x1 = QwtScaleArithmetic::floorEps(intv.minValue(), stepSize);
890
const double x2 = QwtScaleArithmetic::ceilEps(intv.maxValue(), stepSize);
892
return pow10(QwtDoubleInterval(x1, x2));
896
Return the interval [log10(interval.minValue(), log10(interval.maxValue]
899
QwtDoubleInterval QwtLog10ScaleEngine::log10(
900
const QwtDoubleInterval &interval) const
902
return QwtDoubleInterval(::log10(interval.minValue()),
903
::log10(interval.maxValue()));
907
Return the interval [pow10(interval.minValue(), pow10(interval.maxValue]
909
QwtDoubleInterval QwtLog10ScaleEngine::pow10(
910
const QwtDoubleInterval &interval) const
912
return QwtDoubleInterval(pow(10.0, interval.minValue()),
913
pow(10.0, interval.maxValue()));