SETestKCL  1.0.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
qcustomplot.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 ** **
3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
4 ** Copyright (C) 2011-2015 Emanuel Eichhammer **
5 ** **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
8 ** the Free Software Foundation, either version 3 of the License, or **
9 ** (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, **
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program. If not, see http://www.gnu.org/licenses/. **
18 ** **
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Date: 22.12.15 **
23 ** Version: 1.3.2 **
24 ****************************************************************************/
25 
26 #include "qcustomplot.h"
27 
28 
29 
33 
51  QPainter(),
52  mModes(pmDefault),
53  mIsAntialiasing(false)
54 {
55  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
56  // a call to begin() will follow
57 }
58 
65 QCPPainter::QCPPainter(QPaintDevice *device) :
66  QPainter(device),
67  mModes(pmDefault),
68  mIsAntialiasing(false)
69 {
70 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
71  if (isActive())
72  setRenderHint(QPainter::NonCosmeticDefaultPen);
73 #endif
74 }
75 
77 {
78 }
79 
86 void QCPPainter::setPen(const QPen &pen)
87 {
88  QPainter::setPen(pen);
89  if (mModes.testFlag(pmNonCosmetic))
91 }
92 
100 void QCPPainter::setPen(const QColor &color)
101 {
102  QPainter::setPen(color);
103  if (mModes.testFlag(pmNonCosmetic))
104  makeNonCosmetic();
105 }
106 
114 void QCPPainter::setPen(Qt::PenStyle penStyle)
115 {
116  QPainter::setPen(penStyle);
117  if (mModes.testFlag(pmNonCosmetic))
118  makeNonCosmetic();
119 }
120 
129 void QCPPainter::drawLine(const QLineF &line)
130 {
131  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
132  QPainter::drawLine(line);
133  else
134  QPainter::drawLine(line.toLine());
135 }
136 
143 void QCPPainter::setAntialiasing(bool enabled)
144 {
145  setRenderHint(QPainter::Antialiasing, enabled);
146  if (mIsAntialiasing != enabled)
147  {
148  mIsAntialiasing = enabled;
149  if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
150  {
151  if (mIsAntialiasing)
152  translate(0.5, 0.5);
153  else
154  translate(-0.5, -0.5);
155  }
156  }
157 }
158 
163 void QCPPainter::setModes(QCPPainter::PainterModes modes)
164 {
165  mModes = modes;
166 }
167 
179 bool QCPPainter::begin(QPaintDevice *device)
180 {
181  bool result = QPainter::begin(device);
182 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
183  if (result)
184  setRenderHint(QPainter::NonCosmeticDefaultPen);
185 #endif
186  return result;
187 }
188 
195 {
196  if (!enabled && mModes.testFlag(mode))
197  mModes &= ~mode;
198  else if (enabled && !mModes.testFlag(mode))
199  mModes |= mode;
200 }
201 
211 {
213  QPainter::save();
214 }
215 
225 {
226  if (!mAntialiasingStack.isEmpty())
228  else
229  qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
230  QPainter::restore();
231 }
232 
238 {
239  if (qFuzzyIsNull(pen().widthF()))
240  {
241  QPen p = pen();
242  p.setWidth(1);
243  QPainter::setPen(p);
244  }
245 }
246 
247 
251 
302 /* start documentation of inline functions */
303 
323 /* end documentation of inline functions */
324 
332  mSize(6),
333  mShape(ssNone),
334  mPen(Qt::NoPen),
335  mBrush(Qt::NoBrush),
336  mPenDefined(false)
337 {
338 }
339 
348  mSize(size),
349  mShape(shape),
350  mPen(Qt::NoPen),
351  mBrush(Qt::NoBrush),
352  mPenDefined(false)
353 {
354 }
355 
360 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) :
361  mSize(size),
362  mShape(shape),
363  mPen(QPen(color)),
364  mBrush(Qt::NoBrush),
365  mPenDefined(true)
366 {
367 }
368 
373 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
374  mSize(size),
375  mShape(shape),
376  mPen(QPen(color)),
377  mBrush(QBrush(fill)),
378  mPenDefined(true)
379 {
380 }
381 
397 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
398  mSize(size),
399  mShape(shape),
400  mPen(pen),
401  mBrush(brush),
402  mPenDefined(pen.style() != Qt::NoPen)
403 {
404 }
405 
410 QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) :
411  mSize(5),
412  mShape(ssPixmap),
413  mPen(Qt::NoPen),
414  mBrush(Qt::NoBrush),
415  mPixmap(pixmap),
416  mPenDefined(false)
417 {
418 }
419 
429 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
430  mSize(size),
431  mShape(ssCustom),
432  mPen(pen),
433  mBrush(brush),
434  mCustomPath(customPath),
435  mPenDefined(pen.style() != Qt::NoPen)
436 {
437 }
438 
444 void QCPScatterStyle::setSize(double size)
445 {
446  mSize = size;
447 }
448 
458 {
459  mShape = shape;
460 }
461 
470 void QCPScatterStyle::setPen(const QPen &pen)
471 {
472  mPenDefined = true;
473  mPen = pen;
474 }
475 
482 void QCPScatterStyle::setBrush(const QBrush &brush)
483 {
484  mBrush = brush;
485 }
486 
494 void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
495 {
497  mPixmap = pixmap;
498 }
499 
505 void QCPScatterStyle::setCustomPath(const QPainterPath &customPath)
506 {
509 }
510 
520 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
521 {
522  painter->setPen(mPenDefined ? mPen : defaultPen);
523  painter->setBrush(mBrush);
524 }
525 
534 void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
535 {
536  drawShape(painter, pos.x(), pos.y());
537 }
538 
542 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
543 {
544  double w = mSize/2.0;
545  switch (mShape)
546  {
547  case ssNone: break;
548  case ssDot:
549  {
550  painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
551  break;
552  }
553  case ssCross:
554  {
555  painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
556  painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
557  break;
558  }
559  case ssPlus:
560  {
561  painter->drawLine(QLineF(x-w, y, x+w, y));
562  painter->drawLine(QLineF( x, y+w, x, y-w));
563  break;
564  }
565  case ssCircle:
566  {
567  painter->drawEllipse(QPointF(x , y), w, w);
568  break;
569  }
570  case ssDisc:
571  {
572  QBrush b = painter->brush();
573  painter->setBrush(painter->pen().color());
574  painter->drawEllipse(QPointF(x , y), w, w);
575  painter->setBrush(b);
576  break;
577  }
578  case ssSquare:
579  {
580  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
581  break;
582  }
583  case ssDiamond:
584  {
585  painter->drawLine(QLineF(x-w, y, x, y-w));
586  painter->drawLine(QLineF( x, y-w, x+w, y));
587  painter->drawLine(QLineF(x+w, y, x, y+w));
588  painter->drawLine(QLineF( x, y+w, x-w, y));
589  break;
590  }
591  case ssStar:
592  {
593  painter->drawLine(QLineF(x-w, y, x+w, y));
594  painter->drawLine(QLineF( x, y+w, x, y-w));
595  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
596  painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
597  break;
598  }
599  case ssTriangle:
600  {
601  painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
602  painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
603  painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w));
604  break;
605  }
606  case ssTriangleInverted:
607  {
608  painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
609  painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
610  painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w));
611  break;
612  }
613  case ssCrossSquare:
614  {
615  painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
616  painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
617  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
618  break;
619  }
620  case ssPlusSquare:
621  {
622  painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
623  painter->drawLine(QLineF( x, y+w, x, y-w));
624  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
625  break;
626  }
627  case ssCrossCircle:
628  {
629  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
630  painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
631  painter->drawEllipse(QPointF(x, y), w, w);
632  break;
633  }
634  case ssPlusCircle:
635  {
636  painter->drawLine(QLineF(x-w, y, x+w, y));
637  painter->drawLine(QLineF( x, y+w, x, y-w));
638  painter->drawEllipse(QPointF(x, y), w, w);
639  break;
640  }
641  case ssPeace:
642  {
643  painter->drawLine(QLineF(x, y-w, x, y+w));
644  painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
645  painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
646  painter->drawEllipse(QPointF(x, y), w, w);
647  break;
648  }
649  case ssPixmap:
650  {
651  painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
652  break;
653  }
654  case ssCustom:
655  {
656  QTransform oldTransform = painter->transform();
657  painter->translate(x, y);
658  painter->scale(mSize/6.0, mSize/6.0);
659  painter->drawPath(mCustomPath);
660  painter->setTransform(oldTransform);
661  break;
662  }
663  }
664 }
665 
666 
670 
713 /* start documentation of inline functions */
714 
729 /* end documentation of inline functions */
730 
739 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
740  QObject(parentPlot),
741  mParentPlot(parentPlot),
742  mName(layerName),
743  mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
744  mVisible(true)
745 {
746  // Note: no need to make sure layerName is unique, because layer
747  // management is done with QCustomPlot functions.
748 }
749 
751 {
752  // If child layerables are still on this layer, detach them, so they don't try to reach back to this
753  // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
754  // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
755  // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
756 
757  while (!mChildren.isEmpty())
758  mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
759 
760  if (mParentPlot->currentLayer() == this)
761  qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand.";
762 }
763 
772 void QCPLayer::setVisible(bool visible)
773 {
774  mVisible = visible;
775 }
776 
787 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
788 {
789  if (!mChildren.contains(layerable))
790  {
791  if (prepend)
792  mChildren.prepend(layerable);
793  else
794  mChildren.append(layerable);
795  } else
796  qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
797 }
798 
809 {
810  if (!mChildren.removeOne(layerable))
811  qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
812 }
813 
814 
818 
831 /* start documentation of inline functions */
832 
846 /* end documentation of inline functions */
847 /* start documentation of pure virtual functions */
848 
889 /* end documentation of pure virtual functions */
890 /* start documentation of signals */
891 
900 /* end documentation of signals */
901 
922 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
923  QObject(plot),
924  mVisible(true),
925  mParentPlot(plot),
926  mParentLayerable(parentLayerable),
927  mLayer(0),
928  mAntialiased(true)
929 {
930  if (mParentPlot)
931  {
932  if (targetLayer.isEmpty())
934  else if (!setLayer(targetLayer))
935  qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
936  }
937 }
938 
940 {
941  if (mLayer)
942  {
943  mLayer->removeChild(this);
944  mLayer = 0;
945  }
946 }
947 
954 {
955  mVisible = on;
956 }
957 
968 {
969  return moveToLayer(layer, false);
970 }
971 
977 bool QCPLayerable::setLayer(const QString &layerName)
978 {
979  if (!mParentPlot)
980  {
981  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
982  return false;
983  }
984  if (QCPLayer *layer = mParentPlot->layer(layerName))
985  {
986  return setLayer(layer);
987  } else
988  {
989  qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
990  return false;
991  }
992 }
993 
1001 {
1002  mAntialiased = enabled;
1003 }
1004 
1019 {
1020  return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1021 }
1022 
1057 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1058 {
1059  Q_UNUSED(pos)
1060  Q_UNUSED(onlySelectable)
1061  Q_UNUSED(details)
1062  return -1.0;
1063 }
1064 
1083 {
1084  if (mParentPlot)
1085  {
1086  qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1087  return;
1088  }
1089 
1090  if (!parentPlot)
1091  qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1092 
1095 }
1096 
1109 {
1111 }
1112 
1121 bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
1122 {
1123  if (layer && !mParentPlot)
1124  {
1125  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1126  return false;
1127  }
1128  if (layer && layer->parentPlot() != mParentPlot)
1129  {
1130  qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1131  return false;
1132  }
1133 
1134  QCPLayer *oldLayer = mLayer;
1135  if (mLayer)
1136  mLayer->removeChild(this);
1137  mLayer = layer;
1138  if (mLayer)
1139  mLayer->addChild(this, prepend);
1140  if (mLayer != oldLayer)
1141  emit layerChanged(mLayer);
1142  return true;
1143 }
1144 
1152 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1153 {
1154  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1155  painter->setAntialiasing(false);
1156  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1157  painter->setAntialiasing(true);
1158  else
1159  painter->setAntialiasing(localAntialiased);
1160 }
1161 
1179 {
1180  Q_UNUSED(parentPlot)
1181 }
1182 
1195 {
1196  return QCP::iSelectOther;
1197 }
1198 
1209 {
1210  if (mParentPlot)
1211  return mParentPlot->viewport();
1212  else
1213  return QRect();
1214 }
1215 
1244 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1245 {
1246  Q_UNUSED(event)
1247  Q_UNUSED(additive)
1248  Q_UNUSED(details)
1249  Q_UNUSED(selectionStateChanged)
1250 }
1251 
1264 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1265 {
1266  Q_UNUSED(selectionStateChanged)
1267 }
1268 
1269 
1273 
1288 const double QCPRange::minRange = 1e-280;
1289 
1298 const double QCPRange::maxRange = 1e250;
1299 
1304  lower(0),
1305  upper(0)
1306 {
1307 }
1308 
1312 QCPRange::QCPRange(double lower, double upper) :
1313  lower(lower),
1314  upper(upper)
1315 {
1316  normalize();
1317 }
1318 
1322 double QCPRange::size() const
1323 {
1324  return upper-lower;
1325 }
1326 
1330 double QCPRange::center() const
1331 {
1332  return (upper+lower)*0.5;
1333 }
1334 
1340 {
1341  if (lower > upper)
1342  qSwap(lower, upper);
1343 }
1344 
1353 void QCPRange::expand(const QCPRange &otherRange)
1354 {
1355  if (lower > otherRange.lower)
1356  lower = otherRange.lower;
1357  if (upper < otherRange.upper)
1358  upper = otherRange.upper;
1359 }
1360 
1361 
1368 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1369 {
1370  QCPRange result = *this;
1371  result.expand(otherRange);
1372  return result;
1373 }
1374 
1388 {
1389  double rangeFac = 1e-3;
1390  QCPRange sanitizedRange(lower, upper);
1391  sanitizedRange.normalize();
1392  // can't have range spanning negative and positive values in log plot, so change range to fix it
1393  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
1394  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
1395  {
1396  // case lower is 0
1397  if (rangeFac < sanitizedRange.upper*rangeFac)
1398  sanitizedRange.lower = rangeFac;
1399  else
1400  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1401  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1402  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
1403  {
1404  // case upper is 0
1405  if (-rangeFac > sanitizedRange.lower*rangeFac)
1406  sanitizedRange.upper = -rangeFac;
1407  else
1408  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1409  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
1410  {
1411  // find out whether negative or positive interval is wider to decide which sign domain will be chosen
1412  if (-sanitizedRange.lower > sanitizedRange.upper)
1413  {
1414  // negative is wider, do same as in case upper is 0
1415  if (-rangeFac > sanitizedRange.lower*rangeFac)
1416  sanitizedRange.upper = -rangeFac;
1417  else
1418  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1419  } else
1420  {
1421  // positive is wider, do same as in case lower is 0
1422  if (rangeFac < sanitizedRange.upper*rangeFac)
1423  sanitizedRange.lower = rangeFac;
1424  else
1425  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1426  }
1427  }
1428  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
1429  return sanitizedRange;
1430 }
1431 
1437 {
1438  QCPRange sanitizedRange(lower, upper);
1439  sanitizedRange.normalize();
1440  return sanitizedRange;
1441 }
1442 
1446 bool QCPRange::contains(double value) const
1447 {
1448  return value >= lower && value <= upper;
1449 }
1450 
1459 bool QCPRange::validRange(double lower, double upper)
1460 {
1461  return (lower > -maxRange &&
1462  upper < maxRange &&
1463  qAbs(lower-upper) > minRange &&
1464  qAbs(lower-upper) < maxRange &&
1465  !(lower > 0 && qIsInf(upper/lower)) &&
1466  !(upper < 0 && qIsInf(lower/upper)));
1467 }
1468 
1478 bool QCPRange::validRange(const QCPRange &range)
1479 {
1480  return (range.lower > -maxRange &&
1481  range.upper < maxRange &&
1482  qAbs(range.lower-range.upper) > minRange &&
1483  qAbs(range.lower-range.upper) < maxRange &&
1484  !(range.lower > 0 && qIsInf(range.upper/range.lower)) &&
1485  !(range.upper < 0 && qIsInf(range.lower/range.upper)));
1486 }
1487 
1488 
1492 
1524 /* start documentation of inline functions */
1525 
1532 /* end documentation of inline functions */
1533 
1538  QObject(parentPlot),
1539  mParentPlot(parentPlot)
1540 {
1541  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
1542  mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
1543  mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
1544  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
1545 }
1546 
1548 {
1549  clear();
1550 }
1551 
1557 {
1558  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1559  while (it.hasNext())
1560  {
1561  it.next();
1562  if (!it.value().isEmpty())
1563  return false;
1564  }
1565  return true;
1566 }
1567 
1573 {
1574  // make all children remove themselves from this margin group:
1575  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1576  while (it.hasNext())
1577  {
1578  it.next();
1579  const QList<QCPLayoutElement*> elements = it.value();
1580  for (int i=elements.size()-1; i>=0; --i)
1581  elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
1582  }
1583 }
1584 
1596 {
1597  // query all automatic margins of the layout elements in this margin group side and find maximum:
1598  int result = 0;
1599  const QList<QCPLayoutElement*> elements = mChildren.value(side);
1600  for (int i=0; i<elements.size(); ++i)
1601  {
1602  if (!elements.at(i)->autoMargins().testFlag(side))
1603  continue;
1604  int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1605  if (m > result)
1606  result = m;
1607  }
1608  return result;
1609 }
1610 
1618 {
1619  if (!mChildren[side].contains(element))
1620  mChildren[side].append(element);
1621  else
1622  qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
1623 }
1624 
1632 {
1633  if (!mChildren[side].removeOne(element))
1634  qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
1635 }
1636 
1637 
1641 
1668 /* start documentation of inline functions */
1669 
1717 /* end documentation of inline functions */
1718 
1723  QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
1724  mParentLayout(0),
1725  mMinimumSize(),
1726  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1727  mRect(0, 0, 0, 0),
1728  mOuterRect(0, 0, 0, 0),
1729  mMargins(0, 0, 0, 0),
1730  mMinimumMargins(0, 0, 0, 0),
1731  mAutoMargins(QCP::msAll)
1732 {
1733 }
1734 
1736 {
1737  setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
1738  // unregister at layout:
1739  if (qobject_cast<QCPLayout*>(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor
1740  mParentLayout->take(this);
1741 }
1742 
1754 void QCPLayoutElement::setOuterRect(const QRect &rect)
1755 {
1756  if (mOuterRect != rect)
1757  {
1758  mOuterRect = rect;
1759  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1760  }
1761 }
1762 
1774 void QCPLayoutElement::setMargins(const QMargins &margins)
1775 {
1776  if (mMargins != margins)
1777  {
1778  mMargins = margins;
1779  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1780  }
1781 }
1782 
1792 void QCPLayoutElement::setMinimumMargins(const QMargins &margins)
1793 {
1794  if (mMinimumMargins != margins)
1795  {
1797  }
1798 }
1799 
1810 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
1811 {
1812  mAutoMargins = sides;
1813 }
1814 
1824 void QCPLayoutElement::setMinimumSize(const QSize &size)
1825 {
1826  if (mMinimumSize != size)
1827  {
1828  mMinimumSize = size;
1829  if (mParentLayout)
1831  }
1832 }
1833 
1838 void QCPLayoutElement::setMinimumSize(int width, int height)
1839 {
1840  setMinimumSize(QSize(width, height));
1841 }
1842 
1847 void QCPLayoutElement::setMaximumSize(const QSize &size)
1848 {
1849  if (mMaximumSize != size)
1850  {
1851  mMaximumSize = size;
1852  if (mParentLayout)
1854  }
1855 }
1856 
1861 void QCPLayoutElement::setMaximumSize(int width, int height)
1862 {
1863  setMaximumSize(QSize(width, height));
1864 }
1865 
1877 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
1878 {
1879  QVector<QCP::MarginSide> sideVector;
1880  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1881  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1882  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1883  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1884 
1885  for (int i=0; i<sideVector.size(); ++i)
1886  {
1887  QCP::MarginSide side = sideVector.at(i);
1888  if (marginGroup(side) != group)
1889  {
1890  QCPMarginGroup *oldGroup = marginGroup(side);
1891  if (oldGroup) // unregister at old group
1892  oldGroup->removeChild(side, this);
1893 
1894  if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
1895  {
1896  mMarginGroups.remove(side);
1897  } else // setting to a new group
1898  {
1899  mMarginGroups[side] = group;
1900  group->addChild(side, this);
1901  }
1902  }
1903  }
1904 }
1905 
1919 {
1920  if (phase == upMargins)
1921  {
1922  if (mAutoMargins != QCP::msNone)
1923  {
1924  // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
1925  QMargins newMargins = mMargins;
1926  QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
1927  foreach (QCP::MarginSide side, allMarginSides)
1928  {
1929  if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
1930  {
1931  if (mMarginGroups.contains(side))
1932  QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
1933  else
1934  QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
1935  // apply minimum margin restrictions:
1936  if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
1937  QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
1938  }
1939  }
1940  setMargins(newMargins);
1941  }
1942  }
1943 }
1944 
1953 {
1954  return mMinimumSize;
1955 }
1956 
1965 {
1966  return mMaximumSize;
1967 }
1968 
1976 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
1977 {
1978  Q_UNUSED(recursive)
1979  return QList<QCPLayoutElement*>();
1980 }
1981 
1993 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1994 {
1995  Q_UNUSED(details)
1996 
1997  if (onlySelectable)
1998  return -1;
1999 
2000  if (QRectF(mOuterRect).contains(pos))
2001  {
2002  if (mParentPlot)
2003  return mParentPlot->selectionTolerance()*0.99;
2004  else
2005  {
2006  qDebug() << Q_FUNC_INFO << "parent plot not defined";
2007  return -1;
2008  }
2009  } else
2010  return -1;
2011 }
2012 
2019 {
2020  foreach (QCPLayoutElement* el, elements(false))
2021  {
2022  if (!el->parentPlot())
2023  el->initializeParentPlot(parentPlot);
2024  }
2025 }
2026 
2037 {
2039 }
2040 
2044 
2068 /* start documentation of pure virtual functions */
2069 
2112 /* end documentation of pure virtual functions */
2113 
2119 {
2120 }
2121 
2131 {
2132  QCPLayoutElement::update(phase);
2133 
2134  // set child element rects according to layout:
2135  if (phase == upLayout)
2136  updateLayout();
2137 
2138  // propagate update call to child elements:
2139  const int elCount = elementCount();
2140  for (int i=0; i<elCount; ++i)
2141  {
2142  if (QCPLayoutElement *el = elementAt(i))
2143  el->update(phase);
2144  }
2145 }
2146 
2147 /* inherits documentation from base class */
2148 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2149 {
2150  const int c = elementCount();
2151  QList<QCPLayoutElement*> result;
2152 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2153  result.reserve(c);
2154 #endif
2155  for (int i=0; i<c; ++i)
2156  result.append(elementAt(i));
2157  if (recursive)
2158  {
2159  for (int i=0; i<c; ++i)
2160  {
2161  if (result.at(i))
2162  result << result.at(i)->elements(recursive);
2163  }
2164  }
2165  return result;
2166 }
2167 
2176 {
2177 }
2178 
2190 bool QCPLayout::removeAt(int index)
2191 {
2192  if (QCPLayoutElement *el = takeAt(index))
2193  {
2194  delete el;
2195  return true;
2196  } else
2197  return false;
2198 }
2199 
2212 {
2213  if (take(element))
2214  {
2215  delete element;
2216  return true;
2217  } else
2218  return false;
2219 }
2220 
2228 {
2229  for (int i=elementCount()-1; i>=0; --i)
2230  {
2231  if (elementAt(i))
2232  removeAt(i);
2233  }
2234  simplify();
2235 }
2236 
2246 {
2247  if (QWidget *w = qobject_cast<QWidget*>(parent()))
2248  w->updateGeometry();
2249  else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
2250  l->sizeConstraintsChanged();
2251 }
2252 
2266 {
2267 }
2268 
2269 
2283 {
2284  if (el)
2285  {
2286  el->mParentLayout = this;
2287  el->setParentLayerable(this);
2288  el->setParent(this);
2289  if (!el->parentPlot())
2291  } else
2292  qDebug() << Q_FUNC_INFO << "Null element passed";
2293 }
2294 
2306 {
2307  if (el)
2308  {
2309  el->mParentLayout = 0;
2310  el->setParentLayerable(0);
2311  el->setParent(mParentPlot);
2312  // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
2313  } else
2314  qDebug() << Q_FUNC_INFO << "Null element passed";
2315 }
2316 
2346 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
2347 {
2348  if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
2349  {
2350  qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
2351  return QVector<int>();
2352  }
2353  if (stretchFactors.isEmpty())
2354  return QVector<int>();
2355  int sectionCount = stretchFactors.size();
2356  QVector<double> sectionSizes(sectionCount);
2357  // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
2358  int minSizeSum = 0;
2359  for (int i=0; i<sectionCount; ++i)
2360  minSizeSum += minSizes.at(i);
2361  if (totalSize < minSizeSum)
2362  {
2363  // new stretch factors are minimum sizes and minimum sizes are set to zero:
2364  for (int i=0; i<sectionCount; ++i)
2365  {
2366  stretchFactors[i] = minSizes.at(i);
2367  minSizes[i] = 0;
2368  }
2369  }
2370 
2371  QList<int> minimumLockedSections;
2372  QList<int> unfinishedSections;
2373  for (int i=0; i<sectionCount; ++i)
2374  unfinishedSections.append(i);
2375  double freeSize = totalSize;
2376 
2377  int outerIterations = 0;
2378  while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2379  {
2380  ++outerIterations;
2381  int innerIterations = 0;
2382  while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2383  {
2384  ++innerIterations;
2385  // find section that hits its maximum next:
2386  int nextId = -1;
2387  double nextMax = 1e12;
2388  for (int i=0; i<unfinishedSections.size(); ++i)
2389  {
2390  int secId = unfinishedSections.at(i);
2391  double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
2392  if (hitsMaxAt < nextMax)
2393  {
2394  nextMax = hitsMaxAt;
2395  nextId = secId;
2396  }
2397  }
2398  // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section
2399  // actually hits its maximum, without exceeding the total size when we add up all sections)
2400  double stretchFactorSum = 0;
2401  for (int i=0; i<unfinishedSections.size(); ++i)
2402  stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2403  double nextMaxLimit = freeSize/stretchFactorSum;
2404  if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
2405  {
2406  for (int i=0; i<unfinishedSections.size(); ++i)
2407  {
2408  sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2409  freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
2410  }
2411  unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
2412  } else // next maximum isn't hit, just distribute rest of free space on remaining sections
2413  {
2414  for (int i=0; i<unfinishedSections.size(); ++i)
2415  sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2416  unfinishedSections.clear();
2417  }
2418  }
2419  if (innerIterations == sectionCount*2)
2420  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2421 
2422  // now check whether the resulting section sizes violate minimum restrictions:
2423  bool foundMinimumViolation = false;
2424  for (int i=0; i<sectionSizes.size(); ++i)
2425  {
2426  if (minimumLockedSections.contains(i))
2427  continue;
2428  if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
2429  {
2430  sectionSizes[i] = minSizes.at(i); // set it to minimum
2431  foundMinimumViolation = true; // make sure we repeat the whole optimization process
2432  minimumLockedSections.append(i);
2433  }
2434  }
2435  if (foundMinimumViolation)
2436  {
2437  freeSize = totalSize;
2438  for (int i=0; i<sectionCount; ++i)
2439  {
2440  if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
2441  unfinishedSections.append(i);
2442  else
2443  freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
2444  }
2445  // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
2446  for (int i=0; i<unfinishedSections.size(); ++i)
2447  sectionSizes[unfinishedSections.at(i)] = 0;
2448  }
2449  }
2450  if (outerIterations == sectionCount*2)
2451  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2452 
2453  QVector<int> result(sectionCount);
2454  for (int i=0; i<sectionCount; ++i)
2455  result[i] = qRound(sectionSizes.at(i));
2456  return result;
2457 }
2458 
2459 
2463 
2484  mColumnSpacing(5),
2485  mRowSpacing(5)
2486 {
2487 }
2488 
2490 {
2491  // clear all child layout elements. This is important because only the specific layouts know how
2492  // to handle removing elements (clear calls virtual removeAt method to do that).
2493  clear();
2494 }
2495 
2504 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
2505 {
2506  if (row >= 0 && row < mElements.size())
2507  {
2508  if (column >= 0 && column < mElements.first().size())
2509  {
2510  if (QCPLayoutElement *result = mElements.at(row).at(column))
2511  return result;
2512  else
2513  qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
2514  } else
2515  qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
2516  } else
2517  qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
2518  return 0;
2519 }
2520 
2527 {
2528  return mElements.size();
2529 }
2530 
2537 {
2538  if (mElements.size() > 0)
2539  return mElements.first().size();
2540  else
2541  return 0;
2542 }
2543 
2554 bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
2555 {
2556  if (element)
2557  {
2558  if (!hasElement(row, column))
2559  {
2560  if (element->layout()) // remove from old layout first
2561  element->layout()->take(element);
2562  expandTo(row+1, column+1);
2563  mElements[row][column] = element;
2564  adoptElement(element);
2565  return true;
2566  } else
2567  qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2568  } else
2569  qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2570  return false;
2571 }
2572 
2579 bool QCPLayoutGrid::hasElement(int row, int column)
2580 {
2581  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2582  return mElements.at(row).at(column);
2583  else
2584  return false;
2585 }
2586 
2598 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
2599 {
2600  if (column >= 0 && column < columnCount())
2601  {
2602  if (factor > 0)
2603  mColumnStretchFactors[column] = factor;
2604  else
2605  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2606  } else
2607  qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2608 }
2609 
2621 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
2622 {
2623  if (factors.size() == mColumnStretchFactors.size())
2624  {
2625  mColumnStretchFactors = factors;
2626  for (int i=0; i<mColumnStretchFactors.size(); ++i)
2627  {
2628  if (mColumnStretchFactors.at(i) <= 0)
2629  {
2630  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
2631  mColumnStretchFactors[i] = 1;
2632  }
2633  }
2634  } else
2635  qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
2636 }
2637 
2649 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
2650 {
2651  if (row >= 0 && row < rowCount())
2652  {
2653  if (factor > 0)
2654  mRowStretchFactors[row] = factor;
2655  else
2656  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2657  } else
2658  qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2659 }
2660 
2672 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
2673 {
2674  if (factors.size() == mRowStretchFactors.size())
2675  {
2676  mRowStretchFactors = factors;
2677  for (int i=0; i<mRowStretchFactors.size(); ++i)
2678  {
2679  if (mRowStretchFactors.at(i) <= 0)
2680  {
2681  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
2682  mRowStretchFactors[i] = 1;
2683  }
2684  }
2685  } else
2686  qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
2687 }
2688 
2695 {
2696  mColumnSpacing = pixels;
2697 }
2698 
2705 {
2706  mRowSpacing = pixels;
2707 }
2708 
2723 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
2724 {
2725  // add rows as necessary:
2726  while (rowCount() < newRowCount)
2727  {
2728  mElements.append(QList<QCPLayoutElement*>());
2729  mRowStretchFactors.append(1);
2730  }
2731  // go through rows and expand columns as necessary:
2732  int newColCount = qMax(columnCount(), newColumnCount);
2733  for (int i=0; i<rowCount(); ++i)
2734  {
2735  while (mElements.at(i).size() < newColCount)
2736  mElements[i].append(0);
2737  }
2738  while (mColumnStretchFactors.size() < newColCount)
2739  mColumnStretchFactors.append(1);
2740 }
2741 
2748 void QCPLayoutGrid::insertRow(int newIndex)
2749 {
2750  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2751  {
2752  expandTo(1, 1);
2753  return;
2754  }
2755 
2756  if (newIndex < 0)
2757  newIndex = 0;
2758  if (newIndex > rowCount())
2759  newIndex = rowCount();
2760 
2761  mRowStretchFactors.insert(newIndex, 1);
2762  QList<QCPLayoutElement*> newRow;
2763  for (int col=0; col<columnCount(); ++col)
2764  newRow.append((QCPLayoutElement*)0);
2765  mElements.insert(newIndex, newRow);
2766 }
2767 
2775 {
2776  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2777  {
2778  expandTo(1, 1);
2779  return;
2780  }
2781 
2782  if (newIndex < 0)
2783  newIndex = 0;
2784  if (newIndex > columnCount())
2785  newIndex = columnCount();
2786 
2787  mColumnStretchFactors.insert(newIndex, 1);
2788  for (int row=0; row<rowCount(); ++row)
2789  mElements[row].insert(newIndex, (QCPLayoutElement*)0);
2790 }
2791 
2792 /* inherits documentation from base class */
2794 {
2795  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2796  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2797  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2798 
2799  int totalRowSpacing = (rowCount()-1) * mRowSpacing;
2800  int totalColSpacing = (columnCount()-1) * mColumnSpacing;
2801  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
2802  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
2803 
2804  // go through cells and set rects accordingly:
2805  int yOffset = mRect.top();
2806  for (int row=0; row<rowCount(); ++row)
2807  {
2808  if (row > 0)
2809  yOffset += rowHeights.at(row-1)+mRowSpacing;
2810  int xOffset = mRect.left();
2811  for (int col=0; col<columnCount(); ++col)
2812  {
2813  if (col > 0)
2814  xOffset += colWidths.at(col-1)+mColumnSpacing;
2815  if (mElements.at(row).at(col))
2816  mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2817  }
2818  }
2819 }
2820 
2821 /* inherits documentation from base class */
2823 {
2824  return rowCount()*columnCount();
2825 }
2826 
2827 /* inherits documentation from base class */
2829 {
2830  if (index >= 0 && index < elementCount())
2831  return mElements.at(index / columnCount()).at(index % columnCount());
2832  else
2833  return 0;
2834 }
2835 
2836 /* inherits documentation from base class */
2838 {
2839  if (QCPLayoutElement *el = elementAt(index))
2840  {
2841  releaseElement(el);
2842  mElements[index / columnCount()][index % columnCount()] = 0;
2843  return el;
2844  } else
2845  {
2846  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2847  return 0;
2848  }
2849 }
2850 
2851 /* inherits documentation from base class */
2853 {
2854  if (element)
2855  {
2856  for (int i=0; i<elementCount(); ++i)
2857  {
2858  if (elementAt(i) == element)
2859  {
2860  takeAt(i);
2861  return true;
2862  }
2863  }
2864  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2865  } else
2866  qDebug() << Q_FUNC_INFO << "Can't take null element";
2867  return false;
2868 }
2869 
2870 /* inherits documentation from base class */
2871 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
2872 {
2873  QList<QCPLayoutElement*> result;
2874  int colC = columnCount();
2875  int rowC = rowCount();
2876 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2877  result.reserve(colC*rowC);
2878 #endif
2879  for (int row=0; row<rowC; ++row)
2880  {
2881  for (int col=0; col<colC; ++col)
2882  {
2883  result.append(mElements.at(row).at(col));
2884  }
2885  }
2886  if (recursive)
2887  {
2888  int c = result.size();
2889  for (int i=0; i<c; ++i)
2890  {
2891  if (result.at(i))
2892  result << result.at(i)->elements(recursive);
2893  }
2894  }
2895  return result;
2896 }
2897 
2902 {
2903  // remove rows with only empty cells:
2904  for (int row=rowCount()-1; row>=0; --row)
2905  {
2906  bool hasElements = false;
2907  for (int col=0; col<columnCount(); ++col)
2908  {
2909  if (mElements.at(row).at(col))
2910  {
2911  hasElements = true;
2912  break;
2913  }
2914  }
2915  if (!hasElements)
2916  {
2917  mRowStretchFactors.removeAt(row);
2918  mElements.removeAt(row);
2919  if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
2920  mColumnStretchFactors.clear();
2921  }
2922  }
2923 
2924  // remove columns with only empty cells:
2925  for (int col=columnCount()-1; col>=0; --col)
2926  {
2927  bool hasElements = false;
2928  for (int row=0; row<rowCount(); ++row)
2929  {
2930  if (mElements.at(row).at(col))
2931  {
2932  hasElements = true;
2933  break;
2934  }
2935  }
2936  if (!hasElements)
2937  {
2938  mColumnStretchFactors.removeAt(col);
2939  for (int row=0; row<rowCount(); ++row)
2940  mElements[row].removeAt(col);
2941  }
2942  }
2943 }
2944 
2945 /* inherits documentation from base class */
2947 {
2948  QVector<int> minColWidths, minRowHeights;
2949  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2950  QSize result(0, 0);
2951  for (int i=0; i<minColWidths.size(); ++i)
2952  result.rwidth() += minColWidths.at(i);
2953  for (int i=0; i<minRowHeights.size(); ++i)
2954  result.rheight() += minRowHeights.at(i);
2955  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2956  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2957  return result;
2958 }
2959 
2960 /* inherits documentation from base class */
2962 {
2963  QVector<int> maxColWidths, maxRowHeights;
2964  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2965 
2966  QSize result(0, 0);
2967  for (int i=0; i<maxColWidths.size(); ++i)
2968  result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
2969  for (int i=0; i<maxRowHeights.size(); ++i)
2970  result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
2971  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2972  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2973  return result;
2974 }
2975 
2988 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
2989 {
2990  *minColWidths = QVector<int>(columnCount(), 0);
2991  *minRowHeights = QVector<int>(rowCount(), 0);
2992  for (int row=0; row<rowCount(); ++row)
2993  {
2994  for (int col=0; col<columnCount(); ++col)
2995  {
2996  if (mElements.at(row).at(col))
2997  {
2998  QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
2999  QSize min = mElements.at(row).at(col)->minimumSize();
3000  QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
3001  if (minColWidths->at(col) < final.width())
3002  (*minColWidths)[col] = final.width();
3003  if (minRowHeights->at(row) < final.height())
3004  (*minRowHeights)[row] = final.height();
3005  }
3006  }
3007  }
3008 }
3009 
3022 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
3023 {
3024  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3025  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3026  for (int row=0; row<rowCount(); ++row)
3027  {
3028  for (int col=0; col<columnCount(); ++col)
3029  {
3030  if (mElements.at(row).at(col))
3031  {
3032  QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3033  QSize max = mElements.at(row).at(col)->maximumSize();
3034  QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
3035  if (maxColWidths->at(col) > final.width())
3036  (*maxColWidths)[col] = final.width();
3037  if (maxRowHeights->at(row) > final.height())
3038  (*maxRowHeights)[row] = final.height();
3039  }
3040  }
3041  }
3042 }
3043 
3044 
3048 
3066 /* start documentation of inline functions */
3067 
3074 /* end documentation of inline functions */
3075 
3080 {
3081 }
3082 
3084 {
3085  // clear all child layout elements. This is important because only the specific layouts know how
3086  // to handle removing elements (clear calls virtual removeAt method to do that).
3087  clear();
3088 }
3089 
3094 {
3095  if (elementAt(index))
3096  return mInsetPlacement.at(index);
3097  else
3098  {
3099  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3100  return ipFree;
3101  }
3102 }
3103 
3108 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
3109 {
3110  if (elementAt(index))
3111  return mInsetAlignment.at(index);
3112  else
3113  {
3114  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3115  return 0;
3116  }
3117 }
3118 
3123 QRectF QCPLayoutInset::insetRect(int index) const
3124 {
3125  if (elementAt(index))
3126  return mInsetRect.at(index);
3127  else
3128  {
3129  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3130  return QRectF();
3131  }
3132 }
3133 
3140 {
3141  if (elementAt(index))
3142  mInsetPlacement[index] = placement;
3143  else
3144  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3145 }
3146 
3155 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
3156 {
3157  if (elementAt(index))
3158  mInsetAlignment[index] = alignment;
3159  else
3160  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3161 }
3162 
3174 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
3175 {
3176  if (elementAt(index))
3177  mInsetRect[index] = rect;
3178  else
3179  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3180 }
3181 
3182 /* inherits documentation from base class */
3184 {
3185  for (int i=0; i<mElements.size(); ++i)
3186  {
3187  QRect insetRect;
3188  QSize finalMinSize, finalMaxSize;
3189  QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3190  QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3191  finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
3192  finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
3193  finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
3194  finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
3195  if (mInsetPlacement.at(i) == ipFree)
3196  {
3197  insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
3198  rect().y()+rect().height()*mInsetRect.at(i).y(),
3199  rect().width()*mInsetRect.at(i).width(),
3200  rect().height()*mInsetRect.at(i).height());
3201  if (insetRect.size().width() < finalMinSize.width())
3202  insetRect.setWidth(finalMinSize.width());
3203  if (insetRect.size().height() < finalMinSize.height())
3204  insetRect.setHeight(finalMinSize.height());
3205  if (insetRect.size().width() > finalMaxSize.width())
3206  insetRect.setWidth(finalMaxSize.width());
3207  if (insetRect.size().height() > finalMaxSize.height())
3208  insetRect.setHeight(finalMaxSize.height());
3209  } else if (mInsetPlacement.at(i) == ipBorderAligned)
3210  {
3211  insetRect.setSize(finalMinSize);
3212  Qt::Alignment al = mInsetAlignment.at(i);
3213  if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
3214  else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
3215  else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
3216  if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
3217  else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
3218  else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
3219  }
3220  mElements.at(i)->setOuterRect(insetRect);
3221  }
3222 }
3223 
3224 /* inherits documentation from base class */
3226 {
3227  return mElements.size();
3228 }
3229 
3230 /* inherits documentation from base class */
3232 {
3233  if (index >= 0 && index < mElements.size())
3234  return mElements.at(index);
3235  else
3236  return 0;
3237 }
3238 
3239 /* inherits documentation from base class */
3241 {
3242  if (QCPLayoutElement *el = elementAt(index))
3243  {
3244  releaseElement(el);
3245  mElements.removeAt(index);
3246  mInsetPlacement.removeAt(index);
3247  mInsetAlignment.removeAt(index);
3248  mInsetRect.removeAt(index);
3249  return el;
3250  } else
3251  {
3252  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3253  return 0;
3254  }
3255 }
3256 
3257 /* inherits documentation from base class */
3259 {
3260  if (element)
3261  {
3262  for (int i=0; i<elementCount(); ++i)
3263  {
3264  if (elementAt(i) == element)
3265  {
3266  takeAt(i);
3267  return true;
3268  }
3269  }
3270  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3271  } else
3272  qDebug() << Q_FUNC_INFO << "Can't take null element";
3273  return false;
3274 }
3275 
3285 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3286 {
3287  Q_UNUSED(details)
3288  if (onlySelectable)
3289  return -1;
3290 
3291  for (int i=0; i<mElements.size(); ++i)
3292  {
3293  // inset layout shall only return positive selectTest, if actually an inset object is at pos
3294  // else it would block the entire underlying QCPAxisRect with its surface.
3295  if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3296  return mParentPlot->selectionTolerance()*0.99;
3297  }
3298  return -1;
3299 }
3300 
3312 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3313 {
3314  if (element)
3315  {
3316  if (element->layout()) // remove from old layout first
3317  element->layout()->take(element);
3318  mElements.append(element);
3320  mInsetAlignment.append(alignment);
3321  mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3322  adoptElement(element);
3323  } else
3324  qDebug() << Q_FUNC_INFO << "Can't add null element";
3325 }
3326 
3338 void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
3339 {
3340  if (element)
3341  {
3342  if (element->layout()) // remove from old layout first
3343  element->layout()->take(element);
3344  mElements.append(element);
3345  mInsetPlacement.append(ipFree);
3346  mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
3347  mInsetRect.append(rect);
3348  adoptElement(element);
3349  } else
3350  qDebug() << Q_FUNC_INFO << "Can't add null element";
3351 }
3352 
3353 
3357 
3382  mStyle(esNone),
3383  mWidth(8),
3384  mLength(10),
3385  mInverted(false)
3386 {
3387 }
3388 
3392 QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
3393  mStyle(style),
3394  mWidth(width),
3395  mLength(length),
3396  mInverted(inverted)
3397 {
3398 }
3399 
3404 {
3405  mStyle = style;
3406 }
3407 
3414 void QCPLineEnding::setWidth(double width)
3415 {
3416  mWidth = width;
3417 }
3418 
3425 void QCPLineEnding::setLength(double length)
3426 {
3427  mLength = length;
3428 }
3429 
3438 void QCPLineEnding::setInverted(bool inverted)
3439 {
3440  mInverted = inverted;
3441 }
3442 
3453 {
3454  switch (mStyle)
3455  {
3456  case esNone:
3457  return 0;
3458 
3459  case esFlatArrow:
3460  case esSpikeArrow:
3461  case esLineArrow:
3462  case esSkewedBar:
3463  return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
3464 
3465  case esDisc:
3466  case esSquare:
3467  case esDiamond:
3468  case esBar:
3469  case esHalfBar:
3470  return mWidth*1.42; // items that only have a width -> width*sqrt(2)
3471 
3472  }
3473  return 0;
3474 }
3475 
3488 {
3489  switch (mStyle)
3490  {
3491  case esNone:
3492  case esLineArrow:
3493  case esSkewedBar:
3494  case esBar:
3495  case esHalfBar:
3496  return 0;
3497 
3498  case esFlatArrow:
3499  return mLength;
3500 
3501  case esDisc:
3502  case esSquare:
3503  case esDiamond:
3504  return mWidth*0.5;
3505 
3506  case esSpikeArrow:
3507  return mLength*0.8;
3508  }
3509  return 0;
3510 }
3511 
3517 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
3518 {
3519  if (mStyle == esNone)
3520  return;
3521 
3522  QVector2D lengthVec(dir.normalized());
3523  if (lengthVec.isNull())
3524  lengthVec = QVector2D(1, 0);
3525  QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3526  lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3527  widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3528 
3529  QPen penBackup = painter->pen();
3530  QBrush brushBackup = painter->brush();
3531  QPen miterPen = penBackup;
3532  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3533  QBrush brush(painter->pen().color(), Qt::SolidPattern);
3534  switch (mStyle)
3535  {
3536  case esNone: break;
3537  case esFlatArrow:
3538  {
3539  QPointF points[3] = {pos.toPointF(),
3540  (pos-lengthVec+widthVec).toPointF(),
3541  (pos-lengthVec-widthVec).toPointF()
3542  };
3543  painter->setPen(miterPen);
3544  painter->setBrush(brush);
3545  painter->drawConvexPolygon(points, 3);
3546  painter->setBrush(brushBackup);
3547  painter->setPen(penBackup);
3548  break;
3549  }
3550  case esSpikeArrow:
3551  {
3552  QPointF points[4] = {pos.toPointF(),
3553  (pos-lengthVec+widthVec).toPointF(),
3554  (pos-lengthVec*0.8f).toPointF(),
3555  (pos-lengthVec-widthVec).toPointF()
3556  };
3557  painter->setPen(miterPen);
3558  painter->setBrush(brush);
3559  painter->drawConvexPolygon(points, 4);
3560  painter->setBrush(brushBackup);
3561  painter->setPen(penBackup);
3562  break;
3563  }
3564  case esLineArrow:
3565  {
3566  QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
3567  pos.toPointF(),
3568  (pos-lengthVec-widthVec).toPointF()
3569  };
3570  painter->setPen(miterPen);
3571  painter->drawPolyline(points, 3);
3572  painter->setPen(penBackup);
3573  break;
3574  }
3575  case esDisc:
3576  {
3577  painter->setBrush(brush);
3578  painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
3579  painter->setBrush(brushBackup);
3580  break;
3581  }
3582  case esSquare:
3583  {
3584  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3585  QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
3586  (pos-widthVecPerp-widthVec).toPointF(),
3587  (pos+widthVecPerp-widthVec).toPointF(),
3588  (pos+widthVecPerp+widthVec).toPointF()
3589  };
3590  painter->setPen(miterPen);
3591  painter->setBrush(brush);
3592  painter->drawConvexPolygon(points, 4);
3593  painter->setBrush(brushBackup);
3594  painter->setPen(penBackup);
3595  break;
3596  }
3597  case esDiamond:
3598  {
3599  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3600  QPointF points[4] = {(pos-widthVecPerp).toPointF(),
3601  (pos-widthVec).toPointF(),
3602  (pos+widthVecPerp).toPointF(),
3603  (pos+widthVec).toPointF()
3604  };
3605  painter->setPen(miterPen);
3606  painter->setBrush(brush);
3607  painter->drawConvexPolygon(points, 4);
3608  painter->setBrush(brushBackup);
3609  painter->setPen(penBackup);
3610  break;
3611  }
3612  case esBar:
3613  {
3614  painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
3615  break;
3616  }
3617  case esHalfBar:
3618  {
3619  painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
3620  break;
3621  }
3622  case esSkewedBar:
3623  {
3624  if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3625  {
3626  // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3627  painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3628  (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3629  } else
3630  {
3631  // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3632  painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3633  (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
3634  }
3635  break;
3636  }
3637  }
3638 }
3639 
3645 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
3646 {
3647  draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3648 }
3649 
3650 
3654 
3674  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3675  mParentAxis(parentAxis)
3676 {
3677  // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
3678  setParent(parentAxis);
3679  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
3680  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
3681  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
3682  setSubGridVisible(false);
3683  setAntialiased(false);
3684  setAntialiasedSubGrid(false);
3685  setAntialiasedZeroLine(false);
3686 }
3687 
3693 void QCPGrid::setSubGridVisible(bool visible)
3694 {
3696 }
3697 
3702 {
3703  mAntialiasedSubGrid = enabled;
3704 }
3705 
3710 {
3711  mAntialiasedZeroLine = enabled;
3712 }
3713 
3717 void QCPGrid::setPen(const QPen &pen)
3718 {
3719  mPen = pen;
3720 }
3721 
3725 void QCPGrid::setSubGridPen(const QPen &pen)
3726 {
3727  mSubGridPen = pen;
3728 }
3729 
3736 void QCPGrid::setZeroLinePen(const QPen &pen)
3737 {
3738  mZeroLinePen = pen;
3739 }
3740 
3755 {
3757 }
3758 
3765 {
3766  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3767 
3768  if (mSubGridVisible)
3769  drawSubGridLines(painter);
3770  drawGridLines(painter);
3771 }
3772 
3780 {
3781  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3782 
3783  int lowTick = mParentAxis->mLowestVisibleTick;
3784  int highTick = mParentAxis->mHighestVisibleTick;
3785  double t; // helper variable, result of coordinate-to-pixel transforms
3786  if (mParentAxis->orientation() == Qt::Horizontal)
3787  {
3788  // draw zeroline:
3789  int zeroLineIndex = -1;
3790  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3791  {
3793  painter->setPen(mZeroLinePen);
3794  double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
3795  for (int i=lowTick; i <= highTick; ++i)
3796  {
3797  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3798  {
3799  zeroLineIndex = i;
3800  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3801  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3802  break;
3803  }
3804  }
3805  }
3806  // draw grid lines:
3808  painter->setPen(mPen);
3809  for (int i=lowTick; i <= highTick; ++i)
3810  {
3811  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3812  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3813  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3814  }
3815  } else
3816  {
3817  // draw zeroline:
3818  int zeroLineIndex = -1;
3819  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3820  {
3822  painter->setPen(mZeroLinePen);
3823  double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
3824  for (int i=lowTick; i <= highTick; ++i)
3825  {
3826  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3827  {
3828  zeroLineIndex = i;
3829  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3830  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3831  break;
3832  }
3833  }
3834  }
3835  // draw grid lines:
3837  painter->setPen(mPen);
3838  for (int i=lowTick; i <= highTick; ++i)
3839  {
3840  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3841  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3842  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3843  }
3844  }
3845 }
3846 
3854 {
3855  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3856 
3858  double t; // helper variable, result of coordinate-to-pixel transforms
3859  painter->setPen(mSubGridPen);
3860  if (mParentAxis->orientation() == Qt::Horizontal)
3861  {
3862  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3863  {
3865  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3866  }
3867  } else
3868  {
3869  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3870  {
3872  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3873  }
3874  }
3875 }
3876 
3877 
3881 
3900 /* start of documentation of inline functions */
3901 
3923 /* end of documentation of inline functions */
3924 /* start of documentation of signals */
3925 
3980 /* end of documentation of signals */
3981 
3990  QCPLayerable(parent->parentPlot(), QString(), parent),
3991  // axis base:
3992  mAxisType(type),
3993  mAxisRect(parent),
3994  mPadding(5),
3995  mOrientation(orientation(type)),
3996  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
3997  mSelectedParts(spNone),
3998  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
3999  mSelectedBasePen(QPen(Qt::blue, 2)),
4000  // axis label:
4001  mLabel(),
4002  mLabelFont(mParentPlot->font()),
4003  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4004  mLabelColor(Qt::black),
4005  mSelectedLabelColor(Qt::blue),
4006  // tick labels:
4007  mTickLabels(true),
4008  mAutoTickLabels(true),
4009  mTickLabelType(ltNumber),
4010  mTickLabelFont(mParentPlot->font()),
4011  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4012  mTickLabelColor(Qt::black),
4013  mSelectedTickLabelColor(Qt::blue),
4014  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
4015  mDateTimeSpec(Qt::LocalTime),
4016  mNumberPrecision(6),
4017  mNumberFormatChar('g'),
4018  mNumberBeautifulPowers(true),
4019  // ticks and subticks:
4020  mTicks(true),
4021  mTickStep(1),
4022  mSubTickCount(4),
4023  mAutoTickCount(6),
4024  mAutoTicks(true),
4025  mAutoTickStep(true),
4026  mAutoSubTicks(true),
4027  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4028  mSelectedTickPen(QPen(Qt::blue, 2)),
4029  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4030  mSelectedSubTickPen(QPen(Qt::blue, 2)),
4031  // scale and range:
4032  mRange(0, 5),
4033  mRangeReversed(false),
4034  mScaleType(stLinear),
4035  mScaleLogBase(10),
4036  mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4037  // internal members:
4038  mGrid(new QCPGrid(this)),
4039  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4040  mLowestVisibleTick(0),
4041  mHighestVisibleTick(-1),
4042  mCachedMarginValid(false),
4043  mCachedMargin(0)
4044 {
4045  setParent(parent);
4046  mGrid->setVisible(false);
4047  setAntialiased(false);
4048  setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again
4049 
4050  if (type == atTop)
4051  {
4053  setLabelPadding(6);
4054  } else if (type == atRight)
4055  {
4057  setLabelPadding(12);
4058  } else if (type == atBottom)
4059  {
4061  setLabelPadding(3);
4062  } else if (type == atLeft)
4063  {
4065  setLabelPadding(10);
4066  }
4067 }
4068 
4070 {
4071  delete mAxisPainter;
4072  delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
4073 }
4074 
4075 /* No documentation as it is a property getter */
4077 {
4079 }
4080 
4081 /* No documentation as it is a property getter */
4083 {
4085 }
4086 
4087 /* No documentation as it is a property getter */
4089 {
4090  return mAxisPainter->tickLabelSide;
4091 }
4092 
4093 /* No documentation as it is a property getter */
4094 QString QCPAxis::numberFormat() const
4095 {
4096  QString result;
4097  result.append(mNumberFormatChar);
4099  {
4100  result.append(QLatin1Char('b'));
4102  result.append(QLatin1Char('c'));
4103  }
4104  return result;
4105 }
4106 
4107 /* No documentation as it is a property getter */
4109 {
4110  return mAxisPainter->tickLengthIn;
4111 }
4112 
4113 /* No documentation as it is a property getter */
4115 {
4116  return mAxisPainter->tickLengthOut;
4117 }
4118 
4119 /* No documentation as it is a property getter */
4121 {
4122  return mAxisPainter->subTickLengthIn;
4123 }
4124 
4125 /* No documentation as it is a property getter */
4127 {
4129 }
4130 
4131 /* No documentation as it is a property getter */
4133 {
4134  return mAxisPainter->labelPadding;
4135 }
4136 
4137 /* No documentation as it is a property getter */
4138 int QCPAxis::offset() const
4139 {
4140  return mAxisPainter->offset;
4141 }
4142 
4143 /* No documentation as it is a property getter */
4145 {
4146  return mAxisPainter->lowerEnding;
4147 }
4148 
4149 /* No documentation as it is a property getter */
4151 {
4152  return mAxisPainter->upperEnding;
4153 }
4154 
4169 {
4170  if (mScaleType != type)
4171  {
4172  mScaleType = type;
4173  if (mScaleType == stLogarithmic)
4175  mCachedMarginValid = false;
4177  }
4178 }
4179 
4187 void QCPAxis::setScaleLogBase(double base)
4188 {
4189  if (base > 1)
4190  {
4191  mScaleLogBase = base;
4192  mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4193  mCachedMarginValid = false;
4194  } else
4195  qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
4196 }
4197 
4206 void QCPAxis::setRange(const QCPRange &range)
4207 {
4208  if (range.lower == mRange.lower && range.upper == mRange.upper)
4209  return;
4210 
4211  if (!QCPRange::validRange(range)) return;
4212  QCPRange oldRange = mRange;
4213  if (mScaleType == stLogarithmic)
4214  {
4215  mRange = range.sanitizedForLogScale();
4216  } else
4217  {
4218  mRange = range.sanitizedForLinScale();
4219  }
4220  mCachedMarginValid = false;
4221  emit rangeChanged(mRange);
4222  emit rangeChanged(mRange, oldRange);
4223 }
4224 
4235 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4236 {
4237  if (mSelectableParts != selectable)
4238  {
4239  mSelectableParts = selectable;
4241  }
4242 }
4243 
4259 void QCPAxis::setSelectedParts(const SelectableParts &selected)
4260 {
4261  if (mSelectedParts != selected)
4262  {
4263  mSelectedParts = selected;
4265  }
4266 }
4267 
4277 void QCPAxis::setRange(double lower, double upper)
4278 {
4279  if (lower == mRange.lower && upper == mRange.upper)
4280  return;
4281 
4282  if (!QCPRange::validRange(lower, upper)) return;
4283  QCPRange oldRange = mRange;
4284  mRange.lower = lower;
4285  mRange.upper = upper;
4286  if (mScaleType == stLogarithmic)
4287  {
4289  } else
4290  {
4292  }
4293  mCachedMarginValid = false;
4294  emit rangeChanged(mRange);
4295  emit rangeChanged(mRange, oldRange);
4296 }
4297 
4309 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
4310 {
4311  if (alignment == Qt::AlignLeft)
4312  setRange(position, position+size);
4313  else if (alignment == Qt::AlignRight)
4314  setRange(position-size, position);
4315  else // alignment == Qt::AlignCenter
4316  setRange(position-size/2.0, position+size/2.0);
4317 }
4318 
4323 void QCPAxis::setRangeLower(double lower)
4324 {
4325  if (mRange.lower == lower)
4326  return;
4327 
4328  QCPRange oldRange = mRange;
4329  mRange.lower = lower;
4330  if (mScaleType == stLogarithmic)
4331  {
4333  } else
4334  {
4336  }
4337  mCachedMarginValid = false;
4338  emit rangeChanged(mRange);
4339  emit rangeChanged(mRange, oldRange);
4340 }
4341 
4346 void QCPAxis::setRangeUpper(double upper)
4347 {
4348  if (mRange.upper == upper)
4349  return;
4350 
4351  QCPRange oldRange = mRange;
4352  mRange.upper = upper;
4353  if (mScaleType == stLogarithmic)
4354  {
4356  } else
4357  {
4359  }
4360  mCachedMarginValid = false;
4361  emit rangeChanged(mRange);
4362  emit rangeChanged(mRange, oldRange);
4363 }
4364 
4374 void QCPAxis::setRangeReversed(bool reversed)
4375 {
4376  if (mRangeReversed != reversed)
4377  {
4378  mRangeReversed = reversed;
4379  mCachedMarginValid = false;
4380  }
4381 }
4382 
4399 {
4400  if (mAutoTicks != on)
4401  {
4402  mAutoTicks = on;
4403  mCachedMarginValid = false;
4404  }
4405 }
4406 
4418 void QCPAxis::setAutoTickCount(int approximateCount)
4419 {
4420  if (mAutoTickCount != approximateCount)
4421  {
4422  if (approximateCount > 0)
4423  {
4424  mAutoTickCount = approximateCount;
4425  mCachedMarginValid = false;
4426  } else
4427  qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
4428  }
4429 }
4430 
4447 {
4448  if (mAutoTickLabels != on)
4449  {
4450  mAutoTickLabels = on;
4451  mCachedMarginValid = false;
4452  }
4453 }
4454 
4468 {
4469  if (mAutoTickStep != on)
4470  {
4471  mAutoTickStep = on;
4472  mCachedMarginValid = false;
4473  }
4474 }
4475 
4486 {
4487  if (mAutoSubTicks != on)
4488  {
4489  mAutoSubTicks = on;
4490  mCachedMarginValid = false;
4491  }
4492 }
4493 
4500 void QCPAxis::setTicks(bool show)
4501 {
4502  if (mTicks != show)
4503  {
4504  mTicks = show;
4505  mCachedMarginValid = false;
4506  }
4507 }
4508 
4512 void QCPAxis::setTickLabels(bool show)
4513 {
4514  if (mTickLabels != show)
4515  {
4516  mTickLabels = show;
4517  mCachedMarginValid = false;
4518  }
4519 }
4520 
4526 {
4527  if (mAxisPainter->tickLabelPadding != padding)
4528  {
4530  mCachedMarginValid = false;
4531  }
4532 }
4533 
4556 {
4557  if (mTickLabelType != type)
4558  {
4559  mTickLabelType = type;
4560  mCachedMarginValid = false;
4561  }
4562 }
4563 
4569 void QCPAxis::setTickLabelFont(const QFont &font)
4570 {
4571  if (font != mTickLabelFont)
4572  {
4573  mTickLabelFont = font;
4574  mCachedMarginValid = false;
4575  }
4576 }
4577 
4583 void QCPAxis::setTickLabelColor(const QColor &color)
4584 {
4585  if (color != mTickLabelColor)
4586  {
4587  mTickLabelColor = color;
4588  mCachedMarginValid = false;
4589  }
4590 }
4591 
4601 void QCPAxis::setTickLabelRotation(double degrees)
4602 {
4603  if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4604  {
4605  mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4606  mCachedMarginValid = false;
4607  }
4608 }
4609 
4618 {
4619  mAxisPainter->tickLabelSide = side;
4620  mCachedMarginValid = false;
4621 }
4622 
4631 void QCPAxis::setDateTimeFormat(const QString &format)
4632 {
4633  if (mDateTimeFormat != format)
4634  {
4635  mDateTimeFormat = format;
4636  mCachedMarginValid = false;
4637  }
4638 }
4639 
4650 void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
4651 {
4652  mDateTimeSpec = timeSpec;
4653 }
4654 
4691 void QCPAxis::setNumberFormat(const QString &formatCode)
4692 {
4693  if (formatCode.isEmpty())
4694  {
4695  qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4696  return;
4697  }
4698  mCachedMarginValid = false;
4699 
4700  // interpret first char as number format char:
4701  QString allowedFormatChars(QLatin1String("eEfgG"));
4702  if (allowedFormatChars.contains(formatCode.at(0)))
4703  {
4704  mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
4705  } else
4706  {
4707  qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4708  return;
4709  }
4710  if (formatCode.length() < 2)
4711  {
4712  mNumberBeautifulPowers = false;
4714  return;
4715  }
4716 
4717  // interpret second char as indicator for beautiful decimal powers:
4718  if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
4719  {
4720  mNumberBeautifulPowers = true;
4721  } else
4722  {
4723  qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4724  return;
4725  }
4726  if (formatCode.length() < 3)
4727  {
4729  return;
4730  }
4731 
4732  // interpret third char as indicator for dot or cross multiplication symbol:
4733  if (formatCode.at(2) == QLatin1Char('c'))
4734  {
4736  } else if (formatCode.at(2) == QLatin1Char('d'))
4737  {
4739  } else
4740  {
4741  qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4742  return;
4743  }
4744 }
4745 
4757 void QCPAxis::setNumberPrecision(int precision)
4758 {
4759  if (mNumberPrecision != precision)
4760  {
4761  mNumberPrecision = precision;
4762  mCachedMarginValid = false;
4763  }
4764 }
4765 
4771 void QCPAxis::setTickStep(double step)
4772 {
4773  if (mTickStep != step)
4774  {
4775  mTickStep = step;
4776  mCachedMarginValid = false;
4777  }
4778 }
4779 
4793 void QCPAxis::setTickVector(const QVector<double> &vec)
4794 {
4795  // don't check whether mTickVector != vec here, because it takes longer than we would save
4796  mTickVector = vec;
4797  mCachedMarginValid = false;
4798 }
4799 
4811 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4812 {
4813  // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
4814  mTickVectorLabels = vec;
4815  mCachedMarginValid = false;
4816 }
4817 
4826 void QCPAxis::setTickLength(int inside, int outside)
4827 {
4828  setTickLengthIn(inside);
4829  setTickLengthOut(outside);
4830 }
4831 
4839 {
4840  if (mAxisPainter->tickLengthIn != inside)
4841  {
4842  mAxisPainter->tickLengthIn = inside;
4843  }
4844 }
4845 
4853 void QCPAxis::setTickLengthOut(int outside)
4854 {
4855  if (mAxisPainter->tickLengthOut != outside)
4856  {
4857  mAxisPainter->tickLengthOut = outside;
4858  mCachedMarginValid = false; // only outside tick length can change margin
4859  }
4860 }
4861 
4874 {
4875  mSubTickCount = count;
4876 }
4877 
4886 void QCPAxis::setSubTickLength(int inside, int outside)
4887 {
4888  setSubTickLengthIn(inside);
4889  setSubTickLengthOut(outside);
4890 }
4891 
4899 {
4900  if (mAxisPainter->subTickLengthIn != inside)
4901  {
4902  mAxisPainter->subTickLengthIn = inside;
4903  }
4904 }
4905 
4914 {
4915  if (mAxisPainter->subTickLengthOut != outside)
4916  {
4917  mAxisPainter->subTickLengthOut = outside;
4918  mCachedMarginValid = false; // only outside tick length can change margin
4919  }
4920 }
4921 
4927 void QCPAxis::setBasePen(const QPen &pen)
4928 {
4929  mBasePen = pen;
4930 }
4931 
4937 void QCPAxis::setTickPen(const QPen &pen)
4938 {
4939  mTickPen = pen;
4940 }
4941 
4947 void QCPAxis::setSubTickPen(const QPen &pen)
4948 {
4949  mSubTickPen = pen;
4950 }
4951 
4957 void QCPAxis::setLabelFont(const QFont &font)
4958 {
4959  if (mLabelFont != font)
4960  {
4961  mLabelFont = font;
4962  mCachedMarginValid = false;
4963  }
4964 }
4965 
4971 void QCPAxis::setLabelColor(const QColor &color)
4972 {
4973  mLabelColor = color;
4974 }
4975 
4980 void QCPAxis::setLabel(const QString &str)
4981 {
4982  if (mLabel != str)
4983  {
4984  mLabel = str;
4985  mCachedMarginValid = false;
4986  }
4987 }
4988 
4994 void QCPAxis::setLabelPadding(int padding)
4995 {
4996  if (mAxisPainter->labelPadding != padding)
4997  {
4999  mCachedMarginValid = false;
5000  }
5001 }
5002 
5013 void QCPAxis::setPadding(int padding)
5014 {
5015  if (mPadding != padding)
5016  {
5017  mPadding = padding;
5018  mCachedMarginValid = false;
5019  }
5020 }
5021 
5030 void QCPAxis::setOffset(int offset)
5031 {
5033 }
5034 
5040 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
5041 {
5042  if (font != mSelectedTickLabelFont)
5043  {
5044  mSelectedTickLabelFont = font;
5045  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5046  }
5047 }
5048 
5054 void QCPAxis::setSelectedLabelFont(const QFont &font)
5055 {
5056  mSelectedLabelFont = font;
5057  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5058 }
5059 
5065 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
5066 {
5067  if (color != mSelectedTickLabelColor)
5068  {
5069  mSelectedTickLabelColor = color;
5070  }
5071 }
5072 
5078 void QCPAxis::setSelectedLabelColor(const QColor &color)
5079 {
5080  mSelectedLabelColor = color;
5081 }
5082 
5088 void QCPAxis::setSelectedBasePen(const QPen &pen)
5089 {
5090  mSelectedBasePen = pen;
5091 }
5092 
5098 void QCPAxis::setSelectedTickPen(const QPen &pen)
5099 {
5100  mSelectedTickPen = pen;
5101 }
5102 
5108 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
5109 {
5110  mSelectedSubTickPen = pen;
5111 }
5112 
5124 {
5125  mAxisPainter->lowerEnding = ending;
5126 }
5127 
5139 {
5140  mAxisPainter->upperEnding = ending;
5141 }
5142 
5150 void QCPAxis::moveRange(double diff)
5151 {
5152  QCPRange oldRange = mRange;
5153  if (mScaleType == stLinear)
5154  {
5155  mRange.lower += diff;
5156  mRange.upper += diff;
5157  } else // mScaleType == stLogarithmic
5158  {
5159  mRange.lower *= diff;
5160  mRange.upper *= diff;
5161  }
5162  mCachedMarginValid = false;
5163  emit rangeChanged(mRange);
5164  emit rangeChanged(mRange, oldRange);
5165 }
5166 
5173 void QCPAxis::scaleRange(double factor, double center)
5174 {
5175  QCPRange oldRange = mRange;
5176  if (mScaleType == stLinear)
5177  {
5178  QCPRange newRange;
5179  newRange.lower = (mRange.lower-center)*factor + center;
5180  newRange.upper = (mRange.upper-center)*factor + center;
5181  if (QCPRange::validRange(newRange))
5182  mRange = newRange.sanitizedForLinScale();
5183  } else // mScaleType == stLogarithmic
5184  {
5185  if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
5186  {
5187  QCPRange newRange;
5188  newRange.lower = qPow(mRange.lower/center, factor)*center;
5189  newRange.upper = qPow(mRange.upper/center, factor)*center;
5190  if (QCPRange::validRange(newRange))
5191  mRange = newRange.sanitizedForLogScale();
5192  } else
5193  qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
5194  }
5195  mCachedMarginValid = false;
5196  emit rangeChanged(mRange);
5197  emit rangeChanged(mRange, oldRange);
5198 }
5199 
5213 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
5214 {
5215  int otherPixelSize, ownPixelSize;
5216 
5217  if (otherAxis->orientation() == Qt::Horizontal)
5218  otherPixelSize = otherAxis->axisRect()->width();
5219  else
5220  otherPixelSize = otherAxis->axisRect()->height();
5221 
5222  if (orientation() == Qt::Horizontal)
5223  ownPixelSize = axisRect()->width();
5224  else
5225  ownPixelSize = axisRect()->height();
5226 
5227  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
5228  setRange(range().center(), newRangeSize, Qt::AlignCenter);
5229 }
5230 
5237 void QCPAxis::rescale(bool onlyVisiblePlottables)
5238 {
5239  QList<QCPAbstractPlottable*> p = plottables();
5240  QCPRange newRange;
5241  bool haveRange = false;
5242  for (int i=0; i<p.size(); ++i)
5243  {
5244  if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5245  continue;
5246  QCPRange plottableRange;
5247  bool currentFoundRange;
5249  if (mScaleType == stLogarithmic)
5251  if (p.at(i)->keyAxis() == this)
5252  plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5253  else
5254  plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5255  if (currentFoundRange)
5256  {
5257  if (!haveRange)
5258  newRange = plottableRange;
5259  else
5260  newRange.expand(plottableRange);
5261  haveRange = true;
5262  }
5263  }
5264  if (haveRange)
5265  {
5266  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
5267  {
5268  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
5269  if (mScaleType == stLinear)
5270  {
5271  newRange.lower = center-mRange.size()/2.0;
5272  newRange.upper = center+mRange.size()/2.0;
5273  } else // mScaleType == stLogarithmic
5274  {
5275  newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
5276  newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
5277  }
5278  }
5279  setRange(newRange);
5280  }
5281 }
5282 
5286 double QCPAxis::pixelToCoord(double value) const
5287 {
5288  if (orientation() == Qt::Horizontal)
5289  {
5290  if (mScaleType == stLinear)
5291  {
5292  if (!mRangeReversed)
5293  return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
5294  else
5295  return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
5296  } else // mScaleType == stLogarithmic
5297  {
5298  if (!mRangeReversed)
5299  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
5300  else
5301  return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
5302  }
5303  } else // orientation() == Qt::Vertical
5304  {
5305  if (mScaleType == stLinear)
5306  {
5307  if (!mRangeReversed)
5308  return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
5309  else
5310  return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
5311  } else // mScaleType == stLogarithmic
5312  {
5313  if (!mRangeReversed)
5314  return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
5315  else
5316  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
5317  }
5318  }
5319 }
5320 
5324 double QCPAxis::coordToPixel(double value) const
5325 {
5326  if (orientation() == Qt::Horizontal)
5327  {
5328  if (mScaleType == stLinear)
5329  {
5330  if (!mRangeReversed)
5331  return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5332  else
5333  return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5334  } else // mScaleType == stLogarithmic
5335  {
5336  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5337  return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
5338  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5339  return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
5340  else
5341  {
5342  if (!mRangeReversed)
5344  else
5346  }
5347  }
5348  } else // orientation() == Qt::Vertical
5349  {
5350  if (mScaleType == stLinear)
5351  {
5352  if (!mRangeReversed)
5353  return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
5354  else
5355  return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5356  } else // mScaleType == stLogarithmic
5357  {
5358  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5359  return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5360  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5361  return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
5362  else
5363  {
5364  if (!mRangeReversed)
5366  else
5368  }
5369  }
5370  }
5371 }
5372 
5383 {
5384  if (!mVisible)
5385  return spNone;
5386 
5387  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5388  return spAxis;
5389  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5390  return spTickLabels;
5391  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5392  return spAxisLabel;
5393  else
5394  return spNone;
5395 }
5396 
5397 /* inherits documentation from base class */
5398 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5399 {
5400  if (!mParentPlot) return -1;
5401  SelectablePart part = getPartAt(pos);
5402  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5403  return -1;
5404 
5405  if (details)
5406  details->setValue(part);
5407  return mParentPlot->selectionTolerance()*0.99;
5408 }
5409 
5417 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
5418 {
5419  QList<QCPAbstractPlottable*> result;
5420  if (!mParentPlot) return result;
5421 
5422  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
5423  {
5424  if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
5425  result.append(mParentPlot->mPlottables.at(i));
5426  }
5427  return result;
5428 }
5429 
5435 QList<QCPGraph*> QCPAxis::graphs() const
5436 {
5437  QList<QCPGraph*> result;
5438  if (!mParentPlot) return result;
5439 
5440  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
5441  {
5442  if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
5443  result.append(mParentPlot->mGraphs.at(i));
5444  }
5445  return result;
5446 }
5447 
5454 QList<QCPAbstractItem*> QCPAxis::items() const
5455 {
5456  QList<QCPAbstractItem*> result;
5457  if (!mParentPlot) return result;
5458 
5459  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
5460  {
5461  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
5462  for (int posId=0; posId<positions.size(); ++posId)
5463  {
5464  if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
5465  {
5466  result.append(mParentPlot->mItems.at(itemId));
5467  break;
5468  }
5469  }
5470  }
5471  return result;
5472 }
5473 
5479 {
5480  switch (side)
5481  {
5482  case QCP::msLeft: return atLeft;
5483  case QCP::msRight: return atRight;
5484  case QCP::msTop: return atTop;
5485  case QCP::msBottom: return atBottom;
5486  default: break;
5487  }
5488  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5489  return atLeft;
5490 }
5491 
5496 {
5497  switch (type)
5498  {
5499  case atLeft: return atRight; break;
5500  case atRight: return atLeft; break;
5501  case atBottom: return atTop; break;
5502  case atTop: return atBottom; break;
5503  default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
5504  }
5505 }
5506 
5515 {
5516  if (!mParentPlot) return;
5517  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
5518 
5519  // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
5520  if (mAutoTicks)
5521  {
5523  } else
5524  {
5525  emit ticksRequest();
5526  }
5527 
5529  if (mTickVector.isEmpty())
5530  {
5531  mSubTickVector.clear();
5532  return;
5533  }
5534 
5535  // generate subticks between ticks:
5536  mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
5537  if (mSubTickCount > 0)
5538  {
5539  double subTickStep = 0;
5540  double subTickPosition = 0;
5541  int subTickIndex = 0;
5542  bool done = false;
5545  for (int i=lowTick+1; i<=highTick; ++i)
5546  {
5547  subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
5548  for (int k=1; k<=mSubTickCount; ++k)
5549  {
5550  subTickPosition = mTickVector.at(i-1) + k*subTickStep;
5551  if (subTickPosition < mRange.lower)
5552  continue;
5553  if (subTickPosition > mRange.upper)
5554  {
5555  done = true;
5556  break;
5557  }
5558  mSubTickVector[subTickIndex] = subTickPosition;
5559  subTickIndex++;
5560  }
5561  if (done) break;
5562  }
5563  mSubTickVector.resize(subTickIndex);
5564  }
5565 
5566  // generate tick labels according to tick positions:
5567  if (mAutoTickLabels)
5568  {
5569  int vecsize = mTickVector.size();
5570  mTickVectorLabels.resize(vecsize);
5571  if (mTickLabelType == ltNumber)
5572  {
5573  for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5574  mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision);
5575  } else if (mTickLabelType == ltDateTime)
5576  {
5577  for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5578  {
5579 #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) // use fromMSecsSinceEpoch function if available, to gain sub-second accuracy on tick labels (e.g. for format "hh:mm:ss:zzz")
5580  mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5581 #else
5582  mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5583 #endif
5584  }
5585  }
5586  } else // mAutoTickLabels == false
5587  {
5588  if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
5589  {
5590  emit ticksRequest();
5591  }
5592  // make sure provided tick label vector has correct (minimal) length:
5593  if (mTickVectorLabels.size() < mTickVector.size())
5594  mTickVectorLabels.resize(mTickVector.size());
5595  }
5596 }
5597 
5608 {
5609  if (mScaleType == stLinear)
5610  {
5611  if (mAutoTickStep)
5612  {
5613  // Generate tick positions according to linear scaling:
5614  mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
5615  double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5616  double tickStepMantissa = mTickStep/magnitudeFactor;
5617  if (tickStepMantissa < 5)
5618  {
5619  // round digit after decimal point to 0.5
5620  mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
5621  } else
5622  {
5623  // round to first digit in multiples of 2
5624  mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
5625  }
5626  }
5627  if (mAutoSubTicks)
5629  // Generate tick positions according to mTickStep:
5630  qint64 firstStep = floor(mRange.lower/mTickStep); // do not use qFloor here, or we'll lose 64 bit precision
5631  qint64 lastStep = ceil(mRange.upper/mTickStep); // do not use qCeil here, or we'll lose 64 bit precision
5632  int tickcount = lastStep-firstStep+1;
5633  if (tickcount < 0) tickcount = 0;
5634  mTickVector.resize(tickcount);
5635  for (int i=0; i<tickcount; ++i)
5636  mTickVector[i] = (firstStep+i)*mTickStep;
5637  } else // mScaleType == stLogarithmic
5638  {
5639  // Generate tick positions according to logbase scaling:
5640  if (mRange.lower > 0 && mRange.upper > 0) // positive range
5641  {
5642  double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
5643  double currentMag = lowerMag;
5644  mTickVector.clear();
5645  mTickVector.append(currentMag);
5646  while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5647  {
5648  currentMag *= mScaleLogBase;
5649  mTickVector.append(currentMag);
5650  }
5651  } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5652  {
5653  double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
5654  double currentMag = lowerMag;
5655  mTickVector.clear();
5656  mTickVector.append(currentMag);
5657  while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5658  {
5659  currentMag /= mScaleLogBase;
5660  mTickVector.append(currentMag);
5661  }
5662  } else // invalid range for logarithmic scale, because lower and upper have different sign
5663  {
5664  mTickVector.clear();
5665  qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
5666  }
5667  }
5668 }
5669 
5683 int QCPAxis::calculateAutoSubTickCount(double tickStep) const
5684 {
5685  int result = mSubTickCount; // default to current setting, if no proper value can be found
5686 
5687  // get mantissa of tickstep:
5688  double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5689  double tickStepMantissa = tickStep/magnitudeFactor;
5690 
5691  // separate integer and fractional part of mantissa:
5692  double epsilon = 0.01;
5693  double intPartf;
5694  int intPart;
5695  double fracPart = modf(tickStepMantissa, &intPartf);
5696  intPart = intPartf;
5697 
5698  // handle cases with (almost) integer mantissa:
5699  if (fracPart < epsilon || 1.0-fracPart < epsilon)
5700  {
5701  if (1.0-fracPart < epsilon)
5702  ++intPart;
5703  switch (intPart)
5704  {
5705  case 1: result = 4; break; // 1.0 -> 0.2 substep
5706  case 2: result = 3; break; // 2.0 -> 0.5 substep
5707  case 3: result = 2; break; // 3.0 -> 1.0 substep
5708  case 4: result = 3; break; // 4.0 -> 1.0 substep
5709  case 5: result = 4; break; // 5.0 -> 1.0 substep
5710  case 6: result = 2; break; // 6.0 -> 2.0 substep
5711  case 7: result = 6; break; // 7.0 -> 1.0 substep
5712  case 8: result = 3; break; // 8.0 -> 2.0 substep
5713  case 9: result = 2; break; // 9.0 -> 3.0 substep
5714  }
5715  } else
5716  {
5717  // handle cases with significantly fractional mantissa:
5718  if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5719  {
5720  switch (intPart)
5721  {
5722  case 1: result = 2; break; // 1.5 -> 0.5 substep
5723  case 2: result = 4; break; // 2.5 -> 0.5 substep
5724  case 3: result = 4; break; // 3.5 -> 0.7 substep
5725  case 4: result = 2; break; // 4.5 -> 1.5 substep
5726  case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
5727  case 6: result = 4; break; // 6.5 -> 1.3 substep
5728  case 7: result = 2; break; // 7.5 -> 2.5 substep
5729  case 8: result = 4; break; // 8.5 -> 1.7 substep
5730  case 9: result = 4; break; // 9.5 -> 1.9 substep
5731  }
5732  }
5733  // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5734  }
5735 
5736  return result;
5737 }
5738 
5739 /* inherits documentation from base class */
5740 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
5741 {
5742  Q_UNUSED(event)
5743  SelectablePart part = details.value<SelectablePart>();
5744  if (mSelectableParts.testFlag(part))
5745  {
5746  SelectableParts selBefore = mSelectedParts;
5747  setSelectedParts(additive ? mSelectedParts^part : part);
5748  if (selectionStateChanged)
5749  *selectionStateChanged = mSelectedParts != selBefore;
5750  }
5751 }
5752 
5753 /* inherits documentation from base class */
5754 void QCPAxis::deselectEvent(bool *selectionStateChanged)
5755 {
5756  SelectableParts selBefore = mSelectedParts;
5758  if (selectionStateChanged)
5759  *selectionStateChanged = mSelectedParts != selBefore;
5760 }
5761 
5776 {
5778 }
5779 
5786 {
5787  const int lowTick = mLowestVisibleTick;
5788  const int highTick = mHighestVisibleTick;
5789  QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5790  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5791  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5792  tickPositions.reserve(highTick-lowTick+1);
5793  tickLabels.reserve(highTick-lowTick+1);
5794  subTickPositions.reserve(mSubTickVector.size());
5795 
5796  if (mTicks)
5797  {
5798  for (int i=lowTick; i<=highTick; ++i)
5799  {
5800  tickPositions.append(coordToPixel(mTickVector.at(i)));
5801  if (mTickLabels)
5802  tickLabels.append(mTickVectorLabels.at(i));
5803  }
5804 
5805  if (mSubTickCount > 0)
5806  {
5807  const int subTickCount = mSubTickVector.size();
5808  for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
5809  subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5810  }
5811  }
5812  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
5813  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
5828  mAxisPainter->tickPositions = tickPositions;
5830  mAxisPainter->subTickPositions = subTickPositions;
5831  mAxisPainter->draw(painter);
5832 }
5833 
5850 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5851 {
5852  bool lowFound = false;
5853  bool highFound = false;
5854  lowIndex = 0;
5855  highIndex = -1;
5856 
5857  for (int i=0; i < mTickVector.size(); ++i)
5858  {
5859  if (mTickVector.at(i) >= mRange.lower)
5860  {
5861  lowFound = true;
5862  lowIndex = i;
5863  break;
5864  }
5865  }
5866  for (int i=mTickVector.size()-1; i >= 0; --i)
5867  {
5868  if (mTickVector.at(i) <= mRange.upper)
5869  {
5870  highFound = true;
5871  highIndex = i;
5872  break;
5873  }
5874  }
5875 
5876  if (!lowFound && highFound)
5877  lowIndex = highIndex+1;
5878  else if (lowFound && !highFound)
5879  highIndex = lowIndex-1;
5880 }
5881 
5890 double QCPAxis::baseLog(double value) const
5891 {
5892  return qLn(value)*mScaleLogBaseLogInv;
5893 }
5894 
5902 double QCPAxis::basePow(double value) const
5903 {
5904  return qPow(mScaleLogBase, value);
5905 }
5906 
5913 {
5914  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5915 }
5916 
5923 {
5924  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5925 }
5926 
5933 {
5935 }
5936 
5943 {
5945 }
5946 
5953 {
5955 }
5956 
5963 {
5965 }
5966 
5973 {
5975 }
5976 
5992 {
5993  if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
5994  return 0;
5995 
5996  if (mCachedMarginValid)
5997  return mCachedMargin;
5998 
5999  // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6000  int margin = 0;
6001 
6002  int lowTick, highTick;
6003  visibleTickBounds(lowTick, highTick);
6004  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
6005  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6006  tickPositions.reserve(highTick-lowTick+1);
6007  tickLabels.reserve(highTick-lowTick+1);
6008  if (mTicks)
6009  {
6010  for (int i=lowTick; i<=highTick; ++i)
6011  {
6012  tickPositions.append(coordToPixel(mTickVector.at(i)));
6013  if (mTickLabels)
6014  tickLabels.append(mTickVectorLabels.at(i));
6015  }
6016  }
6017  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6018  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6025  mAxisPainter->tickPositions = tickPositions;
6027  margin += mAxisPainter->size();
6028  margin += mPadding;
6029 
6030  mCachedMargin = margin;
6031  mCachedMarginValid = true;
6032  return margin;
6033 }
6034 
6035 /* inherits documentation from base class */
6037 {
6038  return QCP::iSelectAxes;
6039 }
6040 
6041 
6045 
6063  type(QCPAxis::atLeft),
6064  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6065  lowerEnding(QCPLineEnding::esNone),
6066  upperEnding(QCPLineEnding::esNone),
6067  labelPadding(0),
6068  tickLabelPadding(0),
6069  tickLabelRotation(0),
6070  tickLabelSide(QCPAxis::lsOutside),
6071  substituteExponent(true),
6072  numberMultiplyCross(false),
6073  tickLengthIn(5),
6074  tickLengthOut(0),
6075  subTickLengthIn(2),
6076  subTickLengthOut(0),
6077  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6078  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6079  offset(0),
6080  abbreviateDecimalPowers(false),
6081  reversedEndings(false),
6082  mParentPlot(parentPlot),
6083  mLabelCache(16) // cache at most 16 (tick) labels
6084 {
6085 }
6086 
6088 {
6089 }
6090 
6099 {
6100  QByteArray newHash = generateLabelParameterHash();
6101  if (newHash != mLabelParameterHash)
6102  {
6103  mLabelCache.clear();
6104  mLabelParameterHash = newHash;
6105  }
6106 
6107  QPoint origin;
6108  switch (type)
6109  {
6110  case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
6111  case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
6112  case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break;
6113  case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
6114  }
6115 
6116  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
6117  switch (type)
6118  {
6119  case QCPAxis::atTop: yCor = -1; break;
6120  case QCPAxis::atRight: xCor = 1; break;
6121  default: break;
6122  }
6123  int margin = 0;
6124  // draw baseline:
6125  QLineF baseLine;
6126  painter->setPen(basePen);
6127  if (QCPAxis::orientation(type) == Qt::Horizontal)
6128  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
6129  else
6130  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
6131  if (reversedEndings)
6132  baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
6133  painter->drawLine(baseLine);
6134 
6135  // draw ticks:
6136  if (!tickPositions.isEmpty())
6137  {
6138  painter->setPen(tickPen);
6139  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
6140  if (QCPAxis::orientation(type) == Qt::Horizontal)
6141  {
6142  for (int i=0; i<tickPositions.size(); ++i)
6143  painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
6144  } else
6145  {
6146  for (int i=0; i<tickPositions.size(); ++i)
6147  painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
6148  }
6149  }
6150 
6151  // draw subticks:
6152  if (!subTickPositions.isEmpty())
6153  {
6154  painter->setPen(subTickPen);
6155  // direction of ticks ("inward" is right for left axis and left for right axis)
6156  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6157  if (QCPAxis::orientation(type) == Qt::Horizontal)
6158  {
6159  for (int i=0; i<subTickPositions.size(); ++i)
6160  painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
6161  } else
6162  {
6163  for (int i=0; i<subTickPositions.size(); ++i)
6164  painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
6165  }
6166  }
6167  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6168 
6169  // draw axis base endings:
6170  bool antialiasingBackup = painter->antialiasing();
6171  painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
6172  painter->setBrush(QBrush(basePen.color()));
6173  QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6175  lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6177  upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
6178  painter->setAntialiasing(antialiasingBackup);
6179 
6180  // tick labels:
6181  QRect oldClipRect;
6182  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
6183  {
6184  oldClipRect = painter->clipRegion().boundingRect();
6185  painter->setClipRect(axisRect);
6186  }
6187  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
6188  if (!tickLabels.isEmpty())
6189  {
6191  margin += tickLabelPadding;
6192  painter->setFont(tickLabelFont);
6193  painter->setPen(QPen(tickLabelColor));
6194  const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6195  int distanceToAxis = margin;
6197  distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6198  for (int i=0; i<maxLabelIndex; ++i)
6199  placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
6201  margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
6202  }
6204  painter->setClipRect(oldClipRect);
6205 
6206  // axis label:
6207  QRect labelBounds;
6208  if (!label.isEmpty())
6209  {
6210  margin += labelPadding;
6211  painter->setFont(labelFont);
6212  painter->setPen(QPen(labelColor));
6213  labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
6214  if (type == QCPAxis::atLeft)
6215  {
6216  QTransform oldTransform = painter->transform();
6217  painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
6218  painter->rotate(-90);
6219  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6220  painter->setTransform(oldTransform);
6221  }
6222  else if (type == QCPAxis::atRight)
6223  {
6224  QTransform oldTransform = painter->transform();
6225  painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
6226  painter->rotate(90);
6227  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6228  painter->setTransform(oldTransform);
6229  }
6230  else if (type == QCPAxis::atTop)
6231  painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6232  else if (type == QCPAxis::atBottom)
6233  painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6234  }
6235 
6236  // set selection boxes:
6237  int selectionTolerance = 0;
6238  if (mParentPlot)
6239  selectionTolerance = mParentPlot->selectionTolerance();
6240  else
6241  qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6242  int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6243  int selAxisInSize = selectionTolerance;
6244  int selTickLabelSize;
6245  int selTickLabelOffset;
6247  {
6248  selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6249  selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
6250  } else
6251  {
6252  selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6253  selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6254  }
6255  int selLabelSize = labelBounds.height();
6256  int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
6257  if (type == QCPAxis::atLeft)
6258  {
6259  mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
6260  mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
6261  mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
6262  } else if (type == QCPAxis::atRight)
6263  {
6264  mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
6265  mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
6266  mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
6267  } else if (type == QCPAxis::atTop)
6268  {
6269  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
6270  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
6271  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
6272  } else if (type == QCPAxis::atBottom)
6273  {
6274  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
6275  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
6276  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
6277  }
6278  mAxisSelectionBox = mAxisSelectionBox.normalized();
6280  mLabelSelectionBox = mLabelSelectionBox.normalized();
6281  // draw hitboxes for debug purposes:
6282  //painter->setBrush(Qt::NoBrush);
6283  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
6284 }
6285 
6292 {
6293  int result = 0;
6294 
6295  // get length of tick marks pointing outwards:
6296  if (!tickPositions.isEmpty())
6297  result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6298 
6299  // calculate size of tick labels:
6301  {
6302  QSize tickLabelsSize(0, 0);
6303  if (!tickLabels.isEmpty())
6304  {
6305  for (int i=0; i<tickLabels.size(); ++i)
6306  getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6307  result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6308  result += tickLabelPadding;
6309  }
6310  }
6311 
6312  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6313  if (!label.isEmpty())
6314  {
6315  QFontMetrics fontMetrics(labelFont);
6316  QRect bounds;
6317  bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6318  result += bounds.height() + labelPadding;
6319  }
6320 
6321  return result;
6322 }
6323 
6331 {
6332  mLabelCache.clear();
6333 }
6334 
6343 {
6344  QByteArray result;
6345  result.append(QByteArray::number(tickLabelRotation));
6346  result.append(QByteArray::number((int)tickLabelSide));
6347  result.append(QByteArray::number((int)substituteExponent));
6348  result.append(QByteArray::number((int)numberMultiplyCross));
6349  result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
6350  result.append(tickLabelFont.toString().toLatin1());
6351  return result;
6352 }
6353 
6373 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
6374 {
6375  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
6376  if (text.isEmpty()) return;
6377  QSize finalSize;
6378  QPointF labelAnchor;
6379  switch (type)
6380  {
6381  case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
6382  case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
6383  case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
6384  case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
6385  }
6386  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
6387  {
6388  CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache
6389  if (!cachedLabel) // no cached label existed, create it
6390  {
6391  cachedLabel = new CachedLabel;
6392  TickLabelData labelData = getTickLabelData(painter->font(), text);
6393  cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
6394  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6395  cachedLabel->pixmap.fill(Qt::transparent);
6396  QCPPainter cachePainter(&cachedLabel->pixmap);
6397  cachePainter.setPen(painter->pen());
6398  drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6399  }
6400  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6401  bool labelClippedByBorder = false;
6403  {
6404  if (QCPAxis::orientation(type) == Qt::Horizontal)
6405  labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
6406  else
6407  labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
6408  }
6409  if (!labelClippedByBorder)
6410  {
6411  painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
6412  finalSize = cachedLabel->pixmap.size();
6413  }
6414  mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created
6415  } else // label caching disabled, draw text directly on surface:
6416  {
6417  TickLabelData labelData = getTickLabelData(painter->font(), text);
6418  QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6419  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6420  bool labelClippedByBorder = false;
6422  {
6423  if (QCPAxis::orientation(type) == Qt::Horizontal)
6424  labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
6425  else
6426  labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
6427  }
6428  if (!labelClippedByBorder)
6429  {
6430  drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6431  finalSize = labelData.rotatedTotalBounds.size();
6432  }
6433  }
6434 
6435  // expand passed tickLabelsSize if current tick label is larger:
6436  if (finalSize.width() > tickLabelsSize->width())
6437  tickLabelsSize->setWidth(finalSize.width());
6438  if (finalSize.height() > tickLabelsSize->height())
6439  tickLabelsSize->setHeight(finalSize.height());
6440 }
6441 
6451 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
6452 {
6453  // backup painter settings that we're about to change:
6454  QTransform oldTransform = painter->transform();
6455  QFont oldFont = painter->font();
6456 
6457  // transform painter to position/rotation:
6458  painter->translate(x, y);
6459  if (!qFuzzyIsNull(tickLabelRotation))
6460  painter->rotate(tickLabelRotation);
6461 
6462  // draw text:
6463  if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6464  {
6465  painter->setFont(labelData.baseFont);
6466  painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6467  painter->setFont(labelData.expFont);
6468  painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
6469  } else
6470  {
6471  painter->setFont(labelData.baseFont);
6472  painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6473  }
6474 
6475  // reset painter settings to what it was before:
6476  painter->setTransform(oldTransform);
6477  painter->setFont(oldFont);
6478 }
6479 
6489 {
6490  TickLabelData result;
6491 
6492  // determine whether beautiful decimal powers should be used
6493  bool useBeautifulPowers = false;
6494  int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart
6495  int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart
6496  if (substituteExponent)
6497  {
6498  ePos = text.indexOf(QLatin1Char('e'));
6499  if (ePos > 0 && text.at(ePos-1).isDigit())
6500  {
6501  eLast = ePos;
6502  while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))
6503  ++eLast;
6504  if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power
6505  useBeautifulPowers = true;
6506  }
6507  }
6508 
6509  // calculate text bounding rects and do string preparation for beautiful decimal powers:
6510  result.baseFont = font;
6511  if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
6512  result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
6513  if (useBeautifulPowers)
6514  {
6515  // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6516  result.basePart = text.left(ePos);
6517  // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6518  if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
6519  result.basePart = QLatin1String("10");
6520  else
6521  result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
6522  result.expPart = text.mid(ePos+1);
6523  // clip "+" and leading zeros off expPart:
6524  while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
6525  result.expPart.remove(1, 1);
6526  if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
6527  result.expPart.remove(0, 1);
6528  // prepare smaller font for exponent:
6529  result.expFont = font;
6530  if (result.expFont.pointSize() > 0)
6531  result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6532  else
6533  result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
6534  // calculate bounding rects of base part, exponent part and total one:
6535  result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6536  result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6537  result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA
6538  } else // useBeautifulPowers == false
6539  {
6540  result.basePart = text;
6541  result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6542  }
6543  result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler
6544 
6545  // calculate possibly different bounding rect after rotation:
6546  result.rotatedTotalBounds = result.totalBounds;
6547  if (!qFuzzyIsNull(tickLabelRotation))
6548  {
6549  QTransform transform;
6550  transform.rotate(tickLabelRotation);
6551  result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6552  }
6553 
6554  return result;
6555 }
6556 
6568 {
6569  /*
6570  calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6571  explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6572  horizontally under the corresponding tick is always on the label side that is closer to the
6573  axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6574  is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6575  will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6576  time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6577  labels.
6578  */
6579  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6580  bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6581  double radians = tickLabelRotation/180.0*M_PI;
6582  int x=0, y=0;
6583  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
6584  {
6585  if (doRotation)
6586  {
6587  if (tickLabelRotation > 0)
6588  {
6589  x = -qCos(radians)*labelData.totalBounds.width();
6590  y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6591  } else
6592  {
6593  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6594  y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6595  }
6596  } else
6597  {
6598  x = -labelData.totalBounds.width();
6599  y = -labelData.totalBounds.height()/2.0;
6600  }
6601  } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
6602  {
6603  if (doRotation)
6604  {
6605  if (tickLabelRotation > 0)
6606  {
6607  x = +qSin(radians)*labelData.totalBounds.height();
6608  y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6609  } else
6610  {
6611  x = 0;
6612  y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6613  }
6614  } else
6615  {
6616  x = 0;
6617  y = -labelData.totalBounds.height()/2.0;
6618  }
6619  } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
6620  {
6621  if (doRotation)
6622  {
6623  if (tickLabelRotation > 0)
6624  {
6625  x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6626  y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6627  } else
6628  {
6629  x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6630  y = -qCos(-radians)*labelData.totalBounds.height();
6631  }
6632  } else
6633  {
6634  x = -labelData.totalBounds.width()/2.0;
6635  y = -labelData.totalBounds.height();
6636  }
6637  } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
6638  {
6639  if (doRotation)
6640  {
6641  if (tickLabelRotation > 0)
6642  {
6643  x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6644  y = 0;
6645  } else
6646  {
6647  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6648  y = +qSin(-radians)*labelData.totalBounds.width();
6649  }
6650  } else
6651  {
6652  x = -labelData.totalBounds.width()/2.0;
6653  y = 0;
6654  }
6655  }
6656 
6657  return QPointF(x, y);
6658 }
6659 
6667 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
6668 {
6669  // note: this function must return the same tick label sizes as the placeTickLabel function.
6670  QSize finalSize;
6671  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6672  {
6673  const CachedLabel *cachedLabel = mLabelCache.object(text);
6674  finalSize = cachedLabel->pixmap.size();
6675  } else // label caching disabled or no label with this text cached:
6676  {
6677  TickLabelData labelData = getTickLabelData(font, text);
6678  finalSize = labelData.rotatedTotalBounds.size();
6679  }
6680 
6681  // expand passed tickLabelsSize if current tick label is larger:
6682  if (finalSize.width() > tickLabelsSize->width())
6683  tickLabelsSize->setWidth(finalSize.width());
6684  if (finalSize.height() > tickLabelsSize->height())
6685  tickLabelsSize->setHeight(finalSize.height());
6686 }
6687 
6688 
6692 
6761 /* start of documentation of pure virtual functions */
6762 
6811 /* end of documentation of pure virtual functions */
6812 /* start of documentation of signals */
6813 
6827 /* end of documentation of signals */
6828 
6841  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
6842  mName(),
6843  mAntialiasedFill(true),
6844  mAntialiasedScatters(true),
6845  mAntialiasedErrorBars(false),
6846  mPen(Qt::black),
6847  mSelectedPen(Qt::black),
6848  mBrush(Qt::NoBrush),
6849  mSelectedBrush(Qt::NoBrush),
6850  mKeyAxis(keyAxis),
6851  mValueAxis(valueAxis),
6852  mSelectable(true),
6853  mSelected(false)
6854 {
6855  if (keyAxis->parentPlot() != valueAxis->parentPlot())
6856  qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
6857  if (keyAxis->orientation() == valueAxis->orientation())
6858  qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
6859 }
6860 
6865 void QCPAbstractPlottable::setName(const QString &name)
6866 {
6867  mName = name;
6868 }
6869 
6877 {
6878  mAntialiasedFill = enabled;
6879 }
6880 
6888 {
6889  mAntialiasedScatters = enabled;
6890 }
6891 
6899 {
6900  mAntialiasedErrorBars = enabled;
6901 }
6902 
6903 
6912 void QCPAbstractPlottable::setPen(const QPen &pen)
6913 {
6914  mPen = pen;
6915 }
6916 
6924 {
6925  mSelectedPen = pen;
6926 }
6927 
6937 void QCPAbstractPlottable::setBrush(const QBrush &brush)
6938 {
6939  mBrush = brush;
6940 }
6941 
6949 {
6951 }
6952 
6965 {
6966  mKeyAxis = axis;
6967 }
6968 
6981 {
6982  mValueAxis = axis;
6983 }
6984 
6995 {
6996  if (mSelectable != selectable)
6997  {
7000  }
7001 }
7002 
7018 {
7019  if (mSelected != selected)
7020  {
7021  mSelected = selected;
7023  }
7024 }
7025 
7039 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
7040 {
7041  rescaleKeyAxis(onlyEnlarge);
7042  rescaleValueAxis(onlyEnlarge);
7043 }
7044 
7050 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
7051 {
7052  QCPAxis *keyAxis = mKeyAxis.data();
7053  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
7054 
7055  SignDomain signDomain = sdBoth;
7056  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
7057  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7058 
7059  bool foundRange;
7060  QCPRange newRange = getKeyRange(foundRange, signDomain);
7061  if (foundRange)
7062  {
7063  if (onlyEnlarge)
7064  newRange.expand(keyAxis->range());
7065  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
7066  {
7067  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
7068  if (keyAxis->scaleType() == QCPAxis::stLinear)
7069  {
7070  newRange.lower = center-keyAxis->range().size()/2.0;
7071  newRange.upper = center+keyAxis->range().size()/2.0;
7072  } else // scaleType() == stLogarithmic
7073  {
7074  newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7075  newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7076  }
7077  }
7078  keyAxis->setRange(newRange);
7079  }
7080 }
7081 
7090 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
7091 {
7092  QCPAxis *valueAxis = mValueAxis.data();
7093  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
7094 
7095  SignDomain signDomain = sdBoth;
7096  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
7097  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7098 
7099  bool foundRange;
7100  QCPRange newRange = getValueRange(foundRange, signDomain);
7101  if (foundRange)
7102  {
7103  if (onlyEnlarge)
7104  newRange.expand(valueAxis->range());
7105  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
7106  {
7107  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
7108  if (valueAxis->scaleType() == QCPAxis::stLinear)
7109  {
7110  newRange.lower = center-valueAxis->range().size()/2.0;
7111  newRange.upper = center+valueAxis->range().size()/2.0;
7112  } else // scaleType() == stLogarithmic
7113  {
7114  newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7115  newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7116  }
7117  }
7118  valueAxis->setRange(newRange);
7119  }
7120 }
7121 
7135 {
7136  if (!mParentPlot || !mParentPlot->legend)
7137  return false;
7138 
7140  {
7142  return true;
7143  } else
7144  return false;
7145 }
7146 
7158 {
7159  if (!mParentPlot->legend)
7160  return false;
7161 
7163  return mParentPlot->legend->removeItem(lip);
7164  else
7165  return false;
7166 }
7167 
7168 /* inherits documentation from base class */
7170 {
7171  if (mKeyAxis && mValueAxis)
7172  return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
7173  else
7174  return QRect();
7175 }
7176 
7177 /* inherits documentation from base class */
7179 {
7180  return QCP::iSelectPlottables;
7181 }
7182 
7193 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
7194 {
7195  QCPAxis *keyAxis = mKeyAxis.data();
7196  QCPAxis *valueAxis = mValueAxis.data();
7197  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7198 
7199  if (keyAxis->orientation() == Qt::Horizontal)
7200  {
7201  x = keyAxis->coordToPixel(key);
7202  y = valueAxis->coordToPixel(value);
7203  } else
7204  {
7205  y = keyAxis->coordToPixel(key);
7206  x = valueAxis->coordToPixel(value);
7207  }
7208 }
7209 
7215 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
7216 {
7217  QCPAxis *keyAxis = mKeyAxis.data();
7218  QCPAxis *valueAxis = mValueAxis.data();
7219  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
7220 
7221  if (keyAxis->orientation() == Qt::Horizontal)
7222  return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7223  else
7224  return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7225 }
7226 
7237 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
7238 {
7239  QCPAxis *keyAxis = mKeyAxis.data();
7240  QCPAxis *valueAxis = mValueAxis.data();
7241  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7242 
7243  if (keyAxis->orientation() == Qt::Horizontal)
7244  {
7245  key = keyAxis->pixelToCoord(x);
7246  value = valueAxis->pixelToCoord(y);
7247  } else
7248  {
7249  key = keyAxis->pixelToCoord(y);
7250  value = valueAxis->pixelToCoord(x);
7251  }
7252 }
7253 
7259 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
7260 {
7261  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7262 }
7263 
7270 {
7271  return mSelected ? mSelectedPen : mPen;
7272 }
7273 
7280 {
7281  return mSelected ? mSelectedBrush : mBrush;
7282 }
7283 
7298 {
7300 }
7301 
7314 {
7316 }
7317 
7330 {
7332 }
7333 
7346 {
7348 }
7349 
7360 double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
7361 {
7362  QVector2D a(start);
7363  QVector2D b(end);
7364  QVector2D p(point);
7365  QVector2D v(b-a);
7366 
7367  double vLengthSqr = v.lengthSquared();
7368  if (!qFuzzyIsNull(vLengthSqr))
7369  {
7370  double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
7371  if (mu < 0)
7372  return (a-p).lengthSquared();
7373  else if (mu > 1)
7374  return (b-p).lengthSquared();
7375  else
7376  return ((a + mu*v)-p).lengthSquared();
7377  } else
7378  return (a-p).lengthSquared();
7379 }
7380 
7381 /* inherits documentation from base class */
7382 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
7383 {
7384  Q_UNUSED(event)
7385  Q_UNUSED(details)
7386  if (mSelectable)
7387  {
7388  bool selBefore = mSelected;
7389  setSelected(additive ? !mSelected : true);
7390  if (selectionStateChanged)
7391  *selectionStateChanged = mSelected != selBefore;
7392  }
7393 }
7394 
7395 /* inherits documentation from base class */
7396 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
7397 {
7398  if (mSelectable)
7399  {
7400  bool selBefore = mSelected;
7401  setSelected(false);
7402  if (selectionStateChanged)
7403  *selectionStateChanged = mSelected != selBefore;
7404  }
7405 }
7406 
7407 
7411 
7433 /* start documentation of inline functions */
7434 
7445 /* end documentation of inline functions */
7446 
7452 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
7453  mName(name),
7454  mParentPlot(parentPlot),
7455  mParentItem(parentItem),
7456  mAnchorId(anchorId)
7457 {
7458 }
7459 
7461 {
7462  // unregister as parent at children:
7463  foreach (QCPItemPosition *child, mChildrenX.toList())
7464  {
7465  if (child->parentAnchorX() == this)
7466  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7467  }
7468  foreach (QCPItemPosition *child, mChildrenY.toList())
7469  {
7470  if (child->parentAnchorY() == this)
7471  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7472  }
7473 }
7474 
7482 {
7483  if (mParentItem)
7484  {
7485  if (mAnchorId > -1)
7486  {
7488  } else
7489  {
7490  qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7491  return QPointF();
7492  }
7493  } else
7494  {
7495  qDebug() << Q_FUNC_INFO << "no parent item set";
7496  return QPointF();
7497  }
7498 }
7499 
7509 {
7510  if (!mChildrenX.contains(pos))
7511  mChildrenX.insert(pos);
7512  else
7513  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7514 }
7515 
7523 {
7524  if (!mChildrenX.remove(pos))
7525  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7526 }
7527 
7537 {
7538  if (!mChildrenY.contains(pos))
7539  mChildrenY.insert(pos);
7540  else
7541  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7542 }
7543 
7551 {
7552  if (!mChildrenY.remove(pos))
7553  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7554 }
7555 
7556 
7560 
7595 /* start documentation of inline functions */
7596 
7618 /* end documentation of inline functions */
7619 
7625 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
7626  QCPItemAnchor(parentPlot, parentItem, name),
7627  mPositionTypeX(ptAbsolute),
7628  mPositionTypeY(ptAbsolute),
7629  mKey(0),
7630  mValue(0),
7631  mParentAnchorX(0),
7632  mParentAnchorY(0)
7633 {
7634 }
7635 
7637 {
7638  // unregister as parent at children:
7639  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
7640  // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
7641  foreach (QCPItemPosition *child, mChildrenX.toList())
7642  {
7643  if (child->parentAnchorX() == this)
7644  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7645  }
7646  foreach (QCPItemPosition *child, mChildrenY.toList())
7647  {
7648  if (child->parentAnchorY() == this)
7649  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7650  }
7651  // unregister as child in parent:
7652  if (mParentAnchorX)
7654  if (mParentAnchorY)
7656 }
7657 
7658 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
7660 {
7661  return mAxisRect.data();
7662 }
7663 
7690 {
7691  setTypeX(type);
7692  setTypeY(type);
7693 }
7694 
7703 {
7704  if (mPositionTypeX != type)
7705  {
7706  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7707  // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7708  bool retainPixelPosition = true;
7709  if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7710  retainPixelPosition = false;
7711  if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7712  retainPixelPosition = false;
7713 
7714  QPointF pixel;
7715  if (retainPixelPosition)
7716  pixel = pixelPoint();
7717 
7718  mPositionTypeX = type;
7719 
7720  if (retainPixelPosition)
7721  setPixelPoint(pixel);
7722  }
7723 }
7724 
7733 {
7734  if (mPositionTypeY != type)
7735  {
7736  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7737  // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7738  bool retainPixelPosition = true;
7739  if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7740  retainPixelPosition = false;
7741  if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7742  retainPixelPosition = false;
7743 
7744  QPointF pixel;
7745  if (retainPixelPosition)
7746  pixel = pixelPoint();
7747 
7748  mPositionTypeY = type;
7749 
7750  if (retainPixelPosition)
7751  setPixelPoint(pixel);
7752  }
7753 }
7754 
7773 bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7774 {
7775  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
7776  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
7777  return successX && successY;
7778 }
7779 
7787 bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7788 {
7789  // make sure self is not assigned as parent:
7790  if (parentAnchor == this)
7791  {
7792  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7793  return false;
7794  }
7795  // make sure no recursive parent-child-relationships are created:
7796  QCPItemAnchor *currentParent = parentAnchor;
7797  while (currentParent)
7798  {
7799  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7800  {
7801  // is a QCPItemPosition, might have further parent, so keep iterating
7802  if (currentParentPos == this)
7803  {
7804  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7805  return false;
7806  }
7807  currentParent = currentParentPos->parentAnchorX();
7808  } else
7809  {
7810  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7811  // same, to prevent a position being child of an anchor which itself depends on the position,
7812  // because they're both on the same item:
7813  if (currentParent->mParentItem == mParentItem)
7814  {
7815  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7816  return false;
7817  }
7818  break;
7819  }
7820  }
7821 
7822  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7825 
7826  // save pixel position:
7827  QPointF pixelP;
7828  if (keepPixelPosition)
7829  pixelP = pixelPoint();
7830  // unregister at current parent anchor:
7831  if (mParentAnchorX)
7833  // register at new parent anchor:
7834  if (parentAnchor)
7835  parentAnchor->addChildX(this);
7837  // restore pixel position under new parent:
7838  if (keepPixelPosition)
7839  setPixelPoint(pixelP);
7840  else
7841  setCoords(0, coords().y());
7842  return true;
7843 }
7844 
7852 bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7853 {
7854  // make sure self is not assigned as parent:
7855  if (parentAnchor == this)
7856  {
7857  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7858  return false;
7859  }
7860  // make sure no recursive parent-child-relationships are created:
7861  QCPItemAnchor *currentParent = parentAnchor;
7862  while (currentParent)
7863  {
7864  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7865  {
7866  // is a QCPItemPosition, might have further parent, so keep iterating
7867  if (currentParentPos == this)
7868  {
7869  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7870  return false;
7871  }
7872  currentParent = currentParentPos->parentAnchorY();
7873  } else
7874  {
7875  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7876  // same, to prevent a position being child of an anchor which itself depends on the position,
7877  // because they're both on the same item:
7878  if (currentParent->mParentItem == mParentItem)
7879  {
7880  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7881  return false;
7882  }
7883  break;
7884  }
7885  }
7886 
7887  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7890 
7891  // save pixel position:
7892  QPointF pixelP;
7893  if (keepPixelPosition)
7894  pixelP = pixelPoint();
7895  // unregister at current parent anchor:
7896  if (mParentAnchorY)
7898  // register at new parent anchor:
7899  if (parentAnchor)
7900  parentAnchor->addChildY(this);
7902  // restore pixel position under new parent:
7903  if (keepPixelPosition)
7904  setPixelPoint(pixelP);
7905  else
7906  setCoords(coords().x(), 0);
7907  return true;
7908 }
7909 
7927 void QCPItemPosition::setCoords(double key, double value)
7928 {
7929  mKey = key;
7930  mValue = value;
7931 }
7932 
7938 void QCPItemPosition::setCoords(const QPointF &pos)
7939 {
7940  setCoords(pos.x(), pos.y());
7941 }
7942 
7950 {
7951  QPointF result;
7952 
7953  // determine X:
7954  switch (mPositionTypeX)
7955  {
7956  case ptAbsolute:
7957  {
7958  result.rx() = mKey;
7959  if (mParentAnchorX)
7960  result.rx() += mParentAnchorX->pixelPoint().x();
7961  break;
7962  }
7963  case ptViewportRatio:
7964  {
7965  result.rx() = mKey*mParentPlot->viewport().width();
7966  if (mParentAnchorX)
7967  result.rx() += mParentAnchorX->pixelPoint().x();
7968  else
7969  result.rx() += mParentPlot->viewport().left();
7970  break;
7971  }
7972  case ptAxisRectRatio:
7973  {
7974  if (mAxisRect)
7975  {
7976  result.rx() = mKey*mAxisRect.data()->width();
7977  if (mParentAnchorX)
7978  result.rx() += mParentAnchorX->pixelPoint().x();
7979  else
7980  result.rx() += mAxisRect.data()->left();
7981  } else
7982  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
7983  break;
7984  }
7985  case ptPlotCoords:
7986  {
7987  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
7988  result.rx() = mKeyAxis.data()->coordToPixel(mKey);
7989  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
7990  result.rx() = mValueAxis.data()->coordToPixel(mValue);
7991  else
7992  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
7993  break;
7994  }
7995  }
7996 
7997  // determine Y:
7998  switch (mPositionTypeY)
7999  {
8000  case ptAbsolute:
8001  {
8002  result.ry() = mValue;
8003  if (mParentAnchorY)
8004  result.ry() += mParentAnchorY->pixelPoint().y();
8005  break;
8006  }
8007  case ptViewportRatio:
8008  {
8009  result.ry() = mValue*mParentPlot->viewport().height();
8010  if (mParentAnchorY)
8011  result.ry() += mParentAnchorY->pixelPoint().y();
8012  else
8013  result.ry() += mParentPlot->viewport().top();
8014  break;
8015  }
8016  case ptAxisRectRatio:
8017  {
8018  if (mAxisRect)
8019  {
8020  result.ry() = mValue*mAxisRect.data()->height();
8021  if (mParentAnchorY)
8022  result.ry() += mParentAnchorY->pixelPoint().y();
8023  else
8024  result.ry() += mAxisRect.data()->top();
8025  } else
8026  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8027  break;
8028  }
8029  case ptPlotCoords:
8030  {
8031  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8032  result.ry() = mKeyAxis.data()->coordToPixel(mKey);
8033  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8034  result.ry() = mValueAxis.data()->coordToPixel(mValue);
8035  else
8036  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8037  break;
8038  }
8039  }
8040 
8041  return result;
8042 }
8043 
8049 void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
8050 {
8051  mKeyAxis = keyAxis;
8053 }
8054 
8061 {
8062  mAxisRect = axisRect;
8063 }
8064 
8075 void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
8076 {
8077  double x = pixelPoint.x();
8078  double y = pixelPoint.y();
8079 
8080  switch (mPositionTypeX)
8081  {
8082  case ptAbsolute:
8083  {
8084  if (mParentAnchorX)
8085  x -= mParentAnchorX->pixelPoint().x();
8086  break;
8087  }
8088  case ptViewportRatio:
8089  {
8090  if (mParentAnchorX)
8091  x -= mParentAnchorX->pixelPoint().x();
8092  else
8093  x -= mParentPlot->viewport().left();
8094  x /= (double)mParentPlot->viewport().width();
8095  break;
8096  }
8097  case ptAxisRectRatio:
8098  {
8099  if (mAxisRect)
8100  {
8101  if (mParentAnchorX)
8102  x -= mParentAnchorX->pixelPoint().x();
8103  else
8104  x -= mAxisRect.data()->left();
8105  x /= (double)mAxisRect.data()->width();
8106  } else
8107  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
8108  break;
8109  }
8110  case ptPlotCoords:
8111  {
8112  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8113  x = mKeyAxis.data()->pixelToCoord(x);
8114  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
8115  y = mValueAxis.data()->pixelToCoord(x);
8116  else
8117  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
8118  break;
8119  }
8120  }
8121 
8122  switch (mPositionTypeY)
8123  {
8124  case ptAbsolute:
8125  {
8126  if (mParentAnchorY)
8127  y -= mParentAnchorY->pixelPoint().y();
8128  break;
8129  }
8130  case ptViewportRatio:
8131  {
8132  if (mParentAnchorY)
8133  y -= mParentAnchorY->pixelPoint().y();
8134  else
8135  y -= mParentPlot->viewport().top();
8136  y /= (double)mParentPlot->viewport().height();
8137  break;
8138  }
8139  case ptAxisRectRatio:
8140  {
8141  if (mAxisRect)
8142  {
8143  if (mParentAnchorY)
8144  y -= mParentAnchorY->pixelPoint().y();
8145  else
8146  y -= mAxisRect.data()->top();
8147  y /= (double)mAxisRect.data()->height();
8148  } else
8149  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8150  break;
8151  }
8152  case ptPlotCoords:
8153  {
8154  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8155  x = mKeyAxis.data()->pixelToCoord(y);
8156  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8157  y = mValueAxis.data()->pixelToCoord(y);
8158  else
8159  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8160  break;
8161  }
8162  }
8163 
8164  setCoords(x, y);
8165 }
8166 
8167 
8171 
8297 /* start of documentation of inline functions */
8298 
8314 /* end of documentation of inline functions */
8315 /* start documentation of pure virtual functions */
8316 
8327 /* end documentation of pure virtual functions */
8328 /* start documentation of signals */
8329 
8335 /* end documentation of signals */
8336 
8341  QCPLayerable(parentPlot),
8342  mClipToAxisRect(false),
8343  mSelectable(true),
8344  mSelected(false)
8345 {
8346  QList<QCPAxisRect*> rects = parentPlot->axisRects();
8347  if (rects.size() > 0)
8348  {
8349  setClipToAxisRect(true);
8350  setClipAxisRect(rects.first());
8351  }
8352 }
8353 
8355 {
8356  // don't delete mPositions because every position is also an anchor and thus in mAnchors
8357  qDeleteAll(mAnchors);
8358 }
8359 
8360 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
8362 {
8363  return mClipAxisRect.data();
8364 }
8365 
8373 {
8374  mClipToAxisRect = clip;
8375  if (mClipToAxisRect)
8377 }
8378 
8386 {
8387  mClipAxisRect = rect;
8388  if (mClipToAxisRect)
8390 }
8391 
8401 void QCPAbstractItem::setSelectable(bool selectable)
8402 {
8403  if (mSelectable != selectable)
8404  {
8407  }
8408 }
8409 
8424 void QCPAbstractItem::setSelected(bool selected)
8425 {
8426  if (mSelected != selected)
8427  {
8428  mSelected = selected;
8430  }
8431 }
8432 
8443 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
8444 {
8445  for (int i=0; i<mPositions.size(); ++i)
8446  {
8447  if (mPositions.at(i)->name() == name)
8448  return mPositions.at(i);
8449  }
8450  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8451  return 0;
8452 }
8453 
8464 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8465 {
8466  for (int i=0; i<mAnchors.size(); ++i)
8467  {
8468  if (mAnchors.at(i)->name() == name)
8469  return mAnchors.at(i);
8470  }
8471  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8472  return 0;
8473 }
8474 
8483 bool QCPAbstractItem::hasAnchor(const QString &name) const
8484 {
8485  for (int i=0; i<mAnchors.size(); ++i)
8486  {
8487  if (mAnchors.at(i)->name() == name)
8488  return true;
8489  }
8490  return false;
8491 }
8492 
8503 {
8505  return mClipAxisRect.data()->rect();
8506  else
8507  return mParentPlot->viewport();
8508 }
8509 
8524 {
8526 }
8527 
8540 double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8541 {
8542  QVector2D a(start);
8543  QVector2D b(end);
8544  QVector2D p(point);
8545  QVector2D v(b-a);
8546 
8547  double vLengthSqr = v.lengthSquared();
8548  if (!qFuzzyIsNull(vLengthSqr))
8549  {
8550  double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8551  if (mu < 0)
8552  return (a-p).lengthSquared();
8553  else if (mu > 1)
8554  return (b-p).lengthSquared();
8555  else
8556  return ((a + mu*v)-p).lengthSquared();
8557  } else
8558  return (a-p).lengthSquared();
8559 }
8560 
8576 double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
8577 {
8578  double result = -1;
8579 
8580  // distance to border:
8581  QList<QLineF> lines;
8582  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
8583  << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
8584  double minDistSqr = std::numeric_limits<double>::max();
8585  for (int i=0; i<lines.size(); ++i)
8586  {
8587  double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8588  if (distSqr < minDistSqr)
8589  minDistSqr = distSqr;
8590  }
8591  result = qSqrt(minDistSqr);
8592 
8593  // filled rect, allow click inside to count as hit:
8594  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
8595  {
8596  if (rect.contains(pos))
8597  result = mParentPlot->selectionTolerance()*0.99;
8598  }
8599  return result;
8600 }
8601 
8612 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
8613 {
8614  qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
8615  return QPointF();
8616 }
8617 
8633 {
8634  if (hasAnchor(name))
8635  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8636  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8637  mPositions.append(newPosition);
8638  mAnchors.append(newPosition); // every position is also an anchor
8639  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8640  newPosition->setType(QCPItemPosition::ptPlotCoords);
8641  if (mParentPlot->axisRect())
8642  newPosition->setAxisRect(mParentPlot->axisRect());
8643  newPosition->setCoords(0, 0);
8644  return newPosition;
8645 }
8646 
8666 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
8667 {
8668  if (hasAnchor(name))
8669  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8670  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
8671  mAnchors.append(newAnchor);
8672  return newAnchor;
8673 }
8674 
8675 /* inherits documentation from base class */
8676 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8677 {
8678  Q_UNUSED(event)
8679  Q_UNUSED(details)
8680  if (mSelectable)
8681  {
8682  bool selBefore = mSelected;
8683  setSelected(additive ? !mSelected : true);
8684  if (selectionStateChanged)
8685  *selectionStateChanged = mSelected != selBefore;
8686  }
8687 }
8688 
8689 /* inherits documentation from base class */
8690 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
8691 {
8692  if (mSelectable)
8693  {
8694  bool selBefore = mSelected;
8695  setSelected(false);
8696  if (selectionStateChanged)
8697  *selectionStateChanged = mSelected != selBefore;
8698  }
8699 }
8700 
8701 /* inherits documentation from base class */
8703 {
8704  return QCP::iSelectItems;
8705 }
8706 
8707 
8712 
8725 /* start of documentation of inline functions */
8726 
8748 /* end of documentation of inline functions */
8749 /* start of documentation of signals */
8750 
8938 /* end of documentation of signals */
8939 /* start of documentation of public members */
8940 
9012 /* end of documentation of public members */
9013 
9017 QCustomPlot::QCustomPlot(QWidget *parent) :
9018  QWidget(parent),
9019  xAxis(0),
9020  yAxis(0),
9021  xAxis2(0),
9022  yAxis2(0),
9023  legend(0),
9024  mPlotLayout(0),
9025  mAutoAddPlottableToLegend(true),
9026  mAntialiasedElements(QCP::aeNone),
9027  mNotAntialiasedElements(QCP::aeNone),
9028  mInteractions(0),
9029  mSelectionTolerance(8),
9030  mNoAntialiasingOnDrag(false),
9031  mBackgroundBrush(Qt::white, Qt::SolidPattern),
9032  mBackgroundScaled(true),
9033  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9034  mCurrentLayer(0),
9035  mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
9036  mMultiSelectModifier(Qt::ControlModifier),
9037  mPaintBuffer(size()),
9038  mMouseEventElement(0),
9039  mReplotting(false)
9040 {
9041  setAttribute(Qt::WA_NoMousePropagation);
9042  setAttribute(Qt::WA_OpaquePaintEvent);
9043  setMouseTracking(true);
9044  QLocale currentLocale = locale();
9045  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9046  setLocale(currentLocale);
9047 
9048  // create initial layers:
9049  mLayers.append(new QCPLayer(this, QLatin1String("background")));
9050  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
9051  mLayers.append(new QCPLayer(this, QLatin1String("main")));
9052  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
9053  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
9055  setCurrentLayer(QLatin1String("main"));
9056 
9057  // create initial layout, axis rect and legend:
9058  mPlotLayout = new QCPLayoutGrid;
9060  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9061  mPlotLayout->setLayer(QLatin1String("main"));
9062  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9063  mPlotLayout->addElement(0, 0, defaultAxisRect);
9064  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9065  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9066  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9067  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9068  legend = new QCPLegend;
9069  legend->setVisible(false);
9070  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
9071  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9072 
9073  defaultAxisRect->setLayer(QLatin1String("background"));
9074  xAxis->setLayer(QLatin1String("axes"));
9075  yAxis->setLayer(QLatin1String("axes"));
9076  xAxis2->setLayer(QLatin1String("axes"));
9077  yAxis2->setLayer(QLatin1String("axes"));
9078  xAxis->grid()->setLayer(QLatin1String("grid"));
9079  yAxis->grid()->setLayer(QLatin1String("grid"));
9080  xAxis2->grid()->setLayer(QLatin1String("grid"));
9081  yAxis2->grid()->setLayer(QLatin1String("grid"));
9082  legend->setLayer(QLatin1String("legend"));
9083 
9084  setViewport(rect()); // needs to be called after mPlotLayout has been created
9085 
9086  replot();
9087 }
9088 
9090 {
9091  clearPlottables();
9092  clearItems();
9093 
9094  if (mPlotLayout)
9095  {
9096  delete mPlotLayout;
9097  mPlotLayout = 0;
9098  }
9099 
9100  mCurrentLayer = 0;
9101  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
9102  mLayers.clear();
9103 }
9104 
9122 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
9123 {
9125 
9126  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9128  mNotAntialiasedElements |= ~mAntialiasedElements;
9129 }
9130 
9138 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
9139 {
9140  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9141  mAntialiasedElements &= ~antialiasedElement;
9142  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9143  mAntialiasedElements |= antialiasedElement;
9144 
9145  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9147  mNotAntialiasedElements |= ~mAntialiasedElements;
9148 }
9149 
9168 void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
9169 {
9171 
9172  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9174  mAntialiasedElements |= ~mNotAntialiasedElements;
9175 }
9176 
9184 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
9185 {
9186  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9187  mNotAntialiasedElements &= ~notAntialiasedElement;
9188  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9189  mNotAntialiasedElements |= notAntialiasedElement;
9190 
9191  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9193  mAntialiasedElements |= ~mNotAntialiasedElements;
9194 }
9195 
9203 {
9205 }
9206 
9261 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
9262 {
9264 }
9265 
9273 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
9274 {
9275  if (!enabled && mInteractions.testFlag(interaction))
9276  mInteractions &= ~interaction;
9277  else if (enabled && !mInteractions.testFlag(interaction))
9278  mInteractions |= interaction;
9279 }
9280 
9295 {
9296  mSelectionTolerance = pixels;
9297 }
9298 
9309 {
9310  mNoAntialiasingOnDrag = enabled;
9311 }
9312 
9318 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
9319 {
9320  mPlottingHints = hints;
9321 }
9322 
9329 {
9330  QCP::PlottingHints newHints = mPlottingHints;
9331  if (!enabled)
9332  newHints &= ~hint;
9333  else
9334  newHints |= hint;
9335 
9336  if (newHints != mPlottingHints)
9337  setPlottingHints(newHints);
9338 }
9339 
9350 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
9351 {
9352  mMultiSelectModifier = modifier;
9353 }
9354 
9362 void QCustomPlot::setViewport(const QRect &rect)
9363 {
9364  mViewport = rect;
9365  if (mPlotLayout)
9367 }
9368 
9384 void QCustomPlot::setBackground(const QPixmap &pm)
9385 {
9386  mBackgroundPixmap = pm;
9387  mScaledBackgroundPixmap = QPixmap();
9388 }
9389 
9403 void QCustomPlot::setBackground(const QBrush &brush)
9404 {
9405  mBackgroundBrush = brush;
9406 }
9407 
9415 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
9416 {
9417  mBackgroundPixmap = pm;
9418  mScaledBackgroundPixmap = QPixmap();
9419  mBackgroundScaled = scaled;
9420  mBackgroundScaledMode = mode;
9421 }
9422 
9434 {
9435  mBackgroundScaled = scaled;
9436 }
9437 
9444 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
9445 {
9446  mBackgroundScaledMode = mode;
9447 }
9448 
9458 {
9459  if (index >= 0 && index < mPlottables.size())
9460  {
9461  return mPlottables.at(index);
9462  } else
9463  {
9464  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9465  return 0;
9466  }
9467 }
9468 
9477 {
9478  if (!mPlottables.isEmpty())
9479  {
9480  return mPlottables.last();
9481  } else
9482  return 0;
9483 }
9484 
9496 {
9497  if (mPlottables.contains(plottable))
9498  {
9499  qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
9500  return false;
9501  }
9502  if (plottable->parentPlot() != this)
9503  {
9504  qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
9505  return false;
9506  }
9507 
9508  mPlottables.append(plottable);
9509  // possibly add plottable to legend:
9511  plottable->addToLegend();
9512  // special handling for QCPGraphs to maintain the simple graph interface:
9513  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9514  mGraphs.append(graph);
9515  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
9516  plottable->setLayer(currentLayer());
9517  return true;
9518 }
9519 
9528 {
9529  if (!mPlottables.contains(plottable))
9530  {
9531  qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
9532  return false;
9533  }
9534 
9535  // remove plottable from legend:
9536  plottable->removeFromLegend();
9537  // special handling for QCPGraphs to maintain the simple graph interface:
9538  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9539  mGraphs.removeOne(graph);
9540  // remove plottable:
9541  delete plottable;
9542  mPlottables.removeOne(plottable);
9543  return true;
9544 }
9545 
9551 {
9552  if (index >= 0 && index < mPlottables.size())
9553  return removePlottable(mPlottables[index]);
9554  else
9555  {
9556  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9557  return false;
9558  }
9559 }
9560 
9569 {
9570  int c = mPlottables.size();
9571  for (int i=c-1; i >= 0; --i)
9573  return c;
9574 }
9575 
9582 {
9583  return mPlottables.size();
9584 }
9585 
9593 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9594 {
9595  QList<QCPAbstractPlottable*> result;
9597  {
9598  if (plottable->selected())
9599  result.append(plottable);
9600  }
9601  return result;
9602 }
9603 
9616 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
9617 {
9618  QCPAbstractPlottable *resultPlottable = 0;
9619  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9620 
9622  {
9623  if (onlySelectable && !plottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable
9624  continue;
9625  if ((plottable->keyAxis()->axisRect()->rect() & plottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
9626  {
9627  double currentDistance = plottable->selectTest(pos, false);
9628  if (currentDistance >= 0 && currentDistance < resultDistance)
9629  {
9630  resultPlottable = plottable;
9631  resultDistance = currentDistance;
9632  }
9633  }
9634  }
9635 
9636  return resultPlottable;
9637 }
9638 
9645 {
9646  return mPlottables.contains(plottable);
9647 }
9648 
9657 QCPGraph *QCustomPlot::graph(int index) const
9658 {
9659  if (index >= 0 && index < mGraphs.size())
9660  {
9661  return mGraphs.at(index);
9662  } else
9663  {
9664  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9665  return 0;
9666  }
9667 }
9668 
9677 {
9678  if (!mGraphs.isEmpty())
9679  {
9680  return mGraphs.last();
9681  } else
9682  return 0;
9683 }
9684 
9698 {
9699  if (!keyAxis) keyAxis = xAxis;
9700  if (!valueAxis) valueAxis = yAxis;
9701  if (!keyAxis || !valueAxis)
9702  {
9703  qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
9704  return 0;
9705  }
9706  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
9707  {
9708  qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
9709  return 0;
9710  }
9711 
9712  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9713  if (addPlottable(newGraph))
9714  {
9715  newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
9716  return newGraph;
9717  } else
9718  {
9719  delete newGraph;
9720  return 0;
9721  }
9722 }
9723 
9734 {
9735  return removePlottable(graph);
9736 }
9737 
9743 {
9744  if (index >= 0 && index < mGraphs.size())
9745  return removeGraph(mGraphs[index]);
9746  else
9747  return false;
9748 }
9749 
9758 {
9759  int c = mGraphs.size();
9760  for (int i=c-1; i >= 0; --i)
9761  removeGraph(mGraphs[i]);
9762  return c;
9763 }
9764 
9771 {
9772  return mGraphs.size();
9773 }
9774 
9783 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9784 {
9785  QList<QCPGraph*> result;
9786  foreach (QCPGraph *graph, mGraphs)
9787  {
9788  if (graph->selected())
9789  result.append(graph);
9790  }
9791  return result;
9792 }
9793 
9803 {
9804  if (index >= 0 && index < mItems.size())
9805  {
9806  return mItems.at(index);
9807  } else
9808  {
9809  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9810  return 0;
9811  }
9812 }
9813 
9822 {
9823  if (!mItems.isEmpty())
9824  {
9825  return mItems.last();
9826  } else
9827  return 0;
9828 }
9829 
9839 {
9840  if (!mItems.contains(item) && item->parentPlot() == this)
9841  {
9842  mItems.append(item);
9843  return true;
9844  } else
9845  {
9846  qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
9847  return false;
9848  }
9849 }
9850 
9859 {
9860  if (mItems.contains(item))
9861  {
9862  delete item;
9863  mItems.removeOne(item);
9864  return true;
9865  } else
9866  {
9867  qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
9868  return false;
9869  }
9870 }
9871 
9877 {
9878  if (index >= 0 && index < mItems.size())
9879  return removeItem(mItems[index]);
9880  else
9881  {
9882  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9883  return false;
9884  }
9885 }
9886 
9895 {
9896  int c = mItems.size();
9897  for (int i=c-1; i >= 0; --i)
9898  removeItem(mItems[i]);
9899  return c;
9900 }
9901 
9908 {
9909  return mItems.size();
9910 }
9911 
9917 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9918 {
9919  QList<QCPAbstractItem*> result;
9920  foreach (QCPAbstractItem *item, mItems)
9921  {
9922  if (item->selected())
9923  result.append(item);
9924  }
9925  return result;
9926 }
9927 
9941 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
9942 {
9943  QCPAbstractItem *resultItem = 0;
9944  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9945 
9946  foreach (QCPAbstractItem *item, mItems)
9947  {
9948  if (onlySelectable && !item->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
9949  continue;
9950  if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
9951  {
9952  double currentDistance = item->selectTest(pos, false);
9953  if (currentDistance >= 0 && currentDistance < resultDistance)
9954  {
9955  resultItem = item;
9956  resultDistance = currentDistance;
9957  }
9958  }
9959  }
9960 
9961  return resultItem;
9962 }
9963 
9970 {
9971  return mItems.contains(item);
9972 }
9973 
9982 QCPLayer *QCustomPlot::layer(const QString &name) const
9983 {
9984  foreach (QCPLayer *layer, mLayers)
9985  {
9986  if (layer->name() == name)
9987  return layer;
9988  }
9989  return 0;
9990 }
9991 
9998 QCPLayer *QCustomPlot::layer(int index) const
9999 {
10000  if (index >= 0 && index < mLayers.size())
10001  {
10002  return mLayers.at(index);
10003  } else
10004  {
10005  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10006  return 0;
10007  }
10008 }
10009 
10014 {
10015  return mCurrentLayer;
10016 }
10017 
10028 bool QCustomPlot::setCurrentLayer(const QString &name)
10029 {
10030  if (QCPLayer *newCurrentLayer = layer(name))
10031  {
10032  return setCurrentLayer(newCurrentLayer);
10033  } else
10034  {
10035  qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10036  return false;
10037  }
10038 }
10039 
10049 {
10050  if (!mLayers.contains(layer))
10051  {
10052  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10053  return false;
10054  }
10055 
10056  mCurrentLayer = layer;
10057  return true;
10058 }
10059 
10066 {
10067  return mLayers.size();
10068 }
10069 
10083 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10084 {
10085  if (!otherLayer)
10086  otherLayer = mLayers.last();
10087  if (!mLayers.contains(otherLayer))
10088  {
10089  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10090  return false;
10091  }
10092  if (layer(name))
10093  {
10094  qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10095  return false;
10096  }
10097 
10098  QCPLayer *newLayer = new QCPLayer(this, name);
10099  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
10101  return true;
10102 }
10103 
10119 {
10120  if (!mLayers.contains(layer))
10121  {
10122  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10123  return false;
10124  }
10125  if (mLayers.size() < 2)
10126  {
10127  qDebug() << Q_FUNC_INFO << "can't remove last layer";
10128  return false;
10129  }
10130 
10131  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
10132  int removedIndex = layer->index();
10133  bool isFirstLayer = removedIndex==0;
10134  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
10135  QList<QCPLayerable*> children = layer->children();
10136  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
10137  {
10138  for (int i=children.size()-1; i>=0; --i)
10139  children.at(i)->moveToLayer(targetLayer, true);
10140  } else // append normally
10141  {
10142  for (int i=0; i<children.size(); ++i)
10143  children.at(i)->moveToLayer(targetLayer, false);
10144  }
10145  // if removed layer is current layer, change current layer to layer below/above:
10146  if (layer == mCurrentLayer)
10147  setCurrentLayer(targetLayer);
10148  // remove layer:
10149  delete layer;
10150  mLayers.removeOne(layer);
10152  return true;
10153 }
10154 
10165 {
10166  if (!mLayers.contains(layer))
10167  {
10168  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10169  return false;
10170  }
10171  if (!mLayers.contains(otherLayer))
10172  {
10173  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10174  return false;
10175  }
10176 
10177  if (layer->index() > otherLayer->index())
10178  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
10179  else if (layer->index() < otherLayer->index())
10180  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1));
10181 
10183  return true;
10184 }
10185 
10196 {
10197  return axisRects().size();
10198 }
10199 
10210 {
10211  const QList<QCPAxisRect*> rectList = axisRects();
10212  if (index >= 0 && index < rectList.size())
10213  {
10214  return rectList.at(index);
10215  } else
10216  {
10217  qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10218  return 0;
10219  }
10220 }
10221 
10227 QList<QCPAxisRect*> QCustomPlot::axisRects() const
10228 {
10229  QList<QCPAxisRect*> result;
10230  QStack<QCPLayoutElement*> elementStack;
10231  if (mPlotLayout)
10232  elementStack.push(mPlotLayout);
10233 
10234  while (!elementStack.isEmpty())
10235  {
10236  foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
10237  {
10238  if (element)
10239  {
10240  elementStack.push(element);
10241  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10242  result.append(ar);
10243  }
10244  }
10245  }
10246 
10247  return result;
10248 }
10249 
10260 {
10261  QCPLayoutElement *currentElement = mPlotLayout;
10262  bool searchSubElements = true;
10263  while (searchSubElements && currentElement)
10264  {
10265  searchSubElements = false;
10266  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10267  {
10268  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10269  {
10270  currentElement = subElement;
10271  searchSubElements = true;
10272  break;
10273  }
10274  }
10275  }
10276  return currentElement;
10277 }
10278 
10286 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10287 {
10288  QList<QCPAxis*> result, allAxes;
10289  foreach (QCPAxisRect *rect, axisRects())
10290  allAxes << rect->axes();
10291 
10292  foreach (QCPAxis *axis, allAxes)
10293  {
10294  if (axis->selectedParts() != QCPAxis::spNone)
10295  result.append(axis);
10296  }
10297 
10298  return result;
10299 }
10300 
10308 QList<QCPLegend*> QCustomPlot::selectedLegends() const
10309 {
10310  QList<QCPLegend*> result;
10311 
10312  QStack<QCPLayoutElement*> elementStack;
10313  if (mPlotLayout)
10314  elementStack.push(mPlotLayout);
10315 
10316  while (!elementStack.isEmpty())
10317  {
10318  foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
10319  {
10320  if (subElement)
10321  {
10322  elementStack.push(subElement);
10323  if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10324  {
10325  if (leg->selectedParts() != QCPLegend::spNone)
10326  result.append(leg);
10327  }
10328  }
10329  }
10330  }
10331 
10332  return result;
10333 }
10334 
10345 {
10346  foreach (QCPLayer *layer, mLayers)
10347  {
10348  foreach (QCPLayerable *layerable, layer->children())
10349  layerable->deselectEvent(0);
10350  }
10351 }
10352 
10367 {
10368  if (mReplotting) // incase signals loop back to replot slot
10369  return;
10370  mReplotting = true;
10371  emit beforeReplot();
10372 
10373  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10374  QCPPainter painter;
10375  painter.begin(&mPaintBuffer);
10376  if (painter.isActive())
10377  {
10378  painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10379  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10380  painter.fillRect(mViewport, mBackgroundBrush);
10381  draw(&painter);
10382  painter.end();
10383  if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10384  repaint();
10385  else
10386  update();
10387  } else // might happen if QCustomPlot has width or height zero
10388  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
10389 
10390  emit afterReplot();
10391  mReplotting = false;
10392 }
10393 
10402 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10403 {
10404  QList<QCPAxis*> allAxes;
10405  foreach (QCPAxisRect *rect, axisRects())
10406  allAxes << rect->axes();
10407 
10408  foreach (QCPAxis *axis, allAxes)
10409  axis->rescale(onlyVisiblePlottables);
10410 }
10411 
10449 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10450 {
10451  bool success = false;
10452 #ifdef QT_NO_PRINTER
10453  Q_UNUSED(fileName)
10454  Q_UNUSED(noCosmeticPen)
10455  Q_UNUSED(width)
10456  Q_UNUSED(height)
10457  Q_UNUSED(pdfCreator)
10458  Q_UNUSED(pdfTitle)
10459  qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
10460 #else
10461  int newWidth, newHeight;
10462  if (width == 0 || height == 0)
10463  {
10464  newWidth = this->width();
10465  newHeight = this->height();
10466  } else
10467  {
10468  newWidth = width;
10469  newHeight = height;
10470  }
10471 
10472  QPrinter printer(QPrinter::ScreenResolution);
10473  printer.setOutputFileName(fileName);
10474  printer.setOutputFormat(QPrinter::PdfFormat);
10475  printer.setColorMode(QPrinter::Color);
10476  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10477  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10478  QRect oldViewport = viewport();
10479  setViewport(QRect(0, 0, newWidth, newHeight));
10480 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
10481  printer.setFullPage(true);
10482  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10483 #else
10484  QPageLayout pageLayout;
10485  pageLayout.setMode(QPageLayout::FullPageMode);
10486  pageLayout.setOrientation(QPageLayout::Portrait);
10487  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
10488  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
10489  printer.setPageLayout(pageLayout);
10490 #endif
10491  QCPPainter printpainter;
10492  if (printpainter.begin(&printer))
10493  {
10494  printpainter.setMode(QCPPainter::pmVectorized);
10495  printpainter.setMode(QCPPainter::pmNoCaching);
10496  printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10497  printpainter.setWindow(mViewport);
10498  if (mBackgroundBrush.style() != Qt::NoBrush &&
10499  mBackgroundBrush.color() != Qt::white &&
10500  mBackgroundBrush.color() != Qt::transparent &&
10501  mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
10502  printpainter.fillRect(viewport(), mBackgroundBrush);
10503  draw(&printpainter);
10504  printpainter.end();
10505  success = true;
10506  }
10507  setViewport(oldViewport);
10508 #endif // QT_NO_PRINTER
10509  return success;
10510 }
10511 
10549 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10550 {
10551  return saveRastered(fileName, width, height, scale, "PNG", quality);
10552 }
10553 
10588 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
10589 {
10590  return saveRastered(fileName, width, height, scale, "JPG", quality);
10591 }
10592 
10624 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
10625 {
10626  return saveRastered(fileName, width, height, scale, "BMP");
10627 }
10628 
10638 {
10639  return mPlotLayout->minimumSizeHint();
10640 }
10641 
10648 {
10649  return mPlotLayout->minimumSizeHint();
10650 }
10651 
10657 void QCustomPlot::paintEvent(QPaintEvent *event)
10658 {
10659  Q_UNUSED(event);
10660  QPainter painter(this);
10661  painter.drawPixmap(0, 0, mPaintBuffer);
10662 }
10663 
10670 void QCustomPlot::resizeEvent(QResizeEvent *event)
10671 {
10672  // resize and repaint the buffer:
10673  mPaintBuffer = QPixmap(event->size());
10674  setViewport(rect());
10675  replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10676 }
10677 
10687 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
10688 {
10689  emit mouseDoubleClick(event);
10690 
10691  QVariant details;
10692  QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10693 
10694  // emit specialized object double click signals:
10695  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10696  emit plottableDoubleClick(ap, event);
10697  else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10698  emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10699  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10700  emit itemDoubleClick(ai, event);
10701  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10702  emit legendDoubleClick(lg, 0, event);
10703  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10704  emit legendDoubleClick(li->parentLegend(), li, event);
10705  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10706  emit titleDoubleClick(event, pt);
10707 
10708  // call double click event of affected layout element:
10709  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10710  el->mouseDoubleClickEvent(event);
10711 
10712  // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
10713  if (mMouseEventElement)
10714  {
10715  mMouseEventElement->mouseReleaseEvent(event);
10716  mMouseEventElement = 0;
10717  }
10718 
10719  //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
10720 }
10721 
10729 void QCustomPlot::mousePressEvent(QMouseEvent *event)
10730 {
10731  emit mousePress(event);
10732  mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
10733 
10734  // call event of affected layout element:
10735  mMouseEventElement = layoutElementAt(event->pos());
10736  if (mMouseEventElement)
10737  mMouseEventElement->mousePressEvent(event);
10738 
10739  QWidget::mousePressEvent(event);
10740 }
10741 
10751 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
10752 {
10753  emit mouseMove(event);
10754 
10755  // call event of affected layout element:
10756  if (mMouseEventElement)
10757  mMouseEventElement->mouseMoveEvent(event);
10758 
10759  QWidget::mouseMoveEvent(event);
10760 }
10761 
10776 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
10777 {
10778  emit mouseRelease(event);
10779  bool doReplot = false;
10780 
10781  if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
10782  {
10783  if (event->button() == Qt::LeftButton)
10784  {
10785  // handle selection mechanism:
10786  QVariant details;
10787  QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10788  bool selectionStateChanged = false;
10789  bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
10790  // deselect all other layerables if not additive selection:
10791  if (!additive)
10792  {
10793  foreach (QCPLayer *layer, mLayers)
10794  {
10795  foreach (QCPLayerable *layerable, layer->children())
10796  {
10797  if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
10798  {
10799  bool selChanged = false;
10800  layerable->deselectEvent(&selChanged);
10801  selectionStateChanged |= selChanged;
10802  }
10803  }
10804  }
10805  }
10806  if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10807  {
10808  // a layerable was actually clicked, call its selectEvent:
10809  bool selChanged = false;
10810  clickedLayerable->selectEvent(event, additive, details, &selChanged);
10811  selectionStateChanged |= selChanged;
10812  }
10813  if (selectionStateChanged)
10814  {
10815  doReplot = true;
10816  emit selectionChangedByUser();
10817  }
10818  }
10819 
10820  // emit specialized object click signals:
10821  QVariant details;
10822  QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); // for these signals, selectability is ignored, that's why we call this again with onlySelectable set to false
10823  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10824  emit plottableClick(ap, event);
10825  else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10826  emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10827  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10828  emit itemClick(ai, event);
10829  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10830  emit legendClick(lg, 0, event);
10831  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10832  emit legendClick(li->parentLegend(), li, event);
10833  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10834  emit titleClick(event, pt);
10835  }
10836 
10837  // call event of affected layout element:
10838  if (mMouseEventElement)
10839  {
10840  mMouseEventElement->mouseReleaseEvent(event);
10841  mMouseEventElement = 0;
10842  }
10843 
10844  if (doReplot || noAntialiasingOnDrag())
10845  replot();
10846 
10847  QWidget::mouseReleaseEvent(event);
10848 }
10849 
10856 void QCustomPlot::wheelEvent(QWheelEvent *event)
10857 {
10858  emit mouseWheel(event);
10859 
10860  // call event of affected layout element:
10861  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10862  el->wheelEvent(event);
10863 
10864  QWidget::wheelEvent(event);
10865 }
10866 
10875 {
10876  // run through layout phases:
10880 
10881  // draw viewport background pixmap:
10882  drawBackground(painter);
10883 
10884  // draw all layered objects (grid, axes, plottables, items, legend,...):
10885  foreach (QCPLayer *layer, mLayers)
10886  {
10887  foreach (QCPLayerable *child, layer->children())
10888  {
10889  if (child->realVisibility())
10890  {
10891  painter->save();
10892  painter->setClipRect(child->clipRect().translated(0, -1));
10893  child->applyDefaultAntialiasingHint(painter);
10894  child->draw(painter);
10895  painter->restore();
10896  }
10897  }
10898  }
10899 
10900  /* Debug code to draw all layout element rects
10901  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
10902  {
10903  painter->setBrush(Qt::NoBrush);
10904  painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
10905  painter->drawRect(el->rect());
10906  painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
10907  painter->drawRect(el->outerRect());
10908  }
10909  */
10910 }
10911 
10930 {
10931  // Note: background color is handled in individual replot/save functions
10932 
10933  // draw background pixmap (on top of fill, if brush specified):
10934  if (!mBackgroundPixmap.isNull())
10935  {
10936  if (mBackgroundScaled)
10937  {
10938  // check whether mScaledBackground needs to be updated:
10939  QSize scaledSize(mBackgroundPixmap.size());
10940  scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
10941  if (mScaledBackgroundPixmap.size() != scaledSize)
10942  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
10943  painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
10944  } else
10945  {
10946  painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
10947  }
10948  }
10949 }
10950 
10951 
10958 {
10959  if (xAxis == axis)
10960  xAxis = 0;
10961  if (xAxis2 == axis)
10962  xAxis2 = 0;
10963  if (yAxis == axis)
10964  yAxis = 0;
10965  if (yAxis2 == axis)
10966  yAxis2 = 0;
10967 
10968  // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
10969 }
10970 
10977 {
10978  if (this->legend == legend)
10979  this->legend = 0;
10980 }
10981 
10989 {
10990  for (int i=0; i<mLayers.size(); ++i)
10991  mLayers.at(i)->mIndex = i;
10992 }
10993 
11006 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
11007 {
11008  for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
11009  {
11010  const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
11011  double minimumDistance = selectionTolerance()*1.1;
11012  QCPLayerable *minimumDistanceLayerable = 0;
11013  for (int i=layerables.size()-1; i>=0; --i)
11014  {
11015  if (!layerables.at(i)->realVisibility())
11016  continue;
11017  QVariant details;
11018  double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11019  if (dist >= 0 && dist < minimumDistance)
11020  {
11021  minimumDistance = dist;
11022  minimumDistanceLayerable = layerables.at(i);
11023  if (selectionDetails) *selectionDetails = details;
11024  }
11025  }
11026  if (minimumDistance < selectionTolerance())
11027  return minimumDistanceLayerable;
11028  }
11029  return 0;
11030 }
11031 
11043 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
11044 {
11045  QPixmap buffer = toPixmap(width, height, scale);
11046  if (!buffer.isNull())
11047  return buffer.save(fileName, format, quality);
11048  else
11049  return false;
11050 }
11051 
11060 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
11061 {
11062  // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11063  int newWidth, newHeight;
11064  if (width == 0 || height == 0)
11065  {
11066  newWidth = this->width();
11067  newHeight = this->height();
11068  } else
11069  {
11070  newWidth = width;
11071  newHeight = height;
11072  }
11073  int scaledWidth = qRound(scale*newWidth);
11074  int scaledHeight = qRound(scale*newHeight);
11075 
11076  QPixmap result(scaledWidth, scaledHeight);
11077  result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
11078  QCPPainter painter;
11079  painter.begin(&result);
11080  if (painter.isActive())
11081  {
11082  QRect oldViewport = viewport();
11083  setViewport(QRect(0, 0, newWidth, newHeight));
11085  if (!qFuzzyCompare(scale, 1.0))
11086  {
11087  if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
11089  painter.scale(scale, scale);
11090  }
11091  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill
11092  painter.fillRect(mViewport, mBackgroundBrush);
11093  draw(&painter);
11094  setViewport(oldViewport);
11095  painter.end();
11096  } else // might happen if pixmap has width or height zero
11097  {
11098  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11099  return QPixmap();
11100  }
11101  return result;
11102 }
11103 
11116 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
11117 {
11118  // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11119  int newWidth, newHeight;
11120  if (width == 0 || height == 0)
11121  {
11122  newWidth = this->width();
11123  newHeight = this->height();
11124  } else
11125  {
11126  newWidth = width;
11127  newHeight = height;
11128  }
11129 
11130  if (painter->isActive())
11131  {
11132  QRect oldViewport = viewport();
11133  setViewport(QRect(0, 0, newWidth, newHeight));
11134  painter->setMode(QCPPainter::pmNoCaching);
11135  if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here
11136  painter->fillRect(mViewport, mBackgroundBrush);
11137  draw(painter);
11138  setViewport(oldViewport);
11139  } else
11140  qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11141 }
11142 
11143 
11147 
11179  mLevelCount(350),
11180  mColorInterpolation(ciRGB),
11181  mPeriodic(false),
11182  mColorBufferInvalidated(true)
11183 {
11184  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11185  loadPreset(preset);
11186 }
11187 
11188 /* undocumented operator */
11190 {
11191  return ((other.mLevelCount == this->mLevelCount) &&
11192  (other.mColorInterpolation == this->mColorInterpolation) &&
11193  (other.mPeriodic == this->mPeriodic) &&
11194  (other.mColorStops == this->mColorStops));
11195 }
11196 
11204 {
11205  if (n < 2)
11206  {
11207  qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11208  n = 2;
11209  }
11210  if (n != mLevelCount)
11211  {
11212  mLevelCount = n;
11213  mColorBufferInvalidated = true;
11214  }
11215 }
11216 
11227 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11228 {
11230  mColorBufferInvalidated = true;
11231 }
11232 
11239 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
11240 {
11241  mColorStops.insert(position, color);
11242  mColorBufferInvalidated = true;
11243 }
11244 
11253 {
11254  if (interpolation != mColorInterpolation)
11255  {
11256  mColorInterpolation = interpolation;
11257  mColorBufferInvalidated = true;
11258  }
11259 }
11260 
11277 {
11278  mPeriodic = enabled;
11279 }
11280 
11293 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
11294 {
11295  // If you change something here, make sure to also adapt ::color()
11296  if (!data)
11297  {
11298  qDebug() << Q_FUNC_INFO << "null pointer given as data";
11299  return;
11300  }
11301  if (!scanLine)
11302  {
11303  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11304  return;
11305  }
11308 
11309  if (!logarithmic)
11310  {
11311  const double posToIndexFactor = (mLevelCount-1)/range.size();
11312  if (mPeriodic)
11313  {
11314  for (int i=0; i<n; ++i)
11315  {
11316  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
11317  if (index < 0)
11318  index += mLevelCount;
11319  scanLine[i] = mColorBuffer.at(index);
11320  }
11321  } else
11322  {
11323  for (int i=0; i<n; ++i)
11324  {
11325  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
11326  if (index < 0)
11327  index = 0;
11328  else if (index >= mLevelCount)
11329  index = mLevelCount-1;
11330  scanLine[i] = mColorBuffer.at(index);
11331  }
11332  }
11333  } else // logarithmic == true
11334  {
11335  if (mPeriodic)
11336  {
11337  for (int i=0; i<n; ++i)
11338  {
11339  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
11340  if (index < 0)
11341  index += mLevelCount;
11342  scanLine[i] = mColorBuffer.at(index);
11343  }
11344  } else
11345  {
11346  for (int i=0; i<n; ++i)
11347  {
11348  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11349  if (index < 0)
11350  index = 0;
11351  else if (index >= mLevelCount)
11352  index = mLevelCount-1;
11353  scanLine[i] = mColorBuffer.at(index);
11354  }
11355  }
11356  }
11357 }
11358 
11368 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11369 {
11370  // If you change something here, make sure to also adapt ::colorize()
11373  int index = 0;
11374  if (!logarithmic)
11375  index = (position-range.lower)*(mLevelCount-1)/range.size();
11376  else
11377  index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11378  if (mPeriodic)
11379  {
11380  index = index % mLevelCount;
11381  if (index < 0)
11382  index += mLevelCount;
11383  } else
11384  {
11385  if (index < 0)
11386  index = 0;
11387  else if (index >= mLevelCount)
11388  index = mLevelCount-1;
11389  }
11390  return mColorBuffer.at(index);
11391 }
11392 
11401 {
11402  clearColorStops();
11403  switch (preset)
11404  {
11405  case gpGrayscale:
11407  setColorStopAt(0, Qt::black);
11408  setColorStopAt(1, Qt::white);
11409  break;
11410  case gpHot:
11412  setColorStopAt(0, QColor(50, 0, 0));
11413  setColorStopAt(0.2, QColor(180, 10, 0));
11414  setColorStopAt(0.4, QColor(245, 50, 0));
11415  setColorStopAt(0.6, QColor(255, 150, 10));
11416  setColorStopAt(0.8, QColor(255, 255, 50));
11417  setColorStopAt(1, QColor(255, 255, 255));
11418  break;
11419  case gpCold:
11421  setColorStopAt(0, QColor(0, 0, 50));
11422  setColorStopAt(0.2, QColor(0, 10, 180));
11423  setColorStopAt(0.4, QColor(0, 50, 245));
11424  setColorStopAt(0.6, QColor(10, 150, 255));
11425  setColorStopAt(0.8, QColor(50, 255, 255));
11426  setColorStopAt(1, QColor(255, 255, 255));
11427  break;
11428  case gpNight:
11430  setColorStopAt(0, QColor(10, 20, 30));
11431  setColorStopAt(1, QColor(250, 255, 250));
11432  break;
11433  case gpCandy:
11435  setColorStopAt(0, QColor(0, 0, 255));
11436  setColorStopAt(1, QColor(255, 250, 250));
11437  break;
11438  case gpGeography:
11440  setColorStopAt(0, QColor(70, 170, 210));
11441  setColorStopAt(0.20, QColor(90, 160, 180));
11442  setColorStopAt(0.25, QColor(45, 130, 175));
11443  setColorStopAt(0.30, QColor(100, 140, 125));
11444  setColorStopAt(0.5, QColor(100, 140, 100));
11445  setColorStopAt(0.6, QColor(130, 145, 120));
11446  setColorStopAt(0.7, QColor(140, 130, 120));
11447  setColorStopAt(0.9, QColor(180, 190, 190));
11448  setColorStopAt(1, QColor(210, 210, 230));
11449  break;
11450  case gpIon:
11452  setColorStopAt(0, QColor(50, 10, 10));
11453  setColorStopAt(0.45, QColor(0, 0, 255));
11454  setColorStopAt(0.8, QColor(0, 255, 255));
11455  setColorStopAt(1, QColor(0, 255, 0));
11456  break;
11457  case gpThermal:
11459  setColorStopAt(0, QColor(0, 0, 50));
11460  setColorStopAt(0.15, QColor(20, 0, 120));
11461  setColorStopAt(0.33, QColor(200, 30, 140));
11462  setColorStopAt(0.6, QColor(255, 100, 0));
11463  setColorStopAt(0.85, QColor(255, 255, 40));
11464  setColorStopAt(1, QColor(255, 255, 255));
11465  break;
11466  case gpPolar:
11468  setColorStopAt(0, QColor(50, 255, 255));
11469  setColorStopAt(0.18, QColor(10, 70, 255));
11470  setColorStopAt(0.28, QColor(10, 10, 190));
11471  setColorStopAt(0.5, QColor(0, 0, 0));
11472  setColorStopAt(0.72, QColor(190, 10, 10));
11473  setColorStopAt(0.82, QColor(255, 70, 10));
11474  setColorStopAt(1, QColor(255, 255, 50));
11475  break;
11476  case gpSpectrum:
11478  setColorStopAt(0, QColor(50, 0, 50));
11479  setColorStopAt(0.15, QColor(0, 0, 255));
11480  setColorStopAt(0.35, QColor(0, 255, 255));
11481  setColorStopAt(0.6, QColor(255, 255, 0));
11482  setColorStopAt(0.75, QColor(255, 30, 0));
11483  setColorStopAt(1, QColor(50, 0, 0));
11484  break;
11485  case gpJet:
11487  setColorStopAt(0, QColor(0, 0, 100));
11488  setColorStopAt(0.15, QColor(0, 50, 255));
11489  setColorStopAt(0.35, QColor(0, 255, 255));
11490  setColorStopAt(0.65, QColor(255, 255, 0));
11491  setColorStopAt(0.85, QColor(255, 30, 0));
11492  setColorStopAt(1, QColor(100, 0, 0));
11493  break;
11494  case gpHues:
11496  setColorStopAt(0, QColor(255, 0, 0));
11497  setColorStopAt(1.0/3.0, QColor(0, 0, 255));
11498  setColorStopAt(2.0/3.0, QColor(0, 255, 0));
11499  setColorStopAt(1, QColor(255, 0, 0));
11500  break;
11501  }
11502 }
11503 
11510 {
11511  mColorStops.clear();
11512  mColorBufferInvalidated = true;
11513 }
11514 
11522 {
11523  QCPColorGradient result(*this);
11524  result.clearColorStops();
11525  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
11526  result.setColorStopAt(1.0-it.key(), it.value());
11527  return result;
11528 }
11529 
11536 {
11537  if (mColorBuffer.size() != mLevelCount)
11538  mColorBuffer.resize(mLevelCount);
11539  if (mColorStops.size() > 1)
11540  {
11541  double indexToPosFactor = 1.0/(double)(mLevelCount-1);
11542  for (int i=0; i<mLevelCount; ++i)
11543  {
11544  double position = i*indexToPosFactor;
11545  QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
11546  if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
11547  {
11548  mColorBuffer[i] = (it-1).value().rgb();
11549  } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
11550  {
11551  mColorBuffer[i] = it.value().rgb();
11552  } else // position is in between stops (or on an intermediate stop), interpolate color
11553  {
11554  QMap<double, QColor>::const_iterator high = it;
11555  QMap<double, QColor>::const_iterator low = it-1;
11556  double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
11557  switch (mColorInterpolation)
11558  {
11559  case ciRGB:
11560  {
11561  mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
11562  (1-t)*low.value().green() + t*high.value().green(),
11563  (1-t)*low.value().blue() + t*high.value().blue());
11564  break;
11565  }
11566  case ciHSV:
11567  {
11568  QColor lowHsv = low.value().toHsv();
11569  QColor highHsv = high.value().toHsv();
11570  double hue = 0;
11571  double hueDiff = highHsv.hueF()-lowHsv.hueF();
11572  if (hueDiff > 0.5)
11573  hue = lowHsv.hueF() - t*(1.0-hueDiff);
11574  else if (hueDiff < -0.5)
11575  hue = lowHsv.hueF() + t*(1.0+hueDiff);
11576  else
11577  hue = lowHsv.hueF() + t*hueDiff;
11578  if (hue < 0) hue += 1.0;
11579  else if (hue >= 1.0) hue -= 1.0;
11580  mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
11581  break;
11582  }
11583  }
11584  }
11585  }
11586  } else if (mColorStops.size() == 1)
11587  {
11588  mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11589  } else // mColorStops is empty, fill color buffer with black
11590  {
11591  mColorBuffer.fill(qRgb(0, 0, 0));
11592  }
11593  mColorBufferInvalidated = false;
11594 }
11595 
11596 
11600 
11638 /* start documentation of inline functions */
11639 
11720 /* end documentation of inline functions */
11721 
11726 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
11727  QCPLayoutElement(parentPlot),
11728  mBackgroundBrush(Qt::NoBrush),
11729  mBackgroundScaled(true),
11730  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
11731  mInsetLayout(new QCPLayoutInset),
11732  mRangeDrag(Qt::Horizontal|Qt::Vertical),
11733  mRangeZoom(Qt::Horizontal|Qt::Vertical),
11734  mRangeZoomFactorHorz(0.85),
11735  mRangeZoomFactorVert(0.85),
11736  mDragging(false)
11737 {
11740  mInsetLayout->setParent(this);
11741 
11742  setMinimumSize(50, 50);
11743  setMinimumMargins(QMargins(15, 15, 15, 15));
11744  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
11745  mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
11746  mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
11747  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
11748 
11749  if (setupDefaultAxes)
11750  {
11751  QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
11752  QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
11753  QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
11754  QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
11755  setRangeDragAxes(xAxis, yAxis);
11756  setRangeZoomAxes(xAxis, yAxis);
11757  xAxis2->setVisible(false);
11758  yAxis2->setVisible(false);
11759  xAxis->grid()->setVisible(true);
11760  yAxis->grid()->setVisible(true);
11761  xAxis2->grid()->setVisible(false);
11762  yAxis2->grid()->setVisible(false);
11763  xAxis2->grid()->setZeroLinePen(Qt::NoPen);
11764  yAxis2->grid()->setZeroLinePen(Qt::NoPen);
11765  xAxis2->grid()->setVisible(false);
11766  yAxis2->grid()->setVisible(false);
11767  }
11768 }
11769 
11771 {
11772  delete mInsetLayout;
11773  mInsetLayout = 0;
11774 
11775  QList<QCPAxis*> axesList = axes();
11776  for (int i=0; i<axesList.size(); ++i)
11777  removeAxis(axesList.at(i));
11778 }
11779 
11786 {
11787  return mAxes.value(type).size();
11788 }
11789 
11796 {
11797  QList<QCPAxis*> ax(mAxes.value(type));
11798  if (index >= 0 && index < ax.size())
11799  {
11800  return ax.at(index);
11801  } else
11802  {
11803  qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
11804  return 0;
11805  }
11806 }
11807 
11816 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
11817 {
11818  QList<QCPAxis*> result;
11819  if (types.testFlag(QCPAxis::atLeft))
11820  result << mAxes.value(QCPAxis::atLeft);
11821  if (types.testFlag(QCPAxis::atRight))
11822  result << mAxes.value(QCPAxis::atRight);
11823  if (types.testFlag(QCPAxis::atTop))
11824  result << mAxes.value(QCPAxis::atTop);
11825  if (types.testFlag(QCPAxis::atBottom))
11826  result << mAxes.value(QCPAxis::atBottom);
11827  return result;
11828 }
11829 
11834 QList<QCPAxis*> QCPAxisRect::axes() const
11835 {
11836  QList<QCPAxis*> result;
11837  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11838  while (it.hasNext())
11839  {
11840  it.next();
11841  result << it.value();
11842  }
11843  return result;
11844 }
11845 
11866 {
11867  QCPAxis *newAxis = axis;
11868  if (!newAxis)
11869  {
11870  newAxis = new QCPAxis(this, type);
11871  } else // user provided existing axis instance, do some sanity checks
11872  {
11873  if (newAxis->axisType() != type)
11874  {
11875  qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
11876  return 0;
11877  }
11878  if (newAxis->axisRect() != this)
11879  {
11880  qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
11881  return 0;
11882  }
11883  if (axes().contains(newAxis))
11884  {
11885  qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
11886  return 0;
11887  }
11888  }
11889  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
11890  {
11891  bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
11892  newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
11893  newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
11894  }
11895  mAxes[type].append(newAxis);
11896  return newAxis;
11897 }
11898 
11907 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
11908 {
11909  QList<QCPAxis*> result;
11910  if (types.testFlag(QCPAxis::atLeft))
11911  result << addAxis(QCPAxis::atLeft);
11912  if (types.testFlag(QCPAxis::atRight))
11913  result << addAxis(QCPAxis::atRight);
11914  if (types.testFlag(QCPAxis::atTop))
11915  result << addAxis(QCPAxis::atTop);
11916  if (types.testFlag(QCPAxis::atBottom))
11917  result << addAxis(QCPAxis::atBottom);
11918  return result;
11919 }
11920 
11929 {
11930  // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
11931  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11932  while (it.hasNext())
11933  {
11934  it.next();
11935  if (it.value().contains(axis))
11936  {
11937  mAxes[it.key()].removeOne(axis);
11938  if (qobject_cast<QCustomPlot*>(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot)
11939  parentPlot()->axisRemoved(axis);
11940  delete axis;
11941  return true;
11942  }
11943  }
11944  qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
11945  return false;
11946 }
11947 
11974 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
11975 {
11976  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
11977  if (axisCount(QCPAxis::atBottom) == 0)
11978  xAxis = addAxis(QCPAxis::atBottom);
11979  else
11980  xAxis = axis(QCPAxis::atBottom);
11981 
11982  if (axisCount(QCPAxis::atLeft) == 0)
11983  yAxis = addAxis(QCPAxis::atLeft);
11984  else
11985  yAxis = axis(QCPAxis::atLeft);
11986 
11987  if (axisCount(QCPAxis::atTop) == 0)
11988  xAxis2 = addAxis(QCPAxis::atTop);
11989  else
11990  xAxis2 = axis(QCPAxis::atTop);
11991 
11992  if (axisCount(QCPAxis::atRight) == 0)
11993  yAxis2 = addAxis(QCPAxis::atRight);
11994  else
11995  yAxis2 = axis(QCPAxis::atRight);
11996 
11997  xAxis->setVisible(true);
11998  yAxis->setVisible(true);
11999  xAxis2->setVisible(true);
12000  yAxis2->setVisible(true);
12001  xAxis2->setTickLabels(false);
12002  yAxis2->setTickLabels(false);
12003 
12004  xAxis2->setRange(xAxis->range());
12005  xAxis2->setRangeReversed(xAxis->rangeReversed());
12006  xAxis2->setScaleType(xAxis->scaleType());
12007  xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12008  xAxis2->setTicks(xAxis->ticks());
12009  xAxis2->setAutoTickCount(xAxis->autoTickCount());
12010  xAxis2->setSubTickCount(xAxis->subTickCount());
12011  xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12012  xAxis2->setTickStep(xAxis->tickStep());
12013  xAxis2->setAutoTickStep(xAxis->autoTickStep());
12014  xAxis2->setNumberFormat(xAxis->numberFormat());
12015  xAxis2->setNumberPrecision(xAxis->numberPrecision());
12016  xAxis2->setTickLabelType(xAxis->tickLabelType());
12017  xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12018  xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12019 
12020  yAxis2->setRange(yAxis->range());
12021  yAxis2->setRangeReversed(yAxis->rangeReversed());
12022  yAxis2->setScaleType(yAxis->scaleType());
12023  yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12024  yAxis2->setTicks(yAxis->ticks());
12025  yAxis2->setAutoTickCount(yAxis->autoTickCount());
12026  yAxis2->setSubTickCount(yAxis->subTickCount());
12027  yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12028  yAxis2->setTickStep(yAxis->tickStep());
12029  yAxis2->setAutoTickStep(yAxis->autoTickStep());
12030  yAxis2->setNumberFormat(yAxis->numberFormat());
12031  yAxis2->setNumberPrecision(yAxis->numberPrecision());
12032  yAxis2->setTickLabelType(yAxis->tickLabelType());
12033  yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12034  yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12035 
12036  if (connectRanges)
12037  {
12038  connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
12039  connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
12040  }
12041 }
12042 
12051 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
12052 {
12053  // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
12054  QList<QCPAbstractPlottable*> result;
12055  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
12056  {
12057  if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12058  result.append(mParentPlot->mPlottables.at(i));
12059  }
12060  return result;
12061 }
12062 
12071 QList<QCPGraph*> QCPAxisRect::graphs() const
12072 {
12073  // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
12074  QList<QCPGraph*> result;
12075  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
12076  {
12077  if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12078  result.append(mParentPlot->mGraphs.at(i));
12079  }
12080  return result;
12081 }
12082 
12093 QList<QCPAbstractItem *> QCPAxisRect::items() const
12094 {
12095  // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
12096  // and miss those items that have this axis rect as clipAxisRect.
12097  QList<QCPAbstractItem*> result;
12098  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
12099  {
12100  if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
12101  {
12102  result.append(mParentPlot->mItems.at(itemId));
12103  continue;
12104  }
12105  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
12106  for (int posId=0; posId<positions.size(); ++posId)
12107  {
12108  if (positions.at(posId)->axisRect() == this ||
12109  positions.at(posId)->keyAxis()->axisRect() == this ||
12110  positions.at(posId)->valueAxis()->axisRect() == this)
12111  {
12112  result.append(mParentPlot->mItems.at(itemId));
12113  break;
12114  }
12115  }
12116  }
12117  return result;
12118 }
12119 
12129 {
12130  QCPLayoutElement::update(phase);
12131 
12132  switch (phase)
12133  {
12134  case upPreparation:
12135  {
12136  QList<QCPAxis*> allAxes = axes();
12137  for (int i=0; i<allAxes.size(); ++i)
12138  allAxes.at(i)->setupTickVectors();
12139  break;
12140  }
12141  case upLayout:
12142  {
12144  break;
12145  }
12146  default: break;
12147  }
12148 
12149  // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
12150  mInsetLayout->update(phase);
12151 }
12152 
12153 /* inherits documentation from base class */
12154 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
12155 {
12156  QList<QCPLayoutElement*> result;
12157  if (mInsetLayout)
12158  {
12159  result << mInsetLayout;
12160  if (recursive)
12161  result << mInsetLayout->elements(recursive);
12162  }
12163  return result;
12164 }
12165 
12166 /* inherits documentation from base class */
12168 {
12169  painter->setAntialiasing(false);
12170 }
12171 
12172 /* inherits documentation from base class */
12174 {
12175  drawBackground(painter);
12176 }
12177 
12193 void QCPAxisRect::setBackground(const QPixmap &pm)
12194 {
12195  mBackgroundPixmap = pm;
12196  mScaledBackgroundPixmap = QPixmap();
12197 }
12198 
12212 void QCPAxisRect::setBackground(const QBrush &brush)
12213 {
12214  mBackgroundBrush = brush;
12215 }
12216 
12224 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
12225 {
12226  mBackgroundPixmap = pm;
12227  mScaledBackgroundPixmap = QPixmap();
12228  mBackgroundScaled = scaled;
12229  mBackgroundScaledMode = mode;
12230 }
12231 
12243 {
12244  mBackgroundScaled = scaled;
12245 }
12246 
12252 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12253 {
12254  mBackgroundScaledMode = mode;
12255 }
12256 
12262 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12263 {
12264  return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12265 }
12266 
12272 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12273 {
12274  return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12275 }
12276 
12282 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12283 {
12284  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12285 }
12286 
12303 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12304 {
12305  mRangeDrag = orientations;
12306 }
12307 
12323 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12324 {
12325  mRangeZoom = orientations;
12326 }
12327 
12334 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
12335 {
12336  mRangeDragHorzAxis = horizontal;
12337  mRangeDragVertAxis = vertical;
12338 }
12339 
12347 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
12348 {
12349  mRangeZoomHorzAxis = horizontal;
12350  mRangeZoomVertAxis = vertical;
12351 }
12352 
12363 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12364 {
12365  mRangeZoomFactorHorz = horizontalFactor;
12366  mRangeZoomFactorVert = verticalFactor;
12367 }
12368 
12374 {
12375  mRangeZoomFactorHorz = factor;
12376  mRangeZoomFactorVert = factor;
12377 }
12378 
12398 {
12399  // draw background fill:
12400  if (mBackgroundBrush != Qt::NoBrush)
12401  painter->fillRect(mRect, mBackgroundBrush);
12402 
12403  // draw background pixmap (on top of fill, if brush specified):
12404  if (!mBackgroundPixmap.isNull())
12405  {
12406  if (mBackgroundScaled)
12407  {
12408  // check whether mScaledBackground needs to be updated:
12409  QSize scaledSize(mBackgroundPixmap.size());
12410  scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12411  if (mScaledBackgroundPixmap.size() != scaledSize)
12412  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12413  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
12414  } else
12415  {
12416  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
12417  }
12418  }
12419 }
12420 
12432 {
12433  const QList<QCPAxis*> axesList = mAxes.value(type);
12434  if (axesList.isEmpty())
12435  return;
12436 
12437  bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false
12438  for (int i=1; i<axesList.size(); ++i)
12439  {
12440  int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
12441  if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible)
12442  {
12443  if (!isFirstVisible)
12444  offset += axesList.at(i)->tickLengthIn();
12445  isFirstVisible = false;
12446  }
12447  axesList.at(i)->setOffset(offset);
12448  }
12449 }
12450 
12451 /* inherits documentation from base class */
12453 {
12454  if (!mAutoMargins.testFlag(side))
12455  qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12456 
12458 
12459  // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
12460  const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
12461  if (axesList.size() > 0)
12462  return axesList.last()->offset() + axesList.last()->calculateMargin();
12463  else
12464  return 0;
12465 }
12466 
12478 void QCPAxisRect::mousePressEvent(QMouseEvent *event)
12479 {
12480  mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release)
12481  if (event->buttons() & Qt::LeftButton)
12482  {
12483  mDragging = true;
12484  // initialize antialiasing backup in case we start dragging:
12486  {
12489  }
12490  // Mouse range dragging interaction:
12491  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12492  {
12493  if (mRangeDragHorzAxis)
12494  mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12495  if (mRangeDragVertAxis)
12496  mDragStartVertRange = mRangeDragVertAxis.data()->range();
12497  }
12498  }
12499 }
12500 
12508 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
12509 {
12510  // Mouse range dragging interaction:
12511  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12512  {
12513  if (mRangeDrag.testFlag(Qt::Horizontal))
12514  {
12515  if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
12516  {
12517  if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
12518  {
12519  double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
12520  rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
12521  } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
12522  {
12523  double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
12524  rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
12525  }
12526  }
12527  }
12528  if (mRangeDrag.testFlag(Qt::Vertical))
12529  {
12530  if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
12531  {
12532  if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
12533  {
12534  double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
12535  rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
12536  } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
12537  {
12538  double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
12539  rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
12540  }
12541  }
12542  }
12543  if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
12544  {
12547  mParentPlot->replot();
12548  }
12549  }
12550 }
12551 
12552 /* inherits documentation from base class */
12553 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
12554 {
12555  Q_UNUSED(event)
12556  mDragging = false;
12558  {
12561  }
12562 }
12563 
12578 void QCPAxisRect::wheelEvent(QWheelEvent *event)
12579 {
12580  // Mouse range zooming interaction:
12581  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
12582  {
12583  if (mRangeZoom != 0)
12584  {
12585  double factor;
12586  double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
12587  if (mRangeZoom.testFlag(Qt::Horizontal))
12588  {
12589  factor = qPow(mRangeZoomFactorHorz, wheelSteps);
12590  if (mRangeZoomHorzAxis.data())
12591  mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
12592  }
12593  if (mRangeZoom.testFlag(Qt::Vertical))
12594  {
12595  factor = qPow(mRangeZoomFactorVert, wheelSteps);
12596  if (mRangeZoomVertAxis.data())
12597  mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
12598  }
12599  mParentPlot->replot();
12600  }
12601  }
12602 }
12603 
12604 
12608 
12633 /* start of documentation of signals */
12634 
12641 /* end of documentation of signals */
12642 
12648  QCPLayoutElement(parent->parentPlot()),
12649  mParentLegend(parent),
12650  mFont(parent->font()),
12651  mTextColor(parent->textColor()),
12652  mSelectedFont(parent->selectedFont()),
12653  mSelectedTextColor(parent->selectedTextColor()),
12654  mSelectable(true),
12655  mSelected(false)
12656 {
12657  setLayer(QLatin1String("legend"));
12658  setMargins(QMargins(8, 2, 8, 2));
12659 }
12660 
12666 void QCPAbstractLegendItem::setFont(const QFont &font)
12667 {
12668  mFont = font;
12669 }
12670 
12676 void QCPAbstractLegendItem::setTextColor(const QColor &color)
12677 {
12678  mTextColor = color;
12679 }
12680 
12688 {
12689  mSelectedFont = font;
12690 }
12691 
12699 {
12700  mSelectedTextColor = color;
12701 }
12702 
12709 {
12710  if (mSelectable != selectable)
12711  {
12714  }
12715 }
12716 
12726 {
12727  if (mSelected != selected)
12728  {
12729  mSelected = selected;
12731  }
12732 }
12733 
12734 /* inherits documentation from base class */
12735 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
12736 {
12737  Q_UNUSED(details)
12738  if (!mParentPlot) return -1;
12739  if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
12740  return -1;
12741 
12742  if (mRect.contains(pos.toPoint()))
12743  return mParentPlot->selectionTolerance()*0.99;
12744  else
12745  return -1;
12746 }
12747 
12748 /* inherits documentation from base class */
12750 {
12752 }
12753 
12754 /* inherits documentation from base class */
12756 {
12757  return mOuterRect;
12758 }
12759 
12760 /* inherits documentation from base class */
12761 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12762 {
12763  Q_UNUSED(event)
12764  Q_UNUSED(details)
12766  {
12767  bool selBefore = mSelected;
12768  setSelected(additive ? !mSelected : true);
12769  if (selectionStateChanged)
12770  *selectionStateChanged = mSelected != selBefore;
12771  }
12772 }
12773 
12774 /* inherits documentation from base class */
12775 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
12776 {
12778  {
12779  bool selBefore = mSelected;
12780  setSelected(false);
12781  if (selectionStateChanged)
12782  *selectionStateChanged = mSelected != selBefore;
12783  }
12784 }
12785 
12789 
12824  QCPAbstractLegendItem(parent),
12825  mPlottable(plottable)
12826 {
12827 }
12828 
12835 {
12837 }
12838 
12845 {
12847 }
12848 
12855 {
12856  return mSelected ? mSelectedFont : mFont;
12857 }
12858 
12866 {
12867  if (!mPlottable) return;
12868  painter->setFont(getFont());
12869  painter->setPen(QPen(getTextColor()));
12870  QSizeF iconSize = mParentLegend->iconSize();
12871  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12872  QRectF iconRect(mRect.topLeft(), iconSize);
12873  int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
12874  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
12875  // draw icon:
12876  painter->save();
12877  painter->setClipRect(iconRect, Qt::IntersectClip);
12878  mPlottable->drawLegendIcon(painter, iconRect);
12879  painter->restore();
12880  // draw icon border:
12881  if (getIconBorderPen().style() != Qt::NoPen)
12882  {
12883  painter->setPen(getIconBorderPen());
12884  painter->setBrush(Qt::NoBrush);
12885  painter->drawRect(iconRect);
12886  }
12887 }
12888 
12895 {
12896  if (!mPlottable) return QSize();
12897  QSize result(0, 0);
12898  QRect textRect;
12899  QFontMetrics fontMetrics(getFont());
12900  QSize iconSize = mParentLegend->iconSize();
12901  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12902  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
12903  result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
12904  return result;
12905 }
12906 
12907 
12911 
12936 /* start of documentation of signals */
12937 
12945 /* end of documentation of signals */
12946 
12954 {
12955  setRowSpacing(0);
12956  setColumnSpacing(10);
12957  setMargins(QMargins(2, 3, 2, 2));
12958  setAntialiased(false);
12959  setIconSize(32, 18);
12960 
12961  setIconTextPadding(7);
12962 
12965 
12966  setBorderPen(QPen(Qt::black));
12967  setSelectedBorderPen(QPen(Qt::blue, 2));
12968  setIconBorderPen(Qt::NoPen);
12969  setSelectedIconBorderPen(QPen(Qt::blue, 2));
12970  setBrush(Qt::white);
12971  setSelectedBrush(Qt::white);
12972  setTextColor(Qt::black);
12973  setSelectedTextColor(Qt::blue);
12974 }
12975 
12977 {
12978  clearItems();
12979  if (qobject_cast<QCustomPlot*>(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot)
12980  mParentPlot->legendRemoved(this);
12981 }
12982 
12983 /* no doc for getter, see setSelectedParts */
12984 QCPLegend::SelectableParts QCPLegend::selectedParts() const
12985 {
12986  // check whether any legend elements selected, if yes, add spItems to return value
12987  bool hasSelectedItems = false;
12988  for (int i=0; i<itemCount(); ++i)
12989  {
12990  if (item(i) && item(i)->selected())
12991  {
12992  hasSelectedItems = true;
12993  break;
12994  }
12995  }
12996  if (hasSelectedItems)
12997  return mSelectedParts | spItems;
12998  else
12999  return mSelectedParts & ~spItems;
13000 }
13001 
13005 void QCPLegend::setBorderPen(const QPen &pen)
13006 {
13007  mBorderPen = pen;
13008 }
13009 
13013 void QCPLegend::setBrush(const QBrush &brush)
13014 {
13015  mBrush = brush;
13016 }
13017 
13027 void QCPLegend::setFont(const QFont &font)
13028 {
13029  mFont = font;
13030  for (int i=0; i<itemCount(); ++i)
13031  {
13032  if (item(i))
13033  item(i)->setFont(mFont);
13034  }
13035 }
13036 
13046 void QCPLegend::setTextColor(const QColor &color)
13047 {
13048  mTextColor = color;
13049  for (int i=0; i<itemCount(); ++i)
13050  {
13051  if (item(i))
13052  item(i)->setTextColor(color);
13053  }
13054 }
13055 
13060 void QCPLegend::setIconSize(const QSize &size)
13061 {
13062  mIconSize = size;
13063 }
13064 
13067 void QCPLegend::setIconSize(int width, int height)
13068 {
13069  mIconSize.setWidth(width);
13070  mIconSize.setHeight(height);
13071 }
13072 
13079 {
13080  mIconTextPadding = padding;
13081 }
13082 
13089 void QCPLegend::setIconBorderPen(const QPen &pen)
13090 {
13091  mIconBorderPen = pen;
13092 }
13093 
13104 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13105 {
13106  if (mSelectableParts != selectable)
13107  {
13108  mSelectableParts = selectable;
13110  }
13111 }
13112 
13134 void QCPLegend::setSelectedParts(const SelectableParts &selected)
13135 {
13136  SelectableParts newSelected = selected;
13137  mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
13138 
13139  if (mSelectedParts != newSelected)
13140  {
13141  if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
13142  {
13143  qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
13144  newSelected &= ~spItems;
13145  }
13146  if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
13147  {
13148  for (int i=0; i<itemCount(); ++i)
13149  {
13150  if (item(i))
13151  item(i)->setSelected(false);
13152  }
13153  }
13154  mSelectedParts = newSelected;
13156  }
13157 }
13158 
13165 void QCPLegend::setSelectedBorderPen(const QPen &pen)
13166 {
13167  mSelectedBorderPen = pen;
13168 }
13169 
13176 {
13177  mSelectedIconBorderPen = pen;
13178 }
13179 
13186 void QCPLegend::setSelectedBrush(const QBrush &brush)
13187 {
13189 }
13190 
13198 void QCPLegend::setSelectedFont(const QFont &font)
13199 {
13200  mSelectedFont = font;
13201  for (int i=0; i<itemCount(); ++i)
13202  {
13203  if (item(i))
13204  item(i)->setSelectedFont(font);
13205  }
13206 }
13207 
13215 void QCPLegend::setSelectedTextColor(const QColor &color)
13216 {
13217  mSelectedTextColor = color;
13218  for (int i=0; i<itemCount(); ++i)
13219  {
13220  if (item(i))
13221  item(i)->setSelectedTextColor(color);
13222  }
13223 }
13224 
13231 {
13232  return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13233 }
13234 
13242 {
13243  for (int i=0; i<itemCount(); ++i)
13244  {
13245  if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
13246  {
13247  if (pli->plottable() == plottable)
13248  return pli;
13249  }
13250  }
13251  return 0;
13252 }
13253 
13259 {
13260  return elementCount();
13261 }
13262 
13267 {
13268  for (int i=0; i<itemCount(); ++i)
13269  {
13270  if (item == this->item(i))
13271  return true;
13272  }
13273  return false;
13274 }
13275 
13283 {
13284  return itemWithPlottable(plottable);
13285 }
13286 
13295 {
13296  if (!hasItem(item))
13297  {
13298  return addElement(rowCount(), 0, item);
13299  } else
13300  return false;
13301 }
13302 
13310 bool QCPLegend::removeItem(int index)
13311 {
13312  if (QCPAbstractLegendItem *ali = item(index))
13313  {
13314  bool success = remove(ali);
13315  simplify();
13316  return success;
13317  } else
13318  return false;
13319 }
13320 
13330 {
13331  bool success = remove(item);
13332  simplify();
13333  return success;
13334 }
13335 
13340 {
13341  for (int i=itemCount()-1; i>=0; --i)
13342  removeItem(i);
13343 }
13344 
13351 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
13352 {
13353  QList<QCPAbstractLegendItem*> result;
13354  for (int i=0; i<itemCount(); ++i)
13355  {
13356  if (QCPAbstractLegendItem *ali = item(i))
13357  {
13358  if (ali->selected())
13359  result.append(ali);
13360  }
13361  }
13362  return result;
13363 }
13364 
13379 {
13381 }
13382 
13389 {
13391 }
13392 
13398 QBrush QCPLegend::getBrush() const
13399 {
13400  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13401 }
13402 
13409 {
13410  // draw background rect:
13411  painter->setBrush(getBrush());
13412  painter->setPen(getBorderPen());
13413  painter->drawRect(mOuterRect);
13414 }
13415 
13416 /* inherits documentation from base class */
13417 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13418 {
13419  if (!mParentPlot) return -1;
13420  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
13421  return -1;
13422 
13423  if (mOuterRect.contains(pos.toPoint()))
13424  {
13425  if (details) details->setValue(spLegendBox);
13426  return mParentPlot->selectionTolerance()*0.99;
13427  }
13428  return -1;
13429 }
13430 
13431 /* inherits documentation from base class */
13432 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13433 {
13434  Q_UNUSED(event)
13435  mSelectedParts = selectedParts(); // in case item selection has changed
13436  if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
13437  {
13438  SelectableParts selBefore = mSelectedParts;
13439  setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent)
13440  if (selectionStateChanged)
13441  *selectionStateChanged = mSelectedParts != selBefore;
13442  }
13443 }
13444 
13445 /* inherits documentation from base class */
13446 void QCPLegend::deselectEvent(bool *selectionStateChanged)
13447 {
13448  mSelectedParts = selectedParts(); // in case item selection has changed
13449  if (mSelectableParts.testFlag(spLegendBox))
13450  {
13451  SelectableParts selBefore = mSelectedParts;
13453  if (selectionStateChanged)
13454  *selectionStateChanged = mSelectedParts != selBefore;
13455  }
13456 }
13457 
13458 /* inherits documentation from base class */
13460 {
13461  return QCP::iSelectLegend;
13462 }
13463 
13464 /* inherits documentation from base class */
13466 {
13467  return QCP::iSelectLegend;
13468 }
13469 
13470 /* inherits documentation from base class */
13472 {
13473  Q_UNUSED(parentPlot)
13474 }
13475 
13476 
13480 
13496 /* start documentation of signals */
13497 
13506 /* end documentation of signals */
13507 
13514  QCPLayoutElement(parentPlot),
13515  mFont(QFont(QLatin1String("sans serif"), 13*1.5, QFont::Bold)),
13516  mTextColor(Qt::black),
13517  mSelectedFont(QFont(QLatin1String("sans serif"), 13*1.6, QFont::Bold)),
13518  mSelectedTextColor(Qt::blue),
13519  mSelectable(false),
13520  mSelected(false)
13521 {
13522  if (parentPlot)
13523  {
13524  setLayer(parentPlot->currentLayer());
13525  mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
13526  mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
13527  }
13528  setMargins(QMargins(5, 5, 5, 0));
13529 }
13530 
13535 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
13536  QCPLayoutElement(parentPlot),
13537  mText(text),
13538  mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
13539  mTextColor(Qt::black),
13540  mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
13541  mSelectedTextColor(Qt::blue),
13542  mSelectable(false),
13543  mSelected(false)
13544 {
13545  setLayer(QLatin1String("axes"));
13546  setMargins(QMargins(5, 5, 5, 0));
13547 }
13548 
13554 void QCPPlotTitle::setText(const QString &text)
13555 {
13556  mText = text;
13557 }
13558 
13564 void QCPPlotTitle::setFont(const QFont &font)
13565 {
13566  mFont = font;
13567 }
13568 
13574 void QCPPlotTitle::setTextColor(const QColor &color)
13575 {
13576  mTextColor = color;
13577 }
13578 
13584 void QCPPlotTitle::setSelectedFont(const QFont &font)
13585 {
13586  mSelectedFont = font;
13587 }
13588 
13594 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
13595 {
13596  mSelectedTextColor = color;
13597 }
13598 
13605 void QCPPlotTitle::setSelectable(bool selectable)
13606 {
13607  if (mSelectable != selectable)
13608  {
13611  }
13612 }
13613 
13621 void QCPPlotTitle::setSelected(bool selected)
13622 {
13623  if (mSelected != selected)
13624  {
13625  mSelected = selected;
13627  }
13628 }
13629 
13630 /* inherits documentation from base class */
13632 {
13634 }
13635 
13636 /* inherits documentation from base class */
13638 {
13639  painter->setFont(mainFont());
13640  painter->setPen(QPen(mainTextColor()));
13641  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
13642 }
13643 
13644 /* inherits documentation from base class */
13646 {
13647  QFontMetrics metrics(mFont);
13648  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13649  result.rwidth() += mMargins.left() + mMargins.right();
13650  result.rheight() += mMargins.top() + mMargins.bottom();
13651  return result;
13652 }
13653 
13654 /* inherits documentation from base class */
13656 {
13657  QFontMetrics metrics(mFont);
13658  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13659  result.rheight() += mMargins.top() + mMargins.bottom();
13660  result.setWidth(QWIDGETSIZE_MAX);
13661  return result;
13662 }
13663 
13664 /* inherits documentation from base class */
13665 void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13666 {
13667  Q_UNUSED(event)
13668  Q_UNUSED(details)
13669  if (mSelectable)
13670  {
13671  bool selBefore = mSelected;
13672  setSelected(additive ? !mSelected : true);
13673  if (selectionStateChanged)
13674  *selectionStateChanged = mSelected != selBefore;
13675  }
13676 }
13677 
13678 /* inherits documentation from base class */
13679 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
13680 {
13681  if (mSelectable)
13682  {
13683  bool selBefore = mSelected;
13684  setSelected(false);
13685  if (selectionStateChanged)
13686  *selectionStateChanged = mSelected != selBefore;
13687  }
13688 }
13689 
13690 /* inherits documentation from base class */
13691 double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13692 {
13693  Q_UNUSED(details)
13694  if (onlySelectable && !mSelectable)
13695  return -1;
13696 
13697  if (mTextBoundingRect.contains(pos.toPoint()))
13698  return mParentPlot->selectionTolerance()*0.99;
13699  else
13700  return -1;
13701 }
13702 
13709 {
13710  return mSelected ? mSelectedFont : mFont;
13711 }
13712 
13719 {
13721 }
13722 
13723 
13727 
13766 /* start documentation of inline functions */
13767 
13781 /* end documentation of signals */
13782 /* start documentation of signals */
13783 
13805 /* end documentation of signals */
13806 
13811  QCPLayoutElement(parentPlot),
13812  mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
13813  mDataScaleType(QCPAxis::stLinear),
13814  mBarWidth(20),
13815  mAxisRect(new QCPColorScaleAxisRectPrivate(this))
13816 {
13817  setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used)
13819  setDataRange(QCPRange(0, 6));
13820 }
13821 
13823 {
13824  delete mAxisRect;
13825 }
13826 
13827 /* undocumented getter */
13828 QString QCPColorScale::label() const
13829 {
13830  if (!mColorAxis)
13831  {
13832  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13833  return QString();
13834  }
13835 
13836  return mColorAxis.data()->label();
13837 }
13838 
13839 /* undocumented getter */
13841 {
13842  if (!mAxisRect)
13843  {
13844  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13845  return false;
13846  }
13847 
13848  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
13849  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
13850  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13851 }
13852 
13853 /* undocumented getter */
13855 {
13856  if (!mAxisRect)
13857  {
13858  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13859  return false;
13860  }
13861 
13862  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
13863  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
13864  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13865 }
13866 
13875 {
13876  if (!mAxisRect)
13877  {
13878  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13879  return;
13880  }
13881  if (mType != type)
13882  {
13883  mType = type;
13884  QCPRange rangeTransfer(0, 6);
13885  double logBaseTransfer = 10;
13886  QString labelTransfer;
13887  // revert some settings on old axis:
13888  if (mColorAxis)
13889  {
13890  rangeTransfer = mColorAxis.data()->range();
13891  labelTransfer = mColorAxis.data()->label();
13892  logBaseTransfer = mColorAxis.data()->scaleLogBase();
13893  mColorAxis.data()->setLabel(QString());
13894  disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13895  disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13896  }
13897  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
13898  foreach (QCPAxis::AxisType atype, allAxisTypes)
13899  {
13900  mAxisRect.data()->axis(atype)->setTicks(atype == mType);
13901  mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
13902  }
13903  // set new mColorAxis pointer:
13904  mColorAxis = mAxisRect.data()->axis(mType);
13905  // transfer settings to new axis:
13906  mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
13907  mColorAxis.data()->setLabel(labelTransfer);
13908  mColorAxis.data()->setScaleLogBase(logBaseTransfer); // scaleType is synchronized among axes in realtime via signals (connected in QCPColorScale ctor), so we only need to take care of log base here
13909  connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13910  connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13911  mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
13912  QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
13913  }
13914 }
13915 
13926 {
13927  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
13928  {
13930  if (mColorAxis)
13931  mColorAxis.data()->setRange(mDataRange);
13933  }
13934 }
13935 
13947 {
13948  if (mDataScaleType != scaleType)
13949  {
13950  mDataScaleType = scaleType;
13951  if (mColorAxis)
13952  mColorAxis.data()->setScaleType(mDataScaleType);
13956  }
13957 }
13958 
13967 {
13968  if (mGradient != gradient)
13969  {
13970  mGradient = gradient;
13971  if (mAxisRect)
13972  mAxisRect.data()->mGradientImageInvalidated = true;
13973  emit gradientChanged(mGradient);
13974  }
13975 }
13976 
13981 void QCPColorScale::setLabel(const QString &str)
13982 {
13983  if (!mColorAxis)
13984  {
13985  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13986  return;
13987  }
13988 
13989  mColorAxis.data()->setLabel(str);
13990 }
13991 
13997 {
13998  mBarWidth = width;
13999 }
14000 
14008 {
14009  if (!mAxisRect)
14010  {
14011  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14012  return;
14013  }
14014 
14015  if (enabled)
14016  mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14017  else
14018  mAxisRect.data()->setRangeDrag(0);
14019 }
14020 
14028 {
14029  if (!mAxisRect)
14030  {
14031  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14032  return;
14033  }
14034 
14035  if (enabled)
14036  mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14037  else
14038  mAxisRect.data()->setRangeZoom(0);
14039 }
14040 
14044 QList<QCPColorMap*> QCPColorScale::colorMaps() const
14045 {
14046  QList<QCPColorMap*> result;
14047  for (int i=0; i<mParentPlot->plottableCount(); ++i)
14048  {
14049  if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
14050  if (cm->colorScale() == this)
14051  result.append(cm);
14052  }
14053  return result;
14054 }
14055 
14062 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
14063 {
14064  QList<QCPColorMap*> maps = colorMaps();
14065  QCPRange newRange;
14066  bool haveRange = false;
14067  int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
14069  sign = (mDataRange.upper < 0 ? -1 : 1);
14070  for (int i=0; i<maps.size(); ++i)
14071  {
14072  if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
14073  continue;
14074  QCPRange mapRange;
14075  if (maps.at(i)->colorScale() == this)
14076  {
14077  bool currentFoundRange = true;
14078  mapRange = maps.at(i)->data()->dataBounds();
14079  if (sign == 1)
14080  {
14081  if (mapRange.lower <= 0 && mapRange.upper > 0)
14082  mapRange.lower = mapRange.upper*1e-3;
14083  else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14084  currentFoundRange = false;
14085  } else if (sign == -1)
14086  {
14087  if (mapRange.upper >= 0 && mapRange.lower < 0)
14088  mapRange.upper = mapRange.lower*1e-3;
14089  else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14090  currentFoundRange = false;
14091  }
14092  if (currentFoundRange)
14093  {
14094  if (!haveRange)
14095  newRange = mapRange;
14096  else
14097  newRange.expand(mapRange);
14098  haveRange = true;
14099  }
14100  }
14101  }
14102  if (haveRange)
14103  {
14104  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data
14105  {
14106  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
14108  {
14109  newRange.lower = center-mDataRange.size()/2.0;
14110  newRange.upper = center+mDataRange.size()/2.0;
14111  } else // mScaleType == stLogarithmic
14112  {
14113  newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
14114  newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
14115  }
14116  }
14117  setDataRange(newRange);
14118  }
14119 }
14120 
14121 /* inherits documentation from base class */
14123 {
14124  QCPLayoutElement::update(phase);
14125  if (!mAxisRect)
14126  {
14127  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14128  return;
14129  }
14130 
14131  mAxisRect.data()->update(phase);
14132 
14133  switch (phase)
14134  {
14135  case upMargins:
14136  {
14138  {
14139  setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14140  setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14141  } else
14142  {
14143  setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
14144  setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
14145  }
14146  break;
14147  }
14148  case upLayout:
14149  {
14150  mAxisRect.data()->setOuterRect(rect());
14151  break;
14152  }
14153  default: break;
14154  }
14155 }
14156 
14157 /* inherits documentation from base class */
14159 {
14160  painter->setAntialiasing(false);
14161 }
14162 
14163 /* inherits documentation from base class */
14164 void QCPColorScale::mousePressEvent(QMouseEvent *event)
14165 {
14166  if (!mAxisRect)
14167  {
14168  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14169  return;
14170  }
14171  mAxisRect.data()->mousePressEvent(event);
14172 }
14173 
14174 /* inherits documentation from base class */
14175 void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
14176 {
14177  if (!mAxisRect)
14178  {
14179  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14180  return;
14181  }
14182  mAxisRect.data()->mouseMoveEvent(event);
14183 }
14184 
14185 /* inherits documentation from base class */
14186 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
14187 {
14188  if (!mAxisRect)
14189  {
14190  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14191  return;
14192  }
14193  mAxisRect.data()->mouseReleaseEvent(event);
14194 }
14195 
14196 /* inherits documentation from base class */
14197 void QCPColorScale::wheelEvent(QWheelEvent *event)
14198 {
14199  if (!mAxisRect)
14200  {
14201  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14202  return;
14203  }
14204  mAxisRect.data()->wheelEvent(event);
14205 }
14206 
14210 
14226  QCPAxisRect(parentColorScale->parentPlot(), true),
14227  mParentColorScale(parentColorScale),
14228  mGradientImageInvalidated(true)
14229 {
14230  setParentLayerable(parentColorScale);
14231  setMinimumMargins(QMargins(0, 0, 0, 0));
14232  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14233  foreach (QCPAxis::AxisType type, allAxisTypes)
14234  {
14235  axis(type)->setVisible(true);
14236  axis(type)->grid()->setVisible(false);
14237  axis(type)->setPadding(0);
14238  connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14239  connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14240  }
14241 
14242  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14243  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14244  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14245  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14246  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14247  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14248  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14249  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14250 
14251  // make layer transfers of color scale transfer to axis rect and axes
14252  // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
14253  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
14254  foreach (QCPAxis::AxisType type, allAxisTypes)
14255  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
14256 }
14257 
14263 {
14266 
14267  bool mirrorHorz = false;
14268  bool mirrorVert = false;
14270  {
14271  mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
14272  mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
14273  }
14274 
14275  painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
14276  QCPAxisRect::draw(painter);
14277 }
14278 
14285 {
14286  if (rect().isEmpty())
14287  return;
14288 
14290  int w, h;
14291  QVector<double> data(n);
14292  for (int i=0; i<n; ++i)
14293  data[i] = i;
14295  {
14296  w = n;
14297  h = rect().height();
14298  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14299  QVector<QRgb*> pixels;
14300  for (int y=0; y<h; ++y)
14301  pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
14302  mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
14303  for (int y=1; y<h; ++y)
14304  memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
14305  } else
14306  {
14307  w = rect().width();
14308  h = n;
14309  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14310  for (int y=0; y<h; ++y)
14311  {
14312  QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
14313  const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
14314  for (int x=0; x<w; ++x)
14315  pixels[x] = lineColor;
14316  }
14317  }
14318  mGradientImageInvalidated = false;
14319 }
14320 
14326 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
14327 {
14328  // axis bases of four axes shall always (de-)selected synchronously:
14329  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14330  foreach (QCPAxis::AxisType type, allAxisTypes)
14331  {
14332  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14333  if (senderAxis->axisType() == type)
14334  continue;
14335 
14336  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14337  {
14338  if (selectedParts.testFlag(QCPAxis::spAxis))
14339  axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
14340  else
14341  axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
14342  }
14343  }
14344 }
14345 
14351 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
14352 {
14353  // synchronize axis base selectability:
14354  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14355  foreach (QCPAxis::AxisType type, allAxisTypes)
14356  {
14357  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14358  if (senderAxis->axisType() == type)
14359  continue;
14360 
14361  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14362  {
14363  if (selectableParts.testFlag(QCPAxis::spAxis))
14364  axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
14365  else
14366  axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
14367  }
14368  }
14369 }
14370 
14371 
14375 
14396  key(0),
14397  value(0),
14398  keyErrorPlus(0),
14399  keyErrorMinus(0),
14400  valueErrorPlus(0),
14401  valueErrorMinus(0)
14402 {
14403 }
14404 
14408 QCPData::QCPData(double key, double value) :
14409  key(key),
14410  value(value),
14411  keyErrorPlus(0),
14412  keyErrorMinus(0),
14413  valueErrorPlus(0),
14414  valueErrorMinus(0)
14415 {
14416 }
14417 
14418 
14422 
14461 /* start of documentation of inline functions */
14462 
14470 /* end of documentation of inline functions */
14471 
14483 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
14484  QCPAbstractPlottable(keyAxis, valueAxis)
14485 {
14486  mData = new QCPDataMap;
14487 
14488  setPen(QPen(Qt::blue, 0));
14489  setErrorPen(QPen(Qt::black));
14490  setBrush(Qt::NoBrush);
14491  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14492  setSelectedBrush(Qt::NoBrush);
14493 
14496  setErrorBarSize(6);
14497  setErrorBarSkipSymbol(true);
14499  setAdaptiveSampling(true);
14500 }
14501 
14503 {
14504  delete mData;
14505 }
14506 
14517 void QCPGraph::setData(QCPDataMap *data, bool copy)
14518 {
14519  if (mData == data)
14520  {
14521  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
14522  return;
14523  }
14524  if (copy)
14525  {
14526  *mData = *data;
14527  } else
14528  {
14529  delete mData;
14530  mData = data;
14531  }
14532 }
14533 
14540 void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
14541 {
14542  mData->clear();
14543  int n = key.size();
14544  n = qMin(n, value.size());
14545  QCPData newData;
14546  for (int i=0; i<n; ++i)
14547  {
14548  newData.key = key[i];
14549  newData.value = value[i];
14550  mData->insertMulti(newData.key, newData);
14551  }
14552 }
14553 
14563 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
14564 {
14565  mData->clear();
14566  int n = key.size();
14567  n = qMin(n, value.size());
14568  n = qMin(n, valueError.size());
14569  QCPData newData;
14570  for (int i=0; i<n; ++i)
14571  {
14572  newData.key = key[i];
14573  newData.value = value[i];
14574  newData.valueErrorMinus = valueError[i];
14575  newData.valueErrorPlus = valueError[i];
14576  mData->insertMulti(key[i], newData);
14577  }
14578 }
14579 
14589 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14590 {
14591  mData->clear();
14592  int n = key.size();
14593  n = qMin(n, value.size());
14594  n = qMin(n, valueErrorMinus.size());
14595  n = qMin(n, valueErrorPlus.size());
14596  QCPData newData;
14597  for (int i=0; i<n; ++i)
14598  {
14599  newData.key = key[i];
14600  newData.value = value[i];
14601  newData.valueErrorMinus = valueErrorMinus[i];
14602  newData.valueErrorPlus = valueErrorPlus[i];
14603  mData->insertMulti(key[i], newData);
14604  }
14605 }
14606 
14616 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
14617 {
14618  mData->clear();
14619  int n = key.size();
14620  n = qMin(n, value.size());
14621  n = qMin(n, keyError.size());
14622  QCPData newData;
14623  for (int i=0; i<n; ++i)
14624  {
14625  newData.key = key[i];
14626  newData.value = value[i];
14627  newData.keyErrorMinus = keyError[i];
14628  newData.keyErrorPlus = keyError[i];
14629  mData->insertMulti(key[i], newData);
14630  }
14631 }
14632 
14642 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
14643 {
14644  mData->clear();
14645  int n = key.size();
14646  n = qMin(n, value.size());
14647  n = qMin(n, keyErrorMinus.size());
14648  n = qMin(n, keyErrorPlus.size());
14649  QCPData newData;
14650  for (int i=0; i<n; ++i)
14651  {
14652  newData.key = key[i];
14653  newData.value = value[i];
14654  newData.keyErrorMinus = keyErrorMinus[i];
14655  newData.keyErrorPlus = keyErrorPlus[i];
14656  mData->insertMulti(key[i], newData);
14657  }
14658 }
14659 
14669 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
14670 {
14671  mData->clear();
14672  int n = key.size();
14673  n = qMin(n, value.size());
14674  n = qMin(n, valueError.size());
14675  n = qMin(n, keyError.size());
14676  QCPData newData;
14677  for (int i=0; i<n; ++i)
14678  {
14679  newData.key = key[i];
14680  newData.value = value[i];
14681  newData.keyErrorMinus = keyError[i];
14682  newData.keyErrorPlus = keyError[i];
14683  newData.valueErrorMinus = valueError[i];
14684  newData.valueErrorPlus = valueError[i];
14685  mData->insertMulti(key[i], newData);
14686  }
14687 }
14688 
14698 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14699 {
14700  mData->clear();
14701  int n = key.size();
14702  n = qMin(n, value.size());
14703  n = qMin(n, valueErrorMinus.size());
14704  n = qMin(n, valueErrorPlus.size());
14705  n = qMin(n, keyErrorMinus.size());
14706  n = qMin(n, keyErrorPlus.size());
14707  QCPData newData;
14708  for (int i=0; i<n; ++i)
14709  {
14710  newData.key = key[i];
14711  newData.value = value[i];
14712  newData.keyErrorMinus = keyErrorMinus[i];
14713  newData.keyErrorPlus = keyErrorPlus[i];
14714  newData.valueErrorMinus = valueErrorMinus[i];
14715  newData.valueErrorPlus = valueErrorPlus[i];
14716  mData->insertMulti(key[i], newData);
14717  }
14718 }
14719 
14720 
14728 {
14729  mLineStyle = ls;
14730 }
14731 
14739 {
14740  mScatterStyle = style;
14741 }
14742 
14752 {
14754 }
14755 
14760 void QCPGraph::setErrorPen(const QPen &pen)
14761 {
14762  mErrorPen = pen;
14763 }
14764 
14768 void QCPGraph::setErrorBarSize(double size)
14769 {
14770  mErrorBarSize = size;
14771 }
14772 
14785 {
14786  mErrorBarSkipSymbol = enabled;
14787 }
14788 
14799 {
14800  // prevent setting channel target to this graph itself:
14801  if (targetGraph == this)
14802  {
14803  qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
14804  mChannelFillGraph = 0;
14805  return;
14806  }
14807  // prevent setting channel target to a graph not in the plot:
14808  if (targetGraph && targetGraph->mParentPlot != mParentPlot)
14809  {
14810  qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
14811  mChannelFillGraph = 0;
14812  return;
14813  }
14814 
14815  mChannelFillGraph = targetGraph;
14816 }
14817 
14850 {
14851  mAdaptiveSampling = enabled;
14852 }
14853 
14862 void QCPGraph::addData(const QCPDataMap &dataMap)
14863 {
14864  mData->unite(dataMap);
14865 }
14866 
14875 void QCPGraph::addData(const QCPData &data)
14876 {
14877  mData->insertMulti(data.key, data);
14878 }
14879 
14888 void QCPGraph::addData(double key, double value)
14889 {
14890  QCPData newData;
14891  newData.key = key;
14892  newData.value = value;
14893  mData->insertMulti(newData.key, newData);
14894 }
14895 
14904 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
14905 {
14906  int n = qMin(keys.size(), values.size());
14907  QCPData newData;
14908  for (int i=0; i<n; ++i)
14909  {
14910  newData.key = keys[i];
14911  newData.value = values[i];
14912  mData->insertMulti(newData.key, newData);
14913  }
14914 }
14915 
14921 {
14922  QCPDataMap::iterator it = mData->begin();
14923  while (it != mData->end() && it.key() < key)
14924  it = mData->erase(it);
14925 }
14926 
14932 {
14933  if (mData->isEmpty()) return;
14934  QCPDataMap::iterator it = mData->upperBound(key);
14935  while (it != mData->end())
14936  it = mData->erase(it);
14937 }
14938 
14946 void QCPGraph::removeData(double fromKey, double toKey)
14947 {
14948  if (fromKey >= toKey || mData->isEmpty()) return;
14949  QCPDataMap::iterator it = mData->upperBound(fromKey);
14950  QCPDataMap::iterator itEnd = mData->upperBound(toKey);
14951  while (it != itEnd)
14952  it = mData->erase(it);
14953 }
14954 
14963 void QCPGraph::removeData(double key)
14964 {
14965  mData->remove(key);
14966 }
14967 
14973 {
14974  mData->clear();
14975 }
14976 
14977 /* inherits documentation from base class */
14978 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
14979 {
14980  Q_UNUSED(details)
14981  if ((onlySelectable && !mSelectable) || mData->isEmpty())
14982  return -1;
14983  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
14984 
14985  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
14986  return pointDistance(pos);
14987  else
14988  return -1;
14989 }
14990 
14998 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
14999 {
15000  rescaleKeyAxis(onlyEnlarge, includeErrorBars);
15001  rescaleValueAxis(onlyEnlarge, includeErrorBars);
15002 }
15003 
15011 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
15012 {
15013  // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
15014  // that getKeyRange is passed the includeErrorBars value.
15015  if (mData->isEmpty()) return;
15016 
15017  QCPAxis *keyAxis = mKeyAxis.data();
15018  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15019 
15020  SignDomain signDomain = sdBoth;
15021  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
15022  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15023 
15024  bool foundRange;
15025  QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15026 
15027  if (foundRange)
15028  {
15029  if (onlyEnlarge)
15030  {
15031  if (keyAxis->range().lower < newRange.lower)
15032  newRange.lower = keyAxis->range().lower;
15033  if (keyAxis->range().upper > newRange.upper)
15034  newRange.upper = keyAxis->range().upper;
15035  }
15036  keyAxis->setRange(newRange);
15037  }
15038 }
15039 
15047 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
15048 {
15049  // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
15050  // is that getValueRange is passed the includeErrorBars value.
15051  if (mData->isEmpty()) return;
15052 
15053  QCPAxis *valueAxis = mValueAxis.data();
15054  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
15055 
15056  SignDomain signDomain = sdBoth;
15057  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
15058  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15059 
15060  bool foundRange;
15061  QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15062 
15063  if (foundRange)
15064  {
15065  if (onlyEnlarge)
15066  {
15067  if (valueAxis->range().lower < newRange.lower)
15068  newRange.lower = valueAxis->range().lower;
15069  if (valueAxis->range().upper > newRange.upper)
15070  newRange.upper = valueAxis->range().upper;
15071  }
15072  valueAxis->setRange(newRange);
15073  }
15074 }
15075 
15076 /* inherits documentation from base class */
15078 {
15079  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15080  if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15081  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15082 
15083  // allocate line and (if necessary) point vectors:
15084  QVector<QPointF> *lineData = new QVector<QPointF>;
15085  QVector<QCPData> *scatterData = 0;
15086  if (!mScatterStyle.isNone())
15087  scatterData = new QVector<QCPData>;
15088 
15089  // fill vectors with data appropriate to plot style:
15090  getPlotData(lineData, scatterData);
15091 
15092  // check data validity if flag set:
15093 #ifdef QCUSTOMPLOT_CHECK_DATA
15094  QCPDataMap::const_iterator it;
15095  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
15096  {
15097  if (QCP::isInvalidData(it.value().key, it.value().value) ||
15098  QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15099  QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
15100  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
15101  }
15102 #endif
15103 
15104  // draw fill of graph:
15105  if (mLineStyle != lsNone)
15106  drawFill(painter, lineData);
15107 
15108  // draw line:
15109  if (mLineStyle == lsImpulse)
15110  drawImpulsePlot(painter, lineData);
15111  else if (mLineStyle != lsNone)
15112  drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15113 
15114  // draw scatters:
15115  if (scatterData)
15116  drawScatterPlot(painter, scatterData);
15117 
15118  // free allocated line and point vectors:
15119  delete lineData;
15120  if (scatterData)
15121  delete scatterData;
15122 }
15123 
15124 /* inherits documentation from base class */
15125 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
15126 {
15127  // draw fill:
15128  if (mBrush.style() != Qt::NoBrush)
15129  {
15130  applyFillAntialiasingHint(painter);
15131  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
15132  }
15133  // draw line vertically centered:
15134  if (mLineStyle != lsNone)
15135  {
15137  painter->setPen(mPen);
15138  painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
15139  }
15140  // draw scatter symbol:
15141  if (!mScatterStyle.isNone())
15142  {
15144  // scale scatter pixmap if it's too large to fit in legend icon rect:
15145  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
15146  {
15147  QCPScatterStyle scaledStyle(mScatterStyle);
15148  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15149  scaledStyle.applyTo(painter, mPen);
15150  scaledStyle.drawShape(painter, QRectF(rect).center());
15151  } else
15152  {
15153  mScatterStyle.applyTo(painter, mPen);
15154  mScatterStyle.drawShape(painter, QRectF(rect).center());
15155  }
15156  }
15157 }
15158 
15177 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
15178 {
15179  switch(mLineStyle)
15180  {
15181  case lsNone: getScatterPlotData(scatterData); break;
15182  case lsLine: getLinePlotData(lineData, scatterData); break;
15183  case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
15184  case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
15185  case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
15186  case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
15187  }
15188 }
15189 
15201 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15202 {
15203  getPreparedData(0, scatterData);
15204 }
15205 
15217 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15218 {
15219  QCPAxis *keyAxis = mKeyAxis.data();
15220  QCPAxis *valueAxis = mValueAxis.data();
15221  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15222  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15223 
15224  QVector<QCPData> lineData;
15225  getPreparedData(&lineData, scatterData);
15226  linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15227  linePixelData->resize(lineData.size());
15228 
15229  // transform lineData points to pixels:
15230  if (keyAxis->orientation() == Qt::Vertical)
15231  {
15232  for (int i=0; i<lineData.size(); ++i)
15233  {
15234  (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15235  (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15236  }
15237  } else // key axis is horizontal
15238  {
15239  for (int i=0; i<lineData.size(); ++i)
15240  {
15241  (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15242  (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15243  }
15244  }
15245 }
15246 
15258 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15259 {
15260  QCPAxis *keyAxis = mKeyAxis.data();
15261  QCPAxis *valueAxis = mValueAxis.data();
15262  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15263  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15264 
15265  QVector<QCPData> lineData;
15266  getPreparedData(&lineData, scatterData);
15267  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15268  linePixelData->resize(lineData.size()*2);
15269 
15270  // calculate steps from lineData and transform to pixel coordinates:
15271  if (keyAxis->orientation() == Qt::Vertical)
15272  {
15273  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15274  double key;
15275  for (int i=0; i<lineData.size(); ++i)
15276  {
15277  key = keyAxis->coordToPixel(lineData.at(i).key);
15278  (*linePixelData)[i*2+0].setX(lastValue);
15279  (*linePixelData)[i*2+0].setY(key);
15280  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15281  (*linePixelData)[i*2+1].setX(lastValue);
15282  (*linePixelData)[i*2+1].setY(key);
15283  }
15284  } else // key axis is horizontal
15285  {
15286  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15287  double key;
15288  for (int i=0; i<lineData.size(); ++i)
15289  {
15290  key = keyAxis->coordToPixel(lineData.at(i).key);
15291  (*linePixelData)[i*2+0].setX(key);
15292  (*linePixelData)[i*2+0].setY(lastValue);
15293  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15294  (*linePixelData)[i*2+1].setX(key);
15295  (*linePixelData)[i*2+1].setY(lastValue);
15296  }
15297  }
15298 }
15299 
15311 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15312 {
15313  QCPAxis *keyAxis = mKeyAxis.data();
15314  QCPAxis *valueAxis = mValueAxis.data();
15315  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15316  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15317 
15318  QVector<QCPData> lineData;
15319  getPreparedData(&lineData, scatterData);
15320  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15321  linePixelData->resize(lineData.size()*2);
15322 
15323  // calculate steps from lineData and transform to pixel coordinates:
15324  if (keyAxis->orientation() == Qt::Vertical)
15325  {
15326  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15327  double value;
15328  for (int i=0; i<lineData.size(); ++i)
15329  {
15330  value = valueAxis->coordToPixel(lineData.at(i).value);
15331  (*linePixelData)[i*2+0].setX(value);
15332  (*linePixelData)[i*2+0].setY(lastKey);
15333  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15334  (*linePixelData)[i*2+1].setX(value);
15335  (*linePixelData)[i*2+1].setY(lastKey);
15336  }
15337  } else // key axis is horizontal
15338  {
15339  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15340  double value;
15341  for (int i=0; i<lineData.size(); ++i)
15342  {
15343  value = valueAxis->coordToPixel(lineData.at(i).value);
15344  (*linePixelData)[i*2+0].setX(lastKey);
15345  (*linePixelData)[i*2+0].setY(value);
15346  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15347  (*linePixelData)[i*2+1].setX(lastKey);
15348  (*linePixelData)[i*2+1].setY(value);
15349  }
15350  }
15351 }
15352 
15364 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15365 {
15366  QCPAxis *keyAxis = mKeyAxis.data();
15367  QCPAxis *valueAxis = mValueAxis.data();
15368  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15369  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15370 
15371  QVector<QCPData> lineData;
15372  getPreparedData(&lineData, scatterData);
15373  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15374  linePixelData->resize(lineData.size()*2);
15375  // calculate steps from lineData and transform to pixel coordinates:
15376  if (keyAxis->orientation() == Qt::Vertical)
15377  {
15378  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15379  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15380  double key;
15381  (*linePixelData)[0].setX(lastValue);
15382  (*linePixelData)[0].setY(lastKey);
15383  for (int i=1; i<lineData.size(); ++i)
15384  {
15385  key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15386  (*linePixelData)[i*2-1].setX(lastValue);
15387  (*linePixelData)[i*2-1].setY(key);
15388  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15389  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15390  (*linePixelData)[i*2+0].setX(lastValue);
15391  (*linePixelData)[i*2+0].setY(key);
15392  }
15393  (*linePixelData)[lineData.size()*2-1].setX(lastValue);
15394  (*linePixelData)[lineData.size()*2-1].setY(lastKey);
15395  } else // key axis is horizontal
15396  {
15397  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15398  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15399  double key;
15400  (*linePixelData)[0].setX(lastKey);
15401  (*linePixelData)[0].setY(lastValue);
15402  for (int i=1; i<lineData.size(); ++i)
15403  {
15404  key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15405  (*linePixelData)[i*2-1].setX(key);
15406  (*linePixelData)[i*2-1].setY(lastValue);
15407  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15408  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15409  (*linePixelData)[i*2+0].setX(key);
15410  (*linePixelData)[i*2+0].setY(lastValue);
15411  }
15412  (*linePixelData)[lineData.size()*2-1].setX(lastKey);
15413  (*linePixelData)[lineData.size()*2-1].setY(lastValue);
15414  }
15415 
15416 }
15417 
15429 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15430 {
15431  QCPAxis *keyAxis = mKeyAxis.data();
15432  QCPAxis *valueAxis = mValueAxis.data();
15433  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15434  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15435 
15436  QVector<QCPData> lineData;
15437  getPreparedData(&lineData, scatterData);
15438  linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
15439 
15440  // transform lineData points to pixels:
15441  if (keyAxis->orientation() == Qt::Vertical)
15442  {
15443  double zeroPointX = valueAxis->coordToPixel(0);
15444  double key;
15445  for (int i=0; i<lineData.size(); ++i)
15446  {
15447  key = keyAxis->coordToPixel(lineData.at(i).key);
15448  (*linePixelData)[i*2+0].setX(zeroPointX);
15449  (*linePixelData)[i*2+0].setY(key);
15450  (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
15451  (*linePixelData)[i*2+1].setY(key);
15452  }
15453  } else // key axis is horizontal
15454  {
15455  double zeroPointY = valueAxis->coordToPixel(0);
15456  double key;
15457  for (int i=0; i<lineData.size(); ++i)
15458  {
15459  key = keyAxis->coordToPixel(lineData.at(i).key);
15460  (*linePixelData)[i*2+0].setX(key);
15461  (*linePixelData)[i*2+0].setY(zeroPointY);
15462  (*linePixelData)[i*2+1].setX(key);
15463  (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
15464  }
15465  }
15466 }
15467 
15481 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
15482 {
15483  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
15484  if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
15485 
15486  applyFillAntialiasingHint(painter);
15487  if (!mChannelFillGraph)
15488  {
15489  // draw base fill under graph, fill goes all the way to the zero-value-line:
15490  addFillBasePoints(lineData);
15491  painter->setPen(Qt::NoPen);
15492  painter->setBrush(mainBrush());
15493  painter->drawPolygon(QPolygonF(*lineData));
15494  removeFillBasePoints(lineData);
15495  } else
15496  {
15497  // draw channel fill between this graph and mChannelFillGraph:
15498  painter->setPen(Qt::NoPen);
15499  painter->setBrush(mainBrush());
15500  painter->drawPolygon(getChannelFillPolygon(lineData));
15501  }
15502 }
15503 
15513 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
15514 {
15515  QCPAxis *keyAxis = mKeyAxis.data();
15516  QCPAxis *valueAxis = mValueAxis.data();
15517  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15518 
15519  // draw error bars:
15520  if (mErrorType != etNone)
15521  {
15523  painter->setPen(mErrorPen);
15524  if (keyAxis->orientation() == Qt::Vertical)
15525  {
15526  for (int i=0; i<scatterData->size(); ++i)
15527  drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
15528  } else
15529  {
15530  for (int i=0; i<scatterData->size(); ++i)
15531  drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
15532  }
15533  }
15534 
15535  // draw scatter point symbols:
15537  mScatterStyle.applyTo(painter, mPen);
15538  if (keyAxis->orientation() == Qt::Vertical)
15539  {
15540  for (int i=0; i<scatterData->size(); ++i)
15541  if (!qIsNaN(scatterData->at(i).value))
15542  mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
15543  } else
15544  {
15545  for (int i=0; i<scatterData->size(); ++i)
15546  if (!qIsNaN(scatterData->at(i).value))
15547  mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
15548  }
15549 }
15550 
15560 void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15561 {
15562  // draw line of graph:
15563  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15564  {
15566  painter->setPen(mainPen());
15567  painter->setBrush(Qt::NoBrush);
15568 
15569  /* Draws polyline in batches, currently not used:
15570  int p = 0;
15571  while (p < lineData->size())
15572  {
15573  int batch = qMin(25, lineData->size()-p);
15574  if (p != 0)
15575  {
15576  ++batch;
15577  --p; // to draw the connection lines between two batches
15578  }
15579  painter->drawPolyline(lineData->constData()+p, batch);
15580  p += batch;
15581  }
15582  */
15583 
15584  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
15585  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
15586  painter->pen().style() == Qt::SolidLine &&
15587  !painter->modes().testFlag(QCPPainter::pmVectorized) &&
15588  !painter->modes().testFlag(QCPPainter::pmNoCaching))
15589  {
15590  int i = 0;
15591  bool lastIsNan = false;
15592  const int lineDataSize = lineData->size();
15593  while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
15594  ++i;
15595  ++i; // because drawing works in 1 point retrospect
15596  while (i < lineDataSize)
15597  {
15598  if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
15599  {
15600  if (!lastIsNan)
15601  painter->drawLine(lineData->at(i-1), lineData->at(i));
15602  else
15603  lastIsNan = false;
15604  } else
15605  lastIsNan = true;
15606  ++i;
15607  }
15608  } else
15609  {
15610  int segmentStart = 0;
15611  int i = 0;
15612  const int lineDataSize = lineData->size();
15613  while (i < lineDataSize)
15614  {
15615  if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()) || qIsInf(lineData->at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block
15616  {
15617  painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
15618  segmentStart = i+1;
15619  }
15620  ++i;
15621  }
15622  // draw last segment:
15623  painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
15624  }
15625  }
15626 }
15627 
15635 void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15636 {
15637  // draw impulses:
15638  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15639  {
15641  QPen pen = mainPen();
15642  pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
15643  painter->setPen(pen);
15644  painter->setBrush(Qt::NoBrush);
15645  painter->drawLines(*lineData);
15646  }
15647 }
15648 
15661 void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
15662 {
15663  QCPAxis *keyAxis = mKeyAxis.data();
15664  QCPAxis *valueAxis = mValueAxis.data();
15665  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15666  // get visible data range:
15667  QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
15668  getVisibleDataBounds(lower, upper);
15669  if (lower == mData->constEnd() || upper == mData->constEnd())
15670  return;
15671 
15672  // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
15673  int maxCount = std::numeric_limits<int>::max();
15674  if (mAdaptiveSampling)
15675  {
15676  int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
15677  maxCount = 2*keyPixelSpan+2;
15678  }
15679  int dataCount = countDataInBounds(lower, upper, maxCount);
15680 
15681  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
15682  {
15683  if (lineData)
15684  {
15685  QCPDataMap::const_iterator it = lower;
15686  QCPDataMap::const_iterator upperEnd = upper+1;
15687  double minValue = it.value().value;
15688  double maxValue = it.value().value;
15689  QCPDataMap::const_iterator currentIntervalFirstPoint = it;
15690  int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15691  int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15692  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15693  double lastIntervalEndKey = currentIntervalStartKey;
15694  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15695  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15696  int intervalDataCount = 1;
15697  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15698  while (it != upperEnd)
15699  {
15700  if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
15701  {
15702  if (it.value().value < minValue)
15703  minValue = it.value().value;
15704  else if (it.value().value > maxValue)
15705  maxValue = it.value().value;
15706  ++intervalDataCount;
15707  } else // new pixel interval started
15708  {
15709  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15710  {
15711  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
15712  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15713  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15714  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15715  if (it.key() > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point
15716  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
15717  } else
15718  lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15719  lastIntervalEndKey = (it-1).value().key;
15720  minValue = it.value().value;
15721  maxValue = it.value().value;
15722  currentIntervalFirstPoint = it;
15723  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15724  if (keyEpsilonVariable)
15725  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15726  intervalDataCount = 1;
15727  }
15728  ++it;
15729  }
15730  // handle last interval:
15731  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15732  {
15733  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
15734  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15735  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15736  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15737  } else
15738  lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15739  }
15740 
15741  if (scatterData)
15742  {
15743  double valueMaxRange = valueAxis->range().upper;
15744  double valueMinRange = valueAxis->range().lower;
15745  QCPDataMap::const_iterator it = lower;
15746  QCPDataMap::const_iterator upperEnd = upper+1;
15747  double minValue = it.value().value;
15748  double maxValue = it.value().value;
15749  QCPDataMap::const_iterator minValueIt = it;
15750  QCPDataMap::const_iterator maxValueIt = it;
15751  QCPDataMap::const_iterator currentIntervalStart = it;
15752  int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15753  int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15754  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15755  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15756  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15757  int intervalDataCount = 1;
15758  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15759  while (it != upperEnd)
15760  {
15761  if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
15762  {
15763  if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15764  {
15765  minValue = it.value().value;
15766  minValueIt = it;
15767  } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15768  {
15769  maxValue = it.value().value;
15770  maxValueIt = it;
15771  }
15772  ++intervalDataCount;
15773  } else // new pixel started
15774  {
15775  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15776  {
15777  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15778  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15779  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15780  QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15781  int c = 0;
15782  while (intervalIt != it)
15783  {
15784  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15785  scatterData->append(intervalIt.value());
15786  ++c;
15787  ++intervalIt;
15788  }
15789  } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15790  scatterData->append(currentIntervalStart.value());
15791  minValue = it.value().value;
15792  maxValue = it.value().value;
15793  currentIntervalStart = it;
15794  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15795  if (keyEpsilonVariable)
15796  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15797  intervalDataCount = 1;
15798  }
15799  ++it;
15800  }
15801  // handle last interval:
15802  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15803  {
15804  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15805  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15806  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15807  QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15808  int c = 0;
15809  while (intervalIt != it)
15810  {
15811  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15812  scatterData->append(intervalIt.value());
15813  ++c;
15814  ++intervalIt;
15815  }
15816  } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15817  scatterData->append(currentIntervalStart.value());
15818  }
15819  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
15820  {
15821  QVector<QCPData> *dataVector = 0;
15822  if (lineData)
15823  dataVector = lineData;
15824  else if (scatterData)
15825  dataVector = scatterData;
15826  if (dataVector)
15827  {
15828  QCPDataMap::const_iterator it = lower;
15829  QCPDataMap::const_iterator upperEnd = upper+1;
15830  dataVector->reserve(dataCount+2); // +2 for possible fill end points
15831  while (it != upperEnd)
15832  {
15833  dataVector->append(it.value());
15834  ++it;
15835  }
15836  }
15837  if (lineData && scatterData)
15838  *scatterData = *dataVector;
15839  }
15840 }
15841 
15849 void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
15850 {
15851  if (qIsNaN(data.value))
15852  return;
15853  QCPAxis *keyAxis = mKeyAxis.data();
15854  QCPAxis *valueAxis = mValueAxis.data();
15855  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15856 
15857  double a, b; // positions of error bar bounds in pixels
15858  double barWidthHalf = mErrorBarSize*0.5;
15859  double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
15860 
15861  if (keyAxis->orientation() == Qt::Vertical)
15862  {
15863  // draw key error vertically and value error horizontally
15864  if (mErrorType == etKey || mErrorType == etBoth)
15865  {
15866  a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15867  b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15868  if (keyAxis->rangeReversed())
15869  qSwap(a,b);
15870  // draw spine:
15871  if (mErrorBarSkipSymbol)
15872  {
15873  if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15874  painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15875  if (y-b > skipSymbolMargin)
15876  painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15877  } else
15878  painter->drawLine(QLineF(x, a, x, b));
15879  // draw handles:
15880  painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15881  painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15882  }
15883  if (mErrorType == etValue || mErrorType == etBoth)
15884  {
15885  a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15886  b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15887  if (valueAxis->rangeReversed())
15888  qSwap(a,b);
15889  // draw spine:
15890  if (mErrorBarSkipSymbol)
15891  {
15892  if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15893  painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15894  if (b-x > skipSymbolMargin)
15895  painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15896  } else
15897  painter->drawLine(QLineF(a, y, b, y));
15898  // draw handles:
15899  painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15900  painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15901  }
15902  } else // mKeyAxis->orientation() is Qt::Horizontal
15903  {
15904  // draw value error vertically and key error horizontally
15905  if (mErrorType == etKey || mErrorType == etBoth)
15906  {
15907  a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15908  b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15909  if (keyAxis->rangeReversed())
15910  qSwap(a,b);
15911  // draw spine:
15912  if (mErrorBarSkipSymbol)
15913  {
15914  if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15915  painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15916  if (b-x > skipSymbolMargin)
15917  painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15918  } else
15919  painter->drawLine(QLineF(a, y, b, y));
15920  // draw handles:
15921  painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15922  painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15923  }
15924  if (mErrorType == etValue || mErrorType == etBoth)
15925  {
15926  a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15927  b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15928  if (valueAxis->rangeReversed())
15929  qSwap(a,b);
15930  // draw spine:
15931  if (mErrorBarSkipSymbol)
15932  {
15933  if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15934  painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15935  if (y-b > skipSymbolMargin)
15936  painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15937  } else
15938  painter->drawLine(QLineF(x, a, x, b));
15939  // draw handles:
15940  painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15941  painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15942  }
15943  }
15944 }
15945 
15960 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
15961 {
15962  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15963  if (mData->isEmpty())
15964  {
15965  lower = mData->constEnd();
15966  upper = mData->constEnd();
15967  return;
15968  }
15969 
15970  // get visible data range as QMap iterators
15971  QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
15972  QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
15973  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
15974  bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
15975 
15976  lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
15977  upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
15978 }
15979 
15990 int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
15991 {
15992  if (upper == mData->constEnd() && lower == mData->constEnd())
15993  return 0;
15994  QCPDataMap::const_iterator it = lower;
15995  int count = 1;
15996  while (it != upper && count < maxCount)
15997  {
15998  ++it;
15999  ++count;
16000  }
16001  return count;
16002 }
16003 
16019 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
16020 {
16021  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16022  if (!lineData) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; }
16023  if (lineData->isEmpty()) return;
16024 
16025  // append points that close the polygon fill at the key axis:
16026  if (mKeyAxis.data()->orientation() == Qt::Vertical)
16027  {
16028  *lineData << upperFillBasePoint(lineData->last().y());
16029  *lineData << lowerFillBasePoint(lineData->first().y());
16030  } else
16031  {
16032  *lineData << upperFillBasePoint(lineData->last().x());
16033  *lineData << lowerFillBasePoint(lineData->first().x());
16034  }
16035 }
16036 
16043 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
16044 {
16045  if (!lineData) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; }
16046  if (lineData->isEmpty()) return;
16047 
16048  lineData->remove(lineData->size()-2, 2);
16049 }
16050 
16065 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
16066 {
16067  QCPAxis *keyAxis = mKeyAxis.data();
16068  QCPAxis *valueAxis = mValueAxis.data();
16069  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16070 
16071  QPointF point;
16072  if (valueAxis->scaleType() == QCPAxis::stLinear)
16073  {
16074  if (keyAxis->axisType() == QCPAxis::atLeft)
16075  {
16076  point.setX(valueAxis->coordToPixel(0));
16077  point.setY(lowerKey);
16078  } else if (keyAxis->axisType() == QCPAxis::atRight)
16079  {
16080  point.setX(valueAxis->coordToPixel(0));
16081  point.setY(lowerKey);
16082  } else if (keyAxis->axisType() == QCPAxis::atTop)
16083  {
16084  point.setX(lowerKey);
16085  point.setY(valueAxis->coordToPixel(0));
16086  } else if (keyAxis->axisType() == QCPAxis::atBottom)
16087  {
16088  point.setX(lowerKey);
16089  point.setY(valueAxis->coordToPixel(0));
16090  }
16091  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16092  {
16093  // In logarithmic scaling we can't just draw to value zero so we just fill all the way
16094  // to the axis which is in the direction towards zero
16095  if (keyAxis->orientation() == Qt::Vertical)
16096  {
16097  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16098  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16099  point.setX(keyAxis->axisRect()->right());
16100  else
16101  point.setX(keyAxis->axisRect()->left());
16102  point.setY(lowerKey);
16103  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16104  {
16105  point.setX(lowerKey);
16106  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16107  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16108  point.setY(keyAxis->axisRect()->top());
16109  else
16110  point.setY(keyAxis->axisRect()->bottom());
16111  }
16112  }
16113  return point;
16114 }
16115 
16130 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
16131 {
16132  QCPAxis *keyAxis = mKeyAxis.data();
16133  QCPAxis *valueAxis = mValueAxis.data();
16134  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16135 
16136  QPointF point;
16137  if (valueAxis->scaleType() == QCPAxis::stLinear)
16138  {
16139  if (keyAxis->axisType() == QCPAxis::atLeft)
16140  {
16141  point.setX(valueAxis->coordToPixel(0));
16142  point.setY(upperKey);
16143  } else if (keyAxis->axisType() == QCPAxis::atRight)
16144  {
16145  point.setX(valueAxis->coordToPixel(0));
16146  point.setY(upperKey);
16147  } else if (keyAxis->axisType() == QCPAxis::atTop)
16148  {
16149  point.setX(upperKey);
16150  point.setY(valueAxis->coordToPixel(0));
16151  } else if (keyAxis->axisType() == QCPAxis::atBottom)
16152  {
16153  point.setX(upperKey);
16154  point.setY(valueAxis->coordToPixel(0));
16155  }
16156  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16157  {
16158  // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
16159  // to the axis which is in the direction towards 0
16160  if (keyAxis->orientation() == Qt::Vertical)
16161  {
16162  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16163  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16164  point.setX(keyAxis->axisRect()->right());
16165  else
16166  point.setX(keyAxis->axisRect()->left());
16167  point.setY(upperKey);
16168  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16169  {
16170  point.setX(upperKey);
16171  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16172  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16173  point.setY(keyAxis->axisRect()->top());
16174  else
16175  point.setY(keyAxis->axisRect()->bottom());
16176  }
16177  }
16178  return point;
16179 }
16180 
16190 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
16191 {
16192  if (!mChannelFillGraph)
16193  return QPolygonF();
16194 
16195  QCPAxis *keyAxis = mKeyAxis.data();
16196  QCPAxis *valueAxis = mValueAxis.data();
16197  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
16198  if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
16199 
16200  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
16201  return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)
16202 
16203  if (lineData->isEmpty()) return QPolygonF();
16204  QVector<QPointF> otherData;
16205  mChannelFillGraph.data()->getPlotData(&otherData, 0);
16206  if (otherData.isEmpty()) return QPolygonF();
16207  QVector<QPointF> thisData;
16208  thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
16209  for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
16210  thisData << lineData->at(i);
16211 
16212  // pointers to be able to swap them, depending which data range needs cropping:
16213  QVector<QPointF> *staticData = &thisData;
16214  QVector<QPointF> *croppedData = &otherData;
16215 
16216  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
16217  if (keyAxis->orientation() == Qt::Horizontal)
16218  {
16219  // x is key
16220  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16221  if (staticData->first().x() > staticData->last().x())
16222  {
16223  int size = staticData->size();
16224  for (int i=0; i<size/2; ++i)
16225  qSwap((*staticData)[i], (*staticData)[size-1-i]);
16226  }
16227  if (croppedData->first().x() > croppedData->last().x())
16228  {
16229  int size = croppedData->size();
16230  for (int i=0; i<size/2; ++i)
16231  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16232  }
16233  // crop lower bound:
16234  if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
16235  qSwap(staticData, croppedData);
16236  int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16237  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16238  croppedData->remove(0, lowBound);
16239  // set lowest point of cropped data to fit exactly key position of first static data
16240  // point via linear interpolation:
16241  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16242  double slope;
16243  if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
16244  slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
16245  else
16246  slope = 0;
16247  (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
16248  (*croppedData)[0].setX(staticData->first().x());
16249 
16250  // crop upper bound:
16251  if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
16252  qSwap(staticData, croppedData);
16253  int highBound = findIndexAboveX(croppedData, staticData->last().x());
16254  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16255  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16256  // set highest point of cropped data to fit exactly key position of last static data
16257  // point via linear interpolation:
16258  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16259  int li = croppedData->size()-1; // last index
16260  if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
16261  slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
16262  else
16263  slope = 0;
16264  (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
16265  (*croppedData)[li].setX(staticData->last().x());
16266  } else // mKeyAxis->orientation() == Qt::Vertical
16267  {
16268  // y is key
16269  // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
16270  // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
16271  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16272  if (staticData->first().y() < staticData->last().y())
16273  {
16274  int size = staticData->size();
16275  for (int i=0; i<size/2; ++i)
16276  qSwap((*staticData)[i], (*staticData)[size-1-i]);
16277  }
16278  if (croppedData->first().y() < croppedData->last().y())
16279  {
16280  int size = croppedData->size();
16281  for (int i=0; i<size/2; ++i)
16282  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16283  }
16284  // crop lower bound:
16285  if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
16286  qSwap(staticData, croppedData);
16287  int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16288  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16289  croppedData->remove(0, lowBound);
16290  // set lowest point of cropped data to fit exactly key position of first static data
16291  // point via linear interpolation:
16292  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16293  double slope;
16294  if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
16295  slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
16296  else
16297  slope = 0;
16298  (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
16299  (*croppedData)[0].setY(staticData->first().y());
16300 
16301  // crop upper bound:
16302  if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
16303  qSwap(staticData, croppedData);
16304  int highBound = findIndexBelowY(croppedData, staticData->last().y());
16305  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16306  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16307  // set highest point of cropped data to fit exactly key position of last static data
16308  // point via linear interpolation:
16309  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16310  int li = croppedData->size()-1; // last index
16311  if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
16312  slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
16313  else
16314  slope = 0;
16315  (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
16316  (*croppedData)[li].setY(staticData->last().y());
16317  }
16318 
16319  // return joined:
16320  for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
16321  thisData << otherData.at(i);
16322  return QPolygonF(thisData);
16323 }
16324 
16332 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
16333 {
16334  for (int i=data->size()-1; i>=0; --i)
16335  {
16336  if (data->at(i).x() < x)
16337  {
16338  if (i<data->size()-1)
16339  return i+1;
16340  else
16341  return data->size()-1;
16342  }
16343  }
16344  return -1;
16345 }
16346 
16354 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
16355 {
16356  for (int i=0; i<data->size(); ++i)
16357  {
16358  if (data->at(i).x() > x)
16359  {
16360  if (i>0)
16361  return i-1;
16362  else
16363  return 0;
16364  }
16365  }
16366  return -1;
16367 }
16368 
16376 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
16377 {
16378  for (int i=0; i<data->size(); ++i)
16379  {
16380  if (data->at(i).y() < y)
16381  {
16382  if (i>0)
16383  return i-1;
16384  else
16385  return 0;
16386  }
16387  }
16388  return -1;
16389 }
16390 
16400 double QCPGraph::pointDistance(const QPointF &pixelPoint) const
16401 {
16402  if (mData->isEmpty())
16403  return -1.0;
16404  if (mLineStyle == lsNone && mScatterStyle.isNone())
16405  return -1.0;
16406 
16407  // calculate minimum distances to graph representation:
16408  if (mLineStyle == lsNone)
16409  {
16410  // no line displayed, only calculate distance to scatter points:
16411  QVector<QCPData> scatterData;
16412  getScatterPlotData(&scatterData);
16413  if (scatterData.size() > 0)
16414  {
16415  double minDistSqr = std::numeric_limits<double>::max();
16416  for (int i=0; i<scatterData.size(); ++i)
16417  {
16418  double currentDistSqr = QVector2D(coordsToPixels(scatterData.at(i).key, scatterData.at(i).value)-pixelPoint).lengthSquared();
16419  if (currentDistSqr < minDistSqr)
16420  minDistSqr = currentDistSqr;
16421  }
16422  return qSqrt(minDistSqr);
16423  } else // no data available in view to calculate distance to
16424  return -1.0;
16425  } else
16426  {
16427  // line displayed, calculate distance to line segments:
16428  QVector<QPointF> lineData;
16429  getPlotData(&lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
16430  if (lineData.size() > 1) // at least one line segment, compare distance to line segments
16431  {
16432  double minDistSqr = std::numeric_limits<double>::max();
16433  if (mLineStyle == lsImpulse)
16434  {
16435  // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
16436  for (int i=0; i<lineData.size()-1; i+=2) // iterate pairs
16437  {
16438  double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16439  if (currentDistSqr < minDistSqr)
16440  minDistSqr = currentDistSqr;
16441  }
16442  } else
16443  {
16444  // all other line plots (line and step) connect points directly:
16445  for (int i=0; i<lineData.size()-1; ++i)
16446  {
16447  double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16448  if (currentDistSqr < minDistSqr)
16449  minDistSqr = currentDistSqr;
16450  }
16451  }
16452  return qSqrt(minDistSqr);
16453  } else if (lineData.size() > 0) // only single data point, calculate distance to that point
16454  {
16455  return QVector2D(lineData.at(0)-pixelPoint).length();
16456  } else // no data available in view to calculate distance to
16457  return -1.0;
16458  }
16459 }
16460 
16469 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
16470 {
16471  for (int i=data->size()-1; i>=0; --i)
16472  {
16473  if (data->at(i).y() > y)
16474  {
16475  if (i<data->size()-1)
16476  return i+1;
16477  else
16478  return data->size()-1;
16479  }
16480  }
16481  return -1;
16482 }
16483 
16484 /* inherits documentation from base class */
16485 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
16486 {
16487  // just call the specialized version which takes an additional argument whether error bars
16488  // should also be taken into consideration for range calculation. We set this to true here.
16489  return getKeyRange(foundRange, inSignDomain, true);
16490 }
16491 
16492 /* inherits documentation from base class */
16493 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
16494 {
16495  // just call the specialized version which takes an additional argument whether error bars
16496  // should also be taken into consideration for range calculation. We set this to true here.
16497  return getValueRange(foundRange, inSignDomain, true);
16498 }
16499 
16506 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16507 {
16508  QCPRange range;
16509  bool haveLower = false;
16510  bool haveUpper = false;
16511 
16512  double current, currentErrorMinus, currentErrorPlus;
16513 
16514  if (inSignDomain == sdBoth) // range may be anywhere
16515  {
16516  QCPDataMap::const_iterator it = mData->constBegin();
16517  while (it != mData->constEnd())
16518  {
16519  if (!qIsNaN(it.value().value))
16520  {
16521  current = it.value().key;
16522  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16523  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16524  if (current-currentErrorMinus < range.lower || !haveLower)
16525  {
16526  range.lower = current-currentErrorMinus;
16527  haveLower = true;
16528  }
16529  if (current+currentErrorPlus > range.upper || !haveUpper)
16530  {
16531  range.upper = current+currentErrorPlus;
16532  haveUpper = true;
16533  }
16534  }
16535  ++it;
16536  }
16537  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16538  {
16539  QCPDataMap::const_iterator it = mData->constBegin();
16540  while (it != mData->constEnd())
16541  {
16542  if (!qIsNaN(it.value().value))
16543  {
16544  current = it.value().key;
16545  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16546  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16547  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16548  {
16549  range.lower = current-currentErrorMinus;
16550  haveLower = true;
16551  }
16552  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16553  {
16554  range.upper = current+currentErrorPlus;
16555  haveUpper = true;
16556  }
16557  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16558  {
16559  if ((current < range.lower || !haveLower) && current < 0)
16560  {
16561  range.lower = current;
16562  haveLower = true;
16563  }
16564  if ((current > range.upper || !haveUpper) && current < 0)
16565  {
16566  range.upper = current;
16567  haveUpper = true;
16568  }
16569  }
16570  }
16571  ++it;
16572  }
16573  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16574  {
16575  QCPDataMap::const_iterator it = mData->constBegin();
16576  while (it != mData->constEnd())
16577  {
16578  if (!qIsNaN(it.value().value))
16579  {
16580  current = it.value().key;
16581  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16582  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16583  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16584  {
16585  range.lower = current-currentErrorMinus;
16586  haveLower = true;
16587  }
16588  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16589  {
16590  range.upper = current+currentErrorPlus;
16591  haveUpper = true;
16592  }
16593  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16594  {
16595  if ((current < range.lower || !haveLower) && current > 0)
16596  {
16597  range.lower = current;
16598  haveLower = true;
16599  }
16600  if ((current > range.upper || !haveUpper) && current > 0)
16601  {
16602  range.upper = current;
16603  haveUpper = true;
16604  }
16605  }
16606  }
16607  ++it;
16608  }
16609  }
16610 
16611  foundRange = haveLower && haveUpper;
16612  return range;
16613 }
16614 
16621 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16622 {
16623  QCPRange range;
16624  bool haveLower = false;
16625  bool haveUpper = false;
16626 
16627  double current, currentErrorMinus, currentErrorPlus;
16628 
16629  if (inSignDomain == sdBoth) // range may be anywhere
16630  {
16631  QCPDataMap::const_iterator it = mData->constBegin();
16632  while (it != mData->constEnd())
16633  {
16634  current = it.value().value;
16635  if (!qIsNaN(current))
16636  {
16637  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16638  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16639  if (current-currentErrorMinus < range.lower || !haveLower)
16640  {
16641  range.lower = current-currentErrorMinus;
16642  haveLower = true;
16643  }
16644  if (current+currentErrorPlus > range.upper || !haveUpper)
16645  {
16646  range.upper = current+currentErrorPlus;
16647  haveUpper = true;
16648  }
16649  }
16650  ++it;
16651  }
16652  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16653  {
16654  QCPDataMap::const_iterator it = mData->constBegin();
16655  while (it != mData->constEnd())
16656  {
16657  current = it.value().value;
16658  if (!qIsNaN(current))
16659  {
16660  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16661  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16662  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16663  {
16664  range.lower = current-currentErrorMinus;
16665  haveLower = true;
16666  }
16667  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16668  {
16669  range.upper = current+currentErrorPlus;
16670  haveUpper = true;
16671  }
16672  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16673  {
16674  if ((current < range.lower || !haveLower) && current < 0)
16675  {
16676  range.lower = current;
16677  haveLower = true;
16678  }
16679  if ((current > range.upper || !haveUpper) && current < 0)
16680  {
16681  range.upper = current;
16682  haveUpper = true;
16683  }
16684  }
16685  }
16686  ++it;
16687  }
16688  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16689  {
16690  QCPDataMap::const_iterator it = mData->constBegin();
16691  while (it != mData->constEnd())
16692  {
16693  current = it.value().value;
16694  if (!qIsNaN(current))
16695  {
16696  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16697  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16698  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16699  {
16700  range.lower = current-currentErrorMinus;
16701  haveLower = true;
16702  }
16703  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16704  {
16705  range.upper = current+currentErrorPlus;
16706  haveUpper = true;
16707  }
16708  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16709  {
16710  if ((current < range.lower || !haveLower) && current > 0)
16711  {
16712  range.lower = current;
16713  haveLower = true;
16714  }
16715  if ((current > range.upper || !haveUpper) && current > 0)
16716  {
16717  range.upper = current;
16718  haveUpper = true;
16719  }
16720  }
16721  }
16722  ++it;
16723  }
16724  }
16725 
16726  foundRange = haveLower && haveUpper;
16727  return range;
16728 }
16729 
16730 
16734 
16752  t(0),
16753  key(0),
16754  value(0)
16755 {
16756 }
16757 
16761 QCPCurveData::QCPCurveData(double t, double key, double value) :
16762  t(t),
16763  key(key),
16764  value(value)
16765 {
16766 }
16767 
16768 
16772 
16812 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
16813  QCPAbstractPlottable(keyAxis, valueAxis)
16814 {
16815  mData = new QCPCurveDataMap;
16816  mPen.setColor(Qt::blue);
16817  mPen.setStyle(Qt::SolidLine);
16818  mBrush.setColor(Qt::blue);
16819  mBrush.setStyle(Qt::NoBrush);
16820  mSelectedPen = mPen;
16821  mSelectedPen.setWidthF(2.5);
16822  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
16824 
16827 }
16828 
16830 {
16831  delete mData;
16832 }
16833 
16841 void QCPCurve::setData(QCPCurveDataMap *data, bool copy)
16842 {
16843  if (mData == data)
16844  {
16845  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
16846  return;
16847  }
16848  if (copy)
16849  {
16850  *mData = *data;
16851  } else
16852  {
16853  delete mData;
16854  mData = data;
16855  }
16856 }
16857 
16864 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
16865 {
16866  mData->clear();
16867  int n = t.size();
16868  n = qMin(n, key.size());
16869  n = qMin(n, value.size());
16870  QCPCurveData newData;
16871  for (int i=0; i<n; ++i)
16872  {
16873  newData.t = t[i];
16874  newData.key = key[i];
16875  newData.value = value[i];
16876  mData->insertMulti(newData.t, newData);
16877  }
16878 }
16879 
16885 void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
16886 {
16887  mData->clear();
16888  int n = key.size();
16889  n = qMin(n, value.size());
16890  QCPCurveData newData;
16891  for (int i=0; i<n; ++i)
16892  {
16893  newData.t = i; // no t vector given, so we assign t the index of the key/value pair
16894  newData.key = key[i];
16895  newData.value = value[i];
16896  mData->insertMulti(newData.t, newData);
16897  }
16898 }
16899 
16908 {
16909  mScatterStyle = style;
16910 }
16911 
16920 {
16921  mLineStyle = style;
16922 }
16923 
16929 {
16930  mData->unite(dataMap);
16931 }
16932 
16938 {
16939  mData->insertMulti(data.t, data);
16940 }
16941 
16946 void QCPCurve::addData(double t, double key, double value)
16947 {
16948  QCPCurveData newData;
16949  newData.t = t;
16950  newData.key = key;
16951  newData.value = value;
16952  mData->insertMulti(newData.t, newData);
16953 }
16954 
16963 void QCPCurve::addData(double key, double value)
16964 {
16965  QCPCurveData newData;
16966  if (!mData->isEmpty())
16967  newData.t = (mData->constEnd()-1).key()+1;
16968  else
16969  newData.t = 0;
16970  newData.key = key;
16971  newData.value = value;
16972  mData->insertMulti(newData.t, newData);
16973 }
16974 
16979 void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
16980 {
16981  int n = ts.size();
16982  n = qMin(n, keys.size());
16983  n = qMin(n, values.size());
16984  QCPCurveData newData;
16985  for (int i=0; i<n; ++i)
16986  {
16987  newData.t = ts[i];
16988  newData.key = keys[i];
16989  newData.value = values[i];
16990  mData->insertMulti(newData.t, newData);
16991  }
16992 }
16993 
16999 {
17000  QCPCurveDataMap::iterator it = mData->begin();
17001  while (it != mData->end() && it.key() < t)
17002  it = mData->erase(it);
17003 }
17004 
17010 {
17011  if (mData->isEmpty()) return;
17012  QCPCurveDataMap::iterator it = mData->upperBound(t);
17013  while (it != mData->end())
17014  it = mData->erase(it);
17015 }
17016 
17024 void QCPCurve::removeData(double fromt, double tot)
17025 {
17026  if (fromt >= tot || mData->isEmpty()) return;
17027  QCPCurveDataMap::iterator it = mData->upperBound(fromt);
17028  QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
17029  while (it != itEnd)
17030  it = mData->erase(it);
17031 }
17032 
17042 void QCPCurve::removeData(double t)
17043 {
17044  mData->remove(t);
17045 }
17046 
17052 {
17053  mData->clear();
17054 }
17055 
17056 /* inherits documentation from base class */
17057 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17058 {
17059  Q_UNUSED(details)
17060  if ((onlySelectable && !mSelectable) || mData->isEmpty())
17061  return -1;
17062  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17063 
17064  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17065  return pointDistance(pos);
17066  else
17067  return -1;
17068 }
17069 
17070 /* inherits documentation from base class */
17072 {
17073  if (mData->isEmpty()) return;
17074 
17075  // allocate line vector:
17076  QVector<QPointF> *lineData = new QVector<QPointF>;
17077 
17078  // fill with curve data:
17079  getCurveData(lineData);
17080 
17081  // check data validity if flag set:
17082 #ifdef QCUSTOMPLOT_CHECK_DATA
17083  QCPCurveDataMap::const_iterator it;
17084  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17085  {
17086  if (QCP::isInvalidData(it.value().t) ||
17087  QCP::isInvalidData(it.value().key, it.value().value))
17088  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
17089  }
17090 #endif
17091 
17092  // draw curve fill:
17093  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17094  {
17095  applyFillAntialiasingHint(painter);
17096  painter->setPen(Qt::NoPen);
17097  painter->setBrush(mainBrush());
17098  painter->drawPolygon(QPolygonF(*lineData));
17099  }
17100 
17101  // draw curve line:
17102  if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17103  {
17105  painter->setPen(mainPen());
17106  painter->setBrush(Qt::NoBrush);
17107  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
17108  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17109  painter->pen().style() == Qt::SolidLine &&
17110  !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17111  !painter->modes().testFlag(QCPPainter::pmNoCaching))
17112  {
17113  int i = 0;
17114  bool lastIsNan = false;
17115  const int lineDataSize = lineData->size();
17116  while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
17117  ++i;
17118  ++i; // because drawing works in 1 point retrospect
17119  while (i < lineDataSize)
17120  {
17121  if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17122  {
17123  if (!lastIsNan)
17124  painter->drawLine(lineData->at(i-1), lineData->at(i));
17125  else
17126  lastIsNan = false;
17127  } else
17128  lastIsNan = true;
17129  ++i;
17130  }
17131  } else
17132  {
17133  int segmentStart = 0;
17134  int i = 0;
17135  const int lineDataSize = lineData->size();
17136  while (i < lineDataSize)
17137  {
17138  if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17139  {
17140  painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
17141  segmentStart = i+1;
17142  }
17143  ++i;
17144  }
17145  // draw last segment:
17146  painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
17147  }
17148  }
17149 
17150  // draw scatters:
17151  if (!mScatterStyle.isNone())
17152  drawScatterPlot(painter, lineData);
17153 
17154  // free allocated line data:
17155  delete lineData;
17156 }
17157 
17158 /* inherits documentation from base class */
17159 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17160 {
17161  // draw fill:
17162  if (mBrush.style() != Qt::NoBrush)
17163  {
17164  applyFillAntialiasingHint(painter);
17165  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
17166  }
17167  // draw line vertically centered:
17168  if (mLineStyle != lsNone)
17169  {
17171  painter->setPen(mPen);
17172  painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
17173  }
17174  // draw scatter symbol:
17175  if (!mScatterStyle.isNone())
17176  {
17178  // scale scatter pixmap if it's too large to fit in legend icon rect:
17179  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
17180  {
17181  QCPScatterStyle scaledStyle(mScatterStyle);
17182  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17183  scaledStyle.applyTo(painter, mPen);
17184  scaledStyle.drawShape(painter, QRectF(rect).center());
17185  } else
17186  {
17187  mScatterStyle.applyTo(painter, mPen);
17188  mScatterStyle.drawShape(painter, QRectF(rect).center());
17189  }
17190  }
17191 }
17192 
17198 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
17199 {
17200  // draw scatter point symbols:
17202  mScatterStyle.applyTo(painter, mPen);
17203  for (int i=0; i<pointData->size(); ++i)
17204  if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
17205  mScatterStyle.drawShape(painter, pointData->at(i));
17206 }
17207 
17221 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
17222 {
17223  QCPAxis *keyAxis = mKeyAxis.data();
17224  QCPAxis *valueAxis = mValueAxis.data();
17225  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17226 
17227  // add margins to rect to compensate for stroke width
17228  double strokeMargin = qMax(qreal(1.0), qreal(mainPen().widthF()*0.75)); // stroke radius + 50% safety
17229  if (!mScatterStyle.isNone())
17230  strokeMargin = qMax(strokeMargin, mScatterStyle.size());
17231  double rectLeft = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17232  double rectRight = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17233  double rectBottom = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)+strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17234  double rectTop = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)-strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17235  int currentRegion;
17236  QCPCurveDataMap::const_iterator it = mData->constBegin();
17237  QCPCurveDataMap::const_iterator prevIt = mData->constEnd()-1;
17238  int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom);
17239  QVector<QPointF> trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right)
17240  while (it != mData->constEnd())
17241  {
17242  currentRegion = getRegion(it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17243  if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
17244  {
17245  if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
17246  {
17247  QPointF crossA, crossB;
17248  if (prevRegion == 5) // we're coming from R, so add this point optimized
17249  {
17250  lineData->append(getOptimizedPoint(currentRegion, it.value().key, it.value().value, prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom));
17251  // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point
17252  *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17253  } else if (mayTraverse(prevRegion, currentRegion) &&
17254  getTraverse(prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom, crossA, crossB))
17255  {
17256  // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
17257  QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
17258  getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop, rectRight, rectBottom, beforeTraverseCornerPoints, afterTraverseCornerPoints);
17259  if (it != mData->constBegin())
17260  {
17261  *lineData << beforeTraverseCornerPoints;
17262  lineData->append(crossA);
17263  lineData->append(crossB);
17264  *lineData << afterTraverseCornerPoints;
17265  } else
17266  {
17267  lineData->append(crossB);
17268  *lineData << afterTraverseCornerPoints;
17269  trailingPoints << beforeTraverseCornerPoints << crossA ;
17270  }
17271  } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s)
17272  {
17273  *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17274  }
17275  } else // segment does end in R, so we add previous point optimized and this point at original position
17276  {
17277  if (it == mData->constBegin()) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end
17278  trailingPoints << getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17279  else
17280  lineData->append(getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom));
17281  lineData->append(coordsToPixels(it.value().key, it.value().value));
17282  }
17283  } else // region didn't change
17284  {
17285  if (currentRegion == 5) // still in R, keep adding original points
17286  {
17287  lineData->append(coordsToPixels(it.value().key, it.value().value));
17288  } else // still outside R, no need to add anything
17289  {
17290  // see how this is not doing anything? That's the main optimization...
17291  }
17292  }
17293  prevIt = it;
17294  prevRegion = currentRegion;
17295  ++it;
17296  }
17297  *lineData << trailingPoints;
17298 }
17299 
17318 int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17319 {
17320  if (x < rectLeft) // region 123
17321  {
17322  if (y > rectTop)
17323  return 1;
17324  else if (y < rectBottom)
17325  return 3;
17326  else
17327  return 2;
17328  } else if (x > rectRight) // region 789
17329  {
17330  if (y > rectTop)
17331  return 7;
17332  else if (y < rectBottom)
17333  return 9;
17334  else
17335  return 8;
17336  } else // region 456
17337  {
17338  if (y > rectTop)
17339  return 4;
17340  else if (y < rectBottom)
17341  return 6;
17342  else
17343  return 5;
17344  }
17345 }
17346 
17362 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17363 {
17364  double intersectKey = rectLeft; // initial value is just fail-safe
17365  double intersectValue = rectTop; // initial value is just fail-safe
17366  switch (otherRegion)
17367  {
17368  case 1: // top and left edge
17369  {
17370  intersectValue = rectTop;
17371  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17372  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17373  {
17374  intersectKey = rectLeft;
17375  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17376  }
17377  break;
17378  }
17379  case 2: // left edge
17380  {
17381  intersectKey = rectLeft;
17382  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17383  break;
17384  }
17385  case 3: // bottom and left edge
17386  {
17387  intersectValue = rectBottom;
17388  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17389  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17390  {
17391  intersectKey = rectLeft;
17392  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17393  }
17394  break;
17395  }
17396  case 4: // top edge
17397  {
17398  intersectValue = rectTop;
17399  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17400  break;
17401  }
17402  case 5:
17403  {
17404  break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
17405  }
17406  case 6: // bottom edge
17407  {
17408  intersectValue = rectBottom;
17409  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17410  break;
17411  }
17412  case 7: // top and right edge
17413  {
17414  intersectValue = rectTop;
17415  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17416  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17417  {
17418  intersectKey = rectRight;
17419  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17420  }
17421  break;
17422  }
17423  case 8: // right edge
17424  {
17425  intersectKey = rectRight;
17426  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17427  break;
17428  }
17429  case 9: // bottom and right edge
17430  {
17431  intersectValue = rectBottom;
17432  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17433  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17434  {
17435  intersectKey = rectRight;
17436  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17437  }
17438  break;
17439  }
17440  }
17441  return coordsToPixels(intersectKey, intersectValue);
17442 }
17443 
17462 QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17463 {
17464  QVector<QPointF> result;
17465  switch (prevRegion)
17466  {
17467  case 1:
17468  {
17469  switch (currentRegion)
17470  {
17471  case 2: { result << coordsToPixels(rectLeft, rectTop); break; }
17472  case 4: { result << coordsToPixels(rectLeft, rectTop); break; }
17473  case 3: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); break; }
17474  case 7: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); break; }
17475  case 6: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17476  case 8: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17477  case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17478  if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17479  { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17480  else
17481  { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17482  break;
17483  }
17484  }
17485  break;
17486  }
17487  case 2:
17488  {
17489  switch (currentRegion)
17490  {
17491  case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17492  case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17493  case 4: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17494  case 6: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17495  case 7: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17496  case 9: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17497  }
17498  break;
17499  }
17500  case 3:
17501  {
17502  switch (currentRegion)
17503  {
17504  case 2: { result << coordsToPixels(rectLeft, rectBottom); break; }
17505  case 6: { result << coordsToPixels(rectLeft, rectBottom); break; }
17506  case 1: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); break; }
17507  case 9: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); break; }
17508  case 4: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17509  case 8: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17510  case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17511  if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17512  { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17513  else
17514  { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17515  break;
17516  }
17517  }
17518  break;
17519  }
17520  case 4:
17521  {
17522  switch (currentRegion)
17523  {
17524  case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17525  case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17526  case 2: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17527  case 8: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17528  case 3: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17529  case 9: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17530  }
17531  break;
17532  }
17533  case 5:
17534  {
17535  switch (currentRegion)
17536  {
17537  case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17538  case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17539  case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17540  case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17541  }
17542  break;
17543  }
17544  case 6:
17545  {
17546  switch (currentRegion)
17547  {
17548  case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17549  case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17550  case 2: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17551  case 8: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17552  case 1: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17553  case 7: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17554  }
17555  break;
17556  }
17557  case 7:
17558  {
17559  switch (currentRegion)
17560  {
17561  case 4: { result << coordsToPixels(rectRight, rectTop); break; }
17562  case 8: { result << coordsToPixels(rectRight, rectTop); break; }
17563  case 1: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); break; }
17564  case 9: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); break; }
17565  case 2: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17566  case 6: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17567  case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17568  if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17569  { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17570  else
17571  { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17572  break;
17573  }
17574  }
17575  break;
17576  }
17577  case 8:
17578  {
17579  switch (currentRegion)
17580  {
17581  case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17582  case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17583  case 4: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17584  case 6: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17585  case 1: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17586  case 3: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17587  }
17588  break;
17589  }
17590  case 9:
17591  {
17592  switch (currentRegion)
17593  {
17594  case 6: { result << coordsToPixels(rectRight, rectBottom); break; }
17595  case 8: { result << coordsToPixels(rectRight, rectBottom); break; }
17596  case 3: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); break; }
17597  case 7: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); break; }
17598  case 2: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17599  case 4: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17600  case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17601  if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17602  { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17603  else
17604  { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17605  break;
17606  }
17607  }
17608  break;
17609  }
17610  }
17611  return result;
17612 }
17613 
17626 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
17627 {
17628  switch (prevRegion)
17629  {
17630  case 1:
17631  {
17632  switch (currentRegion)
17633  {
17634  case 4:
17635  case 7:
17636  case 2:
17637  case 3: return false;
17638  default: return true;
17639  }
17640  }
17641  case 2:
17642  {
17643  switch (currentRegion)
17644  {
17645  case 1:
17646  case 3: return false;
17647  default: return true;
17648  }
17649  }
17650  case 3:
17651  {
17652  switch (currentRegion)
17653  {
17654  case 1:
17655  case 2:
17656  case 6:
17657  case 9: return false;
17658  default: return true;
17659  }
17660  }
17661  case 4:
17662  {
17663  switch (currentRegion)
17664  {
17665  case 1:
17666  case 7: return false;
17667  default: return true;
17668  }
17669  }
17670  case 5: return false; // should never occur
17671  case 6:
17672  {
17673  switch (currentRegion)
17674  {
17675  case 3:
17676  case 9: return false;
17677  default: return true;
17678  }
17679  }
17680  case 7:
17681  {
17682  switch (currentRegion)
17683  {
17684  case 1:
17685  case 4:
17686  case 8:
17687  case 9: return false;
17688  default: return true;
17689  }
17690  }
17691  case 8:
17692  {
17693  switch (currentRegion)
17694  {
17695  case 7:
17696  case 9: return false;
17697  default: return true;
17698  }
17699  }
17700  case 9:
17701  {
17702  switch (currentRegion)
17703  {
17704  case 3:
17705  case 6:
17706  case 8:
17707  case 7: return false;
17708  default: return true;
17709  }
17710  }
17711  default: return true;
17712  }
17713 }
17714 
17715 
17729 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
17730 {
17731  QList<QPointF> intersections; // x of QPointF corresponds to key and y to value
17732  if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
17733  {
17734  // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17735  intersections.append(QPointF(key, rectBottom)); // direction will be taken care of at end of method
17736  intersections.append(QPointF(key, rectTop));
17737  } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
17738  {
17739  // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17740  intersections.append(QPointF(rectLeft, value)); // direction will be taken care of at end of method
17741  intersections.append(QPointF(rectRight, value));
17742  } else // line is skewed
17743  {
17744  double gamma;
17745  double keyPerValue = (key-prevKey)/(value-prevValue);
17746  // check top of rect:
17747  gamma = prevKey + (rectTop-prevValue)*keyPerValue;
17748  if (gamma >= rectLeft && gamma <= rectRight)
17749  intersections.append(QPointF(gamma, rectTop));
17750  // check bottom of rect:
17751  gamma = prevKey + (rectBottom-prevValue)*keyPerValue;
17752  if (gamma >= rectLeft && gamma <= rectRight)
17753  intersections.append(QPointF(gamma, rectBottom));
17754  double valuePerKey = 1.0/keyPerValue;
17755  // check left of rect:
17756  gamma = prevValue + (rectLeft-prevKey)*valuePerKey;
17757  if (gamma >= rectBottom && gamma <= rectTop)
17758  intersections.append(QPointF(rectLeft, gamma));
17759  // check right of rect:
17760  gamma = prevValue + (rectRight-prevKey)*valuePerKey;
17761  if (gamma >= rectBottom && gamma <= rectTop)
17762  intersections.append(QPointF(rectRight, gamma));
17763  }
17764 
17765  // handle cases where found points isn't exactly 2:
17766  if (intersections.size() > 2)
17767  {
17768  // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
17769  double distSqrMax = 0;
17770  QPointF pv1, pv2;
17771  for (int i=0; i<intersections.size()-1; ++i)
17772  {
17773  for (int k=i+1; k<intersections.size(); ++k)
17774  {
17775  QPointF distPoint = intersections.at(i)-intersections.at(k);
17776  double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
17777  if (distSqr > distSqrMax)
17778  {
17779  pv1 = intersections.at(i);
17780  pv2 = intersections.at(k);
17781  distSqrMax = distSqr;
17782  }
17783  }
17784  }
17785  intersections = QList<QPointF>() << pv1 << pv2;
17786  } else if (intersections.size() != 2)
17787  {
17788  // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
17789  return false;
17790  }
17791 
17792  // possibly re-sort points so optimized point segment has same direction as original segment:
17793  if ((key-prevKey)*(intersections.at(1).x()-intersections.at(0).x()) + (value-prevValue)*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction
17794  intersections.move(0, 1);
17795  crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
17796  crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
17797  return true;
17798 }
17799 
17825 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
17826 {
17827  switch (prevRegion)
17828  {
17829  case 1:
17830  {
17831  switch (currentRegion)
17832  {
17833  case 6: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17834  case 9: { beforeTraverse << coordsToPixels(rectLeft, rectTop); afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17835  case 8: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17836  }
17837  break;
17838  }
17839  case 2:
17840  {
17841  switch (currentRegion)
17842  {
17843  case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17844  case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17845  }
17846  break;
17847  }
17848  case 3:
17849  {
17850  switch (currentRegion)
17851  {
17852  case 4: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17853  case 7: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17854  case 8: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17855  }
17856  break;
17857  }
17858  case 4:
17859  {
17860  switch (currentRegion)
17861  {
17862  case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17863  case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17864  }
17865  break;
17866  }
17867  case 5: { break; } // shouldn't happen because this method only handles full traverses
17868  case 6:
17869  {
17870  switch (currentRegion)
17871  {
17872  case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17873  case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17874  }
17875  break;
17876  }
17877  case 7:
17878  {
17879  switch (currentRegion)
17880  {
17881  case 2: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17882  case 3: { beforeTraverse << coordsToPixels(rectRight, rectTop); afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17883  case 6: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17884  }
17885  break;
17886  }
17887  case 8:
17888  {
17889  switch (currentRegion)
17890  {
17891  case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17892  case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17893  }
17894  break;
17895  }
17896  case 9:
17897  {
17898  switch (currentRegion)
17899  {
17900  case 2: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17901  case 1: { beforeTraverse << coordsToPixels(rectRight, rectBottom); afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17902  case 4: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17903  }
17904  break;
17905  }
17906  }
17907 }
17908 
17915 double QCPCurve::pointDistance(const QPointF &pixelPoint) const
17916 {
17917  if (mData->isEmpty())
17918  {
17919  qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
17920  return 500;
17921  }
17922  if (mData->size() == 1)
17923  {
17924  QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
17925  return QVector2D(dataPoint-pixelPoint).length();
17926  }
17927 
17928  // calculate minimum distance to line segments:
17929  QVector<QPointF> *lineData = new QVector<QPointF>;
17930  getCurveData(lineData);
17931  double minDistSqr = std::numeric_limits<double>::max();
17932  for (int i=0; i<lineData->size()-1; ++i)
17933  {
17934  double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
17935  if (currentDistSqr < minDistSqr)
17936  minDistSqr = currentDistSqr;
17937  }
17938  delete lineData;
17939  return qSqrt(minDistSqr);
17940 }
17941 
17942 /* inherits documentation from base class */
17943 QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17944 {
17945  QCPRange range;
17946  bool haveLower = false;
17947  bool haveUpper = false;
17948 
17949  double current;
17950 
17951  QCPCurveDataMap::const_iterator it = mData->constBegin();
17952  while (it != mData->constEnd())
17953  {
17954  current = it.value().key;
17955  if (!qIsNaN(current) && !qIsNaN(it.value().value))
17956  {
17957  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17958  {
17959  if (current < range.lower || !haveLower)
17960  {
17961  range.lower = current;
17962  haveLower = true;
17963  }
17964  if (current > range.upper || !haveUpper)
17965  {
17966  range.upper = current;
17967  haveUpper = true;
17968  }
17969  }
17970  }
17971  ++it;
17972  }
17973 
17974  foundRange = haveLower && haveUpper;
17975  return range;
17976 }
17977 
17978 /* inherits documentation from base class */
17979 QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17980 {
17981  QCPRange range;
17982  bool haveLower = false;
17983  bool haveUpper = false;
17984 
17985  double current;
17986 
17987  QCPCurveDataMap::const_iterator it = mData->constBegin();
17988  while (it != mData->constEnd())
17989  {
17990  current = it.value().value;
17991  if (!qIsNaN(current) && !qIsNaN(it.value().key))
17992  {
17993  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17994  {
17995  if (current < range.lower || !haveLower)
17996  {
17997  range.lower = current;
17998  haveLower = true;
17999  }
18000  if (current > range.upper || !haveUpper)
18001  {
18002  range.upper = current;
18003  haveUpper = true;
18004  }
18005  }
18006  }
18007  ++it;
18008  }
18009 
18010  foundRange = haveLower && haveUpper;
18011  return range;
18012 }
18013 
18014 
18018 
18053 /* start of documentation of inline functions */
18054 
18081 /* end of documentation of inline functions */
18082 
18087  QObject(parentPlot),
18088  mParentPlot(parentPlot),
18089  mSpacingType(stAbsolute),
18090  mSpacing(4)
18091 {
18092 }
18093 
18095 {
18096  clear();
18097 }
18098 
18107 {
18109 }
18110 
18117 void QCPBarsGroup::setSpacing(double spacing)
18118 {
18119  mSpacing = spacing;
18120 }
18121 
18128 QCPBars *QCPBarsGroup::bars(int index) const
18129 {
18130  if (index >= 0 && index < mBars.size())
18131  {
18132  return mBars.at(index);
18133  } else
18134  {
18135  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
18136  return 0;
18137  }
18138 }
18139 
18146 {
18147  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
18148  bars->setBarsGroup(0); // removes itself via removeBars
18149 }
18150 
18158 {
18159  if (!bars)
18160  {
18161  qDebug() << Q_FUNC_INFO << "bars is 0";
18162  return;
18163  }
18164 
18165  if (!mBars.contains(bars))
18166  bars->setBarsGroup(this);
18167  else
18168  qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
18169 }
18170 
18180 void QCPBarsGroup::insert(int i, QCPBars *bars)
18181 {
18182  if (!bars)
18183  {
18184  qDebug() << Q_FUNC_INFO << "bars is 0";
18185  return;
18186  }
18187 
18188  // first append to bars list normally:
18189  if (!mBars.contains(bars))
18190  bars->setBarsGroup(this);
18191  // then move to according position:
18192  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
18193 }
18194 
18201 {
18202  if (!bars)
18203  {
18204  qDebug() << Q_FUNC_INFO << "bars is 0";
18205  return;
18206  }
18207 
18208  if (mBars.contains(bars))
18209  bars->setBarsGroup(0);
18210  else
18211  qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
18212 }
18213 
18222 {
18223  if (!mBars.contains(bars))
18224  mBars.append(bars);
18225 }
18226 
18235 {
18236  mBars.removeOne(bars);
18237 }
18238 
18245 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
18246 {
18247  // find list of all base bars in case some mBars are stacked:
18248  QList<const QCPBars*> baseBars;
18249  foreach (const QCPBars *b, mBars)
18250  {
18251  while (b->barBelow())
18252  b = b->barBelow();
18253  if (!baseBars.contains(b))
18254  baseBars.append(b);
18255  }
18256  // find base bar this "bars" is stacked on:
18257  const QCPBars *thisBase = bars;
18258  while (thisBase->barBelow())
18259  thisBase = thisBase->barBelow();
18260 
18261  // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
18262  double result = 0;
18263  int index = baseBars.indexOf(thisBase);
18264  if (index >= 0)
18265  {
18266  int startIndex;
18267  double lowerPixelWidth, upperPixelWidth;
18268  if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
18269  {
18270  return result;
18271  } else if (index < (baseBars.size()-1)/2.0) // bar is to the left of center
18272  {
18273  if (baseBars.size() % 2 == 0) // even number of bars
18274  {
18275  startIndex = baseBars.size()/2-1;
18276  result -= getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18277  } else // uneven number of bars
18278  {
18279  startIndex = (baseBars.size()-1)/2-1;
18280  baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18281  result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18282  result -= getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18283  }
18284  for (int i=startIndex; i>index; --i) // add widths and spacings of bars in between center and our bars
18285  {
18286  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18287  result -= qAbs(upperPixelWidth-lowerPixelWidth);
18288  result -= getPixelSpacing(baseBars.at(i), keyCoord);
18289  }
18290  // finally half of our bars width:
18291  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18292  result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18293  } else // bar is to the right of center
18294  {
18295  if (baseBars.size() % 2 == 0) // even number of bars
18296  {
18297  startIndex = baseBars.size()/2;
18298  result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18299  } else // uneven number of bars
18300  {
18301  startIndex = (baseBars.size()-1)/2+1;
18302  baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18303  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18304  result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18305  }
18306  for (int i=startIndex; i<index; ++i) // add widths and spacings of bars in between center and our bars
18307  {
18308  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18309  result += qAbs(upperPixelWidth-lowerPixelWidth);
18310  result += getPixelSpacing(baseBars.at(i), keyCoord);
18311  }
18312  // finally half of our bars width:
18313  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18314  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18315  }
18316  }
18317  return result;
18318 }
18319 
18330 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
18331 {
18332  switch (mSpacingType)
18333  {
18334  case stAbsolute:
18335  {
18336  return mSpacing;
18337  }
18338  case stAxisRectRatio:
18339  {
18340  if (bars->keyAxis()->orientation() == Qt::Horizontal)
18341  return bars->keyAxis()->axisRect()->width()*mSpacing;
18342  else
18343  return bars->keyAxis()->axisRect()->height()*mSpacing;
18344  }
18345  case stPlotCoords:
18346  {
18347  double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
18348  return bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel;
18349  }
18350  }
18351  return 0;
18352 }
18353 
18354 
18358 
18375  key(0),
18376  value(0)
18377 {
18378 }
18379 
18383 QCPBarData::QCPBarData(double key, double value) :
18384  key(key),
18385  value(value)
18386 {
18387 }
18388 
18389 
18393 
18429 /* start of documentation of inline functions */
18430 
18445 /* end of documentation of inline functions */
18446 
18456 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
18457  QCPAbstractPlottable(keyAxis, valueAxis),
18458  mData(new QCPBarDataMap),
18459  mWidth(0.75),
18460  mWidthType(wtPlotCoords),
18461  mBarsGroup(0),
18462  mBaseValue(0)
18463 {
18464  // modify inherited properties from abstract plottable:
18465  mPen.setColor(Qt::blue);
18466  mPen.setStyle(Qt::SolidLine);
18467  mBrush.setColor(QColor(40, 50, 255, 30));
18468  mBrush.setStyle(Qt::SolidPattern);
18469  mSelectedPen = mPen;
18470  mSelectedPen.setWidthF(2.5);
18471  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
18473 }
18474 
18476 {
18477  setBarsGroup(0);
18478  if (mBarBelow || mBarAbove)
18479  connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
18480  delete mData;
18481 }
18482 
18489 void QCPBars::setWidth(double width)
18490 {
18491  mWidth = width;
18492 }
18493 
18503 {
18505 }
18506 
18514 {
18515  // deregister at old group:
18516  if (mBarsGroup)
18517  mBarsGroup->unregisterBars(this);
18519  // register at new group:
18520  if (mBarsGroup)
18521  mBarsGroup->registerBars(this);
18522 }
18523 
18536 void QCPBars::setBaseValue(double baseValue)
18537 {
18539 }
18540 
18548 void QCPBars::setData(QCPBarDataMap *data, bool copy)
18549 {
18550  if (mData == data)
18551  {
18552  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
18553  return;
18554  }
18555  if (copy)
18556  {
18557  *mData = *data;
18558  } else
18559  {
18560  delete mData;
18561  mData = data;
18562  }
18563 }
18564 
18571 void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
18572 {
18573  mData->clear();
18574  int n = key.size();
18575  n = qMin(n, value.size());
18576  QCPBarData newData;
18577  for (int i=0; i<n; ++i)
18578  {
18579  newData.key = key[i];
18580  newData.value = value[i];
18581  mData->insertMulti(newData.key, newData);
18582  }
18583 }
18584 
18600 {
18601  if (bars == this) return;
18602  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18603  {
18604  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18605  return;
18606  }
18607  // remove from stacking:
18608  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18609  // if new bar given, insert this bar below it:
18610  if (bars)
18611  {
18612  if (bars->mBarBelow)
18613  connectBars(bars->mBarBelow.data(), this);
18614  connectBars(this, bars);
18615  }
18616 }
18617 
18633 {
18634  if (bars == this) return;
18635  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18636  {
18637  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18638  return;
18639  }
18640  // remove from stacking:
18641  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18642  // if new bar given, insert this bar above it:
18643  if (bars)
18644  {
18645  if (bars->mBarAbove)
18646  connectBars(this, bars->mBarAbove.data());
18647  connectBars(bars, this);
18648  }
18649 }
18650 
18655 void QCPBars::addData(const QCPBarDataMap &dataMap)
18656 {
18657  mData->unite(dataMap);
18658 }
18659 
18664 void QCPBars::addData(const QCPBarData &data)
18665 {
18666  mData->insertMulti(data.key, data);
18667 }
18668 
18673 void QCPBars::addData(double key, double value)
18674 {
18675  QCPBarData newData;
18676  newData.key = key;
18677  newData.value = value;
18678  mData->insertMulti(newData.key, newData);
18679 }
18680 
18685 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
18686 {
18687  int n = keys.size();
18688  n = qMin(n, values.size());
18689  QCPBarData newData;
18690  for (int i=0; i<n; ++i)
18691  {
18692  newData.key = keys[i];
18693  newData.value = values[i];
18694  mData->insertMulti(newData.key, newData);
18695  }
18696 }
18697 
18703 {
18704  QCPBarDataMap::iterator it = mData->begin();
18705  while (it != mData->end() && it.key() < key)
18706  it = mData->erase(it);
18707 }
18708 
18714 {
18715  if (mData->isEmpty()) return;
18716  QCPBarDataMap::iterator it = mData->upperBound(key);
18717  while (it != mData->end())
18718  it = mData->erase(it);
18719 }
18720 
18728 void QCPBars::removeData(double fromKey, double toKey)
18729 {
18730  if (fromKey >= toKey || mData->isEmpty()) return;
18731  QCPBarDataMap::iterator it = mData->upperBound(fromKey);
18732  QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
18733  while (it != itEnd)
18734  it = mData->erase(it);
18735 }
18736 
18745 void QCPBars::removeData(double key)
18746 {
18747  mData->remove(key);
18748 }
18749 
18755 {
18756  mData->clear();
18757 }
18758 
18759 /* inherits documentation from base class */
18760 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18761 {
18762  Q_UNUSED(details)
18763  if (onlySelectable && !mSelectable)
18764  return -1;
18765  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
18766 
18767  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
18768  {
18769  QCPBarDataMap::ConstIterator it;
18770  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
18771  {
18772  if (getBarPolygon(it.value().key, it.value().value).boundingRect().contains(pos))
18773  return mParentPlot->selectionTolerance()*0.99;
18774  }
18775  }
18776  return -1;
18777 }
18778 
18779 /* inherits documentation from base class */
18781 {
18782  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
18783  if (mData->isEmpty()) return;
18784 
18785  QCPBarDataMap::const_iterator it, lower, upperEnd;
18786  getVisibleDataBounds(lower, upperEnd);
18787  for (it = lower; it != upperEnd; ++it)
18788  {
18789  // check data validity if flag set:
18790 #ifdef QCUSTOMPLOT_CHECK_DATA
18791  if (QCP::isInvalidData(it.value().key, it.value().value))
18792  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
18793 #endif
18794  QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
18795  // draw bar fill:
18796  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
18797  {
18798  applyFillAntialiasingHint(painter);
18799  painter->setPen(Qt::NoPen);
18800  painter->setBrush(mainBrush());
18801  painter->drawPolygon(barPolygon);
18802  }
18803  // draw bar line:
18804  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
18805  {
18807  painter->setPen(mainPen());
18808  painter->setBrush(Qt::NoBrush);
18809  painter->drawPolyline(barPolygon);
18810  }
18811  }
18812 }
18813 
18814 /* inherits documentation from base class */
18815 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
18816 {
18817  // draw filled rect:
18819  painter->setBrush(mBrush);
18820  painter->setPen(mPen);
18821  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
18822  r.moveCenter(rect.center());
18823  painter->drawRect(r);
18824 }
18825 
18840 void QCPBars::getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
18841 {
18842  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
18843  if (mData->isEmpty())
18844  {
18845  lower = mData->constEnd();
18846  upperEnd = mData->constEnd();
18847  return;
18848  }
18849 
18850  // get visible data range as QMap iterators
18851  lower = mData->lowerBound(mKeyAxis.data()->range().lower);
18852  upperEnd = mData->upperBound(mKeyAxis.data()->range().upper);
18853  double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
18854  double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
18855  bool isVisible = false;
18856  // walk left from lbound to find lower bar that actually is completely outside visible pixel range:
18857  QCPBarDataMap::const_iterator it = lower;
18858  while (it != mData->constBegin())
18859  {
18860  --it;
18861  QRectF barBounds = getBarPolygon(it.value().key, it.value().value).boundingRect();
18862  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18863  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.left() <= lowerPixelBound));
18864  else // keyaxis is vertical
18865  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= lowerPixelBound));
18866  if (isVisible)
18867  lower = it;
18868  else
18869  break;
18870  }
18871  // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
18872  it = upperEnd;
18873  while (it != mData->constEnd())
18874  {
18875  QRectF barBounds = getBarPolygon(upperEnd.value().key, upperEnd.value().value).boundingRect();
18876  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18877  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.right() >= upperPixelBound));
18878  else // keyaxis is vertical
18879  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.top() <= upperPixelBound));
18880  if (isVisible)
18881  upperEnd = it+1;
18882  else
18883  break;
18884  ++it;
18885  }
18886 }
18887 
18894 QPolygonF QCPBars::getBarPolygon(double key, double value) const
18895 {
18896  QCPAxis *keyAxis = mKeyAxis.data();
18897  QCPAxis *valueAxis = mValueAxis.data();
18898  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
18899 
18900  QPolygonF result;
18901  double lowerPixelWidth, upperPixelWidth;
18902  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
18903  double base = getStackedBaseValue(key, value >= 0);
18904  double basePixel = valueAxis->coordToPixel(base);
18905  double valuePixel = valueAxis->coordToPixel(base+value);
18906  double keyPixel = keyAxis->coordToPixel(key);
18907  if (mBarsGroup)
18908  keyPixel += mBarsGroup->keyPixelOffset(this, key);
18909  if (keyAxis->orientation() == Qt::Horizontal)
18910  {
18911  result << QPointF(keyPixel+lowerPixelWidth, basePixel);
18912  result << QPointF(keyPixel+lowerPixelWidth, valuePixel);
18913  result << QPointF(keyPixel+upperPixelWidth, valuePixel);
18914  result << QPointF(keyPixel+upperPixelWidth, basePixel);
18915  } else
18916  {
18917  result << QPointF(basePixel, keyPixel+lowerPixelWidth);
18918  result << QPointF(valuePixel, keyPixel+lowerPixelWidth);
18919  result << QPointF(valuePixel, keyPixel+upperPixelWidth);
18920  result << QPointF(basePixel, keyPixel+upperPixelWidth);
18921  }
18922  return result;
18923 }
18924 
18934 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
18935 {
18936  switch (mWidthType)
18937  {
18938  case wtAbsolute:
18939  {
18940  upper = mWidth*0.5;
18941  lower = -upper;
18942  if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18943  qSwap(lower, upper);
18944  break;
18945  }
18946  case wtAxisRectRatio:
18947  {
18948  if (mKeyAxis && mKeyAxis.data()->axisRect())
18949  {
18950  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18951  upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5;
18952  else
18953  upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5;
18954  lower = -upper;
18955  if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18956  qSwap(lower, upper);
18957  } else
18958  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
18959  break;
18960  }
18961  case wtPlotCoords:
18962  {
18963  if (mKeyAxis)
18964  {
18965  double keyPixel = mKeyAxis.data()->coordToPixel(key);
18966  upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
18967  lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
18968  // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
18969  // coordinate transform which includes range direction
18970  } else
18971  qDebug() << Q_FUNC_INFO << "No key axis defined";
18972  break;
18973  }
18974  }
18975 }
18976 
18986 double QCPBars::getStackedBaseValue(double key, bool positive) const
18987 {
18988  if (mBarBelow)
18989  {
18990  double max = 0; // don't use mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
18991  // find bars of mBarBelow that are approximately at key and find largest one:
18992  double epsilon = qAbs(key)*1e-6; // should be safe even when changed to use float at some point
18993  if (key == 0)
18994  epsilon = 1e-6;
18995  QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-epsilon);
18996  QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+epsilon);
18997  while (it != itEnd)
18998  {
18999  if ((positive && it.value().value > max) ||
19000  (!positive && it.value().value < max))
19001  max = it.value().value;
19002  ++it;
19003  }
19004  // recurse down the bar-stack to find the total height:
19005  return max + mBarBelow.data()->getStackedBaseValue(key, positive);
19006  } else
19007  return mBaseValue;
19008 }
19009 
19019 {
19020  if (!lower && !upper) return;
19021 
19022  if (!lower) // disconnect upper at bottom
19023  {
19024  // disconnect old bar below upper:
19025  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19026  upper->mBarBelow.data()->mBarAbove = 0;
19027  upper->mBarBelow = 0;
19028  } else if (!upper) // disconnect lower at top
19029  {
19030  // disconnect old bar above lower:
19031  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19032  lower->mBarAbove.data()->mBarBelow = 0;
19033  lower->mBarAbove = 0;
19034  } else // connect lower and upper
19035  {
19036  // disconnect old bar above lower:
19037  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19038  lower->mBarAbove.data()->mBarBelow = 0;
19039  // disconnect old bar below upper:
19040  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19041  upper->mBarBelow.data()->mBarAbove = 0;
19042  lower->mBarAbove = upper;
19043  upper->mBarBelow = lower;
19044  }
19045 }
19046 
19047 /* inherits documentation from base class */
19048 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19049 {
19050  QCPRange range;
19051  bool haveLower = false;
19052  bool haveUpper = false;
19053 
19054  double current;
19055  QCPBarDataMap::const_iterator it = mData->constBegin();
19056  while (it != mData->constEnd())
19057  {
19058  current = it.value().key;
19059  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19060  {
19061  if (current < range.lower || !haveLower)
19062  {
19063  range.lower = current;
19064  haveLower = true;
19065  }
19066  if (current > range.upper || !haveUpper)
19067  {
19068  range.upper = current;
19069  haveUpper = true;
19070  }
19071  }
19072  ++it;
19073  }
19074  // determine exact range of bars by including bar width and barsgroup offset:
19075  if (haveLower && mKeyAxis)
19076  {
19077  double lowerPixelWidth, upperPixelWidth, keyPixel;
19078  getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
19079  keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
19080  if (mBarsGroup)
19081  keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
19082  range.lower = mKeyAxis.data()->pixelToCoord(keyPixel);
19083  }
19084  if (haveUpper && mKeyAxis)
19085  {
19086  double lowerPixelWidth, upperPixelWidth, keyPixel;
19087  getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
19088  keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
19089  if (mBarsGroup)
19090  keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
19091  range.upper = mKeyAxis.data()->pixelToCoord(keyPixel);
19092  }
19093  foundRange = haveLower && haveUpper;
19094  return range;
19095 }
19096 
19097 /* inherits documentation from base class */
19098 QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19099 {
19100  QCPRange range;
19101  range.lower = mBaseValue;
19102  range.upper = mBaseValue;
19103  bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
19104  bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
19105  double current;
19106 
19107  QCPBarDataMap::const_iterator it = mData->constBegin();
19108  while (it != mData->constEnd())
19109  {
19110  current = it.value().value + getStackedBaseValue(it.value().key, it.value().value >= 0);
19111  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19112  {
19113  if (current < range.lower || !haveLower)
19114  {
19115  range.lower = current;
19116  haveLower = true;
19117  }
19118  if (current > range.upper || !haveUpper)
19119  {
19120  range.upper = current;
19121  haveUpper = true;
19122  }
19123  }
19124  ++it;
19125  }
19126 
19127  foundRange = true; // return true because bar charts always have the 0-line visible
19128  return range;
19129 }
19130 
19131 
19135 
19189  QCPAbstractPlottable(keyAxis, valueAxis),
19190  mKey(0),
19191  mMinimum(0),
19192  mLowerQuartile(0),
19193  mMedian(0),
19194  mUpperQuartile(0),
19195  mMaximum(0)
19196 {
19198  setWhiskerWidth(0.2);
19199  setWidth(0.5);
19200 
19201  setPen(QPen(Qt::black));
19202  setSelectedPen(QPen(Qt::blue, 2.5));
19203  setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
19204  setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
19205  setWhiskerBarPen(QPen(Qt::black));
19206  setBrush(Qt::NoBrush);
19207  setSelectedBrush(Qt::NoBrush);
19208 }
19209 
19214 {
19215  mKey = key;
19216 }
19217 
19225 {
19226  mMinimum = value;
19227 }
19228 
19237 {
19238  mLowerQuartile = value;
19239 }
19240 
19249 {
19250  mMedian = value;
19251 }
19252 
19261 {
19262  mUpperQuartile = value;
19263 }
19264 
19272 {
19273  mMaximum = value;
19274 }
19275 
19283 void QCPStatisticalBox::setOutliers(const QVector<double> &values)
19284 {
19285  mOutliers = values;
19286 }
19287 
19293 void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
19294 {
19295  setKey(key);
19296  setMinimum(minimum);
19297  setLowerQuartile(lowerQuartile);
19298  setMedian(median);
19299  setUpperQuartile(upperQuartile);
19300  setMaximum(maximum);
19301 }
19302 
19309 {
19310  mWidth = width;
19311 }
19312 
19319 {
19320  mWhiskerWidth = width;
19321 }
19322 
19332 {
19333  mWhiskerPen = pen;
19334 }
19335 
19343 {
19344  mWhiskerBarPen = pen;
19345 }
19346 
19350 void QCPStatisticalBox::setMedianPen(const QPen &pen)
19351 {
19352  mMedianPen = pen;
19353 }
19354 
19361 {
19362  mOutlierStyle = style;
19363 }
19364 
19365 /* inherits documentation from base class */
19367 {
19368  setOutliers(QVector<double>());
19369  setKey(0);
19370  setMinimum(0);
19371  setLowerQuartile(0);
19372  setMedian(0);
19373  setUpperQuartile(0);
19374  setMaximum(0);
19375 }
19376 
19377 /* inherits documentation from base class */
19378 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19379 {
19380  Q_UNUSED(details)
19381  if (onlySelectable && !mSelectable)
19382  return -1;
19383  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19384 
19385  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19386  {
19387  double posKey, posValue;
19388  pixelsToCoords(pos, posKey, posValue);
19389  // quartile box:
19390  QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19391  QCPRange valueRange(mLowerQuartile, mUpperQuartile);
19392  if (keyRange.contains(posKey) && valueRange.contains(posValue))
19393  return mParentPlot->selectionTolerance()*0.99;
19394 
19395  // min/max whiskers:
19396  if (QCPRange(mMinimum, mMaximum).contains(posValue))
19397  return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
19398  }
19399  return -1;
19400 }
19401 
19402 /* inherits documentation from base class */
19404 {
19405  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
19406 
19407  // check data validity if flag set:
19408 #ifdef QCUSTOMPLOT_CHECK_DATA
19412  qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
19413  for (int i=0; i<mOutliers.size(); ++i)
19414  if (QCP::isInvalidData(mOutliers.at(i)))
19415  qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
19416 #endif
19417 
19418  QRectF quartileBox;
19419  drawQuartileBox(painter, &quartileBox);
19420 
19421  painter->save();
19422  painter->setClipRect(quartileBox, Qt::IntersectClip);
19423  drawMedian(painter);
19424  painter->restore();
19425 
19426  drawWhiskers(painter);
19427  drawOutliers(painter);
19428 }
19429 
19430 /* inherits documentation from base class */
19431 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19432 {
19433  // draw filled rect:
19435  painter->setPen(mPen);
19436  painter->setBrush(mBrush);
19437  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
19438  r.moveCenter(rect.center());
19439  painter->drawRect(r);
19440 }
19441 
19448 void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
19449 {
19450  QRectF box;
19451  box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
19452  box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
19454  painter->setPen(mainPen());
19455  painter->setBrush(mainBrush());
19456  painter->drawRect(box);
19457  if (quartileBox)
19458  *quartileBox = box;
19459 }
19460 
19466 {
19467  QLineF medianLine;
19468  medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
19469  medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
19471  painter->setPen(mMedianPen);
19472  painter->drawLine(medianLine);
19473 }
19474 
19480 {
19481  QLineF backboneMin, backboneMax, barMin, barMax;
19482  backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
19483  backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
19487  painter->setPen(mWhiskerPen);
19488  painter->drawLine(backboneMin);
19489  painter->drawLine(backboneMax);
19490  painter->setPen(mWhiskerBarPen);
19491  painter->drawLine(barMin);
19492  painter->drawLine(barMax);
19493 }
19494 
19500 {
19502  mOutlierStyle.applyTo(painter, mPen);
19503  for (int i=0; i<mOutliers.size(); ++i)
19505 }
19506 
19507 /* inherits documentation from base class */
19508 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19509 {
19510  foundRange = true;
19511  if (inSignDomain == sdBoth)
19512  {
19513  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19514  } else if (inSignDomain == sdNegative)
19515  {
19516  if (mKey+mWidth*0.5 < 0)
19517  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19518  else if (mKey < 0)
19519  return QCPRange(mKey-mWidth*0.5, mKey);
19520  else
19521  {
19522  foundRange = false;
19523  return QCPRange();
19524  }
19525  } else if (inSignDomain == sdPositive)
19526  {
19527  if (mKey-mWidth*0.5 > 0)
19528  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19529  else if (mKey > 0)
19530  return QCPRange(mKey, mKey+mWidth*0.5);
19531  else
19532  {
19533  foundRange = false;
19534  return QCPRange();
19535  }
19536  }
19537  foundRange = false;
19538  return QCPRange();
19539 }
19540 
19541 /* inherits documentation from base class */
19542 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19543 {
19544  QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
19545  values.reserve(mOutliers.size() + 5);
19546  values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
19547  values << mOutliers;
19548  // go through values and find the ones in legal range:
19549  bool haveUpper = false;
19550  bool haveLower = false;
19551  double upper = 0;
19552  double lower = 0;
19553  for (int i=0; i<values.size(); ++i)
19554  {
19555  if ((inSignDomain == sdNegative && values.at(i) < 0) ||
19556  (inSignDomain == sdPositive && values.at(i) > 0) ||
19557  (inSignDomain == sdBoth))
19558  {
19559  if (values.at(i) > upper || !haveUpper)
19560  {
19561  upper = values.at(i);
19562  haveUpper = true;
19563  }
19564  if (values.at(i) < lower || !haveLower)
19565  {
19566  lower = values.at(i);
19567  haveLower = true;
19568  }
19569  }
19570  }
19571  // return the bounds if we found some sensible values:
19572  if (haveLower && haveUpper)
19573  {
19574  foundRange = true;
19575  return QCPRange(lower, upper);
19576  } else // might happen if all values are in other sign domain
19577  {
19578  foundRange = false;
19579  return QCPRange();
19580  }
19581 }
19582 
19583 
19587 
19616 /* start of documentation of inline functions */
19617 
19624 /* end of documentation of inline functions */
19625 
19633 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
19634  mKeySize(0),
19635  mValueSize(0),
19636  mKeyRange(keyRange),
19637  mValueRange(valueRange),
19638  mIsEmpty(true),
19639  mData(0),
19640  mDataModified(true)
19641 {
19642  setSize(keySize, valueSize);
19643  fill(0);
19644 }
19645 
19647 {
19648  if (mData)
19649  delete[] mData;
19650 }
19651 
19656  mKeySize(0),
19657  mValueSize(0),
19658  mIsEmpty(true),
19659  mData(0),
19660  mDataModified(true)
19661 {
19662  *this = other;
19663 }
19664 
19669 {
19670  if (&other != this)
19671  {
19672  const int keySize = other.keySize();
19673  const int valueSize = other.valueSize();
19674  setSize(keySize, valueSize);
19675  setRange(other.keyRange(), other.valueRange());
19676  if (!mIsEmpty)
19677  memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
19678  mDataBounds = other.mDataBounds;
19679  mDataModified = true;
19680  }
19681  return *this;
19682 }
19683 
19684 /* undocumented getter */
19685 double QCPColorMapData::data(double key, double value)
19686 {
19687  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19688  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19689  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19690  return mData[valueCell*mKeySize + keyCell];
19691  else
19692  return 0;
19693 }
19694 
19695 /* undocumented getter */
19696 double QCPColorMapData::cell(int keyIndex, int valueIndex)
19697 {
19698  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19699  return mData[valueIndex*mKeySize + keyIndex];
19700  else
19701  return 0;
19702 }
19703 
19716 void QCPColorMapData::setSize(int keySize, int valueSize)
19717 {
19718  if (keySize != mKeySize || valueSize != mValueSize)
19719  {
19720  mKeySize = keySize;
19722  if (mData)
19723  delete[] mData;
19724  mIsEmpty = mKeySize == 0 || mValueSize == 0;
19725  if (!mIsEmpty)
19726  {
19727 #ifdef __EXCEPTIONS
19728  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
19729 #endif
19730  mData = new double[mKeySize*mValueSize];
19731 #ifdef __EXCEPTIONS
19732  } catch (...) { mData = 0; }
19733 #endif
19734  if (mData)
19735  fill(0);
19736  else
19737  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
19738  } else
19739  mData = 0;
19740  mDataModified = true;
19741  }
19742 }
19743 
19755 {
19756  setSize(keySize, mValueSize);
19757 }
19758 
19770 {
19771  setSize(mKeySize, valueSize);
19772 }
19773 
19784 void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
19785 {
19786  setKeyRange(keyRange);
19787  setValueRange(valueRange);
19788 }
19789 
19801 {
19802  mKeyRange = keyRange;
19803 }
19804 
19816 {
19818 }
19819 
19832 void QCPColorMapData::setData(double key, double value, double z)
19833 {
19834  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19835  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19836  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19837  {
19838  mData[valueCell*mKeySize + keyCell] = z;
19839  if (z < mDataBounds.lower)
19840  mDataBounds.lower = z;
19841  if (z > mDataBounds.upper)
19842  mDataBounds.upper = z;
19843  mDataModified = true;
19844  }
19845 }
19846 
19858 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
19859 {
19860  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19861  {
19862  mData[valueIndex*mKeySize + keyIndex] = z;
19863  if (z < mDataBounds.lower)
19864  mDataBounds.lower = z;
19865  if (z > mDataBounds.upper)
19866  mDataBounds.upper = z;
19867  mDataModified = true;
19868  }
19869 }
19870 
19885 {
19886  if (mKeySize > 0 && mValueSize > 0)
19887  {
19888  double minHeight = mData[0];
19889  double maxHeight = mData[0];
19890  const int dataCount = mValueSize*mKeySize;
19891  for (int i=0; i<dataCount; ++i)
19892  {
19893  if (mData[i] > maxHeight)
19894  maxHeight = mData[i];
19895  if (mData[i] < minHeight)
19896  minHeight = mData[i];
19897  }
19898  mDataBounds.lower = minHeight;
19899  mDataBounds.upper = maxHeight;
19900  }
19901 }
19902 
19909 {
19910  setSize(0, 0);
19911 }
19912 
19917 {
19918  const int dataCount = mValueSize*mKeySize;
19919  for (int i=0; i<dataCount; ++i)
19920  mData[i] = z;
19921  mDataBounds = QCPRange(z, z);
19922  mDataModified = true;
19923 }
19924 
19942 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
19943 {
19944  if (keyIndex)
19945  *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19946  if (valueIndex)
19947  *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19948 }
19949 
19965 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
19966 {
19967  if (key)
19968  *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
19969  if (value)
19970  *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
19971 }
19972 
19973 
19977 
20036 /* start documentation of inline functions */
20037 
20046 /* end documentation of inline functions */
20047 
20048 /* start documentation of signals */
20049 
20071 /* end documentation of signals */
20072 
20080  QCPAbstractPlottable(keyAxis, valueAxis),
20081  mDataScaleType(QCPAxis::stLinear),
20082  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
20083  mInterpolate(true),
20084  mTightBoundary(false),
20085  mMapImageInvalidated(true)
20086 {
20087 }
20088 
20090 {
20091  delete mMapData;
20092 }
20093 
20102 {
20103  if (mMapData == data)
20104  {
20105  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20106  return;
20107  }
20108  if (copy)
20109  {
20110  *mMapData = *data;
20111  } else
20112  {
20113  delete mMapData;
20114  mMapData = data;
20115  }
20116  mMapImageInvalidated = true;
20117 }
20118 
20127 void QCPColorMap::setDataRange(const QCPRange &dataRange)
20128 {
20129  if (!QCPRange::validRange(dataRange)) return;
20130  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
20131  {
20133  mDataRange = dataRange.sanitizedForLogScale();
20134  else
20135  mDataRange = dataRange.sanitizedForLinScale();
20136  mMapImageInvalidated = true;
20138  }
20139 }
20140 
20147 {
20148  if (mDataScaleType != scaleType)
20149  {
20150  mDataScaleType = scaleType;
20151  mMapImageInvalidated = true;
20155  }
20156 }
20157 
20170 {
20171  if (mGradient != gradient)
20172  {
20173  mGradient = gradient;
20174  mMapImageInvalidated = true;
20175  emit gradientChanged(mGradient);
20176  }
20177 }
20178 
20186 {
20187  mInterpolate = enabled;
20188  mMapImageInvalidated = true; // because oversampling factors might need to change
20189 }
20190 
20203 {
20204  mTightBoundary = enabled;
20205 }
20206 
20222 {
20223  if (mColorScale) // unconnect signals from old color scale
20224  {
20225  disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20226  disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20227  disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20228  disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20229  disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20230  disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20231  }
20233  if (mColorScale) // connect signals to new color scale
20234  {
20235  setGradient(mColorScale.data()->gradient());
20236  setDataRange(mColorScale.data()->dataRange());
20237  setDataScaleType(mColorScale.data()->dataScaleType());
20238  connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20240  connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20241  connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20242  connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20244  }
20245 }
20246 
20267 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
20268 {
20269  if (recalculateDataBounds)
20272 }
20273 
20288 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
20289 {
20290  if (mMapImage.isNull() && !data()->isEmpty())
20291  updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
20292 
20293  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
20294  {
20295  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20296  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20297  mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
20298  }
20299 }
20300 
20306 {
20307  mMapData->clear();
20308 }
20309 
20310 /* inherits documentation from base class */
20311 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20312 {
20313  Q_UNUSED(details)
20314  if (onlySelectable && !mSelectable)
20315  return -1;
20316  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20317 
20318  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20319  {
20320  double posKey, posValue;
20321  pixelsToCoords(pos, posKey, posValue);
20322  if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
20323  return mParentPlot->selectionTolerance()*0.99;
20324  }
20325  return -1;
20326 }
20327 
20343 {
20344  QCPAxis *keyAxis = mKeyAxis.data();
20345  if (!keyAxis) return;
20346  if (mMapData->isEmpty()) return;
20347 
20348  const int keySize = mMapData->keySize();
20349  const int valueSize = mMapData->valueSize();
20350  int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
20351  int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
20352 
20353  // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
20354  if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
20355  mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), QImage::Format_RGB32);
20356  else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
20357  mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), QImage::Format_RGB32);
20358 
20359  QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage
20360  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20361  {
20362  // resize undersampled map image to actual key/value cell sizes:
20363  if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
20364  mUndersampledMapImage = QImage(QSize(keySize, valueSize), QImage::Format_RGB32);
20365  else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
20366  mUndersampledMapImage = QImage(QSize(valueSize, keySize), QImage::Format_RGB32);
20367  localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image
20368  } else if (!mUndersampledMapImage.isNull())
20369  mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it
20370 
20371  const double *rawData = mMapData->mData;
20372  if (keyAxis->orientation() == Qt::Horizontal)
20373  {
20374  const int lineCount = valueSize;
20375  const int rowCount = keySize;
20376  for (int line=0; line<lineCount; ++line)
20377  {
20378  QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
20379  mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
20380  }
20381  } else // keyAxis->orientation() == Qt::Vertical
20382  {
20383  const int lineCount = keySize;
20384  const int rowCount = valueSize;
20385  for (int line=0; line<lineCount; ++line)
20386  {
20387  QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
20388  mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
20389  }
20390  }
20391 
20392  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20393  {
20394  if (keyAxis->orientation() == Qt::Horizontal)
20395  mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20396  else
20397  mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20398  }
20399  mMapData->mDataModified = false;
20400  mMapImageInvalidated = false;
20401 }
20402 
20403 /* inherits documentation from base class */
20405 {
20406  if (mMapData->isEmpty()) return;
20407  if (!mKeyAxis || !mValueAxis) return;
20409 
20411  updateMapImage();
20412 
20413  // use buffer if painting vectorized (PDF):
20414  bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
20415  QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized
20416  QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in
20417  QPixmap mapBuffer;
20418  double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps
20419  if (useBuffer)
20420  {
20421  mapBufferTarget = painter->clipRegion().boundingRect();
20422  mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
20423  mapBuffer.fill(Qt::transparent);
20424  localPainter = new QCPPainter(&mapBuffer);
20425  localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
20426  localPainter->translate(-mapBufferTarget.topLeft());
20427  }
20428 
20429  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20431  // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):
20432  double halfCellWidth = 0; // in pixels
20433  double halfCellHeight = 0; // in pixels
20434  if (keyAxis()->orientation() == Qt::Horizontal)
20435  {
20436  if (mMapData->keySize() > 1)
20437  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
20438  if (mMapData->valueSize() > 1)
20439  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
20440  } else // keyAxis orientation is Qt::Vertical
20441  {
20442  if (mMapData->keySize() > 1)
20443  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
20444  if (mMapData->valueSize() > 1)
20445  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
20446  }
20447  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
20448  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20449  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20450  bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
20451  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
20452  QRegion clipBackup;
20453  if (mTightBoundary)
20454  {
20455  clipBackup = localPainter->clipRegion();
20456  QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20458  localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
20459  }
20460  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
20461  if (mTightBoundary)
20462  localPainter->setClipRegion(clipBackup);
20463  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
20464 
20465  if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter
20466  {
20467  delete localPainter;
20468  painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
20469  }
20470 }
20471 
20472 /* inherits documentation from base class */
20473 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20474 {
20476  // draw map thumbnail:
20477  if (!mLegendIcon.isNull())
20478  {
20479  QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
20480  QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
20481  iconRect.moveCenter(rect.center());
20482  painter->drawPixmap(iconRect.topLeft(), scaledIcon);
20483  }
20484  /*
20485  // draw frame:
20486  painter->setBrush(Qt::NoBrush);
20487  painter->setPen(Qt::black);
20488  painter->drawRect(rect.adjusted(1, 1, 0, 0));
20489  */
20490 }
20491 
20492 /* inherits documentation from base class */
20493 QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
20494 {
20495  foundRange = true;
20496  QCPRange result = mMapData->keyRange();
20497  result.normalize();
20498  if (inSignDomain == QCPAbstractPlottable::sdPositive)
20499  {
20500  if (result.lower <= 0 && result.upper > 0)
20501  result.lower = result.upper*1e-3;
20502  else if (result.lower <= 0 && result.upper <= 0)
20503  foundRange = false;
20504  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20505  {
20506  if (result.upper >= 0 && result.lower < 0)
20507  result.upper = result.lower*1e-3;
20508  else if (result.upper >= 0 && result.lower >= 0)
20509  foundRange = false;
20510  }
20511  return result;
20512 }
20513 
20514 /* inherits documentation from base class */
20515 QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
20516 {
20517  foundRange = true;
20518  QCPRange result = mMapData->valueRange();
20519  result.normalize();
20520  if (inSignDomain == QCPAbstractPlottable::sdPositive)
20521  {
20522  if (result.lower <= 0 && result.upper > 0)
20523  result.lower = result.upper*1e-3;
20524  else if (result.lower <= 0 && result.upper <= 0)
20525  foundRange = false;
20526  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20527  {
20528  if (result.upper >= 0 && result.lower < 0)
20529  result.upper = result.lower*1e-3;
20530  else if (result.upper >= 0 && result.lower >= 0)
20531  foundRange = false;
20532  }
20533  return result;
20534 }
20535 
20536 
20540 
20560  key(0),
20561  open(0),
20562  high(0),
20563  low(0),
20564  close(0)
20565 {
20566 }
20567 
20571 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
20572  key(key),
20573  open(open),
20574  high(high),
20575  low(low),
20576  close(close)
20577 {
20578 }
20579 
20580 
20584 
20617 /* start of documentation of inline functions */
20618 
20626 /* end of documentation of inline functions */
20627 
20638  QCPAbstractPlottable(keyAxis, valueAxis),
20639  mData(0),
20640  mChartStyle(csOhlc),
20641  mWidth(0.5),
20642  mTwoColored(false),
20643  mBrushPositive(QBrush(QColor(210, 210, 255))),
20644  mBrushNegative(QBrush(QColor(255, 210, 210))),
20645  mPenPositive(QPen(QColor(10, 40, 180))),
20646  mPenNegative(QPen(QColor(180, 40, 10)))
20647 {
20648  mData = new QCPFinancialDataMap;
20649 
20650  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
20651  setSelectedBrush(QBrush(QColor(80, 80, 255)));
20652 }
20653 
20655 {
20656  delete mData;
20657 }
20658 
20672 {
20673  if (mData == data)
20674  {
20675  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20676  return;
20677  }
20678  if (copy)
20679  {
20680  *mData = *data;
20681  } else
20682  {
20683  delete mData;
20684  mData = data;
20685  }
20686 }
20687 
20695 void QCPFinancial::setData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20696 {
20697  mData->clear();
20698  int n = key.size();
20699  n = qMin(n, open.size());
20700  n = qMin(n, high.size());
20701  n = qMin(n, low.size());
20702  n = qMin(n, close.size());
20703  for (int i=0; i<n; ++i)
20704  {
20705  mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20706  }
20707 }
20708 
20713 {
20714  mChartStyle = style;
20715 }
20716 
20722 void QCPFinancial::setWidth(double width)
20723 {
20724  mWidth = width;
20725 }
20726 
20736 void QCPFinancial::setTwoColored(bool twoColored)
20737 {
20739 }
20740 
20750 void QCPFinancial::setBrushPositive(const QBrush &brush)
20751 {
20753 }
20754 
20764 void QCPFinancial::setBrushNegative(const QBrush &brush)
20765 {
20767 }
20768 
20778 void QCPFinancial::setPenPositive(const QPen &pen)
20779 {
20780  mPenPositive = pen;
20781 }
20782 
20792 void QCPFinancial::setPenNegative(const QPen &pen)
20793 {
20794  mPenNegative = pen;
20795 }
20796 
20806 {
20807  mData->unite(dataMap);
20808 }
20809 
20820 {
20821  mData->insertMulti(data.key, data);
20822 }
20823 
20834 void QCPFinancial::addData(double key, double open, double high, double low, double close)
20835 {
20836  mData->insertMulti(key, QCPFinancialData(key, open, high, low, close));
20837 }
20838 
20848 void QCPFinancial::addData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20849 {
20850  int n = key.size();
20851  n = qMin(n, open.size());
20852  n = qMin(n, high.size());
20853  n = qMin(n, low.size());
20854  n = qMin(n, close.size());
20855  for (int i=0; i<n; ++i)
20856  {
20857  mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20858  }
20859 }
20860 
20867 {
20868  QCPFinancialDataMap::iterator it = mData->begin();
20869  while (it != mData->end() && it.key() < key)
20870  it = mData->erase(it);
20871 }
20872 
20879 {
20880  if (mData->isEmpty()) return;
20881  QCPFinancialDataMap::iterator it = mData->upperBound(key);
20882  while (it != mData->end())
20883  it = mData->erase(it);
20884 }
20885 
20893 void QCPFinancial::removeData(double fromKey, double toKey)
20894 {
20895  if (fromKey >= toKey || mData->isEmpty()) return;
20896  QCPFinancialDataMap::iterator it = mData->upperBound(fromKey);
20897  QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey);
20898  while (it != itEnd)
20899  it = mData->erase(it);
20900 }
20901 
20911 {
20912  mData->remove(key);
20913 }
20914 
20921 {
20922  mData->clear();
20923 }
20924 
20925 /* inherits documentation from base class */
20926 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20927 {
20928  Q_UNUSED(details)
20929  if (onlySelectable && !mSelectable)
20930  return -1;
20931  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20932 
20933  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20934  {
20935  // get visible data range:
20936  QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20937  getVisibleDataBounds(lower, upper);
20938  if (lower == mData->constEnd() || upper == mData->constEnd())
20939  return -1;
20940  // perform select test according to configured style:
20941  switch (mChartStyle)
20942  {
20943  case QCPFinancial::csOhlc:
20944  return ohlcSelectTest(pos, lower, upper+1); break;
20946  return candlestickSelectTest(pos, lower, upper+1); break;
20947  }
20948  }
20949  return -1;
20950 }
20951 
20965 QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
20966 {
20967  QCPFinancialDataMap map;
20968  int count = qMin(time.size(), value.size());
20969  if (count == 0)
20970  return QCPFinancialDataMap();
20971 
20972  QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
20973  int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
20974  for (int i=0; i<count; ++i)
20975  {
20976  int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
20977  if (currentBinIndex == index) // data point still in current bin, extend high/low:
20978  {
20979  if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
20980  if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
20981  if (i == count-1) // last data point is in current bin, finalize bin:
20982  {
20983  currentBinData.close = value.at(i);
20984  currentBinData.key = timeBinOffset+(index)*timeBinSize;
20985  map.insert(currentBinData.key, currentBinData);
20986  }
20987  } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
20988  {
20989  // finalize current bin:
20990  currentBinData.close = value.at(i-1);
20991  currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
20992  map.insert(currentBinData.key, currentBinData);
20993  // start next bin:
20994  currentBinIndex = index;
20995  currentBinData.open = value.at(i);
20996  currentBinData.high = value.at(i);
20997  currentBinData.low = value.at(i);
20998  }
20999  }
21000 
21001  return map;
21002 }
21003 
21004 /* inherits documentation from base class */
21006 {
21007  // get visible data range:
21008  QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
21009  getVisibleDataBounds(lower, upper);
21010  if (lower == mData->constEnd() || upper == mData->constEnd())
21011  return;
21012 
21013  // draw visible data range according to configured style:
21014  switch (mChartStyle)
21015  {
21016  case QCPFinancial::csOhlc:
21017  drawOhlcPlot(painter, lower, upper+1); break;
21019  drawCandlestickPlot(painter, lower, upper+1); break;
21020  }
21021 }
21022 
21023 /* inherits documentation from base class */
21024 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
21025 {
21026  painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
21027  if (mChartStyle == csOhlc)
21028  {
21029  if (mTwoColored)
21030  {
21031  // draw upper left half icon with positive color:
21032  painter->setBrush(mBrushPositive);
21033  painter->setPen(mPenPositive);
21034  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21035  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21036  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21037  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21038  // draw bottom right hald icon with negative color:
21039  painter->setBrush(mBrushNegative);
21040  painter->setPen(mPenNegative);
21041  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21042  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21043  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21044  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21045  } else
21046  {
21047  painter->setBrush(mBrush);
21048  painter->setPen(mPen);
21049  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21050  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21051  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21052  }
21053  } else if (mChartStyle == csCandlestick)
21054  {
21055  if (mTwoColored)
21056  {
21057  // draw upper left half icon with positive color:
21058  painter->setBrush(mBrushPositive);
21059  painter->setPen(mPenPositive);
21060  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21061  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21062  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21063  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21064  // draw bottom right hald icon with negative color:
21065  painter->setBrush(mBrushNegative);
21066  painter->setPen(mPenNegative);
21067  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21068  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21069  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21070  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21071  } else
21072  {
21073  painter->setBrush(mBrush);
21074  painter->setPen(mPen);
21075  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21076  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21077  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21078  }
21079  }
21080 }
21081 
21082 /* inherits documentation from base class */
21084 {
21085  QCPRange range;
21086  bool haveLower = false;
21087  bool haveUpper = false;
21088 
21089  double current;
21090  QCPFinancialDataMap::const_iterator it = mData->constBegin();
21091  while (it != mData->constEnd())
21092  {
21093  current = it.value().key;
21094  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
21095  {
21096  if (current < range.lower || !haveLower)
21097  {
21098  range.lower = current;
21099  haveLower = true;
21100  }
21101  if (current > range.upper || !haveUpper)
21102  {
21103  range.upper = current;
21104  haveUpper = true;
21105  }
21106  }
21107  ++it;
21108  }
21109  // determine exact range by including width of bars/flags:
21110  if (haveLower && mKeyAxis)
21111  range.lower = range.lower-mWidth*0.5;
21112  if (haveUpper && mKeyAxis)
21113  range.upper = range.upper+mWidth*0.5;
21114  foundRange = haveLower && haveUpper;
21115  return range;
21116 }
21117 
21118 /* inherits documentation from base class */
21120 {
21121  QCPRange range;
21122  bool haveLower = false;
21123  bool haveUpper = false;
21124 
21125  QCPFinancialDataMap::const_iterator it = mData->constBegin();
21126  while (it != mData->constEnd())
21127  {
21128  // high:
21129  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().high < 0) || (inSignDomain == sdPositive && it.value().high > 0))
21130  {
21131  if (it.value().high < range.lower || !haveLower)
21132  {
21133  range.lower = it.value().high;
21134  haveLower = true;
21135  }
21136  if (it.value().high > range.upper || !haveUpper)
21137  {
21138  range.upper = it.value().high;
21139  haveUpper = true;
21140  }
21141  }
21142  // low:
21143  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().low < 0) || (inSignDomain == sdPositive && it.value().low > 0))
21144  {
21145  if (it.value().low < range.lower || !haveLower)
21146  {
21147  range.lower = it.value().low;
21148  haveLower = true;
21149  }
21150  if (it.value().low > range.upper || !haveUpper)
21151  {
21152  range.upper = it.value().low;
21153  haveUpper = true;
21154  }
21155  }
21156  ++it;
21157  }
21158 
21159  foundRange = haveLower && haveUpper;
21160  return range;
21161 }
21162 
21169 void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21170 {
21171  QCPAxis *keyAxis = mKeyAxis.data();
21172  QCPAxis *valueAxis = mValueAxis.data();
21173  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21174 
21175  QPen linePen;
21176 
21177  if (keyAxis->orientation() == Qt::Horizontal)
21178  {
21179  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21180  {
21181  if (mSelected)
21182  linePen = mSelectedPen;
21183  else if (mTwoColored)
21184  linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21185  else
21186  linePen = mPen;
21187  painter->setPen(linePen);
21188  double keyPixel = keyAxis->coordToPixel(it.value().key);
21189  double openPixel = valueAxis->coordToPixel(it.value().open);
21190  double closePixel = valueAxis->coordToPixel(it.value().close);
21191  // draw backbone:
21192  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
21193  // draw open:
21194  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21195  painter->drawLine(QPointF(keyPixel-keyWidthPixels, openPixel), QPointF(keyPixel, openPixel));
21196  // draw close:
21197  painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+keyWidthPixels, closePixel));
21198  }
21199  } else
21200  {
21201  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21202  {
21203  if (mSelected)
21204  linePen = mSelectedPen;
21205  else if (mTwoColored)
21206  linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21207  else
21208  linePen = mPen;
21209  painter->setPen(linePen);
21210  double keyPixel = keyAxis->coordToPixel(it.value().key);
21211  double openPixel = valueAxis->coordToPixel(it.value().open);
21212  double closePixel = valueAxis->coordToPixel(it.value().close);
21213  // draw backbone:
21214  painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
21215  // draw open:
21216  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21217  painter->drawLine(QPointF(openPixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel));
21218  // draw close:
21219  painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+keyWidthPixels));
21220  }
21221  }
21222 }
21223 
21230 void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21231 {
21232  QCPAxis *keyAxis = mKeyAxis.data();
21233  QCPAxis *valueAxis = mValueAxis.data();
21234  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21235 
21236  QPen linePen;
21237  QBrush boxBrush;
21238 
21239  if (keyAxis->orientation() == Qt::Horizontal)
21240  {
21241  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21242  {
21243  if (mSelected)
21244  {
21245  linePen = mSelectedPen;
21246  boxBrush = mSelectedBrush;
21247  } else if (mTwoColored)
21248  {
21249  if (it.value().close >= it.value().open)
21250  {
21251  linePen = mPenPositive;
21252  boxBrush = mBrushPositive;
21253  } else
21254  {
21255  linePen = mPenNegative;
21256  boxBrush = mBrushNegative;
21257  }
21258  } else
21259  {
21260  linePen = mPen;
21261  boxBrush = mBrush;
21262  }
21263  painter->setPen(linePen);
21264  painter->setBrush(boxBrush);
21265  double keyPixel = keyAxis->coordToPixel(it.value().key);
21266  double openPixel = valueAxis->coordToPixel(it.value().open);
21267  double closePixel = valueAxis->coordToPixel(it.value().close);
21268  // draw high:
21269  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))));
21270  // draw low:
21271  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))));
21272  // draw open-close box:
21273  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21274  painter->drawRect(QRectF(QPointF(keyPixel-keyWidthPixels, closePixel), QPointF(keyPixel+keyWidthPixels, openPixel)));
21275  }
21276  } else // keyAxis->orientation() == Qt::Vertical
21277  {
21278  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21279  {
21280  if (mSelected)
21281  {
21282  linePen = mSelectedPen;
21283  boxBrush = mSelectedBrush;
21284  } else if (mTwoColored)
21285  {
21286  if (it.value().close >= it.value().open)
21287  {
21288  linePen = mPenPositive;
21289  boxBrush = mBrushPositive;
21290  } else
21291  {
21292  linePen = mPenNegative;
21293  boxBrush = mBrushNegative;
21294  }
21295  } else
21296  {
21297  linePen = mPen;
21298  boxBrush = mBrush;
21299  }
21300  painter->setPen(linePen);
21301  painter->setBrush(boxBrush);
21302  double keyPixel = keyAxis->coordToPixel(it.value().key);
21303  double openPixel = valueAxis->coordToPixel(it.value().open);
21304  double closePixel = valueAxis->coordToPixel(it.value().close);
21305  // draw high:
21306  painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel));
21307  // draw low:
21308  painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel));
21309  // draw open-close box:
21310  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21311  painter->drawRect(QRectF(QPointF(closePixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel+keyWidthPixels)));
21312  }
21313  }
21314 }
21315 
21321 double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21322 {
21323  QCPAxis *keyAxis = mKeyAxis.data();
21324  QCPAxis *valueAxis = mValueAxis.data();
21325  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21326 
21327  double minDistSqr = std::numeric_limits<double>::max();
21328  QCPFinancialDataMap::const_iterator it;
21329  if (keyAxis->orientation() == Qt::Horizontal)
21330  {
21331  for (it = begin; it != end; ++it)
21332  {
21333  double keyPixel = keyAxis->coordToPixel(it.value().key);
21334  // calculate distance to backbone:
21335  double currentDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos);
21336  if (currentDistSqr < minDistSqr)
21337  minDistSqr = currentDistSqr;
21338  }
21339  } else // keyAxis->orientation() == Qt::Vertical
21340  {
21341  for (it = begin; it != end; ++it)
21342  {
21343  double keyPixel = keyAxis->coordToPixel(it.value().key);
21344  // calculate distance to backbone:
21345  double currentDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos);
21346  if (currentDistSqr < minDistSqr)
21347  minDistSqr = currentDistSqr;
21348  }
21349  }
21350  return qSqrt(minDistSqr);
21351 }
21352 
21359 double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21360 {
21361  QCPAxis *keyAxis = mKeyAxis.data();
21362  QCPAxis *valueAxis = mValueAxis.data();
21363  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21364 
21365  double minDistSqr = std::numeric_limits<double>::max();
21366  QCPFinancialDataMap::const_iterator it;
21367  if (keyAxis->orientation() == Qt::Horizontal)
21368  {
21369  for (it = begin; it != end; ++it)
21370  {
21371  double currentDistSqr;
21372  // determine whether pos is in open-close-box:
21373  QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21374  QCPRange boxValueRange(it.value().close, it.value().open);
21375  double posKey, posValue;
21376  pixelsToCoords(pos, posKey, posValue);
21377  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21378  {
21379  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21380  } else
21381  {
21382  // calculate distance to high/low lines:
21383  double keyPixel = keyAxis->coordToPixel(it.value().key);
21384  double highLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))), pos);
21385  double lowLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))), pos);
21386  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21387  }
21388  if (currentDistSqr < minDistSqr)
21389  minDistSqr = currentDistSqr;
21390  }
21391  } else // keyAxis->orientation() == Qt::Vertical
21392  {
21393  for (it = begin; it != end; ++it)
21394  {
21395  double currentDistSqr;
21396  // determine whether pos is in open-close-box:
21397  QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21398  QCPRange boxValueRange(it.value().close, it.value().open);
21399  double posKey, posValue;
21400  pixelsToCoords(pos, posKey, posValue);
21401  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21402  {
21403  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21404  } else
21405  {
21406  // calculate distance to high/low lines:
21407  double keyPixel = keyAxis->coordToPixel(it.value().key);
21408  double highLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel), pos);
21409  double lowLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel), pos);
21410  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21411  }
21412  if (currentDistSqr < minDistSqr)
21413  minDistSqr = currentDistSqr;
21414  }
21415  }
21416  return qSqrt(minDistSqr);
21417 }
21418 
21435 void QCPFinancial::getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
21436 {
21437  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
21438  if (mData->isEmpty())
21439  {
21440  lower = mData->constEnd();
21441  upper = mData->constEnd();
21442  return;
21443  }
21444 
21445  // get visible data range as QMap iterators
21446  QCPFinancialDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
21447  QCPFinancialDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
21448  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
21449  bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
21450 
21451  lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
21452  upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
21453 }
21454 
21455 
21459 
21474  QCPAbstractItem(parentPlot),
21475  point1(createPosition(QLatin1String("point1"))),
21476  point2(createPosition(QLatin1String("point2")))
21477 {
21478  point1->setCoords(0, 0);
21479  point2->setCoords(1, 1);
21480 
21481  setPen(QPen(Qt::black));
21482  setSelectedPen(QPen(Qt::blue,2));
21483 }
21484 
21486 {
21487 }
21488 
21494 void QCPItemStraightLine::setPen(const QPen &pen)
21495 {
21496  mPen = pen;
21497 }
21498 
21505 {
21506  mSelectedPen = pen;
21507 }
21508 
21509 /* inherits documentation from base class */
21510 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21511 {
21512  Q_UNUSED(details)
21513  if (onlySelectable && !mSelectable)
21514  return -1;
21515 
21516  return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
21517 }
21518 
21519 /* inherits documentation from base class */
21521 {
21522  QVector2D start(point1->pixelPoint());
21523  QVector2D end(point2->pixelPoint());
21524  // get visible segment of straight line inside clipRect:
21525  double clipPad = mainPen().widthF();
21526  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21527  // paint visible segment, if existent:
21528  if (!line.isNull())
21529  {
21530  painter->setPen(mainPen());
21531  painter->drawLine(line);
21532  }
21533 }
21534 
21542 double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
21543 {
21544  return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
21545 }
21546 
21554 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
21555 {
21556  double bx, by;
21557  double gamma;
21558  QLineF result;
21559  if (vec.x() == 0 && vec.y() == 0)
21560  return result;
21561  if (qFuzzyIsNull(vec.x())) // line is vertical
21562  {
21563  // check top of rect:
21564  bx = rect.left();
21565  by = rect.top();
21566  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21567  if (gamma >= 0 && gamma <= rect.width())
21568  result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
21569  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
21570  {
21571  // check left of rect:
21572  bx = rect.left();
21573  by = rect.top();
21574  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21575  if (gamma >= 0 && gamma <= rect.height())
21576  result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
21577  } else // line is skewed
21578  {
21579  QList<QVector2D> pointVectors;
21580  // check top of rect:
21581  bx = rect.left();
21582  by = rect.top();
21583  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21584  if (gamma >= 0 && gamma <= rect.width())
21585  pointVectors.append(QVector2D(bx+gamma, by));
21586  // check bottom of rect:
21587  bx = rect.left();
21588  by = rect.bottom();
21589  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21590  if (gamma >= 0 && gamma <= rect.width())
21591  pointVectors.append(QVector2D(bx+gamma, by));
21592  // check left of rect:
21593  bx = rect.left();
21594  by = rect.top();
21595  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21596  if (gamma >= 0 && gamma <= rect.height())
21597  pointVectors.append(QVector2D(bx, by+gamma));
21598  // check right of rect:
21599  bx = rect.right();
21600  by = rect.top();
21601  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21602  if (gamma >= 0 && gamma <= rect.height())
21603  pointVectors.append(QVector2D(bx, by+gamma));
21604 
21605  // evaluate points:
21606  if (pointVectors.size() == 2)
21607  {
21608  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21609  } else if (pointVectors.size() > 2)
21610  {
21611  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21612  double distSqrMax = 0;
21613  QVector2D pv1, pv2;
21614  for (int i=0; i<pointVectors.size()-1; ++i)
21615  {
21616  for (int k=i+1; k<pointVectors.size(); ++k)
21617  {
21618  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21619  if (distSqr > distSqrMax)
21620  {
21621  pv1 = pointVectors.at(i);
21622  pv2 = pointVectors.at(k);
21623  distSqrMax = distSqr;
21624  }
21625  }
21626  }
21627  result.setPoints(pv1.toPointF(), pv2.toPointF());
21628  }
21629  }
21630  return result;
21631 }
21632 
21639 {
21640  return mSelected ? mSelectedPen : mPen;
21641 }
21642 
21643 
21647 
21664  QCPAbstractItem(parentPlot),
21665  start(createPosition(QLatin1String("start"))),
21666  end(createPosition(QLatin1String("end")))
21667 {
21668  start->setCoords(0, 0);
21669  end->setCoords(1, 1);
21670 
21671  setPen(QPen(Qt::black));
21672  setSelectedPen(QPen(Qt::blue,2));
21673 }
21674 
21676 {
21677 }
21678 
21684 void QCPItemLine::setPen(const QPen &pen)
21685 {
21686  mPen = pen;
21687 }
21688 
21694 void QCPItemLine::setSelectedPen(const QPen &pen)
21695 {
21696  mSelectedPen = pen;
21697 }
21698 
21708 {
21709  mHead = head;
21710 }
21711 
21721 {
21722  mTail = tail;
21723 }
21724 
21725 /* inherits documentation from base class */
21726 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21727 {
21728  Q_UNUSED(details)
21729  if (onlySelectable && !mSelectable)
21730  return -1;
21731 
21732  return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
21733 }
21734 
21735 /* inherits documentation from base class */
21737 {
21738  QVector2D startVec(start->pixelPoint());
21739  QVector2D endVec(end->pixelPoint());
21740  if (startVec.toPoint() == endVec.toPoint())
21741  return;
21742  // get visible segment of straight line inside clipRect:
21743  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
21744  clipPad = qMax(clipPad, (double)mainPen().widthF());
21745  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21746  // paint visible segment, if existent:
21747  if (!line.isNull())
21748  {
21749  painter->setPen(mainPen());
21750  painter->drawLine(line);
21751  painter->setBrush(Qt::SolidPattern);
21753  mTail.draw(painter, startVec, startVec-endVec);
21755  mHead.draw(painter, endVec, endVec-startVec);
21756  }
21757 }
21758 
21766 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
21767 {
21768  bool containsStart = rect.contains(start.x(), start.y());
21769  bool containsEnd = rect.contains(end.x(), end.y());
21770  if (containsStart && containsEnd)
21771  return QLineF(start.toPointF(), end.toPointF());
21772 
21773  QVector2D base = start;
21774  QVector2D vec = end-start;
21775  double bx, by;
21776  double gamma, mu;
21777  QLineF result;
21778  QList<QVector2D> pointVectors;
21779 
21780  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
21781  {
21782  // check top of rect:
21783  bx = rect.left();
21784  by = rect.top();
21785  mu = (by-base.y())/vec.y();
21786  if (mu >= 0 && mu <= 1)
21787  {
21788  gamma = base.x()-bx + mu*vec.x();
21789  if (gamma >= 0 && gamma <= rect.width())
21790  pointVectors.append(QVector2D(bx+gamma, by));
21791  }
21792  // check bottom of rect:
21793  bx = rect.left();
21794  by = rect.bottom();
21795  mu = (by-base.y())/vec.y();
21796  if (mu >= 0 && mu <= 1)
21797  {
21798  gamma = base.x()-bx + mu*vec.x();
21799  if (gamma >= 0 && gamma <= rect.width())
21800  pointVectors.append(QVector2D(bx+gamma, by));
21801  }
21802  }
21803  if (!qFuzzyIsNull(vec.x())) // line is not vertical
21804  {
21805  // check left of rect:
21806  bx = rect.left();
21807  by = rect.top();
21808  mu = (bx-base.x())/vec.x();
21809  if (mu >= 0 && mu <= 1)
21810  {
21811  gamma = base.y()-by + mu*vec.y();
21812  if (gamma >= 0 && gamma <= rect.height())
21813  pointVectors.append(QVector2D(bx, by+gamma));
21814  }
21815  // check right of rect:
21816  bx = rect.right();
21817  by = rect.top();
21818  mu = (bx-base.x())/vec.x();
21819  if (mu >= 0 && mu <= 1)
21820  {
21821  gamma = base.y()-by + mu*vec.y();
21822  if (gamma >= 0 && gamma <= rect.height())
21823  pointVectors.append(QVector2D(bx, by+gamma));
21824  }
21825  }
21826 
21827  if (containsStart)
21828  pointVectors.append(start);
21829  if (containsEnd)
21830  pointVectors.append(end);
21831 
21832  // evaluate points:
21833  if (pointVectors.size() == 2)
21834  {
21835  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21836  } else if (pointVectors.size() > 2)
21837  {
21838  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21839  double distSqrMax = 0;
21840  QVector2D pv1, pv2;
21841  for (int i=0; i<pointVectors.size()-1; ++i)
21842  {
21843  for (int k=i+1; k<pointVectors.size(); ++k)
21844  {
21845  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21846  if (distSqr > distSqrMax)
21847  {
21848  pv1 = pointVectors.at(i);
21849  pv2 = pointVectors.at(k);
21850  distSqrMax = distSqr;
21851  }
21852  }
21853  }
21854  result.setPoints(pv1.toPointF(), pv2.toPointF());
21855  }
21856  return result;
21857 }
21858 
21865 {
21866  return mSelected ? mSelectedPen : mPen;
21867 }
21868 
21869 
21873 
21897  QCPAbstractItem(parentPlot),
21898  start(createPosition(QLatin1String("start"))),
21899  startDir(createPosition(QLatin1String("startDir"))),
21900  endDir(createPosition(QLatin1String("endDir"))),
21901  end(createPosition(QLatin1String("end")))
21902 {
21903  start->setCoords(0, 0);
21904  startDir->setCoords(0.5, 0);
21905  endDir->setCoords(0, 0.5);
21906  end->setCoords(1, 1);
21907 
21908  setPen(QPen(Qt::black));
21909  setSelectedPen(QPen(Qt::blue,2));
21910 }
21911 
21913 {
21914 }
21915 
21921 void QCPItemCurve::setPen(const QPen &pen)
21922 {
21923  mPen = pen;
21924 }
21925 
21931 void QCPItemCurve::setSelectedPen(const QPen &pen)
21932 {
21933  mSelectedPen = pen;
21934 }
21935 
21945 {
21946  mHead = head;
21947 }
21948 
21958 {
21959  mTail = tail;
21960 }
21961 
21962 /* inherits documentation from base class */
21963 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21964 {
21965  Q_UNUSED(details)
21966  if (onlySelectable && !mSelectable)
21967  return -1;
21968 
21969  QPointF startVec(start->pixelPoint());
21970  QPointF startDirVec(startDir->pixelPoint());
21971  QPointF endDirVec(endDir->pixelPoint());
21972  QPointF endVec(end->pixelPoint());
21973 
21974  QPainterPath cubicPath(startVec);
21975  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21976 
21977  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
21978  double minDistSqr = std::numeric_limits<double>::max();
21979  for (int i=1; i<polygon.size(); ++i)
21980  {
21981  double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
21982  if (distSqr < minDistSqr)
21983  minDistSqr = distSqr;
21984  }
21985  return qSqrt(minDistSqr);
21986 }
21987 
21988 /* inherits documentation from base class */
21990 {
21991  QPointF startVec(start->pixelPoint());
21992  QPointF startDirVec(startDir->pixelPoint());
21993  QPointF endDirVec(endDir->pixelPoint());
21994  QPointF endVec(end->pixelPoint());
21995  if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
21996  return;
21997 
21998  QPainterPath cubicPath(startVec);
21999  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
22000 
22001  // paint visible segment, if existent:
22002  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
22003  QRect cubicRect = cubicPath.controlPointRect().toRect();
22004  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
22005  cubicRect.adjust(0, 0, 1, 1);
22006  if (clip.intersects(cubicRect))
22007  {
22008  painter->setPen(mainPen());
22009  painter->drawPath(cubicPath);
22010  painter->setBrush(Qt::SolidPattern);
22012  mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
22014  mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
22015  }
22016 }
22017 
22024 {
22025  return mSelected ? mSelectedPen : mPen;
22026 }
22027 
22028 
22032 
22047  QCPAbstractItem(parentPlot),
22048  topLeft(createPosition(QLatin1String("topLeft"))),
22049  bottomRight(createPosition(QLatin1String("bottomRight"))),
22050  top(createAnchor(QLatin1String("top"), aiTop)),
22051  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22052  right(createAnchor(QLatin1String("right"), aiRight)),
22053  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22054  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22055  left(createAnchor(QLatin1String("left"), aiLeft))
22056 {
22057  topLeft->setCoords(0, 1);
22058  bottomRight->setCoords(1, 0);
22059 
22060  setPen(QPen(Qt::black));
22061  setSelectedPen(QPen(Qt::blue,2));
22062  setBrush(Qt::NoBrush);
22063  setSelectedBrush(Qt::NoBrush);
22064 }
22065 
22067 {
22068 }
22069 
22075 void QCPItemRect::setPen(const QPen &pen)
22076 {
22077  mPen = pen;
22078 }
22079 
22085 void QCPItemRect::setSelectedPen(const QPen &pen)
22086 {
22087  mSelectedPen = pen;
22088 }
22089 
22096 void QCPItemRect::setBrush(const QBrush &brush)
22097 {
22098  mBrush = brush;
22099 }
22100 
22107 void QCPItemRect::setSelectedBrush(const QBrush &brush)
22108 {
22110 }
22111 
22112 /* inherits documentation from base class */
22113 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22114 {
22115  Q_UNUSED(details)
22116  if (onlySelectable && !mSelectable)
22117  return -1;
22118 
22119  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
22120  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
22121  return rectSelectTest(rect, pos, filledRect);
22122 }
22123 
22124 /* inherits documentation from base class */
22126 {
22127  QPointF p1 = topLeft->pixelPoint();
22128  QPointF p2 = bottomRight->pixelPoint();
22129  if (p1.toPoint() == p2.toPoint())
22130  return;
22131  QRectF rect = QRectF(p1, p2).normalized();
22132  double clipPad = mainPen().widthF();
22133  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22134  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
22135  {
22136  painter->setPen(mainPen());
22137  painter->setBrush(mainBrush());
22138  painter->drawRect(rect);
22139  }
22140 }
22141 
22142 /* inherits documentation from base class */
22143 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
22144 {
22145  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22146  switch (anchorId)
22147  {
22148  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22149  case aiTopRight: return rect.topRight();
22150  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22151  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22152  case aiBottomLeft: return rect.bottomLeft();
22153  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22154  }
22155 
22156  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22157  return QPointF();
22158 }
22159 
22166 {
22167  return mSelected ? mSelectedPen : mPen;
22168 }
22169 
22176 {
22177  return mSelected ? mSelectedBrush : mBrush;
22178 }
22179 
22180 
22184 
22205  QCPAbstractItem(parentPlot),
22206  position(createPosition(QLatin1String("position"))),
22207  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
22208  top(createAnchor(QLatin1String("top"), aiTop)),
22209  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22210  right(createAnchor(QLatin1String("right"), aiRight)),
22211  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
22212  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22213  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22214  left(createAnchor(QLatin1String("left"), aiLeft))
22215 {
22216  position->setCoords(0, 0);
22217 
22218  setRotation(0);
22219  setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
22220  setPositionAlignment(Qt::AlignCenter);
22221  setText(QLatin1String("text"));
22222 
22223  setPen(Qt::NoPen);
22224  setSelectedPen(Qt::NoPen);
22225  setBrush(Qt::NoBrush);
22226  setSelectedBrush(Qt::NoBrush);
22227  setColor(Qt::black);
22228  setSelectedColor(Qt::blue);
22229 }
22230 
22232 {
22233 }
22234 
22238 void QCPItemText::setColor(const QColor &color)
22239 {
22240  mColor = color;
22241 }
22242 
22246 void QCPItemText::setSelectedColor(const QColor &color)
22247 {
22249 }
22250 
22257 void QCPItemText::setPen(const QPen &pen)
22258 {
22259  mPen = pen;
22260 }
22261 
22268 void QCPItemText::setSelectedPen(const QPen &pen)
22269 {
22270  mSelectedPen = pen;
22271 }
22272 
22279 void QCPItemText::setBrush(const QBrush &brush)
22280 {
22281  mBrush = brush;
22282 }
22283 
22290 void QCPItemText::setSelectedBrush(const QBrush &brush)
22291 {
22293 }
22294 
22300 void QCPItemText::setFont(const QFont &font)
22301 {
22302  mFont = font;
22303 }
22304 
22310 void QCPItemText::setSelectedFont(const QFont &font)
22311 {
22312  mSelectedFont = font;
22313 }
22314 
22321 void QCPItemText::setText(const QString &text)
22322 {
22323  mText = text;
22324 }
22325 
22338 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
22339 {
22340  mPositionAlignment = alignment;
22341 }
22342 
22346 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
22347 {
22348  mTextAlignment = alignment;
22349 }
22350 
22355 void QCPItemText::setRotation(double degrees)
22356 {
22357  mRotation = degrees;
22358 }
22359 
22364 void QCPItemText::setPadding(const QMargins &padding)
22365 {
22366  mPadding = padding;
22367 }
22368 
22369 /* inherits documentation from base class */
22370 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22371 {
22372  Q_UNUSED(details)
22373  if (onlySelectable && !mSelectable)
22374  return -1;
22375 
22376  // The rect may be rotated, so we transform the actual clicked pos to the rotated
22377  // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
22378  QPointF positionPixels(position->pixelPoint());
22379  QTransform inputTransform;
22380  inputTransform.translate(positionPixels.x(), positionPixels.y());
22381  inputTransform.rotate(-mRotation);
22382  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
22383  QPointF rotatedPos = inputTransform.map(pos);
22384  QFontMetrics fontMetrics(mFont);
22385  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22386  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22387  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
22388  textBoxRect.moveTopLeft(textPos.toPoint());
22389 
22390  return rectSelectTest(textBoxRect, rotatedPos, true);
22391 }
22392 
22393 /* inherits documentation from base class */
22395 {
22396  QPointF pos(position->pixelPoint());
22397  QTransform transform = painter->transform();
22398  transform.translate(pos.x(), pos.y());
22399  if (!qFuzzyIsNull(mRotation))
22400  transform.rotate(mRotation);
22401  painter->setFont(mainFont());
22402  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22403  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22404  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22405  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
22406  textBoxRect.moveTopLeft(textPos.toPoint());
22407  double clipPad = mainPen().widthF();
22408  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22409  if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
22410  {
22411  painter->setTransform(transform);
22412  if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
22413  (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
22414  {
22415  painter->setPen(mainPen());
22416  painter->setBrush(mainBrush());
22417  painter->drawRect(textBoxRect);
22418  }
22419  painter->setBrush(Qt::NoBrush);
22420  painter->setPen(QPen(mainColor()));
22421  painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
22422  }
22423 }
22424 
22425 /* inherits documentation from base class */
22426 QPointF QCPItemText::anchorPixelPoint(int anchorId) const
22427 {
22428  // get actual rect points (pretty much copied from draw function):
22429  QPointF pos(position->pixelPoint());
22430  QTransform transform;
22431  transform.translate(pos.x(), pos.y());
22432  if (!qFuzzyIsNull(mRotation))
22433  transform.rotate(mRotation);
22434  QFontMetrics fontMetrics(mainFont());
22435  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22436  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22437  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22438  textBoxRect.moveTopLeft(textPos.toPoint());
22439  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
22440 
22441  switch (anchorId)
22442  {
22443  case aiTopLeft: return rectPoly.at(0);
22444  case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
22445  case aiTopRight: return rectPoly.at(1);
22446  case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
22447  case aiBottomRight: return rectPoly.at(2);
22448  case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
22449  case aiBottomLeft: return rectPoly.at(3);
22450  case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
22451  }
22452 
22453  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22454  return QPointF();
22455 }
22456 
22467 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
22468 {
22469  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
22470  return pos;
22471 
22472  QPointF result = pos; // start at top left
22473  if (positionAlignment.testFlag(Qt::AlignHCenter))
22474  result.rx() -= rect.width()/2.0;
22475  else if (positionAlignment.testFlag(Qt::AlignRight))
22476  result.rx() -= rect.width();
22477  if (positionAlignment.testFlag(Qt::AlignVCenter))
22478  result.ry() -= rect.height()/2.0;
22479  else if (positionAlignment.testFlag(Qt::AlignBottom))
22480  result.ry() -= rect.height();
22481  return result;
22482 }
22483 
22490 {
22491  return mSelected ? mSelectedFont : mFont;
22492 }
22493 
22500 {
22501  return mSelected ? mSelectedColor : mColor;
22502 }
22503 
22510 {
22511  return mSelected ? mSelectedPen : mPen;
22512 }
22513 
22520 {
22521  return mSelected ? mSelectedBrush : mBrush;
22522 }
22523 
22524 
22528 
22543  QCPAbstractItem(parentPlot),
22544  topLeft(createPosition(QLatin1String("topLeft"))),
22545  bottomRight(createPosition(QLatin1String("bottomRight"))),
22546  topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
22547  top(createAnchor(QLatin1String("top"), aiTop)),
22548  topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
22549  right(createAnchor(QLatin1String("right"), aiRight)),
22550  bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
22551  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22552  bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
22553  left(createAnchor(QLatin1String("left"), aiLeft)),
22554  center(createAnchor(QLatin1String("center"), aiCenter))
22555 {
22556  topLeft->setCoords(0, 1);
22557  bottomRight->setCoords(1, 0);
22558 
22559  setPen(QPen(Qt::black));
22560  setSelectedPen(QPen(Qt::blue, 2));
22561  setBrush(Qt::NoBrush);
22562  setSelectedBrush(Qt::NoBrush);
22563 }
22564 
22566 {
22567 }
22568 
22574 void QCPItemEllipse::setPen(const QPen &pen)
22575 {
22576  mPen = pen;
22577 }
22578 
22584 void QCPItemEllipse::setSelectedPen(const QPen &pen)
22585 {
22586  mSelectedPen = pen;
22587 }
22588 
22595 void QCPItemEllipse::setBrush(const QBrush &brush)
22596 {
22597  mBrush = brush;
22598 }
22599 
22606 void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
22607 {
22609 }
22610 
22611 /* inherits documentation from base class */
22612 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22613 {
22614  Q_UNUSED(details)
22615  if (onlySelectable && !mSelectable)
22616  return -1;
22617 
22618  double result = -1;
22619  QPointF p1 = topLeft->pixelPoint();
22620  QPointF p2 = bottomRight->pixelPoint();
22621  QPointF center((p1+p2)/2.0);
22622  double a = qAbs(p1.x()-p2.x())/2.0;
22623  double b = qAbs(p1.y()-p2.y())/2.0;
22624  double x = pos.x()-center.x();
22625  double y = pos.y()-center.y();
22626 
22627  // distance to border:
22628  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
22629  result = qAbs(c-1)*qSqrt(x*x+y*y);
22630  // filled ellipse, allow click inside to count as hit:
22631  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
22632  {
22633  if (x*x/(a*a) + y*y/(b*b) <= 1)
22634  result = mParentPlot->selectionTolerance()*0.99;
22635  }
22636  return result;
22637 }
22638 
22639 /* inherits documentation from base class */
22641 {
22642  QPointF p1 = topLeft->pixelPoint();
22643  QPointF p2 = bottomRight->pixelPoint();
22644  if (p1.toPoint() == p2.toPoint())
22645  return;
22646  QRectF ellipseRect = QRectF(p1, p2).normalized();
22647  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
22648  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
22649  {
22650  painter->setPen(mainPen());
22651  painter->setBrush(mainBrush());
22652 #ifdef __EXCEPTIONS
22653  try // drawEllipse sometimes throws exceptions if ellipse is too big
22654  {
22655 #endif
22656  painter->drawEllipse(ellipseRect);
22657 #ifdef __EXCEPTIONS
22658  } catch (...)
22659  {
22660  qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
22661  setVisible(false);
22662  }
22663 #endif
22664  }
22665 }
22666 
22667 /* inherits documentation from base class */
22668 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
22669 {
22670  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22671  switch (anchorId)
22672  {
22673  case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
22674  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22675  case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
22676  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22677  case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
22678  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22679  case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
22680  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22681  case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
22682  }
22683 
22684  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22685  return QPointF();
22686 }
22687 
22694 {
22695  return mSelected ? mSelectedPen : mPen;
22696 }
22697 
22704 {
22705  return mSelected ? mSelectedBrush : mBrush;
22706 }
22707 
22708 
22712 
22733  QCPAbstractItem(parentPlot),
22734  topLeft(createPosition(QLatin1String("topLeft"))),
22735  bottomRight(createPosition(QLatin1String("bottomRight"))),
22736  top(createAnchor(QLatin1String("top"), aiTop)),
22737  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22738  right(createAnchor(QLatin1String("right"), aiRight)),
22739  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22740  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22741  left(createAnchor(QLatin1String("left"), aiLeft)),
22742  mScaledPixmapInvalidated(true)
22743 {
22744  topLeft->setCoords(0, 1);
22745  bottomRight->setCoords(1, 0);
22746 
22747  setPen(Qt::NoPen);
22748  setSelectedPen(QPen(Qt::blue));
22749  setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
22750 }
22751 
22753 {
22754 }
22755 
22759 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
22760 {
22761  mPixmap = pixmap;
22762  mScaledPixmapInvalidated = true;
22763  if (mPixmap.isNull())
22764  qDebug() << Q_FUNC_INFO << "pixmap is null";
22765 }
22766 
22771 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
22772 {
22773  mScaled = scaled;
22776  mScaledPixmapInvalidated = true;
22777 }
22778 
22784 void QCPItemPixmap::setPen(const QPen &pen)
22785 {
22786  mPen = pen;
22787 }
22788 
22794 void QCPItemPixmap::setSelectedPen(const QPen &pen)
22795 {
22796  mSelectedPen = pen;
22797 }
22798 
22799 /* inherits documentation from base class */
22800 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22801 {
22802  Q_UNUSED(details)
22803  if (onlySelectable && !mSelectable)
22804  return -1;
22805 
22806  return rectSelectTest(getFinalRect(), pos, true);
22807 }
22808 
22809 /* inherits documentation from base class */
22811 {
22812  bool flipHorz = false;
22813  bool flipVert = false;
22814  QRect rect = getFinalRect(&flipHorz, &flipVert);
22815  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
22816  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22817  if (boundingRect.intersects(clipRect()))
22818  {
22819  updateScaledPixmap(rect, flipHorz, flipVert);
22820  painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
22821  QPen pen = mainPen();
22822  if (pen.style() != Qt::NoPen)
22823  {
22824  painter->setPen(pen);
22825  painter->setBrush(Qt::NoBrush);
22826  painter->drawRect(rect);
22827  }
22828  }
22829 }
22830 
22831 /* inherits documentation from base class */
22832 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
22833 {
22834  bool flipHorz;
22835  bool flipVert;
22836  QRect rect = getFinalRect(&flipHorz, &flipVert);
22837  // we actually want denormal rects (negative width/height) here, so restore
22838  // the flipped state:
22839  if (flipHorz)
22840  rect.adjust(rect.width(), 0, -rect.width(), 0);
22841  if (flipVert)
22842  rect.adjust(0, rect.height(), 0, -rect.height());
22843 
22844  switch (anchorId)
22845  {
22846  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22847  case aiTopRight: return rect.topRight();
22848  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22849  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22850  case aiBottomLeft: return rect.bottomLeft();
22851  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
22852  }
22853 
22854  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22855  return QPointF();
22856 }
22857 
22871 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
22872 {
22873  if (mPixmap.isNull())
22874  return;
22875 
22876  if (mScaled)
22877  {
22878  if (finalRect.isNull())
22879  finalRect = getFinalRect(&flipHorz, &flipVert);
22880  if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size())
22881  {
22882  mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, mTransformationMode);
22883  if (flipHorz || flipVert)
22884  mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
22885  }
22886  } else if (!mScaledPixmap.isNull())
22887  mScaledPixmap = QPixmap();
22888  mScaledPixmapInvalidated = false;
22889 }
22890 
22905 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
22906 {
22907  QRect result;
22908  bool flipHorz = false;
22909  bool flipVert = false;
22910  QPoint p1 = topLeft->pixelPoint().toPoint();
22911  QPoint p2 = bottomRight->pixelPoint().toPoint();
22912  if (p1 == p2)
22913  return QRect(p1, QSize(0, 0));
22914  if (mScaled)
22915  {
22916  QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
22917  QPoint topLeft = p1;
22918  if (newSize.width() < 0)
22919  {
22920  flipHorz = true;
22921  newSize.rwidth() *= -1;
22922  topLeft.setX(p2.x());
22923  }
22924  if (newSize.height() < 0)
22925  {
22926  flipVert = true;
22927  newSize.rheight() *= -1;
22928  topLeft.setY(p2.y());
22929  }
22930  QSize scaledSize = mPixmap.size();
22931  scaledSize.scale(newSize, mAspectRatioMode);
22932  result = QRect(topLeft, scaledSize);
22933  } else
22934  {
22935  result = QRect(p1, mPixmap.size());
22936  }
22937  if (flippedHorz)
22938  *flippedHorz = flipHorz;
22939  if (flippedVert)
22940  *flippedVert = flipVert;
22941  return result;
22942 }
22943 
22950 {
22951  return mSelected ? mSelectedPen : mPen;
22952 }
22953 
22954 
22958 
22995  QCPAbstractItem(parentPlot),
22996  position(createPosition(QLatin1String("position"))),
22997  mGraph(0)
22998 {
22999  position->setCoords(0, 0);
23000 
23001  setBrush(Qt::NoBrush);
23002  setSelectedBrush(Qt::NoBrush);
23003  setPen(QPen(Qt::black));
23004  setSelectedPen(QPen(Qt::blue, 2));
23006  setSize(6);
23007  setInterpolating(false);
23008  setGraphKey(0);
23009 }
23010 
23012 {
23013 }
23014 
23020 void QCPItemTracer::setPen(const QPen &pen)
23021 {
23022  mPen = pen;
23023 }
23024 
23030 void QCPItemTracer::setSelectedPen(const QPen &pen)
23031 {
23032  mSelectedPen = pen;
23033 }
23034 
23040 void QCPItemTracer::setBrush(const QBrush &brush)
23041 {
23042  mBrush = brush;
23043 }
23044 
23050 void QCPItemTracer::setSelectedBrush(const QBrush &brush)
23051 {
23053 }
23054 
23059 void QCPItemTracer::setSize(double size)
23060 {
23061  mSize = size;
23062 }
23063 
23071 {
23072  mStyle = style;
23073 }
23074 
23086 {
23087  if (graph)
23088  {
23089  if (graph->parentPlot() == mParentPlot)
23090  {
23092  position->setAxes(graph->keyAxis(), graph->valueAxis());
23093  mGraph = graph;
23094  updatePosition();
23095  } else
23096  qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
23097  } else
23098  {
23099  mGraph = 0;
23100  }
23101 }
23102 
23113 {
23114  mGraphKey = key;
23115 }
23116 
23129 {
23130  mInterpolating = enabled;
23131 }
23132 
23133 /* inherits documentation from base class */
23134 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23135 {
23136  Q_UNUSED(details)
23137  if (onlySelectable && !mSelectable)
23138  return -1;
23139 
23140  QPointF center(position->pixelPoint());
23141  double w = mSize/2.0;
23142  QRect clip = clipRect();
23143  switch (mStyle)
23144  {
23145  case tsNone: return -1;
23146  case tsPlus:
23147  {
23148  if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23149  return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
23150  distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
23151  break;
23152  }
23153  case tsCrosshair:
23154  {
23155  return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
23156  distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
23157  }
23158  case tsCircle:
23159  {
23160  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23161  {
23162  // distance to border:
23163  double centerDist = QVector2D(center-pos).length();
23164  double circleLine = w;
23165  double result = qAbs(centerDist-circleLine);
23166  // filled ellipse, allow click inside to count as hit:
23167  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
23168  {
23169  if (centerDist <= circleLine)
23170  result = mParentPlot->selectionTolerance()*0.99;
23171  }
23172  return result;
23173  }
23174  break;
23175  }
23176  case tsSquare:
23177  {
23178  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23179  {
23180  QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
23181  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
23182  return rectSelectTest(rect, pos, filledRect);
23183  }
23184  break;
23185  }
23186  }
23187  return -1;
23188 }
23189 
23190 /* inherits documentation from base class */
23192 {
23193  updatePosition();
23194  if (mStyle == tsNone)
23195  return;
23196 
23197  painter->setPen(mainPen());
23198  painter->setBrush(mainBrush());
23199  QPointF center(position->pixelPoint());
23200  double w = mSize/2.0;
23201  QRect clip = clipRect();
23202  switch (mStyle)
23203  {
23204  case tsNone: return;
23205  case tsPlus:
23206  {
23207  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23208  {
23209  painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
23210  painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
23211  }
23212  break;
23213  }
23214  case tsCrosshair:
23215  {
23216  if (center.y() > clip.top() && center.y() < clip.bottom())
23217  painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
23218  if (center.x() > clip.left() && center.x() < clip.right())
23219  painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
23220  break;
23221  }
23222  case tsCircle:
23223  {
23224  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23225  painter->drawEllipse(center, w, w);
23226  break;
23227  }
23228  case tsSquare:
23229  {
23230  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23231  painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
23232  break;
23233  }
23234  }
23235 }
23236 
23250 {
23251  if (mGraph)
23252  {
23254  {
23255  if (mGraph->data()->size() > 1)
23256  {
23257  QCPDataMap::const_iterator first = mGraph->data()->constBegin();
23258  QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
23259  if (mGraphKey < first.key())
23260  position->setCoords(first.key(), first.value().value);
23261  else if (mGraphKey > last.key())
23262  position->setCoords(last.key(), last.value().value);
23263  else
23264  {
23265  QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
23266  if (it != first) // mGraphKey is somewhere between iterators
23267  {
23268  QCPDataMap::const_iterator prevIt = it-1;
23269  if (mInterpolating)
23270  {
23271  // interpolate between iterators around mGraphKey:
23272  double slope = 0;
23273  if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
23274  slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
23275  position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
23276  } else
23277  {
23278  // find iterator with key closest to mGraphKey:
23279  if (mGraphKey < (prevIt.key()+it.key())*0.5)
23280  it = prevIt;
23281  position->setCoords(it.key(), it.value().value);
23282  }
23283  } else // mGraphKey is exactly on first iterator
23284  position->setCoords(it.key(), it.value().value);
23285  }
23286  } else if (mGraph->data()->size() == 1)
23287  {
23288  QCPDataMap::const_iterator it = mGraph->data()->constBegin();
23289  position->setCoords(it.key(), it.value().value);
23290  } else
23291  qDebug() << Q_FUNC_INFO << "graph has no data";
23292  } else
23293  qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
23294  }
23295 }
23296 
23303 {
23304  return mSelected ? mSelectedPen : mPen;
23305 }
23306 
23313 {
23314  return mSelected ? mSelectedBrush : mBrush;
23315 }
23316 
23317 
23321 
23348  QCPAbstractItem(parentPlot),
23349  left(createPosition(QLatin1String("left"))),
23350  right(createPosition(QLatin1String("right"))),
23351  center(createAnchor(QLatin1String("center"), aiCenter))
23352 {
23353  left->setCoords(0, 0);
23354  right->setCoords(1, 1);
23355 
23356  setPen(QPen(Qt::black));
23357  setSelectedPen(QPen(Qt::blue, 2));
23358  setLength(8);
23360 }
23361 
23363 {
23364 }
23365 
23375 void QCPItemBracket::setPen(const QPen &pen)
23376 {
23377  mPen = pen;
23378 }
23379 
23385 void QCPItemBracket::setSelectedPen(const QPen &pen)
23386 {
23387  mSelectedPen = pen;
23388 }
23389 
23398 void QCPItemBracket::setLength(double length)
23399 {
23400  mLength = length;
23401 }
23402 
23409 {
23410  mStyle = style;
23411 }
23412 
23413 /* inherits documentation from base class */
23414 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23415 {
23416  Q_UNUSED(details)
23417  if (onlySelectable && !mSelectable)
23418  return -1;
23419 
23420  QVector2D leftVec(left->pixelPoint());
23421  QVector2D rightVec(right->pixelPoint());
23422  if (leftVec.toPoint() == rightVec.toPoint())
23423  return -1;
23424 
23425  QVector2D widthVec = (rightVec-leftVec)*0.5f;
23426  QVector2D lengthVec(-widthVec.y(), widthVec.x());
23427  lengthVec = lengthVec.normalized()*mLength;
23428  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23429 
23430  switch (mStyle)
23431  {
23434  {
23435  double a = distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23436  double b = distSqrToLine((centerVec-widthVec+lengthVec).toPointF(), (centerVec-widthVec).toPointF(), pos);
23437  double c = distSqrToLine((centerVec+widthVec+lengthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23438  return qSqrt(qMin(qMin(a, b), c));
23439  }
23442  {
23443  double a = distSqrToLine((centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23444  double b = distSqrToLine((centerVec-widthVec+lengthVec*0.7f).toPointF(), (centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23445  double c = distSqrToLine((centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23446  double d = distSqrToLine((centerVec+widthVec+lengthVec*0.7f).toPointF(), (centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23447  return qSqrt(qMin(qMin(a, b), qMin(c, d)));
23448  }
23449  }
23450  return -1;
23451 }
23452 
23453 /* inherits documentation from base class */
23455 {
23456  QVector2D leftVec(left->pixelPoint());
23457  QVector2D rightVec(right->pixelPoint());
23458  if (leftVec.toPoint() == rightVec.toPoint())
23459  return;
23460 
23461  QVector2D widthVec = (rightVec-leftVec)*0.5f;
23462  QVector2D lengthVec(-widthVec.y(), widthVec.x());
23463  lengthVec = lengthVec.normalized()*mLength;
23464  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23465 
23466  QPolygon boundingPoly;
23467  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
23468  << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
23469  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
23470  if (clip.intersects(boundingPoly.boundingRect()))
23471  {
23472  painter->setPen(mainPen());
23473  switch (mStyle)
23474  {
23475  case bsSquare:
23476  {
23477  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
23478  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23479  painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23480  break;
23481  }
23482  case bsRound:
23483  {
23484  painter->setBrush(Qt::NoBrush);
23485  QPainterPath path;
23486  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23487  path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
23488  path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23489  painter->drawPath(path);
23490  break;
23491  }
23492  case bsCurly:
23493  {
23494  painter->setBrush(Qt::NoBrush);
23495  QPainterPath path;
23496  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23497  path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
23498  path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23499  painter->drawPath(path);
23500  break;
23501  }
23502  case bsCalligraphic:
23503  {
23504  painter->setPen(Qt::NoPen);
23505  painter->setBrush(QBrush(mainPen().color()));
23506  QPainterPath path;
23507  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23508 
23509  path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
23510  path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23511 
23512  path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
23513  path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23514 
23515  painter->drawPath(path);
23516  break;
23517  }
23518  }
23519  }
23520 }
23521 
23522 /* inherits documentation from base class */
23523 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
23524 {
23525  QVector2D leftVec(left->pixelPoint());
23526  QVector2D rightVec(right->pixelPoint());
23527  if (leftVec.toPoint() == rightVec.toPoint())
23528  return leftVec.toPointF();
23529 
23530  QVector2D widthVec = (rightVec-leftVec)*0.5f;
23531  QVector2D lengthVec(-widthVec.y(), widthVec.x());
23532  lengthVec = lengthVec.normalized()*mLength;
23533  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23534 
23535  switch (anchorId)
23536  {
23537  case aiCenter:
23538  return centerVec.toPointF();
23539  }
23540  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23541  return QPointF();
23542 }
23543 
23550 {
23551  return mSelected ? mSelectedPen : mPen;
23552 }
23553 
A filled circle.
Definition: qcustomplot.h:892
static const double maxRange
Definition: qcustomplot.h:510
QPointer< QCPAxisRect > mClipAxisRect
Definition: qcustomplot.h:1649
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:1593
int mHighestVisibleTick
Definition: qcustomplot.h:1276
bool selectable() const
Definition: qcustomplot.h:1419
QFont mSelectedFont
Definition: qcustomplot.h:2267
bool rangeReversed() const
Definition: qcustomplot.h:1097
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
QCPAxis * xAxis2
Definition: qcustomplot.h:1819
QPixmap mLegendIcon
Definition: qcustomplot.h:3103
double baseValue() const
Definition: qcustomplot.h:2854
virtual void clearData()
void pixelsToCoords(double x, double y, double &key, double &value) const
A layout element displaying a plot title text.
Definition: qcustomplot.h:2293
int tickLabelPadding() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
A curly brace with varying stroke width giving a calligraphic impression.
Definition: qcustomplot.h:3727
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
QList< QCPLayer * > mLayers
Definition: qcustomplot.h:1852
void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
QPen getIconBorderPen() const
virtual void clearData()
void setTickVector(const QVector< double > &vec)
TracerStyle mStyle
Definition: qcustomplot.h:3700
Tick labels will be displayed inside the axis rect and clipped to the inner axis rect.
Definition: qcustomplot.h:1064
void setHead(const QCPLineEnding &head)
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3541
double getPixelSpacing(const QCPBars *bars, double keyCoord)
QCPLineEnding mHead
Definition: qcustomplot.h:3310
int tickLengthIn() const
void titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
virtual QCP::Interaction selectionCategory() const
void setTwoColored(bool twoColored)
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
virtual bool take(QCPLayoutElement *element)=0
void getStepCenterPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QCP::MarginSides mAutoMargins
Definition: qcustomplot.h:693
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void removeData(double fromKey, double toKey)
A brace with angled edges.
Definition: qcustomplot.h:3724
QList< QCPItemPosition * > mPositions
Definition: qcustomplot.h:1650
void ticksRequest()
bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
The axis label.
Definition: qcustomplot.h:1083
void setCoords(double key, double value)
Manages the position of an item.
Definition: qcustomplot.h:1540
Tick coordinate is regarded as a date/time (seconds since 1970-01-01T00:00:00 UTC) and will be displa...
Definition: qcustomplot.h:1056
Static positioning in pixels, starting from the top left corner of the viewport/widget.
Definition: qcustomplot.h:1549
int findIndexBelowY(const QVector< QPointF > *data, double y) const
void insertColumn(int newIndex)
void selectableChanged(QCPLegend::SelectableParts parts)
virtual void clearData()
double mRotation
Definition: qcustomplot.h:3497
virtual ~QCPColorScale()
bool inverted() const
Definition: qcustomplot.h:907
{ssCrossCircle.png} a circle with a cross inside
Definition: qcustomplot.h:259
QColor getTickLabelColor() const
QPainterPath customPath() const
Definition: qcustomplot.h:280
QFont getTickLabelFont() const
virtual void simplify()
void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3602
QPen mainPen() const
virtual QSize maximumSizeHint() const
bool mAdaptiveSampling
Definition: qcustomplot.h:2590
void setRangeReversed(bool reversed)
void removeData(double fromKey, double toKey)
QPointF lowerFillBasePoint(double lowerKey) const
int width() const
Definition: qcustomplot.h:2029
bool antialiasing() const
Definition: qcustomplot.h:333
QCPAxis::AxisType type() const
Definition: qcustomplot.h:2405
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
SelectablePart getPartAt(const QPointF &pos) const
int top() const
Definition: qcustomplot.h:2027
QCPBarDataMap * data() const
Definition: qcustomplot.h:2857
bool mTickLabels
Definition: qcustomplot.h:1248
QBrush mBrush
Definition: qcustomplot.h:2259
QString name() const
Definition: qcustomplot.h:381
QPixmap pixmap() const
Definition: qcustomplot.h:279
void setSelectedPen(const QPen &pen)
{ssDiamond.png} a diamond
Definition: qcustomplot.h:253
int index() const
Definition: qcustomplot.h:382
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:380
void setData(QCPColorMapData *data, bool copy=false)
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:2045
QMap< double, QCPData > QCPDataMap
Definition: qcustomplot.h:2485
0x004 axis (tick) labels will be cached as pixmaps, increasing replot performance.
Definition: qcustomplot.h:144
int levelCount() const
Definition: qcustomplot.h:1938
int graphCount() const
virtual void draw(QCPPainter *painter)
QList< QCPAbstractItem * > items() const
QList< QCPGraph * > mGraphs
Definition: qcustomplot.h:1850
virtual void generateAutoTicks()
QCPColorScale(QCustomPlot *parentPlot)
void setDataKeyError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError)
Q_SLOT void setDataRange(const QCPRange &dataRange)
QCPColorGradient mGradient
Definition: qcustomplot.h:3097
QCPAbstractLegendItem * item(int index) const
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
AxisType axisType() const
Definition: qcustomplot.h:1092
WidthType widthType() const
Definition: qcustomplot.h:2852
QPen mSelectedBorderPen
Definition: qcustomplot.h:2265
void setOutlierStyle(const QCPScatterStyle &style)
void setTickLengthIn(int inside)
QLineF getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
bool begin(QPaintDevice *device)
A filled diamond (45° rotated square)
Definition: qcustomplot.h:894
virtual void mousePressEvent(QMouseEvent *event)
void makeNonCosmetic()
QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId=-1)
void setMaximumSize(const QSize &size)
void setColumnStretchFactor(int column, double factor)
0x01 Axis is vertical and on the left side of the axis rect
Definition: qcustomplot.h:1042
bool addItem(QCPAbstractItem *item)
QCPColorScale * colorScale() const
Definition: qcustomplot.h:3068
void setBrushNegative(const QBrush &brush)
The abstract base class for layouts.
Definition: qcustomplot.h:719
QCPFinancialDataMap * data() const
Definition: qcustomplot.h:3172
virtual void legendRemoved(QCPLegend *legend)
void setFont(const QFont &font)
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
void setRangeLower(double lower)
The negative sign domain, i.e. numbers smaller than zero.
Definition: qcustomplot.h:1455
Q_SLOT void setSelected(bool selected)
bool isEmpty() const
Definition: qcustomplot.h:3028
double pointDistance(const QPointF &pixelPoint) const
ScaleType mScaleType
Definition: qcustomplot.h:1270
bool noAntialiasingOnDrag() const
Definition: qcustomplot.h:1729
void setPen(const QPen &pen)
QList< QCPLayoutElement * > mElements
Definition: qcustomplot.h:862
virtual int elementCount() const
void setSelectedFont(const QFont &font)
QPen pen() const
Definition: qcustomplot.h:3666
QColor mSelectedColor
Definition: qcustomplot.h:3490
Both sign domains, including zero, i.e. all (rational) numbers.
Definition: qcustomplot.h:1456
QCPCurveDataMap * mData
Definition: qcustomplot.h:2708
virtual void simplify()
virtual void drawWhiskers(QCPPainter *painter) const
QCPItemPosition * position(const QString &name) const
QPen mErrorPen
Definition: qcustomplot.h:2583
int itemCount() const
Qt::KeyboardModifier mMultiSelectModifier
Definition: qcustomplot.h:1864
Bar spacing is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:2756
QCPAxis * valueAxis() const
Definition: qcustomplot.h:1573
LineStyle mLineStyle
Definition: qcustomplot.h:2710
bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
double length() const
Definition: qcustomplot.h:3736
virtual void deselectEvent(bool *selectionStateChanged)
BracketStyle mStyle
Definition: qcustomplot.h:3757
virtual void draw(QCPPainter *painter)
void setRangeZoom(bool enabled)
virtual QPointF anchorPixelPoint(int anchorId) const
QCPBarsGroup * barsGroup() const
Definition: qcustomplot.h:2853
QPen mSelectedPen
Definition: qcustomplot.h:3491
void addData(const QCPBarDataMap &dataMap)
void setSelectedFont(const QFont &font)
void setValueRange(const QCPRange &valueRange)
void setBrush(const QBrush &brush)
QHash< QCPAxis::AxisType, QList< QCPAxis * > > mAxes
Definition: qcustomplot.h:2058
void setInsetPlacement(int index, InsetPlacement placement)
QCPPlottableLegendItem * itemWithPlottable(const QCPAbstractPlottable *plottable) const
void setSubGridVisible(bool visible)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPLegend * legend
Definition: qcustomplot.h:1820
void setColorScale(QCPColorScale *colorScale)
QSize mIconSize
Definition: qcustomplot.h:2262
{ssPeace.png} a circle, with one vertical and two downward diagonal lines
Definition: qcustomplot.h:261
void setLabel(const QString &str)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setSelectedLabelFont(const QFont &font)
void setTickLabelSide(LabelSide side)
void addChildX(QCPItemPosition *pos)
void removeData(double fromKey, double toKey)
bool mAutoTickLabels
Definition: qcustomplot.h:1248
0x0004 Sub grid lines
Definition: qcustomplot.h:120
void setWhiskerPen(const QPen &pen)
QBrush mainBrush() const
void removeChild(QCP::MarginSide side, QCPLayoutElement *element)
int padding() const
Definition: qcustomplot.h:1130
0x01 left margin
Definition: qcustomplot.h:100
QCPItemAnchor * mParentAnchorY
Definition: qcustomplot.h:1596
Represents the range an axis is encompassing.
Definition: qcustomplot.h:476
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:3095
double keyErrorPlus
Definition: qcustomplot.h:2473
void setPadding(const QMargins &padding)
static QCPFinancialDataMap timeSeriesToOhlc(const QVector< double > &time, const QVector< double > &value, double timeBinSize, double timeBinOffset=0)
QCPItemText(QCustomPlot *parentPlot)
QCPRange sanitizedForLogScale() const
void selectionChanged(bool selected)
QList< QCPAbstractPlottable * > selectedPlottables() const
QCPGrid * grid() const
Definition: qcustomplot.h:1143
void setDateTimeFormat(const QString &format)
void setSelectionTolerance(int pixels)
Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts)
QCPAxisPainterPrivate * mAxisPainter
Definition: qcustomplot.h:1275
virtual QSize maximumSizeHint() const
virtual void draw(QCPPainter *painter)
void mouseWheel(QWheelEvent *event)
PositionType type() const
Definition: qcustomplot.h:1563
QCPScatterStyle mOutlierStyle
Definition: qcustomplot.h:2978
virtual void clearData()
Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allo...
Definition: qcustomplot.h:1924
QCPLayoutElement * layoutElementAt(const QPointF &pos) const
bool visible() const
Definition: qcustomplot.h:423
virtual void updateLayout()
bool removeAxis(QCPAxis *axis)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
0x0200 Borders of fills (e.g. under or between graphs)
Definition: qcustomplot.h:127
QCPAxis * keyAxis() const
Definition: qcustomplot.h:1417
void drawLine(const QLineF &line)
int findIndexAboveX(const QVector< QPointF > *data, double x) const
0x004 The user can select multiple objects by holding the modifier set by QCustomPlot::setMultiSelect...
Definition: qcustomplot.h:157
void setLabelFont(const QFont &font)
virtual ~QCPItemStraightLine()
virtual void mousePressEvent(QMouseEvent *event)
QPixmap mPixmap
Definition: qcustomplot.h:3615
void itemClick(QCPAbstractItem *item, QMouseEvent *event)
void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
virtual QSize minimumSizeHint() const
QCPItemPosition *const position
Definition: qcustomplot.h:3693
Qt::AspectRatioMode aspectRatioMode() const
Definition: qcustomplot.h:3588
QCPAxis::AxisType type
Definition: qcustomplot.h:1336
void setBackgroundScaled(bool scaled)
void setSelectedPen(const QPen &pen)
void selectableChanged(bool selectable)
Tick coordinate is regarded as normal number and will be displayed as such. (see setNumberFormat) ...
Definition: qcustomplot.h:1055
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
QList< QCPAbstractLegendItem * > selectedItems() const
QCPAxis(QCPAxisRect *parent, AxisType type)
void insert(int i, QCPBars *bars)
QCP::Interactions mInteractions
Definition: qcustomplot.h:1854
void setPenPositive(const QPen &pen)
Q_SLOT void setGradient(const QCPColorGradient &gradient)
double width() const
Definition: qcustomplot.h:3174
void getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
QPoint mDragStart
Definition: qcustomplot.h:2056
virtual void update(UpdatePhase phase)
QPen mSelectedBasePen
Definition: qcustomplot.h:1239
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3395
QCPAxisRect * axisRect(int index=0) const
int mSubTickCount
Definition: qcustomplot.h:1262
void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Qt::AspectRatioMode mAspectRatioMode
Definition: qcustomplot.h:3619
void setAntialiasedScatters(bool enabled)
void mouseRelease(QMouseEvent *event)
bool remove(QCPLayoutElement *element)
bool mIsAntialiasing
Definition: qcustomplot.h:357
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void setCell(int keyIndex, int valueIndex, double z)
bool mColorBufferInvalidated
Definition: qcustomplot.h:1968
bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1)
void setTightBoundary(bool enabled)
void getPreparedData(QVector< QCPData > *lineData, QVector< QCPData > *scatterData) const
0x04 top margin
Definition: qcustomplot.h:102
virtual void drawLinePlot(QCPPainter *painter, QVector< QPointF > *lineData) const
QPen mainPen() const
QPen getSubTickPen() const
void setTickLengthOut(int outside)
Qt::Orientation orientation() const
Definition: qcustomplot.h:1206
bool mAntialiasedSubGrid
Definition: qcustomplot.h:963
void sizeConstraintsChanged() const
QCPBars * barBelow() const
Definition: qcustomplot.h:2855
QLatin1Char mNumberFormatChar
Definition: qcustomplot.h:1256
friend class QCPLegend
Definition: qcustomplot.h:1893
{ssPlusCircle.png} a circle with a plus inside
Definition: qcustomplot.h:260
virtual void drawImpulsePlot(QCPPainter *painter, QVector< QPointF > *lineData) const
void setColorStopAt(double position, const QColor &color)
QBrush getBrush() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QPointer< QCPColorScaleAxisRectPrivate > mAxisRect
Definition: qcustomplot.h:2445
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable) ...
Definition: qcustomplot.h:158
QCPItemAnchor * createAnchor(const QString &name, int anchorId)
QCPRange dataBounds() const
Definition: qcustomplot.h:3010
Holds the data of one single data point for QCPCurve.
Definition: qcustomplot.h:2638
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Q_SLOT void deselectAll()
void setSelectedBasePen(const QPen &pen)
void setBrushPositive(const QBrush &brush)
QCPItemBracket(QCustomPlot *parentPlot)
QCPLineEnding tail() const
Definition: qcustomplot.h:3338
ChartStyle mChartStyle
Definition: qcustomplot.h:3213
virtual int calculateAutoSubTickCount(double tickStep) const
virtual QCP::Interaction selectionCategory() const
void removeChildY(QCPItemPosition *pos)
{ssDot.png} a single pixel (use ssDisc or ssCircle if you want a round shape with a certain radius) ...
Definition: qcustomplot.h:247
virtual QSize sizeHint() const
QCPLayoutElement(QCustomPlot *parentPlot=0)
QPen pen() const
Definition: qcustomplot.h:277
QString text() const
Definition: qcustomplot.h:2310
void releaseElement(QCPLayoutElement *el)
QList< QCPAbstractPlottable * > plottables() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
void setBaseValue(double baseValue)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
The element is aligned to one of the layout sides, see setInsetAlignment.
Definition: qcustomplot.h:831
QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
double tickStep() const
Definition: qcustomplot.h:1115
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
{ssTriangleInverted.png} an equilateral triangle, standing on corner
Definition: qcustomplot.h:256
QPen mBasePen
Definition: qcustomplot.h:1239
virtual ~QCPItemBracket()
void normalize()
QCustomPlot * mParentPlot
Definition: qcustomplot.h:447
void setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
QCPItemStraightLine(QCustomPlot *parentPlot)
virtual void draw(QCPPainter *painter)
Q_SLOT void setScaleType(QCPAxis::ScaleType type)
QCPGraph * mGraph
Definition: qcustomplot.h:3701
QCPBarDataMap * mData
Definition: qcustomplot.h:2885
QHash< QCP::MarginSide, QList< QCPLayoutElement * > > mChildren
Definition: qcustomplot.h:615
QCPItemEllipse(QCustomPlot *parentPlot)
QCache< QString, CachedLabel > mLabelCache
Definition: qcustomplot.h:1375
QCPAbstractItem * itemAt(const QPointF &pos, bool onlySelectable=false) const
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
void setLabel(const QString &str)
QCPGrid(QCPAxis *parentAxis)
QBrush brush() const
Definition: qcustomplot.h:278
void setVisible(bool visible)
void setAntialiasedErrorBars(bool enabled)
bool removeAt(int index)
bool getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
0x0001 Axis base line and tick marks
Definition: qcustomplot.h:118
virtual void update(UpdatePhase phase)
int mLowestVisibleTick
Definition: qcustomplot.h:1276
SelectableParts selectedParts() const
Definition: qcustomplot.h:1132
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:2709
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:234
void setLength(double length)
void setAntialiasing(bool enabled)
each data point is represented by a line parallel to the value axis, which reaches from the data poin...
Definition: qcustomplot.h:2515
QCPAxisRect * clipAxisRect() const
virtual void draw(QCPPainter *painter)=0
void setSelectedTickLabelFont(const QFont &font)
virtual QCPLayoutElement * elementAt(int index) const
Q_SLOT void setGradient(const QCPColorGradient &gradient)
void setupFullAxesBox(bool connectRanges=false)
virtual void draw(QCPPainter *painter)
EndingStyle mStyle
Definition: qcustomplot.h:923
void setPen(const QPen &pen)
double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const
Candlestick representation.
Definition: qcustomplot.h:3164
int getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
bool removeLayer(QCPLayer *layer)
bool removePlottable(QCPAbstractPlottable *plottable)
void setLabelColor(const QColor &color)
void setRowSpacing(int pixels)
QList< QCPBars * > mBars
Definition: qcustomplot.h:2785
void selectableChanged(bool selectable)
int mIconTextPadding
Definition: qcustomplot.h:2263
double mRangeZoomFactorHorz
Definition: qcustomplot.h:2052
void setTypeY(PositionType type)
SelectableParts mSelectableParts
Definition: qcustomplot.h:1238
void setInverted(bool inverted)
void applyFillAntialiasingHint(QCPPainter *painter) const
int mCachedMargin
Definition: qcustomplot.h:1281
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QBrush mSelectedBrush
Definition: qcustomplot.h:3409
QList< QCPAxis * > selectedAxes() const
WidthType mWidthType
Definition: qcustomplot.h:2887
void getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector< QPointF > &beforeTraverse, QVector< QPointF > &afterTraverse) const
virtual ~QCPAxisPainterPrivate()
int keySize() const
Definition: qcustomplot.h:3006
QPixmap toPixmap(int width=0, int height=0, double scale=1.0)
void applyErrorBarsAntialiasingHint(QCPPainter *painter) const
MarginSide
Definition: qcustomplot.h:100
QCPAbstractLegendItem(QCPLegend *parent)
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:2156
QCPAxis::AxisType mType
Definition: qcustomplot.h:2438
InsetPlacement insetPlacement(int index) const
void selectableChanged(bool selectable)
QString name() const
Definition: qcustomplot.h:1409
void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
friend class QCPLayer
Definition: qcustomplot.h:1895
void setMode(PainterMode mode, bool enabled=true)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
double boundingDistance() const
int subTickCount() const
Definition: qcustomplot.h:1120
QCPGrid * mGrid
Definition: qcustomplot.h:1274
void addChildY(QCPItemPosition *pos)
int findIndexBelowX(const QVector< QPointF > *data, double x) const
double upper
Definition: qcustomplot.h:479
QCPItemAnchor *const center
Definition: qcustomplot.h:3551
QRect viewport() const
Definition: qcustomplot.h:1719
void setTail(const QCPLineEnding &tail)
int mNumberPrecision
Definition: qcustomplot.h:1255
virtual QRect clipRect() const
QColor mSelectedLabelColor
Definition: qcustomplot.h:1245
int selectionTolerance() const
Definition: qcustomplot.h:1728
void setRangeZoomFactor(double horizontalFactor, double verticalFactor)
virtual QPointF anchorPixelPoint(int anchorId) const
void setPen(const QPen &pen)
void setSelectedPen(const QPen &pen)
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1)
void setInterpolating(bool enabled)
QVector< int > getSectionSizes(QVector< int > maxSizes, QVector< int > minSizes, QVector< double > stretchFactors, int totalSize) const
QPen mBorderPen
Definition: qcustomplot.h:2258
void visibleTickBounds(int &lowIndex, int &highIndex) const
QColor mTextColor
Definition: qcustomplot.h:2261
friend class QCPPlottableLegendItem
Definition: qcustomplot.h:1499
no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines) ...
Definition: qcustomplot.h:246
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
bool clipToAxisRect() const
Definition: qcustomplot.h:1621
QBrush mainBrush() const
Q_SLOT void setSelected(bool selected)
virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
QCPItemAnchor * mParentAnchorX
Definition: qcustomplot.h:1596
Blue over pink to white.
Definition: qcustomplot.h:1922
void setUpperQuartile(double value)
{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
Definition: qcustomplot.h:254
void setSelectedTickLabelColor(const QColor &color)
Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18))
void setData(QCPDataMap *data, bool copy=false)
0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see QCPAbstractItem) ...
Definition: qcustomplot.h:161
void mousePress(QMouseEvent *event)
QCPAxisRect * axisRect() const
Definition: qcustomplot.h:1093
bool visible() const
Definition: qcustomplot.h:384
virtual void update(UpdatePhase phase)
QCPAbstractItem * item() const
void setMaximum(double value)
bool hasItem(QCPAbstractLegendItem *item) const
void selectionChangedByUser()
A plus shaped crosshair which spans the complete axis rect.
Definition: qcustomplot.h:3656
Bar width is in absolute pixels.
Definition: qcustomplot.h:2841
QPointer< QCPColorScale > mColorScale
Definition: qcustomplot.h:3100
void setSelectedPen(const QPen &pen)
void setWidth(double width)
QCP::AntialiasedElements mNotAntialiasedElements
Definition: qcustomplot.h:1853
void setInterpolate(bool enabled)
void setText(const QString &text)
int columnCount() const
void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true)
Colors suitable to represent different elevations on geographical maps.
Definition: qcustomplot.h:1923
QCPItemAnchor * anchor(const QString &name) const
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:2055
The positive sign domain, i.e. numbers greater than zero.
Definition: qcustomplot.h:1457
0x0000 No elements
Definition: qcustomplot.h:130
void plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > *pointData) const
QCPRange mDataRange
Definition: qcustomplot.h:3094
virtual void draw(QCPPainter *painter)
QPixmap mPaintBuffer
Definition: qcustomplot.h:1867
void setWidthType(WidthType widthType)
Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
bool mNoAntialiasingOnDrag
Definition: qcustomplot.h:1856
static void connectBars(QCPBars *lower, QCPBars *upper)
void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
virtual void deselectEvent(bool *selectionStateChanged)
QCPItemTracer(QCustomPlot *parentPlot)
double mBaseValue
Definition: qcustomplot.h:2889
QCPAbstractItem(QCustomPlot *parentPlot)
bool selected() const
Definition: qcustomplot.h:1420
void setIconSize(const QSize &size)
A layer that may contain objects, to control the rendering order.
Definition: qcustomplot.h:365
void clearItems()
QCPAxis * rangeDragAxis(Qt::Orientation orientation)
virtual void deselectEvent(bool *selectionStateChanged)
void setFont(const QFont &font)
QBrush brush() const
Definition: qcustomplot.h:1415
Q_SLOT void setSelectable(bool selectable)
void selectionChanged(bool selected)
A filled arrow head with an indented back.
Definition: qcustomplot.h:890
QRgb color(double position, const QCPRange &range, bool logarithmic=false)
bool twoColored() const
Definition: qcustomplot.h:3175
virtual int calculateAutoMargin(QCP::MarginSide side)
{ssSquare.png} a square
Definition: qcustomplot.h:252
void setRowStretchFactor(int row, double factor)
virtual void mousePressEvent(QMouseEvent *event)
void setScaleLogBase(double base)
QPen mainPen() const
QMargins mPadding
Definition: qcustomplot.h:3498
void setZeroLinePen(const QPen &pen)
Handles the different ending decorations for line-like items.
Definition: qcustomplot.h:872
void dataRangeChanged(QCPRange newRange)
bool mAutoTickStep
Definition: qcustomplot.h:1263
void setOuterRect(const QRect &rect)
QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name)
bool mScaledPixmapInvalidated
Definition: qcustomplot.h:3618
QColor mSelectedTickLabelColor
Definition: qcustomplot.h:1252
QCPRange mDataRange
Definition: qcustomplot.h:2439
void setSelectedTextColor(const QColor &color)
void setSelectedTextColor(const QColor &color)
QPen pen() const
Definition: qcustomplot.h:3734
int labelPadding() const
void setAutoTickLabels(bool on)
virtual void setupTickVectors()
QPen mSelectedTickPen
Definition: qcustomplot.h:1265
Responsible for drawing the grid of a QCPAxis.
Definition: qcustomplot.h:930
void setSubGridPen(const QPen &pen)
bool hasItem(QCPAbstractItem *item) const
void setAdaptiveSampling(bool enabled)
void setSelectedFont(const QFont &font)
0xFF all margins
Definition: qcustomplot.h:104
void setTextColor(const QColor &color)
ScaleType scaleType() const
Definition: qcustomplot.h:1094
QPen mIconBorderPen
Definition: qcustomplot.h:2258
Continuous lightness from black over firey colors to white (suited for non-biased data representation...
Definition: qcustomplot.h:1919
void setBrush(const QBrush &brush)
void setPen(const QPen &pen)
QCPLayoutInset * insetLayout() const
Definition: qcustomplot.h:2017
virtual QCPLayoutElement * takeAt(int index)
void setData(double key, double value, double z)
{ssPlusSquare.png} a square with a plus inside
Definition: qcustomplot.h:258
virtual void wheelEvent(QWheelEvent *event)
QCPAxis::LabelSide tickLabelSide
Definition: qcustomplot.h:1345
int height() const
Definition: qcustomplot.h:2030
void setPen(const QPen &pen)
QPixmap mScaledPixmap
Definition: qcustomplot.h:3616
QCPLayoutInset * mInsetLayout
Definition: qcustomplot.h:2049
virtual void updateLayout()
QBrush brush() const
Definition: qcustomplot.h:2206
void setPixmap(const QPixmap &pixmap)
void setPen(const QPen &pen)
QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange)
void setValueAxis(QCPAxis *axis)
QCPLegend * mParentLegend
Definition: qcustomplot.h:2122
bool operator==(const QCPColorGradient &other) const
void setAutoAddPlottableToLegend(bool on)
virtual ~QCPColorMap()
void setWhiskerBarPen(const QPen &pen)
Color channels red, green and blue are linearly interpolated.
Definition: qcustomplot.h:1909
void layerChanged(QCPLayer *newLayer)
QPointer< QCPLayoutElement > mMouseEventElement
Definition: qcustomplot.h:1869
void setLowerEnding(const QCPLineEnding &ending)
void setTextColor(const QColor &color)
void setPen(const QPen &pen)
void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
QBrush mSelectedBrush
Definition: qcustomplot.h:3698
double pixelToCoord(double value) const
void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
QCPLayout * mParentLayout
Definition: qcustomplot.h:689
double mWidth
Definition: qcustomplot.h:2886
void setBarsGroup(QCPBarsGroup *barsGroup)
A bar perpendicular to the line.
Definition: qcustomplot.h:895
PositionType mPositionTypeY
Definition: qcustomplot.h:1592
void drawShape(QCPPainter *painter, QPointF pos) const
virtual void wheelEvent(QWheelEvent *event)
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
bool setCurrentLayer(const QString &name)
0x0400 Zero-lines, see QCPGrid::setZeroLinePen
Definition: qcustomplot.h:128
void afterReplot()
virtual int elementCount() const
QList< QCPLayerable * > mChildren
Definition: qcustomplot.h:394
Layer is inserted above other layer.
Definition: qcustomplot.h:1701
void selectionChanged(bool selected)
void beforeReplot()
QStack< bool > mAntialiasingStack
Definition: qcustomplot.h:360
virtual void axisRemoved(QCPAxis *axis)
QList< QCPAbstractItem * > mItems
Definition: qcustomplot.h:1851
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
data points are connected by a straight line
Definition: qcustomplot.h:2511
QFont font() const
Definition: qcustomplot.h:3450
LabelSide tickLabelSide() const
QCPRange sanitizedForLinScale() const
QPen mainPen() const
Defines a color gradient for use with e.g. QCPColorMap.
Definition: qcustomplot.h:1900
QString label() const
QCPColorGradient inverted() const
bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove)
QRect mViewport
Definition: qcustomplot.h:1846
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
void toPainter(QCPPainter *painter, int width=0, int height=0)
{ssCircle.png} a circle
Definition: qcustomplot.h:250
QRect tickLabelsSelectionBox() const
Definition: qcustomplot.h:1332
void setSubTickLength(int inside, int outside=0)
QVector< QString > tickLabels
Definition: qcustomplot.h:1359
QPolygonF getBarPolygon(double key, double value) const
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual QList< QCPLayoutElement * > elements(bool recursive) const
void setTickVectorLabels(const QVector< QString > &vec)
QCPLayer * currentLayer() const
double tickLabelRotation() const
QCPColorMapData & operator=(const QCPColorMapData &other)
QVector< double > tickPositions
Definition: qcustomplot.h:1358
LabelType tickLabelType() const
Definition: qcustomplot.h:1106
QList< QCPLayerable * > children() const
Definition: qcustomplot.h:383
bool addItem(QCPAbstractLegendItem *item)
QCPItemCurve(QCustomPlot *parentPlot)
0x0008 Legend box
Definition: qcustomplot.h:121
A filled arrow head with a straight/flat back (a triangle)
Definition: qcustomplot.h:889
QList< QCPLayoutElement * > elements(QCP::MarginSide side) const
Definition: qcustomplot.h:608
virtual QPointF pixelPoint() const
QPen pen() const
Definition: qcustomplot.h:3590
virtual ~QCPItemTracer()
virtual void draw(QCPPainter *painter)
void setType(QCPAxis::AxisType type)
virtual void resizeEvent(QResizeEvent *event)
void setSpacingType(SpacingType spacingType)
void removeDataBefore(double t)
double length() const
Definition: qcustomplot.h:906
Q_SLOT void setSelectable(bool selectable)
double cell(int keyIndex, int valueIndex)
void setPen(const QPen &pen)
QCPGraph * graph() const
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
QString mText
Definition: qcustomplot.h:3494
void setLevelCount(int n)
void setTickLabelPadding(int padding)
void setSpacing(double spacing)
void setBrush(const QBrush &brush)
void addData(const QCPCurveDataMap &dataMap)
virtual void mouseReleaseEvent(QMouseEvent *event)
BracketStyle style() const
Definition: qcustomplot.h:3737
QList< QCPGraph * > selectedGraphs() const
QColor mLabelColor
Definition: qcustomplot.h:1245
QColor mainTextColor() const
int itemCount() const
void selectableChanged(const QCPAxis::SelectableParts &parts)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setMedianPen(const QPen &pen)
QPen mSubTickPen
Definition: qcustomplot.h:1266
void setSubTickPen(const QPen &pen)
void setTickStep(double step)
virtual ~QCPLayoutElement()
Q_SLOT bool setLayer(QCPLayer *layer)
0x00 no margin
Definition: qcustomplot.h:105
QFont mFont
Definition: qcustomplot.h:2260
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
virtual QCPLayoutElement * elementAt(int index) const
void setOffset(int offset)
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:1680
void setBrush(const QBrush &brush)
void rescaleKeyAxis(bool onlyEnlarge=false) const
int subTickLengthOut() const
int findIndexAboveY(const QVector< QPointF > *data, double y) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QBrush mainBrush() const
void setMedian(double value)
void setBrush(const QBrush &brush)
double width() const
Definition: qcustomplot.h:905
virtual void paintEvent(QPaintEvent *event)
void setAutoTicks(bool on)
QPen getBorderPen() const
virtual QSize maximumSizeHint() const
void removeFillBasePoints(QVector< QPointF > *lineData) const
double size() const
Definition: qcustomplot.h:275
void setSize(double size)
virtual QSize minimumSizeHint() const
bool mErrorBarSkipSymbol
Definition: qcustomplot.h:2588
void setCustomPath(const QPainterPath &customPath)
double value() const
Definition: qcustomplot.h:1570
QCPColorGradient gradient() const
Definition: qcustomplot.h:2408
void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
void setLineStyle(LineStyle ls)
custom painter operations are performed per scatter (As QPainterPath, see setCustomPath) ...
Definition: qcustomplot.h:263
void removeDataBefore(double key)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPFinancialDataMap * mData
Definition: qcustomplot.h:3212
virtual ~QCPBars()
bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
QCPRange mKeyRange
Definition: qcustomplot.h:3035
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Qt::Alignment insetAlignment(int index) const
void setNumberFormat(const QString &formatCode)
QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
double key() const
Definition: qcustomplot.h:2937
ColorInterpolation mColorInterpolation
Definition: qcustomplot.h:1963
QPen pen() const
Definition: qcustomplot.h:3446
0x0040 Main lines of items
Definition: qcustomplot.h:124
void setErrorType(ErrorType errorType)
QPen mainPen() const
QPen mSelectedIconBorderPen
Definition: qcustomplot.h:2265
virtual void draw(QCPPainter *painter)
void setPeriodic(bool enabled)
Holds the data of one single data point (one bar) for QCPBars.
Definition: qcustomplot.h:2802
double keyErrorMinus
Definition: qcustomplot.h:2473
void rescaleDataRange(bool recalculateDataBounds=false)
QFont mTickLabelFont
Definition: qcustomplot.h:1251
double mScaleLogBase
Definition: qcustomplot.h:1271
Qt::Orientations mRangeDrag
Definition: qcustomplot.h:2050
bool mTicks
Definition: qcustomplot.h:1260
int getMarginValue(const QMargins &margins, QCP::MarginSide side)
Definition: qcustomplot.h:213
QCPLineEnding upperEnding() const
QBrush mBrush
Definition: qcustomplot.h:3409
void setSize(int keySize, int valueSize)
QColor mTickLabelColor
Definition: qcustomplot.h:1252
QPointer< QCPAxis > mColorAxis
Definition: qcustomplot.h:2446
virtual QRect clipRect() const
Manages a legend inside a QCustomPlot.
Definition: qcustomplot.h:2169
Qt::TransformationMode transformationMode() const
Definition: qcustomplot.h:3589
static AxisType marginSideToAxisType(QCP::MarginSide side)
QCPLayoutGrid * mPlotLayout
Definition: qcustomplot.h:1847
void setPen(const QPen &pen)
Definition: qcustomplot.cpp:86
void setViewport(const QRect &rect)
QCPColorGradient mGradient
Definition: qcustomplot.h:2441
int bottom() const
Definition: qcustomplot.h:2028
QList< Qt::Alignment > mInsetAlignment
Definition: qcustomplot.h:864
void moveRange(double diff)
QCPItemPosition *const right
Definition: qcustomplot.h:3749
QSet< QCPItemPosition * > mChildrenY
Definition: qcustomplot.h:1521
void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
Definition: qcustomplot.h:193
void moveAbove(QCPBars *bars)
PlottingHint
Definition: qcustomplot.h:139
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setPadding(int padding)
virtual QCPItemPosition * toQCPItemPosition()
Definition: qcustomplot.h:1524
Qt::TimeSpec mDateTimeSpec
Definition: qcustomplot.h:1254
void getCurveData(QVector< QPointF > *lineData) const
QCPAxis * yAxis
Definition: qcustomplot.h:1819
void setAxisRect(QCPAxisRect *axisRect)
virtual ~QCPItemRect()
virtual void updateMapImage()
void updateAxesOffset(QCPAxis::AxisType type)
QFont mainFont() const
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void mouseMove(QMouseEvent *event)
QFont mLabelFont
Definition: qcustomplot.h:1244
Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and pha...
Definition: qcustomplot.h:1929
QList< QCPBars * > bars() const
Definition: qcustomplot.h:2770
QCP::AntialiasedElements notAntialiasedElements() const
Definition: qcustomplot.h:1725
0x002 Legend items individually (see selectedItems)
Definition: qcustomplot.h:2196
void setFont(const QFont &font)
QCustomPlot(QWidget *parent=0)
QList< QCPAxis * > axes() const
QCPLineEnding tail() const
Definition: qcustomplot.h:3293
void setColorInterpolation(ColorInterpolation interpolation)
void setTickLabelRotation(double degrees)
QCPPlotTitle(QCustomPlot *parentPlot)
void setNoAntialiasingOnDrag(bool enabled)
virtual ~QCPCurve()
QRect mTextBoundingRect
Definition: qcustomplot.h:2341
void setInsetRect(int index, const QRectF &rect)
QCPRange dataRange() const
Definition: qcustomplot.h:2406
A layout that arranges child elements in a grid.
Definition: qcustomplot.h:757
Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion ...
Definition: qcustomplot.h:1928
Continuous lightness from black to white (suited for non-biased data representation) ...
Definition: qcustomplot.h:1918
QCPLayoutElement * element(int row, int column) const
ErrorType errorType() const
Definition: qcustomplot.h:2535
0x0020 Main lines of plottables (excluding error bars, see element aeErrorBars)
Definition: qcustomplot.h:123
void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false)
bool mMapImageInvalidated
Definition: qcustomplot.h:3104
virtual QSize minimumSizeHint() const
int iconTextPadding() const
Definition: qcustomplot.h:2210
QCPRange valueRange() const
Definition: qcustomplot.h:3009
QCP::PlottingHints plottingHints() const
Definition: qcustomplot.h:1730
QCPAbstractPlottable * plottableAt(const QPointF &pos, bool onlySelectable=false) const
LineStyle mLineStyle
Definition: qcustomplot.h:2584
Q_SLOT void setRange(const QCPRange &range)
QBrush brush() const
Definition: qcustomplot.h:3668
void setSelectedTextColor(const QColor &color)
SelectableParts selectableParts() const
Definition: qcustomplot.h:2212
QPainter subclass used internally.
Definition: qcustomplot.h:312
void removeDataAfter(double key)
A bar perpendicular to the line, pointing out to only one side (to which side can be changed with set...
Definition: qcustomplot.h:896
The abstract base class for all objects that form the layout system.
Definition: qcustomplot.h:629
QList< QCPColorMap * > colorMaps() const
void setTicks(bool show)
void setKeyRange(const QCPRange &keyRange)
void setDataValueError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &valueError)
int tickLengthOut() const
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpHint)
virtual ~QCPItemEllipse()
QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const
void setRangeUpper(double upper)
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
TracerStyle style() const
Definition: qcustomplot.h:3671
QCPAxisPainterPrivate(QCustomPlot *parentPlot)
The axis backbone and tick marks.
Definition: qcustomplot.h:1081
QCPMarginGroup(QCustomPlot *parentPlot)
void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
QMargins mMinimumMargins
Definition: qcustomplot.h:692
PositionType mPositionTypeX
Definition: qcustomplot.h:1592
QLineF getRectClippedStraightLine(const QVector2D &point1, const QVector2D &vec, const QRect &rect) const
QMap< double, QCPBarData > QCPBarDataMap
Definition: qcustomplot.h:2818
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:2440
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void setTextAlignment(Qt::Alignment alignment)
double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
bool selected() const
Definition: qcustomplot.h:2103
double mErrorBarSize
Definition: qcustomplot.h:2587
Holds multiple axes and arranges them in a rectangular shape.
Definition: qcustomplot.h:1972
double keyPixelOffset(const QCPBars *bars, double keyCoord)
virtual QPointF anchorPixelPoint(int anchorId) const
QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setColumnStretchFactors(const QList< double > &factors)
QVector< double > mOutliers
Definition: qcustomplot.h:2973
{ssCrossSquare.png} a square with a cross inside
Definition: qcustomplot.h:257
void unregisterBars(QCPBars *bars)
void setSelectedPen(const QPen &pen)
bool rangeZoom() const
void setSelectedBrush(const QBrush &brush)
QColor color() const
Definition: qcustomplot.h:3444
virtual void deselectEvent(bool *selectionStateChanged)
Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
QCPLayer * layer(const QString &name) const
void removeChildX(QCPItemPosition *pos)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setSelectedColor(const QColor &color)
0xFFFF All elements
Definition: qcustomplot.h:129
QCPLineEnding lowerEnding
Definition: qcustomplot.h:1338
void setErrorBarSkipSymbol(bool enabled)
QCPItemPosition *const left
Definition: qcustomplot.h:3748
double lower
Definition: qcustomplot.h:479
QPen mainPen() const
No ending decoration.
Definition: qcustomplot.h:888
virtual void drawScatterPlot(QCPPainter *painter, QVector< QCPData > *scatterData) const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:391
bool contains(double value) const
0x0100 Error bars
Definition: qcustomplot.h:126
double baseLog(double value) const
virtual ~QCPItemLine()
QPainterPath mCustomPath
Definition: qcustomplot.h:304
void setBrush(const QBrush &brush)
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
void coordsToPixels(double key, double value, double &x, double &y) const
Error bars for the value dimension of the data point are shown.
Definition: qcustomplot.h:2523
SelectableParts selectedParts() const
QPen mTickPen
Definition: qcustomplot.h:1265
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
void setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
void setSelectedBrush(const QBrush &brush)
A color scale for use with color coding data such as QCPColorMap.
Definition: qcustomplot.h:2386
QSet< QCPItemPosition * > mChildrenX
Definition: qcustomplot.h:1521
Q_SLOT void setSelectable(bool selectable)
QBrush mBrushNegative
Definition: qcustomplot.h:3216
bool scaled() const
Definition: qcustomplot.h:3587
virtual ~QCPAbstractItem()
SelectableParts mSelectedParts
Definition: qcustomplot.h:2264
QPointer< QCPAxis > mRangeZoomHorzAxis
Definition: qcustomplot.h:2051
void setBarWidth(int width)
bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0)
bool mAntialiasedZeroLine
Definition: qcustomplot.h:963
QList< QCPAxis * > axes(QCPAxis::AxisTypes types) const
virtual QCP::Interaction selectionCategory() const
Groups multiple QCPBars together so they appear side by side.
Definition: qcustomplot.h:2740
void setSelectedIconBorderPen(const QPen &pen)
QCPColorScale * mParentColorScale
Definition: qcustomplot.h:2368
bool mBackgroundScaled
Definition: qcustomplot.h:1860
QCPItemPosition *const endDir
Definition: qcustomplot.h:3351
void adoptElement(QCPLayoutElement *el)
void setRowStretchFactors(const QList< double > &factors)
Linear scaling.
Definition: qcustomplot.h:1072
void setSelectedBrush(const QBrush &brush)
Whether to use immediate repaint or queued update depends on whether the plotting hint QCP::phForceRe...
Definition: qcustomplot.h:1712
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void getStepLeftPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QBrush mSelectedBrush
Definition: qcustomplot.h:2266
void setTickLength(int inside, int outside=0)
int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
QSize iconSize() const
Definition: qcustomplot.h:2209
bool mRangeReversed
Definition: qcustomplot.h:1269
double key() const
Definition: qcustomplot.h:1569
virtual ~QCPItemPosition()
void setMargins(const QMargins &margins)
The element may be positioned/sized arbitrarily, see setInsetRect.
Definition: qcustomplot.h:830
Continuous lightness from black over icey colors to white (suited for non-biased data representation)...
Definition: qcustomplot.h:1920
void draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
QColor getTextColor() const
{ssPlus.png} a plus
Definition: qcustomplot.h:249
QCPRange mDragStartVertRange
Definition: qcustomplot.h:2054
The abstract base class for all entries in a QCPLegend.
Definition: qcustomplot.h:2081
0x04 Axis is horizontal and on the top side of the axis rect
Definition: qcustomplot.h:1044
void getPlotData(QVector< QPointF > *lineData, QVector< QCPData > *scatterData) const
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const =0
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:2048
bool isEmpty() const
Continuous lightness from black over weak blueish colors to white (suited for non-biased data represe...
Definition: qcustomplot.h:1921
QString numberFormat() const
virtual QPointF anchorPixelPoint(int anchorId) const
QMargins margins() const
Definition: qcustomplot.h:659
Tick labels (numbers) of this axis (as a whole, not individually)
Definition: qcustomplot.h:1082
virtual QPointF anchorPixelPoint(int anchorId) const
void gradientChanged(QCPColorGradient newGradient)
0x0010 Legend items
Definition: qcustomplot.h:122
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:2467
double mLength
Definition: qcustomplot.h:924
void setValueSize(int valueSize)
virtual void draw(QCPPainter *painter)
bool selectable() const
Definition: qcustomplot.h:1623
None of the selectable parts.
Definition: qcustomplot.h:1080
double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
void recalculateDataBounds()
virtual void mouseMoveEvent(QMouseEvent *event)
bool moveToLayer(QCPLayer *layer, bool prepend)
void setPixmap(const QPixmap &pixmap)
double width() const
Definition: qcustomplot.h:2944
double pointDistance(const QPointF &pixelPoint) const
Q_SLOT void setSelected(bool selected)
QVector< double > mSubTickVector
Definition: qcustomplot.h:1279
Interaction
Definition: qcustomplot.h:155
void getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
int numberPrecision() const
Definition: qcustomplot.h:1114
double mGraphKey
Definition: qcustomplot.h:3702
void rescaleDataRange(bool onlyVisibleMaps)
void setSelectedTickPen(const QPen &pen)
0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixma...
Definition: qcustomplot.h:322
QCPRange keyRange() const
Definition: qcustomplot.h:3008
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QColor mColor
Definition: qcustomplot.h:3490
QMap< double, QColor > mColorStops
Definition: qcustomplot.h:1962
QPen mZeroLinePen
Definition: qcustomplot.h:964
void setHead(const QCPLineEnding &head)
AntialiasedElement
Definition: qcustomplot.h:118
A legend item representing a plottable with an icon and the plottable name.
Definition: qcustomplot.h:2145
void setUpperEnding(const QCPLineEnding &ending)
Q_SLOT void setDataRange(const QCPRange &dataRange)
QVector< QRgb > mColorBuffer
Definition: qcustomplot.h:1967
void setScatterStyle(const QCPScatterStyle &style)
QList< QCPAxis * > addAxes(QCPAxis::AxisTypes types)
bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1)
void getLinePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
bool isNone() const
Definition: qcustomplot.h:291
void setClipToAxisRect(bool clip)
void setOutliers(const QVector< double > &values)
Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts)
void setSubTickCount(int count)
QString dateTimeFormat() const
Definition: qcustomplot.h:1111
QPen pen() const
Definition: qcustomplot.h:1413
QColor mainColor() const
QPen pen() const
Definition: qcustomplot.h:3249
QPointer< QCPAxis > mRangeDragHorzAxis
Definition: qcustomplot.h:2051
void setIconBorderPen(const QPen &pen)
void setTypeX(PositionType type)
0x080 All other objects are selectable (e.g. your own derived layerables, the plot title...
Definition: qcustomplot.h:162
void setPlottingHint(QCP::PlottingHint hint, bool enabled=true)
virtual bool addToLegend()
QList< QCPAbstractPlottable * > plottables() const
void setAutoTickCount(int approximateCount)
void setData(QCPBarDataMap *data, bool copy=false)
QCPLayer * mCurrentLayer
Definition: qcustomplot.h:1862
QList< QCPItemAnchor * > mAnchors
Definition: qcustomplot.h:1651
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes) ...
Definition: qcustomplot.h:155
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:2055
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPColorMapData * mMapData
Definition: qcustomplot.h:3096
virtual int elementCount() const =0
QBrush mainBrush() const
void clear()
Qt::Alignment mPositionAlignment
Definition: qcustomplot.h:3495
QCPAxis * keyAxis() const
Definition: qcustomplot.h:1572
bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove)
bool isInvalidData(double value)
Definition: qcustomplot.h:172
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:1465
bool tickLabels() const
Definition: qcustomplot.h:1104
virtual void deselectEvent(bool *selectionStateChanged)
Manages a single axis inside a QCustomPlot.
Definition: qcustomplot.h:980
void drawSubGridLines(QCPPainter *painter) const
virtual QList< QCPLayoutElement * > elements(bool recursive) const
QCPItemPixmap(QCustomPlot *parentPlot)
QCPBarsGroup(QCustomPlot *parentPlot)
void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
QCPLineEnding upperEnding
Definition: qcustomplot.h:1338
QCPLineEnding head() const
Definition: qcustomplot.h:3337
QList< QCPLegend * > selectedLegends() const
A brace with round edges.
Definition: qcustomplot.h:3725
QCPAxis * yAxis2
Definition: qcustomplot.h:1819
virtual ~QCPLegend()
QMap< double, QCPFinancialData > QCPFinancialDataMap
Definition: qcustomplot.h:3140
SelectableParts selectableParts() const
Definition: qcustomplot.h:1133
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setChartStyle(ChartStyle style)
bool selected() const
Definition: qcustomplot.h:1624
virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
QFont mSelectedTickLabelFont
Definition: qcustomplot.h:1251
void setAntialiasedZeroLine(bool enabled)
QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
Bar spacing is in absolute pixels.
Definition: qcustomplot.h:2754
Final phase in which the layout system places the rects of the elements.
Definition: qcustomplot.h:648
0x02 Axis is vertical and on the right side of the axis rect
Definition: qcustomplot.h:1043
double value
Definition: qcustomplot.h:2472
void setErrorPen(const QPen &pen)
0x001 The legend box (frame)
Definition: qcustomplot.h:2195
void getPixelWidth(double key, double &lower, double &upper) const
Queues the refresh such that it is performed at a slightly delayed point in time after the replot...
Definition: qcustomplot.h:1711
void append(QCPBars *bars)
QCPItemPosition *const end
Definition: qcustomplot.h:3305
0x02 right margin
Definition: qcustomplot.h:101
A filled square.
Definition: qcustomplot.h:893
QCPColorGradient(GradientPreset preset=gpCold)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void addChild(QCP::MarginSide side, QCPLayoutElement *element)
Error bars for both key and value dimensions of the data point are shown.
Definition: qcustomplot.h:2524
QCPLayout * layout() const
Definition: qcustomplot.h:656
QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setSelectedPen(const QPen &pen)
virtual int calculateAutoMargin(QCP::MarginSide side)
QCPAxisRect * mAxisRect
Definition: qcustomplot.h:1234
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
double center() const
int right() const
Definition: qcustomplot.h:2026
void setErrorBarSize(double size)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QCPItemPosition *const start
Definition: qcustomplot.h:3304
void setVisible(bool on)
QHash< QCP::MarginSide, QCPMarginGroup * > mMarginGroups
Definition: qcustomplot.h:694
QVector< QPointF > getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
double basePow(double value) const
virtual ~QCPAxis()
virtual void draw(QCPPainter *painter)
Bar spacing is given by a fraction of the axis rect size.
Definition: qcustomplot.h:2755
QRectF insetRect(int index) const
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:1465
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const =0
double getStackedBaseValue(double key, bool positive) const
Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white.
Definition: qcustomplot.h:1925
QCPGraph * graph() const
Definition: qcustomplot.h:3672
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3603
virtual void draw(QCPPainter *painter)
QImage mMapImage
Definition: qcustomplot.h:3102
void setData(QCPFinancialDataMap *data, bool copy=false)
int valueSize() const
Definition: qcustomplot.h:3007
QCPItemPosition *const start
Definition: qcustomplot.h:3349
const QCPRange range() const
Definition: qcustomplot.h:1096
void setParentLayerable(QCPLayerable *parentLayerable)
QCPCurveDataMap * data() const
Definition: qcustomplot.h:2680
void moveBelow(QCPBars *bars)
bool autoSubTicks() const
Definition: qcustomplot.h:1102
A margin group allows synchronization of margin sides if working with multiple layout elements...
Definition: qcustomplot.h:600
0x04 Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width...
Definition: qcustomplot.h:323
virtual void draw(QCPPainter *painter)
Error bars for the key dimension of the data point are shown.
Definition: qcustomplot.h:2522
0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap) ...
Definition: qcustomplot.h:125
QPen mSelectedPen
Definition: qcustomplot.h:3309
void rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
QCPMarginGroup * marginGroup(QCP::MarginSide side) const
Definition: qcustomplot.h:664
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lineData) const
int axisRectCount() const
Phase in which the margins are calculated and set.
Definition: qcustomplot.h:647
void rescaleValueAxis(bool onlyEnlarge=false) const
QPointer< QCPGraph > mChannelFillGraph
Definition: qcustomplot.h:2589
bool addPlottable(QCPAbstractPlottable *plottable)
void setBackground(const QPixmap &pm)
QString mLabel
Definition: qcustomplot.h:1243
QCPGraph * addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0)
virtual QSize minimumSizeHint() const
double size() const
Definition: qcustomplot.h:3670
double width() const
Definition: qcustomplot.h:2851
QPen iconBorderPen() const
Definition: qcustomplot.h:2211
void setMinimumSize(const QSize &size)
void setTickLabelColor(const QColor &color)
virtual ~QCPFinancial()
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
const QCP::Interactions interactions() const
Definition: qcustomplot.h:1727
double mScaleLogBaseLogInv
Definition: qcustomplot.h:1271
Q_SLOT void setSelectable(bool selectable)
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
QList< QList< QCPLayoutElement * > > mElements
Definition: qcustomplot.h:809
bool mCachedMarginValid
Definition: qcustomplot.h:1280
An anchor of an item to which positions can be attached to.
Definition: qcustomplot.h:1503
virtual ~QCPItemCurve()
0x0002 Grid lines
Definition: qcustomplot.h:119
void getScatterPlotData(QVector< QCPData > *scatterData) const
void setModes(PainterModes modes)
bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0, const QString &pdfCreator=QString(), const QString &pdfTitle=QString())
virtual void clearData()
void applyScattersAntialiasingHint(QCPPainter *painter) const
friend class QCPItemAnchor
Definition: qcustomplot.h:1676
QList< QCPAbstractPlottable * > mPlottables
Definition: qcustomplot.h:1849
void setLength(double length)
void setName(const QString &name)
virtual QList< QCPLayoutElement * > elements(bool recursive) const
void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
The abstract base class for all items in a plot.
Definition: qcustomplot.h:1607
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
bool addElement(int row, int column, QCPLayoutElement *element)
void setTickLabelType(LabelType type)
double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
void getImpulsePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
line is drawn as steps where the step height is the value of the right data point ...
Definition: qcustomplot.h:2513
void setType(PositionType type)
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:1593
QPixmap pixmap() const
Definition: qcustomplot.h:3586
virtual void draw(QCPPainter *painter)
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
virtual ~QCPItemPixmap()
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:2585
void setColorStops(const QMap< double, QColor > &colorStops)
void mouseDoubleClick(QMouseEvent *event)
void rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
void getStepRightPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QPen pen() const
Definition: qcustomplot.h:3290
QString mDateTimeFormat
Definition: qcustomplot.h:1253
SelectableParts mSelectedParts
Definition: qcustomplot.h:1238
{ssTriangle.png} an equilateral triangle, standing on baseline
Definition: qcustomplot.h:255
QFont mSelectedFont
Definition: qcustomplot.h:2339
bool ticks() const
Definition: qcustomplot.h:1103
QCPAxis * xAxis
Definition: qcustomplot.h:1819
virtual void draw(QCPPainter *painter)
void selectionChanged(QCPLegend::SelectableParts parts)
Q_SLOT void setSelectedParts(const SelectableParts &selectedParts)
void restore()
void selectionChanged(const QCPAxis::SelectableParts &parts)
static AxisType opposite(AxisType type)
QList< InsetPlacement > mInsetPlacement
Definition: qcustomplot.h:863
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:2046
virtual void draw(QCPPainter *painter)
void removeDataAfter(double key)
void setRange(const QCPRange &keyRange, const QCPRange &valueRange)
virtual QRect clipRect() const
void plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
Qt::TransformationMode mTransformationMode
Definition: qcustomplot.h:3620
QCPAxisRect * axisRect() const
double mRangeZoomFactorVert
Definition: qcustomplot.h:2052
QCPItemAnchor * parentAnchorY() const
Definition: qcustomplot.h:1568
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
double valueErrorPlus
Definition: qcustomplot.h:2474
QList< QCPGraph * > graphs() const
Q_SLOT void setSelectableParts(const SelectableParts &selectableParts)
bool mVisible
Definition: qcustomplot.h:395
QCPLineEnding lowerEnding() const
QVector< QString > mTickVectorLabels
Definition: qcustomplot.h:1278
QPointer< QCPLayerable > mParentLayerable
Definition: qcustomplot.h:448
bool mReplotting
Definition: qcustomplot.h:1870
void setKeySize(int keySize)
Qt::Alignment mTextAlignment
Definition: qcustomplot.h:3496
QFont font() const
Definition: qcustomplot.h:2207
QRect labelSelectionBox() const
Definition: qcustomplot.h:1333
QCPRange mRange
Definition: qcustomplot.h:1268
void setTickLabelFont(const QFont &font)
QPointF coords() const
Definition: qcustomplot.h:1571
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
Definition: qcustomplot.h:1556
QFont font() const
Definition: qcustomplot.h:2311
The tracer is not visible.
Definition: qcustomplot.h:3654
virtual void draw(QCPPainter *painter)
QFont mainFont() const
double data(double key, double value)
QCPItemPosition *const position
Definition: qcustomplot.h:3476
Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the ...
Definition: qcustomplot.h:1910
void setSelectedPen(const QPen &pen)
virtual bool removeFromLegend() const
void setLowerQuartile(double value)
A layout that places child elements aligned to the border or arbitrarily positioned.
Definition: qcustomplot.h:823
QPen selectedIconBorderPen() const
Definition: qcustomplot.h:2215
void addData(const QCPDataMap &dataMap)
virtual void drawOutliers(QCPPainter *painter) const
void setSelectedSubTickPen(const QPen &pen)
line is drawn as steps where the step height is the value of the left data point
Definition: qcustomplot.h:2512
virtual void mouseDoubleClickEvent(QMouseEvent *event)
void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
void setTickPen(const QPen &pen)
QPen getBasePen() const
void setSize(double size)
void setKeyAxis(QCPAxis *axis)
QBrush mSelectedBrush
Definition: qcustomplot.h:3492
virtual void mouseReleaseEvent(QMouseEvent *event)
QColor mSelectedTextColor
Definition: qcustomplot.h:2340
Phase used for any type of preparation that needs to be done before margin calculation and layout...
Definition: qcustomplot.h:646
virtual ~QCPItemAnchor()
static const double minRange
Definition: qcustomplot.h:509
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void addFillBasePoints(QVector< QPointF > *lineData) const
QBrush mBackgroundBrush
Definition: qcustomplot.h:2044
QCPItemPosition *const point1
Definition: qcustomplot.h:3259
QMargins padding() const
Definition: qcustomplot.h:3456
void setDataBothError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError, const QVector< double > &valueError)
virtual QCP::Interaction selectionCategory() const
void setSelectedBrush(const QBrush &brush)
double rangeZoomFactor(Qt::Orientation orientation)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setRangeZoom(Qt::Orientations orientations)
PainterModes mModes
Definition: qcustomplot.h:356
Data points are connected with a straight line.
Definition: qcustomplot.h:2674
int commonMargin(QCP::MarginSide side) const
void initializeParentPlot(QCustomPlot *parentPlot)
void setPen(const QPen &pen)
void rescale(bool onlyVisiblePlottables=false)
void setAntialiasedSubGrid(bool enabled)
QCustomPlot * mParentPlot
Definition: qcustomplot.h:1518
virtual void mouseMoveEvent(QMouseEvent *event)
void setPositionAlignment(Qt::Alignment alignment)
void setData(QCPCurveDataMap *data, bool copy=false)
A plottable representing a bar chart in a plot.
Definition: qcustomplot.h:2823
QCPLineEnding mTail
Definition: qcustomplot.h:3310
virtual void clearData()
QList< QCPAxisRect * > axisRects() const
void setInsetAlignment(int index, Qt::Alignment alignment)
QBrush brush() const
Definition: qcustomplot.h:3383
void removeDataBefore(double key)
void expandTo(int newRowCount, int newColumnCount)
void setWhiskerWidth(double width)
An approximation of the visible light spectrum (creates banding illusion but allows more precise magn...
Definition: qcustomplot.h:1927
void setBackground(const QPixmap &pm)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0)
QCPItemPosition * createPosition(const QString &name)
The QCustomPlot surface is immediately refreshed, by calling QWidget::repaint() after the replot...
Definition: qcustomplot.h:1710
void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
void updateLayerIndices() const
QCP::PlottingHints mPlottingHints
Definition: qcustomplot.h:1863
void setColumnSpacing(int pixels)
QPen mSubGridPen
Definition: qcustomplot.h:964
void setScatterStyle(const QCPScatterStyle &style)
double valueErrorMinus
Definition: qcustomplot.h:2474
virtual void mouseMoveEvent(QMouseEvent *event)
PainterModes modes() const
Definition: qcustomplot.h:334
virtual void mouseReleaseEvent(QMouseEvent *event)
QMap< double, QColor > colorStops() const
Definition: qcustomplot.h:1939
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void titleClick(QMouseEvent *event, QCPPlotTitle *title)
void setChannelFillGraph(QCPGraph *targetGraph)
A bar that is skewed (skew controllable via setLength)
Definition: qcustomplot.h:897
QCPLayerable * layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:1858
0x08 bottom margin
Definition: qcustomplot.h:103
int offset() const
int layerCount() const
QPen mSelectedPen
Definition: qcustomplot.h:3408
void selectableChanged(bool selectable)
QList< QCPAbstractItem * > items() const
void setBasePen(const QPen &pen)
int subTickLengthIn() const
virtual void updateLayout()
QCPAxis * addAxis(QCPAxis::AxisType type, QCPAxis *axis=0)
void insertRow(int newIndex)
a custom pixmap specified by setPixmap, centered on the data point coordinates
Definition: qcustomplot.h:262
bool mBackgroundScaled
Definition: qcustomplot.h:2047
Base class for all drawable objects.
Definition: qcustomplot.h:408
QCPItemPosition *const end
Definition: qcustomplot.h:3352
void setDateTimeSpec(const Qt::TimeSpec &timeSpec)
QPoint mMousePressPos
Definition: qcustomplot.h:1868
void drawBackground(QCPPainter *painter)
QString text() const
Definition: qcustomplot.h:3452
QRect axisSelectionBox() const
Definition: qcustomplot.h:1331
void setBorderPen(const QPen &pen)
QBrush mainBrush() const
QCP::AntialiasedElements mAntialiasedElements
Definition: qcustomplot.h:1853
QPointF upperFillBasePoint(double upperKey) const
virtual void draw(QCPPainter *painter)
virtual void deselectEvent(bool *selectionStateChanged)
bool hasAnchor(const QString &name) const
void setKey(double key)
void setPen(const QPen &pen)
SelectableParts mSelectableParts
Definition: qcustomplot.h:2264
void drawBackground(QCPPainter *painter)
QCPRange mValueRange
Definition: qcustomplot.h:3035
int clearPlottables()
void setStyle(TracerStyle style)
QFont getLabelFont() const
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
QVector< double > mTickVector
Definition: qcustomplot.h:1277
bool removeItem(QCPAbstractItem *item)
Bar width is given by a fraction of the axis rect size.
Definition: qcustomplot.h:2842
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle ...
Definition: qcustomplot.h:1926
QCPAxis * axis(QCPAxis::AxisType type, int index=0) const
void setTickLabels(bool show)
void setPen(const QPen &pen)
{ssDisc.png} a circle which is filled with the pen&#39;s color (not the brush as with ssCircle) ...
Definition: qcustomplot.h:251
Tick labels will be displayed outside the axis rect.
Definition: qcustomplot.h:1065
Bar width is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:2843
0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fix...
Definition: qcustomplot.h:321
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
double scaleLogBase() const
Definition: qcustomplot.h:1095
QBrush mBrush
Definition: qcustomplot.h:3492
void addChild(QCPLayerable *layerable, bool prepend)
QFont mSelectedFont
Definition: qcustomplot.h:3493
int mPadding
Definition: qcustomplot.h:1236
QRect rect() const
Definition: qcustomplot.h:657
QCPItemAnchor * parentAnchorX() const
Definition: qcustomplot.h:1567
void setPenNegative(const QPen &pen)
bool removeItem(int index)
QCP::AntialiasedElements antialiasedElements() const
Definition: qcustomplot.h:1724
void setRangeDrag(bool enabled)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void scaleRange(double factor, double center)
Qt::Orientations mRangeZoom
Definition: qcustomplot.h:2050
QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale)
No error bars are shown.
Definition: qcustomplot.h:2521
void setGraph(QCPGraph *graph)
virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const
EndingStyle style() const
Definition: qcustomplot.h:904
static bool validRange(double lower, double upper)
QPen getTickPen() const
virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const
void setShape(ScatterShape shape)
void setAutoSubTicks(bool on)
QCPItemPosition *const startDir
Definition: qcustomplot.h:3350
void setLineStyle(LineStyle style)
virtual QSize minimumSizeHint() const
QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
bool autoTickStep() const
Definition: qcustomplot.h:1101
virtual bool take(QCPLayoutElement *element)
bool rangeDrag() const
void applyTo(QCPPainter *painter, const QPen &defaultPen) const
QCPLayer * mLayer
Definition: qcustomplot.h:449
QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true)
A plottable representing a graph in a plot.
Definition: qcustomplot.h:2490
bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
int mSelectionTolerance
Definition: qcustomplot.h:1855
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
ScatterShape mShape
Definition: qcustomplot.h:300
QFont mSelectedLabelFont
Definition: qcustomplot.h:1244
QCPRange expanded(const QCPRange &otherRange) const
QCPLineEnding mTail
Definition: qcustomplot.h:3357
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const =0
QPen pen() const
Definition: qcustomplot.h:3335
void setIconTextPadding(int padding)
QString mText
Definition: qcustomplot.h:2336
void setMinimum(double value)
QList< QCPGraph * > graphs() const
bool hasPlottable(QCPAbstractPlottable *plottable) const
void expand(const QCPRange &otherRange)
QPointer< QCPAxis > mRangeDragVertAxis
Definition: qcustomplot.h:2051
int left() const
Definition: qcustomplot.h:2025
0x08 Axis is horizontal and on the bottom side of the axis rect
Definition: qcustomplot.h:1045
bool mNumberBeautifulPowers
Definition: qcustomplot.h:1257
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
virtual QCPLayoutElement * elementAt(int index) const =0
void setAutoMargins(QCP::MarginSides sides)
void registerBars(QCPBars *bars)
No line is drawn between data points (e.g. only scatters)
Definition: qcustomplot.h:2673
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
bool mayTraverse(int prevRegion, int currentRegion) const
void setPlottingHints(const QCP::PlottingHints &hints)
QPen mainPen() const
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const =0
void dataRangeChanged(QCPRange newRange)
QCPDataMap * data() const
Definition: qcustomplot.h:2532
virtual int calculateMargin()
{ssCross.png} a cross
Definition: qcustomplot.h:248
Holds the two-dimensional data of a QCPColorMap plottable.
Definition: qcustomplot.h:2997
virtual bool take(QCPLayoutElement *element)
QPen pen() const
Definition: qcustomplot.h:3527
double spacing() const
Definition: qcustomplot.h:2763
void setClipAxisRect(QCPAxisRect *rect)
QPen mainPen() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setColor(const QColor &color)
QCPLineEnding mHead
Definition: qcustomplot.h:3357
A plus shaped crosshair with limited size.
Definition: qcustomplot.h:3655
virtual void draw(QCPPainter *painter)
void setMinimumMargins(const QMargins &margins)
void setAntialiased(bool enabled)
QPointer< QCPAxis > mRangeZoomVertAxis
Definition: qcustomplot.h:2051
void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
QCPItemRect(QCustomPlot *parentPlot)
The abstract base class for all data representing objects in a plot.
Definition: qcustomplot.h:1388
QPen mainPen() const
virtual QCPLayoutElement * takeAt(int index)
QColor getLabelColor() const
virtual ~QCPAxisRect()
void getMaximumRowColSizes(QVector< int > *maxColWidths, QVector< int > *maxRowHeights) const
bool mTightBoundary
Definition: qcustomplot.h:3099
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void addData(const QCPFinancialDataMap &dataMap)
double realLength() const
virtual ~QCPGraph()
void setSelectedPen(const QPen &pen)
void drawGridLines(QCPPainter *painter) const
bool mSubGridVisible
Definition: qcustomplot.h:962
bool removeGraph(QCPGraph *graph)
int mAutoTickCount
Definition: qcustomplot.h:1262
QPointer< QCPBars > mBarBelow
Definition: qcustomplot.h:2890
int axisCount(QCPAxis::AxisType type) const
void setAutoTickStep(bool on)
QPointer< QCPBars > mBarAbove
Definition: qcustomplot.h:2890
SpacingType mSpacingType
Definition: qcustomplot.h:2783
LabelType mTickLabelType
Definition: qcustomplot.h:1250
double coordToPixel(double value) const
void setWidth(double width)
friend class QCPAxisRect
Definition: qcustomplot.h:1896
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:1861
void rangeChanged(const QCPRange &newRange)
void gradientChanged(QCPColorGradient newGradient)
bool mInterpolate
Definition: qcustomplot.h:3098
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lineData) const
QPen mainPen() const
void setSubTickLengthIn(int inside)
AxisType mAxisType
Definition: qcustomplot.h:1233
virtual QCPLayoutElement * takeAt(int index)=0
QVector< double > subTickPositions
Definition: qcustomplot.h:1357
void setText(const QString &text)
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom, QCPAxisRect::setRangeZoomAxes)
Definition: qcustomplot.h:156
virtual QByteArray generateLabelParameterHash() const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:1373
bool selectable() const
Definition: qcustomplot.h:2315
QCPRange mDragStartHorzRange
Definition: qcustomplot.h:2054
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
double key
Definition: qcustomplot.h:2807
virtual void draw(QCPPainter *painter)
virtual QList< QCPLayoutElement * > elements(bool recursive) const
virtual void draw(QCPPainter *painter)
line is drawn as steps where the step is in between two data points
Definition: qcustomplot.h:2514
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
virtual QPointF pixelPoint() const
0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)
Definition: qcustomplot.h:159
A non-filled arrow head with open back.
Definition: qcustomplot.h:891
void removeChild(QCPLayerable *layerable)
virtual QPointF anchorPixelPoint(int anchorId) const
double size() const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QCPLayerable * parentLayerable() const
Definition: qcustomplot.h:425
void setGraphKey(double key)
void removeData(double fromt, double tot)
QCPItemPosition *const point2
Definition: qcustomplot.h:3260
QPen pen() const
Definition: qcustomplot.h:3381
void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
virtual void draw(QCPPainter *painter)
void setPen(const QPen &pen)
QCPItemAnchor * parentAnchor() const
Definition: qcustomplot.h:1566
QByteArray mLabelParameterHash
Definition: qcustomplot.h:1374
void setRangeDrag(Qt::Orientations orientations)
void setBackgroundScaled(bool scaled)
QColor mTextColor
Definition: qcustomplot.h:2338
virtual void draw(QCPPainter *painter)
bool mAntialiased
Definition: qcustomplot.h:450
void setWidth(double width)
void setInteractions(const QCP::Interactions &interactions)
void setRotation(double degrees)
QCPAbstractPlottable * plottable(int index)
void setSubTickLengthOut(int outside)
void setSelectedFont(const QFont &font)
virtual void wheelEvent(QWheelEvent *event)
QPen mPen
Definition: qcustomplot.h:964
A plottable representing a two-dimensional color map in a plot.
Definition: qcustomplot.h:3046
void setSelectedBrush(const QBrush &brush)
QBrush brush() const
Definition: qcustomplot.h:3448
bool mAutoTicks
Definition: qcustomplot.h:1263
void setSelectedBrush(const QBrush &brush)
void setSelectedLabelColor(const QColor &color)
virtual QRect clipRect() const
QColor mSelectedTextColor
Definition: qcustomplot.h:2268
QCPAxis * valueAxis() const
Definition: qcustomplot.h:1418
QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
QList< double > mColumnStretchFactors
Definition: qcustomplot.h:810
virtual int size() const
void setNumberPrecision(int precision)
Q_SLOT void setSelected(bool selected)
bool hasElement(int row, int column)
void rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
void selectionChanged(bool selected)
virtual ~QCustomPlot()
bool realVisibility() const
QList< QCPAbstractItem * > selectedItems() const
QList< QRectF > mInsetRect
Definition: qcustomplot.h:865
ScatterShape shape() const
Definition: qcustomplot.h:276
void setTail(const QCPLineEnding &tail)
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:424
bool mAutoAddPlottableToLegend
Definition: qcustomplot.h:1848
virtual ~QCPItemText()
void setSelectedBorderPen(const QPen &pen)
QBrush mSelectedBrush
Definition: qcustomplot.h:3558
QMap< double, QCPCurveData > QCPCurveDataMap
Definition: qcustomplot.h:2655
QFont font() const
Definition: qcustomplot.h:2098
void setSelectedPen(const QPen &pen)
virtual void update(UpdatePhase phase)
double key
Definition: qcustomplot.h:2472
void rescaleAxes(bool onlyEnlarge=false) const
0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts) ...
Definition: qcustomplot.h:160
QMargins mMargins
Definition: qcustomplot.h:692
QBrush mBrushPositive
Definition: qcustomplot.h:3216
QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
double mTickStep
Definition: qcustomplot.h:1261
bool selected() const
Definition: qcustomplot.h:2316
QCPAbstractPlottable * plottable()
virtual void drawMedian(QCPPainter *painter) const
ErrorType mErrorType
Definition: qcustomplot.h:2586
QBrush brush() const
Definition: qcustomplot.h:3529
QCPAxis * mParentAxis
Definition: qcustomplot.h:966
void removeDataAfter(double t)
void setWidth(double width)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3542
virtual void draw(QCPPainter *painter)
Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power...
Definition: qcustomplot.h:1073
int plottableCount() const
void loadPreset(GradientPreset preset)
Holds the data of one single data point for QCPFinancial.
Definition: qcustomplot.h:3124
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
SpacingType spacingType() const
Definition: qcustomplot.h:2762
virtual ~QCPLayoutInset()
void removeDataAfter(double key)
void fill(double z)
Qt::TimeSpec dateTimeSpec() const
Definition: qcustomplot.h:1112
virtual QCP::Interaction selectionCategory() const
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:1859
int autoTickCount() const
Definition: qcustomplot.h:1099
void setSelectedPen(const QPen &pen)
QPen mSelectedSubTickPen
Definition: qcustomplot.h:1266
void setLabelPadding(int padding)
void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false)
void setFont(const QFont &font)
QPointer< QCPAxisRect > mAxisRect
Definition: qcustomplot.h:1594
virtual ~QCPLayoutGrid()
virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const
void remove(QCPBars *bars)
virtual void deselectEvent(bool *selectionStateChanged)
void setPixelPoint(const QPointF &pixelPoint)
double mSpacing
Definition: qcustomplot.h:2784
virtual QCP::Interaction selectionCategory() const
void addElement(QCPLayoutElement *element, Qt::Alignment alignment)
int rowCount() const
QCPLineEnding head() const
Definition: qcustomplot.h:3292
QCPAxis * rangeZoomAxis(Qt::Orientation orientation)
void setBrush(const QBrush &brush)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3396
virtual void draw(QCPPainter *painter)
QSize size() const
Definition: qcustomplot.h:2031
void setStyle(EndingStyle style)
QCPAbstractItem * mParentItem
Definition: qcustomplot.h:1519
Open-High-Low-Close bar representation.
Definition: qcustomplot.h:3163
QCPRange mDataBounds
Definition: qcustomplot.h:3039
void removeDataBefore(double key)
QBrush mBackgroundBrush
Definition: qcustomplot.h:1857
double value
Definition: qcustomplot.h:2807
QCPColorGradient gradient() const
Definition: qcustomplot.h:3067
QCPLayer * layer() const
Definition: qcustomplot.h:426
QList< double > mRowStretchFactors
Definition: qcustomplot.h:811
void setAntialiasedFill(bool enabled)
void setTextColor(const QColor &color)
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
QCPDataMap * mData
Definition: qcustomplot.h:2582
QCPColorMapData * data() const
Definition: qcustomplot.h:3062
QImage mUndersampledMapImage
Definition: qcustomplot.h:3102
void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
QCPItemLine(QCustomPlot *parentPlot)
void setStyle(BracketStyle style)
bool mAutoSubTicks
Definition: qcustomplot.h:1263
bool selectable() const
Definition: qcustomplot.h:2102
QCPBarsGroup * mBarsGroup
Definition: qcustomplot.h:2888
QPen pen() const
Definition: qcustomplot.h:948