PageRenderTime 30ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/GUISupport/Qt/Chart/vtkQtChartAxis.cxx

https://github.com/b3c/VTK-5.8
C++ | 1913 lines | 1585 code | 209 blank | 119 comment | 396 complexity | e81aee8c4eacaeead57894328b563cb3 MD5 | raw file
  1. /*=========================================================================
  2. Program: Visualization Toolkit
  3. Module: vtkQtChartAxis.cxx
  4. Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  5. All rights reserved.
  6. See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
  7. This software is distributed WITHOUT ANY WARRANTY; without even
  8. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  9. PURPOSE. See the above copyright notice for more information.
  10. =========================================================================*/
  11. /*-------------------------------------------------------------------------
  12. Copyright 2008 Sandia Corporation.
  13. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  14. the U.S. Government retains certain rights in this software.
  15. -------------------------------------------------------------------------*/
  16. /// \file vtkQtChartAxis.cxx
  17. /// \date February 1, 2008
  18. #ifdef _MSC_VER
  19. // Disable warnings that Qt headers give.
  20. #pragma warning(disable:4127)
  21. #endif
  22. #include "vtkQtChartAxis.h"
  23. #include "vtkQtChartAxisDomain.h"
  24. #include "vtkQtChartAxisModel.h"
  25. #include "vtkQtChartAxisOptions.h"
  26. #include "vtkQtChartContentsArea.h"
  27. #include "vtkQtChartContentsSpace.h"
  28. #include <QFontMetricsF>
  29. #include <QGraphicsLineItem>
  30. #include <QGraphicsSimpleTextItem>
  31. #include <QList>
  32. #include <QPainter>
  33. #include <QPen>
  34. #include <QVariant>
  35. #include <QtDebug>
  36. #include <math.h>
  37. class vtkQtChartAxisItem
  38. {
  39. public:
  40. vtkQtChartAxisItem();
  41. ~vtkQtChartAxisItem() {}
  42. float getLocation() const {return this->Location;}
  43. void setLocation(float location) {this->Location = location;}
  44. float getLabelWidth() const {return this->Width;}
  45. void setLabelWidth(float width) {this->Width = width;}
  46. bool isLabelVisible() const {return this->LabelVisible;}
  47. void setLabelVisible(bool visible) {this->LabelVisible = visible;}
  48. bool isTickVisible() const {return this->TickVisible;}
  49. void setTickVisible(bool visible) {this->TickVisible = visible;}
  50. public:
  51. QString Label;
  52. private:
  53. float Location;
  54. float Width;
  55. bool LabelVisible;
  56. bool TickVisible;
  57. };
  58. class vtkQtChartAxisScale
  59. {
  60. public:
  61. vtkQtChartAxisScale();
  62. ~vtkQtChartAxisScale() {}
  63. bool setValueRange(const QVariant &min, const QVariant &max);
  64. bool setPixelRange(float min, float max);
  65. int getPixelRange() const;
  66. bool isValid() const;
  67. QVariant ValueMin; ///< Stores the minimum value.
  68. QVariant ValueMax; ///< Stores the maximum value.
  69. float PixelMin; ///< Stores the minimum pixel.
  70. float PixelMax; ///< Stores the maximum pixel.
  71. bool LogAvailable; ///< True if log10 scale is valid.
  72. };
  73. class vtkQtChartAxisInternal
  74. {
  75. public:
  76. vtkQtChartAxisInternal();
  77. ~vtkQtChartAxisInternal();
  78. QList<vtkQtChartAxisItem *> Items;
  79. vtkQtChartAxisScale Scale;
  80. QSizeF Bounds;
  81. QVariant Minimum;
  82. QVariant Maximum;
  83. float FontHeight;
  84. float TickLabelSpacing;
  85. float TickLength;
  86. float SmallTickLength;
  87. float MaxLabelWidth;
  88. bool InLayout;
  89. bool UsingBestFit;
  90. bool DataAvailable;
  91. bool PadRange;
  92. bool ExpandToZero;
  93. bool AddSpace;
  94. bool SpaceTooSmall;
  95. bool FontChanged;
  96. bool ScaleChanged;
  97. bool PresentationChanged;
  98. };
  99. // The interval list is used to determine a suitable interval for a
  100. // best-fit axis.
  101. static double IntervalList[] = {1.0, 2.0, 2.5, 5.0};
  102. static int IntervalListLength = 4;
  103. //-----------------------------------------------------------------------------
  104. template<class T>
  105. T alignAxisMinimum(T minimum, T interval, T zero, bool extraPadding)
  106. {
  107. T newMinimum = minimum;
  108. if(minimum != 0)
  109. {
  110. int numIntervals = (int)(minimum / interval);
  111. newMinimum = interval * numIntervals;
  112. if(newMinimum > minimum)
  113. {
  114. newMinimum -= interval;
  115. }
  116. else if(extraPadding && newMinimum != zero && newMinimum == minimum)
  117. {
  118. newMinimum -= interval;
  119. }
  120. }
  121. return newMinimum;
  122. }
  123. template<class T>
  124. T alignAxisMaximum(T maximum, T interval, T zero, bool extraPadding)
  125. {
  126. T newMaximum = maximum;
  127. if(maximum != 0)
  128. {
  129. int numIntervals = (int)(maximum / interval);
  130. newMaximum = interval * numIntervals;
  131. if(newMaximum < maximum)
  132. {
  133. newMaximum += interval;
  134. }
  135. else if(extraPadding && newMaximum != zero && newMaximum == maximum)
  136. {
  137. newMaximum += interval;
  138. }
  139. }
  140. return newMaximum;
  141. }
  142. template<class T>
  143. float mapLinearPixel(float pixelMin, float pixelMax,
  144. T value, T valueMin, T valueMax)
  145. {
  146. float result = (float)(value - valueMin);
  147. float valueRange = (float)(valueMax - valueMin);
  148. result *= pixelMax - pixelMin;
  149. if(valueRange != 0)
  150. {
  151. result /= valueRange;
  152. }
  153. return result + pixelMin;
  154. }
  155. //-----------------------------------------------------------------------------
  156. vtkQtChartAxisItem::vtkQtChartAxisItem()
  157. : Label()
  158. {
  159. this->Location = 0.0;
  160. this->Width = 0.0;
  161. this->LabelVisible = true;
  162. this->TickVisible = true;
  163. }
  164. //-----------------------------------------------------------------------------
  165. vtkQtChartAxisScale::vtkQtChartAxisScale()
  166. : ValueMin((int)0), ValueMax((int)0)
  167. {
  168. this->PixelMin = 0;
  169. this->PixelMax = 0;
  170. this->LogAvailable = false;
  171. }
  172. bool vtkQtChartAxisScale::setValueRange(const QVariant &min,
  173. const QVariant &max)
  174. {
  175. if(min != this->ValueMin || max != this->ValueMax)
  176. {
  177. this->ValueMin = min;
  178. this->ValueMax = max;
  179. return true;
  180. }
  181. return false;
  182. }
  183. bool vtkQtChartAxisScale::setPixelRange(float min, float max)
  184. {
  185. if(this->PixelMin != min || this->PixelMax != max)
  186. {
  187. this->PixelMin = min;
  188. this->PixelMax = max;
  189. return true;
  190. }
  191. return false;
  192. }
  193. int vtkQtChartAxisScale::getPixelRange() const
  194. {
  195. if(this->PixelMax > this->PixelMin)
  196. {
  197. return (int)(this->PixelMax - this->PixelMin);
  198. }
  199. else
  200. {
  201. return (int)(this->PixelMin - this->PixelMax);
  202. }
  203. }
  204. bool vtkQtChartAxisScale::isValid() const
  205. {
  206. if(this->PixelMax == this->PixelMin)
  207. {
  208. return false;
  209. }
  210. if(this->ValueMin.type() == QVariant::Int)
  211. {
  212. return this->ValueMin.toInt() != this->ValueMax.toInt();
  213. }
  214. else if(this->ValueMin.type() == QVariant::Double)
  215. {
  216. return this->ValueMin.toDouble() != this->ValueMax.toDouble();
  217. }
  218. return false;
  219. }
  220. //-----------------------------------------------------------------------------
  221. vtkQtChartAxisInternal::vtkQtChartAxisInternal()
  222. : Items(), Scale(), Bounds(), Minimum((int)0), Maximum((int)0)
  223. {
  224. this->FontHeight = 0.0;
  225. this->TickLabelSpacing = 0.0;
  226. this->TickLength = 5.0;
  227. this->SmallTickLength = 3.0;
  228. this->MaxLabelWidth = 0;
  229. this->InLayout = false;
  230. this->UsingBestFit = false;
  231. this->DataAvailable = false;
  232. this->PadRange = false;
  233. this->ExpandToZero = false;
  234. this->AddSpace = false;
  235. this->SpaceTooSmall = false;
  236. this->FontChanged = false;
  237. this->ScaleChanged = false;
  238. this->PresentationChanged = false;
  239. }
  240. vtkQtChartAxisInternal::~vtkQtChartAxisInternal()
  241. {
  242. QList<vtkQtChartAxisItem *>::Iterator iter = this->Items.begin();
  243. for( ; iter != this->Items.end(); ++iter)
  244. {
  245. delete *iter;
  246. }
  247. }
  248. //-----------------------------------------------------------------------------
  249. static double MinIntLogPower = -1;
  250. const double vtkQtChartAxis::MinLogValue = 1e-20;
  251. vtkQtChartAxis::vtkQtChartAxis(vtkQtChartAxis::AxisLocation location,
  252. QGraphicsItem *item)
  253. : QObject(), QGraphicsItem(item, item ? item->scene() : 0)
  254. {
  255. this->Internal = new vtkQtChartAxisInternal();
  256. this->Options = new vtkQtChartAxisOptions(this);
  257. this->Model = 0;
  258. this->AtMin = 0;
  259. this->AtMax = 0;
  260. this->Across = 0;
  261. this->Zoom = 0;
  262. this->Location = location;
  263. // Set up the options object.
  264. this->Options->setObjectName("Options");
  265. this->connect(this->Options, SIGNAL(visibilityChanged()),
  266. this, SIGNAL(layoutNeeded()));
  267. this->connect(this->Options, SIGNAL(colorChanged()),
  268. this, SLOT(handleColorChange()));
  269. this->connect(this->Options, SIGNAL(fontChanged()),
  270. this, SLOT(handleFontChange()));
  271. this->connect(this->Options, SIGNAL(axisScaleChanged()),
  272. this, SLOT(handleAxisScaleChange()));
  273. this->connect(this->Options, SIGNAL(presentationChanged()),
  274. this, SLOT(handlePresentationChange()));
  275. // Set the font height and tick-label space.
  276. QFontMetricsF fm(this->Options->getLabelFont());
  277. this->Internal->FontHeight = fm.height();
  278. if(this->Location == vtkQtChartAxis::Top ||
  279. this->Location == vtkQtChartAxis::Bottom)
  280. {
  281. this->Internal->TickLabelSpacing = fm.leading();
  282. }
  283. else
  284. {
  285. this->Internal->TickLabelSpacing = fm.width(" ");
  286. }
  287. }
  288. vtkQtChartAxis::~vtkQtChartAxis()
  289. {
  290. delete this->Internal;
  291. }
  292. void vtkQtChartAxis::setModel(vtkQtChartAxisModel *model)
  293. {
  294. if(this->Model == model)
  295. {
  296. return;
  297. }
  298. if(this->Model)
  299. {
  300. // Clean up connections to the old model.
  301. this->disconnect(this->Model, 0, this, 0);
  302. }
  303. this->Model = model;
  304. if(this->Model)
  305. {
  306. // Listen to the new model's events.
  307. this->connect(this->Model, SIGNAL(labelInserted(int)),
  308. this, SLOT(insertLabel(int)));
  309. this->connect(this->Model, SIGNAL(removingLabel(int)),
  310. this, SLOT(startLabelRemoval(int)));
  311. this->connect(this->Model, SIGNAL(labelRemoved(int)),
  312. this, SLOT(finishLabelRemoval(int)));
  313. this->connect(this->Model, SIGNAL(labelsReset()),
  314. this, SLOT(reset()));
  315. }
  316. // Clean up the old view data and request a re-layout.
  317. this->reset();
  318. }
  319. void vtkQtChartAxis::setNeigbors(const vtkQtChartAxis *atMin,
  320. const vtkQtChartAxis *atMax)
  321. {
  322. // TODO: Listen for a font change from the other axes to adjust the
  323. // tick length for top and bottom axes.
  324. this->AtMin = atMin;
  325. this->AtMax = atMax;
  326. }
  327. void vtkQtChartAxis::setParallelAxis(const vtkQtChartAxis *across)
  328. {
  329. this->Across = across;
  330. }
  331. void vtkQtChartAxis::setContentsSpace(const vtkQtChartContentsSpace *contents)
  332. {
  333. if(this->Zoom)
  334. {
  335. this->disconnect(this->Zoom, 0, this, 0);
  336. }
  337. this->Zoom = contents;
  338. if(this->Zoom)
  339. {
  340. if(this->Location == vtkQtChartAxis::Top ||
  341. this->Location == vtkQtChartAxis::Bottom)
  342. {
  343. this->connect(this->Zoom, SIGNAL(xOffsetChanged(float)),
  344. this, SLOT(setOffset(float)));
  345. }
  346. else
  347. {
  348. this->connect(this->Zoom, SIGNAL(yOffsetChanged(float)),
  349. this, SLOT(setOffset(float)));
  350. }
  351. }
  352. }
  353. void vtkQtChartAxis::setDataAvailable(bool available)
  354. {
  355. this->Internal->DataAvailable = available;
  356. }
  357. bool vtkQtChartAxis::isBestFitGenerated() const
  358. {
  359. return this->Internal->UsingBestFit;
  360. }
  361. void vtkQtChartAxis::setBestFitGenerated(bool on)
  362. {
  363. this->Internal->UsingBestFit = on;
  364. }
  365. void vtkQtChartAxis::getBestFitRange(QVariant &min, QVariant &max) const
  366. {
  367. min = this->Internal->Minimum;
  368. max = this->Internal->Maximum;
  369. }
  370. void vtkQtChartAxis::setBestFitRange(const QVariant &min, const QVariant &max)
  371. {
  372. if(min.type() != max.type())
  373. {
  374. return;
  375. }
  376. if(min.type() == QVariant::Int || min.type() == QVariant::Double)
  377. {
  378. bool swap = false;
  379. if(min.type() == QVariant::Int)
  380. {
  381. swap = max.toInt() < min.toInt();
  382. }
  383. else if(min.type() == QVariant::Double)
  384. {
  385. swap = max.toDouble() < min.toDouble();
  386. }
  387. if(swap)
  388. {
  389. this->Internal->Minimum = max;
  390. this->Internal->Maximum = min;
  391. }
  392. else
  393. {
  394. this->Internal->Minimum = min;
  395. this->Internal->Maximum = max;
  396. }
  397. }
  398. }
  399. bool vtkQtChartAxis::isRangePaddingUsed() const
  400. {
  401. return this->Internal->PadRange;
  402. }
  403. void vtkQtChartAxis::setRangePaddingUsed(bool padRange)
  404. {
  405. this->Internal->PadRange = padRange;
  406. }
  407. bool vtkQtChartAxis::isExpansionToZeroUsed() const
  408. {
  409. return this->Internal->ExpandToZero;
  410. }
  411. void vtkQtChartAxis::setExpansionToZeroUsed(bool expand)
  412. {
  413. this->Internal->ExpandToZero = expand;
  414. }
  415. bool vtkQtChartAxis::isExtraSpaceUsed() const
  416. {
  417. return this->Internal->AddSpace;
  418. }
  419. void vtkQtChartAxis::setExtraSpaceUsed(bool addSpace)
  420. {
  421. this->Internal->AddSpace = addSpace;
  422. }
  423. bool vtkQtChartAxis::isSpaceTooSmall() const
  424. {
  425. return this->Internal->SpaceTooSmall;
  426. }
  427. void vtkQtChartAxis::setSpaceTooSmall(bool tooSmall)
  428. {
  429. this->Internal->SpaceTooSmall = tooSmall;
  430. }
  431. void vtkQtChartAxis::setOptions(const vtkQtChartAxisOptions &options)
  432. {
  433. // Copy the new options.
  434. *(this->Options) = options;
  435. // Handle the worst case option changes: font and presentation.
  436. this->Internal->PresentationChanged = true;
  437. this->handleFontChange();
  438. }
  439. void vtkQtChartAxis::layoutAxis(const QRectF &area)
  440. {
  441. // Use the total chart area and the neighboring axes to set the
  442. // bounding rectangle. Shrink the width and height of the area to
  443. // account for the way Qt draws rectangles.
  444. float space = 0;
  445. QRectF neighbor;
  446. QRectF bounds(area.x(), area.y(), area.width() - 1, area.height() - 1);
  447. if(this->Location == vtkQtChartAxis::Top)
  448. {
  449. float topDiff = 0;
  450. if(!this->Internal->SpaceTooSmall)
  451. {
  452. space = this->getPreferredSpace();
  453. }
  454. if(this->AtMin && !this->AtMin->isSpaceTooSmall())
  455. {
  456. neighbor = this->AtMin->getBounds();
  457. if(neighbor.isValid())
  458. {
  459. topDiff = neighbor.top() - bounds.top();
  460. if(topDiff > space)
  461. {
  462. space = topDiff;
  463. }
  464. }
  465. }
  466. if(this->AtMax && !this->AtMax->isSpaceTooSmall())
  467. {
  468. neighbor = this->AtMax->getBounds();
  469. if(neighbor.isValid())
  470. {
  471. topDiff = neighbor.top() - bounds.top();
  472. if(topDiff > space)
  473. {
  474. space = topDiff;
  475. }
  476. }
  477. }
  478. bounds.setBottom(bounds.top() + space);
  479. }
  480. else if(this->Location == vtkQtChartAxis::Bottom)
  481. {
  482. float bottomDiff = 0;
  483. if(!this->Internal->SpaceTooSmall)
  484. {
  485. space = this->getPreferredSpace();
  486. }
  487. if(this->AtMin && !this->AtMin->isSpaceTooSmall())
  488. {
  489. neighbor = this->AtMin->getBounds();
  490. if(neighbor.isValid())
  491. {
  492. bottomDiff = bounds.bottom() - neighbor.bottom();
  493. if(bottomDiff > space)
  494. {
  495. space = bottomDiff;
  496. }
  497. }
  498. }
  499. if(this->AtMax && !this->AtMax->isSpaceTooSmall())
  500. {
  501. neighbor = this->AtMax->getBounds();
  502. if(neighbor.isValid())
  503. {
  504. bottomDiff = bounds.bottom() - neighbor.bottom();
  505. if(bottomDiff > space)
  506. {
  507. space = bottomDiff;
  508. }
  509. }
  510. }
  511. bounds.setTop(bounds.bottom() - space);
  512. }
  513. else
  514. {
  515. float halfHeight = 0;
  516. if(!this->Internal->SpaceTooSmall)
  517. {
  518. halfHeight = this->getFontHeight() * 0.5;
  519. }
  520. if(this->Across && !this->Across->isSpaceTooSmall())
  521. {
  522. float otherHeight = this->Across->getFontHeight() * 0.5;
  523. if(otherHeight > halfHeight)
  524. {
  525. halfHeight = otherHeight;
  526. }
  527. }
  528. if(this->AtMin && !this->AtMin->isSpaceTooSmall())
  529. {
  530. space = this->AtMin->getPreferredSpace();
  531. if(halfHeight > space)
  532. {
  533. space = halfHeight;
  534. }
  535. }
  536. else
  537. {
  538. space = halfHeight;
  539. }
  540. bounds.setBottom(bounds.bottom() - space);
  541. if(this->AtMax && !this->AtMax->isSpaceTooSmall())
  542. {
  543. space = this->AtMax->getPreferredSpace();
  544. if(halfHeight > space)
  545. {
  546. space = halfHeight;
  547. }
  548. }
  549. else
  550. {
  551. space = halfHeight;
  552. }
  553. bounds.setTop(bounds.top() + space);
  554. }
  555. // Set up the contents rectangle for label generation.
  556. QRectF contents = bounds;
  557. if(this->Zoom)
  558. {
  559. if(this->Location == vtkQtChartAxis::Left ||
  560. this->Location == vtkQtChartAxis::Right)
  561. {
  562. contents.setBottom(contents.bottom() + this->Zoom->getMaximumYOffset());
  563. }
  564. else
  565. {
  566. contents.setRight(contents.right() + this->Zoom->getMaximumXOffset());
  567. }
  568. }
  569. // If the axis model is based on the size, it needs to be generated
  570. // here. Don't send a layout request change for the model events.
  571. this->Internal->InLayout = true;
  572. if(this->Options->getAxisScale() == vtkQtChartAxisOptions::Linear)
  573. {
  574. this->generateLabels(contents);
  575. }
  576. else
  577. {
  578. this->generateLogLabels(contents);
  579. }
  580. this->Internal->InLayout = false;
  581. // Calculate the label width for any new labels.
  582. int i = 0;
  583. QVariant value;
  584. QFontMetricsF fm(this->Options->getLabelFont());
  585. bool maxWidthReset = this->Internal->MaxLabelWidth == 0;
  586. QList<vtkQtChartAxisItem *>::Iterator iter = this->Internal->Items.begin();
  587. for( ; iter != this->Internal->Items.end(); ++iter, ++i)
  588. {
  589. bool newLabel = false;
  590. if((*iter)->Label.isEmpty() || this->Internal->PresentationChanged)
  591. {
  592. // Get the label value from the model and set the item's text.
  593. this->Model->getLabel(i, value);
  594. (*iter)->Label = this->Options->formatValue(value);
  595. (*iter)->setLabelWidth(fm.width((*iter)->Label));
  596. newLabel = true;
  597. }
  598. else if(this->Internal->FontChanged)
  599. {
  600. (*iter)->setLabelWidth(fm.width((*iter)->Label));
  601. }
  602. if(maxWidthReset || newLabel)
  603. {
  604. // If the max label width was reset or the label is new, use
  605. // the label width to find the new max.
  606. float labelWidth = (*iter)->getLabelWidth();
  607. if(labelWidth > this->Internal->MaxLabelWidth)
  608. {
  609. this->Internal->MaxLabelWidth = labelWidth;
  610. }
  611. }
  612. }
  613. // Use the maximum label width to finish setting the bounds.
  614. this->Internal->FontChanged = false;
  615. if(this->Location == vtkQtChartAxis::Left)
  616. {
  617. space = 0;
  618. if(!this->Internal->SpaceTooSmall && this->Model &&
  619. this->Model->getNumberOfLabels() > 1)
  620. {
  621. space = this->getPreferredSpace();
  622. }
  623. bounds.setRight(bounds.left() + space);
  624. contents.setRight(bounds.right());
  625. }
  626. else if(this->Location == vtkQtChartAxis::Right)
  627. {
  628. space = 0;
  629. if(!this->Internal->SpaceTooSmall && this->Model &&
  630. this->Model->getNumberOfLabels() > 1)
  631. {
  632. space = this->getPreferredSpace();
  633. }
  634. bounds.setLeft(bounds.right() - space);
  635. contents.setLeft(bounds.left());
  636. }
  637. else
  638. {
  639. float halfWidth = 0;
  640. if(!this->Internal->SpaceTooSmall)
  641. {
  642. halfWidth = this->getMaxLabelWidth() * 0.5;
  643. }
  644. if(this->Across && !this->Across->isSpaceTooSmall())
  645. {
  646. float otherWidth = this->Across->getMaxLabelWidth() * 0.5;
  647. if(otherWidth > halfWidth)
  648. {
  649. halfWidth = otherWidth;
  650. }
  651. }
  652. if(this->AtMin && !this->AtMin->isSpaceTooSmall())
  653. {
  654. neighbor = this->AtMin->getBounds();
  655. space = neighbor.isValid() ? neighbor.width() : 0;
  656. if(halfWidth > space)
  657. {
  658. space = halfWidth;
  659. }
  660. }
  661. else
  662. {
  663. space = halfWidth;
  664. }
  665. bounds.setLeft(bounds.left() + space);
  666. contents.setLeft(contents.left() + space);
  667. if(this->AtMax && !this->AtMax->isSpaceTooSmall())
  668. {
  669. neighbor = this->AtMax->getBounds();
  670. space = neighbor.isValid() ? neighbor.width() : 0;
  671. if(halfWidth > space)
  672. {
  673. space = halfWidth;
  674. }
  675. }
  676. else
  677. {
  678. space = halfWidth;
  679. }
  680. bounds.setRight(bounds.right() - space);
  681. contents.setRight(contents.right() - space);
  682. }
  683. // Finalize the viewport and contents areas.
  684. this->prepareGeometryChange();
  685. this->Internal->Bounds = bounds.size();
  686. this->setPos(bounds.topLeft());
  687. // Set up the pixel-value scale. Use the contents size to determine
  688. // the maximum pixel locations.
  689. float pixelMin = 0;
  690. float pixelMax = 0;
  691. bool pixelChanged = false;
  692. if(this->Location == vtkQtChartAxis::Left ||
  693. this->Location == vtkQtChartAxis::Right)
  694. {
  695. pixelMin = contents.height();
  696. pixelMax = 0;
  697. if(this->Internal->AddSpace && !this->Internal->UsingBestFit &&
  698. this->Model->getNumberOfLabels() > 0)
  699. {
  700. // Add space around the min and max.
  701. space = ((pixelMin - pixelMax + 1.0) * 0.5) /
  702. (float)(this->Model->getNumberOfLabels());
  703. pixelMin -= space;
  704. pixelMax += space;
  705. }
  706. if(pixelMin > pixelMax)
  707. {
  708. pixelChanged = this->Internal->Scale.setPixelRange(pixelMin, pixelMax);
  709. }
  710. else
  711. {
  712. pixelChanged = this->Internal->Scale.setPixelRange(0, 0);
  713. }
  714. }
  715. else
  716. {
  717. pixelMin = 0;
  718. pixelMax = contents.width();
  719. if(this->Internal->AddSpace && !this->Internal->UsingBestFit &&
  720. this->Model->getNumberOfLabels() > 0)
  721. {
  722. // Add space around the min and max.
  723. space = ((pixelMax - pixelMin + 1.0) * 0.5) /
  724. (float)(this->Model->getNumberOfLabels());
  725. pixelMin += space;
  726. pixelMax -= space;
  727. }
  728. if(pixelMin < pixelMax)
  729. {
  730. pixelChanged = this->Internal->Scale.setPixelRange(pixelMin, pixelMax);
  731. }
  732. else
  733. {
  734. pixelChanged = this->Internal->Scale.setPixelRange(0, 0);
  735. }
  736. }
  737. bool valueChanged = false;
  738. if(this->Model && this->Model->getNumberOfLabels() > 1)
  739. {
  740. QVariant max;
  741. this->Model->getLabel(0, value);
  742. this->Model->getLabel(this->Model->getNumberOfLabels() - 1, max);
  743. valueChanged = this->Internal->Scale.setValueRange(value, max);
  744. }
  745. else
  746. {
  747. valueChanged = this->Internal->Scale.setValueRange(
  748. QVariant((int)0), QVariant((int)0));
  749. }
  750. if(valueChanged)
  751. {
  752. this->Internal->Scale.LogAvailable = vtkQtChartAxis::isLogScaleValid(
  753. this->Internal->Scale.ValueMin, this->Internal->Scale.ValueMax);
  754. }
  755. if((valueChanged || this->Internal->ScaleChanged) &&
  756. this->Options->getAxisScale() == vtkQtChartAxisOptions::Logarithmic &&
  757. !this->Internal->Scale.LogAvailable)
  758. {
  759. qWarning() << "Warning: Invalid range for a logarithmic scale. "
  760. << "Please specify a range with minimum value greater than 0 "
  761. << "for this axis.";
  762. }
  763. // Signal the chart layers if the pixel-value map changed.
  764. if(pixelChanged || valueChanged || this->Internal->ScaleChanged)
  765. {
  766. emit this->pixelScaleChanged();
  767. }
  768. this->Internal->ScaleChanged = false;
  769. this->setVisible(this->Options->isVisible() &&
  770. this->Internal->Items.size() > 0);
  771. if(this->Options->isVisible() &&
  772. (this->Options->areLabelsVisible() || this->Options->isGridVisible()))
  773. {
  774. // Calculate the pixel location for each label.
  775. iter = this->Internal->Items.begin();
  776. for(i = 0; iter != this->Internal->Items.end(); ++iter, ++i)
  777. {
  778. this->Model->getLabel(i, value);
  779. (*iter)->setLocation(this->getPixel(value));
  780. }
  781. if(this->Options->areLabelsVisible())
  782. {
  783. // If there is not space for all the labels, set up the skip count.
  784. int skip = 1;
  785. int tickSkip = 1;
  786. bool isLog = this->Internal->Scale.LogAvailable &&
  787. this->Options->getAxisScale() == vtkQtChartAxisOptions::Logarithmic;
  788. if(isLog || !this->Internal->UsingBestFit ||
  789. this->Internal->Items.size() < 3)
  790. {
  791. int needed = 0;
  792. if(this->Location == vtkQtChartAxis::Left ||
  793. this->Location == vtkQtChartAxis::Right)
  794. {
  795. needed = 2 * (int)this->Internal->FontHeight;
  796. }
  797. else
  798. {
  799. needed = (int)(this->Internal->FontHeight +
  800. this->Internal->MaxLabelWidth);
  801. }
  802. needed *= this->Internal->Items.size() - 1;
  803. int pixelRange = this->Internal->Scale.getPixelRange();
  804. if(pixelRange > 0)
  805. {
  806. skip = needed / pixelRange;
  807. if(skip == 0 || needed % pixelRange > 0)
  808. {
  809. skip += 1;
  810. }
  811. }
  812. tickSkip = 1;
  813. if(skip > 1)
  814. {
  815. // If there is not enough space for the tick marks, set up the
  816. // tick skip count.
  817. int count = skip;
  818. if(count >= this->Internal->Items.size())
  819. {
  820. count = this->Internal->Items.size() - 1;
  821. }
  822. needed = 4 * count;
  823. pixelRange = (int)this->Internal->Items[0]->getLocation();
  824. int pixel2 = (int)this->Internal->Items[count]->getLocation();
  825. if(pixel2 < pixelRange)
  826. {
  827. pixelRange = pixelRange - pixel2;
  828. }
  829. else
  830. {
  831. pixelRange = pixel2 - pixelRange;
  832. }
  833. if(pixelRange > 0)
  834. {
  835. tickSkip = needed / pixelRange;
  836. if(tickSkip == 0 || needed % pixelRange > 0)
  837. {
  838. tickSkip += 1;
  839. }
  840. }
  841. }
  842. }
  843. // Set up the label and tick mark visibility.
  844. int skipIndex = 0;
  845. iter = this->Internal->Items.begin();
  846. for(i = 0; iter != this->Internal->Items.end(); ++iter, ++i)
  847. {
  848. skipIndex = i % skip;
  849. if(skip == 1 || skipIndex == 0)
  850. {
  851. (*iter)->setTickVisible(true);
  852. (*iter)->setLabelVisible(true);
  853. }
  854. else if(tickSkip == 1 || skipIndex % tickSkip == 0)
  855. {
  856. (*iter)->setTickVisible(true);
  857. (*iter)->setLabelVisible(false);
  858. }
  859. else
  860. {
  861. (*iter)->setTickVisible(false);
  862. }
  863. }
  864. }
  865. }
  866. }
  867. void vtkQtChartAxis::adjustAxisLayout()
  868. {
  869. if(!this->Internal->Bounds.isValid())
  870. {
  871. return;
  872. }
  873. float diff = 0;
  874. QRectF bounds;
  875. if(this->Location == vtkQtChartAxis::Left)
  876. {
  877. float right = this->pos().x() + this->Internal->Bounds.width();
  878. diff = right;
  879. if(this->AtMin)
  880. {
  881. bounds = this->AtMin->getBounds();
  882. if(bounds.left() > right)
  883. {
  884. right = bounds.left();
  885. }
  886. }
  887. if(this->AtMax)
  888. {
  889. bounds = this->AtMin->getBounds();
  890. if(bounds.left() > right)
  891. {
  892. right = bounds.left();
  893. }
  894. }
  895. diff = right - diff;
  896. if(diff > 0)
  897. {
  898. this->Internal->Bounds.setWidth(right - this->pos().x());
  899. }
  900. }
  901. else if(this->Location == vtkQtChartAxis::Right)
  902. {
  903. float left = this->pos().x();
  904. if(this->AtMin)
  905. {
  906. bounds = this->AtMin->getBounds();
  907. if(bounds.right() < left)
  908. {
  909. left = bounds.right();
  910. }
  911. }
  912. if(this->AtMax)
  913. {
  914. bounds = this->AtMin->getBounds();
  915. if(bounds.right() < left)
  916. {
  917. left = bounds.right();
  918. }
  919. }
  920. diff = this->pos().x() - left;
  921. if(diff > 0)
  922. {
  923. this->setPos(left, this->pos().y());
  924. this->Internal->Bounds.setWidth(this->Internal->Bounds.width() + diff);
  925. }
  926. }
  927. }
  928. float vtkQtChartAxis::getPreferredSpace() const
  929. {
  930. if(this->Model && this->Options->isVisible() &&
  931. this->Options->areLabelsVisible())
  932. {
  933. if(this->Internal->UsingBestFit && !this->Internal->DataAvailable &&
  934. this->Internal->Minimum == this->Internal->Maximum)
  935. {
  936. return 0;
  937. }
  938. if(this->Location == vtkQtChartAxis::Top ||
  939. this->Location == vtkQtChartAxis::Bottom)
  940. {
  941. // The preferred height is the sum of the font height, the tick
  942. // length and the tick-label spacing.
  943. return this->Internal->FontHeight + this->Internal->TickLength +
  944. this->Internal->TickLabelSpacing;
  945. }
  946. else
  947. {
  948. // The preferred width is the sum of the widest label, the tick
  949. // length and the tick-label spacing.
  950. return this->Internal->MaxLabelWidth + this->Internal->TickLength +
  951. this->Internal->TickLabelSpacing;
  952. }
  953. }
  954. return 0;
  955. }
  956. float vtkQtChartAxis::getFontHeight() const
  957. {
  958. if(this->Model && this->Options->isVisible() &&
  959. this->Options->areLabelsVisible())
  960. {
  961. if(this->Internal->UsingBestFit && !this->Internal->DataAvailable &&
  962. this->Internal->Minimum == this->Internal->Maximum)
  963. {
  964. return 0;
  965. }
  966. return this->Internal->FontHeight;
  967. }
  968. return 0;
  969. }
  970. float vtkQtChartAxis::getMaxLabelWidth() const
  971. {
  972. if(this->Options->isVisible() && this->Options->areLabelsVisible())
  973. {
  974. return this->Internal->MaxLabelWidth;
  975. }
  976. return 0;
  977. }
  978. float vtkQtChartAxis::getTickLength() const
  979. {
  980. return this->Internal->TickLength;
  981. }
  982. float vtkQtChartAxis::getSmallTickLength() const
  983. {
  984. return this->Internal->SmallTickLength;
  985. }
  986. float vtkQtChartAxis::getTickLabelSpacing() const
  987. {
  988. return this->Internal->TickLabelSpacing;
  989. }
  990. bool vtkQtChartAxis::isLogScaleAvailable() const
  991. {
  992. return this->Internal->Scale.LogAvailable;
  993. }
  994. void vtkQtChartAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
  995. QWidget *)
  996. {
  997. if(!this->Options->isVisible())
  998. {
  999. return;
  1000. }
  1001. // If the model is empty, there's nothing to paint.
  1002. if(!this->Model || this->Model->getNumberOfLabels() == 0)
  1003. {
  1004. return;
  1005. }
  1006. // Draw the axis line.
  1007. painter->setPen(this->Options->getAxisColor());
  1008. if(this->Location == vtkQtChartAxis::Left)
  1009. {
  1010. float right = this->Internal->Bounds.width();
  1011. painter->drawLine(QPointF(right, 0.0),
  1012. QPointF(right, this->Internal->Bounds.height()));
  1013. }
  1014. else if(this->Location == vtkQtChartAxis::Top)
  1015. {
  1016. float bottom = this->Internal->Bounds.height();
  1017. painter->drawLine(QPointF(0.0, bottom),
  1018. QPointF(this->Internal->Bounds.width(), bottom));
  1019. }
  1020. else if(this->Location == vtkQtChartAxis::Right)
  1021. {
  1022. painter->drawLine(QPointF(0.0, 0.0),
  1023. QPointF(0.0, this->Internal->Bounds.height()));
  1024. }
  1025. else
  1026. {
  1027. painter->drawLine(QPointF(0.0, 0.0),
  1028. QPointF(this->Internal->Bounds.width(), 0.0));
  1029. }
  1030. if(!this->Options->areLabelsVisible())
  1031. {
  1032. return;
  1033. }
  1034. // Set up the constant values based on the axis location.
  1035. float paintsX = 0;
  1036. float paintsY = 0;
  1037. float tick = 0;
  1038. float tickSmall = 0;
  1039. if(this->Location == vtkQtChartAxis::Left)
  1040. {
  1041. paintsX = this->Internal->Bounds.width();
  1042. tick = paintsX - this->Internal->TickLength;
  1043. tickSmall = paintsX - this->Internal->SmallTickLength;
  1044. }
  1045. else if(this->Location == vtkQtChartAxis::Top)
  1046. {
  1047. paintsY = this->Internal->Bounds.height();
  1048. tick = paintsY - this->Internal->TickLength;
  1049. tickSmall = paintsY - this->Internal->SmallTickLength;
  1050. }
  1051. else if(this->Location == vtkQtChartAxis::Right)
  1052. {
  1053. paintsX = 0.0;
  1054. tick = paintsX + this->Internal->TickLength;
  1055. tickSmall = paintsX + this->Internal->SmallTickLength;
  1056. }
  1057. else
  1058. {
  1059. paintsY = 0.0;
  1060. tick = paintsY + this->Internal->TickLength;
  1061. tickSmall = paintsY + this->Internal->SmallTickLength;
  1062. }
  1063. QFontMetricsF fm(this->Options->getLabelFont());
  1064. float fontAscent = fm.ascent();
  1065. float halfAscent = fontAscent * 0.4;
  1066. float fontDescent = fm.descent();
  1067. bool vertical = this->Location == vtkQtChartAxis::Left ||
  1068. this->Location == vtkQtChartAxis::Right;
  1069. // Draw the axis ticks and labels.
  1070. painter->setFont(this->Options->getLabelFont());
  1071. QList<vtkQtChartAxisItem *>::Iterator iter = this->Internal->Items.begin();
  1072. for( ; iter != this->Internal->Items.end(); ++iter)
  1073. {
  1074. if(vertical)
  1075. {
  1076. // Transform the contents coordinate to bounds space.
  1077. paintsY = (*iter)->getLocation();
  1078. if(this->Zoom)
  1079. {
  1080. paintsY -= this->Zoom->getYOffset();
  1081. }
  1082. // Make sure the label is inside the axis bounds.
  1083. if(paintsY > this->Internal->Bounds.height() + 0.5)
  1084. {
  1085. continue;
  1086. }
  1087. else if(paintsY < -0.5)
  1088. {
  1089. break;
  1090. }
  1091. // Draw the tick mark for the label. If the label won't fit,
  1092. // draw a smaller tick mark.
  1093. if((*iter)->isTickVisible())
  1094. {
  1095. painter->setPen(this->Options->getAxisColor());
  1096. if((*iter)->isLabelVisible())
  1097. {
  1098. painter->drawLine(QPointF(tick, paintsY), QPointF(paintsX, paintsY));
  1099. painter->setPen(this->Options->getLabelColor());
  1100. paintsY += halfAscent;
  1101. if(this->Location == vtkQtChartAxis::Left)
  1102. {
  1103. painter->drawText(QPointF(tick - (*iter)->getLabelWidth() -
  1104. this->Internal->TickLabelSpacing, paintsY), (*iter)->Label);
  1105. }
  1106. else
  1107. {
  1108. painter->drawText(
  1109. QPointF(tick + this->Internal->TickLabelSpacing, paintsY),
  1110. (*iter)->Label);
  1111. }
  1112. }
  1113. else
  1114. {
  1115. painter->drawLine(QPointF(tickSmall, paintsY), QPointF(paintsX, paintsY));
  1116. }
  1117. }
  1118. }
  1119. else
  1120. {
  1121. // Transform the contents coordinate to bounds space.
  1122. paintsX = (*iter)->getLocation();
  1123. if(this->Zoom)
  1124. {
  1125. paintsX -= this->Zoom->getXOffset();
  1126. }
  1127. // Make sure the label is inside the axis bounds.
  1128. if(paintsX < -0.5)
  1129. {
  1130. continue;
  1131. }
  1132. else if(paintsX > this->Internal->Bounds.width() + 0.5)
  1133. {
  1134. break;
  1135. }
  1136. // Draw the tick mark for the label. If the label won't fit,
  1137. // draw a smaller tick mark.
  1138. if((*iter)->isTickVisible())
  1139. {
  1140. painter->setPen(this->Options->getAxisColor());
  1141. if((*iter)->isLabelVisible())
  1142. {
  1143. painter->drawLine(QPointF(paintsX, tick), QPointF(paintsX, paintsY));
  1144. painter->setPen(this->Options->getLabelColor());
  1145. paintsX -= (*iter)->getLabelWidth() * 0.5;
  1146. if(this->Location == vtkQtChartAxis::Top)
  1147. {
  1148. painter->drawText(QPointF(paintsX,
  1149. tick - this->Internal->TickLabelSpacing - fontDescent),
  1150. (*iter)->Label);
  1151. }
  1152. else
  1153. {
  1154. painter->drawText(QPointF(paintsX,
  1155. tick + this->Internal->TickLabelSpacing + fontAscent),
  1156. (*iter)->Label);
  1157. }
  1158. }
  1159. else
  1160. {
  1161. painter->drawLine(QPointF(paintsX, tickSmall), QPointF(paintsX, paintsY));
  1162. }
  1163. }
  1164. }
  1165. }
  1166. }
  1167. QRectF vtkQtChartAxis::boundingRect() const
  1168. {
  1169. if(this->Location == vtkQtChartAxis::Left ||
  1170. this->Location == vtkQtChartAxis::Right)
  1171. {
  1172. return QRectF(0.0, -this->Internal->FontHeight * 0.5,
  1173. this->Internal->Bounds.width(), this->Internal->Bounds.height() +
  1174. this->Internal->FontHeight);
  1175. }
  1176. else
  1177. {
  1178. return QRectF(-this->Internal->MaxLabelWidth * 0.5, 0.0,
  1179. this->Internal->Bounds.width() + this->Internal->MaxLabelWidth,
  1180. this->Internal->Bounds.height());
  1181. }
  1182. }
  1183. QRectF vtkQtChartAxis::getBounds() const
  1184. {
  1185. return QRectF(this->pos(), this->Internal->Bounds);
  1186. }
  1187. bool vtkQtChartAxis::isLabelTickVisible(int index) const
  1188. {
  1189. if(index >= 0 || index < this->Internal->Items.size())
  1190. {
  1191. return this->Internal->Items[index]->isTickVisible();
  1192. }
  1193. return false;
  1194. }
  1195. float vtkQtChartAxis::getLabelLocation(int index) const
  1196. {
  1197. if(index >= 0 || index < this->Internal->Items.size())
  1198. {
  1199. return this->Internal->Items[index]->getLocation();
  1200. }
  1201. return -1;
  1202. }
  1203. vtkQtChartAxis::AxisDomain vtkQtChartAxis::getAxisDomain() const
  1204. {
  1205. QVariant::Type domain = this->Internal->Scale.ValueMin.type();
  1206. return vtkQtChartAxisDomain::getAxisDomain(domain);
  1207. }
  1208. bool vtkQtChartAxis::isValueInDomain(const QVariant &value) const
  1209. {
  1210. QVariant::Type domain = this->Internal->Scale.ValueMin.type();
  1211. if(value.type() == domain)
  1212. {
  1213. return true;
  1214. }
  1215. else if((value.type() == QVariant::Int && domain == QVariant::Double) ||
  1216. (value.type() == QVariant::Double && domain == QVariant::Int))
  1217. {
  1218. return true;
  1219. }
  1220. else if((value.type() == QVariant::Date && domain == QVariant::DateTime) ||
  1221. (value.type() == QVariant::DateTime && domain == QVariant::Date))
  1222. {
  1223. return true;
  1224. }
  1225. return false;
  1226. }
  1227. float vtkQtChartAxis::getPixel(const QVariant &value) const
  1228. {
  1229. if(!this->isValueInDomain(value))
  1230. {
  1231. return -1;
  1232. }
  1233. QVariant::Type domain = this->Internal->Scale.ValueMin.type();
  1234. if(domain == QVariant::Int)
  1235. {
  1236. if(this->Internal->Scale.isValid())
  1237. {
  1238. if(this->Internal->Scale.LogAvailable &&
  1239. this->Options->getAxisScale() == vtkQtChartAxisOptions::Logarithmic)
  1240. {
  1241. double doubleValue = value.toDouble();
  1242. if(doubleValue < 1.0)
  1243. {
  1244. return this->Internal->Scale.PixelMin;
  1245. }
  1246. else
  1247. {
  1248. doubleValue = log10(doubleValue);
  1249. }
  1250. double doubleMin = this->Internal->Scale.ValueMin.toDouble();
  1251. if(doubleMin < 1.0)
  1252. {
  1253. doubleMin = MinIntLogPower;
  1254. }
  1255. else
  1256. {
  1257. doubleMin = log10(doubleMin);
  1258. }
  1259. double doubleMax = this->Internal->Scale.ValueMax.toDouble();
  1260. if(doubleMax < 1.0)
  1261. {
  1262. doubleMax = MinIntLogPower;
  1263. }
  1264. else
  1265. {
  1266. doubleMax = log10(doubleMax);
  1267. }
  1268. return mapLinearPixel<double>(this->Internal->Scale.PixelMin,
  1269. this->Internal->Scale.PixelMax, doubleValue, doubleMin, doubleMax);
  1270. }
  1271. else
  1272. {
  1273. return mapLinearPixel<int>(this->Internal->Scale.PixelMin,
  1274. this->Internal->Scale.PixelMax, value.toInt(),
  1275. this->Internal->Scale.ValueMin.toInt(),
  1276. this->Internal->Scale.ValueMax.toInt());
  1277. }
  1278. }
  1279. }
  1280. else if(domain == QVariant::Double)
  1281. {
  1282. if(this->Internal->Scale.isValid())
  1283. {
  1284. if(this->Internal->Scale.LogAvailable &&
  1285. this->Options->getAxisScale() == vtkQtChartAxisOptions::Logarithmic)
  1286. {
  1287. double doubleValue = value.toDouble();
  1288. if(doubleValue < vtkQtChartAxis::MinLogValue)
  1289. {
  1290. return this->Internal->Scale.PixelMin;
  1291. }
  1292. return mapLinearPixel<double>(this->Internal->Scale.PixelMin,
  1293. this->Internal->Scale.PixelMax, log10(doubleValue),
  1294. log10(this->Internal->Scale.ValueMin.toDouble()),
  1295. log10(this->Internal->Scale.ValueMax.toDouble()));
  1296. }
  1297. else
  1298. {
  1299. return mapLinearPixel<double>(this->Internal->Scale.PixelMin,
  1300. this->Internal->Scale.PixelMax, value.toDouble(),
  1301. this->Internal->Scale.ValueMin.toDouble(),
  1302. this->Internal->Scale.ValueMax.toDouble());
  1303. }
  1304. }
  1305. }
  1306. else if(domain == QVariant::String)
  1307. {
  1308. int index = this->Model->getLabelIndex(value);
  1309. if(index != -1)
  1310. {
  1311. return mapLinearPixel<int>(this->Internal->Scale.PixelMin,
  1312. this->Internal->Scale.PixelMax, index,
  1313. 0, this->Model->getNumberOfLabels() - 1);
  1314. }
  1315. }
  1316. return -1;
  1317. }
  1318. float vtkQtChartAxis::getZeroPixel() const
  1319. {
  1320. QVariant::Type domain = this->Internal->Scale.ValueMin.type();
  1321. if(domain == QVariant::Int || domain == QVariant::Double)
  1322. {
  1323. float pixel = 0.0;
  1324. if(domain == QVariant::Int)
  1325. {
  1326. pixel = this->getPixel(QVariant((int)0));
  1327. }
  1328. else
  1329. {
  1330. pixel = this->getPixel(QVariant((double)0.0));
  1331. }
  1332. if(this->Internal->Scale.PixelMin > this->Internal->Scale.PixelMax)
  1333. {
  1334. return qBound<float>(this->Internal->Scale.PixelMax, pixel,
  1335. this->Internal->Scale.PixelMin);
  1336. }
  1337. else
  1338. {
  1339. return qBound<float>(this->Internal->Scale.PixelMin, pixel,
  1340. this->Internal->Scale.PixelMax);
  1341. }
  1342. }
  1343. return this->Internal->Scale.PixelMin;
  1344. }
  1345. bool vtkQtChartAxis::isLogScaleValid(const QVariant &min, const QVariant &max)
  1346. {
  1347. bool available = false;
  1348. if(max.type() == QVariant::Int)
  1349. {
  1350. int intMin = min.toInt();
  1351. int intMax = max.toInt();
  1352. available = intMin > 0 && intMax > 0;
  1353. if(!available)
  1354. {
  1355. available = (intMin == 0 && intMin < intMax) ||
  1356. (intMax == 0 && intMax < intMin);
  1357. }
  1358. }
  1359. else if(max.type() == QVariant::Double)
  1360. {
  1361. available = min.toDouble() > 0 && max.toDouble() > 0;
  1362. }
  1363. return available;
  1364. }
  1365. void vtkQtChartAxis::reset()
  1366. {
  1367. // Clean up the current view data.
  1368. QList<vtkQtChartAxisItem *>::Iterator iter = this->Internal->Items.begin();
  1369. for( ; iter != this->Internal->Items.end(); ++iter)
  1370. {
  1371. delete *iter;
  1372. }
  1373. this->Internal->Items.clear();
  1374. this->Internal->MaxLabelWidth = 0;
  1375. if(this->Model)
  1376. {
  1377. // Query the model for the new list of labels.
  1378. int total = this->Model->getNumberOfLabels();
  1379. for(int i = 0; i < total; i++)
  1380. {
  1381. this->Internal->Items.append(new vtkQtChartAxisItem());
  1382. }
  1383. }
  1384. // Request a re-layout.
  1385. if(!this->Internal->InLayout)
  1386. {
  1387. emit this->layoutNeeded();
  1388. }
  1389. }
  1390. void vtkQtChartAxis::setOffset(float /*offset*/)
  1391. {
  1392. this->update();
  1393. }
  1394. void vtkQtChartAxis::handleFontChange()
  1395. {
  1396. // Set the font height and tick-label spacing.
  1397. QFontMetricsF fm(this->Options->getLabelFont());
  1398. this->Internal->FontHeight = fm.height();
  1399. if(this->Location == vtkQtChartAxis::Top ||
  1400. this->Location == vtkQtChartAxis::Bottom)
  1401. {
  1402. this->Internal->TickLabelSpacing = fm.leading();
  1403. }
  1404. else
  1405. {
  1406. this->Internal->TickLabelSpacing = fm.width(" ");
  1407. }
  1408. // Set the font changed flag to update the label layout. Clear the
  1409. // max label width so it will be recalculated for the new font.
  1410. this->Internal->FontChanged = true;
  1411. this->Internal->MaxLabelWidth = 0;
  1412. // Request a re-layout.
  1413. emit this->layoutNeeded();
  1414. }
  1415. void vtkQtChartAxis::handlePresentationChange()
  1416. {
  1417. // Clear the max label width and clear the text labels.
  1418. this->Internal->MaxLabelWidth = 0;
  1419. this->Internal->PresentationChanged = true;
  1420. // Request a re-layout.
  1421. emit this->layoutNeeded();
  1422. }
  1423. void vtkQtChartAxis::handleColorChange()
  1424. {
  1425. this->update();
  1426. }
  1427. void vtkQtChartAxis::handleAxisScaleChange()
  1428. {
  1429. this->Internal->ScaleChanged = true;
  1430. emit this->layoutNeeded();
  1431. }
  1432. void vtkQtChartAxis::insertLabel(int index)
  1433. {
  1434. if(index < 0)
  1435. {
  1436. qDebug() << "Chart axis label inserted at index less than zero.";
  1437. return;
  1438. }
  1439. if(index < this->Internal->Items.size())
  1440. {
  1441. this->Internal->Items.insert(index, new vtkQtChartAxisItem());
  1442. }
  1443. else
  1444. {
  1445. this->Internal->Items.append(new vtkQtChartAxisItem());
  1446. }
  1447. // Request a re-layout.
  1448. if(!this->Internal->InLayout)
  1449. {
  1450. emit this->layoutNeeded();
  1451. }
  1452. }
  1453. void vtkQtChartAxis::startLabelRemoval(int index)
  1454. {
  1455. if(index >= 0 && index < this->Internal->Items.size())
  1456. {
  1457. delete this->Internal->Items.takeAt(index);
  1458. }
  1459. }
  1460. void vtkQtChartAxis::finishLabelRemoval(int /*index*/)
  1461. {
  1462. // Reset the max width.
  1463. this->Internal->MaxLabelWidth = 0;
  1464. // Request a re-layout.
  1465. if(!this->Internal->InLayout)
  1466. {
  1467. emit this->layoutNeeded();
  1468. }
  1469. }
  1470. float vtkQtChartAxis::getLabelWidthGuess(const QVariant &minimum,
  1471. const QVariant &maximum) const
  1472. {
  1473. // If the axis uses logarithmic scale with integer values, the
  1474. // values can be converted to floats.
  1475. int length1 = 0;
  1476. int length2 = 0;
  1477. if(this->Options->getAxisScale() == vtkQtChartAxisOptions::Logarithmic &&
  1478. this->Internal->Minimum.type() == QVariant::Int)
  1479. {
  1480. QVariant value = maximum.toDouble();
  1481. length1 = this->Options->formatValue(value).length();
  1482. value = minimum.toDouble();
  1483. length2 = this->Options->formatValue(value).length();
  1484. }
  1485. else
  1486. {
  1487. length1 = this->Options->formatValue(maximum).length();
  1488. length2 = this->Options->formatValue(minimum).length();
  1489. }
  1490. if(length2 > length1)
  1491. {
  1492. length1 = length2;
  1493. }
  1494. // Use a string of '8's to determine the maximum font width
  1495. // in case the font is not fixed-pitch.
  1496. QFontMetricsF fm(this->Options->getLabelFont());
  1497. QString label;
  1498. label.fill('8', length1);
  1499. return fm.width(label);
  1500. }
  1501. void vtkQtChartAxis::generateLabels(const QRectF &contents)
  1502. {
  1503. if(!this->Internal->UsingBestFit || !this->Model)
  1504. {
  1505. return;
  1506. }
  1507. // Clear the current labels from the model.
  1508. this->Model->startModifyingData();
  1509. this->Model->removeAllLabels();
  1510. // Expand the minimum/maximum to zero if needed.
  1511. QVariant minimum = this->Internal->Minimum;
  1512. QVariant maximum = this->Internal->Maximum;
  1513. if(this->Internal->DataAvailable && this->Internal->ExpandToZero)
  1514. {
  1515. if(minimum.type() == QVariant::Double)
  1516. {
  1517. if(maximum.toDouble() < 0.0)
  1518. {
  1519. maximum = (double)0.0;
  1520. }
  1521. else if(minimum.toDouble() > 0.0)
  1522. {
  1523. minimum = (double)0.0;
  1524. }
  1525. }
  1526. else if(minimum.type() == QVariant::Int)
  1527. {
  1528. if(maximum.toInt() < 0)
  1529. {
  1530. maximum = (int)0;
  1531. }
  1532. else if(minimum.toInt() > 0)
  1533. {
  1534. minimum = (int)0;
  1535. }
  1536. }
  1537. }
  1538. if(minimum != maximum)
  1539. {
  1540. // Find the number of labels that will fit in the contents.
  1541. int allowed = 0;
  1542. if(this->Location == vtkQtChartAxis::Top ||
  1543. this->Location == vtkQtChartAxis::Bottom)
  1544. {
  1545. // The contents width doesn't account for the label width, the
  1546. // neighbor width, or the label width from the axis parallel to
  1547. // this one.
  1548. QRectF neighbor;
  1549. float labelWidth = this->getLabelWidthGuess(minimum, maximum);
  1550. float halfWidth = labelWidth * 0.5;
  1551. if(this->Across && !this->Across->isSpaceTooSmall())
  1552. {
  1553. float otherWidth = this->Across->getMaxLabelWidth() * 0.5;
  1554. if(otherWidth > halfWidth)
  1555. {
  1556. halfWidth = otherWidth;
  1557. }
  1558. }
  1559. float total = contents.width();
  1560. float space = halfWidth;
  1561. if(this->AtMin && !this->AtMin->isSpaceTooSmall())
  1562. {
  1563. neighbor = this->AtMin->getBounds();
  1564. space = neighbor.isValid() ? neighbor.width() : 0;
  1565. if(space < halfWidth)
  1566. {
  1567. space = halfWidth;
  1568. }
  1569. }
  1570. total -= space;
  1571. space = halfWidth;
  1572. if(this->AtMax && !this->AtMax->isSpaceTooSmall())
  1573. {
  1574. neighbor = this->AtMax->getBounds();
  1575. space = neighbor.isValid() ? neighbor.width() : 0;
  1576. if(space < halfWidth)
  1577. {
  1578. space = halfWidth;
  1579. }
  1580. }
  1581. total -= space;
  1582. allowed = (int)(total / (labelWidth + this->Internal->FontHeight));
  1583. }
  1584. else
  1585. {
  1586. allowed = (int)(contents.height() / (2.0 * this->Internal->FontHeight));
  1587. }
  1588. if(allowed > 1)
  1589. {
  1590. // Find the value range. Convert integers to floating point
  1591. // values to compare with the interval list.
  1592. double range = maximum.toDouble() - minimum.toDouble();
  1593. // Convert the value interval to exponent format for comparison.
  1594. // Save the exponent for re-application.
  1595. range /= allowed;
  1596. QString rangeString;
  1597. rangeString.setNum(range, 'e', 1);
  1598. int exponent = 0;
  1599. int index = rangeString.indexOf("e");
  1600. if(index != -1)
  1601. {
  1602. exponent = rangeString.right(rangeString.length() - index - 1).toInt();
  1603. rangeString.truncate((unsigned int)index);
  1604. }
  1605. // Set the new value for the range, which excludes exponent.
  1606. range = rangeString.toDouble();
  1607. // Search through the interval list for the closest one.
  1608. // Convert the negative interval to match the positive
  1609. // list values. Make sure the interval is not too small
  1610. // for the chart label precision.
  1611. bool negative = range < 0.0;
  1612. if(negative)
  1613. {
  1614. range *= -1;
  1615. }
  1616. bool found = false;
  1617. int minExponent = -this->Options->getPrecision();
  1618. if(this->Internal->Maximum.type() == QVariant::Int)
  1619. {
  1620. minExponent = 0;
  1621. }
  1622. // FIX: If the range is very small (exponent<0), we want to use
  1623. // more intervals, not fewer.
  1624. if(exponent < minExponent && exponent > 0)
  1625. {
  1626. found = true;
  1627. range = IntervalList[0];
  1628. exponent = minExponent;
  1629. }
  1630. else
  1631. {
  1632. int i = 0;
  1633. for( ; i < IntervalListLength; i++)
  1634. {
  1635. // Skip 2.5 if the precision is reached.
  1636. if(exponent == minExponent && i == 2)
  1637. {
  1638. continue;
  1639. }
  1640. if(range <= IntervalList[i])
  1641. {
  1642. range = IntervalList[i];
  1643. found = true;
  1644. break;
  1645. }
  1646. }
  1647. }
  1648. if(!found)
  1649. {
  1650. range = IntervalList[0];
  1651. exponent++;
  1652. }
  1653. if(negative)
  1654. {
  1655. range *= -1;
  1656. }
  1657. // After finding a suitable interval, convert it back to
  1658. // a usable form.
  1659. rangeString.setNum(range, 'f', 1);
  1660. QString expString;
  1661. expString.setNum(exponent);
  1662. rangeString.append("e").append(expString);
  1663. range = rangeString.toDouble();
  1664. // Assign the pixel interval from the calculated value interval.
  1665. if(maximum.type() == QVariant::Int)
  1666. {
  1667. int interval = (int)range;
  1668. if(interval == 0)
  1669. {
  1670. interval = maximum.toInt() - minimum.toInt();
  1671. }
  1672. // Adjust the displayed min/max to align to the interval.
  1673. int value = alignAxisMinimum<int>(minimum.toInt(), interval, 0,
  1674. this->Internal->PadRange);
  1675. int rangeMaximum = alignAxisMaximum<int>(maximum.toInt(), interval, 0,
  1676. this->Internal->PadRange);
  1677. // Fill in the data based on the interval.
  1678. rangeMaximum += interval / 2; // Account for round-off error.
  1679. for( ; value < rangeMaximum; value += interval)
  1680. {
  1681. this->Model->addLabel(QVariant(value));
  1682. }
  1683. // Adding half the interval misses the last value when the
  1684. // interval is an integer of 1.
  1685. if(interval == 1)
  1686. {
  1687. this->Model->addLabel(QVariant(value));
  1688. }
  1689. }
  1690. else if(maximum.type() == QVariant::Double)
  1691. {
  1692. // Adjust the displayed min/max to align to the interval.
  1693. double interval = range;
  1694. double value = alignAxisMinimum<double>(minimum.toDouble(), interval,
  1695. 0.0, this->Internal->PadRange);
  1696. double rangeMaximum = alignAxisMaximum<double>(maximum.toDouble(),
  1697. interval, 0.0, this->Internal->PadRange);
  1698. // Fill in the data based on the interval.
  1699. rangeMaximum += interval / 2; // Account for round-off error.
  1700. if ( minimum.toDouble() != HUGE_VAL &&