qwt_scale_engine.cpp 24 KB

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