qwt_scale_engine.cpp 22.2 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
5
 *
pixhawk's avatar
pixhawk committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include "qwt_math.h"
#include "qwt_scale_map.h"
#include "qwt_scale_engine.h"

static const double _eps = 1.0e-6;

/*!
  \brief Compare 2 values, relative to an interval

  Values are "equal", when :
  \f$\cdot value2 - value1 <= abs(intervalSize * 10e^{-6})\f$

  \param value1 First value to compare
  \param value2 Second value to compare
  \param intervalSize interval size

  \return 0: if equal, -1: if value2 > value1, 1: if value1 > value2
*/
28 29
int QwtScaleArithmetic::compareEps(double value1, double value2,
                                   double intervalSize)
pixhawk's avatar
pixhawk committed
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
{
    const double eps = qwtAbs(_eps * intervalSize);

    if ( value2 - value1 > eps )
        return -1;

    if ( value1 - value2 > eps )
        return 1;

    return 0;
}

/*!
  Ceil a value, relative to an interval

  \param value Value to ceil
  \param intervalSize Interval size
47

pixhawk's avatar
pixhawk committed
48 49
  \sa floorEps
*/
50 51
double QwtScaleArithmetic::ceilEps(double value,
                                   double intervalSize)
pixhawk's avatar
pixhawk committed
52 53 54 55 56 57 58 59 60 61 62 63
{
    const double eps = _eps * intervalSize;

    value = (value - eps) / intervalSize;
    return ceil(value) * intervalSize;
}

/*!
  Floor a value, relative to an interval

  \param value Value to floor
  \param intervalSize Interval size
64

pixhawk's avatar
pixhawk committed
65 66
  \sa floorEps
*/
67
double QwtScaleArithmetic::floorEps(double value, double intervalSize)
pixhawk's avatar
pixhawk committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
{
    const double eps = _eps * intervalSize;

    value = (value + eps) / intervalSize;
    return floor(value) * intervalSize;
}

/*
  \brief Divide an interval into steps

  \f$stepSize = (intervalSize - intervalSize * 10e^{-6}) / numSteps\f$

  \param intervalSize Interval size
  \param numSteps Number of steps
  \return Step size
*/
84
double QwtScaleArithmetic::divideEps(double intervalSize, double numSteps)
pixhawk's avatar
pixhawk committed
85 86 87 88 89
{
    if ( numSteps == 0.0 || intervalSize == 0.0 )
        return 0.0;

    return (intervalSize - (_eps * intervalSize)) / numSteps;
90
}
pixhawk's avatar
pixhawk committed
91 92 93 94

/*!
  Find the smallest value out of {1,2,5}*10^n with an integer number n
  which is greater than or equal to x
95

pixhawk's avatar
pixhawk committed
96 97
  \param x Input value
*/
98
double QwtScaleArithmetic::ceil125(double x)
pixhawk's avatar
pixhawk committed
99
{
100
    if (x == 0.0)
pixhawk's avatar
pixhawk committed
101 102 103 104 105
        return 0.0;

    const double sign = (x > 0) ? 1.0 : -1.0;
    const double lx = log10(fabs(x));
    const double p10 = floor(lx);
106

pixhawk's avatar
pixhawk committed
107 108
    double fr = pow(10.0, lx - p10);
    if (fr <=1.0)
109
        fr = 1.0;
pixhawk's avatar
pixhawk committed
110
    else if (fr <= 2.0)
111 112 113
        fr = 2.0;
    else if (fr <= 5.0)
        fr = 5.0;
pixhawk's avatar
pixhawk committed
114
    else
115
        fr = 10.0;
pixhawk's avatar
pixhawk committed
116 117 118 119 120 121 122 123 124 125

    return sign * fr * pow(10.0, p10);
}

/*!
  \brief Find the largest value out of {1,2,5}*10^n with an integer number n
  which is smaller than or equal to x

  \param x Input value
*/
126
double QwtScaleArithmetic::floor125(double x)
pixhawk's avatar
pixhawk committed
127 128 129 130 131 132 133 134 135 136
{
    if (x == 0.0)
        return 0.0;

    double sign = (x > 0) ? 1.0 : -1.0;
    const double lx = log10(fabs(x));
    const double p10 = floor(lx);

    double fr = pow(10.0, lx - p10);
    if (fr >= 10.0)
137
        fr = 10.0;
pixhawk's avatar
pixhawk committed
138
    else if (fr >= 5.0)
139
        fr = 5.0;
pixhawk's avatar
pixhawk committed
140
    else if (fr >= 2.0)
141
        fr = 2.0;
pixhawk's avatar
pixhawk committed
142
    else
143
        fr = 1.0;
pixhawk's avatar
pixhawk committed
144 145 146 147 148 149 150 151 152 153 154

    return sign * fr * pow(10.0, p10);
}

