[4271] | 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 |
|
---|
| 16 | #if QT_VERSION < 0x040601
|
---|
| 17 | #define qFabs(x) ::fabs(x)
|
---|
| 18 | #define qExp(x) ::exp(x)
|
---|
| 19 | #endif
|
---|
| 20 |
|
---|
| 21 | static const double _eps = 1.0e-6;
|
---|
| 22 |
|
---|
| 23 | /*!
|
---|
| 24 | Ceil a value, relative to an interval
|
---|
| 25 |
|
---|
| 26 | \param value Value to ceil
|
---|
| 27 | \param intervalSize Interval size
|
---|
| 28 |
|
---|
| 29 | \sa floorEps()
|
---|
| 30 | */
|
---|
| 31 | double QwtScaleArithmetic::ceilEps( double value,
|
---|
| 32 | double intervalSize )
|
---|
| 33 | {
|
---|
| 34 | const double eps = _eps * intervalSize;
|
---|
| 35 |
|
---|
| 36 | value = ( value - eps ) / intervalSize;
|
---|
| 37 | return qwtCeilF( value ) * intervalSize;
|
---|
| 38 | }
|
---|
| 39 |
|
---|
| 40 | /*!
|
---|
| 41 | Floor a value, relative to an interval
|
---|
| 42 |
|
---|
| 43 | \param value Value to floor
|
---|
| 44 | \param intervalSize Interval size
|
---|
| 45 |
|
---|
| 46 | \sa floorEps()
|
---|
| 47 | */
|
---|
| 48 | double QwtScaleArithmetic::floorEps( double value, double intervalSize )
|
---|
| 49 | {
|
---|
| 50 | const double eps = _eps * intervalSize;
|
---|
| 51 |
|
---|
| 52 | value = ( value + eps ) / intervalSize;
|
---|
| 53 | return qwtFloorF( value ) * intervalSize;
|
---|
| 54 | }
|
---|
| 55 |
|
---|
| 56 | /*!
|
---|
| 57 | \brief Divide an interval into steps
|
---|
| 58 |
|
---|
| 59 | \f$stepSize = (intervalSize - intervalSize * 10e^{-6}) / numSteps\f$
|
---|
| 60 |
|
---|
| 61 | \param intervalSize Interval size
|
---|
| 62 | \param numSteps Number of steps
|
---|
| 63 | \return Step size
|
---|
| 64 | */
|
---|
| 65 | double QwtScaleArithmetic::divideEps( double intervalSize, double numSteps )
|
---|
| 66 | {
|
---|
| 67 | if ( numSteps == 0.0 || intervalSize == 0.0 )
|
---|
| 68 | return 0.0;
|
---|
| 69 |
|
---|
| 70 | return ( intervalSize - ( _eps * intervalSize ) ) / numSteps;
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | /*!
|
---|
| 74 | Find the smallest value out of {1,2,5}*10^n with an integer number n
|
---|
| 75 | which is greater than or equal to x
|
---|
| 76 |
|
---|
| 77 | \param x Input value
|
---|
| 78 | */
|
---|
| 79 | double QwtScaleArithmetic::ceil125( double x )
|
---|
| 80 | {
|
---|
| 81 | if ( x == 0.0 )
|
---|
| 82 | return 0.0;
|
---|
| 83 |
|
---|
| 84 | const double sign = ( x > 0 ) ? 1.0 : -1.0;
|
---|
| 85 | const double lx = ::log10( qFabs( x ) );
|
---|
| 86 | const double p10 = qwtFloorF( lx );
|
---|
| 87 |
|
---|
| 88 | double fr = qPow( 10.0, lx - p10 );
|
---|
| 89 | if ( fr <= 1.0 )
|
---|
| 90 | fr = 1.0;
|
---|
| 91 | else if ( fr <= 2.0 )
|
---|
| 92 | fr = 2.0;
|
---|
| 93 | else if ( fr <= 5.0 )
|
---|
| 94 | fr = 5.0;
|
---|
| 95 | else
|
---|
| 96 | fr = 10.0;
|
---|
| 97 |
|
---|
| 98 | return sign * fr * qPow( 10.0, p10 );
|
---|
| 99 | }
|
---|
| 100 |
|
---|
| 101 | /*!
|
---|
| 102 | \brief Find the largest value out of {1,2,5}*10^n with an integer number n
|
---|
| 103 | which is smaller than or equal to x
|
---|
| 104 |
|
---|
| 105 | \param x Input value
|
---|
| 106 | */
|
---|
| 107 | double QwtScaleArithmetic::floor125( double x )
|
---|
| 108 | {
|
---|
| 109 | if ( x == 0.0 )
|
---|
| 110 | return 0.0;
|
---|
| 111 |
|
---|
| 112 | double sign = ( x > 0 ) ? 1.0 : -1.0;
|
---|
| 113 | const double lx = ::log10( qFabs( x ) );
|
---|
| 114 | const double p10 = qwtFloorF( lx );
|
---|
| 115 |
|
---|
| 116 | double fr = qPow( 10.0, lx - p10 );
|
---|
| 117 | if ( fr >= 10.0 )
|
---|
| 118 | fr = 10.0;
|
---|
| 119 | else if ( fr >= 5.0 )
|
---|
| 120 | fr = 5.0;
|
---|
| 121 | else if ( fr >= 2.0 )
|
---|
| 122 | fr = 2.0;
|
---|
| 123 | else
|
---|
| 124 | fr = 1.0;
|
---|
| 125 |
|
---|
| 126 | return sign * fr * qPow( 10.0, p10 );
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | class QwtScaleEngine::PrivateData
|
---|
| 130 | {
|
---|
| 131 | public:
|
---|
| 132 | PrivateData():
|
---|
| 133 | attributes( QwtScaleEngine::NoAttribute ),
|
---|
| 134 | lowerMargin( 0.0 ),
|
---|
| 135 | upperMargin( 0.0 ),
|
---|
| 136 | referenceValue( 0.0 )
|
---|
| 137 | {
|
---|
| 138 | }
|
---|
| 139 |
|
---|
| 140 | QwtScaleEngine::Attributes attributes; // scale attributes
|
---|
| 141 |
|
---|
| 142 | double lowerMargin; // margins
|
---|
| 143 | double upperMargin;
|
---|
| 144 |
|
---|
| 145 | double referenceValue; // reference value
|
---|
| 146 |
|
---|
| 147 | };
|
---|
| 148 |
|
---|
| 149 | //! Constructor
|
---|
| 150 | QwtScaleEngine::QwtScaleEngine()
|
---|
| 151 | {
|
---|
| 152 | d_data = new PrivateData;
|
---|
| 153 | }
|
---|
| 154 |
|
---|
| 155 |
|
---|
| 156 | //! Destructor
|
---|
| 157 | QwtScaleEngine::~QwtScaleEngine ()
|
---|
| 158 | {
|
---|
| 159 | delete d_data;
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | /*!
|
---|
| 163 | \return the margin at the lower end of the scale
|
---|
| 164 | The default margin is 0.
|
---|
| 165 |
|
---|
| 166 | \sa setMargins()
|
---|
| 167 | */
|
---|
| 168 | double QwtScaleEngine::lowerMargin() const
|
---|
| 169 | {
|
---|
| 170 | return d_data->lowerMargin;
|
---|
| 171 | }
|
---|
| 172 |
|
---|
| 173 | /*!
|
---|
| 174 | \return the margin at the upper end of the scale
|
---|
| 175 | The default margin is 0.
|
---|
| 176 |
|
---|
| 177 | \sa setMargins()
|
---|
| 178 | */
|
---|
| 179 | double QwtScaleEngine::upperMargin() const
|
---|
| 180 | {
|
---|
| 181 | return d_data->upperMargin;
|
---|
| 182 | }
|
---|
| 183 |
|
---|
| 184 | /*!
|
---|
| 185 | \brief Specify margins at the scale's endpoints
|
---|
| 186 | \param lower minimum distance between the scale's lower boundary and the
|
---|
| 187 | smallest enclosed value
|
---|
| 188 | \param upper minimum distance between the scale's upper boundary and the
|
---|
| 189 | greatest enclosed value
|
---|
| 190 |
|
---|
| 191 | Margins can be used to leave a minimum amount of space between
|
---|
| 192 | the enclosed intervals and the boundaries of the scale.
|
---|
| 193 |
|
---|
| 194 | \warning
|
---|
| 195 | \li QwtLog10ScaleEngine measures the margins in decades.
|
---|
| 196 |
|
---|
| 197 | \sa upperMargin(), lowerMargin()
|
---|
| 198 | */
|
---|
| 199 |
|
---|
| 200 | void QwtScaleEngine::setMargins( double lower, double upper )
|
---|
| 201 | {
|
---|
| 202 | d_data->lowerMargin = qMax( lower, 0.0 );
|
---|
| 203 | d_data->upperMargin = qMax( upper, 0.0 );
|
---|
| 204 | }
|
---|
| 205 |
|
---|
| 206 | /*!
|
---|
| 207 | Calculate a step size for an interval size
|
---|
| 208 |
|
---|
| 209 | \param intervalSize Interval size
|
---|
| 210 | \param numSteps Number of steps
|
---|
| 211 |
|
---|
| 212 | \return Step size
|
---|
| 213 | */
|
---|
| 214 | double QwtScaleEngine::divideInterval(
|
---|
| 215 | double intervalSize, int numSteps ) const
|
---|
| 216 | {
|
---|
| 217 | if ( numSteps <= 0 )
|
---|
| 218 | return 0.0;
|
---|
| 219 |
|
---|
| 220 | double v = QwtScaleArithmetic::divideEps( intervalSize, numSteps );
|
---|
| 221 | return QwtScaleArithmetic::ceil125( v );
|
---|
| 222 | }
|
---|
| 223 |
|
---|
| 224 | /*!
|
---|
| 225 | Check if an interval "contains" a value
|
---|
| 226 |
|
---|
| 227 | \param interval Interval
|
---|
| 228 | \param value Value
|
---|
| 229 |
|
---|
| 230 | \sa QwtScaleArithmetic::compareEps()
|
---|
| 231 | */
|
---|
| 232 | bool QwtScaleEngine::contains(
|
---|
| 233 | const QwtInterval &interval, double value ) const
|
---|
| 234 | {
|
---|
| 235 | if ( !interval.isValid() )
|
---|
| 236 | return false;
|
---|
| 237 |
|
---|
| 238 | if ( qwtFuzzyCompare( value, interval.minValue(), interval.width() ) < 0 )
|
---|
| 239 | return false;
|
---|
| 240 |
|
---|
| 241 | if ( qwtFuzzyCompare( value, interval.maxValue(), interval.width() ) > 0 )
|
---|
| 242 | return false;
|
---|
| 243 |
|
---|
| 244 | return true;
|
---|
| 245 | }
|
---|
| 246 |
|
---|
| 247 | /*!
|
---|
| 248 | Remove ticks from a list, that are not inside an interval
|
---|
| 249 |
|
---|
| 250 | \param ticks Tick list
|
---|
| 251 | \param interval Interval
|
---|
| 252 |
|
---|
| 253 | \return Stripped tick list
|
---|
| 254 | */
|
---|
| 255 | QList<double> QwtScaleEngine::strip( const QList<double>& ticks,
|
---|
| 256 | const QwtInterval &interval ) const
|
---|
| 257 | {
|
---|
| 258 | if ( !interval.isValid() || ticks.count() == 0 )
|
---|
| 259 | return QList<double>();
|
---|
| 260 |
|
---|
| 261 | if ( contains( interval, ticks.first() )
|
---|
| 262 | && contains( interval, ticks.last() ) )
|
---|
| 263 | {
|
---|
| 264 | return ticks;
|
---|
| 265 | }
|
---|
| 266 |
|
---|
| 267 | QList<double> strippedTicks;
|
---|
| 268 | for ( int i = 0; i < ticks.count(); i++ )
|
---|
| 269 | {
|
---|
| 270 | if ( contains( interval, ticks[i] ) )
|
---|
| 271 | strippedTicks += ticks[i];
|
---|
| 272 | }
|
---|
| 273 | return strippedTicks;
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | /*!
|
---|
| 277 | \brief Build an interval for a value
|
---|
| 278 |
|
---|
| 279 | In case of v == 0.0 the interval is [-0.5, 0.5],
|
---|
| 280 | otherwide it is [0.5 * v, 1.5 * v]
|
---|
| 281 | */
|
---|
| 282 |
|
---|
| 283 | QwtInterval QwtScaleEngine::buildInterval( double v ) const
|
---|
| 284 | {
|
---|
| 285 | const double delta = ( v == 0.0 ) ? 0.5 : qAbs( 0.5 * v );
|
---|
| 286 | return QwtInterval( v - delta, v + delta );
|
---|
| 287 | }
|
---|
| 288 |
|
---|
| 289 | /*!
|
---|
| 290 | Change a scale attribute
|
---|
| 291 |
|
---|
| 292 | \param attribute Attribute to change
|
---|
| 293 | \param on On/Off
|
---|
| 294 |
|
---|
| 295 | \sa Attribute, testAttribute()
|
---|
| 296 | */
|
---|
| 297 | void QwtScaleEngine::setAttribute( Attribute attribute, bool on )
|
---|
| 298 | {
|
---|
| 299 | if ( on )
|
---|
| 300 | d_data->attributes |= attribute;
|
---|
| 301 | else
|
---|
| 302 | d_data->attributes &= ~attribute;
|
---|
| 303 | }
|
---|
| 304 |
|
---|
| 305 | /*!
|
---|
| 306 | Check if a attribute is set.
|
---|
| 307 |
|
---|
| 308 | \param attribute Attribute to be tested
|
---|
| 309 | \sa Attribute, setAttribute()
|
---|
| 310 | */
|
---|
| 311 | bool QwtScaleEngine::testAttribute( Attribute attribute ) const
|
---|
| 312 | {
|
---|
| 313 | return ( d_data->attributes & attribute );
|
---|
| 314 | }
|
---|
| 315 |
|
---|
| 316 | /*!
|
---|
| 317 | Change the scale attribute
|
---|
| 318 |
|
---|
| 319 | \param attributes Set scale attributes
|
---|
| 320 | \sa Attribute, attributes()
|
---|
| 321 | */
|
---|
| 322 | void QwtScaleEngine::setAttributes( Attributes attributes )
|
---|
| 323 | {
|
---|
| 324 | d_data->attributes = attributes;
|
---|
| 325 | }
|
---|
| 326 |
|
---|
| 327 | /*!
|
---|
| 328 | Return the scale attributes
|
---|
| 329 | \sa Attribute, setAttributes(), testAttribute()
|
---|
| 330 | */
|
---|
| 331 | QwtScaleEngine::Attributes QwtScaleEngine::attributes() const
|
---|
| 332 | {
|
---|
| 333 | return d_data->attributes;
|
---|
| 334 | }
|
---|
| 335 |
|
---|
| 336 | /*!
|
---|
| 337 | \brief Specify a reference point
|
---|
| 338 | \param r new reference value
|
---|
| 339 |
|
---|
| 340 | The reference point is needed if options IncludeReference or
|
---|
| 341 | Symmetric are active. Its default value is 0.0.
|
---|
| 342 |
|
---|
| 343 | \sa Attribute
|
---|
| 344 | */
|
---|
| 345 | void QwtScaleEngine::setReference( double r )
|
---|
| 346 | {
|
---|
| 347 | d_data->referenceValue = r;
|
---|
| 348 | }
|
---|
| 349 |
|
---|
| 350 | /*!
|
---|
| 351 | \return the reference value
|
---|
| 352 | \sa setReference(), setAttribute()
|
---|
| 353 | */
|
---|
| 354 | double QwtScaleEngine::reference() const
|
---|
| 355 | {
|
---|
| 356 | return d_data->referenceValue;
|
---|
| 357 | }
|
---|
| 358 |
|
---|
| 359 | /*!
|
---|
| 360 | Return a transformation, for linear scales
|
---|
| 361 | */
|
---|
| 362 | QwtScaleTransformation *QwtLinearScaleEngine::transformation() const
|
---|
| 363 | {
|
---|
| 364 | return new QwtScaleTransformation( QwtScaleTransformation::Linear );
|
---|
| 365 | }
|
---|
| 366 |
|
---|
| 367 | /*!
|
---|
| 368 | Align and divide an interval
|
---|
| 369 |
|
---|
| 370 | \param maxNumSteps Max. number of steps
|
---|
| 371 | \param x1 First limit of the interval (In/Out)
|
---|
| 372 | \param x2 Second limit of the interval (In/Out)
|
---|
| 373 | \param stepSize Step size (Out)
|
---|
| 374 |
|
---|
| 375 | \sa setAttribute()
|
---|
| 376 | */
|
---|
| 377 | void QwtLinearScaleEngine::autoScale( int maxNumSteps,
|
---|
| 378 | double &x1, double &x2, double &stepSize ) const
|
---|
| 379 | {
|
---|
| 380 | QwtInterval interval( x1, x2 );
|
---|
| 381 | interval = interval.normalized();
|
---|
| 382 |
|
---|
| 383 | interval.setMinValue( interval.minValue() - lowerMargin() );
|
---|
| 384 | interval.setMaxValue( interval.maxValue() + upperMargin() );
|
---|
| 385 |
|
---|
| 386 | if ( testAttribute( QwtScaleEngine::Symmetric ) )
|
---|
| 387 | interval = interval.symmetrize( reference() );
|
---|
| 388 |
|
---|
| 389 | if ( testAttribute( QwtScaleEngine::IncludeReference ) )
|
---|
| 390 | interval = interval.extend( reference() );
|
---|
| 391 |
|
---|
| 392 | if ( interval.width() == 0.0 )
|
---|
| 393 | interval = buildInterval( interval.minValue() );
|
---|
| 394 |
|
---|
| 395 | stepSize = divideInterval( interval.width(), qMax( maxNumSteps, 1 ) );
|
---|
| 396 |
|
---|
| 397 | if ( !testAttribute( QwtScaleEngine::Floating ) )
|
---|
| 398 | interval = align( interval, stepSize );
|
---|
| 399 |
|
---|
| 400 | x1 = interval.minValue();
|
---|
| 401 | x2 = interval.maxValue();
|
---|
| 402 |
|
---|
| 403 | if ( testAttribute( QwtScaleEngine::Inverted ) )
|
---|
| 404 | {
|
---|
| 405 | qSwap( x1, x2 );
|
---|
| 406 | stepSize = -stepSize;
|
---|
| 407 | }
|
---|
| 408 | }
|
---|
| 409 |
|
---|
| 410 | /*!
|
---|
| 411 | \brief Calculate a scale division
|
---|
| 412 |
|
---|
| 413 | \param x1 First interval limit
|
---|
| 414 | \param x2 Second interval limit
|
---|
| 415 | \param maxMajSteps Maximum for the number of major steps
|
---|
| 416 | \param maxMinSteps Maximum number of minor steps
|
---|
| 417 | \param stepSize Step size. If stepSize == 0, the scaleEngine
|
---|
| 418 | calculates one.
|
---|
| 419 |
|
---|
| 420 | \sa QwtScaleEngine::stepSize(), QwtScaleEngine::subDivide()
|
---|
| 421 | */
|
---|
| 422 | QwtScaleDiv QwtLinearScaleEngine::divideScale( double x1, double x2,
|
---|
| 423 | int maxMajSteps, int maxMinSteps, double stepSize ) const
|
---|
| 424 | {
|
---|
| 425 | QwtInterval interval = QwtInterval( x1, x2 ).normalized();
|
---|
| 426 | if ( interval.width() <= 0 )
|
---|
| 427 | return QwtScaleDiv();
|
---|
| 428 |
|
---|
| 429 | stepSize = qAbs( stepSize );
|
---|
| 430 | if ( stepSize == 0.0 )
|
---|
| 431 | {
|
---|
| 432 | if ( maxMajSteps < 1 )
|
---|
| 433 | maxMajSteps = 1;
|
---|
| 434 |
|
---|
| 435 | stepSize = divideInterval( interval.width(), maxMajSteps );
|
---|
| 436 | }
|
---|
| 437 |
|
---|
| 438 | QwtScaleDiv scaleDiv;
|
---|
| 439 |
|
---|
| 440 | if ( stepSize != 0.0 )
|
---|
| 441 | {
|
---|
| 442 | QList<double> ticks[QwtScaleDiv::NTickTypes];
|
---|
| 443 | buildTicks( interval, stepSize, maxMinSteps, ticks );
|
---|
| 444 |
|
---|
| 445 | scaleDiv = QwtScaleDiv( interval, ticks );
|
---|
| 446 | }
|
---|
| 447 |
|
---|
| 448 | if ( x1 > x2 )
|
---|
| 449 | scaleDiv.invert();
|
---|
| 450 |
|
---|
| 451 | return scaleDiv;
|
---|
| 452 | }
|
---|
| 453 |
|
---|
| 454 | /*!
|
---|
| 455 | \brief Calculate ticks for an interval
|
---|
| 456 |
|
---|
| 457 | \param interval Interval
|
---|
| 458 | \param stepSize Step size
|
---|
| 459 | \param maxMinSteps Maximum number of minor steps
|
---|
| 460 | \param ticks Arrays to be filled with the calculated ticks
|
---|
| 461 |
|
---|
| 462 | \sa buildMajorTicks(), buildMinorTicks
|
---|
| 463 | */
|
---|
| 464 | void QwtLinearScaleEngine::buildTicks(
|
---|
| 465 | const QwtInterval& interval, double stepSize, int maxMinSteps,
|
---|
| 466 | QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
|
---|
| 467 | {
|
---|
| 468 | const QwtInterval boundingInterval =
|
---|
| 469 | align( interval, stepSize );
|
---|
| 470 |
|
---|
| 471 | ticks[QwtScaleDiv::MajorTick] =
|
---|
| 472 | buildMajorTicks( boundingInterval, stepSize );
|
---|
| 473 |
|
---|
| 474 | if ( maxMinSteps > 0 )
|
---|
| 475 | {
|
---|
| 476 | buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize,
|
---|
| 477 | ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
|
---|
| 478 | }
|
---|
| 479 |
|
---|
| 480 | for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
|
---|
| 481 | {
|
---|
| 482 | ticks[i] = strip( ticks[i], interval );
|
---|
| 483 |
|
---|
| 484 | // ticks very close to 0.0 are
|
---|
| 485 | // explicitely set to 0.0
|
---|
| 486 |
|
---|
| 487 | for ( int j = 0; j < ticks[i].count(); j++ )
|
---|
| 488 | {
|
---|
| 489 | if ( qwtFuzzyCompare( ticks[i][j], 0.0, stepSize ) == 0 )
|
---|
| 490 | ticks[i][j] = 0.0;
|
---|
| 491 | }
|
---|
| 492 | }
|
---|
| 493 | }
|
---|
| 494 |
|
---|
| 495 | /*!
|
---|
| 496 | \brief Calculate major ticks for an interval
|
---|
| 497 |
|
---|
| 498 | \param interval Interval
|
---|
| 499 | \param stepSize Step size
|
---|
| 500 |
|
---|
| 501 | \return Calculated ticks
|
---|
| 502 | */
|
---|
| 503 | QList<double> QwtLinearScaleEngine::buildMajorTicks(
|
---|
| 504 | const QwtInterval &interval, double stepSize ) const
|
---|
| 505 | {
|
---|
| 506 | int numTicks = qRound( interval.width() / stepSize ) + 1;
|
---|
| 507 | if ( numTicks > 10000 )
|
---|
| 508 | numTicks = 10000;
|
---|
| 509 |
|
---|
| 510 | QList<double> ticks;
|
---|
| 511 |
|
---|
| 512 | ticks += interval.minValue();
|
---|
| 513 | for ( int i = 1; i < numTicks - 1; i++ )
|
---|
| 514 | ticks += interval.minValue() + i * stepSize;
|
---|
| 515 | ticks += interval.maxValue();
|
---|
| 516 |
|
---|
| 517 | return ticks;
|
---|
| 518 | }
|
---|
| 519 |
|
---|
| 520 | /*!
|
---|
| 521 | \brief Calculate minor/medium ticks for major ticks
|
---|
| 522 |
|
---|
| 523 | \param majorTicks Major ticks
|
---|
| 524 | \param maxMinSteps Maximum number of minor steps
|
---|
| 525 | \param stepSize Step size
|
---|
| 526 | \param minorTicks Array to be filled with the calculated minor ticks
|
---|
| 527 | \param mediumTicks Array to be filled with the calculated medium ticks
|
---|
| 528 |
|
---|
| 529 | */
|
---|
| 530 | void QwtLinearScaleEngine::buildMinorTicks(
|
---|
| 531 | const QList<double>& majorTicks,
|
---|
| 532 | int maxMinSteps, double stepSize,
|
---|
| 533 | QList<double> &minorTicks,
|
---|
| 534 | QList<double> &mediumTicks ) const
|
---|
| 535 | {
|
---|
| 536 | double minStep = divideInterval( stepSize, maxMinSteps );
|
---|
| 537 | if ( minStep == 0.0 )
|
---|
| 538 | return;
|
---|
| 539 |
|
---|
| 540 | // # ticks per interval
|
---|
| 541 | int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1;
|
---|
| 542 |
|
---|
| 543 | // Do the minor steps fit into the interval?
|
---|
| 544 | if ( qwtFuzzyCompare( ( numTicks + 1 ) * qAbs( minStep ),
|
---|
| 545 | qAbs( stepSize ), stepSize ) > 0 )
|
---|
| 546 | {
|
---|
| 547 | numTicks = 1;
|
---|
| 548 | minStep = stepSize * 0.5;
|
---|
| 549 | }
|
---|
| 550 |
|
---|
| 551 | int medIndex = -1;
|
---|
| 552 | if ( numTicks % 2 )
|
---|
| 553 | medIndex = numTicks / 2;
|
---|
| 554 |
|
---|
| 555 | // calculate minor ticks
|
---|
| 556 |
|
---|
| 557 | for ( int i = 0; i < majorTicks.count(); i++ )
|
---|
| 558 | {
|
---|
| 559 | double val = majorTicks[i];
|
---|
| 560 | for ( int k = 0; k < numTicks; k++ )
|
---|
| 561 | {
|
---|
| 562 | val += minStep;
|
---|
| 563 |
|
---|
| 564 | double alignedValue = val;
|
---|
| 565 | if ( qwtFuzzyCompare( val, 0.0, stepSize ) == 0 )
|
---|
| 566 | alignedValue = 0.0;
|
---|
| 567 |
|
---|
| 568 | if ( k == medIndex )
|
---|
| 569 | mediumTicks += alignedValue;
|
---|
| 570 | else
|
---|
| 571 | minorTicks += alignedValue;
|
---|
| 572 | }
|
---|
| 573 | }
|
---|
| 574 | }
|
---|
| 575 |
|
---|
| 576 | /*!
|
---|
| 577 | \brief Align an interval to a step size
|
---|
| 578 |
|
---|
| 579 | The limits of an interval are aligned that both are integer
|
---|
| 580 | multiples of the step size.
|
---|
| 581 |
|
---|
| 582 | \param interval Interval
|
---|
| 583 | \param stepSize Step size
|
---|
| 584 |
|
---|
| 585 | \return Aligned interval
|
---|
| 586 | */
|
---|
| 587 | QwtInterval QwtLinearScaleEngine::align(
|
---|
| 588 | const QwtInterval &interval, double stepSize ) const
|
---|
| 589 | {
|
---|
| 590 | double x1 = QwtScaleArithmetic::floorEps( interval.minValue(), stepSize );
|
---|
| 591 | if ( qwtFuzzyCompare( interval.minValue(), x1, stepSize ) == 0 )
|
---|
| 592 | x1 = interval.minValue();
|
---|
| 593 |
|
---|
| 594 | double x2 = QwtScaleArithmetic::ceilEps( interval.maxValue(), stepSize );
|
---|
| 595 | if ( qwtFuzzyCompare( interval.maxValue(), x2, stepSize ) == 0 )
|
---|
| 596 | x2 = interval.maxValue();
|
---|
| 597 |
|
---|
| 598 | return QwtInterval( x1, x2 );
|
---|
| 599 | }
|
---|
| 600 |
|
---|
| 601 | /*!
|
---|
| 602 | Return a transformation, for logarithmic (base 10) scales
|
---|
| 603 | */
|
---|
| 604 | QwtScaleTransformation *QwtLog10ScaleEngine::transformation() const
|
---|
| 605 | {
|
---|
| 606 | return new QwtScaleTransformation( QwtScaleTransformation::Log10 );
|
---|
| 607 | }
|
---|
| 608 |
|
---|
| 609 | /*!
|
---|
| 610 | Align and divide an interval
|
---|
| 611 |
|
---|
| 612 | \param maxNumSteps Max. number of steps
|
---|
| 613 | \param x1 First limit of the interval (In/Out)
|
---|
| 614 | \param x2 Second limit of the interval (In/Out)
|
---|
| 615 | \param stepSize Step size (Out)
|
---|
| 616 |
|
---|
| 617 | \sa QwtScaleEngine::setAttribute()
|
---|
| 618 | */
|
---|
| 619 | void QwtLog10ScaleEngine::autoScale( int maxNumSteps,
|
---|
| 620 | double &x1, double &x2, double &stepSize ) const
|
---|
| 621 | {
|
---|
| 622 | if ( x1 > x2 )
|
---|
| 623 | qSwap( x1, x2 );
|
---|
| 624 |
|
---|
| 625 | QwtInterval interval( x1 / qPow( 10.0, lowerMargin() ),
|
---|
| 626 | x2 * qPow( 10.0, upperMargin() ) );
|
---|
| 627 |
|
---|
| 628 | if ( interval.maxValue() / interval.minValue() < 10.0 )
|
---|
| 629 | {
|
---|
| 630 | // scale width is less than one decade -> build linear scale
|
---|
| 631 |
|
---|
| 632 | QwtLinearScaleEngine linearScaler;
|
---|
| 633 | linearScaler.setAttributes( attributes() );
|
---|
| 634 | linearScaler.setReference( reference() );
|
---|
| 635 | linearScaler.setMargins( lowerMargin(), upperMargin() );
|
---|
| 636 |
|
---|
| 637 | linearScaler.autoScale( maxNumSteps, x1, x2, stepSize );
|
---|
| 638 | stepSize = ::log10( stepSize );
|
---|
| 639 |
|
---|
| 640 | return;
|
---|
| 641 | }
|
---|
| 642 |
|
---|
| 643 | double logRef = 1.0;
|
---|
| 644 | if ( reference() > LOG_MIN / 2 )
|
---|
| 645 | logRef = qMin( reference(), LOG_MAX / 2 );
|
---|
| 646 |
|
---|
| 647 | if ( testAttribute( QwtScaleEngine::Symmetric ) )
|
---|
| 648 | {
|
---|
| 649 | const double delta = qMax( interval.maxValue() / logRef,
|
---|
| 650 | logRef / interval.minValue() );
|
---|
| 651 | interval.setInterval( logRef / delta, logRef * delta );
|
---|
| 652 | }
|
---|
| 653 |
|
---|
| 654 | if ( testAttribute( QwtScaleEngine::IncludeReference ) )
|
---|
| 655 | interval = interval.extend( logRef );
|
---|
| 656 |
|
---|
| 657 | interval = interval.limited( LOG_MIN, LOG_MAX );
|
---|
| 658 |
|
---|
| 659 | if ( interval.width() == 0.0 )
|
---|
| 660 | interval = buildInterval( interval.minValue() );
|
---|
| 661 |
|
---|
| 662 | stepSize = divideInterval( log10( interval ).width(), qMax( maxNumSteps, 1 ) );
|
---|
| 663 | if ( stepSize < 1.0 )
|
---|
| 664 | stepSize = 1.0;
|
---|
| 665 |
|
---|
| 666 | if ( !testAttribute( QwtScaleEngine::Floating ) )
|
---|
| 667 | interval = align( interval, stepSize );
|
---|
| 668 |
|
---|
| 669 | x1 = interval.minValue();
|
---|
| 670 | x2 = interval.maxValue();
|
---|
| 671 |
|
---|
| 672 | if ( testAttribute( QwtScaleEngine::Inverted ) )
|
---|
| 673 | {
|
---|
| 674 | qSwap( x1, x2 );
|
---|
| 675 | stepSize = -stepSize;
|
---|
| 676 | }
|
---|
| 677 | }
|
---|
| 678 |
|
---|
| 679 | /*!
|
---|
| 680 | \brief Calculate a scale division
|
---|
| 681 |
|
---|
| 682 | \param x1 First interval limit
|
---|
| 683 | \param x2 Second interval limit
|
---|
| 684 | \param maxMajSteps Maximum for the number of major steps
|
---|
| 685 | \param maxMinSteps Maximum number of minor steps
|
---|
| 686 | \param stepSize Step size. If stepSize == 0, the scaleEngine
|
---|
| 687 | calculates one.
|
---|
| 688 |
|
---|
| 689 | \sa QwtScaleEngine::stepSize(), QwtLog10ScaleEngine::subDivide()
|
---|
| 690 | */
|
---|
| 691 | QwtScaleDiv QwtLog10ScaleEngine::divideScale( double x1, double x2,
|
---|
| 692 | int maxMajSteps, int maxMinSteps, double stepSize ) const
|
---|
| 693 | {
|
---|
| 694 | QwtInterval interval = QwtInterval( x1, x2 ).normalized();
|
---|
| 695 | interval = interval.limited( LOG_MIN, LOG_MAX );
|
---|
| 696 |
|
---|
| 697 | if ( interval.width() <= 0 )
|
---|
| 698 | return QwtScaleDiv();
|
---|
| 699 |
|
---|
| 700 | if ( interval.maxValue() / interval.minValue() < 10.0 )
|
---|
| 701 | {
|
---|
| 702 | // scale width is less than one decade -> build linear scale
|
---|
| 703 |
|
---|
| 704 | QwtLinearScaleEngine linearScaler;
|
---|
| 705 | linearScaler.setAttributes( attributes() );
|
---|
| 706 | linearScaler.setReference( reference() );
|
---|
| 707 | linearScaler.setMargins( lowerMargin(), upperMargin() );
|
---|
| 708 |
|
---|
| 709 | if ( stepSize != 0.0 )
|
---|
| 710 | stepSize = qPow( 10.0, stepSize );
|
---|
| 711 |
|
---|
| 712 | return linearScaler.divideScale( x1, x2,
|
---|
| 713 | maxMajSteps, maxMinSteps, stepSize );
|
---|
| 714 | }
|
---|
| 715 |
|
---|
| 716 | stepSize = qAbs( stepSize );
|
---|
| 717 | if ( stepSize == 0.0 )
|
---|
| 718 | {
|
---|
| 719 | if ( maxMajSteps < 1 )
|
---|
| 720 | maxMajSteps = 1;
|
---|
| 721 |
|
---|
| 722 | stepSize = divideInterval( log10( interval ).width(), maxMajSteps );
|
---|
| 723 | if ( stepSize < 1.0 )
|
---|
| 724 | stepSize = 1.0; // major step must be >= 1 decade
|
---|
| 725 | }
|
---|
| 726 |
|
---|
| 727 | QwtScaleDiv scaleDiv;
|
---|
| 728 | if ( stepSize != 0.0 )
|
---|
| 729 | {
|
---|
| 730 | QList<double> ticks[QwtScaleDiv::NTickTypes];
|
---|
| 731 | buildTicks( interval, stepSize, maxMinSteps, ticks );
|
---|
| 732 |
|
---|
| 733 | scaleDiv = QwtScaleDiv( interval, ticks );
|
---|
| 734 | }
|
---|
| 735 |
|
---|
| 736 | if ( x1 > x2 )
|
---|
| 737 | scaleDiv.invert();
|
---|
| 738 |
|
---|
| 739 | return scaleDiv;
|
---|
| 740 | }
|
---|
| 741 |
|
---|
| 742 | /*!
|
---|
| 743 | \brief Calculate ticks for an interval
|
---|
| 744 |
|
---|
| 745 | \param interval Interval
|
---|
| 746 | \param maxMinSteps Maximum number of minor steps
|
---|
| 747 | \param stepSize Step size
|
---|
| 748 | \param ticks Arrays to be filled with the calculated ticks
|
---|
| 749 |
|
---|
| 750 | \sa buildMajorTicks(), buildMinorTicks
|
---|
| 751 | */
|
---|
| 752 | void QwtLog10ScaleEngine::buildTicks(
|
---|
| 753 | const QwtInterval& interval, double stepSize, int maxMinSteps,
|
---|
| 754 | QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
|
---|
| 755 | {
|
---|
| 756 | const QwtInterval boundingInterval = align( interval, stepSize );
|
---|
| 757 |
|
---|
| 758 | ticks[QwtScaleDiv::MajorTick] =
|
---|
| 759 | buildMajorTicks( boundingInterval, stepSize );
|
---|
| 760 |
|
---|
| 761 | if ( maxMinSteps > 0 )
|
---|
| 762 | {
|
---|
| 763 | ticks[QwtScaleDiv::MinorTick] = buildMinorTicks(
|
---|
| 764 | ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize );
|
---|
| 765 | }
|
---|
| 766 |
|
---|
| 767 | for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
|
---|
| 768 | ticks[i] = strip( ticks[i], interval );
|
---|
| 769 | }
|
---|
| 770 |
|
---|
| 771 | /*!
|
---|
| 772 | \brief Calculate major ticks for an interval
|
---|
| 773 |
|
---|
| 774 | \param interval Interval
|
---|
| 775 | \param stepSize Step size
|
---|
| 776 |
|
---|
| 777 | \return Calculated ticks
|
---|
| 778 | */
|
---|
| 779 | QList<double> QwtLog10ScaleEngine::buildMajorTicks(
|
---|
| 780 | const QwtInterval &interval, double stepSize ) const
|
---|
| 781 | {
|
---|
| 782 | double width = log10( interval ).width();
|
---|
| 783 |
|
---|
| 784 | int numTicks = qRound( width / stepSize ) + 1;
|
---|
| 785 | if ( numTicks > 10000 )
|
---|
| 786 | numTicks = 10000;
|
---|
| 787 |
|
---|
| 788 | const double lxmin = ::log( interval.minValue() );
|
---|
| 789 | const double lxmax = ::log( interval.maxValue() );
|
---|
| 790 | const double lstep = ( lxmax - lxmin ) / double( numTicks - 1 );
|
---|
| 791 |
|
---|
| 792 | QList<double> ticks;
|
---|
| 793 |
|
---|
| 794 | ticks += interval.minValue();
|
---|
| 795 |
|
---|
| 796 | for ( int i = 1; i < numTicks - 1; i++ )
|
---|
| 797 | ticks += qExp( lxmin + double( i ) * lstep );
|
---|
| 798 |
|
---|
| 799 | ticks += interval.maxValue();
|
---|
| 800 |
|
---|
| 801 | return ticks;
|
---|
| 802 | }
|
---|
| 803 |
|
---|
| 804 | /*!
|
---|
| 805 | \brief Calculate minor/medium ticks for major ticks
|
---|
| 806 |
|
---|
| 807 | \param majorTicks Major ticks
|
---|
| 808 | \param maxMinSteps Maximum number of minor steps
|
---|
| 809 | \param stepSize Step size
|
---|
| 810 | */
|
---|
| 811 | QList<double> QwtLog10ScaleEngine::buildMinorTicks(
|
---|
| 812 | const QList<double> &majorTicks,
|
---|
| 813 | int maxMinSteps, double stepSize ) const
|
---|
| 814 | {
|
---|
| 815 | if ( stepSize < 1.1 ) // major step width is one decade
|
---|
| 816 | {
|
---|
| 817 | if ( maxMinSteps < 1 )
|
---|
| 818 | return QList<double>();
|
---|
| 819 |
|
---|
| 820 | int k0, kstep, kmax;
|
---|
| 821 |
|
---|
| 822 | if ( maxMinSteps >= 8 )
|
---|
| 823 | {
|
---|
| 824 | k0 = 2;
|
---|
| 825 | kmax = 9;
|
---|
| 826 | kstep = 1;
|
---|
| 827 | }
|
---|
| 828 | else if ( maxMinSteps >= 4 )
|
---|
| 829 | {
|
---|
| 830 | k0 = 2;
|
---|
| 831 | kmax = 8;
|
---|
| 832 | kstep = 2;
|
---|
| 833 | }
|
---|
| 834 | else if ( maxMinSteps >= 2 )
|
---|
| 835 | {
|
---|
| 836 | k0 = 2;
|
---|
| 837 | kmax = 5;
|
---|
| 838 | kstep = 3;
|
---|
| 839 | }
|
---|
| 840 | else
|
---|
| 841 | {
|
---|
| 842 | k0 = 5;
|
---|
| 843 | kmax = 5;
|
---|
| 844 | kstep = 1;
|
---|
| 845 | }
|
---|
| 846 |
|
---|
| 847 | QList<double> minorTicks;
|
---|
| 848 |
|
---|
| 849 | for ( int i = 0; i < majorTicks.count(); i++ )
|
---|
| 850 | {
|
---|
| 851 | const double v = majorTicks[i];
|
---|
| 852 | for ( int k = k0; k <= kmax; k += kstep )
|
---|
| 853 | minorTicks += v * double( k );
|
---|
| 854 | }
|
---|
| 855 |
|
---|
| 856 | return minorTicks;
|
---|
| 857 | }
|
---|
| 858 | else // major step > one decade
|
---|
| 859 | {
|
---|
| 860 | double minStep = divideInterval( stepSize, maxMinSteps );
|
---|
| 861 | if ( minStep == 0.0 )
|
---|
| 862 | return QList<double>();
|
---|
| 863 |
|
---|
| 864 | if ( minStep < 1.0 )
|
---|
| 865 | minStep = 1.0;
|
---|
| 866 |
|
---|
| 867 | // # subticks per interval
|
---|
| 868 | int nMin = qRound( stepSize / minStep ) - 1;
|
---|
| 869 |
|
---|
| 870 | // Do the minor steps fit into the interval?
|
---|
| 871 |
|
---|
| 872 | if ( qwtFuzzyCompare( ( nMin + 1 ) * minStep,
|
---|
| 873 | qAbs( stepSize ), stepSize ) > 0 )
|
---|
| 874 | {
|
---|
| 875 | nMin = 0;
|
---|
| 876 | }
|
---|
| 877 |
|
---|
| 878 | if ( nMin < 1 )
|
---|
| 879 | return QList<double>(); // no subticks
|
---|
| 880 |
|
---|
| 881 | // substep factor = 10^substeps
|
---|
| 882 | const qreal minFactor = qMax( qPow( 10.0, minStep ), qreal( 10.0 ) );
|
---|
| 883 |
|
---|
| 884 | QList<double> minorTicks;
|
---|
| 885 | for ( int i = 0; i < majorTicks.count(); i++ )
|
---|
| 886 | {
|
---|
| 887 | double val = majorTicks[i];
|
---|
| 888 | for ( int k = 0; k < nMin; k++ )
|
---|
| 889 | {
|
---|
| 890 | val *= minFactor;
|
---|
| 891 | minorTicks += val;
|
---|
| 892 | }
|
---|
| 893 | }
|
---|
| 894 | return minorTicks;
|
---|
| 895 | }
|
---|
| 896 | }
|
---|
| 897 |
|
---|
| 898 | /*!
|
---|
| 899 | \brief Align an interval to a step size
|
---|
| 900 |
|
---|
| 901 | The limits of an interval are aligned that both are integer
|
---|
| 902 | multiples of the step size.
|
---|
| 903 |
|
---|
| 904 | \param interval Interval
|
---|
| 905 | \param stepSize Step size
|
---|
| 906 |
|
---|
| 907 | \return Aligned interval
|
---|
| 908 | */
|
---|
| 909 | QwtInterval QwtLog10ScaleEngine::align(
|
---|
| 910 | const QwtInterval &interval, double stepSize ) const
|
---|
| 911 | {
|
---|
| 912 | const QwtInterval intv = log10( interval );
|
---|
| 913 |
|
---|
| 914 | double x1 = QwtScaleArithmetic::floorEps( intv.minValue(), stepSize );
|
---|
| 915 | if ( qwtFuzzyCompare( interval.minValue(), x1, stepSize ) == 0 )
|
---|
| 916 | x1 = interval.minValue();
|
---|
| 917 |
|
---|
| 918 | double x2 = QwtScaleArithmetic::ceilEps( intv.maxValue(), stepSize );
|
---|
| 919 | if ( qwtFuzzyCompare( interval.maxValue(), x2, stepSize ) == 0 )
|
---|
| 920 | x2 = interval.maxValue();
|
---|
| 921 |
|
---|
| 922 | return pow10( QwtInterval( x1, x2 ) );
|
---|
| 923 | }
|
---|
| 924 |
|
---|
| 925 | /*!
|
---|
| 926 | Return the interval [log10(interval.minValue(), log10(interval.maxValue]
|
---|
| 927 | */
|
---|
| 928 |
|
---|
| 929 | QwtInterval QwtLog10ScaleEngine::log10( const QwtInterval &interval ) const
|
---|
| 930 | {
|
---|
| 931 | return QwtInterval( ::log10( interval.minValue() ),
|
---|
| 932 | ::log10( interval.maxValue() ) );
|
---|
| 933 | }
|
---|
| 934 |
|
---|
| 935 | /*!
|
---|
| 936 | Return the interval [pow10(interval.minValue(), pow10(interval.maxValue]
|
---|
| 937 | */
|
---|
| 938 | QwtInterval QwtLog10ScaleEngine::pow10( const QwtInterval &interval ) const
|
---|
| 939 | {
|
---|
| 940 | return QwtInterval( qPow( 10.0, interval.minValue() ),
|
---|
| 941 | qPow( 10.0, interval.maxValue() ) );
|
---|
| 942 | }
|
---|