Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

# qwt_scldiv.cpp

    00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
    00002  * Qwt Widget Library
    00003  * Copyright (C) 1997   Josef Wilgen
    00004  * Copyright (C) 2002   Uwe Rathmann
    00005  *
    00006  * This library is free software; you can redistribute it and/or
    00007  * modify it under the terms of the Qwt License, Version 1.0
    00008  *****************************************************************************/
    00009
    00010 #include "qwt_scldiv.h"
    00011 #include "qwt_math.h"
    00012
    00013 static const double step_eps = 1.0e-3;
    00014 static const double border_eps = 1.0e-10;
    00015
    00016 static bool qwtLimRange(double &val, double v1, double v2,
    00017     double eps_rel = 0.0, double eps_abs = 0.0)
    00018 {
    00019     bool rv = TRUE;
    00020     double vmin = qwtMin(v1, v2);
    00021     double vmax = qwtMax(v1, v2);
    00022     double delta_min = qwtMax(qwtAbs(eps_rel * vmin), qwtAbs(eps_abs));
    00023     double delta_max = qwtMax(qwtAbs(eps_rel * vmax), qwtAbs(eps_abs));
    00024
    00025     if (val < vmin)
    00026     {
    00027         if (val < vmin - delta_min)
    00028             rv = FALSE;
    00029         val = vmin;
    00030     }
    00031     else if (val > vmax)
    00032     {
    00033         if (val > vmax + delta_max)
    00034             rv = FALSE;
    00035         val = vmax;
    00036     }
    00037     return rv;
    00038 }
    00039
    00040
    00042 QwtScaleDiv::QwtScaleDiv()
    00043 {
    00044     d_lBound = 0.0;
    00045     d_hBound = 0.0;
    00046     d_majStep = 0.0;
    00047     d_log = FALSE;
    00048 }
    00049
    00086 bool QwtScaleDiv::rebuild(double x1, double x2,
    00087     int maxMajSteps, int maxMinSteps, bool log, double step, bool ascend)
    00088 {
    00089   int rv;
    00090
    00091   d_lBound = qwtMin(x1, x2);
    00092   d_hBound = qwtMax(x1, x2);
    00093   d_log = log;
    00094
    00095   if (d_log)
    00096       rv = buildLogDiv(maxMajSteps,maxMinSteps,step);
    00097   else
    00098       rv = buildLinDiv(maxMajSteps, maxMinSteps, step);
    00099
    00100   if ((!ascend) && (x2 < x1))
    00101   {
    00102       d_lBound = x1;
    00103       d_hBound = x2;
    00104       qwtTwistArray(d_majMarks.data(), d_majMarks.size());
    00105       qwtTwistArray(d_minMarks.data(), d_minMarks.size());
    00106   }
    00107
    00108   return rv;
    00109 }
    00110
    00129 bool QwtScaleDiv::buildLinDiv(int maxMajSteps, int maxMinSteps, double step)
    00130 {
    00131     int nMaj, nMin, minSize, i0,i,k;
    00132     double val, mval;
    00133     double firstTick, lastTick;
    00134     double minStep;
    00135     QwtArray<double> buffer;
    00136     bool rv = TRUE;
    00137
    00138     // parameter range check
    00139     maxMajSteps = qwtMax(1, maxMajSteps);
    00140     maxMinSteps = qwtMax(0, maxMinSteps);
    00141     step = qwtAbs(step);
    00142
    00143     // detach arrays
    00144     d_majMarks.duplicate(0,0);
    00145     d_minMarks.duplicate(0,0);
    00146
    00147     if (d_lBound == d_hBound) return TRUE;
    00148
    00149     //
    00150     // Set up major divisions
    00151     //
    00152     if (step == 0.0)
    00153        d_majStep = qwtCeil125(qwtAbs(d_hBound - d_lBound) * 0.999999
    00154                   / double(maxMajSteps));
    00155     else
    00156        d_majStep = step;
    00157
    00158     if (d_majStep == 0.0) return TRUE;
    00159
    00160     firstTick = ceil( (d_lBound - step_eps * d_majStep) / d_majStep) * d_majStep;
    00161     lastTick = floor( (d_hBound + step_eps * d_majStep) / d_majStep) * d_majStep;
    00162
    00163     nMaj = qwtMin(10000, int(floor ((lastTick - firstTick) / d_majStep + 0.5)) + 1);
    00164
    00165     if ((rv = d_majMarks.resize(nMaj)))
    00166        qwtLinSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick);
    00167     else
    00168        return FALSE;
    00169
    00170     //
    00171     // Set up minor divisions
    00172     //
    00173     if (maxMinSteps < 1) // no minor divs
    00174        return TRUE;
    00175
    00176     minStep = qwtCeil125( d_majStep  /  double(maxMinSteps) );
    00177
    00178     if (minStep == 0.0) return TRUE;
    00179
    00180     nMin = qwtAbs(int(floor(d_majStep / minStep + 0.5))) - 1; // # minor steps per interval
    00181
    00182     // Do the minor steps fit into the interval?
    00183     if ( qwtAbs(double(nMin +  1) * minStep - d_majStep)
    00184         >  step_eps * d_majStep)
    00185     {
    00186         nMin = 1;
    00187         minStep = d_majStep * 0.5;
    00188     }
    00189
    00190     // Are there minor ticks below the first major tick?
    00191     if (d_majMarks[0] > d_lBound )
    00192        i0 = -1;
    00193     else
    00194        i0 = 0;
    00195
    00196     // resize buffer to the maximum possible number of minor ticks
    00197     rv = buffer.resize(nMin * (nMaj + 1));
    00198
    00199     // calculate minor ticks
    00200     if (rv)
    00201     {
    00202         minSize = 0;
    00203         for (i = i0; i < (int)d_majMarks.size(); i++)
    00204         {
    00205             if (i >= 0)
    00206                val = d_majMarks[i];
    00207             else
    00208                val = d_majMarks[0] - d_majStep;
    00209
    00210             for (k=0; k< nMin; k++)
    00211             {
    00212                 mval = (val += minStep);
    00213                 if (qwtLimRange(mval, d_lBound, d_hBound, border_eps))
    00214                 {
    00215                     buffer[minSize] = mval;
    00216                     minSize++;
    00217                 }
    00218             }
    00219         }
    00220         d_minMarks.duplicate(buffer.data(), minSize);
    00221     }
    00222
    00223     return rv;
    00224 }
    00225
    00232 bool QwtScaleDiv::buildLogDiv(int maxMajSteps, int maxMinSteps, double majStep)
    00233 {
    00234     double firstTick, lastTick;
    00235     double lFirst, lLast;
    00236     double val, sval, minStep, minFactor;
    00237     int nMaj, nMin, minSize, i, k, k0, kstep, kmax, i0;
    00238     int rv = TRUE;
    00239     double width;
    00240
    00241     QwtArray<double> buffer;
    00242
    00243
    00244     // Parameter range check
    00245     maxMajSteps = qwtMax(1, qwtAbs(maxMajSteps));
    00246     maxMinSteps = qwtMax(0, qwtAbs(maxMinSteps));
    00247     majStep = qwtAbs(majStep);
    00248
    00249     // boundary check
    00250     qwtLimRange(d_hBound, LOG_MIN, LOG_MAX);
    00251     qwtLimRange(d_lBound, LOG_MIN, LOG_MAX);
    00252
    00253     // detach arrays
    00254     d_majMarks.duplicate(0,0);
    00255     d_minMarks.duplicate(0,0);
    00256
    00257     if (d_lBound == d_hBound) return TRUE;
    00258
    00259     // scale width in decades
    00260     width = log10(d_hBound) - log10(d_lBound);
    00261
    00262     // scale width is less than one decade -> build linear scale
    00263     if (width < 1.0)
    00264     {
    00265         rv = buildLinDiv(maxMajSteps, maxMinSteps, 0.0);
    00266         // convert step width to decades
    00267         if (d_majStep > 0)
    00268            d_majStep = log10(d_majStep);
    00269
    00270         return rv;
    00271     }
    00272
    00273     //
    00274     //  Set up major scale divisions
    00275     //
    00276     if (majStep == 0.0)
    00277        d_majStep = qwtCeil125( width * 0.999999 / double(maxMajSteps));
    00278     else
    00279        d_majStep = majStep;
    00280
    00281     // major step must be >= 1 decade
    00282     d_majStep = qwtMax(d_majStep, 1.0);
    00283
    00284
    00285     lFirst = ceil((log10(d_lBound) - step_eps * d_majStep) / d_majStep) * d_majStep;
    00286     lLast = floor((log10(d_hBound) + step_eps * d_majStep) / d_majStep) * d_majStep;
    00287
    00288     firstTick = pow(10.0, lFirst);
    00289     lastTick = pow(10.0, lLast);
    00290
    00291     nMaj = qwtMin(10000, int(floor (qwtAbs(lLast - lFirst) / d_majStep + 0.5)) + 1);
    00292
    00293     if (d_majMarks.resize(nMaj))
    00294        qwtLogSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick);
    00295     else
    00296        return FALSE;
    00297
    00298
    00299     //
    00300     // Set up minor scale divisions
    00301     //
    00302
    00303     if ((d_majMarks.size() < 1) || (maxMinSteps < 1)) return TRUE; // no minor marks
    00304
    00305     if (d_majStep < 1.1)            // major step width is one decade
    00306     {
    00307         if (maxMinSteps >= 8)
    00308         {
    00309             k0 = 2;
    00310             kmax = 9;
    00311             kstep = 1;
    00312             minSize = (d_majMarks.size() + 1) * 8;
    00313         }
    00314         else if (maxMinSteps >= 4)
    00315         {
    00316             k0 = 2;
    00317             kmax = 8;
    00318             kstep = 2;
    00319             minSize = (d_majMarks.size() + 1) * 4;
    00320         }
    00321         else if (maxMinSteps >= 2)
    00322         {
    00323             k0 = 2;
    00324             kmax = 5;
    00325             kstep = 3;
    00326             minSize = (d_majMarks.size() + 1) * 2;
    00327         }
    00328         else
    00329         {
    00330             k0 = 5;
    00331             kmax = 5;
    00332             kstep = 1;
    00333             minSize = (d_majMarks.size() + 1);
    00334         }
    00335
    00336         // resize buffer to the max. possible number of minor marks
    00337         buffer.resize(minSize);
    00338
    00339         // Are there minor ticks below the first major tick?
    00340         if ( d_lBound < firstTick )
    00341             i0 = -1;
    00342         else
    00343            i0 = 0;
    00344
    00345         minSize = 0;
    00346         for (i = i0; i< (int)d_majMarks.size(); i++)
    00347         {
    00348             if (i >= 0)
    00349                val = d_majMarks[i];
    00350             else
    00351                val = d_majMarks[0] / pow(10.0, d_majStep);
    00352
    00353             for (k=k0; k<= kmax; k+=kstep)
    00354             {
    00355             sval = val * double(k);
    00356             if (qwtLimRange(sval, d_lBound, d_hBound, border_eps))
    00357             {
    00358                 buffer[minSize] = sval;
    00359                 minSize++;
    00360             }
    00361             }
    00362         }
    00363
    00364         // copy values into the minMarks array
    00365         d_minMarks.duplicate(buffer.data(), minSize);
    00366
    00367     }
    00368     else                // major step > one decade
    00369     {
    00370
    00371         // substep width in decades, at least one decade
    00372         minStep = qwtCeil125( (d_majStep - step_eps * (d_majStep / double(maxMinSteps)))
    00373                  /  double(maxMinSteps) );
    00374         minStep = qwtMax(1.0, minStep);
    00375
    00376         // # subticks per interval
    00377         nMin = int(floor (d_majStep / minStep + 0.5)) - 1;
    00378
    00379         // Do the minor steps fit into the interval?
    00380         if ( qwtAbs( double(nMin + 1) * minStep - d_majStep)  >  step_eps * d_majStep)
    00381             nMin = 0;
    00382
    00383         if (nMin < 1) return TRUE;      // no subticks
    00384
    00385         // resize buffer to max. possible number of subticks
    00386         buffer.resize((d_majMarks.size() + 1) * nMin );
    00387
    00388         // substep factor = 10^substeps
    00389         minFactor = qwtMax(pow(10.0, minStep), 10.0);
    00390
    00391         // Are there minor ticks below the first major tick?
    00392         if ( d_lBound < firstTick )
    00393             i0 = -1;
    00394         else
    00395            i0 = 0;
    00396
    00397         minSize = 0;
    00398         for (i = i0; i< (int)d_majMarks.size(); i++)
    00399         {
    00400             if (i >= 0)
    00401                val = d_majMarks[i];
    00402             else
    00403                val = firstTick / pow(10.0, d_majStep);
    00404
    00405             for (k=0; k< nMin; k++)
    00406             {
    00407                 sval = (val *= minFactor);
    00408                 if (qwtLimRange(sval, d_lBound, d_hBound, border_eps))
    00409                 {
    00410                     buffer[minSize] = sval;
    00411                     minSize++;
    00412                 }
    00413             }
    00414         }
    00415         d_minMarks.duplicate(buffer.data(), minSize);
    00416     }
    00417
    00418     return rv;
    00419 }
    00420
    00425 int QwtScaleDiv::operator==(const QwtScaleDiv &s) const
    00426 {
    00427     if (d_lBound != s.d_lBound) return 0;
    00428     if (d_hBound != s.d_hBound) return 0;
    00429     if (d_log != s.d_log) return 0;
    00430     if (d_majStep != s.d_majStep) return 0;
    00431     if (d_majMarks != s.d_majMarks) return 0;
    00432     return (d_minMarks == s.d_minMarks);
    00433 }
    00434
    00439 int QwtScaleDiv::operator!=(const QwtScaleDiv &s) const
    00440 {
    00441     return (!(*this == s));
    00442 }
    00443
    00445 void QwtScaleDiv::reset()
    00446 {
    00447     // detach arrays
    00448     d_majMarks.duplicate(0,0);
    00449     d_minMarks.duplicate(0,0);
    00450
    00451     d_lBound = 0.0;
    00452     d_hBound = 0.0;
    00453     d_majStep = 0.0;
    00454     d_log = FALSE;
    00455 }