class QwtScaleEngine::PrivateData
{
public:
    PrivateData():
        attributes(QwtScaleEngine::NoAttribute),
        loMargin(0.0),
        hiMargin(0.0),
155
        referenceValue(0.0) {
pixhawk's avatar
pixhawk committed
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
    }

    int attributes;       // scale attributes

    double loMargin;      // margins
    double hiMargin;

    double referenceValue; // reference value

};

//! Ctor
QwtScaleEngine::QwtScaleEngine()
{
    d_data = new PrivateData;
}


//! Dtor
QwtScaleEngine::~QwtScaleEngine ()
{
    delete d_data;
}

/*!
    \return the margin at the lower end of the scale
    The default margin is 0.

    \sa QwtScaleEngine::setMargins()
*/
186 187 188
double QwtScaleEngine::loMargin() const
{
    return d_data->loMargin;
pixhawk's avatar
pixhawk committed
189 190 191 192 193 194 195 196
}

/*!
    \return the margin at the upper end of the scale
    The default margin is 0.

    \sa QwtScaleEngine::setMargins()
*/
197 198 199
double QwtScaleEngine::hiMargin() const
{
    return d_data->hiMargin;
pixhawk's avatar
pixhawk committed
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
}

/*!
  \brief Specify margins at the scale's endpoints
  \param mlo minimum distance between the scale's lower boundary and the
             smallest enclosed value
  \param mhi minimum distance between the scale's upper boundary and the
             greatest enclosed value

  Margins can be used to leave a minimum amount of space between
  the enclosed intervals and the boundaries of the scale.

  \warning
  \li QwtLog10ScaleEngine measures the margins in decades.

  \sa QwtScaleEngine::hiMargin, QwtScaleEngine::loMargin
*/

void QwtScaleEngine::setMargins(double mlo, double mhi)
{
    d_data->loMargin = qwtMax(mlo,0.0);
    d_data->hiMargin = qwtMax(mhi,0.0);
}

/*!
  Calculate a step size for an interval size

  \param intervalSize Interval size
  \param numSteps Number of steps
229

pixhawk's avatar
pixhawk committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
  \return Step size
*/
double QwtScaleEngine::divideInterval(
    double intervalSize, int numSteps) const
{
    if ( numSteps <= 0 )
        return 0.0;

    double v = QwtScaleArithmetic::divideEps(intervalSize, numSteps);
    return QwtScaleArithmetic::ceil125(v);
}

/*!
  Check if an interval "contains" a value

  \param interval Interval
  \param value Value

  \sa QwtScaleArithmetic::compareEps
*/
bool QwtScaleEngine::contains(
    const QwtDoubleInterval &interval, double value) const
{
    if (!interval.isValid() )
        return false;
255 256 257

    if ( QwtScaleArithmetic::compareEps(value,
                                        interval.minValue(), interval.width()) < 0 ) {
pixhawk's avatar
pixhawk committed
258 259 260
        return false;
    }

261 262
    if ( QwtScaleArithmetic::compareEps(value,
                                        interval.maxValue(), interval.width()) > 0 ) {
pixhawk's avatar
pixhawk committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276
        return false;
    }

    return true;
}

/*!
  Remove ticks from a list, that are not inside an interval

  \param ticks Tick list
  \param interval Interval

  \return Stripped tick list
*/
277 278
QwtValueList QwtScaleEngine::strip(
    const QwtValueList& ticks,
pixhawk's avatar
pixhawk committed
279 280 281 282 283 284
    const QwtDoubleInterval &interval) const
{
    if ( !interval.isValid() || ticks.count() == 0 )
        return QwtValueList();

    if ( contains(interval, ticks.first())
285
            && contains(interval, ticks.last()) ) {
pixhawk's avatar
pixhawk committed
286 287 288 289
        return ticks;
    }

    QwtValueList strippedTicks;
290
    for ( int i = 0; i < (int)ticks.count(); i++ ) {
pixhawk's avatar
pixhawk committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
        if ( contains(interval, ticks[i]) )
            strippedTicks += ticks[i];
    }
    return strippedTicks;
}

