source: ntrip/trunk/BNC/qwt/qwt_scale_engine.cpp@ 8541

Last change on this file since 8541 was 8127, checked in by stoecker, 8 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 28.2 KB
Line 
1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
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 *****************************************************************************/
9
10#include "qwt_scale_engine.h"
11#include "qwt_math.h"
12#include "qwt_scale_map.h"
13#include <qalgorithms.h>
14#include <qmath.h>
15#include <float.h>
16#include <limits>
17
18#if QT_VERSION < 0x040601
19#define qFabs(x) ::fabs(x)
20#define qExp(x) ::exp(x)
21#endif
22
23static inline double qwtLog( double base, double value )
24{
25 return log( value ) / log( base );
26}
27
28static inline QwtInterval qwtLogInterval( double base, const QwtInterval &interval )
29{
30 return QwtInterval( qwtLog( base, interval.minValue() ),
31 qwtLog( base, interval.maxValue() ) );
32}
33
34static inline QwtInterval qwtPowInterval( double base, const QwtInterval &interval )
35{
36 return QwtInterval( qPow( base, interval.minValue() ),
37 qPow( base, interval.maxValue() ) );
38}
39
40static inline long double qwtIntervalWidthL( const QwtInterval &interval )
41{
42 if ( !interval.isValid() )
43 return 0.0;
44
45 return static_cast<long double>( interval.maxValue() )
46 - static_cast<long double>( interval.minValue() );
47}
48
49#if 1
50
51// this version often doesn't find the best ticks: f.e for 15: 5, 10
52static double qwtStepSize( double intervalSize, int maxSteps, uint base )
53{
54 const double minStep =
55 QwtScaleArithmetic::divideInterval( intervalSize, maxSteps, base );
56
57 if ( minStep != 0.0 )
58 {
59 // # ticks per interval
60 const int numTicks = qCeil( qAbs( intervalSize / minStep ) ) - 1;
61
62 // Do the minor steps fit into the interval?
63 if ( qwtFuzzyCompare( ( numTicks + 1 ) * qAbs( minStep ),
64 qAbs( intervalSize ), intervalSize ) > 0 )
65 {
66 // The minor steps doesn't fit into the interval
67 return 0.5 * intervalSize;
68 }
69 }
70
71 return minStep;
72}
73
74#else
75
76static double qwtStepSize( double intervalSize, int maxSteps, uint base )
77{
78 if ( maxSteps <= 0 )
79 return 0.0;
80
81 if ( maxSteps > 2 )
82 {
83 for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
84 {
85 const double stepSize = intervalSize / numSteps;
86
87 const double p = ::floor( ::log( stepSize ) / ::log( base ) );
88 const double fraction = qPow( base, p );
89
90 for ( uint n = base; n > 1; n /= 2 )
91 {
92 if ( qFuzzyCompare( stepSize, n * fraction ) )
93 return stepSize;
94
95 if ( n == 3 && ( base % 2 ) == 0 )
96 {
97 if ( qFuzzyCompare( stepSize, 2 * fraction ) )
98 return stepSize;
99 }
100 }
101 }
102 }
103
104 return intervalSize * 0.5;
105}
106
107#endif
108
109static const double _eps = 1.0e-6;
110
111/*!
112 Ceil a value, relative to an interval
113
114 \param value Value to be ceiled
115 \param intervalSize Interval size
116
117 \return Rounded value
118
119 \sa floorEps()
120*/
121double QwtScaleArithmetic::ceilEps( double value,
122 double intervalSize )
123{
124 const double eps = _eps * intervalSize;
125
126 value = ( value - eps ) / intervalSize;
127 return ::ceil( value ) * intervalSize;
128}
129
130/*!
131 Floor a value, relative to an interval
132
133 \param value Value to be floored
134 \param intervalSize Interval size
135
136 \return Rounded value
137 \sa floorEps()
138*/
139double QwtScaleArithmetic::floorEps( double value, double intervalSize )
140{
141 const double eps = _eps * intervalSize;
142
143 value = ( value + eps ) / intervalSize;
144 return ::floor( value ) * intervalSize;
145}
146
147/*!
148 \brief Divide an interval into steps
149
150 \f$stepSize = (intervalSize - intervalSize * 10e^{-6}) / numSteps\f$
151
152 \param intervalSize Interval size
153 \param numSteps Number of steps
154 \return Step size
155*/
156double QwtScaleArithmetic::divideEps( double intervalSize, double numSteps )
157{
158 if ( numSteps == 0.0 || intervalSize == 0.0 )
159 return 0.0;
160
161 return ( intervalSize - ( _eps * intervalSize ) ) / numSteps;
162}
163
164/*!
165 Calculate a step size for a given interval
166
167 \param intervalSize Interval size
168 \param numSteps Number of steps
169 \param base Base for the division ( usually 10 )
170
171 \return Calculated step size
172 */
173double QwtScaleArithmetic::divideInterval(
174 double intervalSize, int numSteps, uint base )
175{
176 if ( numSteps <= 0 )
177 return 0.0;
178
179 const double v = QwtScaleArithmetic::divideEps( intervalSize, numSteps );
180 if ( v == 0.0 )
181 return 0.0;
182
183 const double lx = qwtLog( base, qFabs( v ) );
184 const double p = ::floor( lx );
185
186 const double fraction = qPow( base, lx - p );
187
188 uint n = base;
189 while ( ( n > 1 ) && ( fraction <= n / 2 ) )
190 n /= 2;
191
192 double stepSize = n * qPow( base, p );
193 if ( v < 0 )
194 stepSize = -stepSize;
195
196 return stepSize;
197}
198
199class QwtScaleEngine::PrivateData
200{
201public:
202 PrivateData():
203 attributes( QwtScaleEngine::NoAttribute ),
204 lowerMargin( 0.0 ),
205 upperMargin( 0.0 ),
206 referenceValue( 0.0 ),
207 base( 10 ),
208 transform( NULL )
209 {
210 }
211
212 ~PrivateData()
213 {
214 delete transform;
215 }
216
217 QwtScaleEngine::Attributes attributes;
218
219 double lowerMargin;
220 double upperMargin;
221
222 double referenceValue;
223
224 uint base;
225
226 QwtTransform* transform;
227};
228
229/*!
230 Constructor
231
232 \param base Base of the scale engine
233 \sa setBase()
234 */
235QwtScaleEngine::QwtScaleEngine( uint base )
236{
237 d_data = new PrivateData;
238 setBase( base );
239}
240
241
242//! Destructor
243QwtScaleEngine::~QwtScaleEngine ()
244{
245 delete d_data;
246}
247
248/*!
249 Assign a transformation
250
251 \param transform Transformation
252
253 The transformation object is used as factory for clones
254 that are returned by transformation()
255
256 The scale engine takes ownership of the transformation.
257
258 \sa QwtTransform::copy(), transformation()
259
260 */
261void QwtScaleEngine::setTransformation( QwtTransform *transform )
262{
263 if ( transform != d_data->transform )
264 {
265 delete d_data->transform;
266 d_data->transform = transform;
267 }
268}
269
270/*!
271 Create and return a clone of the transformation
272 of the engine. When the engine has no special transformation
273 NULL is returned, indicating no transformation.
274
275 \return A clone of the transfomation
276 \sa setTransformation()
277 */
278QwtTransform *QwtScaleEngine::transformation() const
279{
280 QwtTransform *transform = NULL;
281 if ( d_data->transform )
282 transform = d_data->transform->copy();
283
284 return transform;
285}
286
287/*!
288 \return the margin at the lower end of the scale
289 The default margin is 0.
290
291 \sa setMargins()
292*/
293double QwtScaleEngine::lowerMargin() const
294{
295 return d_data->lowerMargin;
296}
297
298/*!
299 \return the margin at the upper end of the scale
300 The default margin is 0.
301
302 \sa setMargins()
303*/
304double QwtScaleEngine::upperMargin() const
305{
306 return d_data->upperMargin;
307}
308
309/*!
310 \brief Specify margins at the scale's endpoints
311 \param lower minimum distance between the scale's lower boundary and the
312 smallest enclosed value
313 \param upper minimum distance between the scale's upper boundary and the
314 greatest enclosed value
315
316 Margins can be used to leave a minimum amount of space between
317 the enclosed intervals and the boundaries of the scale.
318
319 \warning
320 \li QwtLogScaleEngine measures the margins in decades.
321
322 \sa upperMargin(), lowerMargin()
323*/
324
325void QwtScaleEngine::setMargins( double lower, double upper )
326{
327 d_data->lowerMargin = qMax( lower, 0.0 );
328 d_data->upperMargin = qMax( upper, 0.0 );
329}
330
331/*!
332 Calculate a step size for an interval size
333
334 \param intervalSize Interval size
335 \param numSteps Number of steps
336
337 \return Step size
338*/
339double QwtScaleEngine::divideInterval(
340 double intervalSize, int numSteps ) const
341{
342 return QwtScaleArithmetic::divideInterval(
343 intervalSize, numSteps, d_data->base );
344}
345
346/*!
347 Check if an interval "contains" a value
348
349 \param interval Interval
350 \param value Value
351
352 \return True, when the value is inside the interval
353*/
354bool QwtScaleEngine::contains(
355 const QwtInterval &interval, double value ) const
356{
357 if ( !interval.isValid() )
358 return false;
359
360 if ( qwtFuzzyCompare( value, interval.minValue(), interval.width() ) < 0 )
361 return false;
362
363 if ( qwtFuzzyCompare( value, interval.maxValue(), interval.width() ) > 0 )
364 return false;
365
366 return true;
367}
368
369/*!
370 Remove ticks from a list, that are not inside an interval
371
372 \param ticks Tick list
373 \param interval Interval
374
375 \return Stripped tick list
376*/
377QList<double> QwtScaleEngine::strip( const QList<double>& ticks,
378 const QwtInterval &interval ) const
379{
380 if ( !interval.isValid() || ticks.count() == 0 )
381 return QList<double>();
382
383 if ( contains( interval, ticks.first() )
384 && contains( interval, ticks.last() ) )
385 {
386 return ticks;
387 }
388
389 QList<double> strippedTicks;
390 for ( int i = 0; i < ticks.count(); i++ )
391 {
392 if ( contains( interval, ticks[i] ) )
393 strippedTicks += ticks[i];
394 }
395 return strippedTicks;
396}
397
398/*!
399 \brief Build an interval around a value
400
401 In case of v == 0.0 the interval is [-0.5, 0.5],
402 otherwide it is [0.5 * v, 1.5 * v]
403
404 \param value Initial value
405 \return Calculated interval
406*/
407
408QwtInterval QwtScaleEngine::buildInterval( double value ) const
409{
410 const double delta = ( value == 0.0 ) ? 0.5 : qAbs( 0.5 * value );
411
412 if ( DBL_MAX - delta < value )
413 return QwtInterval( DBL_MAX - delta, DBL_MAX );
414
415 if ( -DBL_MAX + delta > value )
416 return QwtInterval( -DBL_MAX, -DBL_MAX + delta );
417
418 return QwtInterval( value - delta, value + delta );
419}
420
421/*!
422 Change a scale attribute
423
424 \param attribute Attribute to change
425 \param on On/Off
426
427 \sa Attribute, testAttribute()
428*/
429void QwtScaleEngine::setAttribute( Attribute attribute, bool on )
430{
431 if ( on )
432 d_data->attributes |= attribute;
433 else
434 d_data->attributes &= ~attribute;
435}
436
437/*!
438 \return True, if attribute is enabled.
439
440 \param attribute Attribute to be tested
441 \sa Attribute, setAttribute()
442*/
443bool QwtScaleEngine::testAttribute( Attribute attribute ) const
444{
445 return ( d_data->attributes & attribute );
446}
447
448/*!
449 Change the scale attribute
450
451 \param attributes Set scale attributes
452 \sa Attribute, attributes()
453*/
454void QwtScaleEngine::setAttributes( Attributes attributes )
455{
456 d_data->attributes = attributes;
457}
458
459/*!
460 \return Scale attributes
461 \sa Attribute, setAttributes(), testAttribute()
462*/
463QwtScaleEngine::Attributes QwtScaleEngine::attributes() const
464{
465 return d_data->attributes;
466}
467
468/*!
469 \brief Specify a reference point
470 \param r new reference value
471
472 The reference point is needed if options IncludeReference or
473 Symmetric are active. Its default value is 0.0.
474
475 \sa Attribute
476*/
477void QwtScaleEngine::setReference( double r )
478{
479 d_data->referenceValue = r;
480}
481
482/*!
483 \return the reference value
484 \sa setReference(), setAttribute()
485*/
486double QwtScaleEngine::reference() const
487{
488 return d_data->referenceValue;
489}
490
491/*!
492 Set the base of the scale engine
493
494 While a base of 10 is what 99.9% of all applications need
495 certain scales might need a different base: f.e 2
496
497 The default setting is 10
498
499 \param base Base of the engine
500
501 \sa base()
502 */
503void QwtScaleEngine::setBase( uint base )
504{
505 d_data->base = qMax( base, 2U );
506}
507
508/*!
509 \return base Base of the scale engine
510 \sa setBase()
511 */
512uint QwtScaleEngine::base() const
513{
514 return d_data->base;
515}
516
517/*!
518 Constructor
519
520 \param base Base of the scale engine
521 \sa setBase()
522 */
523QwtLinearScaleEngine::QwtLinearScaleEngine( uint base ):
524 QwtScaleEngine( base )
525{
526}
527
528//! Destructor
529QwtLinearScaleEngine::~QwtLinearScaleEngine()
530{
531}
532
533/*!
534 Align and divide an interval
535
536 \param maxNumSteps Max. number of steps
537 \param x1 First limit of the interval (In/Out)
538 \param x2 Second limit of the interval (In/Out)
539 \param stepSize Step size (Out)
540
541 \sa setAttribute()
542*/
543void QwtLinearScaleEngine::autoScale( int maxNumSteps,
544 double &x1, double &x2, double &stepSize ) const
545{
546 QwtInterval interval( x1, x2 );
547 interval = interval.normalized();
548
549 interval.setMinValue( interval.minValue() - lowerMargin() );
550 interval.setMaxValue( interval.maxValue() + upperMargin() );
551
552 if ( testAttribute( QwtScaleEngine::Symmetric ) )
553 interval = interval.symmetrize( reference() );
554
555 if ( testAttribute( QwtScaleEngine::IncludeReference ) )
556 interval = interval.extend( reference() );
557
558 if ( interval.width() == 0.0 )
559 interval = buildInterval( interval.minValue() );
560
561 stepSize = QwtScaleArithmetic::divideInterval(
562 interval.width(), qMax( maxNumSteps, 1 ), base() );
563
564 if ( !testAttribute( QwtScaleEngine::Floating ) )
565 interval = align( interval, stepSize );
566
567 x1 = interval.minValue();
568 x2 = interval.maxValue();
569
570 if ( testAttribute( QwtScaleEngine::Inverted ) )
571 {
572 qSwap( x1, x2 );
573 stepSize = -stepSize;
574 }
575}
576
577/*!
578 \brief Calculate a scale division for an interval
579
580 \param x1 First interval limit
581 \param x2 Second interval limit
582 \param maxMajorSteps Maximum for the number of major steps
583 \param maxMinorSteps Maximum number of minor steps
584 \param stepSize Step size. If stepSize == 0, the engine
585 calculates one.
586
587 \return Calculated scale division
588*/
589QwtScaleDiv QwtLinearScaleEngine::divideScale( double x1, double x2,
590 int maxMajorSteps, int maxMinorSteps, double stepSize ) const
591{
592 QwtInterval interval = QwtInterval( x1, x2 ).normalized();
593
594 if ( qwtIntervalWidthL( interval ) > std::numeric_limits<double>::max() )
595 {
596 qWarning() << "QwtLinearScaleEngine::divideScale: overflow";
597 return QwtScaleDiv();
598 }
599
600 if ( interval.width() <= 0 )
601 return QwtScaleDiv();
602
603 stepSize = qAbs( stepSize );
604 if ( stepSize == 0.0 )
605 {
606 if ( maxMajorSteps < 1 )
607 maxMajorSteps = 1;
608
609 stepSize = QwtScaleArithmetic::divideInterval(
610 interval.width(), maxMajorSteps, base() );
611 }
612
613 QwtScaleDiv scaleDiv;
614
615 if ( stepSize != 0.0 )
616 {
617 QList<double> ticks[QwtScaleDiv::NTickTypes];
618 buildTicks( interval, stepSize, maxMinorSteps, ticks );
619
620 scaleDiv = QwtScaleDiv( interval, ticks );
621 }
622
623 if ( x1 > x2 )
624 scaleDiv.invert();
625
626 return scaleDiv;
627}
628
629/*!
630 \brief Calculate ticks for an interval
631
632 \param interval Interval
633 \param stepSize Step size
634 \param maxMinorSteps Maximum number of minor steps
635 \param ticks Arrays to be filled with the calculated ticks
636
637 \sa buildMajorTicks(), buildMinorTicks
638*/
639void QwtLinearScaleEngine::buildTicks(
640 const QwtInterval& interval, double stepSize, int maxMinorSteps,
641 QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
642{
643 const QwtInterval boundingInterval = align( interval, stepSize );
644
645 ticks[QwtScaleDiv::MajorTick] =
646 buildMajorTicks( boundingInterval, stepSize );
647
648 if ( maxMinorSteps > 0 )
649 {
650 buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
651 ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
652 }
653
654 for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
655 {
656 ticks[i] = strip( ticks[i], interval );
657
658 // ticks very close to 0.0 are
659 // explicitely set to 0.0
660
661 for ( int j = 0; j < ticks[i].count(); j++ )
662 {
663 if ( qwtFuzzyCompare( ticks[i][j], 0.0, stepSize ) == 0 )
664 ticks[i][j] = 0.0;
665 }
666 }
667}
668
669/*!
670 \brief Calculate major ticks for an interval
671
672 \param interval Interval
673 \param stepSize Step size
674
675 \return Calculated ticks
676*/
677QList<double> QwtLinearScaleEngine::buildMajorTicks(
678 const QwtInterval &interval, double stepSize ) const
679{
680 int numTicks = qRound( interval.width() / stepSize ) + 1;
681 if ( numTicks > 10000 )
682 numTicks = 10000;
683
684 QList<double> ticks;
685
686 ticks += interval.minValue();
687 for ( int i = 1; i < numTicks - 1; i++ )
688 ticks += interval.minValue() + i * stepSize;
689 ticks += interval.maxValue();
690
691 return ticks;
692}
693
694/*!
695 \brief Calculate minor/medium ticks for major ticks
696
697 \param majorTicks Major ticks
698 \param maxMinorSteps Maximum number of minor steps
699 \param stepSize Step size
700 \param minorTicks Array to be filled with the calculated minor ticks
701 \param mediumTicks Array to be filled with the calculated medium ticks
702
703*/
704void QwtLinearScaleEngine::buildMinorTicks(
705 const QList<double>& majorTicks,
706 int maxMinorSteps, double stepSize,
707 QList<double> &minorTicks,
708 QList<double> &mediumTicks ) const
709{
710 double minStep = qwtStepSize( stepSize, maxMinorSteps, base() );
711 if ( minStep == 0.0 )
712 return;
713
714 // # ticks per interval
715 const int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1;
716
717 int medIndex = -1;
718 if ( numTicks % 2 )
719 medIndex = numTicks / 2;
720
721 // calculate minor ticks
722
723 for ( int i = 0; i < majorTicks.count(); i++ )
724 {
725 double val = majorTicks[i];
726 for ( int k = 0; k < numTicks; k++ )
727 {
728 val += minStep;
729
730 double alignedValue = val;
731 if ( qwtFuzzyCompare( val, 0.0, stepSize ) == 0 )
732 alignedValue = 0.0;
733
734 if ( k == medIndex )
735 mediumTicks += alignedValue;
736 else
737 minorTicks += alignedValue;
738 }
739 }
740}
741
742/*!
743 \brief Align an interval to a step size
744
745 The limits of an interval are aligned that both are integer
746 multiples of the step size.
747
748 \param interval Interval
749 \param stepSize Step size
750
751 \return Aligned interval
752*/
753QwtInterval QwtLinearScaleEngine::align(
754 const QwtInterval &interval, double stepSize ) const
755{
756 double x1 = interval.minValue();
757 double x2 = interval.maxValue();
758
759 // when there is no rounding beside some effect, when
760 // calculating with doubles, we keep the original value
761
762 const double eps = 0.000000000001; // since Qt 4.8: qFuzzyIsNull
763 if ( -DBL_MAX + stepSize <= x1 )
764 {
765 const double x = QwtScaleArithmetic::floorEps( x1, stepSize );
766 if ( qAbs(x) <= eps || !qFuzzyCompare( x1, x ) )
767 x1 = x;
768 }
769
770 if ( DBL_MAX - stepSize >= x2 )
771 {
772 const double x = QwtScaleArithmetic::ceilEps( x2, stepSize );
773 if ( qAbs(x) <= eps || !qFuzzyCompare( x2, x ) )
774 x2 = x;
775 }
776
777 return QwtInterval( x1, x2 );
778}
779
780/*!
781 Constructor
782
783 \param base Base of the scale engine
784 \sa setBase()
785 */
786QwtLogScaleEngine::QwtLogScaleEngine( uint base ):
787 QwtScaleEngine( base )
788{
789 setTransformation( new QwtLogTransform() );
790}
791
792//! Destructor
793QwtLogScaleEngine::~QwtLogScaleEngine()
794{
795}
796
797/*!
798 Align and divide an interval
799
800 \param maxNumSteps Max. number of steps
801 \param x1 First limit of the interval (In/Out)
802 \param x2 Second limit of the interval (In/Out)
803 \param stepSize Step size (Out)
804
805 \sa QwtScaleEngine::setAttribute()
806*/
807void QwtLogScaleEngine::autoScale( int maxNumSteps,
808 double &x1, double &x2, double &stepSize ) const
809{
810 if ( x1 > x2 )
811 qSwap( x1, x2 );
812
813 const double logBase = base();
814
815 QwtInterval interval( x1 / qPow( logBase, lowerMargin() ),
816 x2 * qPow( logBase, upperMargin() ) );
817
818 if ( interval.maxValue() / interval.minValue() < logBase )
819 {
820 // scale width is less than one step -> try to build a linear scale
821
822 QwtLinearScaleEngine linearScaler;
823 linearScaler.setAttributes( attributes() );
824 linearScaler.setReference( reference() );
825 linearScaler.setMargins( lowerMargin(), upperMargin() );
826
827 linearScaler.autoScale( maxNumSteps, x1, x2, stepSize );
828
829 QwtInterval linearInterval = QwtInterval( x1, x2 ).normalized();
830 linearInterval = linearInterval.limited( LOG_MIN, LOG_MAX );
831
832 if ( linearInterval.maxValue() / linearInterval.minValue() < logBase )
833 {
834 // the aligned scale is still less than one step
835
836#if 1
837 // this code doesn't make any sense, but for compatibility
838 // reasons we keep it until 6.2. But it will be ignored
839 // in divideScale
840
841 if ( stepSize < 0.0 )
842 stepSize = -qwtLog( logBase, qAbs( stepSize ) );
843 else
844 stepSize = qwtLog( logBase, stepSize );
845#endif
846
847 return;
848 }
849 }
850
851 double logRef = 1.0;
852 if ( reference() > LOG_MIN / 2 )
853 logRef = qMin( reference(), LOG_MAX / 2 );
854
855 if ( testAttribute( QwtScaleEngine::Symmetric ) )
856 {
857 const double delta = qMax( interval.maxValue() / logRef,
858 logRef / interval.minValue() );
859 interval.setInterval( logRef / delta, logRef * delta );
860 }
861
862 if ( testAttribute( QwtScaleEngine::IncludeReference ) )
863 interval = interval.extend( logRef );
864
865 interval = interval.limited( LOG_MIN, LOG_MAX );
866
867 if ( interval.width() == 0.0 )
868 interval = buildInterval( interval.minValue() );
869
870 stepSize = divideInterval( qwtLogInterval( logBase, interval ).width(),
871 qMax( maxNumSteps, 1 ) );
872 if ( stepSize < 1.0 )
873 stepSize = 1.0;
874
875 if ( !testAttribute( QwtScaleEngine::Floating ) )
876 interval = align( interval, stepSize );
877
878 x1 = interval.minValue();
879 x2 = interval.maxValue();
880
881 if ( testAttribute( QwtScaleEngine::Inverted ) )
882 {
883 qSwap( x1, x2 );
884 stepSize = -stepSize;
885 }
886}
887
888/*!
889 \brief Calculate a scale division for an interval
890
891 \param x1 First interval limit
892 \param x2 Second interval limit
893 \param maxMajorSteps Maximum for the number of major steps
894 \param maxMinorSteps Maximum number of minor steps
895 \param stepSize Step size. If stepSize == 0, the engine
896 calculates one.
897
898 \return Calculated scale division
899*/
900QwtScaleDiv QwtLogScaleEngine::divideScale( double x1, double x2,
901 int maxMajorSteps, int maxMinorSteps, double stepSize ) const
902{
903 QwtInterval interval = QwtInterval( x1, x2 ).normalized();
904 interval = interval.limited( LOG_MIN, LOG_MAX );
905
906 if ( interval.width() <= 0 )
907 return QwtScaleDiv();
908
909 const double logBase = base();
910
911 if ( interval.maxValue() / interval.minValue() < logBase )
912 {
913 // scale width is less than one decade -> build linear scale
914
915 QwtLinearScaleEngine linearScaler;
916 linearScaler.setAttributes( attributes() );
917 linearScaler.setReference( reference() );
918 linearScaler.setMargins( lowerMargin(), upperMargin() );
919
920 return linearScaler.divideScale( x1, x2,
921 maxMajorSteps, maxMinorSteps, 0.0 );
922 }
923
924 stepSize = qAbs( stepSize );
925 if ( stepSize == 0.0 )
926 {
927 if ( maxMajorSteps < 1 )
928 maxMajorSteps = 1;
929
930 stepSize = divideInterval(
931 qwtLogInterval( logBase, interval ).width(), maxMajorSteps );
932 if ( stepSize < 1.0 )
933 stepSize = 1.0; // major step must be >= 1 decade
934 }
935
936 QwtScaleDiv scaleDiv;
937 if ( stepSize != 0.0 )
938 {
939 QList<double> ticks[QwtScaleDiv::NTickTypes];
940 buildTicks( interval, stepSize, maxMinorSteps, ticks );
941
942 scaleDiv = QwtScaleDiv( interval, ticks );
943 }
944
945 if ( x1 > x2 )
946 scaleDiv.invert();
947
948 return scaleDiv;
949}
950
951/*!
952 \brief Calculate ticks for an interval
953
954 \param interval Interval
955 \param maxMinorSteps Maximum number of minor steps
956 \param stepSize Step size
957 \param ticks Arrays to be filled with the calculated ticks
958
959 \sa buildMajorTicks(), buildMinorTicks
960*/
961void QwtLogScaleEngine::buildTicks(
962 const QwtInterval& interval, double stepSize, int maxMinorSteps,
963 QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
964{
965 const QwtInterval boundingInterval = align( interval, stepSize );
966
967 ticks[QwtScaleDiv::MajorTick] =
968 buildMajorTicks( boundingInterval, stepSize );
969
970 if ( maxMinorSteps > 0 )
971 {
972 buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
973 ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
974 }
975
976 for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
977 ticks[i] = strip( ticks[i], interval );
978}
979
980/*!
981 \brief Calculate major ticks for an interval
982
983 \param interval Interval
984 \param stepSize Step size
985
986 \return Calculated ticks
987*/
988QList<double> QwtLogScaleEngine::buildMajorTicks(
989 const QwtInterval &interval, double stepSize ) const
990{
991 double width = qwtLogInterval( base(), interval ).width();
992
993 int numTicks = qRound( width / stepSize ) + 1;
994 if ( numTicks > 10000 )
995 numTicks = 10000;
996
997 const double lxmin = ::log( interval.minValue() );
998 const double lxmax = ::log( interval.maxValue() );
999 const double lstep = ( lxmax - lxmin ) / double( numTicks - 1 );
1000
1001 QList<double> ticks;
1002
1003 ticks += interval.minValue();
1004
1005 for ( int i = 1; i < numTicks - 1; i++ )
1006 ticks += qExp( lxmin + double( i ) * lstep );
1007
1008 ticks += interval.maxValue();
1009
1010 return ticks;
1011}
1012
1013/*!
1014 \brief Calculate minor/medium ticks for major ticks
1015
1016 \param majorTicks Major ticks
1017 \param maxMinorSteps Maximum number of minor steps
1018 \param stepSize Step size
1019 \param minorTicks Array to be filled with the calculated minor ticks
1020 \param mediumTicks Array to be filled with the calculated medium ticks
1021*/
1022void QwtLogScaleEngine::buildMinorTicks(
1023 const QList<double> &majorTicks,
1024 int maxMinorSteps, double stepSize,
1025 QList<double> &minorTicks,
1026 QList<double> &mediumTicks ) const
1027{
1028 const double logBase = base();
1029
1030 if ( stepSize < 1.1 ) // major step width is one base
1031 {
1032 double minStep = divideInterval( stepSize, maxMinorSteps + 1 );
1033 if ( minStep == 0.0 )
1034 return;
1035
1036 const int numSteps = qRound( stepSize / minStep );
1037
1038 int mediumTickIndex = -1;
1039 if ( ( numSteps > 2 ) && ( numSteps % 2 == 0 ) )
1040 mediumTickIndex = numSteps / 2;
1041
1042 for ( int i = 0; i < majorTicks.count() - 1; i++ )
1043 {
1044 const double v = majorTicks[i];
1045 const double s = logBase / numSteps;
1046
1047 if ( s >= 1.0 )
1048 {
1049 if ( !qFuzzyCompare( s, 1.0 ) )
1050 minorTicks += v * s;
1051
1052 for ( int j = 2; j < numSteps; j++ )
1053 {
1054 minorTicks += v * j * s;
1055 }
1056 }
1057 else
1058 {
1059 for ( int j = 1; j < numSteps; j++ )
1060 {
1061 const double tick = v + j * v * ( logBase - 1 ) / numSteps;
1062 if ( j == mediumTickIndex )
1063 mediumTicks += tick;
1064 else
1065 minorTicks += tick;
1066 }
1067 }
1068 }
1069 }
1070 else
1071 {
1072 double minStep = divideInterval( stepSize, maxMinorSteps );
1073 if ( minStep == 0.0 )
1074 return;
1075
1076 if ( minStep < 1.0 )
1077 minStep = 1.0;
1078
1079 // # subticks per interval
1080 int numTicks = qRound( stepSize / minStep ) - 1;
1081
1082 // Do the minor steps fit into the interval?
1083 if ( qwtFuzzyCompare( ( numTicks + 1 ) * minStep,
1084 stepSize, stepSize ) > 0 )
1085 {
1086 numTicks = 0;
1087 }
1088
1089 if ( numTicks < 1 )
1090 return;
1091
1092 int mediumTickIndex = -1;
1093 if ( ( numTicks > 2 ) && ( numTicks % 2 ) )
1094 mediumTickIndex = numTicks / 2;
1095
1096 // substep factor = base^substeps
1097 const qreal minFactor = qMax( qPow( logBase, minStep ), qreal( logBase ) );
1098
1099 for ( int i = 0; i < majorTicks.count(); i++ )
1100 {
1101 double tick = majorTicks[i];
1102 for ( int j = 0; j < numTicks; j++ )
1103 {
1104 tick *= minFactor;
1105
1106 if ( j == mediumTickIndex )
1107 mediumTicks += tick;
1108 else
1109 minorTicks += tick;
1110 }
1111 }
1112 }
1113}
1114
1115/*!
1116 \brief Align an interval to a step size
1117
1118 The limits of an interval are aligned that both are integer
1119 multiples of the step size.
1120
1121 \param interval Interval
1122 \param stepSize Step size
1123
1124 \return Aligned interval
1125*/
1126QwtInterval QwtLogScaleEngine::align(
1127 const QwtInterval &interval, double stepSize ) const
1128{
1129 const QwtInterval intv = qwtLogInterval( base(), interval );
1130
1131 double x1 = QwtScaleArithmetic::floorEps( intv.minValue(), stepSize );
1132 if ( qwtFuzzyCompare( interval.minValue(), x1, stepSize ) == 0 )
1133 x1 = interval.minValue();
1134
1135 double x2 = QwtScaleArithmetic::ceilEps( intv.maxValue(), stepSize );
1136 if ( qwtFuzzyCompare( interval.maxValue(), x2, stepSize ) == 0 )
1137 x2 = interval.maxValue();
1138
1139 return qwtPowInterval( base(), QwtInterval( x1, x2 ) );
1140}
Note: See TracBrowser for help on using the repository browser.