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
311
const double delta = (v == 0.0) ? 0.5 : qwtAbs(0.5 * v);
312
return QwtDoubleInterval(v - delta, v + delta);
316
Change a scale attribute
318
\param attribute Attribute to change
321
The behaviour of the scale engine can be changed
322
with the following attributes:
324
<dt>QwtScaleEngine::IncludeReference
325
<dd>Build a scale which includes the reference value.
326
<dt>QwtScaleEngine::Symmetric
327
<dd>Build a scale which is symmetric to the reference value.
328
<dt>QwtScaleEngine::Floating
329
<dd>The endpoints of the scale are supposed to be equal the outmost included
330
values plus the specified margins (see setMargins()). If this attribute is
331
*not* set, the endpoints of the scale will be integer multiples of the step
333
<dt>QwtScaleEngine::Inverted
334
<dd>Turn the scale upside down.
337
\sa QwtScaleEngine::testAttribute()
339
void QwtScaleEngine::setAttribute(Attribute attribute, bool on)
342
d_data->attributes |= attribute;
344
d_data->attributes &= (~attribute);
348
Check if a attribute is set.
350
\param attribute Attribute to be tested
351
\sa QwtScaleEngine::setAttribute() for a description of the possible options.
353
bool QwtScaleEngine::testAttribute(Attribute attribute) const
355
return bool(d_data->attributes & attribute);
359
Change the scale attribute
361
\param attributes Set scale attributes
362
\sa QwtScaleEngine::attributes()
364
void QwtScaleEngine::setAttributes(int attributes)
366
d_data->attributes = attributes;
370
Return the scale attributes
372
int QwtScaleEngine::attributes() const
374
return d_data->attributes;
378
\brief Specify a reference point
379
\param r new reference value
381
The reference point is needed if options IncludeRef or
382
Symmetric are active. Its default value is 0.0.
384
void QwtScaleEngine::setReference(double r)
386
d_data->referenceValue = r;
390
\return the reference value
391
\sa QwtScaleEngine::setReference(), QwtScaleEngine::setAttribute()
393
double QwtScaleEngine::reference() const
395
return d_data->referenceValue;
399
Return a transformation, for linear scales
401
QwtScaleTransformation *QwtLinearScaleEngine::transformation() const
403
return new QwtScaleTransformation(QwtScaleTransformation::Linear);
407
Align and divide an interval
409
\param maxNumSteps Max. number of steps
410
\param x1 First limit of the interval (In/Out)
411
\param x2 Second limit of the interval (In/Out)
412
\param stepSize Step size (Out)
414
\sa QwtLinearScaleEngine::setAttribute
416
void QwtLinearScaleEngine::autoScale(int maxNumSteps,
417
double &x1, double &x2, double &stepSize) const
419
QwtDoubleInterval interval(x1, x2);
420
interval = interval.normalized();
422
interval.setMinValue(interval.minValue() - loMargin());
423
interval.setMaxValue(interval.maxValue() + hiMargin());
425
if (testAttribute(QwtScaleEngine::Symmetric))
426
interval = interval.symmetrize(reference());
428
if (testAttribute(QwtScaleEngine::IncludeReference))
429
interval = interval.extend(reference());
431
if (interval.width() == 0.0)
432
interval = buildInterval(interval.minValue());
434
stepSize = divideInterval(interval.width(), qwtMax(maxNumSteps, 1));
436
if ( !testAttribute(QwtScaleEngine::Floating) )
437
interval = align(interval, stepSize);
439
x1 = interval.minValue();
440
x2 = interval.maxValue();
442
if (testAttribute(QwtScaleEngine::Inverted))
445
stepSize = -stepSize;
450
\brief Calculate a scale division
452
\param x1 First interval limit
453
\param x2 Second interval limit
454
\param maxMajSteps Maximum for the number of major steps
455
\param maxMinSteps Maximum number of minor steps
456
\param stepSize Step size. If stepSize == 0, the scaleEngine
459
\sa QwtScaleEngine::stepSize, QwtScaleEngine::subDivide
461
QwtScaleDiv QwtLinearScaleEngine::divideScale(double x1, double x2,
462
int maxMajSteps, int maxMinSteps, double stepSize) const
464
QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
465
if (interval.width() <= 0 )
466
return QwtScaleDiv();
468
stepSize = qwtAbs(stepSize);
469
if ( stepSize == 0.0 )
471
if ( maxMajSteps < 1 )
474
stepSize = divideInterval(interval.width(), maxMajSteps);
477
QwtScaleDiv scaleDiv;
479
if ( stepSize != 0.0 )
481
QwtValueList ticks[QwtScaleDiv::NTickTypes];
482
buildTicks(interval, stepSize, maxMinSteps, ticks);
484
scaleDiv = QwtScaleDiv(interval, ticks);
493
void QwtLinearScaleEngine::buildTicks(
494
const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
495
QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
497
const QwtDoubleInterval boundingInterval =
498
align(interval, stepSize);
500
ticks[QwtScaleDiv::MajorTick] =
501
buildMajorTicks(boundingInterval, stepSize);
503
if ( maxMinSteps > 0 )
505
buildMinorTicks(ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize,
506
ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick]);
509
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
511
ticks[i] = strip(ticks[i], interval);
513
// ticks very close to 0.0 are
514
// explicitely set to 0.0
516
for ( int j = 0; j < (int)ticks[i].count(); j++ )
518
if ( QwtScaleArithmetic::compareEps(ticks[i][j], 0.0, stepSize) == 0 )
524
QwtValueList QwtLinearScaleEngine::buildMajorTicks(
525
const QwtDoubleInterval &interval, double stepSize) const
527
int numTicks = qRound(interval.width() / stepSize) + 1;
529
if ( numTicks > 10000 )
535
ticks += interval.minValue();
536
for (int i = 1; i < numTicks - 1; i++)
537
ticks += interval.minValue() + i * stepSize;
538
ticks += interval.maxValue();
543
void QwtLinearScaleEngine::buildMinorTicks(
544
const QwtValueList& majorTicks,
545
int maxMinSteps, double stepSize,
546
QwtValueList &minorTicks,
547
QwtValueList &mediumTicks) const
549
double minStep = divideInterval(stepSize, maxMinSteps);
553
// # ticks per interval
554
int numTicks = (int)::ceil(qwtAbs(stepSize / minStep)) - 1;
556
// Do the minor steps fit into the interval?
557
if ( QwtScaleArithmetic::compareEps((numTicks + 1) * qwtAbs(minStep),
558
qwtAbs(stepSize), stepSize) > 0)
561
minStep = stepSize * 0.5;
566
medIndex = numTicks / 2;
568
// calculate minor ticks
570
for (int i = 0; i < (int)majorTicks.count(); i++)
572
double val = majorTicks[i];
573
for (int k = 0; k < numTicks; k++)
577
double alignedValue = val;
578
if (QwtScaleArithmetic::compareEps(val, 0.0, stepSize) == 0)
582
mediumTicks += alignedValue;
584
minorTicks += alignedValue;
590
\brief Align an interval to a step size
592
The limits of an interval are aligned that both are integer
593
multiples of the step size.
595
\param interval Interval
596
\param stepSize Step size
598
\return Aligned interval
600
QwtDoubleInterval QwtLinearScaleEngine::align(
601
const QwtDoubleInterval &interval, double stepSize) const
604
QwtScaleArithmetic::floorEps(interval.minValue(), stepSize);
606
QwtScaleArithmetic::ceilEps(interval.maxValue(), stepSize);
608
return QwtDoubleInterval(x1, x2);
612
Return a transformation, for logarithmic (base 10) scales
614
QwtScaleTransformation *QwtLog10ScaleEngine::transformation() const
616
return new QwtScaleTransformation(QwtScaleTransformation::Log10);
620
Align and divide an interval
622
\param maxNumSteps Max. number of steps
623
\param x1 First limit of the interval (In/Out)
624
\param x2 Second limit of the interval (In/Out)
625
\param stepSize Step size (Out)
627
\sa QwtScaleEngine::setAttribute
629
void QwtLog10ScaleEngine::autoScale(int maxNumSteps,
630
double &x1, double &x2, double &stepSize) const
635
QwtDoubleInterval interval(x1 / pow(10.0, loMargin()),
636
x2 * pow(10.0, hiMargin()) );
639
if (reference() > LOG_MIN / 2)
640
logRef = qwtMin(reference(), LOG_MAX / 2);
642
if (testAttribute(QwtScaleEngine::Symmetric))
644
const double delta = qwtMax(interval.maxValue() / logRef,
645
logRef / interval.minValue());
646
interval.setInterval(logRef / delta, logRef * delta);
649
if (testAttribute(QwtScaleEngine::IncludeReference))
650
interval = interval.extend(logRef);
652
interval = interval.limited(LOG_MIN, LOG_MAX);
654
if (interval.width() == 0.0)
655
interval = buildInterval(interval.minValue());
657
stepSize = divideInterval(log10(interval).width(), qwtMax(maxNumSteps, 1));
658
if ( stepSize < 1.0 )
661
if (!testAttribute(QwtScaleEngine::Floating))
662
interval = align(interval, stepSize);
664
x1 = interval.minValue();
665
x2 = interval.maxValue();
667
if (testAttribute(QwtScaleEngine::Inverted))
670
stepSize = -stepSize;
675
\brief Calculate a scale division
677
\param x1 First interval limit
678
\param x2 Second interval limit
679
\param maxMajSteps Maximum for the number of major steps
680
\param maxMinSteps Maximum number of minor steps
681
\param stepSize Step size. If stepSize == 0, the scaleEngine
684
\sa QwtScaleEngine::stepSize, QwtLog10ScaleEngine::subDivide
686
QwtScaleDiv QwtLog10ScaleEngine::divideScale(double x1, double x2,
687
int maxMajSteps, int maxMinSteps, double stepSize) const
689
QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
690
interval = interval.limited(LOG_MIN, LOG_MAX);
692
if (interval.width() <= 0 )
693
return QwtScaleDiv();
695
if (interval.maxValue() / interval.minValue() < 10.0)
697
// scale width is less than one decade -> build linear scale
699
QwtLinearScaleEngine linearScaler;
700
linearScaler.setAttributes(attributes());
701
linearScaler.setReference(reference());
702
linearScaler.setMargins(loMargin(), hiMargin());
704
return linearScaler.divideScale(x1, x2,
705
maxMajSteps, maxMinSteps, stepSize);
708
stepSize = qwtAbs(stepSize);
709
if ( stepSize == 0.0 )
711
if ( maxMajSteps < 1 )
714
stepSize = divideInterval(log10(interval).width(), maxMajSteps);
715
if ( stepSize < 1.0 )
716
stepSize = 1.0; // major step must be >= 1 decade
719
QwtScaleDiv scaleDiv;
720
if ( stepSize != 0.0 )
722
QwtValueList ticks[QwtScaleDiv::NTickTypes];
723
buildTicks(interval, stepSize, maxMinSteps, ticks);
725
scaleDiv = QwtScaleDiv(interval, ticks);
734
void QwtLog10ScaleEngine::buildTicks(
735
const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
736
QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
738
const QwtDoubleInterval boundingInterval =
739
align(interval, stepSize);
741
ticks[QwtScaleDiv::MajorTick] =
742
buildMajorTicks(boundingInterval, stepSize);
744
if ( maxMinSteps > 0 )
746
ticks[QwtScaleDiv::MinorTick] = buildMinorTicks(
747
ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize);
750
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
751
ticks[i] = strip(ticks[i], interval);
754
QwtValueList QwtLog10ScaleEngine::buildMajorTicks(
755
const QwtDoubleInterval &interval, double stepSize) const
757
double width = log10(interval).width();
759
int numTicks = qRound(width / stepSize) + 1;
760
if ( numTicks > 10000 )
763
const double lxmin = log(interval.minValue());
764
const double lxmax = log(interval.maxValue());
765
const double lstep = (lxmax - lxmin) / double(numTicks - 1);
769
ticks += interval.minValue();
771
for (int i = 1; i < numTicks; i++)
772
ticks += exp(lxmin + double(i) * lstep);
774
ticks += interval.maxValue();
779
QwtValueList QwtLog10ScaleEngine::buildMinorTicks(
780
const QwtValueList &majorTicks,
781
int maxMinSteps, double stepSize) const
783
if (stepSize < 1.1) // major step width is one decade
785
if ( maxMinSteps < 1 )
786
return QwtValueList();
790
if (maxMinSteps >= 8)
796
else if (maxMinSteps >= 4)
802
else if (maxMinSteps >= 2)
815
QwtValueList minorTicks;
817
for (int i = 0; i < (int)majorTicks.count(); i++)
819
const double v = majorTicks[i];
820
for (int k = k0; k<= kmax; k+=kstep)
821
minorTicks += v * double(k);
826
else // major step > one decade
828
double minStep = divideInterval(stepSize, maxMinSteps);
829
if ( minStep == 0.0 )
830
return QwtValueList();
835
// # subticks per interval
836
int nMin = qRound(stepSize / minStep) - 1;
838
// Do the minor steps fit into the interval?
840
if ( QwtScaleArithmetic::compareEps((nMin + 1) * minStep,
841
qwtAbs(stepSize), stepSize) > 0)
847
return QwtValueList(); // no subticks
849
// substep factor = 10^substeps
850
const double minFactor = qwtMax(pow(10.0, minStep), 10.0);
852
QwtValueList minorTicks;
853
for (int i = 0; i < (int)majorTicks.count(); i++)
855
double val = majorTicks[i];
856
for (int k=0; k< nMin; k++)
867
\brief Align an interval to a step size
869
The limits of an interval are aligned that both are integer
870
multiples of the step size.
872
\param interval Interval
873
\param stepSize Step size
875
\return Aligned interval
877
QwtDoubleInterval QwtLog10ScaleEngine::align(
878
const QwtDoubleInterval &interval, double stepSize) const
880
const QwtDoubleInterval intv = log10(interval);
882
const double x1 = QwtScaleArithmetic::floorEps(intv.minValue(), stepSize);
883
const double x2 = QwtScaleArithmetic::ceilEps(intv.maxValue(), stepSize);
885
return pow10(QwtDoubleInterval(x1, x2));
889
Return the interval [log10(interval.minValue(), log10(interval.maxValue]
892
QwtDoubleInterval QwtLog10ScaleEngine::log10(
893
const QwtDoubleInterval &interval) const
895
return QwtDoubleInterval(::log10(interval.minValue()),
896
::log10(interval.maxValue()));
900
Return the interval [pow10(interval.minValue(), pow10(interval.maxValue]
902
QwtDoubleInterval QwtLog10ScaleEngine::pow10(
903
const QwtDoubleInterval &interval) const
905
return QwtDoubleInterval(pow(10.0, interval.minValue()),
906
pow(10.0, interval.maxValue()));