/*!
  \brief Build an interval for a value

  In case of v == 0.0 the interval is [-0.5, 0.5],
  otherwide it is [0.5 * v, 1.5 * v]
*/

QwtDoubleInterval QwtScaleEngine::buildInterval(double v) const
{
    const double delta = (v == 0.0) ? 0.5 : qwtAbs(0.5 * v);
    return QwtDoubleInterval(v - delta, v + delta);
}

/*!
  Change a scale attribute

  \param attribute Attribute to change
  \param on On/Off

  The behaviour of the scale engine can be changed
  with the following attributes:
  <dl>
  <dt>QwtScaleEngine::IncludeReference
  <dd>Build a scale which includes the reference value.
  <dt>QwtScaleEngine::Symmetric
  <dd>Build a scale which is symmetric to the reference value.
  <dt>QwtScaleEngine::Floating
  <dd>The endpoints of the scale are supposed to be equal the outmost included
  values plus the specified margins (see setMargins()). If this attribute is
  *not* set, the endpoints of the scale will be integer multiples of the step
  size.
  <dt>QwtScaleEngine::Inverted
  <dd>Turn the scale upside down.
  </dl>

  \sa QwtScaleEngine::testAttribute()
*/
void QwtScaleEngine::setAttribute(Attribute attribute, bool on)
{
    if (on)
337
        d_data->attributes |= attribute;
pixhawk's avatar
pixhawk committed
338
    else
339
        d_data->attributes &= (~attribute);
pixhawk's avatar
pixhawk committed
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
}

/*!
  Check if a attribute is set.

  \param attribute Attribute to be tested
  \sa QwtScaleEngine::setAttribute() for a description of the possible options.
*/
bool QwtScaleEngine::testAttribute(Attribute attribute) const
{
    return bool(d_data->attributes & attribute);
}

/*!
  Change the scale attribute

  \param attributes Set scale attributes
  \sa QwtScaleEngine::attributes()
*/
void QwtScaleEngine::setAttributes(int attributes)
{
    d_data->attributes = attributes;
}

/*!
  Return the scale attributes
*/
int QwtScaleEngine::attributes() const
{
    return d_data->attributes;
}

/*!
  \brief Specify a reference point
  \param r new reference value

  The reference point is needed if options IncludeRef or
  Symmetric are active. Its default value is 0.0.
*/
void QwtScaleEngine::setReference(double r)
{
    d_data->referenceValue = r;
}

/*!
 \return the reference value
 \sa QwtScaleEngine::setReference(), QwtScaleEngine::setAttribute()
*/
388 389 390
double QwtScaleEngine::reference() const
{
    return d_data->referenceValue;
pixhawk's avatar
pixhawk committed
391 392 393 394 395 396 397 398 399 400 401
}

/*!
  Return a transformation, for linear scales
*/
QwtScaleTransformation *QwtLinearScaleEngine::transformation() const
{
    return new QwtScaleTransformation(QwtScaleTransformation::Linear);
}

/*!
402
    Align and divide an interval
pixhawk's avatar
pixhawk committed
403 404 405 406 407 408 409 410

   \param maxNumSteps Max. number of steps
   \param x1 First limit of the interval (In/Out)
   \param x2 Second limit of the interval (In/Out)
   \param stepSize Step size (Out)

   \sa QwtLinearScaleEngine::setAttribute
*/
411 412
void QwtLinearScaleEngine::autoScale(int maxNumSteps,
                                     double &x1, double &x2, double &stepSize) const
pixhawk's avatar
pixhawk committed
413 414 415 416 417 418 419 420 421
{
    QwtDoubleInterval interval(x1, x2);
    interval = interval.normalized();

    interval.setMinValue(interval.minValue() - loMargin());
    interval.setMaxValue(interval.maxValue() + hiMargin());

    if (testAttribute(QwtScaleEngine::Symmetric))
        interval = interval.symmetrize(reference());
422

pixhawk's avatar
pixhawk committed
423 424 425 426 427 428 429 430 431 432 433 434 435 436
    if (testAttribute(QwtScaleEngine::IncludeReference))
        interval = interval.extend(reference());

    if (interval.width() == 0.0)
        interval = buildInterval(interval.minValue());

    stepSize = divideInterval(interval.width(), qwtMax(maxNumSteps, 1));

    if ( !testAttribute(QwtScaleEngine::Floating) )
        interval = align(interval, stepSize);

    x1 = interval.minValue();
    x2 = interval.maxValue();

437
    if (testAttribute(QwtScaleEngine::Inverted)) {
pixhawk's avatar
pixhawk committed
438 439 440 441 442 443 444 445
        qSwap(x1, x2);
        stepSize = -stepSize;
    }
}

/*!
   \brief Calculate a scale division

446 447
   \param x1 First interval limit
   \param x2 Second interval limit
pixhawk's avatar
pixhawk committed
448 449 450 451 452 453 454 455
   \param maxMajSteps Maximum for the number of major steps
   \param maxMinSteps Maximum number of minor steps
   \param stepSize Step size. If stepSize == 0, the scaleEngine
                   calculates one.

   \sa QwtScaleEngine::stepSize, QwtScaleEngine::subDivide
*/
QwtScaleDiv QwtLinearScaleEngine::divideScale(double x1, double x2,
456
        int maxMajSteps, int maxMinSteps, double stepSize) const
pixhawk's avatar
pixhawk committed
457 458 459 460 461 462
{
    QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
    if (interval.width() <= 0 )
        return QwtScaleDiv();

    stepSize = qwtAbs(stepSize);
463
    if ( stepSize == 0.0 ) {
pixhawk's avatar
pixhawk committed
464 465 466 467 468 469 470 471
        if ( maxMajSteps < 1 )
            maxMajSteps = 1;

        stepSize = divideInterval(interval.width(), maxMajSteps);
    }

    QwtScaleDiv scaleDiv;

472
    if ( stepSize != 0.0 ) {
pixhawk's avatar
pixhawk committed
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
        QwtValueList ticks[QwtScaleDiv::NTickTypes];
        buildTicks(interval, stepSize, maxMinSteps, ticks);

        scaleDiv = QwtScaleDiv(interval, ticks);
    }

    if ( x1 > x2 )
        scaleDiv.invert();

    return scaleDiv;
}

void QwtLinearScaleEngine::buildTicks(
    const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
    QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
{
    const QwtDoubleInterval boundingInterval =
        align(interval, stepSize);
491 492

    ticks[QwtScaleDiv::MajorTick] =
pixhawk's avatar
pixhawk committed
493 494
        buildMajorTicks(boundingInterval, stepSize);

495
    if ( maxMinSteps > 0 ) {
pixhawk's avatar
pixhawk committed
496
        buildMinorTicks(ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize,
497
                        ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick]);
pixhawk's avatar
pixhawk committed
498
    }
499 500

    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) {
pixhawk's avatar
pixhawk committed
501 502
        ticks[i] = strip(ticks[i], interval);

503
        // ticks very close to 0.0 are
pixhawk's avatar
pixhawk committed
504 505
        // explicitely set to 0.0

506
        for ( int j = 0; j < (int)ticks[i].count(); j++ ) {
pixhawk's avatar
pixhawk committed
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
            if ( QwtScaleArithmetic::compareEps(ticks[i][j], 0.0, stepSize) == 0 )
                ticks[i][j] = 0.0;
        }
    }
}

QwtValueList QwtLinearScaleEngine::buildMajorTicks(
    const QwtDoubleInterval &interval, double stepSize) const
{
    int numTicks = qRound(interval.width() / stepSize) + 1;
#if 1
    if ( numTicks > 10000 )
        numTicks = 10000;
#endif

    QwtValueList ticks;

    ticks += interval.minValue();
    for (int i = 1; i < numTicks - 1; i++)
        ticks += interval.minValue() + i * stepSize;
    ticks += interval.maxValue();

    return ticks;
}

void QwtLinearScaleEngine::buildMinorTicks(
    const QwtValueList& majorTicks,
    int maxMinSteps, double stepSize,
535
    QwtValueList &minorTicks,
pixhawk's avatar
pixhawk committed
536
    QwtValueList &mediumTicks) const
537
{
pixhawk's avatar
pixhawk committed
538
    double minStep = divideInterval(stepSize, maxMinSteps);
539 540 541
    if (minStep == 0.0)
        return;

pixhawk's avatar
pixhawk committed
542 543
    // # ticks per interval
    int numTicks = (int)::ceil(qwtAbs(stepSize / minStep)) - 1;
544

pixhawk's avatar
pixhawk committed
545
    // Do the minor steps fit into the interval?
546 547
    if ( QwtScaleArithmetic::compareEps((numTicks +  1) * qwtAbs(minStep),
                                        qwtAbs(stepSize), stepSize) > 0) {
pixhawk's avatar
pixhawk committed
548 549 550 551 552 553 554 555 556 557
        numTicks = 1;
        minStep = stepSize * 0.5;
    }

    int medIndex = -1;
    if ( numTicks % 2 )
        medIndex = numTicks / 2;

    // calculate minor ticks

558
    for (int i = 0; i < (int)majorTicks.count(); i++) {
pixhawk's avatar
pixhawk committed
559
        double val = majorTicks[i];
560
        for (int k = 0; k < numTicks; k++) {
pixhawk's avatar
pixhawk committed
561 562 563
            val += minStep;

            double alignedValue = val;
564
            if (QwtScaleArithmetic::compareEps(val, 0.0, stepSize) == 0)
pixhawk's avatar
pixhawk committed
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
                alignedValue = 0.0;

            if ( k == medIndex )
                mediumTicks += alignedValue;
            else
                minorTicks += alignedValue;
        }
    }
}

/*!
  \brief Align an interval to a step size

  The limits of an interval are aligned that both are integer
  multiples of the step size.

  \param interval Interval
  \param stepSize Step size

  \return Aligned interval
*/
QwtDoubleInterval QwtLinearScaleEngine::align(
    const QwtDoubleInterval &interval, double stepSize) const
{
589
    const double x1 =
pixhawk's avatar
pixhawk committed
590
        QwtScaleArithmetic::floorEps(interval.minValue(), stepSize);
591
    const double x2 =
pixhawk's avatar
pixhawk committed
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
        QwtScaleArithmetic::ceilEps(interval.maxValue(), stepSize);

    return QwtDoubleInterval(x1, x2);
}

/*!
  Return a transformation, for logarithmic (base 10) scales
*/
QwtScaleTransformation *QwtLog10ScaleEngine::transformation() const
{
    return new QwtScaleTransformation(QwtScaleTransformation::Log10);
}

/*!
    Align and divide an interval

   \param maxNumSteps Max. number of steps
   \param x1 First limit of the interval (In/Out)
   \param x2 Second limit of the interval (In/Out)
   \param stepSize Step size (Out)

   \sa QwtScaleEngine::setAttribute
*/
615 616
void QwtLog10ScaleEngine::autoScale(int maxNumSteps,
                                    double &x1, double &x2, double &stepSize) const
pixhawk's avatar
pixhawk committed
617 618 619 620
{
    if ( x1 > x2 )
        qSwap(x1, x2);

621 622
    QwtDoubleInterval interval(x1 / pow(10.0, loMargin()),
                               x2 * pow(10.0, hiMargin()) );
pixhawk's avatar
pixhawk committed
623 624 625 626 627

    double logRef = 1.0;
    if (reference() > LOG_MIN / 2)
        logRef = qwtMin(reference(), LOG_MAX / 2);

628 629 630
    if (testAttribute(QwtScaleEngine::Symmetric)) {
        const double delta = qwtMax(interval.maxValue() / logRef,
                                    logRef / interval.minValue());
pixhawk's avatar
pixhawk committed
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
        interval.setInterval(logRef / delta, logRef * delta);
    }

    if (testAttribute(QwtScaleEngine::IncludeReference))
        interval = interval.extend(logRef);

    interval = interval.limited(LOG_MIN, LOG_MAX);

    if (interval.width() == 0.0)
        interval = buildInterval(interval.minValue());

    stepSize = divideInterval(log10(interval).width(), qwtMax(maxNumSteps, 1));
    if ( stepSize < 1.0 )
        stepSize = 1.0;

    if (!testAttribute(QwtScaleEngine::Floating))
        interval = align(interval, stepSize);

    x1 = interval.minValue();
    x2 = interval.maxValue();

652
    if (testAttribute(QwtScaleEngine::Inverted)) {
pixhawk's avatar
pixhawk committed
653 654 655 656 657 658 659 660
        qSwap(x1, x2);
        stepSize = -stepSize;
    }
}

/*!
   \brief Calculate a scale division

661 662
   \param x1 First interval limit
   \param x2 Second interval limit
pixhawk's avatar
pixhawk committed
663 664 665 666 667 668 669 670
   \param maxMajSteps Maximum for the number of major steps
   \param maxMinSteps Maximum number of minor steps
   \param stepSize Step size. If stepSize == 0, the scaleEngine
                   calculates one.

   \sa QwtScaleEngine::stepSize, QwtLog10ScaleEngine::subDivide
*/
QwtScaleDiv QwtLog10ScaleEngine::divideScale(double x1, double x2,
671
        int maxMajSteps, int maxMinSteps, double stepSize) const
pixhawk's avatar
pixhawk committed
672 673 674 675 676 677 678
{
    QwtDoubleInterval interval = QwtDoubleInterval(x1, x2).normalized();
    interval = interval.limited(LOG_MIN, LOG_MAX);

    if (interval.width() <= 0 )
        return QwtScaleDiv();

679
    if (interval.maxValue() / interval.minValue() < 10.0) {
pixhawk's avatar
pixhawk committed
680
        // scale width is less than one decade -> build linear scale
681

pixhawk's avatar
pixhawk committed
682 683 684 685 686
        QwtLinearScaleEngine linearScaler;
        linearScaler.setAttributes(attributes());
        linearScaler.setReference(reference());
        linearScaler.setMargins(loMargin(), hiMargin());

687 688
        return linearScaler.divideScale(x1, x2,
                                        maxMajSteps, maxMinSteps, stepSize);
pixhawk's avatar
pixhawk committed
689 690 691
    }

    stepSize = qwtAbs(stepSize);
692
    if ( stepSize == 0.0 ) {
pixhawk's avatar
pixhawk committed
693 694 695 696 697 698 699 700 701
        if ( maxMajSteps < 1 )
            maxMajSteps = 1;

        stepSize = divideInterval(log10(interval).width(), maxMajSteps);
        if ( stepSize < 1.0 )
            stepSize = 1.0; // major step must be >= 1 decade
    }

    QwtScaleDiv scaleDiv;
702
    if ( stepSize != 0.0 ) {
pixhawk's avatar
pixhawk committed
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
        QwtValueList ticks[QwtScaleDiv::NTickTypes];
        buildTicks(interval, stepSize, maxMinSteps, ticks);

        scaleDiv = QwtScaleDiv(interval, ticks);
    }

    if ( x1 > x2 )
        scaleDiv.invert();

    return scaleDiv;
}

void QwtLog10ScaleEngine::buildTicks(
    const QwtDoubleInterval& interval, double stepSize, int maxMinSteps,
    QwtValueList ticks[QwtScaleDiv::NTickTypes]) const
{
    const QwtDoubleInterval boundingInterval =
        align(interval, stepSize);
721 722

    ticks[QwtScaleDiv::MajorTick] =
pixhawk's avatar
pixhawk committed
723 724
        buildMajorTicks(boundingInterval, stepSize);

725
    if ( maxMinSteps > 0 ) {
pixhawk's avatar
pixhawk committed
726
        ticks[QwtScaleDiv::MinorTick] = buildMinorTicks(
727
                                            ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize);
pixhawk's avatar
pixhawk committed
728
    }
729

pixhawk's avatar
pixhawk committed
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
        ticks[i] = strip(ticks[i], interval);
}

QwtValueList QwtLog10ScaleEngine::buildMajorTicks(
    const QwtDoubleInterval &interval, double stepSize) const
{
    double width = log10(interval).width();

    int numTicks = qRound(width / stepSize) + 1;
    if ( numTicks > 10000 )
        numTicks = 10000;

    const double lxmin = log(interval.minValue());
    const double lxmax = log(interval.maxValue());
    const double lstep = (lxmax - lxmin) / double(numTicks - 1);

    QwtValueList ticks;

    ticks += interval.minValue();

    for (int i = 1; i < numTicks; i++)
752
        ticks += exp(lxmin + double(i) * lstep);
pixhawk's avatar
pixhawk committed
753 754 755 756 757 758 759

    ticks += interval.maxValue();

    return ticks;
}

QwtValueList QwtLog10ScaleEngine::buildMinorTicks(
760
    const QwtValueList &majorTicks,
pixhawk's avatar
pixhawk committed
761
    int maxMinSteps, double stepSize) const
762 763
{
    if (stepSize < 1.1) {          // major step width is one decade
pixhawk's avatar
pixhawk committed
764 765
        if ( maxMinSteps < 1 )
            return QwtValueList();
766

pixhawk's avatar
pixhawk committed
767
        int k0, kstep, kmax;
768 769

        if (maxMinSteps >= 8) {
pixhawk's avatar
pixhawk committed
770 771 772
            k0 = 2;
            kmax = 9;
            kstep = 1;
773
        } else if (maxMinSteps >= 4) {
pixhawk's avatar
pixhawk committed
774 775 776
            k0 = 2;
            kmax = 8;
            kstep = 2;
777
        } else if (maxMinSteps >= 2) {
pixhawk's avatar
pixhawk committed
778 779 780
            k0 = 2;
            kmax = 5;
            kstep = 3;
781
        } else {
pixhawk's avatar
pixhawk committed
782 783 784 785 786 787 788
            k0 = 5;
            kmax = 5;
            kstep = 1;
        }

        QwtValueList minorTicks;

789
        for (int i = 0; i < (int)majorTicks.count(); i++) {
pixhawk's avatar
pixhawk committed
790 791 792 793 794 795
            const double v = majorTicks[i];
            for (int k = k0; k<= kmax; k+=kstep)
                minorTicks += v * double(k);
        }

        return minorTicks;
796
    } else { // major step > one decade
pixhawk's avatar
pixhawk committed
797 798 799 800 801 802 803 804 805 806 807 808
        double minStep = divideInterval(stepSize, maxMinSteps);
        if ( minStep == 0.0 )
            return QwtValueList();

        if ( minStep < 1.0 )
            minStep = 1.0;

        // # subticks per interval
        int nMin = qRound(stepSize / minStep) - 1;

        // Do the minor steps fit into the interval?

809 810
        if ( QwtScaleArithmetic::compareEps((nMin +  1) * minStep,
                                            qwtAbs(stepSize), stepSize) > 0) {
pixhawk's avatar
pixhawk committed
811 812 813 814 815 816 817 818 819 820
            nMin = 0;
        }

        if (nMin < 1)
            return QwtValueList();      // no subticks

        // substep factor = 10^substeps
        const double minFactor = qwtMax(pow(10.0, minStep), 10.0);

        QwtValueList minorTicks;
821
        for (int i = 0; i < (int)majorTicks.count(); i++) {
pixhawk's avatar
pixhawk committed
822
            double val = majorTicks[i];
823
            for (int k=0; k< nMin; k++) {
pixhawk's avatar
pixhawk committed
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
                val *= minFactor;
                minorTicks += val;
            }
        }
        return minorTicks;
    }
}

/*!
  \brief Align an interval to a step size

  The limits of an interval are aligned that both are integer
  multiples of the step size.

  \param interval Interval
  \param stepSize Step size

  \return Aligned interval
*/
QwtDoubleInterval QwtLog10ScaleEngine::align(
    const QwtDoubleInterval &interval, double stepSize) const
{
    const QwtDoubleInterval intv = log10(interval);

    const double x1 = QwtScaleArithmetic::floorEps(intv.minValue(), stepSize);
    const double x2 = QwtScaleArithmetic::ceilEps(intv.maxValue(), stepSize);

    return pow10(QwtDoubleInterval(x1, x2));
}

/*!
  Return the interval [log10(interval.minValue(), log10(interval.maxValue]
*/

QwtDoubleInterval QwtLog10ScaleEngine::log10(
    const QwtDoubleInterval &interval) const
{
    return QwtDoubleInterval(::log10(interval.minValue()),
862
                             ::log10(interval.maxValue()));
pixhawk's avatar
pixhawk committed
863 864 865 866 867 868 869 870 871
}

/*!
  Return the interval [pow10(interval.minValue(), pow10(interval.maxValue]
*/
QwtDoubleInterval QwtLog10ScaleEngine::pow10(
    const QwtDoubleInterval &interval) const
{
    return QwtDoubleInterval(pow(10.0, interval.minValue()),
872
                             pow(10.0, interval.maxValue()));
pixhawk's avatar
pixhawk committed
873
}