iCub-main
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, 2012, 2013, 2014 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: 07.04.14 **
23 ** Version: 1.2.1 **
24 ****************************************************************************/
25 
26 #include "qcustomplot.h"
27 
28 
29 
30 
34 
52  QPainter(),
53  mModes(pmDefault),
54  mIsAntialiasing(false)
55 {
56  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
57  // a call to begin() will follow
58 }
59 
66 QCPPainter::QCPPainter(QPaintDevice *device) :
67  QPainter(device),
68  mModes(pmDefault),
69  mIsAntialiasing(false)
70 {
71 #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.
72  if (isActive())
73  setRenderHint(QPainter::NonCosmeticDefaultPen);
74 #endif
75 }
76 
78 {
79 }
80 
87 void QCPPainter::setPen(const QPen &pen)
88 {
89  QPainter::setPen(pen);
90  if (mModes.testFlag(pmNonCosmetic))
92 }
93 
101 void QCPPainter::setPen(const QColor &color)
102 {
103  QPainter::setPen(color);
104  if (mModes.testFlag(pmNonCosmetic))
105  makeNonCosmetic();
106 }
107 
115 void QCPPainter::setPen(Qt::PenStyle penStyle)
116 {
117  QPainter::setPen(penStyle);
118  if (mModes.testFlag(pmNonCosmetic))
119  makeNonCosmetic();
120 }
121 
130 void QCPPainter::drawLine(const QLineF &line)
131 {
132  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
133  QPainter::drawLine(line);
134  else
135  QPainter::drawLine(line.toLine());
136 }
137 
144 void QCPPainter::setAntialiasing(bool enabled)
145 {
146  setRenderHint(QPainter::Antialiasing, enabled);
147  if (mIsAntialiasing != enabled)
148  {
149  mIsAntialiasing = enabled;
150  if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
151  {
152  if (mIsAntialiasing)
153  translate(0.5, 0.5);
154  else
155  translate(-0.5, -0.5);
156  }
157  }
158 }
159 
164 void QCPPainter::setModes(QCPPainter::PainterModes modes)
165 {
166  mModes = modes;
167 }
168 
180 bool QCPPainter::begin(QPaintDevice *device)
181 {
182  bool result = QPainter::begin(device);
183 #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.
184  if (result)
185  setRenderHint(QPainter::NonCosmeticDefaultPen);
186 #endif
187  return result;
188 }
189 
196 {
197  if (!enabled && mModes.testFlag(mode))
198  mModes &= ~mode;
199  else if (enabled && !mModes.testFlag(mode))
200  mModes |= mode;
201 }
202 
212 {
214  QPainter::save();
215 }
216 
226 {
227  if (!mAntialiasingStack.isEmpty())
229  else
230  qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
231  QPainter::restore();
232 }
233 
239 {
240  if (qFuzzyIsNull(pen().widthF()))
241  {
242  QPen p = pen();
243  p.setWidth(1);
244  QPainter::setPen(p);
245  }
246 }
247 
248 
252 
314 /* start documentation of inline functions */
315 
335 /* end documentation of inline functions */
336 
344  mSize(6),
345  mShape(ssNone),
346  mPen(Qt::NoPen),
347  mBrush(Qt::NoBrush),
348  mPenDefined(false)
349 {
350 }
351 
360  mSize(size),
361  mShape(shape),
362  mPen(Qt::NoPen),
363  mBrush(Qt::NoBrush),
364  mPenDefined(false)
365 {
366 }
367 
372 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) :
373  mSize(size),
374  mShape(shape),
375  mPen(QPen(color)),
376  mBrush(Qt::NoBrush),
377  mPenDefined(true)
378 {
379 }
380 
385 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
386  mSize(size),
387  mShape(shape),
388  mPen(QPen(color)),
389  mBrush(QBrush(fill)),
390  mPenDefined(true)
391 {
392 }
393 
409 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
410  mSize(size),
411  mShape(shape),
412  mPen(pen),
413  mBrush(brush),
414  mPenDefined(pen.style() != Qt::NoPen)
415 {
416 }
417 
422 QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) :
423  mSize(5),
424  mShape(ssPixmap),
425  mPen(Qt::NoPen),
426  mBrush(Qt::NoBrush),
427  mPixmap(pixmap),
428  mPenDefined(false)
429 {
430 }
431 
441 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
442  mSize(size),
443  mShape(ssCustom),
444  mPen(pen),
445  mBrush(brush),
446  mCustomPath(customPath),
447  mPenDefined(false)
448 {
449 }
450 
456 void QCPScatterStyle::setSize(double size)
457 {
458  mSize = size;
459 }
460 
470 {
471  mShape = shape;
472 }
473 
482 void QCPScatterStyle::setPen(const QPen &pen)
483 {
484  mPenDefined = true;
485  mPen = pen;
486 }
487 
494 void QCPScatterStyle::setBrush(const QBrush &brush)
495 {
496  mBrush = brush;
497 }
498 
506 void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
507 {
509  mPixmap = pixmap;
510 }
511 
517 void QCPScatterStyle::setCustomPath(const QPainterPath &customPath)
518 {
521 }
522 
532 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
533 {
534  painter->setPen(mPenDefined ? mPen : defaultPen);
535  painter->setBrush(mBrush);
536 }
537 
546 void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
547 {
548  drawShape(painter, pos.x(), pos.y());
549 }
550 
554 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
555 {
556  double w = mSize/2.0;
557  switch (mShape)
558  {
559  case ssNone: break;
560  case ssDot:
561  {
562  painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
563  break;
564  }
565  case ssCross:
566  {
567  painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
568  painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
569  break;
570  }
571  case ssPlus:
572  {
573  painter->drawLine(QLineF(x-w, y, x+w, y));
574  painter->drawLine(QLineF( x, y+w, x, y-w));
575  break;
576  }
577  case ssCircle:
578  {
579  painter->drawEllipse(QPointF(x , y), w, w);
580  break;
581  }
582  case ssDisc:
583  {
584  QBrush b = painter->brush();
585  painter->setBrush(painter->pen().color());
586  painter->drawEllipse(QPointF(x , y), w, w);
587  painter->setBrush(b);
588  break;
589  }
590  case ssSquare:
591  {
592  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
593  break;
594  }
595  case ssDiamond:
596  {
597  painter->drawLine(QLineF(x-w, y, x, y-w));
598  painter->drawLine(QLineF( x, y-w, x+w, y));
599  painter->drawLine(QLineF(x+w, y, x, y+w));
600  painter->drawLine(QLineF( x, y+w, x-w, y));
601  break;
602  }
603  case ssStar:
604  {
605  painter->drawLine(QLineF(x-w, y, x+w, y));
606  painter->drawLine(QLineF( x, y+w, x, y-w));
607  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
608  painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
609  break;
610  }
611  case ssTriangle:
612  {
613  painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
614  painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
615  painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w));
616  break;
617  }
618  case ssTriangleInverted:
619  {
620  painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
621  painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
622  painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w));
623  break;
624  }
625  case ssCrossSquare:
626  {
627  painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
628  painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
629  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
630  break;
631  }
632  case ssPlusSquare:
633  {
634  painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
635  painter->drawLine(QLineF( x, y+w, x, y-w));
636  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
637  break;
638  }
639  case ssCrossCircle:
640  {
641  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
642  painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
643  painter->drawEllipse(QPointF(x, y), w, w);
644  break;
645  }
646  case ssPlusCircle:
647  {
648  painter->drawLine(QLineF(x-w, y, x+w, y));
649  painter->drawLine(QLineF( x, y+w, x, y-w));
650  painter->drawEllipse(QPointF(x, y), w, w);
651  break;
652  }
653  case ssPeace:
654  {
655  painter->drawLine(QLineF(x, y-w, x, y+w));
656  painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
657  painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
658  painter->drawEllipse(QPointF(x, y), w, w);
659  break;
660  }
661  case ssPixmap:
662  {
663  painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
664  break;
665  }
666  case ssCustom:
667  {
668  QTransform oldTransform = painter->transform();
669  painter->translate(x, y);
670  painter->scale(mSize/6.0, mSize/6.0);
671  painter->drawPath(mCustomPath);
672  painter->setTransform(oldTransform);
673  break;
674  }
675  }
676 }
677 
678 
682 
725 /* start documentation of inline functions */
726 
741 /* end documentation of inline functions */
742 
751 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
752  QObject(parentPlot),
753  mParentPlot(parentPlot),
754  mName(layerName),
755  mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
756  mVisible(true)
757 {
758  // Note: no need to make sure layerName is unique, because layer
759  // management is done with QCustomPlot functions.
760 }
761 
763 {
764  // If child layerables are still on this layer, detach them, so they don't try to reach back to this
765  // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
766  // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
767  // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
768 
769  while (!mChildren.isEmpty())
770  mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
771 
772  if (mParentPlot->currentLayer() == this)
773  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.";
774 }
775 
784 void QCPLayer::setVisible(bool visible)
785 {
786  mVisible = visible;
787 }
788 
799 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
800 {
801  if (!mChildren.contains(layerable))
802  {
803  if (prepend)
804  mChildren.prepend(layerable);
805  else
806  mChildren.append(layerable);
807  } else
808  qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
809 }
810 
821 {
822  if (!mChildren.removeOne(layerable))
823  qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
824 }
825 
826 
830 
843 /* start documentation of inline functions */
844 
858 /* end documentation of inline functions */
859 /* start documentation of pure virtual functions */
860 
901 /* end documentation of pure virtual functions */
902 /* start documentation of signals */
903 
912 /* end documentation of signals */
913 
932 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
933  QObject(plot),
934  mVisible(true),
935  mParentPlot(plot),
936  mParentLayerable(parentLayerable),
937  mLayer(0),
938  mAntialiased(true)
939 {
940  if (mParentPlot)
941  {
942  if (targetLayer.isEmpty())
944  else if (!setLayer(targetLayer))
945  qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
946  }
947 }
948 
950 {
951  if (mLayer)
952  {
953  mLayer->removeChild(this);
954  mLayer = 0;
955  }
956 }
957 
964 {
965  mVisible = on;
966 }
967 
975 {
976  return moveToLayer(layer, false);
977 }
978 
984 bool QCPLayerable::setLayer(const QString &layerName)
985 {
986  if (!mParentPlot)
987  {
988  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
989  return false;
990  }
991  if (QCPLayer *layer = mParentPlot->layer(layerName))
992  {
993  return setLayer(layer);
994  } else
995  {
996  qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
997  return false;
998  }
999 }
1000 
1008 {
1009  mAntialiased = enabled;
1010 }
1011 
1026 {
1027  return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1028 }
1029 
1064 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1065 {
1066  Q_UNUSED(pos)
1067  Q_UNUSED(onlySelectable)
1068  Q_UNUSED(details)
1069  return -1.0;
1070 }
1071 
1090 {
1091  if (mParentPlot)
1092  {
1093  qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1094  return;
1095  }
1096 
1097  if (!parentPlot)
1098  qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1099 
1102 }
1103 
1116 {
1118 }
1119 
1128 bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
1129 {
1130  if (layer && !mParentPlot)
1131  {
1132  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1133  return false;
1134  }
1135  if (layer && layer->parentPlot() != mParentPlot)
1136  {
1137  qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1138  return false;
1139  }
1140 
1141  QCPLayer *oldLayer = mLayer;
1142  if (mLayer)
1143  mLayer->removeChild(this);
1144  mLayer = layer;
1145  if (mLayer)
1146  mLayer->addChild(this, prepend);
1147  if (mLayer != oldLayer)
1148  emit layerChanged(mLayer);
1149  return true;
1150 }
1151 
1159 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1160 {
1161  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1162  painter->setAntialiasing(false);
1163  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1164  painter->setAntialiasing(true);
1165  else
1166  painter->setAntialiasing(localAntialiased);
1167 }
1168 
1186 {
1187  Q_UNUSED(parentPlot)
1188 }
1189 
1202 {
1203  return QCP::iSelectOther;
1204 }
1205 
1216 {
1217  if (mParentPlot)
1218  return mParentPlot->viewport();
1219  else
1220  return QRect();
1221 }
1222 
1251 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1252 {
1253  Q_UNUSED(event)
1254  Q_UNUSED(additive)
1255  Q_UNUSED(details)
1256  Q_UNUSED(selectionStateChanged)
1257 }
1258 
1271 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1272 {
1273  Q_UNUSED(selectionStateChanged)
1274 }
1275 
1276 
1280 
1295 const double QCPRange::minRange = 1e-280;
1296 
1305 const double QCPRange::maxRange = 1e250;
1306 
1311  lower(0),
1312  upper(0)
1313 {
1314 }
1315 
1319 QCPRange::QCPRange(double lower, double upper) :
1320  lower(lower),
1321  upper(upper)
1322 {
1323  normalize();
1324 }
1325 
1329 double QCPRange::size() const
1330 {
1331  return upper-lower;
1332 }
1333 
1337 double QCPRange::center() const
1338 {
1339  return (upper+lower)*0.5;
1340 }
1341 
1347 {
1348  if (lower > upper)
1349  qSwap(lower, upper);
1350 }
1351 
1360 void QCPRange::expand(const QCPRange &otherRange)
1361 {
1362  if (lower > otherRange.lower)
1363  lower = otherRange.lower;
1364  if (upper < otherRange.upper)
1365  upper = otherRange.upper;
1366 }
1367 
1368 
1375 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1376 {
1377  QCPRange result = *this;
1378  result.expand(otherRange);
1379  return result;
1380 }
1381 
1395 {
1396  double rangeFac = 1e-3;
1397  QCPRange sanitizedRange(lower, upper);
1398  sanitizedRange.normalize();
1399  // can't have range spanning negative and positive values in log plot, so change range to fix it
1400  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
1401  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
1402  {
1403  // case lower is 0
1404  if (rangeFac < sanitizedRange.upper*rangeFac)
1405  sanitizedRange.lower = rangeFac;
1406  else
1407  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1408  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1409  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
1410  {
1411  // case upper is 0
1412  if (-rangeFac > sanitizedRange.lower*rangeFac)
1413  sanitizedRange.upper = -rangeFac;
1414  else
1415  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1416  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
1417  {
1418  // find out whether negative or positive interval is wider to decide which sign domain will be chosen
1419  if (-sanitizedRange.lower > sanitizedRange.upper)
1420  {
1421  // negative is wider, do same as in case upper is 0
1422  if (-rangeFac > sanitizedRange.lower*rangeFac)
1423  sanitizedRange.upper = -rangeFac;
1424  else
1425  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1426  } else
1427  {
1428  // positive is wider, do same as in case lower is 0
1429  if (rangeFac < sanitizedRange.upper*rangeFac)
1430  sanitizedRange.lower = rangeFac;
1431  else
1432  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1433  }
1434  }
1435  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
1436  return sanitizedRange;
1437 }
1438 
1444 {
1445  QCPRange sanitizedRange(lower, upper);
1446  sanitizedRange.normalize();
1447  return sanitizedRange;
1448 }
1449 
1453 bool QCPRange::contains(double value) const
1454 {
1455  return value >= lower && value <= upper;
1456 }
1457 
1466 bool QCPRange::validRange(double lower, double upper)
1467 {
1468  /*
1469  return (lower > -maxRange &&
1470  upper < maxRange &&
1471  qAbs(lower-upper) > minRange &&
1472  (lower < -minRange || lower > minRange) &&
1473  (upper < -minRange || upper > minRange));
1474  */
1475  return (lower > -maxRange &&
1476  upper < maxRange &&
1477  qAbs(lower-upper) > minRange &&
1478  qAbs(lower-upper) < maxRange);
1479 }
1480 
1490 bool QCPRange::validRange(const QCPRange &range)
1491 {
1492  /*
1493  return (range.lower > -maxRange &&
1494  range.upper < maxRange &&
1495  qAbs(range.lower-range.upper) > minRange &&
1496  qAbs(range.lower-range.upper) < maxRange &&
1497  (range.lower < -minRange || range.lower > minRange) &&
1498  (range.upper < -minRange || range.upper > minRange));
1499  */
1500  return (range.lower > -maxRange &&
1501  range.upper < maxRange &&
1502  qAbs(range.lower-range.upper) > minRange &&
1503  qAbs(range.lower-range.upper) < maxRange);
1504 }
1505 
1506 
1625 
1662 /* start documentation of inline functions */
1663 
1670 /* end documentation of inline functions */
1671 
1676  QObject(parentPlot),
1677  mParentPlot(parentPlot)
1678 {
1679  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
1680  mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
1681  mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
1682  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
1683 }
1684 
1686 {
1687  clear();
1688 }
1689 
1695 {
1696  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1697  while (it.hasNext())
1698  {
1699  it.next();
1700  if (!it.value().isEmpty())
1701  return false;
1702  }
1703  return true;
1704 }
1705 
1711 {
1712  // make all children remove themselves from this margin group:
1713  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1714  while (it.hasNext())
1715  {
1716  it.next();
1717  const QList<QCPLayoutElement*> elements = it.value();
1718  for (int i=elements.size()-1; i>=0; --i)
1719  elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
1720  }
1721 }
1722 
1734 {
1735  // query all automatic margins of the layout elements in this margin group side and find maximum:
1736  int result = 0;
1737  const QList<QCPLayoutElement*> elements = mChildren.value(side);
1738  for (int i=0; i<elements.size(); ++i)
1739  {
1740  if (!elements.at(i)->autoMargins().testFlag(side))
1741  continue;
1742  int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1743  if (m > result)
1744  result = m;
1745  }
1746  return result;
1747 }
1748 
1756 {
1757  if (!mChildren[side].contains(element))
1758  mChildren[side].append(element);
1759  else
1760  qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
1761 }
1762 
1770 {
1771  if (!mChildren[side].removeOne(element))
1772  qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
1773 }
1774 
1775 
1779 
1806 /* start documentation of inline functions */
1807 
1855 /* end documentation of inline functions */
1856 
1861  QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
1862  mParentLayout(0),
1863  mMinimumSize(),
1864  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1865  mRect(0, 0, 0, 0),
1866  mOuterRect(0, 0, 0, 0),
1867  mMargins(0, 0, 0, 0),
1868  mMinimumMargins(0, 0, 0, 0),
1869  mAutoMargins(QCP::msAll)
1870 {
1871 }
1872 
1874 {
1875  setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
1876  // unregister at layout:
1877  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
1878  mParentLayout->take(this);
1879 }
1880 
1892 void QCPLayoutElement::setOuterRect(const QRect &rect)
1893 {
1894  if (mOuterRect != rect)
1895  {
1896  mOuterRect = rect;
1897  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1898  }
1899 }
1900 
1912 void QCPLayoutElement::setMargins(const QMargins &margins)
1913 {
1914  if (mMargins != margins)
1915  {
1916  mMargins = margins;
1917  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1918  }
1919 }
1920 
1930 void QCPLayoutElement::setMinimumMargins(const QMargins &margins)
1931 {
1932  if (mMinimumMargins != margins)
1933  {
1935  }
1936 }
1937 
1948 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
1949 {
1950  mAutoMargins = sides;
1951 }
1952 
1962 void QCPLayoutElement::setMinimumSize(const QSize &size)
1963 {
1964  if (mMinimumSize != size)
1965  {
1966  mMinimumSize = size;
1967  if (mParentLayout)
1969  }
1970 }
1971 
1976 void QCPLayoutElement::setMinimumSize(int width, int height)
1977 {
1978  setMinimumSize(QSize(width, height));
1979 }
1980 
1985 void QCPLayoutElement::setMaximumSize(const QSize &size)
1986 {
1987  if (mMaximumSize != size)
1988  {
1989  mMaximumSize = size;
1990  if (mParentLayout)
1992  }
1993 }
1994 
1999 void QCPLayoutElement::setMaximumSize(int width, int height)
2000 {
2001  setMaximumSize(QSize(width, height));
2002 }
2003 
2015 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
2016 {
2017  QVector<QCP::MarginSide> sideVector;
2018  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
2019  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
2020  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
2021  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
2022 
2023  for (int i=0; i<sideVector.size(); ++i)
2024  {
2025  QCP::MarginSide side = sideVector.at(i);
2026  if (marginGroup(side) != group)
2027  {
2028  QCPMarginGroup *oldGroup = marginGroup(side);
2029  if (oldGroup) // unregister at old group
2030  oldGroup->removeChild(side, this);
2031 
2032  if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
2033  {
2034  mMarginGroups.remove(side);
2035  } else // setting to a new group
2036  {
2037  mMarginGroups[side] = group;
2038  group->addChild(side, this);
2039  }
2040  }
2041  }
2057 {
2058  if (phase == upMargins)
2059  {
2060  if (mAutoMargins != QCP::msNone)
2061  {
2062  // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
2063  QMargins newMargins = mMargins;
2064  foreach (QCP::MarginSide side, QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom)
2065  {
2066  if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
2067  {
2068  if (mMarginGroups.contains(side))
2069  QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
2070  else
2071  QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
2072  // apply minimum margin restrictions:
2073  if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
2074  QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
2075  }
2076  }
2077  setMargins(newMargins);
2078  }
2079  }
2080 }
2081 
2090 {
2091  return mMinimumSize;
2092 }
2093 
2102 {
2103  return mMaximumSize;
2104 }
2105 
2113 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
2114 {
2115  Q_UNUSED(recursive)
2116  return QList<QCPLayoutElement*>();
2117 }
2118 
2130 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
2131 {
2132  Q_UNUSED(details)
2133 
2134  if (onlySelectable)
2135  return -1;
2136 
2137  if (QRectF(mOuterRect).contains(pos))
2138  {
2139  if (mParentPlot)
2140  return mParentPlot->selectionTolerance()*0.99;
2141  else
2142  {
2143  qDebug() << Q_FUNC_INFO << "parent plot not defined";
2144  return -1;
2145  }
2146  } else
2147  return -1;
2148 }
2149 
2156 {
2157  foreach (QCPLayoutElement* el, elements(false))
2158  {
2159  if (!el->parentPlot())
2161  }
2162 }
2163 
2174 {
2176 }
2177 
2181 
2205 /* start documentation of pure virtual functions */
2206 
2249 /* end documentation of pure virtual functions */
2250 
2256 {
2257 }
2258 
2268 {
2269  QCPLayoutElement::update(phase);
2270 
2271  // set child element rects according to layout:
2272  if (phase == upLayout)
2273  updateLayout();
2274 
2275  // propagate update call to child elements:
2276  const int elCount = elementCount();
2277  for (int i=0; i<elCount; ++i)
2278  {
2279  if (QCPLayoutElement *el = elementAt(i))
2280  el->update(phase);
2281  }
2282 }
2283 
2284 /* inherits documentation from base class */
2285 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2286 {
2287  const int c = elementCount();
2288  QList<QCPLayoutElement*> result;
2289 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2290  result.reserve(c);
2291 #endif
2292  for (int i=0; i<c; ++i)
2293  result.append(elementAt(i));
2294  if (recursive)
2295  {
2296  for (int i=0; i<c; ++i)
2297  {
2298  if (result.at(i))
2299  result << result.at(i)->elements(recursive);
2300  }
2301  }
2302  return result;
2303 }
2304 
2313 {
2314 }
2315 
2325 bool QCPLayout::removeAt(int index)
2326 {
2327  if (QCPLayoutElement *el = takeAt(index))
2328  {
2329  delete el;
2330  return true;
2331  } else
2332  return false;
2333 }
2334 
2345 {
2346  if (take(element))
2347  {
2348  delete element;
2349  return true;
2350  } else
2351  return false;
2352 }
2353 
2360 {
2361  for (int i=elementCount()-1; i>=0; --i)
2362  {
2363  if (elementAt(i))
2364  removeAt(i);
2365  }
2366  simplify();
2367 }
2368 
2378 {
2379  if (QWidget *w = qobject_cast<QWidget*>(parent()))
2380  w->updateGeometry();
2381  else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
2382  l->sizeConstraintsChanged();
2383 }
2384 
2398 {
2399 }
2400 
2401 
2415 {
2416  if (el)
2417  {
2418  el->mParentLayout = this;
2419  el->setParentLayerable(this);
2420  el->setParent(this);
2421  if (!el->parentPlot())
2423  } else
2424  qDebug() << Q_FUNC_INFO << "Null element passed";
2425 }
2426 
2438 {
2439  if (el)
2440  {
2441  el->mParentLayout = 0;
2442  el->setParentLayerable(0);
2443  el->setParent(mParentPlot);
2444  // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
2445  } else
2446  qDebug() << Q_FUNC_INFO << "Null element passed";
2447 }
2448 
2478 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
2479 {
2480  if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
2481  {
2482  qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
2483  return QVector<int>();
2484  }
2485  if (stretchFactors.isEmpty())
2486  return QVector<int>();
2487  int sectionCount = stretchFactors.size();
2488  QVector<double> sectionSizes(sectionCount);
2489  // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
2490  int minSizeSum = 0;
2491  for (int i=0; i<sectionCount; ++i)
2492  minSizeSum += minSizes.at(i);
2493  if (totalSize < minSizeSum)
2494  {
2495  // new stretch factors are minimum sizes and minimum sizes are set to zero:
2496  for (int i=0; i<sectionCount; ++i)
2497  {
2498  stretchFactors[i] = minSizes.at(i);
2499  minSizes[i] = 0;
2500  }
2501  }
2502 
2503  QList<int> minimumLockedSections;
2504  QList<int> unfinishedSections;
2505  for (int i=0; i<sectionCount; ++i)
2506  unfinishedSections.append(i);
2507  double freeSize = totalSize;
2508 
2509  int outerIterations = 0;
2510  while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2511  {
2512  ++outerIterations;
2513  int innerIterations = 0;
2514  while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2515  {
2516  ++innerIterations;
2517  // find section that hits its maximum next:
2518  int nextId = -1;
2519  double nextMax = 1e12;
2520  for (int i=0; i<unfinishedSections.size(); ++i)
2521  {
2522  int secId = unfinishedSections.at(i);
2523  double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
2524  if (hitsMaxAt < nextMax)
2525  {
2526  nextMax = hitsMaxAt;
2527  nextId = secId;
2528  }
2529  }
2530  // 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
2531  // actually hits its maximum, without exceeding the total size when we add up all sections)
2532  double stretchFactorSum = 0;
2533  for (int i=0; i<unfinishedSections.size(); ++i)
2534  stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2535  double nextMaxLimit = freeSize/stretchFactorSum;
2536  if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
2537  {
2538  for (int i=0; i<unfinishedSections.size(); ++i)
2539  {
2540  sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2541  freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
2542  }
2543  unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
2544  } else // next maximum isn't hit, just distribute rest of free space on remaining sections
2545  {
2546  for (int i=0; i<unfinishedSections.size(); ++i)
2547  sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2548  unfinishedSections.clear();
2549  }
2550  }
2551  if (innerIterations == sectionCount*2)
2552  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2553 
2554  // now check whether the resulting section sizes violate minimum restrictions:
2555  bool foundMinimumViolation = false;
2556  for (int i=0; i<sectionSizes.size(); ++i)
2557  {
2558  if (minimumLockedSections.contains(i))
2559  continue;
2560  if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
2561  {
2562  sectionSizes[i] = minSizes.at(i); // set it to minimum
2563  foundMinimumViolation = true; // make sure we repeat the whole optimization process
2564  minimumLockedSections.append(i);
2565  }
2566  }
2567  if (foundMinimumViolation)
2568  {
2569  freeSize = totalSize;
2570  for (int i=0; i<sectionCount; ++i)
2571  {
2572  if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
2573  unfinishedSections.append(i);
2574  else
2575  freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
2576  }
2577  // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
2578  for (int i=0; i<unfinishedSections.size(); ++i)
2579  sectionSizes[unfinishedSections.at(i)] = 0;
2580  }
2581  }
2582  if (outerIterations == sectionCount*2)
2583  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2584 
2585  QVector<int> result(sectionCount);
2586  for (int i=0; i<sectionCount; ++i)
2587  result[i] = qRound(sectionSizes.at(i));
2588  return result;
2589 }
2590 
2591 
2595 
2616  mColumnSpacing(5),
2617  mRowSpacing(5)
2618 {
2619 }
2620 
2622 {
2623  // clear all child layout elements. This is important because only the specific layouts know how
2624  // to handle removing elements (clear calls virtual removeAt method to do that).
2625  clear();
2626 }
2627 
2636 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
2637 {
2638  if (row >= 0 && row < mElements.size())
2639  {
2640  if (column >= 0 && column < mElements.first().size())
2641  {
2642  if (QCPLayoutElement *result = mElements.at(row).at(column))
2643  return result;
2644  else
2645  qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
2646  } else
2647  qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
2648  } else
2649  qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
2650  return 0;
2651 }
2652 
2659 {
2660  return mElements.size();
2661 }
2662 
2669 {
2670  if (mElements.size() > 0)
2671  return mElements.first().size();
2672  else
2673  return 0;
2674 }
2675 
2686 bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
2687 {
2688  if (element)
2689  {
2690  if (!hasElement(row, column))
2691  {
2692  if (element->layout()) // remove from old layout first
2693  element->layout()->take(element);
2694  expandTo(row+1, column+1);
2695  mElements[row][column] = element;
2697  return true;
2698  } else
2699  qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2700  } else
2701  qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2702  return false;
2703 }
2704 
2711 bool QCPLayoutGrid::hasElement(int row, int column)
2712 {
2713  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2714  return mElements.at(row).at(column);
2715  else
2716  return false;
2717 }
2718 
2730 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
2731 {
2732  if (column >= 0 && column < columnCount())
2733  {
2734  if (factor > 0)
2735  mColumnStretchFactors[column] = factor;
2736  else
2737  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2738  } else
2739  qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2740 }
2741 
2753 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
2754 {
2755  if (factors.size() == mColumnStretchFactors.size())
2756  {
2757  mColumnStretchFactors = factors;
2758  for (int i=0; i<mColumnStretchFactors.size(); ++i)
2759  {
2760  if (mColumnStretchFactors.at(i) <= 0)
2761  {
2762  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
2763  mColumnStretchFactors[i] = 1;
2764  }
2765  }
2766  } else
2767  qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
2768 }
2769 
2781 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
2782 {
2783  if (row >= 0 && row < rowCount())
2784  {
2785  if (factor > 0)
2786  mRowStretchFactors[row] = factor;
2787  else
2788  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2789  } else
2790  qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2791 }
2792 
2804 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
2805 {
2806  if (factors.size() == mRowStretchFactors.size())
2807  {
2808  mRowStretchFactors = factors;
2809  for (int i=0; i<mRowStretchFactors.size(); ++i)
2810  {
2811  if (mRowStretchFactors.at(i) <= 0)
2812  {
2813  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
2814  mRowStretchFactors[i] = 1;
2815  }
2816  }
2817  } else
2818  qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
2819 }
2820 
2827 {
2828  mColumnSpacing = pixels;
2829 }
2830 
2837 {
2838  mRowSpacing = pixels;
2839 }
2840 
2855 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
2856 {
2857  // add rows as necessary:
2858  while (rowCount() < newRowCount)
2859  {
2860  mElements.append(QList<QCPLayoutElement*>());
2861  mRowStretchFactors.append(1);
2862  }
2863  // go through rows and expand columns as necessary:
2864  int newColCount = qMax(columnCount(), newColumnCount);
2865  for (int i=0; i<rowCount(); ++i)
2866  {
2867  while (mElements.at(i).size() < newColCount)
2868  mElements[i].append(0);
2869  }
2870  while (mColumnStretchFactors.size() < newColCount)
2871  mColumnStretchFactors.append(1);
2872 }
2873 
2880 void QCPLayoutGrid::insertRow(int newIndex)
2881 {
2882  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2883  {
2884  expandTo(1, 1);
2885  return;
2886  }
2887 
2888  if (newIndex < 0)
2889  newIndex = 0;
2890  if (newIndex > rowCount())
2891  newIndex = rowCount();
2892 
2893  mRowStretchFactors.insert(newIndex, 1);
2894  QList<QCPLayoutElement*> newRow;
2895  for (int col=0; col<columnCount(); ++col)
2896  newRow.append((QCPLayoutElement*)0);
2897  mElements.insert(newIndex, newRow);
2898 }
2899 
2907 {
2908  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2909  {
2910  expandTo(1, 1);
2911  return;
2912  }
2913 
2914  if (newIndex < 0)
2915  newIndex = 0;
2916  if (newIndex > columnCount())
2917  newIndex = columnCount();
2918 
2919  mColumnStretchFactors.insert(newIndex, 1);
2920  for (int row=0; row<rowCount(); ++row)
2921  mElements[row].insert(newIndex, (QCPLayoutElement*)0);
2922 }
2923 
2924 /* inherits documentation from base class */
2926 {
2927  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2928  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2929  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2930 
2931  int totalRowSpacing = (rowCount()-1) * mRowSpacing;
2932  int totalColSpacing = (columnCount()-1) * mColumnSpacing;
2933  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
2934  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
2935 
2936  // go through cells and set rects accordingly:
2937  int yOffset = mRect.top();
2938  for (int row=0; row<rowCount(); ++row)
2939  {
2940  if (row > 0)
2941  yOffset += rowHeights.at(row-1)+mRowSpacing;
2942  int xOffset = mRect.left();
2943  for (int col=0; col<columnCount(); ++col)
2944  {
2945  if (col > 0)
2946  xOffset += colWidths.at(col-1)+mColumnSpacing;
2947  if (mElements.at(row).at(col))
2948  mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2949  }
2950  }
2951 }
2952 
2953 /* inherits documentation from base class */
2955 {
2956  return rowCount()*columnCount();
2957 }
2958 
2959 /* inherits documentation from base class */
2961 {
2962  if (index >= 0 && index < elementCount())
2963  return mElements.at(index / columnCount()).at(index % columnCount());
2964  else
2965  return 0;
2966 }
2967 
2968 /* inherits documentation from base class */
2970 {
2971  if (QCPLayoutElement *el = elementAt(index))
2972  {
2973  releaseElement(el);
2974  mElements[index / columnCount()][index % columnCount()] = 0;
2975  return el;
2976  } else
2977  {
2978  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2979  return 0;
2980  }
2981 }
2982 
2983 /* inherits documentation from base class */
2985 {
2986  if (element)
2987  {
2988  for (int i=0; i<elementCount(); ++i)
2989  {
2990  if (elementAt(i) == element)
2991  {
2992  takeAt(i);
2993  return true;
2994  }
2995  }
2996  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2997  } else
2998  qDebug() << Q_FUNC_INFO << "Can't take null element";
2999  return false;
3000 }
3001 
3002 /* inherits documentation from base class */
3003 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
3004 {
3005  QList<QCPLayoutElement*> result;
3006  int colC = columnCount();
3007  int rowC = rowCount();
3008 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
3009  result.reserve(colC*rowC);
3010 #endif
3011  for (int row=0; row<rowC; ++row)
3012  {
3013  for (int col=0; col<colC; ++col)
3014  {
3015  result.append(mElements.at(row).at(col));
3016  }
3017  }
3018  if (recursive)
3019  {
3020  int c = result.size();
3021  for (int i=0; i<c; ++i)
3022  {
3023  if (result.at(i))
3024  result << result.at(i)->elements(recursive);
3025  }
3026  }
3027  return result;
3028 }
3029 
3034 {
3035  // remove rows with only empty cells:
3036  for (int row=rowCount()-1; row>=0; --row)
3037  {
3038  bool hasElements = false;
3039  for (int col=0; col<columnCount(); ++col)
3040  {
3041  if (mElements.at(row).at(col))
3042  {
3043  hasElements = true;
3044  break;
3045  }
3046  }
3047  if (!hasElements)
3048  {
3049  mRowStretchFactors.removeAt(row);
3050  mElements.removeAt(row);
3051  if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
3052  mColumnStretchFactors.clear();
3053  }
3054  }
3055 
3056  // remove columns with only empty cells:
3057  for (int col=columnCount()-1; col>=0; --col)
3058  {
3059  bool hasElements = false;
3060  for (int row=0; row<rowCount(); ++row)
3061  {
3062  if (mElements.at(row).at(col))
3063  {
3064  hasElements = true;
3065  break;
3066  }
3067  }
3068  if (!hasElements)
3069  {
3070  mColumnStretchFactors.removeAt(col);
3071  for (int row=0; row<rowCount(); ++row)
3072  mElements[row].removeAt(col);
3073  }
3074  }
3075 }
3076 
3077 /* inherits documentation from base class */
3079 {
3080  QVector<int> minColWidths, minRowHeights;
3081  getMinimumRowColSizes(&minColWidths, &minRowHeights);
3082  QSize result(0, 0);
3083  for (int i=0; i<minColWidths.size(); ++i)
3084  result.rwidth() += minColWidths.at(i);
3085  for (int i=0; i<minRowHeights.size(); ++i)
3086  result.rheight() += minRowHeights.at(i);
3087  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
3088  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
3089  return result;
3090 }
3091 
3092 /* inherits documentation from base class */
3094 {
3095  QVector<int> maxColWidths, maxRowHeights;
3096  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
3097 
3098  QSize result(0, 0);
3099  for (int i=0; i<maxColWidths.size(); ++i)
3100  result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
3101  for (int i=0; i<maxRowHeights.size(); ++i)
3102  result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
3103  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
3104  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
3105  return result;
3106 }
3107 
3120 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
3121 {
3122  *minColWidths = QVector<int>(columnCount(), 0);
3123  *minRowHeights = QVector<int>(rowCount(), 0);
3124  for (int row=0; row<rowCount(); ++row)
3125  {
3126  for (int col=0; col<columnCount(); ++col)
3127  {
3128  if (mElements.at(row).at(col))
3129  {
3130  QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
3131  QSize min = mElements.at(row).at(col)->minimumSize();
3132  QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
3133  if (minColWidths->at(col) < final.width())
3134  (*minColWidths)[col] = final.width();
3135  if (minRowHeights->at(row) < final.height())
3136  (*minRowHeights)[row] = final.height();
3137  }
3138  }
3139  }
3140 }
3141 
3154 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
3155 {
3156  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3157  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3158  for (int row=0; row<rowCount(); ++row)
3159  {
3160  for (int col=0; col<columnCount(); ++col)
3161  {
3162  if (mElements.at(row).at(col))
3163  {
3164  QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3165  QSize max = mElements.at(row).at(col)->maximumSize();
3166  QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
3167  if (maxColWidths->at(col) > final.width())
3168  (*maxColWidths)[col] = final.width();
3169  if (maxRowHeights->at(row) > final.height())
3170  (*maxRowHeights)[row] = final.height();
3171  }
3172  }
3173  }
3174 }
3175 
3176 
3180 
3198 /* start documentation of inline functions */
3199 
3206 /* end documentation of inline functions */
3207 
3212 {
3213 }
3214 
3216 {
3217  // clear all child layout elements. This is important because only the specific layouts know how
3218  // to handle removing elements (clear calls virtual removeAt method to do that).
3219  clear();
3220 }
3221 
3226 {
3227  if (elementAt(index))
3228  return mInsetPlacement.at(index);
3229  else
3230  {
3231  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3232  return ipFree;
3233  }
3234 }
3235 
3240 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
3241 {
3242  if (elementAt(index))
3243  return mInsetAlignment.at(index);
3244  else
3245  {
3246  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3247  return 0;
3248  }
3249 }
3250 
3255 QRectF QCPLayoutInset::insetRect(int index) const
3256 {
3257  if (elementAt(index))
3258  return mInsetRect.at(index);
3259  else
3260  {
3261  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3262  return QRectF();
3263  }
3264 }
3265 
3272 {
3273  if (elementAt(index))
3274  mInsetPlacement[index] = placement;
3275  else
3276  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3277 }
3278 
3287 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
3288 {
3289  if (elementAt(index))
3290  mInsetAlignment[index] = alignment;
3291  else
3292  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3293 }
3294 
3306 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
3307 {
3308  if (elementAt(index))
3309  mInsetRect[index] = rect;
3310  else
3311  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3312 }
3313 
3314 /* inherits documentation from base class */
3316 {
3317  for (int i=0; i<mElements.size(); ++i)
3318  {
3319  QRect insetRect;
3320  QSize finalMinSize, finalMaxSize;
3321  QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3322  QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3323  finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
3324  finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
3325  finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
3326  finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
3327  if (mInsetPlacement.at(i) == ipFree)
3328  {
3329  insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
3330  rect().y()+rect().height()*mInsetRect.at(i).y(),
3331  rect().width()*mInsetRect.at(i).width(),
3332  rect().height()*mInsetRect.at(i).height());
3333  if (insetRect.size().width() < finalMinSize.width())
3334  insetRect.setWidth(finalMinSize.width());
3335  if (insetRect.size().height() < finalMinSize.height())
3336  insetRect.setHeight(finalMinSize.height());
3337  if (insetRect.size().width() > finalMaxSize.width())
3338  insetRect.setWidth(finalMaxSize.width());
3339  if (insetRect.size().height() > finalMaxSize.height())
3340  insetRect.setHeight(finalMaxSize.height());
3341  } else if (mInsetPlacement.at(i) == ipBorderAligned)
3342  {
3343  insetRect.setSize(finalMinSize);
3344  Qt::Alignment al = mInsetAlignment.at(i);
3345  if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
3346  else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
3347  else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
3348  if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
3349  else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
3350  else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
3351  }
3352  mElements.at(i)->setOuterRect(insetRect);
3353  }
3354 }
3355 
3356 /* inherits documentation from base class */
3358 {
3359  return mElements.size();
3360 }
3361 
3362 /* inherits documentation from base class */
3364 {
3365  if (index >= 0 && index < mElements.size())
3366  return mElements.at(index);
3367  else
3368  return 0;
3369 }
3370 
3371 /* inherits documentation from base class */
3373 {
3374  if (QCPLayoutElement *el = elementAt(index))
3375  {
3376  releaseElement(el);
3377  mElements.removeAt(index);
3378  mInsetPlacement.removeAt(index);
3379  mInsetAlignment.removeAt(index);
3380  mInsetRect.removeAt(index);
3381  return el;
3382  } else
3383  {
3384  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3385  return 0;
3386  }
3387 }
3388 
3389 /* inherits documentation from base class */
3391 {
3392  if (element)
3393  {
3394  for (int i=0; i<elementCount(); ++i)
3395  {
3396  if (elementAt(i) == element)
3397  {
3398  takeAt(i);
3399  return true;
3400  }
3401  }
3402  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3403  } else
3404  qDebug() << Q_FUNC_INFO << "Can't take null element";
3405  return false;
3406 }
3407 
3417 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3418 {
3419  Q_UNUSED(details)
3420  if (onlySelectable)
3421  return -1;
3422 
3423  for (int i=0; i<mElements.size(); ++i)
3424  {
3425  // inset layout shall only return positive selectTest, if actually an inset object is at pos
3426  // else it would block the entire underlying QCPAxisRect with its surface.
3427  if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3428  return mParentPlot->selectionTolerance()*0.99;
3429  }
3430  return -1;
3431 }
3432 
3444 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3445 {
3446  if (element)
3447  {
3448  if (element->layout()) // remove from old layout first
3449  element->layout()->take(element);
3450  mElements.append(element);
3452  mInsetAlignment.append(alignment);
3453  mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3454  adoptElement(element);
3455  } else
3456  qDebug() << Q_FUNC_INFO << "Can't add null element";
3457 }
3458 
3470 void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
3471 {
3472  if (element)
3473  {
3474  if (element->layout()) // remove from old layout first
3475  element->layout()->take(element);
3476  mElements.append(element);
3477  mInsetPlacement.append(ipFree);
3478  mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
3479  mInsetRect.append(rect);
3480  adoptElement(element);
3481  } else
3482  qDebug() << Q_FUNC_INFO << "Can't add null element";
3483 }
3484 
3485 
3489 
3514  mStyle(esNone),
3515  mWidth(8),
3516  mLength(10),
3517  mInverted(false)
3518 {
3519 }
3520 
3524 QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
3525  mStyle(style),
3526  mWidth(width),
3527  mLength(length),
3528  mInverted(inverted)
3529 {
3530 }
3531 
3536 {
3537  mStyle = style;
3538 }
3539 
3546 void QCPLineEnding::setWidth(double width)
3547 {
3548  mWidth = width;
3549 }
3550 
3557 void QCPLineEnding::setLength(double length)
3558 {
3559  mLength = length;
3560 }
3561 
3570 void QCPLineEnding::setInverted(bool inverted)
3571 {
3572  mInverted = inverted;
3573 }
3574 
3585 {
3586  switch (mStyle)
3587  {
3588  case esNone:
3589  return 0;
3590 
3591  case esFlatArrow:
3592  case esSpikeArrow:
3593  case esLineArrow:
3594  case esSkewedBar:
3595  return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
3596 
3597  case esDisc:
3598  case esSquare:
3599  case esDiamond:
3600  case esBar:
3601  case esHalfBar:
3602  return mWidth*1.42; // items that only have a width -> width*sqrt(2)
3603 
3604  }
3605  return 0;
3606 }
3607 
3620 {
3621  switch (mStyle)
3622  {
3623  case esNone:
3624  case esLineArrow:
3625  case esSkewedBar:
3626  case esBar:
3627  case esHalfBar:
3628  return 0;
3629 
3630  case esFlatArrow:
3631  return mLength;
3632 
3633  case esDisc:
3634  case esSquare:
3635  case esDiamond:
3636  return mWidth*0.5;
3637 
3638  case esSpikeArrow:
3639  return mLength*0.8;
3640  }
3641  return 0;
3642 }
3643 
3649 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
3650 {
3651  if (mStyle == esNone)
3652  return;
3653 
3654  QVector2D lengthVec(dir.normalized());
3655  if (lengthVec.isNull())
3656  lengthVec = QVector2D(1, 0);
3657  QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3658  lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3659  widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3660 
3661  QPen penBackup = painter->pen();
3662  QBrush brushBackup = painter->brush();
3663  QPen miterPen = penBackup;
3664  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3665  QBrush brush(painter->pen().color(), Qt::SolidPattern);
3666  switch (mStyle)
3667  {
3668  case esNone: break;
3669  case esFlatArrow:
3670  {
3671  QPointF points[3] = {pos.toPointF(),
3672  (pos-lengthVec+widthVec).toPointF(),
3673  (pos-lengthVec-widthVec).toPointF()
3674  };
3675  painter->setPen(miterPen);
3676  painter->setBrush(brush);
3677  painter->drawConvexPolygon(points, 3);
3678  painter->setBrush(brushBackup);
3679  painter->setPen(penBackup);
3680  break;
3681  }
3682  case esSpikeArrow:
3683  {
3684  QPointF points[4] = {pos.toPointF(),
3685  (pos-lengthVec+widthVec).toPointF(),
3686  (pos-lengthVec*0.8f).toPointF(),
3687  (pos-lengthVec-widthVec).toPointF()
3688  };
3689  painter->setPen(miterPen);
3690  painter->setBrush(brush);
3691  painter->drawConvexPolygon(points, 4);
3692  painter->setBrush(brushBackup);
3693  painter->setPen(penBackup);
3694  break;
3695  }
3696  case esLineArrow:
3697  {
3698  QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
3699  pos.toPointF(),
3700  (pos-lengthVec-widthVec).toPointF()
3701  };
3702  painter->setPen(miterPen);
3703  painter->drawPolyline(points, 3);
3704  painter->setPen(penBackup);
3705  break;
3706  }
3707  case esDisc:
3708  {
3709  painter->setBrush(brush);
3710  painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
3711  painter->setBrush(brushBackup);
3712  break;
3713  }
3714  case esSquare:
3715  {
3716  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3717  QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
3718  (pos-widthVecPerp-widthVec).toPointF(),
3719  (pos+widthVecPerp-widthVec).toPointF(),
3720  (pos+widthVecPerp+widthVec).toPointF()
3721  };
3722  painter->setPen(miterPen);
3723  painter->setBrush(brush);
3724  painter->drawConvexPolygon(points, 4);
3725  painter->setBrush(brushBackup);
3726  painter->setPen(penBackup);
3727  break;
3728  }
3729  case esDiamond:
3730  {
3731  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3732  QPointF points[4] = {(pos-widthVecPerp).toPointF(),
3733  (pos-widthVec).toPointF(),
3734  (pos+widthVecPerp).toPointF(),
3735  (pos+widthVec).toPointF()
3736  };
3737  painter->setPen(miterPen);
3738  painter->setBrush(brush);
3739  painter->drawConvexPolygon(points, 4);
3740  painter->setBrush(brushBackup);
3741  painter->setPen(penBackup);
3742  break;
3743  }
3744  case esBar:
3745  {
3746  painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
3747  break;
3748  }
3749  case esHalfBar:
3750  {
3751  painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
3752  break;
3753  }
3754  case esSkewedBar:
3755  {
3756  if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3757  {
3758  // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3759  painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3760  (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3761  } else
3762  {
3763  // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3764  painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3765  (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
3766  }
3767  break;
3768  }
3769  }
3770 }
3771 
3777 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
3778 {
3779  draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3780 }
3781 
3782 
3786 
3806  QCPLayerable(parentAxis->parentPlot(), "", parentAxis),
3807  mParentAxis(parentAxis)
3808 {
3809  // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
3810  setParent(parentAxis);
3811  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
3812  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
3813  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
3814  setSubGridVisible(false);
3815  setAntialiased(false);
3816  setAntialiasedSubGrid(false);
3817  setAntialiasedZeroLine(false);
3818 }
3819 
3825 void QCPGrid::setSubGridVisible(bool visible)
3826 {
3828 }
3829 
3834 {
3835  mAntialiasedSubGrid = enabled;
3836 }
3837 
3842 {
3843  mAntialiasedZeroLine = enabled;
3844 }
3845 
3849 void QCPGrid::setPen(const QPen &pen)
3850 {
3851  mPen = pen;
3852 }
3853 
3857 void QCPGrid::setSubGridPen(const QPen &pen)
3858 {
3859  mSubGridPen = pen;
3860 }
3861 
3868 void QCPGrid::setZeroLinePen(const QPen &pen)
3869 {
3870  mZeroLinePen = pen;
3871 }
3872 
3887 {
3889 }
3890 
3897 {
3898  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3899 
3900  if (mSubGridVisible)
3901  drawSubGridLines(painter);
3902  drawGridLines(painter);
3903 }
3904 
3912 {
3913  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3914 
3915  int lowTick = mParentAxis->mLowestVisibleTick;
3916  int highTick = mParentAxis->mHighestVisibleTick;
3917  double t; // helper variable, result of coordinate-to-pixel transforms
3918  if (mParentAxis->orientation() == Qt::Horizontal)
3919  {
3920  // draw zeroline:
3921  int zeroLineIndex = -1;
3922  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3923  {
3925  painter->setPen(mZeroLinePen);
3926  double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
3927  for (int i=lowTick; i <= highTick; ++i)
3928  {
3929  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3930  {
3931  zeroLineIndex = i;
3932  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3933  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3934  break;
3935  }
3936  }
3937  }
3938  // draw grid lines:
3940  painter->setPen(mPen);
3941  for (int i=lowTick; i <= highTick; ++i)
3942  {
3943  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3944  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3945  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3946  }
3947  } else
3948  {
3949  // draw zeroline:
3950  int zeroLineIndex = -1;
3951  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3952  {
3954  painter->setPen(mZeroLinePen);
3955  double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
3956  for (int i=lowTick; i <= highTick; ++i)
3957  {
3958  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3959  {
3960  zeroLineIndex = i;
3961  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3962  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3963  break;
3964  }
3965  }
3966  }
3967  // draw grid lines:
3969  painter->setPen(mPen);
3970  for (int i=lowTick; i <= highTick; ++i)
3971  {
3972  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3973  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3974  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3975  }
3976  }
3977 }
3978 
3986 {
3987  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3988 
3990  double t; // helper variable, result of coordinate-to-pixel transforms
3991  painter->setPen(mSubGridPen);
3992  if (mParentAxis->orientation() == Qt::Horizontal)
3993  {
3994  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3995  {
3997  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3998  }
3999  } else
4000  {
4001  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
4002  {
4004  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
4005  }
4006  }
4007 }
4008 
4009 
4013 
4032 /* start of documentation of inline functions */
4033 
4055 /* end of documentation of inline functions */
4056 /* start of documentation of signals */
4057 
4103 /* end of documentation of signals */
4104 
4110  QCPLayerable(parent->parentPlot(), "", parent),
4111  // axis base:
4112  mAxisType(type),
4113  mAxisRect(parent),
4114  mPadding(5),
4115  mOrientation(orientation(type)),
4116  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
4117  mSelectedParts(spNone),
4118  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4119  mSelectedBasePen(QPen(Qt::blue, 2)),
4120  // axis label:
4121  mLabel(""),
4122  mLabelFont(mParentPlot->font()),
4123  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4124  mLabelColor(Qt::black),
4125  mSelectedLabelColor(Qt::blue),
4126  // tick labels:
4127  mTickLabels(true),
4128  mAutoTickLabels(true),
4129  mTickLabelType(ltNumber),
4130  mTickLabelFont(mParentPlot->font()),
4131  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4132  mTickLabelColor(Qt::black),
4133  mSelectedTickLabelColor(Qt::blue),
4134  mDateTimeFormat("hh:mm:ss\ndd.MM.yy"),
4135  mDateTimeSpec(Qt::LocalTime),
4136  mNumberPrecision(6),
4137  mNumberFormatChar('g'),
4138  mNumberBeautifulPowers(true),
4139  // ticks and subticks:
4140  mTicks(true),
4141  mTickStep(1),
4142  mSubTickCount(4),
4143  mAutoTickCount(6),
4144  mAutoTicks(true),
4145  mAutoTickStep(true),
4146  mAutoSubTicks(true),
4147  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4148  mSelectedTickPen(QPen(Qt::blue, 2)),
4149  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4150  mSelectedSubTickPen(QPen(Qt::blue, 2)),
4151  // scale and range:
4152  mRange(0, 5),
4153  mRangeReversed(false),
4154  mScaleType(stLinear),
4155  mScaleLogBase(10),
4156  mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4157  // internal members:
4158  mGrid(new QCPGrid(this)),
4159  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4160  mLowestVisibleTick(0),
4161  mHighestVisibleTick(-1),
4162  mCachedMarginValid(false),
4163  mCachedMargin(0)
4164 {
4165  mGrid->setVisible(false);
4166  setAntialiased(false);
4167  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
4168 
4169  if (type == atTop)
4170  {
4172  setLabelPadding(6);
4173  } else if (type == atRight)
4174  {
4176  setLabelPadding(12);
4177  } else if (type == atBottom)
4178  {
4180  setLabelPadding(3);
4181  } else if (type == atLeft)
4182  {
4184  setLabelPadding(10);
4185  }
4186 }
4187 
4189 {
4190  delete mAxisPainter;
4191 }
4192 
4193 /* No documentation as it is a property getter */
4195 {
4197 }
4198 
4199 /* No documentation as it is a property getter */
4201 {
4203 }
4204 
4205 /* No documentation as it is a property getter */
4206 QString QCPAxis::numberFormat() const
4207 {
4208  QString result;
4209  result.append(mNumberFormatChar);
4211  {
4212  result.append("b");
4214  result.append("c");
4215  }
4216  return result;
4217 }
4218 
4219 /* No documentation as it is a property getter */
4221 {
4222  return mAxisPainter->tickLengthIn;
4223 }
4224 
4225 /* No documentation as it is a property getter */
4227 {
4228  return mAxisPainter->tickLengthOut;
4229 }
4230 
4231 /* No documentation as it is a property getter */
4233 {
4234  return mAxisPainter->subTickLengthIn;
4235 }
4236 
4237 /* No documentation as it is a property getter */
4239 {
4241 }
4242 
4243 /* No documentation as it is a property getter */
4245 {
4246  return mAxisPainter->labelPadding;
4247 }
4248 
4249 /* No documentation as it is a property getter */
4250 int QCPAxis::offset() const
4251 {
4252  return mAxisPainter->offset;
4253 }
4254 
4255 /* No documentation as it is a property getter */
4257 {
4258  return mAxisPainter->lowerEnding;
4259 }
4260 
4261 /* No documentation as it is a property getter */
4263 {
4264  return mAxisPainter->upperEnding;
4265 }
4266 
4281 {
4282  if (mScaleType != type)
4283  {
4284  mScaleType = type;
4285  if (mScaleType == stLogarithmic)
4287  mCachedMarginValid = false;
4289  }
4290 }
4291 
4299 void QCPAxis::setScaleLogBase(double base)
4300 {
4301  if (base > 1)
4302  {
4303  mScaleLogBase = base;
4304  mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4305  mCachedMarginValid = false;
4306  } else
4307  qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
4308 }
4309 
4318 void QCPAxis::setRange(const QCPRange &range)
4319 {
4320  if (range.lower == mRange.lower && range.upper == mRange.upper)
4321  return;
4322 
4323  if (!QCPRange::validRange(range)) return;
4324  QCPRange oldRange = mRange;
4325  if (mScaleType == stLogarithmic)
4326  {
4328  } else
4329  {
4331  }
4332  mCachedMarginValid = false;
4333  emit rangeChanged(mRange);
4334  emit rangeChanged(mRange, oldRange);
4335 }
4336 
4347 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4348 {
4349  if (mSelectableParts != selectable)
4350  {
4351  mSelectableParts = selectable;
4353  }
4354 }
4355 
4371 void QCPAxis::setSelectedParts(const SelectableParts &selected)
4372 {
4373  if (mSelectedParts != selected)
4374  {
4375  mSelectedParts = selected;
4377  }
4378 }
4379 
4389 void QCPAxis::setRange(double lower, double upper)
4390 {
4391  if (lower == mRange.lower && upper == mRange.upper)
4392  return;
4393 
4394  if (!QCPRange::validRange(lower, upper)) return;
4395  QCPRange oldRange = mRange;
4396  mRange.lower = lower;
4397  mRange.upper = upper;
4398  if (mScaleType == stLogarithmic)
4399  {
4401  } else
4402  {
4404  }
4405  mCachedMarginValid = false;
4406  emit rangeChanged(mRange);
4407  emit rangeChanged(mRange, oldRange);
4408 }
4409 
4421 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
4422 {
4423  if (alignment == Qt::AlignLeft)
4424  setRange(position, position+size);
4425  else if (alignment == Qt::AlignRight)
4426  setRange(position-size, position);
4427  else // alignment == Qt::AlignCenter
4428  setRange(position-size/2.0, position+size/2.0);
4429 }
4430 
4436 {
4437  if (mRange.lower == lower)
4438  return;
4439 
4440  QCPRange oldRange = mRange;
4441  mRange.lower = lower;
4442  if (mScaleType == stLogarithmic)
4443  {
4445  } else
4446  {
4448  }
4449  mCachedMarginValid = false;
4450  emit rangeChanged(mRange);
4451  emit rangeChanged(mRange, oldRange);
4452 }
4453 
4458 void QCPAxis::setRangeUpper(double upper)
4459 {
4460  if (mRange.upper == upper)
4461  return;
4462 
4463  QCPRange oldRange = mRange;
4464  mRange.upper = upper;
4465  if (mScaleType == stLogarithmic)
4466  {
4468  } else
4469  {
4471  }
4472  mCachedMarginValid = false;
4473  emit rangeChanged(mRange);
4474  emit rangeChanged(mRange, oldRange);
4475 }
4476 
4486 void QCPAxis::setRangeReversed(bool reversed)
4487 {
4488  if (mRangeReversed != reversed)
4489  {
4490  mRangeReversed = reversed;
4491  mCachedMarginValid = false;
4492  }
4493 }
4494 
4511 {
4512  if (mAutoTicks != on)
4513  {
4514  mAutoTicks = on;
4515  mCachedMarginValid = false;
4516  }
4517 }
4518 
4530 void QCPAxis::setAutoTickCount(int approximateCount)
4531 {
4532  if (mAutoTickCount != approximateCount)
4533  {
4534  if (approximateCount > 0)
4535  {
4536  mAutoTickCount = approximateCount;
4537  mCachedMarginValid = false;
4538  } else
4539  qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
4540  }
4541 }
4542 
4559 {
4560  if (mAutoTickLabels != on)
4561  {
4562  mAutoTickLabels = on;
4563  mCachedMarginValid = false;
4564  }
4565 }
4566 
4580 {
4581  if (mAutoTickStep != on)
4582  {
4583  mAutoTickStep = on;
4584  mCachedMarginValid = false;
4585  }
4586 }
4587 
4598 {
4599  if (mAutoSubTicks != on)
4600  {
4601  mAutoSubTicks = on;
4602  mCachedMarginValid = false;
4603  }
4604 }
4605 
4612 void QCPAxis::setTicks(bool show)
4613 {
4614  if (mTicks != show)
4615  {
4616  mTicks = show;
4617  mCachedMarginValid = false;
4618  }
4619 }
4620 
4624 void QCPAxis::setTickLabels(bool show)
4625 {
4626  if (mTickLabels != show)
4627  {
4628  mTickLabels = show;
4629  mCachedMarginValid = false;
4630  }
4631 }
4632 
4638 {
4640  {
4642  mCachedMarginValid = false;
4643  }
4644 }
4645 
4668 {
4669  if (mTickLabelType != type)
4670  {
4671  mTickLabelType = type;
4672  mCachedMarginValid = false;
4673  }
4674 }
4675 
4681 void QCPAxis::setTickLabelFont(const QFont &font)
4682 {
4683  if (font != mTickLabelFont)
4684  {
4685  mTickLabelFont = font;
4686  mCachedMarginValid = false;
4687  }
4688 }
4689 
4695 void QCPAxis::setTickLabelColor(const QColor &color)
4696 {
4697  if (color != mTickLabelColor)
4698  {
4699  mTickLabelColor = color;
4700  mCachedMarginValid = false;
4701  }
4702 }
4703 
4713 void QCPAxis::setTickLabelRotation(double degrees)
4714 {
4715  if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4716  {
4717  mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4718  mCachedMarginValid = false;
4719  }
4720 }
4721 
4730 void QCPAxis::setDateTimeFormat(const QString &format)
4731 {
4732  if (mDateTimeFormat != format)
4733  {
4734  mDateTimeFormat = format;
4735  mCachedMarginValid = false;
4736  }
4737 }
4738 
4749 void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
4750 {
4751  mDateTimeSpec = timeSpec;
4752 }
4753 
4790 void QCPAxis::setNumberFormat(const QString &formatCode)
4791 {
4792  if (formatCode.isEmpty())
4793  {
4794  qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4795  return;
4796  }
4797  mCachedMarginValid = false;
4798 
4799  // interpret first char as number format char:
4800  QString allowedFormatChars = "eEfgG";
4801  if (allowedFormatChars.contains(formatCode.at(0)))
4802  {
4803  mNumberFormatChar = formatCode.at(0).toLatin1();
4804  } else
4805  {
4806  qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4807  return;
4808  }
4809  if (formatCode.length() < 2)
4810  {
4811  mNumberBeautifulPowers = false;
4813  return;
4814  }
4815 
4816  // interpret second char as indicator for beautiful decimal powers:
4817  if (formatCode.at(1) == 'b' && (mNumberFormatChar == 'e' || mNumberFormatChar == 'g'))
4818  {
4819  mNumberBeautifulPowers = true;
4820  } else
4821  {
4822  qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4823  return;
4824  }
4825  if (formatCode.length() < 3)
4826  {
4828  return;
4829  }
4830 
4831  // interpret third char as indicator for dot or cross multiplication symbol:
4832  if (formatCode.at(2) == 'c')
4833  {
4835  } else if (formatCode.at(2) == 'd')
4836  {
4838  } else
4839  {
4840  qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4841  return;
4842  }
4843 }
4844 
4856 void QCPAxis::setNumberPrecision(int precision)
4857 {
4858  if (mNumberPrecision != precision)
4859  {
4860  mNumberPrecision = precision;
4861  mCachedMarginValid = false;
4862  }
4863 }
4864 
4870 void QCPAxis::setTickStep(double step)
4871 {
4872  if (mTickStep != step)
4873  {
4874  mTickStep = step;
4875  mCachedMarginValid = false;
4876  }
4877 }
4878 
4892 void QCPAxis::setTickVector(const QVector<double> &vec)
4893 {
4894  // don't check whether mTickVector != vec here, because it takes longer than we would save
4895  mTickVector = vec;
4896  mCachedMarginValid = false;
4897 }
4898 
4910 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4911 {
4912  // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
4913  mTickVectorLabels = vec;
4914  mCachedMarginValid = false;
4915 }
4916 
4925 void QCPAxis::setTickLength(int inside, int outside)
4926 {
4927  setTickLengthIn(inside);
4928  setTickLengthOut(outside);
4929 }
4930 
4938 {
4939  if (mAxisPainter->tickLengthIn != inside)
4940  {
4941  mAxisPainter->tickLengthIn = inside;
4942  }
4943 }
4944 
4952 void QCPAxis::setTickLengthOut(int outside)
4953 {
4954  if (mAxisPainter->tickLengthOut != outside)
4955  {
4956  mAxisPainter->tickLengthOut = outside;
4957  mCachedMarginValid = false; // only outside tick length can change margin
4958  }
4959 }
4960 
4973 {
4974  mSubTickCount = count;
4975 }
4976 
4985 void QCPAxis::setSubTickLength(int inside, int outside)
4986 {
4987  setSubTickLengthIn(inside);
4988  setSubTickLengthOut(outside);
4989 }
4990 
4998 {
4999  if (mAxisPainter->subTickLengthIn != inside)
5000  {
5001  mAxisPainter->subTickLengthIn = inside;
5002  }
5003 }
5004 
5013 {
5014  if (mAxisPainter->subTickLengthOut != outside)
5015  {
5016  mAxisPainter->subTickLengthOut = outside;
5017  mCachedMarginValid = false; // only outside tick length can change margin
5018  }
5019 }
5020 
5026 void QCPAxis::setBasePen(const QPen &pen)
5027 {
5028  mBasePen = pen;
5029 }
5030 
5036 void QCPAxis::setTickPen(const QPen &pen)
5037 {
5038  mTickPen = pen;
5039 }
5040 
5046 void QCPAxis::setSubTickPen(const QPen &pen)
5047 {
5048  mSubTickPen = pen;
5049 }
5050 
5056 void QCPAxis::setLabelFont(const QFont &font)
5057 {
5058  if (mLabelFont != font)
5059  {
5060  mLabelFont = font;
5061  mCachedMarginValid = false;
5062  }
5063 }
5064 
5070 void QCPAxis::setLabelColor(const QColor &color)
5071 {
5072  mLabelColor = color;
5073 }
5074 
5079 void QCPAxis::setLabel(const QString &str)
5080 {
5081  if (mLabel != str)
5082  {
5083  mLabel = str;
5084  mCachedMarginValid = false;
5085  }
5086 }
5087 
5093 void QCPAxis::setLabelPadding(int padding)
5094 {
5096  {
5098  mCachedMarginValid = false;
5099  }
5100 }
5101 
5112 void QCPAxis::setPadding(int padding)
5113 {
5114  if (mPadding != padding)
5115  {
5116  mPadding = padding;
5117  mCachedMarginValid = false;
5118  }
5119 }
5120 
5130 {
5132 }
5133 
5139 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
5140 {
5141  if (font != mSelectedTickLabelFont)
5142  {
5143  mSelectedTickLabelFont = font;
5144  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5145  }
5146 }
5147 
5153 void QCPAxis::setSelectedLabelFont(const QFont &font)
5154 {
5155  mSelectedLabelFont = font;
5156  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5157 }
5158 
5164 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
5165 {
5166  if (color != mSelectedTickLabelColor)
5167  {
5168  mSelectedTickLabelColor = color;
5169  }
5170 }
5171 
5177 void QCPAxis::setSelectedLabelColor(const QColor &color)
5178 {
5179  mSelectedLabelColor = color;
5180 }
5181 
5187 void QCPAxis::setSelectedBasePen(const QPen &pen)
5188 {
5189  mSelectedBasePen = pen;
5190 }
5191 
5197 void QCPAxis::setSelectedTickPen(const QPen &pen)
5198 {
5199  mSelectedTickPen = pen;
5200 }
5201 
5207 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
5208 {
5209  mSelectedSubTickPen = pen;
5210 }
5211 
5223 {
5224  mAxisPainter->lowerEnding = ending;
5225 }
5226 
5238 {
5239  mAxisPainter->upperEnding = ending;
5240 }
5241 
5249 void QCPAxis::moveRange(double diff)
5250 {
5251  QCPRange oldRange = mRange;
5252  if (mScaleType == stLinear)
5253  {
5254  mRange.lower += diff;
5255  mRange.upper += diff;
5256  } else // mScaleType == stLogarithmic
5257  {
5258  mRange.lower *= diff;
5259  mRange.upper *= diff;
5260  }
5261  mCachedMarginValid = false;
5262  emit rangeChanged(mRange);
5263  emit rangeChanged(mRange, oldRange);
5264 }
5265 
5272 void QCPAxis::scaleRange(double factor, double center)
5273 {
5274  QCPRange oldRange = mRange;
5275  if (mScaleType == stLinear)
5276  {
5277  QCPRange newRange;
5278  newRange.lower = (mRange.lower-center)*factor + center;
5279  newRange.upper = (mRange.upper-center)*factor + center;
5280  if (QCPRange::validRange(newRange))
5281  mRange = newRange.sanitizedForLinScale();
5282  } else // mScaleType == stLogarithmic
5283  {
5284  if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
5285  {
5286  QCPRange newRange;
5287  newRange.lower = pow(mRange.lower/center, factor)*center;
5288  newRange.upper = pow(mRange.upper/center, factor)*center;
5289  if (QCPRange::validRange(newRange))
5290  mRange = newRange.sanitizedForLogScale();
5291  } else
5292  qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
5293  }
5294  mCachedMarginValid = false;
5295  emit rangeChanged(mRange);
5296  emit rangeChanged(mRange, oldRange);
5297 }
5298 
5312 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
5313 {
5314  int otherPixelSize, ownPixelSize;
5315 
5316  if (otherAxis->orientation() == Qt::Horizontal)
5317  otherPixelSize = otherAxis->axisRect()->width();
5318  else
5319  otherPixelSize = otherAxis->axisRect()->height();
5320 
5321  if (orientation() == Qt::Horizontal)
5322  ownPixelSize = axisRect()->width();
5323  else
5324  ownPixelSize = axisRect()->height();
5325 
5326  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
5327  setRange(range().center(), newRangeSize, Qt::AlignCenter);
5328 }
5329 
5336 void QCPAxis::rescale(bool onlyVisiblePlottables)
5337 {
5338  QList<QCPAbstractPlottable*> p = plottables();
5339  QCPRange newRange;
5340  bool haveRange = false;
5341  for (int i=0; i<p.size(); ++i)
5342  {
5343  if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5344  continue;
5345  QCPRange plottableRange;
5346  bool currentFoundRange;
5348  if (mScaleType == stLogarithmic)
5350  if (p.at(i)->keyAxis() == this)
5351  plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5352  else
5353  plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5354  if (currentFoundRange)
5355  {
5356  if (!haveRange)
5357  newRange = plottableRange;
5358  else
5359  newRange.expand(plottableRange);
5360  haveRange = true;
5361  }
5362  }
5363  if (haveRange)
5364  {
5365  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
5366  {
5367  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
5368  if (mScaleType == stLinear)
5369  {
5370  newRange.lower = center-mRange.size()/2.0;
5371  newRange.upper = center+mRange.size()/2.0;
5372  } else // mScaleType == stLogarithmic
5373  {
5374  newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
5375  newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
5376  }
5377  }
5378  setRange(newRange);
5379  }
5380 }
5381 
5385 double QCPAxis::pixelToCoord(double value) const
5386 {
5387  if (orientation() == Qt::Horizontal)
5388  {
5389  if (mScaleType == stLinear)
5390  {
5391  if (!mRangeReversed)
5392  return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
5393  else
5394  return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
5395  } else // mScaleType == stLogarithmic
5396  {
5397  if (!mRangeReversed)
5398  return pow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
5399  else
5400  return pow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
5401  }
5402  } else // orientation() == Qt::Vertical
5403  {
5404  if (mScaleType == stLinear)
5405  {
5406  if (!mRangeReversed)
5407  return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
5408  else
5409  return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
5410  } else // mScaleType == stLogarithmic
5411  {
5412  if (!mRangeReversed)
5413  return pow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
5414  else
5415  return pow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
5416  }
5417  }
5418 }
5419 
5423 double QCPAxis::coordToPixel(double value) const
5424 {
5425  if (orientation() == Qt::Horizontal)
5426  {
5427  if (mScaleType == stLinear)
5428  {
5429  if (!mRangeReversed)
5430  return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5431  else
5432  return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5433  } else // mScaleType == stLogarithmic
5434  {
5435  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5436  return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
5437  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5438  return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
5439  else
5440  {
5441  if (!mRangeReversed)
5443  else
5445  }
5446  }
5447  } else // orientation() == Qt::Vertical
5448  {
5449  if (mScaleType == stLinear)
5450  {
5451  if (!mRangeReversed)
5452  return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
5453  else
5454  return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5455  } else // mScaleType == stLogarithmic
5456  {
5457  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5458  return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5459  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5460  return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
5461  else
5462  {
5463  if (!mRangeReversed)
5465  else
5467  }
5468  }
5469  }
5470 }
5471 
5482 {
5483  if (!mVisible)
5484  return spNone;
5485 
5486  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5487  return spAxis;
5488  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5489  return spTickLabels;
5490  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5491  return spAxisLabel;
5492  else
5493  return spNone;
5494 }
5495 
5496 /* inherits documentation from base class */
5497 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5498 {
5499  if (!mParentPlot) return -1;
5500  SelectablePart part = getPartAt(pos);
5501  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5502  return -1;
5503 
5504  if (details)
5505  details->setValue(part);
5506  return mParentPlot->selectionTolerance()*0.99;
5507 }
5508 
5516 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
5517 {
5518  QList<QCPAbstractPlottable*> result;
5519  if (!mParentPlot) return result;
5520 
5521  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
5522  {
5523  if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
5524  result.append(mParentPlot->mPlottables.at(i));
5525  }
5526  return result;
5527 }
5528 
5534 QList<QCPGraph*> QCPAxis::graphs() const
5535 {
5536  QList<QCPGraph*> result;
5537  if (!mParentPlot) return result;
5538 
5539  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
5540  {
5541  if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
5542  result.append(mParentPlot->mGraphs.at(i));
5543  }
5544  return result;
5545 }
5546 
5553 QList<QCPAbstractItem*> QCPAxis::items() const
5554 {
5555  QList<QCPAbstractItem*> result;
5556  if (!mParentPlot) return result;
5557 
5558  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
5559  {
5560  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
5561  for (int posId=0; posId<positions.size(); ++posId)
5562  {
5563  if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
5564  {
5565  result.append(mParentPlot->mItems.at(itemId));
5566  break;
5567  }
5568  }
5569  }
5570  return result;
5571 }
5572 
5578 {
5579  switch (side)
5580  {
5581  case QCP::msLeft: return atLeft;
5582  case QCP::msRight: return atRight;
5583  case QCP::msTop: return atTop;
5584  case QCP::msBottom: return atBottom;
5585  default: break;
5586  }
5587  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5588  return atLeft;
5589 }
5590 
5595 {
5596  switch (type)
5597  {
5598  case atLeft: return atRight; break;
5599  case atRight: return atLeft; break;
5600  case atBottom: return atTop; break;
5601  case atTop: return atBottom; break;
5602  default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
5603  }
5604 }
5605 
5614 {
5615  if (!mParentPlot) return;
5616  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
5617 
5618  // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
5619  if (mAutoTicks)
5620  {
5622  } else
5623  {
5624  emit ticksRequest();
5625  }
5626 
5628  if (mTickVector.isEmpty())
5629  {
5630  mSubTickVector.clear();
5631  return;
5632  }
5633 
5634  // generate subticks between ticks:
5635  mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
5636  if (mSubTickCount > 0)
5637  {
5638  double subTickStep = 0;
5639  double subTickPosition = 0;
5640  int subTickIndex = 0;
5641  bool done = false;
5644  for (int i=lowTick+1; i<=highTick; ++i)
5645  {
5646  subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
5647  for (int k=1; k<=mSubTickCount; ++k)
5648  {
5649  subTickPosition = mTickVector.at(i-1) + k*subTickStep;
5650  if (subTickPosition < mRange.lower)
5651  continue;
5652  if (subTickPosition > mRange.upper)
5653  {
5654  done = true;
5655  break;
5656  }
5657  mSubTickVector[subTickIndex] = subTickPosition;
5658  subTickIndex++;
5659  }
5660  if (done) break;
5661  }
5662  mSubTickVector.resize(subTickIndex);
5663  }
5664 
5665  // generate tick labels according to tick positions:
5666  if (mAutoTickLabels)
5667  {
5668  int vecsize = mTickVector.size();
5669  mTickVectorLabels.resize(vecsize);
5670  if (mTickLabelType == ltNumber)
5671  {
5672  for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5674  } else if (mTickLabelType == ltDateTime)
5675  {
5676  for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5677  {
5678 #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")
5679  mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5680 #else
5681  mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5682 #endif
5683  }
5684  }
5685  } else // mAutoTickLabels == false
5686  {
5687  if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
5688  {
5689  emit ticksRequest();
5690  }
5691  // make sure provided tick label vector has correct (minimal) length:
5692  if (mTickVectorLabels.size() < mTickVector.size())
5693  mTickVectorLabels.resize(mTickVector.size());
5694  }
5695 }
5696 
5707 {
5708  if (mScaleType == stLinear)
5709  {
5710  if (mAutoTickStep)
5711  {
5712  // Generate tick positions according to linear scaling:
5713  mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
5714  double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5715  double tickStepMantissa = mTickStep/magnitudeFactor;
5716  if (tickStepMantissa < 5)
5717  {
5718  // round digit after decimal point to 0.5
5719  mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
5720  } else
5721  {
5722  // round to first digit in multiples of 2
5723  mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
5724  }
5725  }
5726  if (mAutoSubTicks)
5728  // Generate tick positions according to mTickStep:
5729  qint64 firstStep = floor(mRange.lower/mTickStep);
5730  qint64 lastStep = ceil(mRange.upper/mTickStep);
5731  int tickcount = lastStep-firstStep+1;
5732  if (tickcount < 0) tickcount = 0;
5733  mTickVector.resize(tickcount);
5734  for (int i=0; i<tickcount; ++i)
5735  mTickVector[i] = (firstStep+i)*mTickStep;
5736  } else // mScaleType == stLogarithmic
5737  {
5738  // Generate tick positions according to logbase scaling:
5739  if (mRange.lower > 0 && mRange.upper > 0) // positive range
5740  {
5741  double lowerMag = basePow((int)floor(baseLog(mRange.lower)));
5742  double currentMag = lowerMag;
5743  mTickVector.clear();
5744  mTickVector.append(currentMag);
5745  while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5746  {
5747  currentMag *= mScaleLogBase;
5748  mTickVector.append(currentMag);
5749  }
5750  } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5751  {
5752  double lowerMag = -basePow((int)ceil(baseLog(-mRange.lower)));
5753  double currentMag = lowerMag;
5754  mTickVector.clear();
5755  mTickVector.append(currentMag);
5756  while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5757  {
5758  currentMag /= mScaleLogBase;
5759  mTickVector.append(currentMag);
5760  }
5761  } else // invalid range for logarithmic scale, because lower and upper have different sign
5762  {
5763  mTickVector.clear();
5764  qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
5765  }
5766  }
5767 }
5768 
5782 int QCPAxis::calculateAutoSubTickCount(double tickStep) const
5783 {
5784  int result = mSubTickCount; // default to current setting, if no proper value can be found
5785 
5786  // get mantissa of tickstep:
5787  double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5788  double tickStepMantissa = tickStep/magnitudeFactor;
5789 
5790  // separate integer and fractional part of mantissa:
5791  double epsilon = 0.01;
5792  double intPartf;
5793  int intPart;
5794  double fracPart = modf(tickStepMantissa, &intPartf);
5795  intPart = intPartf;
5796 
5797  // handle cases with (almost) integer mantissa:
5798  if (fracPart < epsilon || 1.0-fracPart < epsilon)
5799  {
5800  if (1.0-fracPart < epsilon)
5801  ++intPart;
5802  switch (intPart)
5803  {
5804  case 1: result = 4; break; // 1.0 -> 0.2 substep
5805  case 2: result = 3; break; // 2.0 -> 0.5 substep
5806  case 3: result = 2; break; // 3.0 -> 1.0 substep
5807  case 4: result = 3; break; // 4.0 -> 1.0 substep
5808  case 5: result = 4; break; // 5.0 -> 1.0 substep
5809  case 6: result = 2; break; // 6.0 -> 2.0 substep
5810  case 7: result = 6; break; // 7.0 -> 1.0 substep
5811  case 8: result = 3; break; // 8.0 -> 2.0 substep
5812  case 9: result = 2; break; // 9.0 -> 3.0 substep
5813  }
5814  } else
5815  {
5816  // handle cases with significantly fractional mantissa:
5817  if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5818  {
5819  switch (intPart)
5820  {
5821  case 1: result = 2; break; // 1.5 -> 0.5 substep
5822  case 2: result = 4; break; // 2.5 -> 0.5 substep
5823  case 3: result = 4; break; // 3.5 -> 0.7 substep
5824  case 4: result = 2; break; // 4.5 -> 1.5 substep
5825  case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
5826  case 6: result = 4; break; // 6.5 -> 1.3 substep
5827  case 7: result = 2; break; // 7.5 -> 2.5 substep
5828  case 8: result = 4; break; // 8.5 -> 1.7 substep
5829  case 9: result = 4; break; // 9.5 -> 1.9 substep
5830  }
5831  }
5832  // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5833  }
5834 
5835  return result;
5836 }
5837 
5838 /* inherits documentation from base class */
5839 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
5840 {
5841  Q_UNUSED(event)
5842  SelectablePart part = details.value<SelectablePart>();
5843  if (mSelectableParts.testFlag(part))
5844  {
5845  SelectableParts selBefore = mSelectedParts;
5846  setSelectedParts(additive ? mSelectedParts^part : part);
5847  if (selectionStateChanged)
5848  *selectionStateChanged = mSelectedParts != selBefore;
5849  }
5850 }
5851 
5852 /* inherits documentation from base class */
5853 void QCPAxis::deselectEvent(bool *selectionStateChanged)
5854 {
5855  SelectableParts selBefore = mSelectedParts;
5857  if (selectionStateChanged)
5858  *selectionStateChanged = mSelectedParts != selBefore;
5859 }
5860 
5875 {
5877 }
5878 
5885 {
5886  const int lowTick = mLowestVisibleTick;
5887  const int highTick = mHighestVisibleTick;
5888  QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5889  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5890  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5891  tickPositions.reserve(highTick-lowTick+1);
5892  tickLabels.reserve(highTick-lowTick+1);
5893  subTickPositions.reserve(mSubTickVector.size());
5894 
5895  if (mTicks)
5896  {
5897  for (int i=lowTick; i<=highTick; ++i)
5898  {
5899  tickPositions.append(coordToPixel(mTickVector.at(i)));
5900  if (mTickLabels)
5901  tickLabels.append(mTickVectorLabels.at(i));
5902  }
5903 
5904  if (mSubTickCount > 0)
5905  {
5906  const int subTickCount = mSubTickVector.size();
5907  for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
5908  subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5909  }
5910  }
5911  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
5912  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
5927  mAxisPainter->tickPositions = tickPositions;
5929  mAxisPainter->subTickPositions = subTickPositions;
5930  mAxisPainter->draw(painter);
5931 }
5932 
5949 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5950 {
5951  bool lowFound = false;
5952  bool highFound = false;
5953  lowIndex = 0;
5954  highIndex = -1;
5955 
5956  for (int i=0; i < mTickVector.size(); ++i)
5957  {
5958  if (mTickVector.at(i) >= mRange.lower)
5959  {
5960  lowFound = true;
5961  lowIndex = i;
5962  break;
5963  }
5964  }
5965  for (int i=mTickVector.size()-1; i >= 0; --i)
5966  {
5967  if (mTickVector.at(i) <= mRange.upper)
5968  {
5969  highFound = true;
5970  highIndex = i;
5971  break;
5972  }
5973  }
5974 
5975  if (!lowFound && highFound)
5976  lowIndex = highIndex+1;
5977  else if (lowFound && !highFound)
5978  highIndex = lowIndex-1;
5979 }
5980 
5989 double QCPAxis::baseLog(double value) const
5990 {
5991  return qLn(value)*mScaleLogBaseLogInv;
5992 }
5993 
6001 double QCPAxis::basePow(double value) const
6002 {
6003  return qPow(mScaleLogBase, value);
6004 }
6005 
6012 {
6013  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
6014 }
6015 
6022 {
6023  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
6024 }
6025 
6032 {
6034 }
6035 
6042 {
6044 }
6045 
6052 {
6054 }
6055 
6062 {
6064 }
6065 
6072 {
6074 }
6075 
6091 {
6092  if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
6093  return 0;
6094 
6095  if (mCachedMarginValid)
6096  return mCachedMargin;
6097 
6098  // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6099  int margin = 0;
6100 
6101  int lowTick, highTick;
6102  visibleTickBounds(lowTick, highTick);
6103  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
6104  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6105  tickPositions.reserve(highTick-lowTick+1);
6106  tickLabels.reserve(highTick-lowTick+1);
6107  if (mTicks)
6108  {
6109  for (int i=lowTick; i<=highTick; ++i)
6110  {
6111  tickPositions.append(coordToPixel(mTickVector.at(i)));
6112  if (mTickLabels)
6113  tickLabels.append(mTickVectorLabels.at(i));
6114  }
6115  }
6116  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6117  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6124  mAxisPainter->tickPositions = tickPositions;
6126  margin += mAxisPainter->size();
6127  margin += mPadding;
6128 
6129  mCachedMargin = margin;
6130  mCachedMarginValid = true;
6131  return margin;
6132 }
6133 
6134 /* inherits documentation from base class */
6136 {
6137  return QCP::iSelectAxes;
6138 }
6139 
6140 
6144 
6162  type(QCPAxis::atLeft),
6163  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6164  lowerEnding(QCPLineEnding::esNone),
6165  upperEnding(QCPLineEnding::esNone),
6166  labelPadding(0),
6167  tickLabelPadding(0),
6168  tickLabelRotation(0),
6169  substituteExponent(true),
6170  numberMultiplyCross(false),
6171  tickLengthIn(5),
6172  tickLengthOut(0),
6173  subTickLengthIn(2),
6174  subTickLengthOut(0),
6175  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6176  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6177  offset(0),
6178  abbreviateDecimalPowers(false),
6179  reversedEndings(false),
6180  mParentPlot(parentPlot),
6181  mLabelCache(16) // cache at most 16 (tick) labels
6182 {
6183 }
6184 
6186 {
6187 }
6188 
6197 {
6198  QByteArray newHash = generateLabelParameterHash();
6199  if (newHash != mLabelParameterHash)
6200  {
6201  mLabelCache.clear();
6202  mLabelParameterHash = newHash;
6203  }
6204 
6205  QPoint origin;
6206  switch (type)
6207  {
6208  case QCPAxis::atLeft: origin = alignmentRect.bottomLeft() +QPoint(-offset, 0); break;
6209  case QCPAxis::atRight: origin = alignmentRect.bottomRight()+QPoint(+offset, 0); break;
6210  case QCPAxis::atTop: origin = alignmentRect.topLeft() +QPoint(0, -offset); break;
6211  case QCPAxis::atBottom: origin = alignmentRect.bottomLeft() +QPoint(0, +offset); break;
6212  }
6213 
6214  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
6215  switch (type)
6216  {
6217  case QCPAxis::atTop: yCor = -1; break;
6218  case QCPAxis::atRight: xCor = 1; break;
6219  default: break;
6220  }
6221 
6222  int margin = 0;
6223  // draw baseline:
6224  QLineF baseLine;
6225  painter->setPen(basePen);
6226  if (QCPAxis::orientation(type) == Qt::Horizontal)
6227  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(alignmentRect.width()+xCor, yCor));
6228  else
6229  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -alignmentRect.height()+yCor));
6230  if (reversedEndings)
6231  baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
6232  painter->drawLine(baseLine);
6233 
6234  // draw ticks:
6235  if (!tickPositions.isEmpty())
6236  {
6237  painter->setPen(tickPen);
6238  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
6239  if (QCPAxis::orientation(type) == Qt::Horizontal)
6240  {
6241  for (int i=0; i<tickPositions.size(); ++i)
6242  painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
6243  } else
6244  {
6245  for (int i=0; i<tickPositions.size(); ++i)
6246  painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
6247  }
6248  }
6249 
6250  // draw subticks:
6251  if (!subTickPositions.isEmpty())
6252  {
6253  painter->setPen(subTickPen);
6254  // direction of ticks ("inward" is right for left axis and left for right axis)
6255  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6256  if (QCPAxis::orientation(type) == Qt::Horizontal)
6257  {
6258  for (int i=0; i<subTickPositions.size(); ++i)
6259  painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
6260  } else
6261  {
6262  for (int i=0; i<subTickPositions.size(); ++i)
6263  painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
6264  }
6265  }
6266  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6267 
6268  // draw axis base endings:
6269  bool antialiasingBackup = painter->antialiasing();
6270  painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
6271  painter->setBrush(QBrush(basePen.color()));
6272  QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6274  lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6276  upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
6277  painter->setAntialiasing(antialiasingBackup);
6278 
6279  // tick labels:
6280  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
6281  if (!tickLabels.isEmpty())
6282  {
6283  margin += tickLabelPadding;
6284  painter->setFont(tickLabelFont);
6285  painter->setPen(QPen(tickLabelColor));
6286  const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6287  for (int i=0; i<maxLabelIndex; ++i)
6288  placeTickLabel(painter, tickPositions.at(i), margin, tickLabels.at(i), &tickLabelsSize);
6289  if (QCPAxis::orientation(type) == Qt::Horizontal)
6290  margin += tickLabelsSize.height();
6291  else
6292  margin += tickLabelsSize.width();
6293  }
6294 
6295  // axis label:
6296  QRect labelBounds;
6297  if (!label.isEmpty())
6298  {
6299  margin += labelPadding;
6300  painter->setFont(labelFont);
6301  painter->setPen(QPen(labelColor));
6302  labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
6303  if (type == QCPAxis::atLeft)
6304  {
6305  QTransform oldTransform = painter->transform();
6306  painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
6307  painter->rotate(-90);
6308  painter->drawText(0, 0, alignmentRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6309  painter->setTransform(oldTransform);
6310  }
6311  else if (type == QCPAxis::atRight)
6312  {
6313  QTransform oldTransform = painter->transform();
6314  painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-alignmentRect.height());
6315  painter->rotate(90);
6316  painter->drawText(0, 0, alignmentRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6317  painter->setTransform(oldTransform);
6318  }
6319  else if (type == QCPAxis::atTop)
6320  painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), alignmentRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6321  else if (type == QCPAxis::atBottom)
6322  painter->drawText(origin.x(), origin.y()+margin, alignmentRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6323  }
6324 
6325  // set selection boxes:
6326  int selectionTolerance = 0;
6327  if (mParentPlot)
6328  selectionTolerance = mParentPlot->selectionTolerance();
6329  else
6330  qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6331  int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6332  int selAxisInSize = selectionTolerance;
6333  int selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6334  int selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
6335  int selLabelSize = labelBounds.height();
6336  int selLabelOffset = selTickLabelOffset+selTickLabelSize+labelPadding;
6337  if (type == QCPAxis::atLeft)
6338  {
6339  mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, alignmentRect.top(), origin.x()+selAxisInSize, alignmentRect.bottom());
6340  mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, alignmentRect.top(), origin.x()-selTickLabelOffset, alignmentRect.bottom());
6341  mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, alignmentRect.top(), origin.x()-selLabelOffset, alignmentRect.bottom());
6342  } else if (type == QCPAxis::atRight)
6343  {
6344  mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, alignmentRect.top(), origin.x()+selAxisOutSize, alignmentRect.bottom());
6345  mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, alignmentRect.top(), origin.x()+selTickLabelOffset, alignmentRect.bottom());
6346  mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, alignmentRect.top(), origin.x()+selLabelOffset, alignmentRect.bottom());
6347  } else if (type == QCPAxis::atTop)
6348  {
6349  mAxisSelectionBox.setCoords(alignmentRect.left(), origin.y()-selAxisOutSize, alignmentRect.right(), origin.y()+selAxisInSize);
6350  mTickLabelsSelectionBox.setCoords(alignmentRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, alignmentRect.right(), origin.y()-selTickLabelOffset);
6351  mLabelSelectionBox.setCoords(alignmentRect.left(), origin.y()-selLabelOffset-selLabelSize, alignmentRect.right(), origin.y()-selLabelOffset);
6352  } else if (type == QCPAxis::atBottom)
6353  {
6354  mAxisSelectionBox.setCoords(alignmentRect.left(), origin.y()-selAxisInSize, alignmentRect.right(), origin.y()+selAxisOutSize);
6355  mTickLabelsSelectionBox.setCoords(alignmentRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, alignmentRect.right(), origin.y()+selTickLabelOffset);
6356  mLabelSelectionBox.setCoords(alignmentRect.left(), origin.y()+selLabelOffset+selLabelSize, alignmentRect.right(), origin.y()+selLabelOffset);
6357  }
6358  // draw hitboxes for debug purposes:
6359  //painter->setBrush(Qt::NoBrush);
6360  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
6361 }
6362 
6369 {
6370  int result = 0;
6371 
6372  // get length of tick marks pointing outwards:
6373  if (!tickPositions.isEmpty())
6374  result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6375 
6376  // calculate size of tick labels:
6377  QSize tickLabelsSize(0, 0);
6378  if (!tickLabels.isEmpty())
6379  {
6380  for (int i=0; i<tickLabels.size(); ++i)
6381  getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6382  result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6383  result += tickLabelPadding;
6384  }
6385 
6386  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6387  if (!label.isEmpty())
6388  {
6389  QFontMetrics fontMetrics(labelFont);
6390  QRect bounds;
6391  bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6392  result += bounds.height() + labelPadding;
6393  }
6394 
6395  return result;
6396 }
6397 
6405 {
6406  mLabelCache.clear();
6407 }
6408 
6417 {
6418  QByteArray result;
6419  result.append(QByteArray::number(tickLabelRotation));
6420  result.append(QByteArray::number((int)substituteExponent));
6421  result.append(QByteArray::number((int)numberMultiplyCross));
6422  result.append(tickLabelColor.name()+QByteArray::number(tickLabelColor.alpha(), 16));
6423  result.append(tickLabelFont.toString());
6424  return result;
6425 }
6426 
6446 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
6447 {
6448  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
6449  if (text.isEmpty()) return;
6450  QSize finalSize;
6451  QPointF labelAnchor;
6452  switch (type)
6453  {
6454  case QCPAxis::atLeft: labelAnchor = QPointF(alignmentRect.left()-distanceToAxis-offset, position); break;
6455  case QCPAxis::atRight: labelAnchor = QPointF(alignmentRect.right()+distanceToAxis+offset, position); break;
6456  case QCPAxis::atTop: labelAnchor = QPointF(position, alignmentRect.top()-distanceToAxis-offset); break;
6457  case QCPAxis::atBottom: labelAnchor = QPointF(position, alignmentRect.bottom()+distanceToAxis+offset); break;
6458  }
6459  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
6460  {
6461  if (!mLabelCache.contains(text)) // no cached label exists, create it
6462  {
6463  CachedLabel *newCachedLabel = new CachedLabel;
6464  TickLabelData labelData = getTickLabelData(painter->font(), text);
6465  QPointF drawOffset = getTickLabelDrawOffset(labelData);
6466  newCachedLabel->offset = drawOffset+labelData.rotatedTotalBounds.topLeft();
6467  newCachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6468  newCachedLabel->pixmap.fill(Qt::transparent);
6469  QCPPainter cachePainter(&newCachedLabel->pixmap);
6470  cachePainter.setPen(painter->pen());
6471  drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6472  mLabelCache.insert(text, newCachedLabel, 1);
6473  }
6474  // draw cached label:
6475  const CachedLabel *cachedLabel = mLabelCache.object(text);
6476  // if label would be partly clipped by widget border on sides, don't draw it:
6477  if (QCPAxis::orientation(type) == Qt::Horizontal)
6478  {
6479  if (labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() ||
6480  labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left())
6481  return;
6482  } else
6483  {
6484  if (labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() >viewportRect.bottom() ||
6485  labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top())
6486  return;
6487  }
6488  painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
6489  finalSize = cachedLabel->pixmap.size();
6490  } else // label caching disabled, draw text directly on surface:
6491  {
6492  TickLabelData labelData = getTickLabelData(painter->font(), text);
6493  QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6494  // if label would be partly clipped by widget border on sides, don't draw it:
6495  if (QCPAxis::orientation(type) == Qt::Horizontal)
6496  {
6497  if (finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() ||
6498  finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left())
6499  return;
6500  } else
6501  {
6502  if (finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() ||
6503  finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top())
6504  return;
6505  }
6506  drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6507  finalSize = labelData.rotatedTotalBounds.size();
6508  }
6509 
6510  // expand passed tickLabelsSize if current tick label is larger:
6511  if (finalSize.width() > tickLabelsSize->width())
6512  tickLabelsSize->setWidth(finalSize.width());
6513  if (finalSize.height() > tickLabelsSize->height())
6514  tickLabelsSize->setHeight(finalSize.height());
6515 }
6516 
6526 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
6527 {
6528  // backup painter settings that we're about to change:
6529  QTransform oldTransform = painter->transform();
6530  QFont oldFont = painter->font();
6531 
6532  // transform painter to position/rotation:
6533  painter->translate(x, y);
6534  if (!qFuzzyIsNull(tickLabelRotation))
6535  painter->rotate(tickLabelRotation);
6536 
6537  // draw text:
6538  if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6539  {
6540  painter->setFont(labelData.baseFont);
6541  painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6542  painter->setFont(labelData.expFont);
6543  painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
6544  } else
6545  {
6546  painter->setFont(labelData.baseFont);
6547  painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6548  }
6549 
6550  // reset painter settings to what it was before:
6551  painter->setTransform(oldTransform);
6552  painter->setFont(oldFont);
6553 }
6554 
6564 {
6565  TickLabelData result;
6566 
6567  // determine whether beautiful decimal powers should be used
6568  bool useBeautifulPowers = false;
6569  int ePos = -1;
6570  if (substituteExponent)
6571  {
6572  ePos = text.indexOf('e');
6573  if (ePos > -1)
6574  useBeautifulPowers = true;
6575  }
6576 
6577  // calculate text bounding rects and do string preparation for beautiful decimal powers:
6578  result.baseFont = font;
6579  if (result.baseFont.pointSizeF() > 0) // On some rare systems, this sometimes is initialized with -1 (Qt bug?), so we check here before possibly setting a negative value in the next line
6580  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
6581  if (useBeautifulPowers)
6582  {
6583  // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6584  result.basePart = text.left(ePos);
6585  // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6586  if (abbreviateDecimalPowers && result.basePart == "1")
6587  result.basePart = "10";
6588  else
6589  result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + "10";
6590  result.expPart = text.mid(ePos+1);
6591  // clip "+" and leading zeros off expPart:
6592  while (result.expPart.length() > 2 && result.expPart.at(1) == '0') // length > 2 so we leave one zero when numberFormatChar is 'e'
6593  result.expPart.remove(1, 1);
6594  if (!result.expPart.isEmpty() && result.expPart.at(0) == '+')
6595  result.expPart.remove(0, 1);
6596  // prepare smaller font for exponent:
6597  result.expFont = font;
6598  result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6599  // calculate bounding rects of base part, exponent part and total one:
6600  result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6601  result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6602  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
6603  } else // useBeautifulPowers == false
6604  {
6605  result.basePart = text;
6606  result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6607  }
6608  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
6609 
6610  // calculate possibly different bounding rect after rotation:
6611  result.rotatedTotalBounds = result.totalBounds;
6612  if (!qFuzzyIsNull(tickLabelRotation))
6613  {
6614  QTransform transform;
6615  transform.rotate(tickLabelRotation);
6616  result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6617  }
6618 
6619  return result;
6620 }
6621 
6633 {
6634  /*
6635  calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6636  explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6637  horizontally under the corresponding tick is always on the label side that is closer to the
6638  axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6639  is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6640  will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6641  time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6642  labels.
6643  */
6644  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6645  bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6646  double radians = tickLabelRotation/180.0*M_PI;
6647  int x=0, y=0;
6648  if (type == QCPAxis::atLeft)
6649  {
6650  if (doRotation)
6651  {
6652  if (tickLabelRotation > 0)
6653  {
6654  x = -qCos(radians)*labelData.totalBounds.width();
6655  y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6656  } else
6657  {
6658  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6659  y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6660  }
6661  } else
6662  {
6663  x = -labelData.totalBounds.width();
6664  y = -labelData.totalBounds.height()/2.0;
6665  }
6666  } else if (type == QCPAxis::atRight)
6667  {
6668  if (doRotation)
6669  {
6670  if (tickLabelRotation > 0)
6671  {
6672  x = +qSin(radians)*labelData.totalBounds.height();
6673  y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6674  } else
6675  {
6676  x = 0;
6677  y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6678  }
6679  } else
6680  {
6681  x = 0;
6682  y = -labelData.totalBounds.height()/2.0;
6683  }
6684  } else if (type == QCPAxis::atTop)
6685  {
6686  if (doRotation)
6687  {
6688  if (tickLabelRotation > 0)
6689  {
6690  x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6691  y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6692  } else
6693  {
6694  x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6695  y = -qCos(-radians)*labelData.totalBounds.height();
6696  }
6697  } else
6698  {
6699  x = -labelData.totalBounds.width()/2.0;
6700  y = -labelData.totalBounds.height();
6701  }
6702  } else if (type == QCPAxis::atBottom)
6703  {
6704  if (doRotation)
6705  {
6706  if (tickLabelRotation > 0)
6707  {
6708  x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6709  y = 0;
6710  } else
6711  {
6712  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6713  y = +qSin(-radians)*labelData.totalBounds.width();
6714  }
6715  } else
6716  {
6717  x = -labelData.totalBounds.width()/2.0;
6718  y = 0;
6719  }
6720  }
6721 
6722  return QPointF(x, y);
6723 }
6724 
6732 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
6733 {
6734  // note: this function must return the same tick label sizes as the placeTickLabel function.
6735  QSize finalSize;
6736  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6737  {
6738  const CachedLabel *cachedLabel = mLabelCache.object(text);
6739  finalSize = cachedLabel->pixmap.size();
6740  } else // label caching disabled or no label with this text cached:
6741  {
6742  TickLabelData labelData = getTickLabelData(font, text);
6743  finalSize = labelData.rotatedTotalBounds.size();
6744  }
6745 
6746  // expand passed tickLabelsSize if current tick label is larger:
6747  if (finalSize.width() > tickLabelsSize->width())
6748  tickLabelsSize->setWidth(finalSize.width());
6749  if (finalSize.height() > tickLabelsSize->height())
6750  tickLabelsSize->setHeight(finalSize.height());
6751 }
6752 
6753 
6757 
6824 /* start of documentation of pure virtual functions */
6825 
6871 /* end of documentation of pure virtual functions */
6872 /* start of documentation of signals */
6873 
6887 /* end of documentation of signals */
6888 
6901  QCPLayerable(keyAxis->parentPlot(), "", keyAxis->axisRect()),
6902  mName(""),
6903  mAntialiasedFill(true),
6904  mAntialiasedScatters(true),
6905  mAntialiasedErrorBars(false),
6906  mPen(Qt::black),
6907  mSelectedPen(Qt::black),
6908  mBrush(Qt::NoBrush),
6909  mSelectedBrush(Qt::NoBrush),
6910  mKeyAxis(keyAxis),
6911  mValueAxis(valueAxis),
6912  mSelectable(true),
6913  mSelected(false)
6914 {
6915  if (keyAxis->parentPlot() != valueAxis->parentPlot())
6916  qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
6918  qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
6919 }
6920 
6925 void QCPAbstractPlottable::setName(const QString &name)
6926 {
6927  mName = name;
6928 }
6929 
6937 {
6938  mAntialiasedFill = enabled;
6939 }
6940 
6948 {
6949  mAntialiasedScatters = enabled;
6950 }
6951 
6959 {
6960  mAntialiasedErrorBars = enabled;
6961 }
6962 
6963 
6973 void QCPAbstractPlottable::setPen(const QPen &pen)
6974 {
6975  mPen = pen;
6976 }
6977 
6985 {
6986  mSelectedPen = pen;
6987 }
6988 
6998 void QCPAbstractPlottable::setBrush(const QBrush &brush)
6999 {
7000  mBrush = brush;
7001 }
7002 
7010 {
7012 }
7013 
7026 {
7027  mKeyAxis = axis;
7028 }
7029 
7042 {
7043  mValueAxis = axis;
7044 }
7045 
7056 {
7057  if (mSelectable != selectable)
7058  {
7061  }
7062 }
7063 
7079 {
7080  if (mSelected != selected)
7081  {
7082  mSelected = selected;
7084  }
7085 }
7086 
7100 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
7101 {
7102  rescaleKeyAxis(onlyEnlarge);
7103  rescaleValueAxis(onlyEnlarge);
7104 }
7105 
7111 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
7112 {
7113  QCPAxis *keyAxis = mKeyAxis.data();
7114  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
7115 
7116  SignDomain signDomain = sdBoth;
7118  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7119 
7120  bool foundRange;
7121  QCPRange newRange = getKeyRange(foundRange, signDomain);
7122  if (foundRange)
7123  {
7124  if (onlyEnlarge)
7125  newRange.expand(keyAxis->range());
7126  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
7127  {
7128  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
7130  {
7131  newRange.lower = center-keyAxis->range().size()/2.0;
7132  newRange.upper = center+keyAxis->range().size()/2.0;
7133  } else // scaleType() == stLogarithmic
7134  {
7135  newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7136  newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7137  }
7138  }
7139  keyAxis->setRange(newRange);
7140  }
7141 }
7142 
7151 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
7152 {
7153  QCPAxis *valueAxis = mValueAxis.data();
7154  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
7155 
7156  SignDomain signDomain = sdBoth;
7158  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7159 
7160  bool foundRange;
7161  QCPRange newRange = getValueRange(foundRange, signDomain);
7162  if (foundRange)
7163  {
7164  if (onlyEnlarge)
7165  newRange.expand(valueAxis->range());
7166  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
7167  {
7168  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
7170  {
7171  newRange.lower = center-valueAxis->range().size()/2.0;
7172  newRange.upper = center+valueAxis->range().size()/2.0;
7173  } else // scaleType() == stLogarithmic
7174  {
7175  newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7176  newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7177  }
7178  }
7179  valueAxis->setRange(newRange);
7180  }
7181 }
7182 
7196 {
7197  if (!mParentPlot || !mParentPlot->legend)
7198  return false;
7199 
7201  {
7203  return true;
7204  } else
7205  return false;
7206 }
7207 
7219 {
7220  if (!mParentPlot->legend)
7221  return false;
7222 
7224  return mParentPlot->legend->removeItem(lip);
7225  else
7226  return false;
7227 }
7228 
7229 /* inherits documentation from base class */
7231 {
7232  if (mKeyAxis && mValueAxis)
7233  return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
7234  else
7235  return QRect();
7236 }
7237 
7238 /* inherits documentation from base class */
7240 {
7241  return QCP::iSelectPlottables;
7242 }
7243 
7254 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
7255 {
7256  QCPAxis *keyAxis = mKeyAxis.data();
7257  QCPAxis *valueAxis = mValueAxis.data();
7258  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7259 
7260  if (keyAxis->orientation() == Qt::Horizontal)
7261  {
7262  x = keyAxis->coordToPixel(key);
7263  y = valueAxis->coordToPixel(value);
7264  } else
7265  {
7266  y = keyAxis->coordToPixel(key);
7267  x = valueAxis->coordToPixel(value);
7268  }
7269 }
7270 
7276 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
7277 {
7278  QCPAxis *keyAxis = mKeyAxis.data();
7279  QCPAxis *valueAxis = mValueAxis.data();
7280  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
7281 
7282  if (keyAxis->orientation() == Qt::Horizontal)
7283  return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7284  else
7285  return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7286 }
7287 
7298 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
7299 {
7300  QCPAxis *keyAxis = mKeyAxis.data();
7301  QCPAxis *valueAxis = mValueAxis.data();
7302  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7303 
7304  if (keyAxis->orientation() == Qt::Horizontal)
7305  {
7306  key = keyAxis->pixelToCoord(x);
7307  value = valueAxis->pixelToCoord(y);
7308  } else
7309  {
7310  key = keyAxis->pixelToCoord(y);
7311  value = valueAxis->pixelToCoord(x);
7312  }
7313 }
7314 
7320 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
7321 {
7322  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7323 }
7324 
7331 {
7332  return mSelected ? mSelectedPen : mPen;
7333 }
7334 
7341 {
7342  return mSelected ? mSelectedBrush : mBrush;
7343 }
7344 
7359 {
7361 }
7362 
7375 {
7377 }
7378 
7391 {
7393 }
7394 
7407 {
7409 }
7410 
7421 double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
7422 {
7423  QVector2D a(start);
7424  QVector2D b(end);
7425  QVector2D p(point);
7426  QVector2D v(b-a);
7427 
7428  double vLengthSqr = v.lengthSquared();
7429  if (!qFuzzyIsNull(vLengthSqr))
7430  {
7431  double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
7432  if (mu < 0)
7433  return (a-p).lengthSquared();
7434  else if (mu > 1)
7435  return (b-p).lengthSquared();
7436  else
7437  return ((a + mu*v)-p).lengthSquared();
7438  } else
7439  return (a-p).lengthSquared();
7440 }
7441 
7442 /* inherits documentation from base class */
7443 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
7444 {
7445  Q_UNUSED(event)
7446  Q_UNUSED(details)
7447  if (mSelectable)
7448  {
7449  bool selBefore = mSelected;
7450  setSelected(additive ? !mSelected : true);
7451  if (selectionStateChanged)
7452  *selectionStateChanged = mSelected != selBefore;
7453  }
7454 }
7455 
7456 /* inherits documentation from base class */
7457 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
7458 {
7459  if (mSelectable)
7460  {
7461  bool selBefore = mSelected;
7462  setSelected(false);
7463  if (selectionStateChanged)
7464  *selectionStateChanged = mSelected != selBefore;
7465  }
7466 }
7467 
7468 
7472 
7494 /* start documentation of inline functions */
7495 
7506 /* end documentation of inline functions */
7507 
7513 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
7514  mName(name),
7515  mParentPlot(parentPlot),
7516  mParentItem(parentItem),
7517  mAnchorId(anchorId)
7518 {
7519 }
7520 
7522 {
7523  // unregister as parent at children:
7524  QList<QCPItemPosition*> currentChildren(mChildren.toList());
7525  for (int i=0; i<currentChildren.size(); ++i)
7526  currentChildren.at(i)->setParentAnchor(0); // this acts back on this anchor and child removes itself from mChildren
7527 }
7528 
7536 {
7537  if (mParentItem)
7538  {
7539  if (mAnchorId > -1)
7540  {
7542  } else
7543  {
7544  qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7545  return QPointF();
7546  }
7547  } else
7548  {
7549  qDebug() << Q_FUNC_INFO << "no parent item set";
7550  return QPointF();
7551  }
7552 }
7553 
7562 {
7563  if (!mChildren.contains(pos))
7564  mChildren.insert(pos);
7565  else
7566  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7567 }
7568 
7576 {
7577  if (!mChildren.remove(pos))
7578  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7579 }
7580 
7581 
7585 
7617 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
7618  QCPItemAnchor(parentPlot, parentItem, name),
7619  mPositionType(ptAbsolute),
7620  mKey(0),
7621  mValue(0),
7622  mParentAnchor(0)
7623 {
7624 }
7625 
7627 {
7628  // unregister as parent at children:
7629  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
7630  // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
7631  QList<QCPItemPosition*> currentChildren(mChildren.toList());
7632  for (int i=0; i<currentChildren.size(); ++i)
7633  currentChildren.at(i)->setParentAnchor(0); // this acts back on this anchor and child removes itself from mChildren
7634  // unregister as child in parent:
7635  if (mParentAnchor)
7636  mParentAnchor->removeChild(this);
7637 }
7638 
7639 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
7641 {
7642  return mAxisRect.data();
7643 }
7644 
7668 {
7669  if (mPositionType != type)
7670  {
7671  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7672  // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7673  bool recoverPixelPosition = true;
7675  recoverPixelPosition = false;
7677  recoverPixelPosition = false;
7678 
7679  QPointF pixelP;
7680  if (recoverPixelPosition)
7681  pixelP = pixelPoint();
7682 
7683  mPositionType = type;
7684 
7685  if (recoverPixelPosition)
7686  setPixelPoint(pixelP);
7687  }
7688 }
7689 
7705 bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7706 {
7707  // make sure self is not assigned as parent:
7708  if (parentAnchor == this)
7709  {
7710  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7711  return false;
7712  }
7713  // make sure no recursive parent-child-relationships are created:
7714  QCPItemAnchor *currentParent = parentAnchor;
7715  while (currentParent)
7716  {
7717  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7718  {
7719  // is a QCPItemPosition, might have further parent, so keep iterating
7720  if (currentParentPos == this)
7721  {
7722  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7723  return false;
7724  }
7725  currentParent = currentParentPos->mParentAnchor;
7726  } else
7727  {
7728  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7729  // same, to prevent a position being child of an anchor which itself depends on the position,
7730  // because they're both on the same item:
7731  if (currentParent->mParentItem == mParentItem)
7732  {
7733  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7734  return false;
7735  }
7736  break;
7737  }
7738  }
7739 
7740  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7743 
7744  // save pixel position:
7745  QPointF pixelP;
7746  if (keepPixelPosition)
7747  pixelP = pixelPoint();
7748  // unregister at current parent anchor:
7749  if (mParentAnchor)
7750  mParentAnchor->removeChild(this);
7751  // register at new parent anchor:
7752  if (parentAnchor)
7753  parentAnchor->addChild(this);
7755  // restore pixel position under new parent:
7756  if (keepPixelPosition)
7757  setPixelPoint(pixelP);
7758  else
7759  setCoords(0, 0);
7760  return true;
7761 }
7762 
7776 void QCPItemPosition::setCoords(double key, double value)
7777 {
7778  mKey = key;
7779  mValue = value;
7780 }
7781 
7787 void QCPItemPosition::setCoords(const QPointF &pos)
7788 {
7789  setCoords(pos.x(), pos.y());
7790 }
7791 
7799 {
7800  switch (mPositionType)
7801  {
7802  case ptAbsolute:
7803  {
7804  if (mParentAnchor)
7805  return QPointF(mKey, mValue) + mParentAnchor->pixelPoint();
7806  else
7807  return QPointF(mKey, mValue);
7808  }
7809 
7810  case ptViewportRatio:
7811  {
7812  if (mParentAnchor)
7813  {
7814  return QPointF(mKey*mParentPlot->viewport().width(),
7816  } else
7817  {
7818  return QPointF(mKey*mParentPlot->viewport().width(),
7819  mValue*mParentPlot->viewport().height()) + mParentPlot->viewport().topLeft();
7820  }
7821  }
7822 
7823  case ptAxisRectRatio:
7824  {
7825  if (mAxisRect)
7826  {
7827  if (mParentAnchor)
7828  {
7829  return QPointF(mKey*mAxisRect.data()->width(),
7830  mValue*mAxisRect.data()->height()) + mParentAnchor->pixelPoint();
7831  } else
7832  {
7833  return QPointF(mKey*mAxisRect.data()->width(),
7834  mValue*mAxisRect.data()->height()) + mAxisRect.data()->topLeft();
7835  }
7836  } else
7837  {
7838  qDebug() << Q_FUNC_INFO << "No axis rect defined";
7839  return QPointF(mKey, mValue);
7840  }
7841  }
7842 
7843  case ptPlotCoords:
7844  {
7845  double x, y;
7846  if (mKeyAxis && mValueAxis)
7847  {
7848  // both key and value axis are given, translate key/value to x/y coordinates:
7849  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
7850  {
7851  x = mKeyAxis.data()->coordToPixel(mKey);
7852  y = mValueAxis.data()->coordToPixel(mValue);
7853  } else
7854  {
7855  y = mKeyAxis.data()->coordToPixel(mKey);
7856  x = mValueAxis.data()->coordToPixel(mValue);
7857  }
7858  } else if (mKeyAxis)
7859  {
7860  // only key axis is given, depending on orientation only transform x or y to key coordinate, other stays pixel:
7861  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
7862  {
7863  x = mKeyAxis.data()->coordToPixel(mKey);
7864  y = mValue;
7865  } else
7866  {
7867  y = mKeyAxis.data()->coordToPixel(mKey);
7868  x = mValue;
7869  }
7870  } else if (mValueAxis)
7871  {
7872  // only value axis is given, depending on orientation only transform x or y to value coordinate, other stays pixel:
7873  if (mValueAxis.data()->orientation() == Qt::Horizontal)
7874  {
7875  x = mValueAxis.data()->coordToPixel(mValue);
7876  y = mKey;
7877  } else
7878  {
7879  y = mValueAxis.data()->coordToPixel(mValue);
7880  x = mKey;
7881  }
7882  } else
7883  {
7884  // no axis given, basically the same as if mPositionType were ptAbsolute
7885  qDebug() << Q_FUNC_INFO << "No axes defined";
7886  x = mKey;
7887  y = mValue;
7888  }
7889  return QPointF(x, y);
7890  }
7891  }
7892  return QPointF();
7893 }
7894 
7900 void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
7901 {
7902  mKeyAxis = keyAxis;
7904 }
7905 
7912 {
7913  mAxisRect = axisRect;
7914 }
7915 
7926 void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
7927 {
7928  switch (mPositionType)
7929  {
7930  case ptAbsolute:
7931  {
7932  if (mParentAnchor)
7934  else
7936  break;
7937  }
7938 
7939  case ptViewportRatio:
7940  {
7941  if (mParentAnchor)
7942  {
7943  QPointF p(pixelPoint-mParentAnchor->pixelPoint());
7944  p.rx() /= (double)mParentPlot->viewport().width();
7945  p.ry() /= (double)mParentPlot->viewport().height();
7946  setCoords(p);
7947  } else
7948  {
7949  QPointF p(pixelPoint-mParentPlot->viewport().topLeft());
7950  p.rx() /= (double)mParentPlot->viewport().width();
7951  p.ry() /= (double)mParentPlot->viewport().height();
7952  setCoords(p);
7953  }
7954  break;
7955  }
7956 
7957  case ptAxisRectRatio:
7958  {
7959  if (mAxisRect)
7960  {
7961  if (mParentAnchor)
7962  {
7963  QPointF p(pixelPoint-mParentAnchor->pixelPoint());
7964  p.rx() /= (double)mAxisRect.data()->width();
7965  p.ry() /= (double)mAxisRect.data()->height();
7966  setCoords(p);
7967  } else
7968  {
7969  QPointF p(pixelPoint-mAxisRect.data()->topLeft());
7970  p.rx() /= (double)mAxisRect.data()->width();
7971  p.ry() /= (double)mAxisRect.data()->height();
7972  setCoords(p);
7973  }
7974  } else
7975  {
7976  qDebug() << Q_FUNC_INFO << "No axis rect defined";
7978  }
7979  break;
7980  }
7981 
7982  case ptPlotCoords:
7983  {
7984  double newKey, newValue;
7985  if (mKeyAxis && mValueAxis)
7986  {
7987  // both key and value axis are given, translate point to key/value coordinates:
7988  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
7989  {
7990  newKey = mKeyAxis.data()->pixelToCoord(pixelPoint.x());
7991  newValue = mValueAxis.data()->pixelToCoord(pixelPoint.y());
7992  } else
7993  {
7994  newKey = mKeyAxis.data()->pixelToCoord(pixelPoint.y());
7995  newValue = mValueAxis.data()->pixelToCoord(pixelPoint.x());
7996  }
7997  } else if (mKeyAxis)
7998  {
7999  // only key axis is given, depending on orientation only transform x or y to key coordinate, other stays pixel:
8000  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
8001  {
8002  newKey = mKeyAxis.data()->pixelToCoord(pixelPoint.x());
8003  newValue = pixelPoint.y();
8004  } else
8005  {
8006  newKey = mKeyAxis.data()->pixelToCoord(pixelPoint.y());
8007  newValue = pixelPoint.x();
8008  }
8009  } else if (mValueAxis)
8010  {
8011  // only value axis is given, depending on orientation only transform x or y to value coordinate, other stays pixel:
8012  if (mValueAxis.data()->orientation() == Qt::Horizontal)
8013  {
8014  newKey = pixelPoint.y();
8015  newValue = mValueAxis.data()->pixelToCoord(pixelPoint.x());
8016  } else
8017  {
8018  newKey = pixelPoint.x();
8019  newValue = mValueAxis.data()->pixelToCoord(pixelPoint.y());
8020  }
8021  } else
8022  {
8023  // no axis given, basically the same as if mPositionType were ptAbsolute
8024  qDebug() << Q_FUNC_INFO << "No axes defined";
8025  newKey = pixelPoint.x();
8026  newValue = pixelPoint.y();
8027  }
8028  setCoords(newKey, newValue);
8029  break;
8030  }
8031  }
8032 }
8033 
8034 
8038 
8161 /* start of documentation of inline functions */
8162 
8178 /* end of documentation of inline functions */
8179 /* start documentation of pure virtual functions */
8180 
8191 /* end documentation of pure virtual functions */
8192 /* start documentation of signals */
8193 
8199 /* end documentation of signals */
8200 
8205  QCPLayerable(parentPlot),
8206  mClipToAxisRect(false),
8207  mSelectable(true),
8208  mSelected(false)
8209 {
8210  QList<QCPAxisRect*> rects = parentPlot->axisRects();
8211  if (rects.size() > 0)
8212  {
8213  setClipToAxisRect(true);
8214  setClipAxisRect(rects.first());
8215  }
8216 }
8217 
8219 {
8220  // don't delete mPositions because every position is also an anchor and thus in mAnchors
8221  qDeleteAll(mAnchors);
8222 }
8223 
8224 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
8226 {
8227  return mClipAxisRect.data();
8228 }
8229 
8237 {
8238  mClipToAxisRect = clip;
8239  if (mClipToAxisRect)
8241 }
8242 
8250 {
8251  mClipAxisRect = rect;
8252  if (mClipToAxisRect)
8254 }
8255 
8265 void QCPAbstractItem::setSelectable(bool selectable)
8266 {
8267  if (mSelectable != selectable)
8268  {
8271  }
8272 }
8273 
8288 void QCPAbstractItem::setSelected(bool selected)
8289 {
8290  if (mSelected != selected)
8291  {
8292  mSelected = selected;
8294  }
8295 }
8296 
8307 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
8308 {
8309  for (int i=0; i<mPositions.size(); ++i)
8310  {
8311  if (mPositions.at(i)->name() == name)
8312  return mPositions.at(i);
8313  }
8314  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8315  return 0;
8316 }
8317 
8328 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8329 {
8330  for (int i=0; i<mAnchors.size(); ++i)
8331  {
8332  if (mAnchors.at(i)->name() == name)
8333  return mAnchors.at(i);
8334  }
8335  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8336  return 0;
8337 }
8338 
8347 bool QCPAbstractItem::hasAnchor(const QString &name) const
8348 {
8349  for (int i=0; i<mAnchors.size(); ++i)
8350  {
8351  if (mAnchors.at(i)->name() == name)
8352  return true;
8353  }
8354  return false;
8355 }
8356 
8367 {
8369  return mClipAxisRect.data()->rect();
8370  else
8371  return mParentPlot->viewport();
8372 }
8373 
8388 {
8390 }
8391 
8404 double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8405 {
8406  QVector2D a(start);
8407  QVector2D b(end);
8408  QVector2D p(point);
8409  QVector2D v(b-a);
8410 
8411  double vLengthSqr = v.lengthSquared();
8412  if (!qFuzzyIsNull(vLengthSqr))
8413  {
8414  double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8415  if (mu < 0)
8416  return (a-p).lengthSquared();
8417  else if (mu > 1)
8418  return (b-p).lengthSquared();
8419  else
8420  return ((a + mu*v)-p).lengthSquared();
8421  } else
8422  return (a-p).lengthSquared();
8423 }
8424 
8442 double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
8443 {
8444  double result = -1;
8445 
8446  // distance to border:
8447  QList<QLineF> lines;
8448  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
8449  << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
8450  double minDistSqr = std::numeric_limits<double>::max();
8451  for (int i=0; i<lines.size(); ++i)
8452  {
8453  double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8454  if (distSqr < minDistSqr)
8455  minDistSqr = distSqr;
8456  }
8457  result = qSqrt(minDistSqr);
8458 
8459  // filled rect, allow click inside to count as hit:
8460  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
8461  {
8462  if (rect.contains(pos))
8463  result = mParentPlot->selectionTolerance()*0.99;
8464  }
8465  return result;
8466 }
8467 
8478 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
8479 {
8480  qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
8481  return QPointF();
8482 }
8483 
8499 {
8500  if (hasAnchor(name))
8501  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8502  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8503  mPositions.append(newPosition);
8504  mAnchors.append(newPosition); // every position is also an anchor
8505  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8506  newPosition->setType(QCPItemPosition::ptPlotCoords);
8507  if (mParentPlot->axisRect())
8508  newPosition->setAxisRect(mParentPlot->axisRect());
8509  newPosition->setCoords(0, 0);
8510  return newPosition;
8511 }
8512 
8532 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
8533 {
8534  if (hasAnchor(name))
8535  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8536  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
8537  mAnchors.append(newAnchor);
8538  return newAnchor;
8539 }
8540 
8541 /* inherits documentation from base class */
8542 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8543 {
8544  Q_UNUSED(event)
8545  Q_UNUSED(details)
8546  if (mSelectable)
8547  {
8548  bool selBefore = mSelected;
8549  setSelected(additive ? !mSelected : true);
8550  if (selectionStateChanged)
8551  *selectionStateChanged = mSelected != selBefore;
8552  }
8553 }
8554 
8555 /* inherits documentation from base class */
8556 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
8557 {
8558  if (mSelectable)
8559  {
8560  bool selBefore = mSelected;
8561  setSelected(false);
8562  if (selectionStateChanged)
8563  *selectionStateChanged = mSelected != selBefore;
8564  }
8565 }
8566 
8567 /* inherits documentation from base class */
8569 {
8570  return QCP::iSelectItems;
8571 }
8572 
8573 
8770 
8780 /* start of documentation of inline functions */
8781 
8803 /* end of documentation of inline functions */
8804 /* start of documentation of signals */
8805 
8993 /* end of documentation of signals */
8994 /* start of documentation of public members */
8995 
9067 /* end of documentation of public members */
9068 
9072 QCustomPlot::QCustomPlot(QWidget *parent) :
9073  QWidget(parent),
9074  xAxis(0),
9075  yAxis(0),
9076  xAxis2(0),
9077  yAxis2(0),
9078  legend(0),
9079  mPlotLayout(0),
9080  mAutoAddPlottableToLegend(true),
9081  mAntialiasedElements(QCP::aeNone),
9082  mNotAntialiasedElements(QCP::aeNone),
9083  mInteractions(0),
9084  mSelectionTolerance(8),
9085  mNoAntialiasingOnDrag(false),
9086  mBackgroundBrush(Qt::white, Qt::SolidPattern),
9087  mBackgroundScaled(true),
9088  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9089  mCurrentLayer(0),
9090  mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
9091  mMultiSelectModifier(Qt::ControlModifier),
9092  mPaintBuffer(size()),
9093  mMouseEventElement(0),
9094  mReplotting(false)
9095 {
9096  setAttribute(Qt::WA_NoMousePropagation);
9097  setAttribute(Qt::WA_OpaquePaintEvent);
9098  setMouseTracking(true);
9099  QLocale currentLocale = locale();
9100  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9101  setLocale(currentLocale);
9102 
9103  // create initial layers:
9104  mLayers.append(new QCPLayer(this, "background"));
9105  mLayers.append(new QCPLayer(this, "grid"));
9106  mLayers.append(new QCPLayer(this, "main"));
9107  mLayers.append(new QCPLayer(this, "axes"));
9108  mLayers.append(new QCPLayer(this, "legend"));
9110  setCurrentLayer("main");
9111 
9112  // create initial layout, axis rect and legend:
9113  mPlotLayout = new QCPLayoutGrid;
9115  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9116  mPlotLayout->setLayer("main");
9117  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9118  mPlotLayout->addElement(0, 0, defaultAxisRect);
9119  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9120  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9121  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9122  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9123  legend = new QCPLegend;
9124  legend->setVisible(false);
9125  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
9126  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9127 
9128  defaultAxisRect->setLayer("background");
9129  xAxis->setLayer("axes");
9130  yAxis->setLayer("axes");
9131  xAxis2->setLayer("axes");
9132  yAxis2->setLayer("axes");
9133  xAxis->grid()->setLayer("grid");
9134  yAxis->grid()->setLayer("grid");
9135  xAxis2->grid()->setLayer("grid");
9136  yAxis2->grid()->setLayer("grid");
9137  legend->setLayer("legend");
9138 
9139  setViewport(rect()); // needs to be called after mPlotLayout has been created
9140 
9141  replot();
9142 }
9143 
9145 {
9146  clearPlottables();
9147  clearItems();
9148 
9149  if (mPlotLayout)
9150  {
9151  delete mPlotLayout;
9152  mPlotLayout = 0;
9153  }
9154 
9155  mCurrentLayer = 0;
9156  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
9157  mLayers.clear();
9158 }
9159 
9177 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
9178 {
9180 
9181  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9184 }
9185 
9193 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
9194 {
9195  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9196  mAntialiasedElements &= ~antialiasedElement;
9197  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9198  mAntialiasedElements |= antialiasedElement;
9199 
9200  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9203 }
9204 
9223 void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
9224 {
9226 
9227  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9230 }
9231 
9239 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
9240 {
9241  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9242  mNotAntialiasedElements &= ~notAntialiasedElement;
9243  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9244  mNotAntialiasedElements |= notAntialiasedElement;
9245 
9246  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9249 }
9250 
9258 {
9260 }
9261 
9316 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
9317 {
9319 }
9320 
9328 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
9329 {
9330  if (!enabled && mInteractions.testFlag(interaction))
9331  mInteractions &= ~interaction;
9332  else if (enabled && !mInteractions.testFlag(interaction))
9333  mInteractions |= interaction;
9334 }
9335 
9350 {
9351  mSelectionTolerance = pixels;
9352 }
9353 
9364 {
9365  mNoAntialiasingOnDrag = enabled;
9366 }
9367 
9373 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
9374 {
9375  mPlottingHints = hints;
9376 }
9377 
9384 {
9385  QCP::PlottingHints newHints = mPlottingHints;
9386  if (!enabled)
9387  newHints &= ~hint;
9388  else
9389  newHints |= hint;
9390 
9391  if (newHints != mPlottingHints)
9392  setPlottingHints(newHints);
9393 }
9394 
9405 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
9406 {
9407  mMultiSelectModifier = modifier;
9408 }
9409 
9417 void QCustomPlot::setViewport(const QRect &rect)
9418 {
9419  mViewport = rect;
9420  if (mPlotLayout)
9422 }
9423 
9439 void QCustomPlot::setBackground(const QPixmap &pm)
9440 {
9441  mBackgroundPixmap = pm;
9442  mScaledBackgroundPixmap = QPixmap();
9443 }
9444 
9458 void QCustomPlot::setBackground(const QBrush &brush)
9459 {
9460  mBackgroundBrush = brush;
9461 }
9462 
9470 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
9471 {
9472  mBackgroundPixmap = pm;
9473  mScaledBackgroundPixmap = QPixmap();
9474  mBackgroundScaled = scaled;
9475  mBackgroundScaledMode = mode;
9476 }
9477 
9489 {
9490  mBackgroundScaled = scaled;
9491 }
9492 
9499 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
9500 {
9501  mBackgroundScaledMode = mode;
9502 }
9503 
9513 {
9514  if (index >= 0 && index < mPlottables.size())
9515  {
9516  return mPlottables.at(index);
9517  } else
9518  {
9519  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9520  return 0;
9521  }
9522 }
9523 
9532 {
9533  if (!mPlottables.isEmpty())
9534  {
9535  return mPlottables.last();
9536  } else
9537  return 0;
9538 }
9539 
9551 {
9552  if (mPlottables.contains(plottable))
9553  {
9554  qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
9555  return false;
9556  }
9557  if (plottable->parentPlot() != this)
9558  {
9559  qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
9560  return false;
9561  }
9562 
9563  mPlottables.append(plottable);
9564  // possibly add plottable to legend:
9567  // special handling for QCPGraphs to maintain the simple graph interface:
9568  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9569  mGraphs.append(graph);
9570  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
9572  return true;
9573 }
9574 
9583 {
9584  if (!mPlottables.contains(plottable))
9585  {
9586  qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
9587  return false;
9588  }
9589 
9590  // remove plottable from legend:
9592  // special handling for QCPGraphs to maintain the simple graph interface:
9593  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9594  mGraphs.removeOne(graph);
9595  // remove plottable:
9596  delete plottable;
9597  mPlottables.removeOne(plottable);
9598  return true;
9599 }
9600 
9606 {
9607  if (index >= 0 && index < mPlottables.size())
9608  return removePlottable(mPlottables[index]);
9609  else
9610  {
9611  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9612  return false;
9613  }
9614 }
9615 
9624 {
9625  int c = mPlottables.size();
9626  for (int i=c-1; i >= 0; --i)
9628  return c;
9629 }
9630 
9637 {
9638  return mPlottables.size();
9639 }
9640 
9648 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9649 {
9650  QList<QCPAbstractPlottable*> result;
9652  {
9653  if (plottable->selected())
9654  result.append(plottable);
9655  }
9656  return result;
9657 }
9658 
9671 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
9672 {
9673  QCPAbstractPlottable *resultPlottable = 0;
9674  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9675 
9677  {
9678  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
9679  continue;
9680  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
9681  {
9682  double currentDistance = plottable->selectTest(pos, false);
9683  if (currentDistance >= 0 && currentDistance < resultDistance)
9684  {
9685  resultPlottable = plottable;
9686  resultDistance = currentDistance;
9687  }
9688  }
9689  }
9690 
9691  return resultPlottable;
9692 }
9693 
9700 {
9701  return mPlottables.contains(plottable);
9702 }
9703 
9712 QCPGraph *QCustomPlot::graph(int index) const
9713 {
9714  if (index >= 0 && index < mGraphs.size())
9715  {
9716  return mGraphs.at(index);
9717  } else
9718  {
9719  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9720  return 0;
9721  }
9722 }
9723 
9732 {
9733  if (!mGraphs.isEmpty())
9734  {
9735  return mGraphs.last();
9736  } else
9737  return 0;
9738 }
9739 
9753 {
9754  if (!keyAxis) keyAxis = xAxis;
9755  if (!valueAxis) valueAxis = yAxis;
9756  if (!keyAxis || !valueAxis)
9757  {
9758  qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
9759  return 0;
9760  }
9761  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
9762  {
9763  qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
9764  return 0;
9765  }
9766 
9767  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9768  if (addPlottable(newGraph))
9769  {
9770  newGraph->setName("Graph "+QString::number(mGraphs.size()));
9771  return newGraph;
9772  } else
9773  {
9774  delete newGraph;
9775  return 0;
9776  }
9777 }
9778 
9789 {
9790  return removePlottable(graph);
9791 }
9792 
9798 {
9799  if (index >= 0 && index < mGraphs.size())
9800  return removeGraph(mGraphs[index]);
9801  else
9802  return false;
9803 }
9804 
9813 {
9814  int c = mGraphs.size();
9815  for (int i=c-1; i >= 0; --i)
9816  removeGraph(mGraphs[i]);
9817  return c;
9818 }
9819 
9826 {
9827  return mGraphs.size();
9828 }
9829 
9838 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9839 {
9840  QList<QCPGraph*> result;
9841  foreach (QCPGraph *graph, mGraphs)
9842  {
9843  if (graph->selected())
9844  result.append(graph);
9845  }
9846  return result;
9847 }
9848 
9858 {
9859  if (index >= 0 && index < mItems.size())
9860  {
9861  return mItems.at(index);
9862  } else
9863  {
9864  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9865  return 0;
9866  }
9867 }
9868 
9877 {
9878  if (!mItems.isEmpty())
9879  {
9880  return mItems.last();
9881  } else
9882  return 0;
9883 }
9884 
9894 {
9895  if (!mItems.contains(item) && item->parentPlot() == this)
9896  {
9897  mItems.append(item);
9898  return true;
9899  } else
9900  {
9901  qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
9902  return false;
9903  }
9904 }
9905 
9914 {
9915  if (mItems.contains(item))
9916  {
9917  delete item;
9918  mItems.removeOne(item);
9919  return true;
9920  } else
9921  {
9922  qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
9923  return false;
9924  }
9925 }
9926 
9932 {
9933  if (index >= 0 && index < mItems.size())
9934  return removeItem(mItems[index]);
9935  else
9936  {
9937  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9938  return false;
9939  }
9940 }
9941 
9950 {
9951  int c = mItems.size();
9952  for (int i=c-1; i >= 0; --i)
9953  removeItem(mItems[i]);
9954  return c;
9955 }
9956 
9963 {
9964  return mItems.size();
9965 }
9966 
9972 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9973 {
9974  QList<QCPAbstractItem*> result;
9975  foreach (QCPAbstractItem *item, mItems)
9976  {
9977  if (item->selected())
9978  result.append(item);
9979  }
9980  return result;
9981 }
9982 
9996 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
9997 {
9998  QCPAbstractItem *resultItem = 0;
9999  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
10000 
10001  foreach (QCPAbstractItem *item, mItems)
10002  {
10003  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
10004  continue;
10005  if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
10006  {
10007  double currentDistance = item->selectTest(pos, false);
10008  if (currentDistance >= 0 && currentDistance < resultDistance)
10009  {
10010  resultItem = item;
10011  resultDistance = currentDistance;
10012  }
10013  }
10014  }
10015 
10016  return resultItem;
10017 }
10018 
10025 {
10026  return mItems.contains(item);
10027 }
10028 
10037 QCPLayer *QCustomPlot::layer(const QString &name) const
10038 {
10039  foreach (QCPLayer *layer, mLayers)
10040  {
10041  if (layer->name() == name)
10042  return layer;
10043  }
10044  return 0;
10045 }
10046 
10053 QCPLayer *QCustomPlot::layer(int index) const
10054 {
10055  if (index >= 0 && index < mLayers.size())
10056  {
10057  return mLayers.at(index);
10058  } else
10059  {
10060  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10061  return 0;
10062  }
10063 }
10064 
10069 {
10070  return mCurrentLayer;
10071 }
10072 
10083 bool QCustomPlot::setCurrentLayer(const QString &name)
10084 {
10085  if (QCPLayer *newCurrentLayer = layer(name))
10086  {
10087  return setCurrentLayer(newCurrentLayer);
10088  } else
10089  {
10090  qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10091  return false;
10092  }
10093 }
10094 
10104 {
10105  if (!mLayers.contains(layer))
10106  {
10107  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10108  return false;
10109  }
10110 
10111  mCurrentLayer = layer;
10112  return true;
10113 }
10114 
10121 {
10122  return mLayers.size();
10123 }
10124 
10138 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10139 {
10140  if (!otherLayer)
10141  otherLayer = mLayers.last();
10142  if (!mLayers.contains(otherLayer))
10143  {
10144  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10145  return false;
10146  }
10147  if (layer(name))
10148  {
10149  qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10150  return false;
10151  }
10152 
10153  QCPLayer *newLayer = new QCPLayer(this, name);
10154  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
10156  return true;
10157 }
10158 
10174 {
10175  if (!mLayers.contains(layer))
10176  {
10177  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10178  return false;
10179  }
10180  if (mLayers.size() < 2)
10181  {
10182  qDebug() << Q_FUNC_INFO << "can't remove last layer";
10183  return false;
10184  }
10185 
10186  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
10187  int removedIndex = layer->index();
10188  bool isFirstLayer = removedIndex==0;
10189  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
10190  QList<QCPLayerable*> children = layer->children();
10191  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
10192  {
10193  for (int i=children.size()-1; i>=0; --i)
10194  children.at(i)->moveToLayer(targetLayer, true);
10195  } else // append normally
10196  {
10197  for (int i=0; i<children.size(); ++i)
10198  children.at(i)->moveToLayer(targetLayer, false);
10199  }
10200  // if removed layer is current layer, change current layer to layer below/above:
10201  if (layer == mCurrentLayer)
10202  setCurrentLayer(targetLayer);
10203  // remove layer:
10204  delete layer;
10205  mLayers.removeOne(layer);
10207  return true;
10208 }
10209 
10220 {
10221  if (!mLayers.contains(layer))
10222  {
10223  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10224  return false;
10225  }
10226  if (!mLayers.contains(otherLayer))
10227  {
10228  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10229  return false;
10230  }
10231 
10232  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
10234  return true;
10235 }
10236 
10247 {
10248  return axisRects().size();
10249 }
10250 
10261 {
10262  const QList<QCPAxisRect*> rectList = axisRects();
10263  if (index >= 0 && index < rectList.size())
10264  {
10265  return rectList.at(index);
10266  } else
10267  {
10268  qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10269  return 0;
10270  }
10271 }
10272 
10278 QList<QCPAxisRect*> QCustomPlot::axisRects() const
10279 {
10280  QList<QCPAxisRect*> result;
10281  QStack<QCPLayoutElement*> elementStack;
10282  if (mPlotLayout)
10283  elementStack.push(mPlotLayout);
10284 
10285  while (!elementStack.isEmpty())
10286  {
10287  foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
10288  {
10289  if (element)
10290  {
10291  elementStack.push(element);
10292  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10293  result.append(ar);
10294  }
10295  }
10296  }
10297 
10298  return result;
10299 }
10300 
10311 {
10312  QCPLayoutElement *currentElement = mPlotLayout;
10313  bool searchSubElements = true;
10314  while (searchSubElements && currentElement)
10315  {
10316  searchSubElements = false;
10317  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10318  {
10319  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10320  {
10321  currentElement = subElement;
10322  searchSubElements = true;
10323  break;
10324  }
10325  }
10326  }
10327  return currentElement;
10328 }
10329 
10337 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10338 {
10339  QList<QCPAxis*> result, allAxes;
10340  foreach (QCPAxisRect *rect, axisRects())
10341  allAxes << rect->axes();
10342 
10343  foreach (QCPAxis *axis, allAxes)
10344  {
10345  if (axis->selectedParts() != QCPAxis::spNone)
10346  result.append(axis);
10347  }
10348 
10349  return result;
10350 }
10351 
10359 QList<QCPLegend*> QCustomPlot::selectedLegends() const
10360 {
10361  QList<QCPLegend*> result;
10362 
10363  QStack<QCPLayoutElement*> elementStack;
10364  if (mPlotLayout)
10365  elementStack.push(mPlotLayout);
10366 
10367  while (!elementStack.isEmpty())
10368  {
10369  foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
10370  {
10371  if (subElement)
10372  {
10373  elementStack.push(subElement);
10374  if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10375  {
10376  if (leg->selectedParts() != QCPLegend::spNone)
10377  result.append(leg);
10378  }
10379  }
10380  }
10381  }
10382 
10383  return result;
10384 }
10385 
10396 {
10397  foreach (QCPLayer *layer, mLayers)
10398  {
10399  foreach (QCPLayerable *layerable, layer->children())
10400  layerable->deselectEvent(0);
10401  }
10402 }
10403 
10418 {
10419  if (mReplotting) // incase signals loop back to replot slot
10420  return;
10421  mReplotting = true;
10422  emit beforeReplot();
10423 
10424  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10425  QCPPainter painter;
10426  painter.begin(&mPaintBuffer);
10427  if (painter.isActive())
10428  {
10429  painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10430  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10431  painter.fillRect(mViewport, mBackgroundBrush);
10432  draw(&painter);
10433  painter.end();
10434  if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10435  repaint();
10436  else
10437  update();
10438  } else // might happen if QCustomPlot has width or height zero
10439  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer";
10440 
10441  emit afterReplot();
10442  mReplotting = false;
10443 }
10444 
10453 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10454 {
10455  QList<QCPAxis*> allAxes;
10456  foreach (QCPAxisRect *rect, axisRects())
10457  allAxes << rect->axes();
10458 
10459  foreach (QCPAxis *axis, allAxes)
10460  axis->rescale(onlyVisiblePlottables);
10461 }
10462 
10500 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10501 {
10502  bool success = false;
10503 #ifdef QT_NO_PRINTER
10504  Q_UNUSED(fileName)
10505  Q_UNUSED(noCosmeticPen)
10506  Q_UNUSED(width)
10507  Q_UNUSED(height)
10508  qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
10509 #else
10510  int newWidth, newHeight;
10511  if (width == 0 || height == 0)
10512  {
10513  newWidth = this->width();
10514  newHeight = this->height();
10515  } else
10516  {
10517  newWidth = width;
10518  newHeight = height;
10519  }
10520 
10521  QPrinter printer(QPrinter::ScreenResolution);
10522  printer.setOutputFileName(fileName);
10523  printer.setOutputFormat(QPrinter::PdfFormat);
10524  printer.setFullPage(true);
10525  printer.setColorMode(QPrinter::Color);
10526  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10527  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10528  QRect oldViewport = viewport();
10529  setViewport(QRect(0, 0, newWidth, newHeight));
10530  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10531  QCPPainter printpainter;
10532  if (printpainter.begin(&printer))
10533  {
10534  printpainter.setMode(QCPPainter::pmVectorized);
10535  printpainter.setMode(QCPPainter::pmNoCaching);
10536  printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10537  printpainter.setWindow(mViewport);
10538  if (mBackgroundBrush.style() != Qt::NoBrush &&
10539  mBackgroundBrush.color() != Qt::white &&
10540  mBackgroundBrush.color() != Qt::transparent &&
10541  mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
10542  printpainter.fillRect(viewport(), mBackgroundBrush);
10543  draw(&printpainter);
10544  printpainter.end();
10545  success = true;
10546  }
10547  setViewport(oldViewport);
10548 #endif // QT_NO_PRINTER
10549  return success;
10550 }
10551 
10589 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10590 {
10591  return saveRastered(fileName, width, height, scale, "PNG", quality);
10592 }
10593 
10628 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
10629 {
10630  return saveRastered(fileName, width, height, scale, "JPG", quality);
10631 }
10632 
10664 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
10665 {
10666  return saveRastered(fileName, width, height, scale, "BMP");
10667 }
10668 
10678 {
10679  return mPlotLayout->minimumSizeHint();
10680 }
10681 
10688 {
10689  return mPlotLayout->minimumSizeHint();
10690 }
10691 
10697 void QCustomPlot::paintEvent(QPaintEvent *event)
10698 {
10699  Q_UNUSED(event);
10700  QPainter painter(this);
10701  painter.drawPixmap(0, 0, mPaintBuffer);
10702 }
10703 
10710 void QCustomPlot::resizeEvent(QResizeEvent *event)
10711 {
10712  // resize and repaint the buffer:
10713  mPaintBuffer = QPixmap(event->size());
10714  setViewport(rect());
10715  replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10716 }
10717 
10727 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
10728 {
10729  emit mouseDoubleClick(event);
10730 
10731  QVariant details;
10732  QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10733 
10734  // emit specialized object double click signals:
10735  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10736  emit plottableDoubleClick(ap, event);
10737  else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10738  emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10739  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10740  emit itemDoubleClick(ai, event);
10741  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10742  emit legendDoubleClick(lg, 0, event);
10743  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10744  emit legendDoubleClick(li->parentLegend(), li, event);
10745  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10746  emit titleDoubleClick(event, pt);
10747 
10748  // call double click event of affected layout element:
10749  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10750  el->mouseDoubleClickEvent(event);
10751 
10752  // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
10753  if (mMouseEventElement)
10754  {
10755  mMouseEventElement->mouseReleaseEvent(event);
10756  mMouseEventElement = 0;
10757  }
10758 
10759  //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
10760 }
10761 
10769 void QCustomPlot::mousePressEvent(QMouseEvent *event)
10770 {
10771  emit mousePress(event);
10772  mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
10773 
10774  // call event of affected layout element:
10775  mMouseEventElement = layoutElementAt(event->pos());
10776  if (mMouseEventElement)
10777  mMouseEventElement->mousePressEvent(event);
10778 
10779  QWidget::mousePressEvent(event);
10780 }
10781 
10791 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
10792 {
10793  emit mouseMove(event);
10794 
10795  // call event of affected layout element:
10796  if (mMouseEventElement)
10797  mMouseEventElement->mouseMoveEvent(event);
10798 
10799  QWidget::mouseMoveEvent(event);
10800 }
10801 
10816 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
10817 {
10818  emit mouseRelease(event);
10819  bool doReplot = false;
10820 
10821  if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
10822  {
10823  if (event->button() == Qt::LeftButton)
10824  {
10825  // handle selection mechanism:
10826  QVariant details;
10827  QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10828  bool selectionStateChanged = false;
10829  bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
10830  // deselect all other layerables if not additive selection:
10831  if (!additive)
10832  {
10833  foreach (QCPLayer *layer, mLayers)
10834  {
10835  foreach (QCPLayerable *layerable, layer->children())
10836  {
10837  if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
10838  {
10839  bool selChanged = false;
10840  layerable->deselectEvent(&selChanged);
10841  selectionStateChanged |= selChanged;
10842  }
10843  }
10844  }
10845  }
10846  if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10847  {
10848  // a layerable was actually clicked, call its selectEvent:
10849  bool selChanged = false;
10850  clickedLayerable->selectEvent(event, additive, details, &selChanged);
10851  selectionStateChanged |= selChanged;
10852  }
10853  doReplot = true;
10854  if (selectionStateChanged)
10855  emit selectionChangedByUser();
10856  }
10857 
10858  // emit specialized object click signals:
10859  QVariant details;
10860  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
10861  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10862  emit plottableClick(ap, event);
10863  else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10864  emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10865  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10866  emit itemClick(ai, event);
10867  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10868  emit legendClick(lg, 0, event);
10869  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10870  emit legendClick(li->parentLegend(), li, event);
10871  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10872  emit titleClick(event, pt);
10873  }
10874 
10875  // call event of affected layout element:
10876  if (mMouseEventElement)
10877  {
10878  mMouseEventElement->mouseReleaseEvent(event);
10879  mMouseEventElement = 0;
10880  }
10881 
10882  if (doReplot || noAntialiasingOnDrag())
10883  replot();
10884 
10885  QWidget::mouseReleaseEvent(event);
10886 }
10887 
10894 void QCustomPlot::wheelEvent(QWheelEvent *event)
10895 {
10896  emit mouseWheel(event);
10897 
10898  // call event of affected layout element:
10899  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10900  el->wheelEvent(event);
10901 
10902  QWidget::wheelEvent(event);
10903 }
10904 
10913 {
10914  // run through layout phases:
10918 
10919  // draw viewport background pixmap:
10920  drawBackground(painter);
10921 
10922  // draw all layered objects (grid, axes, plottables, items, legend,...):
10923  foreach (QCPLayer *layer, mLayers)
10924  {
10925  foreach (QCPLayerable *child, layer->children())
10926  {
10927  if (child->realVisibility())
10928  {
10929  painter->save();
10930  painter->setClipRect(child->clipRect().translated(0, -1));
10931  child->applyDefaultAntialiasingHint(painter);
10932  child->draw(painter);
10933  painter->restore();
10934  }
10935  }
10936  }
10937 
10938  /* Debug code to draw all layout element rects
10939  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
10940  {
10941  painter->setBrush(Qt::NoBrush);
10942  painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
10943  painter->drawRect(el->rect());
10944  painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
10945  painter->drawRect(el->outerRect());
10946  }
10947  */
10948 }
10949 
10968 {
10969  // Note: background color is handled in individual replot/save functions
10970 
10971  // draw background pixmap (on top of fill, if brush specified):
10972  if (!mBackgroundPixmap.isNull())
10973  {
10974  if (mBackgroundScaled)
10975  {
10976  // check whether mScaledBackground needs to be updated:
10977  QSize scaledSize(mBackgroundPixmap.size());
10978  scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
10979  if (mScaledBackgroundPixmap.size() != scaledSize)
10980  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
10981  painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
10982  } else
10983  {
10984  painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
10985  }
10986  }
10987 }
10988 
10989 
10996 {
10997  if (xAxis == axis)
10998  xAxis = 0;
10999  if (xAxis2 == axis)
11000  xAxis2 = 0;
11001  if (yAxis == axis)
11002  yAxis = 0;
11003  if (yAxis2 == axis)
11004  yAxis2 = 0;
11005 
11006  // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
11007 }
11008 
11015 {
11016  if (this->legend == legend)
11017  this->legend = 0;
11018 }
11019 
11027 {
11028  for (int i=0; i<mLayers.size(); ++i)
11029  mLayers.at(i)->mIndex = i;
11030 }
11031 
11044 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
11045 {
11046  for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
11047  {
11048  const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
11049  double minimumDistance = selectionTolerance()*1.1;
11050  QCPLayerable *minimumDistanceLayerable = 0;
11051  for (int i=layerables.size()-1; i>=0; --i)
11052  {
11053  if (!layerables.at(i)->realVisibility())
11054  continue;
11055  QVariant details;
11056  double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11057  if (dist >= 0 && dist < minimumDistance)
11058  {
11059  minimumDistance = dist;
11060  minimumDistanceLayerable = layerables.at(i);
11061  if (selectionDetails) *selectionDetails = details;
11062  }
11063  }
11064  if (minimumDistance < selectionTolerance())
11065  return minimumDistanceLayerable;
11066  }
11067  return 0;
11068 }
11069 
11081 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
11082 {
11083  QPixmap buffer = toPixmap(width, height, scale);
11084  if (!buffer.isNull())
11085  return buffer.save(fileName, format, quality);
11086  else
11087  return false;
11088 }
11089 
11098 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
11099 {
11100  // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11101  int newWidth, newHeight;
11102  if (width == 0 || height == 0)
11103  {
11104  newWidth = this->width();
11105  newHeight = this->height();
11106  } else
11107  {
11108  newWidth = width;
11109  newHeight = height;
11110  }
11111  int scaledWidth = qRound(scale*newWidth);
11112  int scaledHeight = qRound(scale*newHeight);
11113 
11114  QPixmap result(scaledWidth, scaledHeight);
11115  result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
11116  QCPPainter painter;
11117  painter.begin(&result);
11118  if (painter.isActive())
11119  {
11120  QRect oldViewport = viewport();
11121  setViewport(QRect(0, 0, newWidth, newHeight));
11123  if (!qFuzzyCompare(scale, 1.0))
11124  {
11125  if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
11127  painter.scale(scale, scale);
11128  }
11129  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
11130  painter.fillRect(mViewport, mBackgroundBrush);
11131  draw(&painter);
11132  setViewport(oldViewport);
11133  painter.end();
11134  } else // might happen if pixmap has width or height zero
11135  {
11136  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11137  return QPixmap();
11138  }
11139  return result;
11140 }
11141 
11154 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
11155 {
11156  // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11157  int newWidth, newHeight;
11158  if (width == 0 || height == 0)
11159  {
11160  newWidth = this->width();
11161  newHeight = this->height();
11162  } else
11163  {
11164  newWidth = width;
11165  newHeight = height;
11166  }
11167 
11168  if (painter->isActive())
11169  {
11170  QRect oldViewport = viewport();
11171  setViewport(QRect(0, 0, newWidth, newHeight));
11172  painter->setMode(QCPPainter::pmNoCaching);
11173  // warning: the following is different in toPixmap, because a solid background color is applied there via QPixmap::fill
11174  // here, we need to do this via QPainter::fillRect.
11175  if (mBackgroundBrush.style() != Qt::NoBrush)
11176  painter->fillRect(mViewport, mBackgroundBrush);
11177  draw(painter);
11178  setViewport(oldViewport);
11179  } else
11180  qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11181 }
11182 
11183 
11187 
11221  mLevelCount(350),
11222  mColorInterpolation(ciRGB),
11223  mPeriodic(false),
11224  mColorBufferInvalidated(true)
11225 {
11226  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11227  loadPreset(preset);
11228 }
11229 
11230 /* undocumented operator */
11232 {
11233  return ((other.mLevelCount == this->mLevelCount) &&
11234  (other.mColorInterpolation == this->mColorInterpolation) &&
11235  (other.mPeriodic == this->mPeriodic) &&
11236  (other.mColorStops == this->mColorStops));
11237 }
11238 
11246 {
11247  if (n < 2)
11248  {
11249  qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11250  n = 2;
11251  }
11252  if (n != mLevelCount)
11253  {
11254  mLevelCount = n;
11255  mColorBufferInvalidated = true;
11256  }
11257 }
11258 
11269 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11270 {
11272  mColorBufferInvalidated = true;
11273 }
11274 
11281 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
11282 {
11283  mColorStops.insert(position, color);
11284  mColorBufferInvalidated = true;
11285 }
11286 
11295 {
11296  if (interpolation != mColorInterpolation)
11297  {
11298  mColorInterpolation = interpolation;
11299  mColorBufferInvalidated = true;
11300  }
11301 }
11302 
11319 {
11320  mPeriodic = enabled;
11321 }
11322 
11335 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
11336 {
11337  // If you change something here, make sure to also adapt ::color()
11338  if (!data)
11339  {
11340  qDebug() << Q_FUNC_INFO << "null pointer given as data";
11341  return;
11342  }
11343  if (!scanLine)
11344  {
11345  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11346  return;
11347  }
11350 
11351  if (!logarithmic)
11352  {
11353  const double posToIndexFactor = mLevelCount/range.size();
11354  if (mPeriodic)
11355  {
11356  for (int i=0; i<n; ++i)
11357  {
11358  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
11359  if (index < 0)
11360  index += mLevelCount;
11361  scanLine[i] = mColorBuffer.at(index);
11362  }
11363  } else
11364  {
11365  for (int i=0; i<n; ++i)
11366  {
11367  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
11368  if (index < 0)
11369  index = 0;
11370  else if (index >= mLevelCount)
11371  index = mLevelCount-1;
11372  scanLine[i] = mColorBuffer.at(index);
11373  }
11374  }
11375  } else // logarithmic == true
11376  {
11377  if (mPeriodic)
11378  {
11379  for (int i=0; i<n; ++i)
11380  {
11381  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*mLevelCount) % mLevelCount;
11382  if (index < 0)
11383  index += mLevelCount;
11384  scanLine[i] = mColorBuffer.at(index);
11385  }
11386  } else
11387  {
11388  for (int i=0; i<n; ++i)
11389  {
11390  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*mLevelCount;
11391  if (index < 0)
11392  index = 0;
11393  else if (index >= mLevelCount)
11394  index = mLevelCount-1;
11395  scanLine[i] = mColorBuffer.at(index);
11396  }
11397  }
11398  }
11399 }
11400 
11410 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11411 {
11412  // If you change something here, make sure to also adapt ::colorize()
11415  int index = 0;
11416  if (!logarithmic)
11417  index = (position-range.lower)*mLevelCount/range.size();
11418  else
11419  index = qLn(position/range.lower)/qLn(range.upper/range.lower)*mLevelCount;
11420  if (mPeriodic)
11421  {
11422  index = index % mLevelCount;
11423  if (index < 0)
11424  index += mLevelCount;
11425  } else
11426  {
11427  if (index < 0)
11428  index = 0;
11429  else if (index >= mLevelCount)
11430  index = mLevelCount-1;
11431  }
11432  return mColorBuffer.at(index);
11433 }
11434 
11443 {
11444  clearColorStops();
11445  switch (preset)
11446  {
11447  case gpGrayscale:
11449  setColorStopAt(0, Qt::black);
11451  break;
11452  case gpHot:
11454  setColorStopAt(0, QColor(50, 0, 0));
11455  setColorStopAt(0.2, QColor(180, 10, 0));
11456  setColorStopAt(0.4, QColor(245, 50, 0));
11457  setColorStopAt(0.6, QColor(255, 150, 10));
11458  setColorStopAt(0.8, QColor(255, 255, 50));
11459  setColorStopAt(1, QColor(255, 255, 255));
11460  break;
11461  case gpCold:
11463  setColorStopAt(0, QColor(0, 0, 50));
11464  setColorStopAt(0.2, QColor(0, 10, 180));
11465  setColorStopAt(0.4, QColor(0, 50, 245));
11466  setColorStopAt(0.6, QColor(10, 150, 255));
11467  setColorStopAt(0.8, QColor(50, 255, 255));
11468  setColorStopAt(1, QColor(255, 255, 255));
11469  break;
11470  case gpNight:
11472  setColorStopAt(0, QColor(10, 20, 30));
11473  setColorStopAt(1, QColor(250, 255, 250));
11474  break;
11475  case gpCandy:
11477  setColorStopAt(0, QColor(0, 0, 255));
11478  setColorStopAt(1, QColor(255, 250, 250));
11479  break;
11480  case gpGeography:
11482  setColorStopAt(0, QColor(70, 170, 210));
11483  setColorStopAt(0.20, QColor(90, 160, 180));
11484  setColorStopAt(0.25, QColor(45, 130, 175));
11485  setColorStopAt(0.30, QColor(100, 140, 125));
11486  setColorStopAt(0.5, QColor(100, 140, 100));
11487  setColorStopAt(0.6, QColor(130, 145, 120));
11488  setColorStopAt(0.7, QColor(140, 130, 120));
11489  setColorStopAt(0.9, QColor(180, 190, 190));
11490  setColorStopAt(1, QColor(210, 210, 230));
11491  break;
11492  case gpIon:
11494  setColorStopAt(0, QColor(50, 10, 10));
11495  setColorStopAt(0.45, QColor(0, 0, 255));
11496  setColorStopAt(0.8, QColor(0, 255, 255));
11497  setColorStopAt(1, QColor(0, 255, 0));
11498  break;
11499  case gpThermal:
11501  setColorStopAt(0, QColor(0, 0, 50));
11502  setColorStopAt(0.15, QColor(20, 0, 120));
11503  setColorStopAt(0.33, QColor(200, 30, 140));
11504  setColorStopAt(0.6, QColor(255, 100, 0));
11505  setColorStopAt(0.85, QColor(255, 255, 40));
11506  setColorStopAt(1, QColor(255, 255, 255));
11507  break;
11508  case gpPolar:
11510  setColorStopAt(0, QColor(50, 255, 255));
11511  setColorStopAt(0.18, QColor(10, 70, 255));
11512  setColorStopAt(0.28, QColor(10, 10, 190));
11513  setColorStopAt(0.5, QColor(0, 0, 0));
11514  setColorStopAt(0.72, QColor(190, 10, 10));
11515  setColorStopAt(0.82, QColor(255, 70, 10));
11516  setColorStopAt(1, QColor(255, 255, 50));
11517  break;
11518  case gpSpectrum:
11520  setColorStopAt(0, QColor(50, 0, 50));
11521  setColorStopAt(0.15, QColor(0, 0, 255));
11522  setColorStopAt(0.35, QColor(0, 255, 255));
11523  setColorStopAt(0.6, QColor(255, 255, 0));
11524  setColorStopAt(0.75, QColor(255, 30, 0));
11525  setColorStopAt(1, QColor(50, 0, 0));
11526  break;
11527  case gpJet:
11529  setColorStopAt(0, QColor(0, 0, 100));
11530  setColorStopAt(0.15, QColor(0, 50, 255));
11531  setColorStopAt(0.35, QColor(0, 255, 255));
11532  setColorStopAt(0.65, QColor(255, 255, 0));
11533  setColorStopAt(0.85, QColor(255, 30, 0));
11534  setColorStopAt(1, QColor(100, 0, 0));
11535  break;
11536  case gpHues:
11538  setColorStopAt(0, QColor(255, 0, 0));
11539  setColorStopAt(1.0/3.0, QColor(0, 0, 255));
11540  setColorStopAt(2.0/3.0, QColor(0, 255, 0));
11541  setColorStopAt(1, QColor(255, 0, 0));
11542  break;
11543  }
11544 }
11545 
11552 {
11553  mColorStops.clear();
11554  mColorBufferInvalidated = true;
11555 }
11556 
11564 {
11565  QCPColorGradient result(*this);
11566  result.clearColorStops();
11567  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
11568  result.setColorStopAt(1.0-it.key(), it.value());
11569  return result;
11570 }
11571 
11578 {
11579  if (mColorBuffer.size() != mLevelCount)
11580  mColorBuffer.resize(mLevelCount);
11581  if (mColorStops.size() > 1)
11582  {
11583  double indexToPosFactor = 1.0/(double)(mLevelCount-1);
11584  for (int i=0; i<mLevelCount; ++i)
11585  {
11586  double position = i*indexToPosFactor;
11587  QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
11588  if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
11589  {
11590  mColorBuffer[i] = (it-1).value().rgb();
11591  } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
11592  {
11593  mColorBuffer[i] = it.value().rgb();
11594  } else // position is in between stops (or on an intermediate stop), interpolate color
11595  {
11596  QMap<double, QColor>::const_iterator high = it;
11597  QMap<double, QColor>::const_iterator low = it-1;
11598  double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
11599  switch (mColorInterpolation)
11600  {
11601  case ciRGB:
11602  {
11603  mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
11604  (1-t)*low.value().green() + t*high.value().green(),
11605  (1-t)*low.value().blue() + t*high.value().blue());
11606  break;
11607  }
11608  case ciHSV:
11609  {
11610  QColor lowHsv = low.value().toHsv();
11611  QColor highHsv = high.value().toHsv();
11612  double hue = 0;
11613  double hueDiff = highHsv.hueF()-lowHsv.hueF();
11614  if (hueDiff > 0.5)
11615  hue = lowHsv.hueF() - t*(1.0-hueDiff);
11616  else if (hueDiff < -0.5)
11617  hue = lowHsv.hueF() + t*(1.0+hueDiff);
11618  else
11619  hue = lowHsv.hueF() + t*hueDiff;
11620  if (hue < 0) hue += 1.0;
11621  else if (hue >= 1.0) hue -= 1.0;
11622  mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
11623  break;
11624  }
11625  }
11626  }
11627  }
11628  } else if (mColorStops.size() == 1)
11629  {
11630  mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11631  } else // mColorStops is empty, fill color buffer with black
11632  {
11633  mColorBuffer.fill(qRgb(0, 0, 0));
11634  }
11635  mColorBufferInvalidated = false;
11636 }
11637 
11638 
11642 
11680 /* start documentation of inline functions */
11681 
11762 /* end documentation of inline functions */
11763 
11768 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
11769  QCPLayoutElement(parentPlot),
11770  mBackgroundBrush(Qt::NoBrush),
11771  mBackgroundScaled(true),
11772  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
11773  mInsetLayout(new QCPLayoutInset),
11774  mRangeDrag(Qt::Horizontal|Qt::Vertical),
11775  mRangeZoom(Qt::Horizontal|Qt::Vertical),
11776  mRangeZoomFactorHorz(0.85),
11777  mRangeZoomFactorVert(0.85),
11778  mDragging(false)
11779 {
11782  mInsetLayout->setParent(this);
11783 
11784  setMinimumSize(50, 50);
11785  setMinimumMargins(QMargins(15, 15, 15, 15));
11786  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
11787  mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
11788  mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
11789  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
11790 
11791  if (setupDefaultAxes)
11792  {
11793  QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
11794  QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
11795  QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
11796  QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
11797  setRangeDragAxes(xAxis, yAxis);
11798  setRangeZoomAxes(xAxis, yAxis);
11799  xAxis2->setVisible(false);
11800  yAxis2->setVisible(false);
11801  xAxis->grid()->setVisible(true);
11802  yAxis->grid()->setVisible(true);
11803  xAxis2->grid()->setVisible(false);
11804  yAxis2->grid()->setVisible(false);
11805  xAxis2->grid()->setZeroLinePen(Qt::NoPen);
11806  yAxis2->grid()->setZeroLinePen(Qt::NoPen);
11807  xAxis2->grid()->setVisible(false);
11808  yAxis2->grid()->setVisible(false);
11809  }
11810 }
11811 
11813 {
11814  delete mInsetLayout;
11815  mInsetLayout = 0;
11816 
11817  QList<QCPAxis*> axesList = axes();
11818  for (int i=0; i<axesList.size(); ++i)
11819  removeAxis(axesList.at(i));
11820 }
11821 
11828 {
11829  return mAxes.value(type).size();
11830 }
11831 
11838 {
11839  QList<QCPAxis*> ax(mAxes.value(type));
11840  if (index >= 0 && index < ax.size())
11841  {
11842  return ax.at(index);
11843  } else
11844  {
11845  qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
11846  return 0;
11847  }
11848 }
11849 
11858 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
11859 {
11860  QList<QCPAxis*> result;
11861  if (types.testFlag(QCPAxis::atLeft))
11862  result << mAxes.value(QCPAxis::atLeft);
11863  if (types.testFlag(QCPAxis::atRight))
11864  result << mAxes.value(QCPAxis::atRight);
11865  if (types.testFlag(QCPAxis::atTop))
11866  result << mAxes.value(QCPAxis::atTop);
11867  if (types.testFlag(QCPAxis::atBottom))
11868  result << mAxes.value(QCPAxis::atBottom);
11869  return result;
11870 }
11871 
11876 QList<QCPAxis*> QCPAxisRect::axes() const
11877 {
11878  QList<QCPAxis*> result;
11879  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11880  while (it.hasNext())
11881  {
11882  it.next();
11883  result << it.value();
11884  }
11885  return result;
11886 }
11887 
11898 {
11899  QCPAxis *newAxis = new QCPAxis(this, type);
11900  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
11901  {
11902  bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
11903  newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
11904  newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
11905  }
11906  mAxes[type].append(newAxis);
11907  return newAxis;
11908 }
11909 
11918 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
11919 {
11920  QList<QCPAxis*> result;
11921  if (types.testFlag(QCPAxis::atLeft))
11922  result << addAxis(QCPAxis::atLeft);
11923  if (types.testFlag(QCPAxis::atRight))
11924  result << addAxis(QCPAxis::atRight);
11925  if (types.testFlag(QCPAxis::atTop))
11926  result << addAxis(QCPAxis::atTop);
11927  if (types.testFlag(QCPAxis::atBottom))
11928  result << addAxis(QCPAxis::atBottom);
11929  return result;
11930 }
11931 
11940 {
11941  // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
11942  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11943  while (it.hasNext())
11944  {
11945  it.next();
11946  if (it.value().contains(axis))
11947  {
11948  mAxes[it.key()].removeOne(axis);
11949  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)
11951  delete axis;
11952  return true;
11953  }
11954  }
11955  qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
11956  return false;
11957 }
11958 
11985 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
11986 {
11987  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
11988  if (axisCount(QCPAxis::atBottom) == 0)
11989  xAxis = addAxis(QCPAxis::atBottom);
11990  else
11991  xAxis = axis(QCPAxis::atBottom);
11992 
11993  if (axisCount(QCPAxis::atLeft) == 0)
11994  yAxis = addAxis(QCPAxis::atLeft);
11995  else
11996  yAxis = axis(QCPAxis::atLeft);
11997 
11998  if (axisCount(QCPAxis::atTop) == 0)
11999  xAxis2 = addAxis(QCPAxis::atTop);
12000  else
12001  xAxis2 = axis(QCPAxis::atTop);
12002 
12003  if (axisCount(QCPAxis::atRight) == 0)
12004  yAxis2 = addAxis(QCPAxis::atRight);
12005  else
12006  yAxis2 = axis(QCPAxis::atRight);
12007 
12008  xAxis->setVisible(true);
12009  yAxis->setVisible(true);
12010  xAxis2->setVisible(true);
12011  yAxis2->setVisible(true);
12012  xAxis2->setTickLabels(false);
12013  yAxis2->setTickLabels(false);
12014 
12015  xAxis2->setRange(xAxis->range());
12016  xAxis2->setRangeReversed(xAxis->rangeReversed());
12017  xAxis2->setScaleType(xAxis->scaleType());
12018  xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12019  xAxis2->setTicks(xAxis->ticks());
12020  xAxis2->setAutoTickCount(xAxis->autoTickCount());
12021  xAxis2->setSubTickCount(xAxis->subTickCount());
12022  xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12023  xAxis2->setTickStep(xAxis->tickStep());
12024  xAxis2->setAutoTickStep(xAxis->autoTickStep());
12025  xAxis2->setNumberFormat(xAxis->numberFormat());
12026  xAxis2->setNumberPrecision(xAxis->numberPrecision());
12027  xAxis2->setTickLabelType(xAxis->tickLabelType());
12028  xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12029  xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12030 
12031  yAxis2->setRange(yAxis->range());
12032  yAxis2->setRangeReversed(yAxis->rangeReversed());
12033  yAxis2->setScaleType(yAxis->scaleType());
12034  yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12035  yAxis2->setTicks(yAxis->ticks());
12036  yAxis2->setAutoTickCount(yAxis->autoTickCount());
12037  yAxis2->setSubTickCount(yAxis->subTickCount());
12038  yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12039  yAxis2->setTickStep(yAxis->tickStep());
12040  yAxis2->setAutoTickStep(yAxis->autoTickStep());
12041  yAxis2->setNumberFormat(yAxis->numberFormat());
12042  yAxis2->setNumberPrecision(yAxis->numberPrecision());
12043  yAxis2->setTickLabelType(yAxis->tickLabelType());
12044  yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12045  yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12046 
12047  if (connectRanges)
12048  {
12049  connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
12050  connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
12051  }
12052 }
12053 
12062 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
12063 {
12064  // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
12065  QList<QCPAbstractPlottable*> result;
12066  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
12067  {
12068  if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12069  result.append(mParentPlot->mPlottables.at(i));
12070  }
12071  return result;
12072 }
12073 
12082 QList<QCPGraph*> QCPAxisRect::graphs() const
12083 {
12084  // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
12085  QList<QCPGraph*> result;
12086  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
12087  {
12088  if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12089  result.append(mParentPlot->mGraphs.at(i));
12090  }
12091  return result;
12092 }
12093 
12104 QList<QCPAbstractItem *> QCPAxisRect::items() const
12105 {
12106  // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
12107  // and miss those items that have this axis rect as clipAxisRect.
12108  QList<QCPAbstractItem*> result;
12109  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
12110  {
12111  if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
12112  {
12113  result.append(mParentPlot->mItems.at(itemId));
12114  continue;
12115  }
12116  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
12117  for (int posId=0; posId<positions.size(); ++posId)
12118  {
12119  if (positions.at(posId)->axisRect() == this ||
12120  positions.at(posId)->keyAxis()->axisRect() == this ||
12121  positions.at(posId)->valueAxis()->axisRect() == this)
12122  {
12123  result.append(mParentPlot->mItems.at(itemId));
12124  break;
12125  }
12126  }
12127  }
12128  return result;
12129 }
12130 
12140 {
12141  QCPLayoutElement::update(phase);
12142 
12143  switch (phase)
12144  {
12145  case upPreparation:
12146  {
12147  QList<QCPAxis*> allAxes = axes();
12148  for (int i=0; i<allAxes.size(); ++i)
12149  allAxes.at(i)->setupTickVectors();
12150  break;
12151  }
12152  case upLayout:
12153  {
12155  break;
12156  }
12157  default: break;
12158  }
12159 
12160  // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
12161  mInsetLayout->update(phase);
12162 }
12163 
12164 /* inherits documentation from base class */
12165 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
12166 {
12167  QList<QCPLayoutElement*> result;
12168  if (mInsetLayout)
12169  {
12170  result << mInsetLayout;
12171  if (recursive)
12172  result << mInsetLayout->elements(recursive);
12173  }
12174  return result;
12175 }
12176 
12177 /* inherits documentation from base class */
12179 {
12180  painter->setAntialiasing(false);
12181 }
12182 
12183 /* inherits documentation from base class */
12185 {
12186  drawBackground(painter);
12187 }
12188 
12204 void QCPAxisRect::setBackground(const QPixmap &pm)
12205 {
12206  mBackgroundPixmap = pm;
12207  mScaledBackgroundPixmap = QPixmap();
12208 }
12209 
12223 void QCPAxisRect::setBackground(const QBrush &brush)
12224 {
12225  mBackgroundBrush = brush;
12226 }
12227 
12235 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
12236 {
12237  mBackgroundPixmap = pm;
12238  mScaledBackgroundPixmap = QPixmap();
12239  mBackgroundScaled = scaled;
12240  mBackgroundScaledMode = mode;
12241 }
12242 
12254 {
12255  mBackgroundScaled = scaled;
12256 }
12257 
12263 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12264 {
12265  mBackgroundScaledMode = mode;
12266 }
12267 
12273 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12274 {
12275  return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12276 }
12277 
12283 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12284 {
12285  return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12286 }
12287 
12293 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12294 {
12295  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12296 }
12297 
12314 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12315 {
12316  mRangeDrag = orientations;
12317 }
12318 
12334 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12335 {
12336  mRangeZoom = orientations;
12337 }
12338 
12345 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
12346 {
12347  mRangeDragHorzAxis = horizontal;
12348  mRangeDragVertAxis = vertical;
12349 }
12350 
12358 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
12359 {
12360  mRangeZoomHorzAxis = horizontal;
12361  mRangeZoomVertAxis = vertical;
12362 }
12363 
12374 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12375 {
12376  mRangeZoomFactorHorz = horizontalFactor;
12377  mRangeZoomFactorVert = verticalFactor;
12378 }
12379 
12385 {
12386  mRangeZoomFactorHorz = factor;
12387  mRangeZoomFactorVert = factor;
12388 }
12389 
12409 {
12410  // draw background fill:
12411  if (mBackgroundBrush != Qt::NoBrush)
12412  painter->fillRect(mRect, mBackgroundBrush);
12413 
12414  // draw background pixmap (on top of fill, if brush specified):
12415  if (!mBackgroundPixmap.isNull())
12416  {
12417  if (mBackgroundScaled)
12418  {
12419  // check whether mScaledBackground needs to be updated:
12420  QSize scaledSize(mBackgroundPixmap.size());
12421  scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12422  if (mScaledBackgroundPixmap.size() != scaledSize)
12423  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12424  painter->drawPixmap(mRect.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
12425  } else
12426  {
12427  painter->drawPixmap(mRect.topLeft(), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
12428  }
12429  }
12430 }
12431 
12443 {
12444  const QList<QCPAxis*> axesList = mAxes.value(type);
12445  if (axesList.isEmpty())
12446  return;
12447 
12448  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
12449  for (int i=1; i<axesList.size(); ++i)
12450  {
12451  int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
12452  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)
12453  {
12454  if (!isFirstVisible)
12455  offset += axesList.at(i)->tickLengthIn();
12456  isFirstVisible = false;
12457  }
12458  axesList.at(i)->setOffset(offset);
12459  }
12460 }
12461 
12462 /* inherits documentation from base class */
12464 {
12465  if (!mAutoMargins.testFlag(side))
12466  qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12467 
12469 
12470  // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
12471  const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
12472  if (axesList.size() > 0)
12473  return axesList.last()->offset() + axesList.last()->calculateMargin();
12474  else
12475  return 0;
12476 }
12477 
12489 void QCPAxisRect::mousePressEvent(QMouseEvent *event)
12490 {
12491  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)
12492  if (event->buttons() & Qt::LeftButton)
12493  {
12494  mDragging = true;
12495  // initialize antialiasing backup in case we start dragging:
12497  {
12500  }
12501  // Mouse range dragging interaction:
12502  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12503  {
12504  if (mRangeDragHorzAxis)
12505  mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12506  if (mRangeDragVertAxis)
12507  mDragStartVertRange = mRangeDragVertAxis.data()->range();
12508  }
12509  }
12510 }
12511 
12519 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
12520 {
12521  // Mouse range dragging interaction:
12522  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12523  {
12524  if (mRangeDrag.testFlag(Qt::Horizontal))
12525  {
12526  if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
12527  {
12528  if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
12529  {
12530  double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
12531  rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
12532  } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
12533  {
12534  double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
12535  rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
12536  }
12537  }
12538  }
12539  if (mRangeDrag.testFlag(Qt::Vertical))
12540  {
12541  if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
12542  {
12543  if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
12544  {
12545  double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
12546  rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
12547  } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
12548  {
12549  double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
12550  rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
12551  }
12552  }
12553  }
12554  if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
12555  {
12558  mParentPlot->replot();
12559  }
12560  }
12561 }
12562 
12563 /* inherits documentation from base class */
12564 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
12565 {
12566  Q_UNUSED(event)
12567  mDragging = false;
12569  {
12572  }
12573 }
12574 
12589 void QCPAxisRect::wheelEvent(QWheelEvent *event)
12590 {
12591  // Mouse range zooming interaction:
12592  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
12593  {
12594  if (mRangeZoom != 0)
12595  {
12596  double factor;
12597  double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
12598  if (mRangeZoom.testFlag(Qt::Horizontal))
12599  {
12600  factor = pow(mRangeZoomFactorHorz, wheelSteps);
12601  if (mRangeZoomHorzAxis.data())
12602  mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
12603  }
12604  if (mRangeZoom.testFlag(Qt::Vertical))
12605  {
12606  factor = pow(mRangeZoomFactorVert, wheelSteps);
12607  if (mRangeZoomVertAxis.data())
12608  mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
12609  }
12610  mParentPlot->replot();
12611  }
12612  }
12613 }
12614 
12615 
12619 
12644 /* start of documentation of signals */
12645 
12652 /* end of documentation of signals */
12653 
12659  QCPLayoutElement(parent->parentPlot()),
12660  mParentLegend(parent),
12661  mFont(parent->font()),
12662  mTextColor(parent->textColor()),
12663  mSelectedFont(parent->selectedFont()),
12664  mSelectedTextColor(parent->selectedTextColor()),
12665  mSelectable(true),
12666  mSelected(false)
12667 {
12668  setLayer("legend");
12669  setMargins(QMargins(8, 2, 8, 2));
12670 }
12671 
12677 void QCPAbstractLegendItem::setFont(const QFont &font)
12678 {
12679  mFont = font;
12680 }
12681 
12687 void QCPAbstractLegendItem::setTextColor(const QColor &color)
12688 {
12689  mTextColor = color;
12690 }
12691 
12699 {
12700  mSelectedFont = font;
12701 }
12702 
12710 {
12711  mSelectedTextColor = color;
12712 }
12713 
12720 {
12721  if (mSelectable != selectable)
12722  {
12725  }
12726 }
12727 
12737 {
12738  if (mSelected != selected)
12739  {
12740  mSelected = selected;
12742  }
12743 }
12744 
12745 /* inherits documentation from base class */
12746 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
12747 {
12748  Q_UNUSED(details)
12749  if (!mParentPlot) return -1;
12750  if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
12751  return -1;
12752 
12753  if (mRect.contains(pos.toPoint()))
12754  return mParentPlot->selectionTolerance()*0.99;
12755  else
12756  return -1;
12757 }
12758 
12759 /* inherits documentation from base class */
12761 {
12763 }
12764 
12765 /* inherits documentation from base class */
12767 {
12768  return mOuterRect;
12769 }
12770 
12771 /* inherits documentation from base class */
12772 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12773 {
12774  Q_UNUSED(event)
12775  Q_UNUSED(details)
12777  {
12778  bool selBefore = mSelected;
12779  setSelected(additive ? !mSelected : true);
12780  if (selectionStateChanged)
12781  *selectionStateChanged = mSelected != selBefore;
12782  }
12783 }
12784 
12785 /* inherits documentation from base class */
12786 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
12787 {
12789  {
12790  bool selBefore = mSelected;
12791  setSelected(false);
12792  if (selectionStateChanged)
12793  *selectionStateChanged = mSelected != selBefore;
12794  }
12795 }
12796 
12800 
12835  QCPAbstractLegendItem(parent),
12836  mPlottable(plottable)
12837 {
12838 }
12839 
12846 {
12848 }
12849 
12856 {
12858 }
12859 
12866 {
12867  return mSelected ? mSelectedFont : mFont;
12868 }
12869 
12877 {
12878  if (!mPlottable) return;
12879  painter->setFont(getFont());
12880  painter->setPen(QPen(getTextColor()));
12881  QSizeF iconSize = mParentLegend->iconSize();
12882  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12883  QRectF iconRect(mRect.topLeft(), iconSize);
12884  int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
12885  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
12886  // draw icon:
12887  painter->save();
12888  painter->setClipRect(iconRect, Qt::IntersectClip);
12889  mPlottable->drawLegendIcon(painter, iconRect);
12890  painter->restore();
12891  // draw icon border:
12892  if (getIconBorderPen().style() != Qt::NoPen)
12893  {
12894  painter->setPen(getIconBorderPen());
12895  painter->setBrush(Qt::NoBrush);
12896  painter->drawRect(iconRect);
12897  }
12898 }
12899 
12906 {
12907  if (!mPlottable) return QSize();
12908  QSize result(0, 0);
12909  QRect textRect;
12910  QFontMetrics fontMetrics(getFont());
12911  QSize iconSize = mParentLegend->iconSize();
12912  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12913  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
12914  result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
12915  return result;
12916 }
12917 
12918 
12922 
12947 /* start of documentation of signals */
12948 
12956 /* end of documentation of signals */
12957 
12965 {
12966  setRowSpacing(0);
12967  setColumnSpacing(10);
12968  setMargins(QMargins(2, 3, 2, 2));
12969  setAntialiased(false);
12970  setIconSize(32, 18);
12971 
12972  setIconTextPadding(7);
12973 
12976 
12977  setBorderPen(QPen(Qt::black));
12978  setSelectedBorderPen(QPen(Qt::blue, 2));
12979  setIconBorderPen(Qt::NoPen);
12980  setSelectedIconBorderPen(QPen(Qt::blue, 2));
12983  setTextColor(Qt::black);
12984  setSelectedTextColor(Qt::blue);
12985 }
12986 
12988 {
12989  clearItems();
12990  if (mParentPlot)
12991  mParentPlot->legendRemoved(this);
12992 }
12993 
12994 /* no doc for getter, see setSelectedParts */
12995 QCPLegend::SelectableParts QCPLegend::selectedParts() const
12996 {
12997  // check whether any legend elements selected, if yes, add spItems to return value
12998  bool hasSelectedItems = false;
12999  for (int i=0; i<itemCount(); ++i)
13000  {
13001  if (item(i) && item(i)->selected())
13002  {
13003  hasSelectedItems = true;
13004  break;
13005  }
13006  }
13007  if (hasSelectedItems)
13008  return mSelectedParts | spItems;
13009  else
13010  return mSelectedParts & ~spItems;
13011 }
13012 
13016 void QCPLegend::setBorderPen(const QPen &pen)
13017 {
13018  mBorderPen = pen;
13019 }
13020 
13024 void QCPLegend::setBrush(const QBrush &brush)
13025 {
13026  mBrush = brush;
13027 }
13028 
13038 void QCPLegend::setFont(const QFont &font)
13039 {
13040  mFont = font;
13041  for (int i=0; i<itemCount(); ++i)
13042  {
13043  if (item(i))
13044  item(i)->setFont(mFont);
13045  }
13046 }
13047 
13057 void QCPLegend::setTextColor(const QColor &color)
13058 {
13059  mTextColor = color;
13060  for (int i=0; i<itemCount(); ++i)
13061  {
13062  if (item(i))
13063  item(i)->setTextColor(color);
13064  }
13065 }
13066 
13071 void QCPLegend::setIconSize(const QSize &size)
13072 {
13073  mIconSize = size;
13074 }
13075 
13078 void QCPLegend::setIconSize(int width, int height)
13079 {
13080  mIconSize.setWidth(width);
13081  mIconSize.setHeight(height);
13082 }
13083 
13090 {
13091  mIconTextPadding = padding;
13092 }
13093 
13100 void QCPLegend::setIconBorderPen(const QPen &pen)
13101 {
13102  mIconBorderPen = pen;
13103 }
13104 
13115 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13116 {
13117  if (mSelectableParts != selectable)
13118  {
13119  mSelectableParts = selectable;
13121  }
13122 }
13123 
13145 void QCPLegend::setSelectedParts(const SelectableParts &selected)
13146 {
13147  SelectableParts newSelected = selected;
13148  mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
13149 
13150  if (mSelectedParts != newSelected)
13151  {
13152  if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
13153  {
13154  qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
13155  newSelected &= ~spItems;
13156  }
13157  if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
13158  {
13159  for (int i=0; i<itemCount(); ++i)
13160  {
13161  if (item(i))
13162  item(i)->setSelected(false);
13163  }
13164  }
13165  mSelectedParts = newSelected;
13167  }
13168 }
13169 
13176 void QCPLegend::setSelectedBorderPen(const QPen &pen)
13177 {
13178  mSelectedBorderPen = pen;
13179 }
13180 
13187 {
13188  mSelectedIconBorderPen = pen;
13189 }
13190 
13197 void QCPLegend::setSelectedBrush(const QBrush &brush)
13198 {
13200 }
13201 
13209 void QCPLegend::setSelectedFont(const QFont &font)
13210 {
13211  mSelectedFont = font;
13212  for (int i=0; i<itemCount(); ++i)
13213  {
13214  if (item(i))
13215  item(i)->setSelectedFont(font);
13216  }
13217 }
13218 
13226 void QCPLegend::setSelectedTextColor(const QColor &color)
13227 {
13228  mSelectedTextColor = color;
13229  for (int i=0; i<itemCount(); ++i)
13230  {
13231  if (item(i))
13232  item(i)->setSelectedTextColor(color);
13233  }
13234 }
13235 
13242 {
13243  return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13244 }
13245 
13253 {
13254  for (int i=0; i<itemCount(); ++i)
13255  {
13256  if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
13257  {
13258  if (pli->plottable() == plottable)
13259  return pli;
13260  }
13261  }
13262  return 0;
13263 }
13264 
13270 {
13271  return elementCount();
13272 }
13273 
13278 {
13279  for (int i=0; i<itemCount(); ++i)
13280  {
13281  if (item == this->item(i))
13282  return true;
13283  }
13284  return false;
13285 }
13286 
13294 {
13295  return itemWithPlottable(plottable);
13296 }
13297 
13306 {
13307  if (!hasItem(item))
13308  {
13309  return addElement(rowCount(), 0, item);
13310  } else
13311  return false;
13312 }
13313 
13321 bool QCPLegend::removeItem(int index)
13322 {
13323  if (QCPAbstractLegendItem *ali = item(index))
13324  {
13325  bool success = remove(ali);
13326  simplify();
13327  return success;
13328  } else
13329  return false;
13330 }
13331 
13341 {
13342  bool success = remove(item);
13343  simplify();
13344  return success;
13345 }
13346 
13351 {
13352  for (int i=itemCount()-1; i>=0; --i)
13353  removeItem(i);
13354 }
13355 
13362 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
13363 {
13364  QList<QCPAbstractLegendItem*> result;
13365  for (int i=0; i<itemCount(); ++i)
13366  {
13367  if (QCPAbstractLegendItem *ali = item(i))
13368  {
13369  if (ali->selected())
13370  result.append(ali);
13371  }
13372  }
13373  return result;
13374 }
13375 
13390 {
13392 }
13393 
13400 {
13402 }
13403 
13409 QBrush QCPLegend::getBrush() const
13410 {
13411  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13412 }
13413 
13420 {
13421  // draw background rect:
13422  painter->setBrush(getBrush());
13423  painter->setPen(getBorderPen());
13424  painter->drawRect(mOuterRect);
13425 }
13426 
13427 /* inherits documentation from base class */
13428 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13429 {
13430  if (!mParentPlot) return -1;
13431  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
13432  return -1;
13433 
13434  if (mOuterRect.contains(pos.toPoint()))
13435  {
13436  if (details) details->setValue(spLegendBox);
13437  return mParentPlot->selectionTolerance()*0.99;
13438  }
13439  return -1;
13440 }
13441 
13442 /* inherits documentation from base class */
13443 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13444 {
13445  Q_UNUSED(event)
13446  mSelectedParts = selectedParts(); // in case item selection has changed
13447  if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
13448  {
13449  SelectableParts selBefore = mSelectedParts;
13450  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)
13451  if (selectionStateChanged)
13452  *selectionStateChanged = mSelectedParts != selBefore;
13453  }
13454 }
13455 
13456 /* inherits documentation from base class */
13457 void QCPLegend::deselectEvent(bool *selectionStateChanged)
13458 {
13459  mSelectedParts = selectedParts(); // in case item selection has changed
13460  if (mSelectableParts.testFlag(spLegendBox))
13461  {
13462  SelectableParts selBefore = mSelectedParts;
13464  if (selectionStateChanged)
13465  *selectionStateChanged = mSelectedParts != selBefore;
13466  }
13467 }
13468 
13469 /* inherits documentation from base class */
13471 {
13472  return QCP::iSelectLegend;
13473 }
13474 
13475 /* inherits documentation from base class */
13477 {
13478  return QCP::iSelectLegend;
13479 }
13480 
13481 /* inherits documentation from base class */
13483 {
13484  Q_UNUSED(parentPlot)
13485 }
13486 
13487 
13491 
13510 /* start documentation of signals */
13511 
13520 /* end documentation of signals */
13521 
13528  QCPLayoutElement(parentPlot),
13529  mFont(QFont("sans serif", 13*1.5, QFont::Bold)),
13530  mTextColor(Qt::black),
13531  mSelectedFont(QFont("sans serif", 13*1.6, QFont::Bold)),
13532  mSelectedTextColor(Qt::blue),
13533  mSelectable(false),
13534  mSelected(false)
13535 {
13536  if (parentPlot)
13537  {
13539  mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
13540  mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
13541  }
13542  setMargins(QMargins(5, 5, 5, 0));
13543 }
13544 
13549 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
13550  QCPLayoutElement(parentPlot),
13551  mText(text),
13552  mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
13553  mTextColor(Qt::black),
13554  mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
13555  mSelectedTextColor(Qt::blue),
13556  mSelectable(false),
13557  mSelected(false)
13558 {
13559  setLayer("axes");
13560  setMargins(QMargins(5, 5, 5, 0));
13561 }
13562 
13568 void QCPPlotTitle::setText(const QString &text)
13569 {
13570  mText = text;
13571 }
13572 
13578 void QCPPlotTitle::setFont(const QFont &font)
13579 {
13580  mFont = font;
13581 }
13582 
13588 void QCPPlotTitle::setTextColor(const QColor &color)
13589 {
13590  mTextColor = color;
13591 }
13592 
13598 void QCPPlotTitle::setSelectedFont(const QFont &font)
13599 {
13600  mSelectedFont = font;
13601 }
13602 
13608 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
13609 {
13610  mSelectedTextColor = color;
13611 }
13612 
13619 void QCPPlotTitle::setSelectable(bool selectable)
13620 {
13621  if (mSelectable != selectable)
13622  {
13625  }
13626 }
13627 
13635 void QCPPlotTitle::setSelected(bool selected)
13636 {
13637  if (mSelected != selected)
13638  {
13639  mSelected = selected;
13641  }
13642 }
13643 
13644 /* inherits documentation from base class */
13646 {
13648 }
13649 
13650 /* inherits documentation from base class */
13652 {
13653  painter->setFont(mainFont());
13654  painter->setPen(QPen(mainTextColor()));
13655  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
13656 }
13657 
13658 /* inherits documentation from base class */
13660 {
13661  QFontMetrics metrics(mFont);
13662  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13663  result.rwidth() += mMargins.left() + mMargins.right();
13664  result.rheight() += mMargins.top() + mMargins.bottom();
13665  return result;
13666 }
13667 
13668 /* inherits documentation from base class */
13670 {
13671  QFontMetrics metrics(mFont);
13672  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13673  result.rheight() += mMargins.top() + mMargins.bottom();
13674  result.setWidth(QWIDGETSIZE_MAX);
13675  return result;
13676 }
13677 
13678 /* inherits documentation from base class */
13679 void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13680 {
13681  Q_UNUSED(event)
13682  Q_UNUSED(details)
13683  if (mSelectable)
13684  {
13685  bool selBefore = mSelected;
13686  setSelected(additive ? !mSelected : true);
13687  if (selectionStateChanged)
13688  *selectionStateChanged = mSelected != selBefore;
13689  }
13690 }
13691 
13692 /* inherits documentation from base class */
13693 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
13694 {
13695  if (mSelectable)
13696  {
13697  bool selBefore = mSelected;
13698  setSelected(false);
13699  if (selectionStateChanged)
13700  *selectionStateChanged = mSelected != selBefore;
13701  }
13702 }
13703 
13704 /* inherits documentation from base class */
13705 double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13706 {
13707  Q_UNUSED(details)
13708  if (onlySelectable && !mSelectable)
13709  return -1;
13710 
13711  if (mTextBoundingRect.contains(pos.toPoint()))
13712  return mParentPlot->selectionTolerance()*0.99;
13713  else
13714  return -1;
13715 }
13716 
13723 {
13724  return mSelected ? mSelectedFont : mFont;
13725 }
13726 
13733 {
13735 }
13736 
13737 
13741 
13791 /* start documentation of inline functions */
13792 
13806 /* end documentation of signals */
13807 /* start documentation of signals */
13808 
13830 /* end documentation of signals */
13831 
13836  QCPLayoutElement(parentPlot),
13837  mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
13838  mDataScaleType(QCPAxis::stLinear),
13839  mBarWidth(20),
13840  mAxisRect(new QCPColorScaleAxisRectPrivate(this))
13841 {
13842  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)
13844  setDataRange(QCPRange(0, 6));
13845 }
13846 
13848 {
13849  delete mAxisRect;
13850 }
13851 
13852 /* undocumented getter */
13853 QString QCPColorScale::label() const
13854 {
13855  if (!mColorAxis)
13856  {
13857  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13858  return QString();
13859  }
13860 
13861  return mColorAxis.data()->label();
13862 }
13863 
13864 /* undocumented getter */
13866 {
13867  if (!mAxisRect)
13868  {
13869  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13870  return false;
13871  }
13872 
13873  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
13874  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
13875  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13876 }
13877 
13878 /* undocumented getter */
13880 {
13881  if (!mAxisRect)
13882  {
13883  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13884  return false;
13885  }
13886 
13887  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
13888  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
13889  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13890 }
13891 
13900 {
13901  if (!mAxisRect)
13902  {
13903  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13904  return;
13905  }
13906  if (mType != type)
13907  {
13908  mType = type;
13909  QCPRange rangeTransfer(0, 6);
13910  double logBaseTransfer = 10;
13911  QString labelTransfer;
13912  // revert some settings on old axis:
13913  if (mColorAxis)
13914  {
13915  rangeTransfer = mColorAxis.data()->range();
13916  labelTransfer = mColorAxis.data()->label();
13917  logBaseTransfer = mColorAxis.data()->scaleLogBase();
13918  mColorAxis.data()->setLabel("");
13919  disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13920  disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13921  }
13922  foreach (QCPAxis::AxisType atype, QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop)
13923  {
13924  mAxisRect.data()->axis(atype)->setTicks(atype == mType);
13925  mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
13926  }
13927  // set new mColorAxis pointer:
13928  mColorAxis = mAxisRect.data()->axis(mType);
13929  // transfer settings to new axis:
13930  mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
13931  mColorAxis.data()->setLabel(labelTransfer);
13932  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
13933  connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13934  connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13935  mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
13936  QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
13937  }
13938 }
13939 
13950 {
13952  {
13954  if (mColorAxis)
13955  mColorAxis.data()->setRange(mDataRange);
13957  }
13958 }
13959 
13971 {
13972  if (mDataScaleType != scaleType)
13973  {
13974  mDataScaleType = scaleType;
13975  if (mColorAxis)
13976  mColorAxis.data()->setScaleType(mDataScaleType);
13980  }
13981 }
13982 
13991 {
13992  if (mGradient != gradient)
13993  {
13994  mGradient = gradient;
13995  if (mAxisRect)
13996  mAxisRect.data()->mGradientImageInvalidated = true;
13997  emit gradientChanged(mGradient);
13998  }
13999 }
14000 
14005 void QCPColorScale::setLabel(const QString &str)
14006 {
14007  if (!mColorAxis)
14008  {
14009  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
14010  return;
14011  }
14012 
14013  mColorAxis.data()->setLabel(str);
14014 }
14015 
14021 {
14022  mBarWidth = width;
14023 }
14024 
14032 {
14033  if (!mAxisRect)
14034  {
14035  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14036  return;
14037  }
14038 
14039  if (enabled)
14040  mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14041  else
14042  mAxisRect.data()->setRangeDrag(0);
14043 }
14044 
14052 {
14053  if (!mAxisRect)
14054  {
14055  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14056  return;
14057  }
14058 
14059  if (enabled)
14060  mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14061  else
14062  mAxisRect.data()->setRangeZoom(0);
14063 }
14064 
14068 QList<QCPColorMap*> QCPColorScale::colorMaps() const
14069 {
14070  QList<QCPColorMap*> result;
14071  for (int i=0; i<mParentPlot->plottableCount(); ++i)
14072  {
14073  if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
14074  if (cm->colorScale() == this)
14075  result.append(cm);
14076  }
14077  return result;
14078 }
14079 
14086 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
14087 {
14088  QList<QCPColorMap*> maps = colorMaps();
14089  QCPRange newRange;
14090  bool haveRange = false;
14091  int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
14093  sign = (mDataRange.upper < 0 ? -1 : 1);
14094  for (int i=0; i<maps.size(); ++i)
14095  {
14096  if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
14097  continue;
14098  QCPRange mapRange;
14099  if (maps.at(i)->colorScale() == this)
14100  {
14101  bool currentFoundRange = true;
14102  mapRange = maps.at(i)->data()->dataBounds();
14103  if (sign == 1)
14104  {
14105  if (mapRange.lower <= 0 && mapRange.upper > 0)
14106  mapRange.lower = mapRange.upper*1e-3;
14107  else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14108  currentFoundRange = false;
14109  } else if (sign == -1)
14110  {
14111  if (mapRange.upper >= 0 && mapRange.lower < 0)
14112  mapRange.upper = mapRange.lower*1e-3;
14113  else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14114  currentFoundRange = false;
14115  }
14116  if (currentFoundRange)
14117  {
14118  if (!haveRange)
14119  newRange = mapRange;
14120  else
14121  newRange.expand(mapRange);
14122  haveRange = true;
14123  }
14124  }
14125  }
14126  if (haveRange)
14127  {
14128  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
14129  {
14130  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
14132  {
14133  newRange.lower = center-mDataRange.size()/2.0;
14134  newRange.upper = center+mDataRange.size()/2.0;
14135  } else // mScaleType == stLogarithmic
14136  {
14137  newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
14138  newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
14139  }
14140  }
14141  setDataRange(newRange);
14142  }
14143 }
14144 
14145 /* inherits documentation from base class */
14147 {
14148  QCPLayoutElement::update(phase);
14149  if (!mAxisRect)
14150  {
14151  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14152  return;
14153  }
14154 
14155  mAxisRect.data()->update(phase);
14156 
14157  switch (phase)
14158  {
14159  case upMargins:
14160  {
14162  {
14163  setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14164  setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14165  } else
14166  {
14167  setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
14168  setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
14169  }
14170  break;
14171  }
14172  case upLayout:
14173  {
14174  mAxisRect.data()->setOuterRect(rect());
14175  break;
14176  }
14177  default: break;
14178  }
14179 }
14180 
14181 /* inherits documentation from base class */
14183 {
14184  painter->setAntialiasing(false);
14185 }
14186 
14187 /* inherits documentation from base class */
14188 void QCPColorScale::mousePressEvent(QMouseEvent *event)
14189 {
14190  if (!mAxisRect)
14191  {
14192  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14193  return;
14194  }
14195  mAxisRect.data()->mousePressEvent(event);
14196 }
14197 
14198 /* inherits documentation from base class */
14199 void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
14200 {
14201  if (!mAxisRect)
14202  {
14203  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14204  return;
14205  }
14206  mAxisRect.data()->mouseMoveEvent(event);
14207 }
14208 
14209 /* inherits documentation from base class */
14210 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
14211 {
14212  if (!mAxisRect)
14213  {
14214  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14215  return;
14216  }
14217  mAxisRect.data()->mouseReleaseEvent(event);
14218 }
14219 
14220 /* inherits documentation from base class */
14221 void QCPColorScale::wheelEvent(QWheelEvent *event)
14222 {
14223  if (!mAxisRect)
14224  {
14225  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14226  return;
14227  }
14228  mAxisRect.data()->wheelEvent(event);
14229 }
14230 
14234 
14250  QCPAxisRect(parentColorScale->parentPlot(), true),
14251  mParentColorScale(parentColorScale),
14252  mGradientImageInvalidated(true)
14253 {
14254  setParentLayerable(parentColorScale);
14255  setMinimumMargins(QMargins(0, 0, 0, 0));
14256  foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14257  {
14258  axis(type)->setVisible(true);
14259  axis(type)->grid()->setVisible(false);
14260  axis(type)->setPadding(0);
14261  connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14262  connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14263  }
14264 
14265  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14266  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14267  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14268  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14269  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14270  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14271  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14272  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14273 
14274  // make layer transfers of color scale transfer to axis rect and axes
14275  // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
14276  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
14277  foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14278  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
14279 }
14280 
14286 {
14289 
14290  bool mirrorHorz = false;
14291  bool mirrorVert = false;
14293  {
14294  mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
14295  mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
14296  }
14297 
14298  painter->drawImage(rect(), mGradientImage.mirrored(mirrorHorz, mirrorVert));
14299  QCPAxisRect::draw(painter);
14300 }
14301 
14308 {
14309  if (rect().isEmpty())
14310  return;
14311 
14313  int w, h;
14314  QVector<double> data(n);
14315  for (int i=0; i<n; ++i)
14316  data[i] = i;
14318  {
14319  w = n;
14320  h = rect().height();
14321  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14322  QVector<QRgb*> pixels;
14323  for (int y=0; y<h; ++y)
14324  pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
14325  mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
14326  for (int y=1; y<h; ++y)
14327  memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
14328  } else
14329  {
14330  w = rect().width();
14331  h = n;
14332  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14333  for (int y=0; y<h; ++y)
14334  {
14335  QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
14336  const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
14337  for (int x=0; x<w; ++x)
14338  pixels[x] = lineColor;
14339  }
14340  }
14341  mGradientImageInvalidated = false;
14342 }
14343 
14349 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
14350 {
14351  // axis bases of four axes shall always (de-)selected synchronously:
14352  foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14353  {
14354  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14355  if (senderAxis->axisType() == type)
14356  continue;
14357 
14358  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14359  {
14360  if (selectedParts.testFlag(QCPAxis::spAxis))
14361  axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
14362  else
14363  axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
14364  }
14365  }
14366 }
14367 
14373 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
14374 {
14375  // synchronize axis base selectability:
14376  foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14377  {
14378  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14379  if (senderAxis->axisType() == type)
14380  continue;
14381 
14382  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14383  {
14384  if (selectableParts.testFlag(QCPAxis::spAxis))
14385  axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
14386  else
14387  axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
14388  }
14389  }
14390 }
14391 
14392 
14396 
14417  key(0),
14418  value(0),
14419  keyErrorPlus(0),
14420  keyErrorMinus(0),
14421  valueErrorPlus(0),
14422  valueErrorMinus(0)
14423 {
14424 }
14425 
14429 QCPData::QCPData(double key, double value) :
14430  key(key),
14431  value(value),
14432  keyErrorPlus(0),
14433  keyErrorMinus(0),
14434  valueErrorPlus(0),
14435  valueErrorMinus(0)
14436 {
14437 }
14438 
14439 
14443 
14478 /* start of documentation of inline functions */
14479 
14487 /* end of documentation of inline functions */
14488 
14500 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
14501  QCPAbstractPlottable(keyAxis, valueAxis)
14502 {
14503  mData = new QCPDataMap;
14504 
14505  setPen(QPen(Qt::blue, 0));
14506  setErrorPen(QPen(Qt::black));
14507  setBrush(Qt::NoBrush);
14508  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14509  setSelectedBrush(Qt::NoBrush);
14510 
14513  setErrorBarSize(6);
14514  setErrorBarSkipSymbol(true);
14516  setAdaptiveSampling(true);
14517 }
14518 
14520 {
14521  delete mData;
14522 }
14523 
14535 {
14536  if (copy)
14537  {
14538  *mData = *data;
14539  } else
14540  {
14541  delete mData;
14542  mData = data;
14543  }
14544 }
14545 
14552 void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
14553 {
14554  mData->clear();
14555  int n = key.size();
14556  n = qMin(n, value.size());
14557  QCPData newData;
14558  for (int i=0; i<n; ++i)
14559  {
14560  newData.key = key[i];
14561  newData.value = value[i];
14562  mData->insertMulti(newData.key, newData);
14563  }
14564 }
14565 
14575 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
14576 {
14577  mData->clear();
14578  int n = key.size();
14579  n = qMin(n, value.size());
14580  n = qMin(n, valueError.size());
14581  QCPData newData;
14582  for (int i=0; i<n; ++i)
14583  {
14584  newData.key = key[i];
14585  newData.value = value[i];
14586  newData.valueErrorMinus = valueError[i];
14587  newData.valueErrorPlus = valueError[i];
14588  mData->insertMulti(key[i], newData);
14589  }
14590 }
14591 
14601 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14602 {
14603  mData->clear();
14604  int n = key.size();
14605  n = qMin(n, value.size());
14606  n = qMin(n, valueErrorMinus.size());
14607  n = qMin(n, valueErrorPlus.size());
14608  QCPData newData;
14609  for (int i=0; i<n; ++i)
14610  {
14611  newData.key = key[i];
14612  newData.value = value[i];
14613  newData.valueErrorMinus = valueErrorMinus[i];
14614  newData.valueErrorPlus = valueErrorPlus[i];
14615  mData->insertMulti(key[i], newData);
14616  }
14617 }
14618 
14628 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
14629 {
14630  mData->clear();
14631  int n = key.size();
14632  n = qMin(n, value.size());
14633  n = qMin(n, keyError.size());
14634  QCPData newData;
14635  for (int i=0; i<n; ++i)
14636  {
14637  newData.key = key[i];
14638  newData.value = value[i];
14639  newData.keyErrorMinus = keyError[i];
14640  newData.keyErrorPlus = keyError[i];
14641  mData->insertMulti(key[i], newData);
14642  }
14643 }
14644 
14654 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
14655 {
14656  mData->clear();
14657  int n = key.size();
14658  n = qMin(n, value.size());
14659  n = qMin(n, keyErrorMinus.size());
14660  n = qMin(n, keyErrorPlus.size());
14661  QCPData newData;
14662  for (int i=0; i<n; ++i)
14663  {
14664  newData.key = key[i];
14665  newData.value = value[i];
14666  newData.keyErrorMinus = keyErrorMinus[i];
14667  newData.keyErrorPlus = keyErrorPlus[i];
14668  mData->insertMulti(key[i], newData);
14669  }
14670 }
14671 
14681 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
14682 {
14683  mData->clear();
14684  int n = key.size();
14685  n = qMin(n, value.size());
14686  n = qMin(n, valueError.size());
14687  n = qMin(n, keyError.size());
14688  QCPData newData;
14689  for (int i=0; i<n; ++i)
14690  {
14691  newData.key = key[i];
14692  newData.value = value[i];
14693  newData.keyErrorMinus = keyError[i];
14694  newData.keyErrorPlus = keyError[i];
14695  newData.valueErrorMinus = valueError[i];
14696  newData.valueErrorPlus = valueError[i];
14697  mData->insertMulti(key[i], newData);
14698  }
14699 }
14700 
14710 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)
14711 {
14712  mData->clear();
14713  int n = key.size();
14714  n = qMin(n, value.size());
14715  n = qMin(n, valueErrorMinus.size());
14716  n = qMin(n, valueErrorPlus.size());
14717  n = qMin(n, keyErrorMinus.size());
14718  n = qMin(n, keyErrorPlus.size());
14719  QCPData newData;
14720  for (int i=0; i<n; ++i)
14721  {
14722  newData.key = key[i];
14723  newData.value = value[i];
14724  newData.keyErrorMinus = keyErrorMinus[i];
14725  newData.keyErrorPlus = keyErrorPlus[i];
14726  newData.valueErrorMinus = valueErrorMinus[i];
14727  newData.valueErrorPlus = valueErrorPlus[i];
14728  mData->insertMulti(key[i], newData);
14729  }
14730 }
14731 
14732 
14740 {
14741  mLineStyle = ls;
14742 }
14743 
14751 {
14752  mScatterStyle = style;
14753 }
14754 
14764 {
14766 }
14767 
14772 void QCPGraph::setErrorPen(const QPen &pen)
14773 {
14774  mErrorPen = pen;
14775 }
14776 
14780 void QCPGraph::setErrorBarSize(double size)
14781 {
14782  mErrorBarSize = size;
14783 }
14784 
14797 {
14798  mErrorBarSkipSymbol = enabled;
14799 }
14800 
14811 {
14812  // prevent setting channel target to this graph itself:
14813  if (targetGraph == this)
14814  {
14815  qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
14816  mChannelFillGraph = 0;
14817  return;
14818  }
14819  // prevent setting channel target to a graph not in the plot:
14820  if (targetGraph && targetGraph->mParentPlot != mParentPlot)
14821  {
14822  qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
14823  mChannelFillGraph = 0;
14824  return;
14825  }
14826 
14827  mChannelFillGraph = targetGraph;
14828 }
14829 
14862 {
14863  mAdaptiveSampling = enabled;
14864 }
14865 
14874 void QCPGraph::addData(const QCPDataMap &dataMap)
14875 {
14876  mData->unite(dataMap);
14877 }
14878 
14888 {
14889  mData->insertMulti(data.key, data);
14890 }
14891 
14900 void QCPGraph::addData(double key, double value)
14901 {
14902  QCPData newData;
14903  newData.key = key;
14904  newData.value = value;
14905  mData->insertMulti(newData.key, newData);
14906 }
14907 
14916 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
14917 {
14918  int n = qMin(keys.size(), values.size());
14919  QCPData newData;
14920  for (int i=0; i<n; ++i)
14921  {
14922  newData.key = keys[i];
14923  newData.value = values[i];
14924  mData->insertMulti(newData.key, newData);
14925  }
14926 }
14927 
14933 {
14934  QCPDataMap::iterator it = mData->begin();
14935  while (it != mData->end() && it.key() < key)
14936  it = mData->erase(it);
14937 }
14938 
14944 {
14945  if (mData->isEmpty()) return;
14946  QCPDataMap::iterator it = mData->upperBound(key);
14947  while (it != mData->end())
14948  it = mData->erase(it);
14949 }
14950 
14958 void QCPGraph::removeData(double fromKey, double toKey)
14959 {
14960  if (fromKey >= toKey || mData->isEmpty()) return;
14961  QCPDataMap::iterator it = mData->upperBound(fromKey);
14962  QCPDataMap::iterator itEnd = mData->upperBound(toKey);
14963  while (it != itEnd)
14964  it = mData->erase(it);
14965 }
14966 
14975 void QCPGraph::removeData(double key)
14976 {
14977  mData->remove(key);
14978 }
14979 
14985 {
14986  mData->clear();
14987 }
14988 
14989 /* inherits documentation from base class */
14990 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
14991 {
14992  Q_UNUSED(details)
14993  if ((onlySelectable && !mSelectable) || mData->isEmpty())
14994  return -1;
14995  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
14996 
14997  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
14998  return pointDistance(pos);
14999  else
15000  return -1;
15001 }
15002 
15010 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
15011 {
15012  rescaleKeyAxis(onlyEnlarge, includeErrorBars);
15013  rescaleValueAxis(onlyEnlarge, includeErrorBars);
15014 }
15015 
15023 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
15024 {
15025  // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
15026  // that getKeyRange is passed the includeErrorBars value.
15027  if (mData->isEmpty()) return;
15028 
15029  QCPAxis *keyAxis = mKeyAxis.data();
15030  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15031 
15032  SignDomain signDomain = sdBoth;
15034  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15035 
15036  bool foundRange;
15037  QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15038 
15039  if (foundRange)
15040  {
15041  if (onlyEnlarge)
15042  {
15043  if (keyAxis->range().lower < newRange.lower)
15044  newRange.lower = keyAxis->range().lower;
15045  if (keyAxis->range().upper > newRange.upper)
15046  newRange.upper = keyAxis->range().upper;
15047  }
15048  keyAxis->setRange(newRange);
15049  }
15050 }
15051 
15059 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
15060 {
15061  // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
15062  // is that getValueRange is passed the includeErrorBars value.
15063  if (mData->isEmpty()) return;
15064 
15065  QCPAxis *valueAxis = mValueAxis.data();
15066  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
15067 
15068  SignDomain signDomain = sdBoth;
15070  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15071 
15072  bool foundRange;
15073  QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15074 
15075  if (foundRange)
15076  {
15077  if (onlyEnlarge)
15078  {
15079  if (valueAxis->range().lower < newRange.lower)
15080  newRange.lower = valueAxis->range().lower;
15081  if (valueAxis->range().upper > newRange.upper)
15082  newRange.upper = valueAxis->range().upper;
15083  }
15084  valueAxis->setRange(newRange);
15085  }
15086 }
15087 
15088 /* inherits documentation from base class */
15090 {
15091  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15092  if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15093  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15094 
15095  // allocate line and (if necessary) point vectors:
15096  QVector<QPointF> *lineData = new QVector<QPointF>;
15097  QVector<QCPData> *scatterData = 0;
15098  if (!mScatterStyle.isNone())
15099  scatterData = new QVector<QCPData>;
15100 
15101  // fill vectors with data appropriate to plot style:
15102  getPlotData(lineData, scatterData);
15103 
15104  // check data validity if flag set:
15105 #ifdef QCUSTOMPLOT_CHECK_DATA
15106  QCPDataMap::const_iterator it;
15107  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
15108  {
15109  if (QCP::isInvalidData(it.value().key, it.value().value) ||
15110  QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15111  QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
15112  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
15113  }
15114 #endif
15115 
15116  // draw fill of graph:
15117  drawFill(painter, lineData);
15118 
15119  // draw line:
15120  if (mLineStyle == lsImpulse)
15121  drawImpulsePlot(painter, lineData);
15122  else if (mLineStyle != lsNone)
15123  drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15124 
15125  // draw scatters:
15126  if (scatterData)
15127  drawScatterPlot(painter, scatterData);
15128 
15129  // free allocated line and point vectors:
15130  delete lineData;
15131  if (scatterData)
15132  delete scatterData;
15133 }
15134 
15135 /* inherits documentation from base class */
15136 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
15137 {
15138  // draw fill:
15139  if (mBrush.style() != Qt::NoBrush)
15140  {
15141  applyFillAntialiasingHint(painter);
15142  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
15143  }
15144  // draw line vertically centered:
15145  if (mLineStyle != lsNone)
15146  {
15148  painter->setPen(mPen);
15149  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
15150  }
15151  // draw scatter symbol:
15152  if (!mScatterStyle.isNone())
15153  {
15155  // scale scatter pixmap if it's too large to fit in legend icon rect:
15156  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
15157  {
15158  QCPScatterStyle scaledStyle(mScatterStyle);
15159  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15160  scaledStyle.applyTo(painter, mPen);
15161  scaledStyle.drawShape(painter, QRectF(rect).center());
15162  } else
15163  {
15164  mScatterStyle.applyTo(painter, mPen);
15165  mScatterStyle.drawShape(painter, QRectF(rect).center());
15166  }
15167  }
15168 }
15169 
15188 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
15189 {
15190  switch(mLineStyle)
15191  {
15192  case lsNone: getScatterPlotData(scatterData); break;
15193  case lsLine: getLinePlotData(lineData, scatterData); break;
15194  case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
15195  case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
15196  case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
15197  case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
15198  }
15199 }
15200 
15212 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15213 {
15214  getPreparedData(0, scatterData);
15215 }
15216 
15228 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15229 {
15230  QCPAxis *keyAxis = mKeyAxis.data();
15231  QCPAxis *valueAxis = mValueAxis.data();
15232  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15233  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15234 
15235  QVector<QCPData> lineData;
15236  getPreparedData(&lineData, scatterData);
15237  linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15238  linePixelData->resize(lineData.size());
15239 
15240  // transform lineData points to pixels:
15241  if (keyAxis->orientation() == Qt::Vertical)
15242  {
15243  for (int i=0; i<lineData.size(); ++i)
15244  {
15245  (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15246  (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15247  }
15248  } else // key axis is horizontal
15249  {
15250  for (int i=0; i<lineData.size(); ++i)
15251  {
15252  (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15253  (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15254  }
15255  }
15256 }
15257 
15269 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15270 {
15271  QCPAxis *keyAxis = mKeyAxis.data();
15272  QCPAxis *valueAxis = mValueAxis.data();
15273  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15274  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15275 
15276  QVector<QCPData> lineData;
15277  getPreparedData(&lineData, scatterData);
15278  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15279  linePixelData->resize(lineData.size()*2);
15280 
15281  // calculate steps from lineData and transform to pixel coordinates:
15282  if (keyAxis->orientation() == Qt::Vertical)
15283  {
15284  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15285  double key;
15286  for (int i=0; i<lineData.size(); ++i)
15287  {
15288  key = keyAxis->coordToPixel(lineData.at(i).key);
15289  (*linePixelData)[i*2+0].setX(lastValue);
15290  (*linePixelData)[i*2+0].setY(key);
15291  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15292  (*linePixelData)[i*2+1].setX(lastValue);
15293  (*linePixelData)[i*2+1].setY(key);
15294  }
15295  } else // key axis is horizontal
15296  {
15297  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15298  double key;
15299  for (int i=0; i<lineData.size(); ++i)
15300  {
15301  key = keyAxis->coordToPixel(lineData.at(i).key);
15302  (*linePixelData)[i*2+0].setX(key);
15303  (*linePixelData)[i*2+0].setY(lastValue);
15304  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15305  (*linePixelData)[i*2+1].setX(key);
15306  (*linePixelData)[i*2+1].setY(lastValue);
15307  }
15308  }
15309 }
15310 
15322 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15323 {
15324  QCPAxis *keyAxis = mKeyAxis.data();
15325  QCPAxis *valueAxis = mValueAxis.data();
15326  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15327  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15328 
15329  QVector<QCPData> lineData;
15330  getPreparedData(&lineData, scatterData);
15331  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15332  linePixelData->resize(lineData.size()*2);
15333 
15334  // calculate steps from lineData and transform to pixel coordinates:
15335  if (keyAxis->orientation() == Qt::Vertical)
15336  {
15337  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15338  double value;
15339  for (int i=0; i<lineData.size(); ++i)
15340  {
15341  value = valueAxis->coordToPixel(lineData.at(i).value);
15342  (*linePixelData)[i*2+0].setX(value);
15343  (*linePixelData)[i*2+0].setY(lastKey);
15344  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15345  (*linePixelData)[i*2+1].setX(value);
15346  (*linePixelData)[i*2+1].setY(lastKey);
15347  }
15348  } else // key axis is horizontal
15349  {
15350  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15351  double value;
15352  for (int i=0; i<lineData.size(); ++i)
15353  {
15354  value = valueAxis->coordToPixel(lineData.at(i).value);
15355  (*linePixelData)[i*2+0].setX(lastKey);
15356  (*linePixelData)[i*2+0].setY(value);
15357  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15358  (*linePixelData)[i*2+1].setX(lastKey);
15359  (*linePixelData)[i*2+1].setY(value);
15360  }
15361  }
15362 }
15363 
15375 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15376 {
15377  QCPAxis *keyAxis = mKeyAxis.data();
15378  QCPAxis *valueAxis = mValueAxis.data();
15379  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15380  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15381 
15382  QVector<QCPData> lineData;
15383  getPreparedData(&lineData, scatterData);
15384  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15385  linePixelData->resize(lineData.size()*2);
15386  // calculate steps from lineData and transform to pixel coordinates:
15387  if (keyAxis->orientation() == Qt::Vertical)
15388  {
15389  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15390  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15391  double key;
15392  (*linePixelData)[0].setX(lastValue);
15393  (*linePixelData)[0].setY(lastKey);
15394  for (int i=1; i<lineData.size(); ++i)
15395  {
15396  key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15397  (*linePixelData)[i*2-1].setX(lastValue);
15398  (*linePixelData)[i*2-1].setY(key);
15399  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15400  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15401  (*linePixelData)[i*2+0].setX(lastValue);
15402  (*linePixelData)[i*2+0].setY(key);
15403  }
15404  (*linePixelData)[lineData.size()*2-1].setX(lastValue);
15405  (*linePixelData)[lineData.size()*2-1].setY(lastKey);
15406  } else // key axis is horizontal
15407  {
15408  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15409  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15410  double key;
15411  (*linePixelData)[0].setX(lastKey);
15412  (*linePixelData)[0].setY(lastValue);
15413  for (int i=1; i<lineData.size(); ++i)
15414  {
15415  key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15416  (*linePixelData)[i*2-1].setX(key);
15417  (*linePixelData)[i*2-1].setY(lastValue);
15418  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15419  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15420  (*linePixelData)[i*2+0].setX(key);
15421  (*linePixelData)[i*2+0].setY(lastValue);
15422  }
15423  (*linePixelData)[lineData.size()*2-1].setX(lastKey);
15424  (*linePixelData)[lineData.size()*2-1].setY(lastValue);
15425  }
15426 
15427 }
15428 
15440 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15441 {
15442  QCPAxis *keyAxis = mKeyAxis.data();
15443  QCPAxis *valueAxis = mValueAxis.data();
15444  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15445  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15446 
15447  QVector<QCPData> lineData;
15448  getPreparedData(&lineData, scatterData);
15449  linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
15450 
15451  // transform lineData points to pixels:
15452  if (keyAxis->orientation() == Qt::Vertical)
15453  {
15454  double zeroPointX = valueAxis->coordToPixel(0);
15455  double key;
15456  for (int i=0; i<lineData.size(); ++i)
15457  {
15458  key = keyAxis->coordToPixel(lineData.at(i).key);
15459  (*linePixelData)[i*2+0].setX(zeroPointX);
15460  (*linePixelData)[i*2+0].setY(key);
15461  (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
15462  (*linePixelData)[i*2+1].setY(key);
15463  }
15464  } else // key axis is horizontal
15465  {
15466  double zeroPointY = valueAxis->coordToPixel(0);
15467  double key;
15468  for (int i=0; i<lineData.size(); ++i)
15469  {
15470  key = keyAxis->coordToPixel(lineData.at(i).key);
15471  (*linePixelData)[i*2+0].setX(key);
15472  (*linePixelData)[i*2+0].setY(zeroPointY);
15473  (*linePixelData)[i*2+1].setX(key);
15474  (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
15475  }
15476  }
15477 }
15478 
15492 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
15493 {
15494  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
15495  if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
15496 
15497  applyFillAntialiasingHint(painter);
15498  if (!mChannelFillGraph)
15499  {
15500  // draw base fill under graph, fill goes all the way to the zero-value-line:
15501  addFillBasePoints(lineData);
15502  painter->setPen(Qt::NoPen);
15503  painter->setBrush(mainBrush());
15504  painter->drawPolygon(QPolygonF(*lineData));
15505  removeFillBasePoints(lineData);
15506  } else
15507  {
15508  // draw channel fill between this graph and mChannelFillGraph:
15509  painter->setPen(Qt::NoPen);
15510  painter->setBrush(mainBrush());
15511  painter->drawPolygon(getChannelFillPolygon(lineData));
15512  }
15513 }
15514 
15524 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
15525 {
15526  QCPAxis *keyAxis = mKeyAxis.data();
15527  QCPAxis *valueAxis = mValueAxis.data();
15528  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15529 
15530  // draw error bars:
15531  if (mErrorType != etNone)
15532  {
15534  painter->setPen(mErrorPen);
15535  if (keyAxis->orientation() == Qt::Vertical)
15536  {
15537  for (int i=0; i<scatterData->size(); ++i)
15538  drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
15539  } else
15540  {
15541  for (int i=0; i<scatterData->size(); ++i)
15542  drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
15543  }
15544  }
15545 
15546  // draw scatter point symbols:
15548  mScatterStyle.applyTo(painter, mPen);
15549  if (keyAxis->orientation() == Qt::Vertical)
15550  {
15551  for (int i=0; i<scatterData->size(); ++i)
15552  mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
15553  } else
15554  {
15555  for (int i=0; i<scatterData->size(); ++i)
15556  mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
15557  }
15558 }
15559 
15569 void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15570 {
15571  // draw line of graph:
15572  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15573  {
15575  painter->setPen(mainPen());
15576  painter->setBrush(Qt::NoBrush);
15577 
15578  /* Draws polyline in batches, currently not used:
15579  int p = 0;
15580  while (p < lineData->size())
15581  {
15582  int batch = qMin(25, lineData->size()-p);
15583  if (p != 0)
15584  {
15585  ++batch;
15586  --p; // to draw the connection lines between two batches
15587  }
15588  painter->drawPolyline(lineData->constData()+p, batch);
15589  p += batch;
15590  }
15591  */
15592 
15593  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
15594  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
15595  painter->pen().style() == Qt::SolidLine &&
15596  !painter->modes().testFlag(QCPPainter::pmVectorized)&&
15597  !painter->modes().testFlag(QCPPainter::pmNoCaching))
15598  {
15599  for (int i=1; i<lineData->size(); ++i)
15600  painter->drawLine(lineData->at(i-1), lineData->at(i));
15601  } else
15602  {
15603  painter->drawPolyline(QPolygonF(*lineData));
15604  }
15605  }
15606 }
15607 
15615 void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15616 {
15617  // draw impulses:
15618  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15619  {
15621  QPen pen = mainPen();
15622  pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
15623  painter->setPen(pen);
15624  painter->setBrush(Qt::NoBrush);
15625  painter->drawLines(*lineData);
15626  }
15627 }
15628 
15641 void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
15642 {
15643  QCPAxis *keyAxis = mKeyAxis.data();
15644  QCPAxis *valueAxis = mValueAxis.data();
15645  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15646  // get visible data range:
15647  QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
15648  getVisibleDataBounds(lower, upper);
15649  if (lower == mData->constEnd() || upper == mData->constEnd())
15650  return;
15651 
15652  // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
15653  int maxCount = std::numeric_limits<int>::max();
15654  if (mAdaptiveSampling)
15655  {
15656  int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
15657  maxCount = 2*keyPixelSpan+2;
15658  }
15659  int dataCount = countDataInBounds(lower, upper, maxCount);
15660 
15661  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
15662  {
15663  if (lineData)
15664  {
15665  QCPDataMap::const_iterator it = lower;
15666  QCPDataMap::const_iterator upperEnd = upper+1;
15667  double minValue = it.value().value;
15668  double maxValue = it.value().value;
15669  QCPDataMap::const_iterator currentIntervalFirstPoint = it;
15670  int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15671  int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15672  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15673  double lastIntervalEndKey = currentIntervalStartKey;
15674  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15675  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15676  int intervalDataCount = 1;
15677  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15678  while (it != upperEnd)
15679  {
15680  if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
15681  {
15682  if (it.value().value < minValue)
15683  minValue = it.value().value;
15684  else if (it.value().value > maxValue)
15685  maxValue = it.value().value;
15686  ++intervalDataCount;
15687  } else // new pixel interval started
15688  {
15689  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15690  {
15691  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
15692  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15693  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15694  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15695  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
15696  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
15697  } else
15698  lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15699  lastIntervalEndKey = (it-1).value().key;
15700  minValue = it.value().value;
15701  maxValue = it.value().value;
15702  currentIntervalFirstPoint = it;
15703  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15704  if (keyEpsilonVariable)
15705  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15706  intervalDataCount = 1;
15707  }
15708  ++it;
15709  }
15710  // handle last interval:
15711  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15712  {
15713  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
15714  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15715  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15716  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15717  } else
15718  lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15719  }
15720 
15721  if (scatterData)
15722  {
15723  double valueMaxRange = valueAxis->range().upper;
15724  double valueMinRange = valueAxis->range().lower;
15725  QCPDataMap::const_iterator it = lower;
15726  QCPDataMap::const_iterator upperEnd = upper+1;
15727  double minValue = it.value().value;
15728  double maxValue = it.value().value;
15729  QCPDataMap::const_iterator minValueIt = it;
15730  QCPDataMap::const_iterator maxValueIt = it;
15731  QCPDataMap::const_iterator currentIntervalStart = it;
15732  int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15733  int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15734  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15735  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15736  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15737  int intervalDataCount = 1;
15738  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15739  while (it != upperEnd)
15740  {
15741  if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
15742  {
15743  if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15744  {
15745  minValue = it.value().value;
15746  minValueIt = it;
15747  } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15748  {
15749  maxValue = it.value().value;
15750  maxValueIt = it;
15751  }
15752  ++intervalDataCount;
15753  } else // new pixel started
15754  {
15755  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15756  {
15757  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15758  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15759  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15760  QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15761  int c = 0;
15762  while (intervalIt != it)
15763  {
15764  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15765  scatterData->append(intervalIt.value());
15766  ++c;
15767  ++intervalIt;
15768  }
15769  } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15770  scatterData->append(currentIntervalStart.value());
15771  minValue = it.value().value;
15772  maxValue = it.value().value;
15773  currentIntervalStart = it;
15774  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15775  if (keyEpsilonVariable)
15776  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15777  intervalDataCount = 1;
15778  }
15779  ++it;
15780  }
15781  // handle last interval:
15782  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15783  {
15784  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15785  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15786  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15787  QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15788  int c = 0;
15789  while (intervalIt != it)
15790  {
15791  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15792  scatterData->append(intervalIt.value());
15793  ++c;
15794  ++intervalIt;
15795  }
15796  } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15797  scatterData->append(currentIntervalStart.value());
15798  }
15799  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
15800  {
15801  QVector<QCPData> *dataVector = 0;
15802  if (lineData)
15803  dataVector = lineData;
15804  else if (scatterData)
15805  dataVector = scatterData;
15806  if (dataVector)
15807  {
15808  QCPDataMap::const_iterator it = lower;
15809  QCPDataMap::const_iterator upperEnd = upper+1;
15810  dataVector->reserve(dataCount+2); // +2 for possible fill end points
15811  while (it != upperEnd)
15812  {
15813  dataVector->append(it.value());
15814  ++it;
15815  }
15816  }
15817  if (lineData && scatterData)
15818  *scatterData = *dataVector;
15819  }
15820 }
15821 
15829 void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
15830 {
15831  QCPAxis *keyAxis = mKeyAxis.data();
15832  QCPAxis *valueAxis = mValueAxis.data();
15833  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15834 
15835  double a, b; // positions of error bar bounds in pixels
15836  double barWidthHalf = mErrorBarSize*0.5;
15837  double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
15838 
15839  if (keyAxis->orientation() == Qt::Vertical)
15840  {
15841  // draw key error vertically and value error horizontally
15842  if (mErrorType == etKey || mErrorType == etBoth)
15843  {
15844  a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15845  b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15846  if (keyAxis->rangeReversed())
15847  qSwap(a,b);
15848  // draw spine:
15849  if (mErrorBarSkipSymbol)
15850  {
15851  if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15852  painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15853  if (y-b > skipSymbolMargin)
15854  painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15855  } else
15856  painter->drawLine(QLineF(x, a, x, b));
15857  // draw handles:
15858  painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15859  painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15860  }
15861  if (mErrorType == etValue || mErrorType == etBoth)
15862  {
15863  a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15864  b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15865  if (valueAxis->rangeReversed())
15866  qSwap(a,b);
15867  // draw spine:
15868  if (mErrorBarSkipSymbol)
15869  {
15870  if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15871  painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15872  if (b-x > skipSymbolMargin)
15873  painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15874  } else
15875  painter->drawLine(QLineF(a, y, b, y));
15876  // draw handles:
15877  painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15878  painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15879  }
15880  } else // mKeyAxis->orientation() is Qt::Horizontal
15881  {
15882  // draw value error vertically and key error horizontally
15883  if (mErrorType == etKey || mErrorType == etBoth)
15884  {
15885  a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15886  b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15887  if (keyAxis->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  if (mErrorType == etValue || mErrorType == etBoth)
15903  {
15904  a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15905  b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15906  if (valueAxis->rangeReversed())
15907  qSwap(a,b);
15908  // draw spine:
15909  if (mErrorBarSkipSymbol)
15910  {
15911  if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15912  painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15913  if (y-b > skipSymbolMargin)
15914  painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15915  } else
15916  painter->drawLine(QLineF(x, a, x, b));
15917  // draw handles:
15918  painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15919  painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15920  }
15921  }
15922 }
15923 
15938 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
15939 {
15940  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15941  if (mData->isEmpty())
15942  {
15943  lower = mData->constEnd();
15944  upper = mData->constEnd();
15945  return;
15946  }
15947 
15948  // get visible data range as QMap iterators
15949  QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
15950  QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
15951  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
15952  bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
15953 
15954  lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
15955  upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
15956 }
15957 
15968 int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
15969 {
15970  if (upper == mData->constEnd() && lower == mData->constEnd())
15971  return 0;
15972  QCPDataMap::const_iterator it = lower;
15973  int count = 1;
15974  while (it != upper && count < maxCount)
15975  {
15976  ++it;
15977  ++count;
15978  }
15979  return count;
15980 }
15981 
15997 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
15998 {
15999  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16000 
16001  // append points that close the polygon fill at the key axis:
16002  if (mKeyAxis.data()->orientation() == Qt::Vertical)
16003  {
16004  *lineData << upperFillBasePoint(lineData->last().y());
16005  *lineData << lowerFillBasePoint(lineData->first().y());
16006  } else
16007  {
16008  *lineData << upperFillBasePoint(lineData->last().x());
16009  *lineData << lowerFillBasePoint(lineData->first().x());
16010  }
16011 }
16012 
16019 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
16020 {
16021  lineData->remove(lineData->size()-2, 2);
16022 }
16023 
16038 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
16039 {
16040  QCPAxis *keyAxis = mKeyAxis.data();
16041  QCPAxis *valueAxis = mValueAxis.data();
16042  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16043 
16044  QPointF point;
16046  {
16047  if (keyAxis->axisType() == QCPAxis::atLeft)
16048  {
16049  point.setX(valueAxis->coordToPixel(0));
16050  point.setY(lowerKey);
16051  } else if (keyAxis->axisType() == QCPAxis::atRight)
16052  {
16053  point.setX(valueAxis->coordToPixel(0));
16054  point.setY(lowerKey);
16055  } else if (keyAxis->axisType() == QCPAxis::atTop)
16056  {
16057  point.setX(lowerKey);
16058  point.setY(valueAxis->coordToPixel(0));
16059  } else if (keyAxis->axisType() == QCPAxis::atBottom)
16060  {
16061  point.setX(lowerKey);
16062  point.setY(valueAxis->coordToPixel(0));
16063  }
16064  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16065  {
16066  // In logarithmic scaling we can't just draw to value zero so we just fill all the way
16067  // to the axis which is in the direction towards zero
16068  if (keyAxis->orientation() == Qt::Vertical)
16069  {
16070  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16071  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16072  point.setX(keyAxis->axisRect()->right());
16073  else
16074  point.setX(keyAxis->axisRect()->left());
16075  point.setY(lowerKey);
16077  {
16078  point.setX(lowerKey);
16079  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16080  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16081  point.setY(keyAxis->axisRect()->top());
16082  else
16083  point.setY(keyAxis->axisRect()->bottom());
16084  }
16085  }
16086  return point;
16087 }
16088 
16103 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
16104 {
16105  QCPAxis *keyAxis = mKeyAxis.data();
16106  QCPAxis *valueAxis = mValueAxis.data();
16107  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16108 
16109  QPointF point;
16111  {
16112  if (keyAxis->axisType() == QCPAxis::atLeft)
16113  {
16114  point.setX(valueAxis->coordToPixel(0));
16115  point.setY(upperKey);
16116  } else if (keyAxis->axisType() == QCPAxis::atRight)
16117  {
16118  point.setX(valueAxis->coordToPixel(0));
16119  point.setY(upperKey);
16120  } else if (keyAxis->axisType() == QCPAxis::atTop)
16121  {
16122  point.setX(upperKey);
16123  point.setY(valueAxis->coordToPixel(0));
16124  } else if (keyAxis->axisType() == QCPAxis::atBottom)
16125  {
16126  point.setX(upperKey);
16127  point.setY(valueAxis->coordToPixel(0));
16128  }
16129  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16130  {
16131  // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
16132  // to the axis which is in the direction towards 0
16133  if (keyAxis->orientation() == Qt::Vertical)
16134  {
16135  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16136  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16137  point.setX(keyAxis->axisRect()->right());
16138  else
16139  point.setX(keyAxis->axisRect()->left());
16140  point.setY(upperKey);
16142  {
16143  point.setX(upperKey);
16144  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16145  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16146  point.setY(keyAxis->axisRect()->top());
16147  else
16148  point.setY(keyAxis->axisRect()->bottom());
16149  }
16150  }
16151  return point;
16152 }
16153 
16163 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
16164 {
16165  if (!mChannelFillGraph)
16166  return QPolygonF();
16167 
16168  QCPAxis *keyAxis = mKeyAxis.data();
16169  QCPAxis *valueAxis = mValueAxis.data();
16170  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
16171  if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
16172 
16173  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
16174  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)
16175 
16176  if (lineData->isEmpty()) return QPolygonF();
16177  QVector<QPointF> otherData;
16178  mChannelFillGraph.data()->getPlotData(&otherData, 0);
16179  if (otherData.isEmpty()) return QPolygonF();
16180  QVector<QPointF> thisData;
16181  thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
16182  for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
16183  thisData << lineData->at(i);
16184 
16185  // pointers to be able to swap them, depending which data range needs cropping:
16186  QVector<QPointF> *staticData = &thisData;
16187  QVector<QPointF> *croppedData = &otherData;
16188 
16189  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
16190  if (keyAxis->orientation() == Qt::Horizontal)
16191  {
16192  // x is key
16193  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16194  if (staticData->first().x() > staticData->last().x())
16195  {
16196  int size = staticData->size();
16197  for (int i=0; i<size/2; ++i)
16198  qSwap((*staticData)[i], (*staticData)[size-1-i]);
16199  }
16200  if (croppedData->first().x() > croppedData->last().x())
16201  {
16202  int size = croppedData->size();
16203  for (int i=0; i<size/2; ++i)
16204  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16205  }
16206  // crop lower bound:
16207  if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
16208  qSwap(staticData, croppedData);
16209  int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16210  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16211  croppedData->remove(0, lowBound);
16212  // set lowest point of cropped data to fit exactly key position of first static data
16213  // point via linear interpolation:
16214  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16215  double slope;
16216  if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
16217  slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
16218  else
16219  slope = 0;
16220  (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
16221  (*croppedData)[0].setX(staticData->first().x());
16222 
16223  // crop upper bound:
16224  if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
16225  qSwap(staticData, croppedData);
16226  int highBound = findIndexAboveX(croppedData, staticData->last().x());
16227  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16228  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16229  // set highest point of cropped data to fit exactly key position of last static data
16230  // point via linear interpolation:
16231  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16232  int li = croppedData->size()-1; // last index
16233  if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
16234  slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
16235  else
16236  slope = 0;
16237  (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
16238  (*croppedData)[li].setX(staticData->last().x());
16239  } else // mKeyAxis->orientation() == Qt::Vertical
16240  {
16241  // y is key
16242  // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
16243  // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
16244  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16245  if (staticData->first().y() < staticData->last().y())
16246  {
16247  int size = staticData->size();
16248  for (int i=0; i<size/2; ++i)
16249  qSwap((*staticData)[i], (*staticData)[size-1-i]);
16250  }
16251  if (croppedData->first().y() < croppedData->last().y())
16252  {
16253  int size = croppedData->size();
16254  for (int i=0; i<size/2; ++i)
16255  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16256  }
16257  // crop lower bound:
16258  if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
16259  qSwap(staticData, croppedData);
16260  int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16261  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16262  croppedData->remove(0, lowBound);
16263  // set lowest point of cropped data to fit exactly key position of first static data
16264  // point via linear interpolation:
16265  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16266  double slope;
16267  if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
16268  slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
16269  else
16270  slope = 0;
16271  (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
16272  (*croppedData)[0].setY(staticData->first().y());
16273 
16274  // crop upper bound:
16275  if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
16276  qSwap(staticData, croppedData);
16277  int highBound = findIndexBelowY(croppedData, staticData->last().y());
16278  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16279  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16280  // set highest point of cropped data to fit exactly key position of last static data
16281  // point via linear interpolation:
16282  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16283  int li = croppedData->size()-1; // last index
16284  if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
16285  slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
16286  else
16287  slope = 0;
16288  (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
16289  (*croppedData)[li].setY(staticData->last().y());
16290  }
16291 
16292  // return joined:
16293  for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
16294  thisData << otherData.at(i);
16295  return QPolygonF(thisData);
16296 }
16297 
16305 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
16306 {
16307  for (int i=data->size()-1; i>=0; --i)
16308  {
16309  if (data->at(i).x() < x)
16310  {
16311  if (i<data->size()-1)
16312  return i+1;
16313  else
16314  return data->size()-1;
16315  }
16316  }
16317  return -1;
16318 }
16319 
16327 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
16328 {
16329  for (int i=0; i<data->size(); ++i)
16330  {
16331  if (data->at(i).x() > x)
16332  {
16333  if (i>0)
16334  return i-1;
16335  else
16336  return 0;
16337  }
16338  }
16339  return -1;
16340 }
16341 
16349 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
16350 {
16351  for (int i=0; i<data->size(); ++i)
16352  {
16353  if (data->at(i).y() < y)
16354  {
16355  if (i>0)
16356  return i-1;
16357  else
16358  return 0;
16359  }
16360  }
16361  return -1;
16362 }
16363 
16374 double QCPGraph::pointDistance(const QPointF &pixelPoint) const
16375 {
16376  if (mData->isEmpty())
16377  {
16378  qDebug() << Q_FUNC_INFO << "requested point distance on graph" << mName << "without data";
16379  return 500;
16380  }
16381  if (mData->size() == 1)
16382  {
16383  QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
16384  return QVector2D(dataPoint-pixelPoint).length();
16385  }
16386 
16387  if (mLineStyle == lsNone && mScatterStyle.isNone())
16388  return 500;
16389 
16390  // calculate minimum distances to graph representation:
16391  if (mLineStyle == lsNone)
16392  {
16393  // no line displayed, only calculate distance to scatter points:
16394  QVector<QCPData> *scatterData = new QVector<QCPData>;
16395  getScatterPlotData(scatterData);
16396  double minDistSqr = std::numeric_limits<double>::max();
16397  QPointF ptA;
16398  QPointF ptB = coordsToPixels(scatterData->at(0).key, scatterData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
16399  for (int i=1; i<scatterData->size(); ++i)
16400  {
16401  ptA = ptB;
16402  ptB = coordsToPixels(scatterData->at(i).key, scatterData->at(i).value);
16403  double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
16404  if (currentDistSqr < minDistSqr)
16405  minDistSqr = currentDistSqr;
16406  }
16407  delete scatterData;
16408  return sqrt(minDistSqr);
16409  } else
16410  {
16411  // line displayed calculate distance to line segments:
16412  QVector<QPointF> *lineData = new QVector<QPointF>;
16413  getPlotData(lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
16414  double minDistSqr = std::numeric_limits<double>::max();
16415  if (mLineStyle == lsImpulse)
16416  {
16417  // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
16418  for (int i=0; i<lineData->size()-1; i+=2) // iterate pairs
16419  {
16420  double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
16421  if (currentDistSqr < minDistSqr)
16422  minDistSqr = currentDistSqr;
16423  }
16424  } else
16425  {
16426  // all other line plots (line and step) connect points directly:
16427  for (int i=0; i<lineData->size()-1; ++i)
16428  {
16429  double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
16430  if (currentDistSqr < minDistSqr)
16431  minDistSqr = currentDistSqr;
16432  }
16433  }
16434  delete lineData;
16435  return sqrt(minDistSqr);
16436  }
16437 }
16438 
16447 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
16448 {
16449  for (int i=data->size()-1; i>=0; --i)
16450  {
16451  if (data->at(i).y() > y)
16452  {
16453  if (i<data->size()-1)
16454  return i+1;
16455  else
16456  return data->size()-1;
16457  }
16458  }
16459  return -1;
16460 }
16461 
16462 /* inherits documentation from base class */
16463 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
16464 {
16465  // just call the specialized version which takes an additional argument whether error bars
16466  // should also be taken into consideration for range calculation. We set this to true here.
16467  return getKeyRange(foundRange, inSignDomain, true);
16468 }
16469 
16470 /* inherits documentation from base class */
16471 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
16472 {
16473  // just call the specialized version which takes an additional argument whether error bars
16474  // should also be taken into consideration for range calculation. We set this to true here.
16475  return getValueRange(foundRange, inSignDomain, true);
16476 }
16477 
16484 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16485 {
16486  QCPRange range;
16487  bool haveLower = false;
16488  bool haveUpper = false;
16489 
16490  double current, currentErrorMinus, currentErrorPlus;
16491 
16492  if (inSignDomain == sdBoth) // range may be anywhere
16493  {
16494  QCPDataMap::const_iterator it = mData->constBegin();
16495  while (it != mData->constEnd())
16496  {
16497  current = it.value().key;
16498  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16499  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16500  if (current-currentErrorMinus < range.lower || !haveLower)
16501  {
16502  range.lower = current-currentErrorMinus;
16503  haveLower = true;
16504  }
16505  if (current+currentErrorPlus > range.upper || !haveUpper)
16506  {
16507  range.upper = current+currentErrorPlus;
16508  haveUpper = true;
16509  }
16510  ++it;
16511  }
16512  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16513  {
16514  QCPDataMap::const_iterator it = mData->constBegin();
16515  while (it != mData->constEnd())
16516  {
16517  current = it.value().key;
16518  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16519  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16520  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16521  {
16522  range.lower = current-currentErrorMinus;
16523  haveLower = true;
16524  }
16525  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16526  {
16527  range.upper = current+currentErrorPlus;
16528  haveUpper = true;
16529  }
16530  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16531  {
16532  if ((current < range.lower || !haveLower) && current < 0)
16533  {
16534  range.lower = current;
16535  haveLower = true;
16536  }
16537  if ((current > range.upper || !haveUpper) && current < 0)
16538  {
16539  range.upper = current;
16540  haveUpper = true;
16541  }
16542  }
16543  ++it;
16544  }
16545  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16546  {
16547  QCPDataMap::const_iterator it = mData->constBegin();
16548  while (it != mData->constEnd())
16549  {
16550  current = it.value().key;
16551  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16552  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16553  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16554  {
16555  range.lower = current-currentErrorMinus;
16556  haveLower = true;
16557  }
16558  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16559  {
16560  range.upper = current+currentErrorPlus;
16561  haveUpper = true;
16562  }
16563  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16564  {
16565  if ((current < range.lower || !haveLower) && current > 0)
16566  {
16567  range.lower = current;
16568  haveLower = true;
16569  }
16570  if ((current > range.upper || !haveUpper) && current > 0)
16571  {
16572  range.upper = current;
16573  haveUpper = true;
16574  }
16575  }
16576  ++it;
16577  }
16578  }
16579 
16580  foundRange = haveLower && haveUpper;
16581  return range;
16582 }
16583 
16590 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16591 {
16592  QCPRange range;
16593  bool haveLower = false;
16594  bool haveUpper = false;
16595 
16596  double current, currentErrorMinus, currentErrorPlus;
16597 
16598  if (inSignDomain == sdBoth) // range may be anywhere
16599  {
16600  QCPDataMap::const_iterator it = mData->constBegin();
16601  while (it != mData->constEnd())
16602  {
16603  current = it.value().value;
16604  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16605  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16606  if (current-currentErrorMinus < range.lower || !haveLower)
16607  {
16608  range.lower = current-currentErrorMinus;
16609  haveLower = true;
16610  }
16611  if (current+currentErrorPlus > range.upper || !haveUpper)
16612  {
16613  range.upper = current+currentErrorPlus;
16614  haveUpper = true;
16615  }
16616  ++it;
16617  }
16618  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16619  {
16620  QCPDataMap::const_iterator it = mData->constBegin();
16621  while (it != mData->constEnd())
16622  {
16623  current = it.value().value;
16624  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16625  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16626  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16627  {
16628  range.lower = current-currentErrorMinus;
16629  haveLower = true;
16630  }
16631  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16632  {
16633  range.upper = current+currentErrorPlus;
16634  haveUpper = true;
16635  }
16636  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16637  {
16638  if ((current < range.lower || !haveLower) && current < 0)
16639  {
16640  range.lower = current;
16641  haveLower = true;
16642  }
16643  if ((current > range.upper || !haveUpper) && current < 0)
16644  {
16645  range.upper = current;
16646  haveUpper = true;
16647  }
16648  }
16649  ++it;
16650  }
16651  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16652  {
16653  QCPDataMap::const_iterator it = mData->constBegin();
16654  while (it != mData->constEnd())
16655  {
16656  current = it.value().value;
16657  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16658  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16659  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16660  {
16661  range.lower = current-currentErrorMinus;
16662  haveLower = true;
16663  }
16664  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16665  {
16666  range.upper = current+currentErrorPlus;
16667  haveUpper = true;
16668  }
16669  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16670  {
16671  if ((current < range.lower || !haveLower) && current > 0)
16672  {
16673  range.lower = current;
16674  haveLower = true;
16675  }
16676  if ((current > range.upper || !haveUpper) && current > 0)
16677  {
16678  range.upper = current;
16679  haveUpper = true;
16680  }
16681  }
16682  ++it;
16683  }
16684  }
16685 
16686  foundRange = haveLower && haveUpper;
16687  return range;
16688 }
16689 
16690 
16694 
16712  t(0),
16713  key(0),
16714  value(0)
16715 {
16716 }
16717 
16721 QCPCurveData::QCPCurveData(double t, double key, double value) :
16722  t(t),
16723  key(key),
16724  value(value)
16725 {
16726 }
16727 
16728 
16732 
16774 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
16775  QCPAbstractPlottable(keyAxis, valueAxis)
16776 {
16777  mData = new QCPCurveDataMap;
16778  mPen.setColor(Qt::blue);
16779  mPen.setStyle(Qt::SolidLine);
16780  mBrush.setColor(Qt::blue);
16781  mBrush.setStyle(Qt::NoBrush);
16782  mSelectedPen = mPen;
16783  mSelectedPen.setWidthF(2.5);
16784  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
16786 
16789 }
16790 
16792 {
16793  delete mData;
16794 }
16795 
16804 {
16805  if (copy)
16806  {
16807  *mData = *data;
16808  } else
16809  {
16810  delete mData;
16811  mData = data;
16812  }
16813 }
16814 
16821 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
16822 {
16823  mData->clear();
16824  int n = t.size();
16825  n = qMin(n, key.size());
16826  n = qMin(n, value.size());
16827  QCPCurveData newData;
16828  for (int i=0; i<n; ++i)
16829  {
16830  newData.t = t[i];
16831  newData.key = key[i];
16832  newData.value = value[i];
16833  mData->insertMulti(newData.t, newData);
16834  }
16835 }
16836 
16842 void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
16843 {
16844  mData->clear();
16845  int n = key.size();
16846  n = qMin(n, value.size());
16847  QCPCurveData newData;
16848  for (int i=0; i<n; ++i)
16849  {
16850  newData.t = i; // no t vector given, so we assign t the index of the key/value pair
16851  newData.key = key[i];
16852  newData.value = value[i];
16853  mData->insertMulti(newData.t, newData);
16854  }
16855 }
16856 
16865 {
16866  mScatterStyle = style;
16867 }
16868 
16877 {
16878  mLineStyle = style;
16879 }
16880 
16886 {
16887  mData->unite(dataMap);
16888 }
16889 
16895 {
16896  mData->insertMulti(data.t, data);
16897 }
16898 
16903 void QCPCurve::addData(double t, double key, double value)
16904 {
16905  QCPCurveData newData;
16906  newData.t = t;
16907  newData.key = key;
16908  newData.value = value;
16909  mData->insertMulti(newData.t, newData);
16910 }
16911 
16920 void QCPCurve::addData(double key, double value)
16921 {
16922  QCPCurveData newData;
16923  if (!mData->isEmpty())
16924  newData.t = (mData->constEnd()-1).key()+1;
16925  else
16926  newData.t = 0;
16927  newData.key = key;
16928  newData.value = value;
16929  mData->insertMulti(newData.t, newData);
16930 }
16931 
16936 void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
16937 {
16938  int n = ts.size();
16939  n = qMin(n, keys.size());
16940  n = qMin(n, values.size());
16941  QCPCurveData newData;
16942  for (int i=0; i<n; ++i)
16943  {
16944  newData.t = ts[i];
16945  newData.key = keys[i];
16946  newData.value = values[i];
16947  mData->insertMulti(newData.t, newData);
16948  }
16949 }
16950 
16956 {
16957  QCPCurveDataMap::iterator it = mData->begin();
16958  while (it != mData->end() && it.key() < t)
16959  it = mData->erase(it);
16960 }
16961 
16967 {
16968  if (mData->isEmpty()) return;
16969  QCPCurveDataMap::iterator it = mData->upperBound(t);
16970  while (it != mData->end())
16971  it = mData->erase(it);
16972 }
16973 
16981 void QCPCurve::removeData(double fromt, double tot)
16982 {
16983  if (fromt >= tot || mData->isEmpty()) return;
16984  QCPCurveDataMap::iterator it = mData->upperBound(fromt);
16985  QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
16986  while (it != itEnd)
16987  it = mData->erase(it);
16988 }
16989 
16999 void QCPCurve::removeData(double t)
17000 {
17001  mData->remove(t);
17002 }
17003 
17009 {
17010  mData->clear();
17011 }
17012 
17013 /* inherits documentation from base class */
17014 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17015 {
17016  Q_UNUSED(details)
17017  if ((onlySelectable && !mSelectable) || mData->isEmpty())
17018  return -1;
17019  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17020 
17021  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17022  return pointDistance(pos);
17023  else
17024  return -1;
17025 }
17026 
17027 /* inherits documentation from base class */
17029 {
17030  if (mData->isEmpty()) return;
17031 
17032  // allocate line vector:
17033  QVector<QPointF> *lineData = new QVector<QPointF>;
17034 
17035  // fill with curve data:
17036  getCurveData(lineData);
17037 
17038  // check data validity if flag set:
17039 #ifdef QCUSTOMPLOT_CHECK_DATA
17040  QCPCurveDataMap::const_iterator it;
17041  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17042  {
17043  if (QCP::isInvalidData(it.value().t) ||
17044  QCP::isInvalidData(it.value().key, it.value().value))
17045  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
17046  }
17047 #endif
17048 
17049  // draw curve fill:
17050  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17051  {
17052  applyFillAntialiasingHint(painter);
17053  painter->setPen(Qt::NoPen);
17054  painter->setBrush(mainBrush());
17055  painter->drawPolygon(QPolygonF(*lineData));
17056  }
17057 
17058  // draw curve line:
17059  if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17060  {
17062  painter->setPen(mainPen());
17063  painter->setBrush(Qt::NoBrush);
17064  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
17065  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17066  painter->pen().style() == Qt::SolidLine &&
17067  !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17068  !painter->modes().testFlag(QCPPainter::pmNoCaching))
17069  {
17070  for (int i=1; i<lineData->size(); ++i)
17071  painter->drawLine(lineData->at(i-1), lineData->at(i));
17072  } else
17073  {
17074  painter->drawPolyline(QPolygonF(*lineData));
17075  }
17076  }
17077 
17078  // draw scatters:
17079  if (!mScatterStyle.isNone())
17080  drawScatterPlot(painter, lineData);
17081 
17082  // free allocated line data:
17083  delete lineData;
17084 }
17085 
17086 /* inherits documentation from base class */
17087 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17088 {
17089  // draw fill:
17090  if (mBrush.style() != Qt::NoBrush)
17091  {
17092  applyFillAntialiasingHint(painter);
17093  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
17094  }
17095  // draw line vertically centered:
17096  if (mLineStyle != lsNone)
17097  {
17099  painter->setPen(mPen);
17100  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
17101  }
17102  // draw scatter symbol:
17103  if (!mScatterStyle.isNone())
17104  {
17106  // scale scatter pixmap if it's too large to fit in legend icon rect:
17107  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
17108  {
17109  QCPScatterStyle scaledStyle(mScatterStyle);
17110  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17111  scaledStyle.applyTo(painter, mPen);
17112  scaledStyle.drawShape(painter, QRectF(rect).center());
17113  } else
17114  {
17115  mScatterStyle.applyTo(painter, mPen);
17116  mScatterStyle.drawShape(painter, QRectF(rect).center());
17117  }
17118  }
17119 }
17120 
17126 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
17127 {
17128  // draw scatter point symbols:
17130  mScatterStyle.applyTo(painter, mPen);
17131  for (int i=0; i<pointData->size(); ++i)
17132  mScatterStyle.drawShape(painter, pointData->at(i));
17133 }
17134 
17141 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
17142 {
17143  /* Extended sides of axis rect R divide space into 9 regions:
17144  1__|_4_|__7
17145  2__|_R_|__8
17146  3 | 6 | 9
17147  General idea: If the two points of a line segment are in the same region (that is not R), the line segment corner is removed.
17148  Curves outside R become straight lines closely outside of R which greatly reduces drawing time, yet keeps the look of lines and
17149  fills inside R consistent.
17150  The region R has index 5.
17151  */
17152  QCPAxis *keyAxis = mKeyAxis.data();
17153  QCPAxis *valueAxis = mValueAxis.data();
17154  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17155 
17156  QRect axisRect = mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
17157  lineData->reserve(mData->size());
17158  QCPCurveDataMap::const_iterator it;
17159  int lastRegion = 5;
17160  int currentRegion = 5;
17161  double RLeft = keyAxis->range().lower;
17162  double RRight = keyAxis->range().upper;
17163  double RBottom = valueAxis->range().lower;
17164  double RTop = valueAxis->range().upper;
17165  double x, y; // current key/value
17166  bool addedLastAlready = true;
17167  bool firstPoint = true; // first point must always be drawn, to make sure fill works correctly
17168  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17169  {
17170  x = it.value().key;
17171  y = it.value().value;
17172  // determine current region:
17173  if (x < RLeft) // region 123
17174  {
17175  if (y > RTop)
17176  currentRegion = 1;
17177  else if (y < RBottom)
17178  currentRegion = 3;
17179  else
17180  currentRegion = 2;
17181  } else if (x > RRight) // region 789
17182  {
17183  if (y > RTop)
17184  currentRegion = 7;
17185  else if (y < RBottom)
17186  currentRegion = 9;
17187  else
17188  currentRegion = 8;
17189  } else // region 456
17190  {
17191  if (y > RTop)
17192  currentRegion = 4;
17193  else if (y < RBottom)
17194  currentRegion = 6;
17195  else
17196  currentRegion = 5;
17197  }
17198 
17199  /*
17200  Watch out, the next part is very tricky. It modifies the curve such that it seems like the
17201  whole thing is still drawn, but actually the points outside the axisRect are simplified
17202  ("optimized") greatly. There are some subtle special cases when line segments are large and
17203  thereby each subsequent point may be in a different region or even skip some.
17204  */
17205  // determine whether to keep current point:
17206  if (currentRegion == 5 || (firstPoint && mBrush.style() != Qt::NoBrush)) // current is in R, add current and last if it wasn't added already
17207  {
17208  if (!addedLastAlready) // in case curve just entered R, make sure the last point outside R is also drawn correctly
17209  lineData->append(coordsToPixels((it-1).value().key, (it-1).value().value)); // add last point to vector
17210  else if (lastRegion != 5) // added last already. If that's the case, we probably added it at optimized position. So go back and make sure it's at original position (else the angle changes under which this segment enters R)
17211  {
17212  if (!firstPoint) // because on firstPoint, currentRegion is 5 and addedLastAlready is true, although there is no last point
17213  lineData->replace(lineData->size()-1, coordsToPixels((it-1).value().key, (it-1).value().value));
17214  }
17215  lineData->append(coordsToPixels(it.value().key, it.value().value)); // add current point to vector
17216  addedLastAlready = true; // so in next iteration, we don't add this point twice
17217  } else if (currentRegion != lastRegion) // changed region, add current and last if not added already
17218  {
17219  // using outsideCoordsToPixels instead of coorsToPixels for optimized point placement (places points just outside axisRect instead of potentially far away)
17220 
17221  // if we're coming from R or we skip diagonally over the corner regions (so line might still be visible in R), we can't place points optimized
17222  if (lastRegion == 5 || // coming from R
17223  ((lastRegion==2 && currentRegion==4) || (lastRegion==4 && currentRegion==2)) || // skip top left diagonal
17224  ((lastRegion==4 && currentRegion==8) || (lastRegion==8 && currentRegion==4)) || // skip top right diagonal
17225  ((lastRegion==8 && currentRegion==6) || (lastRegion==6 && currentRegion==8)) || // skip bottom right diagonal
17226  ((lastRegion==6 && currentRegion==2) || (lastRegion==2 && currentRegion==6)) // skip bottom left diagonal
17227  )
17228  {
17229  // always add last point if not added already, original:
17230  if (!addedLastAlready)
17231  lineData->append(coordsToPixels((it-1).value().key, (it-1).value().value));
17232  // add current point, original:
17233  lineData->append(coordsToPixels(it.value().key, it.value().value));
17234  } else // no special case that forbids optimized point placement, so do it:
17235  {
17236  // always add last point if not added already, optimized:
17237  if (!addedLastAlready)
17238  lineData->append(outsideCoordsToPixels((it-1).value().key, (it-1).value().value, currentRegion, axisRect));
17239  // add current point, optimized:
17240  lineData->append(outsideCoordsToPixels(it.value().key, it.value().value, currentRegion, axisRect));
17241  }
17242  addedLastAlready = true; // so that if next point enters 5, or crosses another region boundary, we don't add this point twice
17243  } else // neither in R, nor crossed a region boundary, skip current point
17244  {
17245  addedLastAlready = false;
17246  }
17247  lastRegion = currentRegion;
17248  firstPoint = false;
17249  }
17250  // If curve ends outside R, we want to add very last point so the fill looks like it should when the curve started inside R:
17251  if (lastRegion != 5 && mBrush.style() != Qt::NoBrush && !mData->isEmpty())
17252  lineData->append(coordsToPixels((mData->constEnd()-1).value().key, (mData->constEnd()-1).value().value));
17253 }
17254 
17261 double QCPCurve::pointDistance(const QPointF &pixelPoint) const
17262 {
17263  if (mData->isEmpty())
17264  {
17265  qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
17266  return 500;
17267  }
17268  if (mData->size() == 1)
17269  {
17270  QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
17271  return QVector2D(dataPoint-pixelPoint).length();
17272  }
17273 
17274  // calculate minimum distance to line segments:
17275  QVector<QPointF> *lineData = new QVector<QPointF>;
17276  getCurveData(lineData);
17277  double minDistSqr = std::numeric_limits<double>::max();
17278  for (int i=0; i<lineData->size()-1; ++i)
17279  {
17280  double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
17281  if (currentDistSqr < minDistSqr)
17282  minDistSqr = currentDistSqr;
17283  }
17284  delete lineData;
17285  return sqrt(minDistSqr);
17286 }
17287 
17299 QPointF QCPCurve::outsideCoordsToPixels(double key, double value, int region, QRect axisRect) const
17300 {
17301  int margin = qCeil(qMax(mScatterStyle.size(), (double)mPen.widthF())) + 2;
17302  QPointF result = coordsToPixels(key, value);
17303  switch (region)
17304  {
17305  case 2: result.setX(axisRect.left()-margin); break; // left
17306  case 8: result.setX(axisRect.right()+margin); break; // right
17307  case 4: result.setY(axisRect.top()-margin); break; // top
17308  case 6: result.setY(axisRect.bottom()+margin); break; // bottom
17309  case 1: result.setX(axisRect.left()-margin);
17310  result.setY(axisRect.top()-margin); break; // top left
17311  case 7: result.setX(axisRect.right()+margin);
17312  result.setY(axisRect.top()-margin); break; // top right
17313  case 9: result.setX(axisRect.right()+margin);
17314  result.setY(axisRect.bottom()+margin); break; // bottom right
17315  case 3: result.setX(axisRect.left()-margin);
17316  result.setY(axisRect.bottom()+margin); break; // bottom left
17317  }
17318  return result;
17319 }
17320 
17321 /* inherits documentation from base class */
17322 QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17323 {
17324  QCPRange range;
17325  bool haveLower = false;
17326  bool haveUpper = false;
17327 
17328  double current;
17329 
17330  QCPCurveDataMap::const_iterator it = mData->constBegin();
17331  while (it != mData->constEnd())
17332  {
17333  current = it.value().key;
17334  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17335  {
17336  if (current < range.lower || !haveLower)
17337  {
17338  range.lower = current;
17339  haveLower = true;
17340  }
17341  if (current > range.upper || !haveUpper)
17342  {
17343  range.upper = current;
17344  haveUpper = true;
17345  }
17346  }
17347  ++it;
17348  }
17349 
17350  foundRange = haveLower && haveUpper;
17351  return range;
17352 }
17353 
17354 /* inherits documentation from base class */
17355 QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17356 {
17357  QCPRange range;
17358  bool haveLower = false;
17359  bool haveUpper = false;
17360 
17361  double current;
17362 
17363  QCPCurveDataMap::const_iterator it = mData->constBegin();
17364  while (it != mData->constEnd())
17365  {
17366  current = it.value().value;
17367  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17368  {
17369  if (current < range.lower || !haveLower)
17370  {
17371  range.lower = current;
17372  haveLower = true;
17373  }
17374  if (current > range.upper || !haveUpper)
17375  {
17376  range.upper = current;
17377  haveUpper = true;
17378  }
17379  }
17380  ++it;
17381  }
17382 
17383  foundRange = haveLower && haveUpper;
17384  return range;
17385 }
17386 
17387 
17391 
17408  key(0),
17409  value(0)
17410 {
17411 }
17412 
17416 QCPBarData::QCPBarData(double key, double value) :
17417  key(key),
17418  value(value)
17419 {
17420 }
17421 
17422 
17426 
17483 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
17484  QCPAbstractPlottable(keyAxis, valueAxis)
17485 {
17486  mData = new QCPBarDataMap;
17487  mPen.setColor(Qt::blue);
17488  mPen.setStyle(Qt::SolidLine);
17489  mBrush.setColor(QColor(40, 50, 255, 30));
17490  mBrush.setStyle(Qt::SolidPattern);
17491  mSelectedPen = mPen;
17492  mSelectedPen.setWidthF(2.5);
17493  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
17495 
17496  mWidth = 0.75;
17497 }
17498 
17500 {
17501  if (mBarBelow || mBarAbove)
17502  connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
17503  delete mData;
17504 }
17505 
17509 void QCPBars::setWidth(double width)
17510 {
17511  mWidth = width;
17512 }
17513 
17522 {
17523  if (copy)
17524  {
17525  *mData = *data;
17526  } else
17527  {
17528  delete mData;
17529  mData = data;
17530  }
17531 }
17532 
17539 void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
17540 {
17541  mData->clear();
17542  int n = key.size();
17543  n = qMin(n, value.size());
17544  QCPBarData newData;
17545  for (int i=0; i<n; ++i)
17546  {
17547  newData.key = key[i];
17548  newData.value = value[i];
17549  mData->insertMulti(newData.key, newData);
17550  }
17551 }
17552 
17568 {
17569  if (bars == this) return;
17570  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
17571  {
17572  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
17573  return;
17574  }
17575  // remove from stacking:
17576  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
17577  // if new bar given, insert this bar below it:
17578  if (bars)
17579  {
17580  if (bars->mBarBelow)
17581  connectBars(bars->mBarBelow.data(), this);
17582  connectBars(this, bars);
17583  }
17584 }
17585 
17601 {
17602  if (bars == this) return;
17603  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
17604  {
17605  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
17606  return;
17607  }
17608  // remove from stacking:
17609  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
17610  // if new bar given, insert this bar above it:
17611  if (bars)
17612  {
17613  if (bars->mBarAbove)
17614  connectBars(this, bars->mBarAbove.data());
17615  connectBars(bars, this);
17616  }
17617 }
17618 
17623 void QCPBars::addData(const QCPBarDataMap &dataMap)
17624 {
17625  mData->unite(dataMap);
17626 }
17627 
17633 {
17634  mData->insertMulti(data.key, data);
17635 }
17636 
17641 void QCPBars::addData(double key, double value)
17642 {
17643  QCPBarData newData;
17644  newData.key = key;
17645  newData.value = value;
17646  mData->insertMulti(newData.key, newData);
17647 }
17648 
17653 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
17654 {
17655  int n = keys.size();
17656  n = qMin(n, values.size());
17657  QCPBarData newData;
17658  for (int i=0; i<n; ++i)
17659  {
17660  newData.key = keys[i];
17661  newData.value = values[i];
17662  mData->insertMulti(newData.key, newData);
17663  }
17664 }
17665 
17671 {
17672  QCPBarDataMap::iterator it = mData->begin();
17673  while (it != mData->end() && it.key() < key)
17674  it = mData->erase(it);
17675 }
17676 
17682 {
17683  if (mData->isEmpty()) return;
17684  QCPBarDataMap::iterator it = mData->upperBound(key);
17685  while (it != mData->end())
17686  it = mData->erase(it);
17687 }
17688 
17696 void QCPBars::removeData(double fromKey, double toKey)
17697 {
17698  if (fromKey >= toKey || mData->isEmpty()) return;
17699  QCPBarDataMap::iterator it = mData->upperBound(fromKey);
17700  QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
17701  while (it != itEnd)
17702  it = mData->erase(it);
17703 }
17704 
17713 void QCPBars::removeData(double key)
17714 {
17715  mData->remove(key);
17716 }
17717 
17723 {
17724  mData->clear();
17725 }
17726 
17727 /* inherits documentation from base class */
17728 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17729 {
17730  Q_UNUSED(details)
17731  if (onlySelectable && !mSelectable)
17732  return -1;
17733  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17734 
17735  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17736  {
17737  QCPBarDataMap::ConstIterator it;
17738  double posKey, posValue;
17739  pixelsToCoords(pos, posKey, posValue);
17740  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17741  {
17742  double baseValue = getBaseValue(it.key(), it.value().value >=0);
17743  QCPRange keyRange(it.key()-mWidth*0.5, it.key()+mWidth*0.5);
17744  QCPRange valueRange(baseValue, baseValue+it.value().value);
17745  if (keyRange.contains(posKey) && valueRange.contains(posValue))
17746  return mParentPlot->selectionTolerance()*0.99;
17747  }
17748  }
17749  return -1;
17750 }
17751 
17752 /* inherits documentation from base class */
17754 {
17755  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17756  if (mData->isEmpty()) return;
17757 
17758  QCPBarDataMap::const_iterator it;
17759  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17760  {
17761  // skip bar if not visible in key axis range:
17762  if (it.key()+mWidth*0.5 < mKeyAxis.data()->range().lower || it.key()-mWidth*0.5 > mKeyAxis.data()->range().upper)
17763  continue;
17764  // check data validity if flag set:
17765 #ifdef QCUSTOMPLOT_CHECK_DATA
17766  if (QCP::isInvalidData(it.value().key, it.value().value))
17767  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
17768 #endif
17769  QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
17770  // draw bar fill:
17771  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17772  {
17773  applyFillAntialiasingHint(painter);
17774  painter->setPen(Qt::NoPen);
17775  painter->setBrush(mainBrush());
17776  painter->drawPolygon(barPolygon);
17777  }
17778  // draw bar line:
17779  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17780  {
17782  painter->setPen(mainPen());
17783  painter->setBrush(Qt::NoBrush);
17784  painter->drawPolyline(barPolygon);
17785  }
17786  }
17787 }
17788 
17789 /* inherits documentation from base class */
17790 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17791 {
17792  // draw filled rect:
17794  painter->setBrush(mBrush);
17795  painter->setPen(mPen);
17796  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
17797  r.moveCenter(rect.center());
17798  painter->drawRect(r);
17799 }
17800 
17806 QPolygonF QCPBars::getBarPolygon(double key, double value) const
17807 {
17808  QPolygonF result;
17809  double baseValue = getBaseValue(key, value >= 0);
17810  result << coordsToPixels(key-mWidth*0.5, baseValue);
17811  result << coordsToPixels(key-mWidth*0.5, baseValue+value);
17812  result << coordsToPixels(key+mWidth*0.5, baseValue+value);
17813  result << coordsToPixels(key+mWidth*0.5, baseValue);
17814  return result;
17815 }
17816 
17826 double QCPBars::getBaseValue(double key, bool positive) const
17827 {
17828  if (mBarBelow)
17829  {
17830  double max = 0;
17831  // find bars of mBarBelow that are approximately at key and find largest one:
17832  QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-mWidth*0.1);
17833  QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+mWidth*0.1);
17834  while (it != itEnd)
17835  {
17836  if ((positive && it.value().value > max) ||
17837  (!positive && it.value().value < max))
17838  max = it.value().value;
17839  ++it;
17840  }
17841  // recurse down the bar-stack to find the total height:
17842  return max + mBarBelow.data()->getBaseValue(key, positive);
17843  } else
17844  return 0;
17845 }
17846 
17856 {
17857  if (!lower && !upper) return;
17858 
17859  if (!lower) // disconnect upper at bottom
17860  {
17861  // disconnect old bar below upper:
17862  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
17863  upper->mBarBelow.data()->mBarAbove = 0;
17864  upper->mBarBelow = 0;
17865  } else if (!upper) // disconnect lower at top
17866  {
17867  // disconnect old bar above lower:
17868  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
17869  lower->mBarAbove.data()->mBarBelow = 0;
17870  lower->mBarAbove = 0;
17871  } else // connect lower and upper
17872  {
17873  // disconnect old bar above lower:
17874  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
17875  lower->mBarAbove.data()->mBarBelow = 0;
17876  // disconnect old bar below upper:
17877  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
17878  upper->mBarBelow.data()->mBarAbove = 0;
17879  lower->mBarAbove = upper;
17880  upper->mBarBelow = lower;
17881  }
17882 }
17883 
17884 /* inherits documentation from base class */
17885 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17886 {
17887  QCPRange range;
17888  bool haveLower = false;
17889  bool haveUpper = false;
17890 
17891  double current;
17892  double barWidthHalf = mWidth*0.5;
17893  QCPBarDataMap::const_iterator it = mData->constBegin();
17894  while (it != mData->constEnd())
17895  {
17896  current = it.value().key;
17897  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current+barWidthHalf < 0) || (inSignDomain == sdPositive && current-barWidthHalf > 0))
17898  {
17899  if (current-barWidthHalf < range.lower || !haveLower)
17900  {
17901  range.lower = current-barWidthHalf;
17902  haveLower = true;
17903  }
17904  if (current+barWidthHalf > range.upper || !haveUpper)
17905  {
17906  range.upper = current+barWidthHalf;
17907  haveUpper = true;
17908  }
17909  }
17910  ++it;
17911  }
17912 
17913  foundRange = haveLower && haveUpper;
17914  return range;
17915 }
17916 
17917 /* inherits documentation from base class */
17918 QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17919 {
17920  QCPRange range;
17921  bool haveLower = true; // set to true, because 0 should always be visible in bar charts
17922  bool haveUpper = true; // set to true, because 0 should always be visible in bar charts
17923 
17924  double current;
17925 
17926  QCPBarDataMap::const_iterator it = mData->constBegin();
17927  while (it != mData->constEnd())
17928  {
17929  current = it.value().value + getBaseValue(it.value().key, it.value().value >= 0);
17930  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17931  {
17932  if (current < range.lower || !haveLower)
17933  {
17934  range.lower = current;
17935  haveLower = true;
17936  }
17937  if (current > range.upper || !haveUpper)
17938  {
17939  range.upper = current;
17940  haveUpper = true;
17941  }
17942  }
17943  ++it;
17944  }
17945 
17946  foundRange = true; // return true because bar charts always have the 0-line visible
17947  return range;
17948 }
17949 
17950 
17954 
18018  QCPAbstractPlottable(keyAxis, valueAxis),
18019  mKey(0),
18020  mMinimum(0),
18021  mLowerQuartile(0),
18022  mMedian(0),
18023  mUpperQuartile(0),
18024  mMaximum(0)
18025 {
18027  setWhiskerWidth(0.2);
18028  setWidth(0.5);
18029 
18030  setPen(QPen(Qt::black));
18031  setSelectedPen(QPen(Qt::blue, 2.5));
18032  setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
18033  setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
18034  setWhiskerBarPen(QPen(Qt::black));
18035  setBrush(Qt::NoBrush);
18036  setSelectedBrush(Qt::NoBrush);
18037 }
18038 
18043 {
18044  mKey = key;
18045 }
18046 
18054 {
18055  mMinimum = value;
18056 }
18057 
18066 {
18067  mLowerQuartile = value;
18068 }
18069 
18078 {
18079  mMedian = value;
18080 }
18081 
18090 {
18091  mUpperQuartile = value;
18092 }
18093 
18101 {
18102  mMaximum = value;
18103 }
18104 
18112 void QCPStatisticalBox::setOutliers(const QVector<double> &values)
18113 {
18114  mOutliers = values;
18115 }
18116 
18122 void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
18123 {
18124  setKey(key);
18127  setMedian(median);
18130 }
18131 
18138 {
18139  mWidth = width;
18140 }
18141 
18148 {
18149  mWhiskerWidth = width;
18150 }
18151 
18161 {
18162  mWhiskerPen = pen;
18163 }
18164 
18172 {
18173  mWhiskerBarPen = pen;
18174 }
18175 
18179 void QCPStatisticalBox::setMedianPen(const QPen &pen)
18180 {
18181  mMedianPen = pen;
18182 }
18183 
18190 {
18191  mOutlierStyle = style;
18192 }
18193 
18194 /* inherits documentation from base class */
18196 {
18197  setOutliers(QVector<double>());
18198  setKey(0);
18199  setMinimum(0);
18200  setLowerQuartile(0);
18201  setMedian(0);
18202  setUpperQuartile(0);
18203  setMaximum(0);
18204 }
18205 
18206 /* inherits documentation from base class */
18207 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18208 {
18209  Q_UNUSED(details)
18210  if (onlySelectable && !mSelectable)
18211  return -1;
18212  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
18213 
18214  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
18215  {
18216  double posKey, posValue;
18217  pixelsToCoords(pos, posKey, posValue);
18218  // quartile box:
18219  QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
18220  QCPRange valueRange(mLowerQuartile, mUpperQuartile);
18221  if (keyRange.contains(posKey) && valueRange.contains(posValue))
18222  return mParentPlot->selectionTolerance()*0.99;
18223 
18224  // min/max whiskers:
18225  if (QCPRange(mMinimum, mMaximum).contains(posValue))
18226  return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
18227  }
18228  return -1;
18229 }
18230 
18231 /* inherits documentation from base class */
18233 {
18234  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
18235 
18236  // check data validity if flag set:
18237 #ifdef QCUSTOMPLOT_CHECK_DATA
18241  qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
18242  for (int i=0; i<mOutliers.size(); ++i)
18243  if (QCP::isInvalidData(mOutliers.at(i)))
18244  qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
18245 #endif
18246 
18247  QRectF quartileBox;
18248  drawQuartileBox(painter, &quartileBox);
18249 
18250  painter->save();
18251  painter->setClipRect(quartileBox, Qt::IntersectClip);
18252  drawMedian(painter);
18253  painter->restore();
18254 
18255  drawWhiskers(painter);
18256  drawOutliers(painter);
18257 }
18258 
18259 /* inherits documentation from base class */
18260 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
18261 {
18262  // draw filled rect:
18264  painter->setPen(mPen);
18265  painter->setBrush(mBrush);
18266  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
18267  r.moveCenter(rect.center());
18268  painter->drawRect(r);
18269 }
18270 
18277 void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
18278 {
18279  QRectF box;
18280  box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
18281  box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
18283  painter->setPen(mainPen());
18284  painter->setBrush(mainBrush());
18285  painter->drawRect(box);
18286  if (quartileBox)
18287  *quartileBox = box;
18288 }
18289 
18295 {
18296  QLineF medianLine;
18297  medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
18298  medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
18300  painter->setPen(mMedianPen);
18301  painter->drawLine(medianLine);
18302 }
18303 
18309 {
18310  QLineF backboneMin, backboneMax, barMin, barMax;
18311  backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
18312  backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
18316  painter->setPen(mWhiskerPen);
18317  painter->drawLine(backboneMin);
18318  painter->drawLine(backboneMax);
18319  painter->setPen(mWhiskerBarPen);
18320  painter->drawLine(barMin);
18321  painter->drawLine(barMax);
18322 }
18323 
18329 {
18331  mOutlierStyle.applyTo(painter, mPen);
18332  for (int i=0; i<mOutliers.size(); ++i)
18334 }
18335 
18336 /* inherits documentation from base class */
18337 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
18338 {
18339  foundRange = true;
18340  if (inSignDomain == sdBoth)
18341  {
18342  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
18343  } else if (inSignDomain == sdNegative)
18344  {
18345  if (mKey+mWidth*0.5 < 0)
18346  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
18347  else if (mKey < 0)
18348  return QCPRange(mKey-mWidth*0.5, mKey);
18349  else
18350  {
18351  foundRange = false;
18352  return QCPRange();
18353  }
18354  } else if (inSignDomain == sdPositive)
18355  {
18356  if (mKey-mWidth*0.5 > 0)
18357  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
18358  else if (mKey > 0)
18359  return QCPRange(mKey, mKey+mWidth*0.5);
18360  else
18361  {
18362  foundRange = false;
18363  return QCPRange();
18364  }
18365  }
18366  foundRange = false;
18367  return QCPRange();
18368 }
18369 
18370 /* inherits documentation from base class */
18371 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
18372 {
18373  QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
18374  values.reserve(mOutliers.size() + 5);
18375  values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
18376  values << mOutliers;
18377  // go through values and find the ones in legal range:
18378  bool haveUpper = false;
18379  bool haveLower = false;
18380  double upper = 0;
18381  double lower = 0;
18382  for (int i=0; i<values.size(); ++i)
18383  {
18384  if ((inSignDomain == sdNegative && values.at(i) < 0) ||
18385  (inSignDomain == sdPositive && values.at(i) > 0) ||
18386  (inSignDomain == sdBoth))
18387  {
18388  if (values.at(i) > upper || !haveUpper)
18389  {
18390  upper = values.at(i);
18391  haveUpper = true;
18392  }
18393  if (values.at(i) < lower || !haveLower)
18394  {
18395  lower = values.at(i);
18396  haveLower = true;
18397  }
18398  }
18399  }
18400  // return the bounds if we found some sensible values:
18401  if (haveLower && haveUpper)
18402  {
18403  foundRange = true;
18404  return QCPRange(lower, upper);
18405  } else // might happen if all values are in other sign domain
18406  {
18407  foundRange = false;
18408  return QCPRange();
18409  }
18410 }
18411 
18412 
18416 
18445 /* start of documentation of inline functions */
18446 
18453 /* end of documentation of inline functions */
18454 
18462 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
18463  mKeySize(0),
18464  mValueSize(0),
18465  mKeyRange(keyRange),
18466  mValueRange(valueRange),
18467  mIsEmpty(true),
18468  mData(0),
18469  mDataModified(true)
18470 {
18472  fill(0);
18473 }
18474 
18476 {
18477  if (mData)
18478  delete[] mData;
18479 }
18480 
18485  mKeySize(0),
18486  mValueSize(0),
18487  mIsEmpty(true),
18488  mData(0),
18489  mDataModified(true)
18490 {
18491  *this = other;
18492 }
18493 
18498 {
18499  if (&other != this)
18500  {
18501  const int keySize = other.keySize();
18502  const int valueSize = other.valueSize();
18504  setRange(other.keyRange(), other.valueRange());
18505  if (!mIsEmpty)
18506  memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
18507  mDataBounds = other.mDataBounds;
18508  mDataModified = true;
18509  }
18510  return *this;
18511 }
18512 
18513 /* undocumented getter */
18514 double QCPColorMapData::data(double key, double value)
18515 {
18516  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
18517  int valueCell = (1.0-(value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower))*(mValueSize-1)+0.5;
18518  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
18519  return mData[valueCell*mKeySize + keyCell];
18520  else
18521  return 0;
18522 }
18523 
18524 /* undocumented getter */
18525 double QCPColorMapData::cell(int keyIndex, int valueIndex)
18526 {
18527  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
18528  return mData[valueIndex*mKeySize + keyIndex];
18529  else
18530  return 0;
18531 }
18532 
18545 void QCPColorMapData::setSize(int keySize, int valueSize)
18546 {
18547  if (keySize != mKeySize || valueSize != mValueSize)
18548  {
18549  mKeySize = keySize;
18551  if (mData)
18552  delete[] mData;
18553  mIsEmpty = mKeySize == 0 || mValueSize == 0;
18554  if (!mIsEmpty)
18555  {
18556 #ifdef __EXCEPTIONS
18557  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
18558 #endif
18559  mData = new double[mKeySize*mValueSize];
18560 #ifdef __EXCEPTIONS
18561  } catch (...) { mData = 0; }
18562 #endif
18563  if (mData)
18564  fill(0);
18565  else
18566  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
18567  } else
18568  mData = 0;
18569  mDataModified = true;
18570  }
18571 }
18572 
18584 {
18586 }
18587 
18599 {
18601 }
18602 
18613 void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
18614 {
18617 }
18618 
18630 {
18631  mKeyRange = keyRange;
18632 }
18633 
18645 {
18647 }
18648 
18655 void QCPColorMapData::setData(double key, double value, double z)
18656 {
18657  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
18658  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
18659  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
18660  {
18661  mData[valueCell*mKeySize + keyCell] = z;
18662  if (z < mDataBounds.lower)
18663  mDataBounds.lower = z;
18664  if (z > mDataBounds.upper)
18665  mDataBounds.upper = z;
18666  mDataModified = true;
18667  }
18668 }
18669 
18681 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
18682 {
18683  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
18684  {
18685  mData[valueIndex*mKeySize + keyIndex] = z;
18686  if (z < mDataBounds.lower)
18687  mDataBounds.lower = z;
18688  if (z > mDataBounds.upper)
18689  mDataBounds.upper = z;
18690  mDataModified = true;
18691  }
18692 }
18693 
18708 {
18709  if (mKeySize > 0 && mValueSize > 0)
18710  {
18711  double minHeight = mData[0];
18712  double maxHeight = mData[0];
18713  const int dataCount = mValueSize*mKeySize;
18714  for (int i=0; i<dataCount; ++i)
18715  {
18716  if (mData[i] > maxHeight)
18717  maxHeight = mData[i];
18718  if (mData[i] < minHeight)
18719  minHeight = mData[i];
18720  }
18721  mDataBounds.lower = minHeight;
18722  mDataBounds.upper = maxHeight;
18723  }
18724 }
18725 
18732 {
18733  setSize(0, 0);
18734 }
18735 
18740 {
18741  const int dataCount = mValueSize*mKeySize;
18742  for (int i=0; i<dataCount; ++i)
18743  mData[i] = z;
18744  mDataBounds = QCPRange(z, z);
18745 }
18746 
18759 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
18760 {
18761  if (keyIndex)
18762  *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
18763  if (valueIndex)
18764  *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
18765 }
18766 
18777 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
18778 {
18779  if (key)
18780  *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
18781  if (value)
18782  *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
18783 }
18784 
18785 
18789 
18862 /* start documentation of inline functions */
18863 
18872 /* end documentation of inline functions */
18873 
18874 /* start documentation of signals */
18875 
18897 /* end documentation of signals */
18898 
18906  QCPAbstractPlottable(keyAxis, valueAxis),
18907  mDataScaleType(QCPAxis::stLinear),
18908  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
18909  mInterpolate(true),
18910  mTightBoundary(false),
18911  mMapImageInvalidated(true)
18912 {
18913 }
18914 
18916 {
18917  delete mMapData;
18918 }
18919 
18928 {
18929  if (copy)
18930  {
18931  *mMapData = *data;
18932  } else
18933  {
18934  delete mMapData;
18935  mMapData = data;
18936  }
18937  mMapImageInvalidated = true;
18938 }
18939 
18948 void QCPColorMap::setDataRange(const QCPRange &dataRange)
18949 {
18950  if (!QCPRange::validRange(dataRange)) return;
18952  {
18955  else
18957  mMapImageInvalidated = true;
18959  }
18960 }
18961 
18968 {
18969  if (mDataScaleType != scaleType)
18970  {
18971  mDataScaleType = scaleType;
18972  mMapImageInvalidated = true;
18976  }
18977 }
18978 
18991 {
18992  if (mGradient != gradient)
18993  {
18994  mGradient = gradient;
18995  mMapImageInvalidated = true;
18996  emit gradientChanged(mGradient);
18997  }
18998 }
18999 
19007 {
19008  mInterpolate = enabled;
19009 }
19010 
19023 {
19024  mTightBoundary = enabled;
19025 }
19026 
19042 {
19043  if (mColorScale) // unconnect signals from old color scale
19044  {
19045  disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
19046  disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
19047  disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
19048  disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19049  disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
19050  disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
19051  }
19053  if (mColorScale) // connect signals to new color scale
19054  {
19055  setGradient(mColorScale.data()->gradient());
19056  setDataRange(mColorScale.data()->dataRange());
19057  setDataScaleType(mColorScale.data()->dataScaleType());
19058  connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
19060  connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
19061  connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19062  connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
19064  }
19065 }
19066 
19087 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
19088 {
19089  if (recalculateDataBounds)
19092 }
19093 
19108 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
19109 {
19110  if (mMapImage.isNull() && !data()->isEmpty())
19111  updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
19112 
19113  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
19114  {
19115  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
19116  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
19117  mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
19118  }
19119 }
19120 
19126 {
19127  mMapData->clear();
19128 }
19129 
19130 /* inherits documentation from base class */
19131 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19132 {
19133  Q_UNUSED(details)
19134  if (onlySelectable && !mSelectable)
19135  return -1;
19136  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19137 
19138  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19139  {
19140  double posKey, posValue;
19141  pixelsToCoords(pos, posKey, posValue);
19142  if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
19143  return mParentPlot->selectionTolerance()*0.99;
19144  }
19145  return -1;
19146 }
19147 
19158 {
19159  QCPAxis *keyAxis = mKeyAxis.data();
19160  if (!keyAxis) return;
19161 
19162  // resize mMapImage to correct dimensions, according to key/value axes orientation:
19163  if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.size().width() != mMapData->keySize() || mMapImage.size().height() != mMapData->valueSize()))
19164  mMapImage = QImage(QSize(mMapData->keySize(), mMapData->valueSize()), QImage::Format_RGB32);
19165  else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.size().width() != mMapData->valueSize() || mMapImage.size().height() != mMapData->keySize()))
19166  mMapImage = QImage(QSize(mMapData->valueSize(), mMapData->keySize()), QImage::Format_RGB32);
19167 
19168  const int keySize = mMapData->keySize();
19169  const int valueSize = mMapData->valueSize();
19170  const double *rawData = mMapData->mData;
19171 
19172  if (keyAxis->orientation() == Qt::Horizontal)
19173  {
19174  const int lineCount = valueSize;
19175  const int rowCount = keySize;
19176  for (int line=0; line<lineCount; ++line)
19177  {
19178  QRgb* pixels = reinterpret_cast<QRgb*>(mMapImage.scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
19179  mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
19180  }
19181  } else // keyAxis->orientation() == Qt::Vertical
19182  {
19183  const int lineCount = keySize;
19184  const int rowCount = valueSize;
19185  for (int line=0; line<lineCount; ++line)
19186  {
19187  QRgb* pixels = reinterpret_cast<QRgb*>(mMapImage.scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
19188  mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
19189  }
19190  }
19191 
19192  mMapData->mDataModified = false;
19193  mMapImageInvalidated = false;
19194 }
19195 
19196 /* inherits documentation from base class */
19198 {
19199  if (mMapData->isEmpty()) return;
19200  if (!mKeyAxis || !mValueAxis) return;
19202 
19204  updateMapImage();
19205 
19206  double halfSampleKey = 0;
19207  double halfSampleValue = 0;
19208  if (mMapData->keySize() > 1)
19209  halfSampleKey = 0.5*mMapData->keyRange().size()/(double)(mMapData->keySize()-1);
19210  if (mMapData->valueSize() > 1)
19211  halfSampleValue = 0.5*mMapData->valueRange().size()/(double)(mMapData->valueSize()-1);
19212  QRectF imageRect(coordsToPixels(mMapData->keyRange().lower-halfSampleKey, mMapData->valueRange().lower-halfSampleValue),
19213  coordsToPixels(mMapData->keyRange().upper+halfSampleKey, mMapData->valueRange().upper+halfSampleValue));
19214  imageRect = imageRect.normalized();
19215  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
19216  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
19217  bool smoothBackup = painter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
19218  painter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
19219  QRegion clipBackup;
19220  if (mTightBoundary)
19221  {
19222  clipBackup = painter->clipRegion();
19223  painter->setClipRect(QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
19224  coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(), Qt::IntersectClip);
19225  }
19226  painter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
19227  if (mTightBoundary)
19228  painter->setClipRegion(clipBackup);
19229  painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
19230 }
19231 
19232 /* inherits documentation from base class */
19233 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19234 {
19236  // draw map thumbnail:
19237  if (!mLegendIcon.isNull())
19238  {
19239  QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
19240  QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
19241  iconRect.moveCenter(rect.center());
19242  painter->drawPixmap(iconRect.topLeft(), scaledIcon);
19243  }
19244  /*
19245  // draw frame:
19246  painter->setBrush(Qt::NoBrush);
19247  painter->setPen(Qt::black);
19248  painter->drawRect(rect.adjusted(1, 1, 0, 0));
19249  */
19250 }
19251 
19252 /* inherits documentation from base class */
19253 QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19254 {
19255  foundRange = true;
19256  QCPRange result = mMapData->keyRange();
19257  result.normalize();
19258  if (inSignDomain == QCPAbstractPlottable::sdPositive)
19259  {
19260  if (result.lower <= 0 && result.upper > 0)
19261  result.lower = result.upper*1e-3;
19262  else if (result.lower <= 0 && result.upper <= 0)
19263  foundRange = false;
19264  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
19265  {
19266  if (result.upper >= 0 && result.lower < 0)
19267  result.upper = result.lower*1e-3;
19268  else if (result.upper >= 0 && result.lower >= 0)
19269  foundRange = false;
19270  }
19271  return result;
19272 }
19273 
19274 /* inherits documentation from base class */
19275 QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19276 {
19277  foundRange = true;
19278  QCPRange result = mMapData->valueRange();
19279  result.normalize();
19280  if (inSignDomain == QCPAbstractPlottable::sdPositive)
19281  {
19282  if (result.lower <= 0 && result.upper > 0)
19283  result.lower = result.upper*1e-3;
19284  else if (result.lower <= 0 && result.upper <= 0)
19285  foundRange = false;
19286  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
19287  {
19288  if (result.upper >= 0 && result.lower < 0)
19289  result.upper = result.lower*1e-3;
19290  else if (result.upper >= 0 && result.lower >= 0)
19291  foundRange = false;
19292  }
19293  return result;
19294 }
19295 
19296 
19300 
19315  QCPAbstractItem(parentPlot),
19316  point1(createPosition("point1")),
19317  point2(createPosition("point2"))
19318 {
19319  point1->setCoords(0, 0);
19320  point2->setCoords(1, 1);
19321 
19322  setPen(QPen(Qt::black));
19323  setSelectedPen(QPen(Qt::blue,2));
19324 }
19325 
19327 {
19328 }
19329 
19335 void QCPItemStraightLine::setPen(const QPen &pen)
19336 {
19337  mPen = pen;
19338 }
19339 
19346 {
19347  mSelectedPen = pen;
19348 }
19349 
19350 /* inherits documentation from base class */
19351 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19352 {
19353  Q_UNUSED(details)
19354  if (onlySelectable && !mSelectable)
19355  return -1;
19356 
19357  return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
19358 }
19359 
19360 /* inherits documentation from base class */
19362 {
19363  QVector2D start(point1->pixelPoint());
19364  QVector2D end(point2->pixelPoint());
19365  // get visible segment of straight line inside clipRect:
19366  double clipPad = mainPen().widthF();
19367  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
19368  // paint visible segment, if existent:
19369  if (!line.isNull())
19370  {
19371  painter->setPen(mainPen());
19372  painter->drawLine(line);
19373  }
19374 }
19375 
19383 double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
19384 {
19385  return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
19386 }
19387 
19395 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
19396 {
19397  double bx, by;
19398  double gamma;
19399  QLineF result;
19400  if (vec.x() == 0 && vec.y() == 0)
19401  return result;
19402  if (qFuzzyIsNull(vec.x())) // line is vertical
19403  {
19404  // check top of rect:
19405  bx = rect.left();
19406  by = rect.top();
19407  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
19408  if (gamma >= 0 && gamma <= rect.width())
19409  result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
19410  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
19411  {
19412  // check left of rect:
19413  bx = rect.left();
19414  by = rect.top();
19415  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
19416  if (gamma >= 0 && gamma <= rect.height())
19417  result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
19418  } else // line is skewed
19419  {
19420  QList<QVector2D> pointVectors;
19421  // check top of rect:
19422  bx = rect.left();
19423  by = rect.top();
19424  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
19425  if (gamma >= 0 && gamma <= rect.width())
19426  pointVectors.append(QVector2D(bx+gamma, by));
19427  // check bottom of rect:
19428  bx = rect.left();
19429  by = rect.bottom();
19430  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
19431  if (gamma >= 0 && gamma <= rect.width())
19432  pointVectors.append(QVector2D(bx+gamma, by));
19433  // check left of rect:
19434  bx = rect.left();
19435  by = rect.top();
19436  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
19437  if (gamma >= 0 && gamma <= rect.height())
19438  pointVectors.append(QVector2D(bx, by+gamma));
19439  // check right of rect:
19440  bx = rect.right();
19441  by = rect.top();
19442  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
19443  if (gamma >= 0 && gamma <= rect.height())
19444  pointVectors.append(QVector2D(bx, by+gamma));
19445 
19446  // evaluate points:
19447  if (pointVectors.size() == 2)
19448  {
19449  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
19450  } else if (pointVectors.size() > 2)
19451  {
19452  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
19453  double distSqrMax = 0;
19454  QVector2D pv1, pv2;
19455  for (int i=0; i<pointVectors.size()-1; ++i)
19456  {
19457  for (int k=i+1; k<pointVectors.size(); ++k)
19458  {
19459  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
19460  if (distSqr > distSqrMax)
19461  {
19462  pv1 = pointVectors.at(i);
19463  pv2 = pointVectors.at(k);
19464  distSqrMax = distSqr;
19465  }
19466  }
19467  }
19468  result.setPoints(pv1.toPointF(), pv2.toPointF());
19469  }
19470  }
19471  return result;
19472 }
19473 
19480 {
19481  return mSelected ? mSelectedPen : mPen;
19482 }
19483 
19484 
19488 
19505  QCPAbstractItem(parentPlot),
19506  start(createPosition("start")),
19507  end(createPosition("end"))
19508 {
19509  start->setCoords(0, 0);
19510  end->setCoords(1, 1);
19511 
19512  setPen(QPen(Qt::black));
19513  setSelectedPen(QPen(Qt::blue,2));
19514 }
19515 
19517 {
19518 }
19519 
19525 void QCPItemLine::setPen(const QPen &pen)
19526 {
19527  mPen = pen;
19528 }
19529 
19535 void QCPItemLine::setSelectedPen(const QPen &pen)
19536 {
19537  mSelectedPen = pen;
19538 }
19539 
19549 {
19550  mHead = head;
19551 }
19552 
19562 {
19563  mTail = tail;
19564 }
19565 
19566 /* inherits documentation from base class */
19567 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19568 {
19569  Q_UNUSED(details)
19570  if (onlySelectable && !mSelectable)
19571  return -1;
19572 
19573  return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
19574 }
19575 
19576 /* inherits documentation from base class */
19578 {
19579  QVector2D startVec(start->pixelPoint());
19580  QVector2D endVec(end->pixelPoint());
19581  if (startVec.toPoint() == endVec.toPoint())
19582  return;
19583  // get visible segment of straight line inside clipRect:
19584  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
19585  clipPad = qMax(clipPad, (double)mainPen().widthF());
19586  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
19587  // paint visible segment, if existent:
19588  if (!line.isNull())
19589  {
19590  painter->setPen(mainPen());
19591  painter->drawLine(line);
19592  painter->setBrush(Qt::SolidPattern);
19594  mTail.draw(painter, startVec, startVec-endVec);
19596  mHead.draw(painter, endVec, endVec-startVec);
19597  }
19598 }
19599 
19607 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
19608 {
19609  bool containsStart = rect.contains(start.x(), start.y());
19610  bool containsEnd = rect.contains(end.x(), end.y());
19611  if (containsStart && containsEnd)
19612  return QLineF(start.toPointF(), end.toPointF());
19613 
19614  QVector2D base = start;
19615  QVector2D vec = end-start;
19616  double bx, by;
19617  double gamma, mu;
19618  QLineF result;
19619  QList<QVector2D> pointVectors;
19620 
19621  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
19622  {
19623  // check top of rect:
19624  bx = rect.left();
19625  by = rect.top();
19626  mu = (by-base.y())/vec.y();
19627  if (mu >= 0 && mu <= 1)
19628  {
19629  gamma = base.x()-bx + mu*vec.x();
19630  if (gamma >= 0 && gamma <= rect.width())
19631  pointVectors.append(QVector2D(bx+gamma, by));
19632  }
19633  // check bottom of rect:
19634  bx = rect.left();
19635  by = rect.bottom();
19636  mu = (by-base.y())/vec.y();
19637  if (mu >= 0 && mu <= 1)
19638  {
19639  gamma = base.x()-bx + mu*vec.x();
19640  if (gamma >= 0 && gamma <= rect.width())
19641  pointVectors.append(QVector2D(bx+gamma, by));
19642  }
19643  }
19644  if (!qFuzzyIsNull(vec.x())) // line is not vertical
19645  {
19646  // check left of rect:
19647  bx = rect.left();
19648  by = rect.top();
19649  mu = (bx-base.x())/vec.x();
19650  if (mu >= 0 && mu <= 1)
19651  {
19652  gamma = base.y()-by + mu*vec.y();
19653  if (gamma >= 0 && gamma <= rect.height())
19654  pointVectors.append(QVector2D(bx, by+gamma));
19655  }
19656  // check right of rect:
19657  bx = rect.right();
19658  by = rect.top();
19659  mu = (bx-base.x())/vec.x();
19660  if (mu >= 0 && mu <= 1)
19661  {
19662  gamma = base.y()-by + mu*vec.y();
19663  if (gamma >= 0 && gamma <= rect.height())
19664  pointVectors.append(QVector2D(bx, by+gamma));
19665  }
19666  }
19667 
19668  if (containsStart)
19669  pointVectors.append(start);
19670  if (containsEnd)
19671  pointVectors.append(end);
19672 
19673  // evaluate points:
19674  if (pointVectors.size() == 2)
19675  {
19676  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
19677  } else if (pointVectors.size() > 2)
19678  {
19679  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
19680  double distSqrMax = 0;
19681  QVector2D pv1, pv2;
19682  for (int i=0; i<pointVectors.size()-1; ++i)
19683  {
19684  for (int k=i+1; k<pointVectors.size(); ++k)
19685  {
19686  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
19687  if (distSqr > distSqrMax)
19688  {
19689  pv1 = pointVectors.at(i);
19690  pv2 = pointVectors.at(k);
19691  distSqrMax = distSqr;
19692  }
19693  }
19694  }
19695  result.setPoints(pv1.toPointF(), pv2.toPointF());
19696  }
19697  return result;
19698 }
19699 
19706 {
19707  return mSelected ? mSelectedPen : mPen;
19708 }
19709 
19710 
19714 
19738  QCPAbstractItem(parentPlot),
19739  start(createPosition("start")),
19740  startDir(createPosition("startDir")),
19741  endDir(createPosition("endDir")),
19742  end(createPosition("end"))
19743 {
19744  start->setCoords(0, 0);
19745  startDir->setCoords(0.5, 0);
19746  endDir->setCoords(0, 0.5);
19747  end->setCoords(1, 1);
19748 
19749  setPen(QPen(Qt::black));
19750  setSelectedPen(QPen(Qt::blue,2));
19751 }
19752 
19754 {
19755 }
19756 
19762 void QCPItemCurve::setPen(const QPen &pen)
19763 {
19764  mPen = pen;
19765 }
19766 
19772 void QCPItemCurve::setSelectedPen(const QPen &pen)
19773 {
19774  mSelectedPen = pen;
19775 }
19776 
19786 {
19787  mHead = head;
19788 }
19789 
19799 {
19800  mTail = tail;
19801 }
19802 
19803 /* inherits documentation from base class */
19804 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19805 {
19806  Q_UNUSED(details)
19807  if (onlySelectable && !mSelectable)
19808  return -1;
19809 
19810  QPointF startVec(start->pixelPoint());
19811  QPointF startDirVec(startDir->pixelPoint());
19812  QPointF endDirVec(endDir->pixelPoint());
19813  QPointF endVec(end->pixelPoint());
19814 
19815  QPainterPath cubicPath(startVec);
19816  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
19817 
19818  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
19819  double minDistSqr = std::numeric_limits<double>::max();
19820  for (int i=1; i<polygon.size(); ++i)
19821  {
19822  double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
19823  if (distSqr < minDistSqr)
19824  minDistSqr = distSqr;
19825  }
19826  return qSqrt(minDistSqr);
19827 }
19828 
19829 /* inherits documentation from base class */
19831 {
19832  QPointF startVec(start->pixelPoint());
19833  QPointF startDirVec(startDir->pixelPoint());
19834  QPointF endDirVec(endDir->pixelPoint());
19835  QPointF endVec(end->pixelPoint());
19836  if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
19837  return;
19838 
19839  QPainterPath cubicPath(startVec);
19840  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
19841 
19842  // paint visible segment, if existent:
19843  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
19844  QRect cubicRect = cubicPath.controlPointRect().toRect();
19845  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
19846  cubicRect.adjust(0, 0, 1, 1);
19847  if (clip.intersects(cubicRect))
19848  {
19849  painter->setPen(mainPen());
19850  painter->drawPath(cubicPath);
19851  painter->setBrush(Qt::SolidPattern);
19853  mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
19855  mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
19856  }
19857 }
19858 
19865 {
19866  return mSelected ? mSelectedPen : mPen;
19867 }
19868 
19869 
19873 
19888  QCPAbstractItem(parentPlot),
19889  topLeft(createPosition("topLeft")),
19890  bottomRight(createPosition("bottomRight")),
19891  top(createAnchor("top", aiTop)),
19892  topRight(createAnchor("topRight", aiTopRight)),
19893  right(createAnchor("right", aiRight)),
19894  bottom(createAnchor("bottom", aiBottom)),
19895  bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
19896  left(createAnchor("left", aiLeft))
19897 {
19898  topLeft->setCoords(0, 1);
19899  bottomRight->setCoords(1, 0);
19900 
19901  setPen(QPen(Qt::black));
19902  setSelectedPen(QPen(Qt::blue,2));
19903  setBrush(Qt::NoBrush);
19904  setSelectedBrush(Qt::NoBrush);
19905 }
19906 
19908 {
19909 }
19910 
19916 void QCPItemRect::setPen(const QPen &pen)
19917 {
19918  mPen = pen;
19919 }
19920 
19926 void QCPItemRect::setSelectedPen(const QPen &pen)
19927 {
19928  mSelectedPen = pen;
19929 }
19930 
19937 void QCPItemRect::setBrush(const QBrush &brush)
19938 {
19939  mBrush = brush;
19940 }
19941 
19948 void QCPItemRect::setSelectedBrush(const QBrush &brush)
19949 {
19951 }
19952 
19953 /* inherits documentation from base class */
19954 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19955 {
19956  Q_UNUSED(details)
19957  if (onlySelectable && !mSelectable)
19958  return -1;
19959 
19960  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
19961  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
19962  return rectSelectTest(rect, pos, filledRect);
19963 }
19964 
19965 /* inherits documentation from base class */
19967 {
19968  QPointF p1 = topLeft->pixelPoint();
19969  QPointF p2 = bottomRight->pixelPoint();
19970  if (p1.toPoint() == p2.toPoint())
19971  return;
19972  QRectF rect = QRectF(p1, p2).normalized();
19973  double clipPad = mainPen().widthF();
19974  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
19975  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
19976  {
19977  painter->setPen(mainPen());
19978  painter->setBrush(mainBrush());
19979  painter->drawRect(rect);
19980  }
19981 }
19982 
19983 /* inherits documentation from base class */
19984 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
19985 {
19986  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
19987  switch (anchorId)
19988  {
19989  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
19990  case aiTopRight: return rect.topRight();
19991  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
19992  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
19993  case aiBottomLeft: return rect.bottomLeft();
19994  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
19995  }
19996 
19997  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
19998  return QPointF();
19999 }
20000 
20007 {
20008  return mSelected ? mSelectedPen : mPen;
20009 }
20010 
20017 {
20018  return mSelected ? mSelectedBrush : mBrush;
20019 }
20020 
20021 
20025 
20046  QCPAbstractItem(parentPlot),
20047  position(createPosition("position")),
20048  topLeft(createAnchor("topLeft", aiTopLeft)),
20049  top(createAnchor("top", aiTop)),
20050  topRight(createAnchor("topRight", aiTopRight)),
20051  right(createAnchor("right", aiRight)),
20052  bottomRight(createAnchor("bottomRight", aiBottomRight)),
20053  bottom(createAnchor("bottom", aiBottom)),
20054  bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
20055  left(createAnchor("left", aiLeft))
20056 {
20057  position->setCoords(0, 0);
20058 
20059  setRotation(0);
20060  setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
20061  setPositionAlignment(Qt::AlignCenter);
20062  setText("text");
20063 
20064  setPen(Qt::NoPen);
20065  setSelectedPen(Qt::NoPen);
20066  setBrush(Qt::NoBrush);
20067  setSelectedBrush(Qt::NoBrush);
20068  setColor(Qt::black);
20069  setSelectedColor(Qt::blue);
20070 }
20071 
20073 {
20074 }
20075 
20079 void QCPItemText::setColor(const QColor &color)
20080 {
20081  mColor = color;
20082 }
20083 
20087 void QCPItemText::setSelectedColor(const QColor &color)
20088 {
20090 }
20091 
20098 void QCPItemText::setPen(const QPen &pen)
20099 {
20100  mPen = pen;
20101 }
20102 
20109 void QCPItemText::setSelectedPen(const QPen &pen)
20110 {
20111  mSelectedPen = pen;
20112 }
20113 
20120 void QCPItemText::setBrush(const QBrush &brush)
20121 {
20122  mBrush = brush;
20123 }
20124 
20131 void QCPItemText::setSelectedBrush(const QBrush &brush)
20132 {
20134 }
20135 
20141 void QCPItemText::setFont(const QFont &font)
20142 {
20143  mFont = font;
20144 }
20145 
20151 void QCPItemText::setSelectedFont(const QFont &font)
20152 {
20153  mSelectedFont = font;
20154 }
20155 
20162 void QCPItemText::setText(const QString &text)
20163 {
20164  mText = text;
20165 }
20166 
20179 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
20180 {
20181  mPositionAlignment = alignment;
20182 }
20183 
20187 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
20188 {
20189  mTextAlignment = alignment;
20190 }
20191 
20196 void QCPItemText::setRotation(double degrees)
20197 {
20198  mRotation = degrees;
20199 }
20200 
20205 void QCPItemText::setPadding(const QMargins &padding)
20206 {
20207  mPadding = padding;
20208 }
20209 
20210 /* inherits documentation from base class */
20211 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20212 {
20213  Q_UNUSED(details)
20214  if (onlySelectable && !mSelectable)
20215  return -1;
20216 
20217  // The rect may be rotated, so we transform the actual clicked pos to the rotated
20218  // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
20219  QPointF positionPixels(position->pixelPoint());
20220  QTransform inputTransform;
20221  inputTransform.translate(positionPixels.x(), positionPixels.y());
20222  inputTransform.rotate(-mRotation);
20223  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
20224  QPointF rotatedPos = inputTransform.map(pos);
20225  QFontMetrics fontMetrics(mFont);
20226  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
20227  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
20228  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
20229  textBoxRect.moveTopLeft(textPos.toPoint());
20230 
20231  return rectSelectTest(textBoxRect, rotatedPos, true);
20232 }
20233 
20234 /* inherits documentation from base class */
20236 {
20237  QPointF pos(position->pixelPoint());
20238  QTransform transform = painter->transform();
20239  transform.translate(pos.x(), pos.y());
20240  if (!qFuzzyIsNull(mRotation))
20241  transform.rotate(mRotation);
20242  painter->setFont(mainFont());
20243  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
20244  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
20245  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
20246  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
20247  textBoxRect.moveTopLeft(textPos.toPoint());
20248  double clipPad = mainPen().widthF();
20249  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
20250  if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
20251  {
20252  painter->setTransform(transform);
20253  if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
20254  (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
20255  {
20256  painter->setPen(mainPen());
20257  painter->setBrush(mainBrush());
20258  painter->drawRect(textBoxRect);
20259  }
20260  painter->setBrush(Qt::NoBrush);
20261  painter->setPen(QPen(mainColor()));
20262  painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
20263  }
20264 }
20265 
20266 /* inherits documentation from base class */
20267 QPointF QCPItemText::anchorPixelPoint(int anchorId) const
20268 {
20269  // get actual rect points (pretty much copied from draw function):
20270  QPointF pos(position->pixelPoint());
20271  QTransform transform;
20272  transform.translate(pos.x(), pos.y());
20273  if (!qFuzzyIsNull(mRotation))
20274  transform.rotate(mRotation);
20275  QFontMetrics fontMetrics(mainFont());
20276  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
20277  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
20278  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
20279  textBoxRect.moveTopLeft(textPos.toPoint());
20280  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
20281 
20282  switch (anchorId)
20283  {
20284  case aiTopLeft: return rectPoly.at(0);
20285  case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
20286  case aiTopRight: return rectPoly.at(1);
20287  case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
20288  case aiBottomRight: return rectPoly.at(2);
20289  case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
20290  case aiBottomLeft: return rectPoly.at(3);
20291  case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
20292  }
20293 
20294  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
20295  return QPointF();
20296 }
20297 
20308 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
20309 {
20310  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
20311  return pos;
20312 
20313  QPointF result = pos; // start at top left
20314  if (positionAlignment.testFlag(Qt::AlignHCenter))
20315  result.rx() -= rect.width()/2.0;
20316  else if (positionAlignment.testFlag(Qt::AlignRight))
20317  result.rx() -= rect.width();
20318  if (positionAlignment.testFlag(Qt::AlignVCenter))
20319  result.ry() -= rect.height()/2.0;
20320  else if (positionAlignment.testFlag(Qt::AlignBottom))
20321  result.ry() -= rect.height();
20322  return result;
20323 }
20324 
20331 {
20332  return mSelected ? mSelectedFont : mFont;
20333 }
20334 
20341 {
20342  return mSelected ? mSelectedColor : mColor;
20343 }
20344 
20351 {
20352  return mSelected ? mSelectedPen : mPen;
20353 }
20354 
20361 {
20362  return mSelected ? mSelectedBrush : mBrush;
20363 }
20364 
20365 
20369 
20384  QCPAbstractItem(parentPlot),
20385  topLeft(createPosition("topLeft")),
20386  bottomRight(createPosition("bottomRight")),
20387  topLeftRim(createAnchor("topLeftRim", aiTopLeftRim)),
20388  top(createAnchor("top", aiTop)),
20389  topRightRim(createAnchor("topRightRim", aiTopRightRim)),
20390  right(createAnchor("right", aiRight)),
20391  bottomRightRim(createAnchor("bottomRightRim", aiBottomRightRim)),
20392  bottom(createAnchor("bottom", aiBottom)),
20393  bottomLeftRim(createAnchor("bottomLeftRim", aiBottomLeftRim)),
20394  left(createAnchor("left", aiLeft)),
20395  center(createAnchor("center", aiCenter))
20396 {
20397  topLeft->setCoords(0, 1);
20398  bottomRight->setCoords(1, 0);
20399 
20400  setPen(QPen(Qt::black));
20401  setSelectedPen(QPen(Qt::blue, 2));
20402  setBrush(Qt::NoBrush);
20403  setSelectedBrush(Qt::NoBrush);
20404 }
20405 
20407 {
20408 }
20409 
20415 void QCPItemEllipse::setPen(const QPen &pen)
20416 {
20417  mPen = pen;
20418 }
20419 
20425 void QCPItemEllipse::setSelectedPen(const QPen &pen)
20426 {
20427  mSelectedPen = pen;
20428 }
20429 
20436 void QCPItemEllipse::setBrush(const QBrush &brush)
20437 {
20438  mBrush = brush;
20439 }
20440 
20447 void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
20448 {
20450 }
20451 
20452 /* inherits documentation from base class */
20453 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20454 {
20455  Q_UNUSED(details)
20456  if (onlySelectable && !mSelectable)
20457  return -1;
20458 
20459  double result = -1;
20460  QPointF p1 = topLeft->pixelPoint();
20461  QPointF p2 = bottomRight->pixelPoint();
20462  QPointF center((p1+p2)/2.0);
20463  double a = qAbs(p1.x()-p2.x())/2.0;
20464  double b = qAbs(p1.y()-p2.y())/2.0;
20465  double x = pos.x()-center.x();
20466  double y = pos.y()-center.y();
20467 
20468  // distance to border:
20469  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
20470  result = qAbs(c-1)*qSqrt(x*x+y*y);
20471  // filled ellipse, allow click inside to count as hit:
20472  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
20473  {
20474  if (x*x/(a*a) + y*y/(b*b) <= 1)
20475  result = mParentPlot->selectionTolerance()*0.99;
20476  }
20477  return result;
20478 }
20479 
20480 /* inherits documentation from base class */
20482 {
20483  QPointF p1 = topLeft->pixelPoint();
20484  QPointF p2 = bottomRight->pixelPoint();
20485  if (p1.toPoint() == p2.toPoint())
20486  return;
20487  QRectF ellipseRect = QRectF(p1, p2).normalized();
20488  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
20489  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
20490  {
20491  painter->setPen(mainPen());
20492  painter->setBrush(mainBrush());
20493 #ifdef __EXCEPTIONS
20494  try // drawEllipse sometimes throws exceptions if ellipse is too big
20495  {
20496 #endif
20497  painter->drawEllipse(ellipseRect);
20498 #ifdef __EXCEPTIONS
20499  } catch (...)
20500  {
20501  qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
20502  setVisible(false);
20503  }
20504 #endif
20505  }
20506 }
20507 
20508 /* inherits documentation from base class */
20509 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
20510 {
20511  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
20512  switch (anchorId)
20513  {
20514  case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
20515  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
20516  case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
20517  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
20518  case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
20519  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
20520  case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
20521  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
20522  case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
20523  }
20524 
20525  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
20526  return QPointF();
20527 }
20528 
20535 {
20536  return mSelected ? mSelectedPen : mPen;
20537 }
20538 
20545 {
20546  return mSelected ? mSelectedBrush : mBrush;
20547 }
20548 
20549 
20553 
20574  QCPAbstractItem(parentPlot),
20575  topLeft(createPosition("topLeft")),
20576  bottomRight(createPosition("bottomRight")),
20577  top(createAnchor("top", aiTop)),
20578  topRight(createAnchor("topRight", aiTopRight)),
20579  right(createAnchor("right", aiRight)),
20580  bottom(createAnchor("bottom", aiBottom)),
20581  bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
20582  left(createAnchor("left", aiLeft))
20583 {
20584  topLeft->setCoords(0, 1);
20585  bottomRight->setCoords(1, 0);
20586 
20587  setPen(Qt::NoPen);
20588  setSelectedPen(QPen(Qt::blue));
20589  setScaled(false, Qt::KeepAspectRatio);
20590 }
20591 
20593 {
20594 }
20595 
20599 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
20600 {
20601  mPixmap = pixmap;
20602  if (mPixmap.isNull())
20603  qDebug() << Q_FUNC_INFO << "pixmap is null";
20604 }
20605 
20610 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode)
20611 {
20612  mScaled = scaled;
20615 }
20616 
20622 void QCPItemPixmap::setPen(const QPen &pen)
20623 {
20624  mPen = pen;
20625 }
20626 
20632 void QCPItemPixmap::setSelectedPen(const QPen &pen)
20633 {
20634  mSelectedPen = pen;
20635 }
20636 
20637 /* inherits documentation from base class */
20638 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20639 {
20640  Q_UNUSED(details)
20641  if (onlySelectable && !mSelectable)
20642  return -1;
20643 
20644  return rectSelectTest(getFinalRect(), pos, true);
20645 }
20646 
20647 /* inherits documentation from base class */
20649 {
20650  bool flipHorz = false;
20651  bool flipVert = false;
20652  QRect rect = getFinalRect(&flipHorz, &flipVert);
20653  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
20654  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
20655  if (boundingRect.intersects(clipRect()))
20656  {
20657  updateScaledPixmap(rect, flipHorz, flipVert);
20658  painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
20659  QPen pen = mainPen();
20660  if (pen.style() != Qt::NoPen)
20661  {
20662  painter->setPen(pen);
20663  painter->setBrush(Qt::NoBrush);
20664  painter->drawRect(rect);
20665  }
20666  }
20667 }
20668 
20669 /* inherits documentation from base class */
20670 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
20671 {
20672  bool flipHorz;
20673  bool flipVert;
20674  QRect rect = getFinalRect(&flipHorz, &flipVert);
20675  // we actually want denormal rects (negative width/height) here, so restore
20676  // the flipped state:
20677  if (flipHorz)
20678  rect.adjust(rect.width(), 0, -rect.width(), 0);
20679  if (flipVert)
20680  rect.adjust(0, rect.height(), 0, -rect.height());
20681 
20682  switch (anchorId)
20683  {
20684  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
20685  case aiTopRight: return rect.topRight();
20686  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
20687  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
20688  case aiBottomLeft: return rect.bottomLeft();
20689  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
20690  }
20691 
20692  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
20693  return QPointF();
20694 }
20695 
20709 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
20710 {
20711  if (mPixmap.isNull())
20712  return;
20713 
20714  if (mScaled)
20715  {
20716  if (finalRect.isNull())
20717  finalRect = getFinalRect(&flipHorz, &flipVert);
20718  if (finalRect.size() != mScaledPixmap.size())
20719  {
20720  mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, Qt::SmoothTransformation);
20721  if (flipHorz || flipVert)
20722  mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
20723  }
20724  } else if (!mScaledPixmap.isNull())
20725  mScaledPixmap = QPixmap();
20726 }
20727 
20742 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
20743 {
20744  QRect result;
20745  bool flipHorz = false;
20746  bool flipVert = false;
20747  QPoint p1 = topLeft->pixelPoint().toPoint();
20748  QPoint p2 = bottomRight->pixelPoint().toPoint();
20749  if (p1 == p2)
20750  return QRect(p1, QSize(0, 0));
20751  if (mScaled)
20752  {
20753  QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
20754  QPoint topLeft = p1;
20755  if (newSize.width() < 0)
20756  {
20757  flipHorz = true;
20758  newSize.rwidth() *= -1;
20759  topLeft.setX(p2.x());
20760  }
20761  if (newSize.height() < 0)
20762  {
20763  flipVert = true;
20764  newSize.rheight() *= -1;
20765  topLeft.setY(p2.y());
20766  }
20767  QSize scaledSize = mPixmap.size();
20768  scaledSize.scale(newSize, mAspectRatioMode);
20769  result = QRect(topLeft, scaledSize);
20770  } else
20771  {
20772  result = QRect(p1, mPixmap.size());
20773  }
20774  if (flippedHorz)
20775  *flippedHorz = flipHorz;
20776  if (flippedVert)
20777  *flippedVert = flipVert;
20778  return result;
20779 }
20780 
20787 {
20788  return mSelected ? mSelectedPen : mPen;
20789 }
20790 
20791 
20795 
20832  QCPAbstractItem(parentPlot),
20833  position(createPosition("position")),
20834  mGraph(0)
20835 {
20836  position->setCoords(0, 0);
20837 
20838  setBrush(Qt::NoBrush);
20839  setSelectedBrush(Qt::NoBrush);
20840  setPen(QPen(Qt::black));
20841  setSelectedPen(QPen(Qt::blue, 2));
20843  setSize(6);
20844  setInterpolating(false);
20845  setGraphKey(0);
20846 }
20847 
20849 {
20850 }
20851 
20857 void QCPItemTracer::setPen(const QPen &pen)
20858 {
20859  mPen = pen;
20860 }
20861 
20867 void QCPItemTracer::setSelectedPen(const QPen &pen)
20868 {
20869  mSelectedPen = pen;
20870 }
20871 
20877 void QCPItemTracer::setBrush(const QBrush &brush)
20878 {
20879  mBrush = brush;
20880 }
20881 
20887 void QCPItemTracer::setSelectedBrush(const QBrush &brush)
20888 {
20890 }
20891 
20896 void QCPItemTracer::setSize(double size)
20897 {
20898  mSize = size;
20899 }
20900 
20908 {
20909  mStyle = style;
20910 }
20911 
20923 {
20924  if (graph)
20925  {
20926  if (graph->parentPlot() == mParentPlot)
20927  {
20930  mGraph = graph;
20931  updatePosition();
20932  } else
20933  qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
20934  } else
20935  {
20936  mGraph = 0;
20937  }
20938 }
20939 
20950 {
20951  mGraphKey = key;
20952 }
20953 
20966 {
20967  mInterpolating = enabled;
20968 }
20969 
20970 /* inherits documentation from base class */
20971 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20972 {
20973  Q_UNUSED(details)
20974  if (onlySelectable && !mSelectable)
20975  return -1;
20976 
20977  QPointF center(position->pixelPoint());
20978  double w = mSize/2.0;
20979  QRect clip = clipRect();
20980  switch (mStyle)
20981  {
20982  case tsNone: return -1;
20983  case tsPlus:
20984  {
20985  if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
20986  return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
20987  distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
20988  break;
20989  }
20990  case tsCrosshair:
20991  {
20992  return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
20993  distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
20994  }
20995  case tsCircle:
20996  {
20997  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
20998  {
20999  // distance to border:
21000  double centerDist = QVector2D(center-pos).length();
21001  double circleLine = w;
21002  double result = qAbs(centerDist-circleLine);
21003  // filled ellipse, allow click inside to count as hit:
21004  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
21005  {
21006  if (centerDist <= circleLine)
21007  result = mParentPlot->selectionTolerance()*0.99;
21008  }
21009  return result;
21010  }
21011  break;
21012  }
21013  case tsSquare:
21014  {
21015  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
21016  {
21017  QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
21018  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
21019  return rectSelectTest(rect, pos, filledRect);
21020  }
21021  break;
21022  }
21023  }
21024  return -1;
21025 }
21026 
21027 /* inherits documentation from base class */
21029 {
21030  updatePosition();
21031  if (mStyle == tsNone)
21032  return;
21033 
21034  painter->setPen(mainPen());
21035  painter->setBrush(mainBrush());
21036  QPointF center(position->pixelPoint());
21037  double w = mSize/2.0;
21038  QRect clip = clipRect();
21039  switch (mStyle)
21040  {
21041  case tsNone: return;
21042  case tsPlus:
21043  {
21044  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
21045  {
21046  painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
21047  painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
21048  }
21049  break;
21050  }
21051  case tsCrosshair:
21052  {
21053  if (center.y() > clip.top() && center.y() < clip.bottom())
21054  painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
21055  if (center.x() > clip.left() && center.x() < clip.right())
21056  painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
21057  break;
21058  }
21059  case tsCircle:
21060  {
21061  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
21062  painter->drawEllipse(center, w, w);
21063  break;
21064  }
21065  case tsSquare:
21066  {
21067  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
21068  painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
21069  break;
21070  }
21071  }
21072 }
21073 
21087 {
21088  if (mGraph)
21089  {
21091  {
21092  if (mGraph->data()->size() > 1)
21093  {
21094  QCPDataMap::const_iterator first = mGraph->data()->constBegin();
21095  QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
21096  if (mGraphKey < first.key())
21097  position->setCoords(first.key(), first.value().value);
21098  else if (mGraphKey > last.key())
21099  position->setCoords(last.key(), last.value().value);
21100  else
21101  {
21102  QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
21103  if (it != first) // mGraphKey is somewhere between iterators
21104  {
21105  QCPDataMap::const_iterator prevIt = it-1;
21106  if (mInterpolating)
21107  {
21108  // interpolate between iterators around mGraphKey:
21109  double slope = 0;
21110  if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
21111  slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
21112  position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
21113  } else
21114  {
21115  // find iterator with key closest to mGraphKey:
21116  if (mGraphKey < (prevIt.key()+it.key())*0.5)
21117  it = prevIt;
21118  position->setCoords(it.key(), it.value().value);
21119  }
21120  } else // mGraphKey is exactly on first iterator
21121  position->setCoords(it.key(), it.value().value);
21122  }
21123  } else if (mGraph->data()->size() == 1)
21124  {
21125  QCPDataMap::const_iterator it = mGraph->data()->constBegin();
21126  position->setCoords(it.key(), it.value().value);
21127  } else
21128  qDebug() << Q_FUNC_INFO << "graph has no data";
21129  } else
21130  qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
21131  }
21132 }
21133 
21140 {
21141  return mSelected ? mSelectedPen : mPen;
21142 }
21143 
21150 {
21151  return mSelected ? mSelectedBrush : mBrush;
21152 }
21153 
21154 
21158 
21185  QCPAbstractItem(parentPlot),
21186  left(createPosition("left")),
21187  right(createPosition("right")),
21188  center(createAnchor("center", aiCenter))
21189 {
21190  left->setCoords(0, 0);
21191  right->setCoords(1, 1);
21192 
21193  setPen(QPen(Qt::black));
21194  setSelectedPen(QPen(Qt::blue, 2));
21195  setLength(8);
21197 }
21198 
21200 {
21201 }
21202 
21212 void QCPItemBracket::setPen(const QPen &pen)
21213 {
21214  mPen = pen;
21215 }
21216 
21222 void QCPItemBracket::setSelectedPen(const QPen &pen)
21223 {
21224  mSelectedPen = pen;
21225 }
21226 
21235 void QCPItemBracket::setLength(double length)
21236 {
21237  mLength = length;
21238 }
21239 
21246 {
21247  mStyle = style;
21248 }
21249 
21250 /* inherits documentation from base class */
21251 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21252 {
21253  Q_UNUSED(details)
21254  if (onlySelectable && !mSelectable)
21255  return -1;
21256 
21257  QVector2D leftVec(left->pixelPoint());
21258  QVector2D rightVec(right->pixelPoint());
21259  if (leftVec.toPoint() == rightVec.toPoint())
21260  return -1;
21261 
21262  QVector2D widthVec = (rightVec-leftVec)*0.5f;
21263  QVector2D lengthVec(-widthVec.y(), widthVec.x());
21264  lengthVec = lengthVec.normalized()*mLength;
21265  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
21266 
21267  return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
21268 }
21269 
21270 /* inherits documentation from base class */
21272 {
21273  QVector2D leftVec(left->pixelPoint());
21274  QVector2D rightVec(right->pixelPoint());
21275  if (leftVec.toPoint() == rightVec.toPoint())
21276  return;
21277 
21278  QVector2D widthVec = (rightVec-leftVec)*0.5f;
21279  QVector2D lengthVec(-widthVec.y(), widthVec.x());
21280  lengthVec = lengthVec.normalized()*mLength;
21281  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
21282 
21283  QPolygon boundingPoly;
21284  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
21285  << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
21286  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
21287  if (clip.intersects(boundingPoly.boundingRect()))
21288  {
21289  painter->setPen(mainPen());
21290  switch (mStyle)
21291  {
21292  case bsSquare:
21293  {
21294  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
21295  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
21296  painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
21297  break;
21298  }
21299  case bsRound:
21300  {
21301  painter->setBrush(Qt::NoBrush);
21302  QPainterPath path;
21303  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
21304  path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
21305  path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
21306  painter->drawPath(path);
21307  break;
21308  }
21309  case bsCurly:
21310  {
21311  painter->setBrush(Qt::NoBrush);
21312  QPainterPath path;
21313  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
21314  path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
21315  path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
21316  painter->drawPath(path);
21317  break;
21318  }
21319  case bsCalligraphic:
21320  {
21321  painter->setPen(Qt::NoPen);
21322  painter->setBrush(QBrush(mainPen().color()));
21323  QPainterPath path;
21324  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
21325 
21326  path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
21327  path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
21328 
21329  path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
21330  path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
21331 
21332  painter->drawPath(path);
21333  break;
21334  }
21335  }
21336  }
21337 }
21338 
21339 /* inherits documentation from base class */
21340 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
21341 {
21342  QVector2D leftVec(left->pixelPoint());
21343  QVector2D rightVec(right->pixelPoint());
21344  if (leftVec.toPoint() == rightVec.toPoint())
21345  return leftVec.toPointF();
21346 
21347  QVector2D widthVec = (rightVec-leftVec)*0.5f;
21348  QVector2D lengthVec(-widthVec.y(), widthVec.x());
21349  lengthVec = lengthVec.normalized()*mLength;
21350  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
21351 
21352  switch (anchorId)
21353  {
21354  case aiCenter:
21355  return centerVec.toPointF();
21356  }
21357  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
21358  return QPointF();
21359 }
21360 
21367 {
21368  return mSelected ? mSelectedPen : mPen;
21369 }
21370 
@ data
#define M_PI
Definition: XSensMTx.cpp:24
The abstract base class for all items in a plot.
Definition: qcustomplot.h:1584
QCPItemAnchor * anchor(const QString &name) const
Returns the QCPItemAnchor with the specified name.
Q_SLOT void setSelected(bool selected)
Sets whether this item is selected or not.
QCPItemPosition * position(const QString &name) const
Returns the QCPItemPosition with the specified name.
double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
virtual ~QCPAbstractItem()
void setClipToAxisRect(bool clip)
Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the entire Q...
QPointer< QCPAxisRect > mClipAxisRect
Definition: qcustomplot.h:1625
bool clipToAxisRect() const
Definition: qcustomplot.h:1597
virtual QRect clipRect() const
virtual QPointF anchorPixelPoint(int anchorId) const
virtual QCP::Interaction selectionCategory() const
void selectableChanged(bool selectable)
friend class QCPItemAnchor
Definition: qcustomplot.h:1652
QCPItemPosition * createPosition(const QString &name)
void setClipAxisRect(QCPAxisRect *rect)
Sets the clip axis rect.
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
bool hasAnchor(const QString &name) const
Returns whether this item has an anchor with the specified name.
Q_SLOT void setSelectable(bool selectable)
Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
QList< QCPItemAnchor * > mAnchors
Definition: qcustomplot.h:1627
virtual void deselectEvent(bool *selectionStateChanged)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
This function is used to decide whether a click hits a layerable object or not.
QCPAbstractItem(QCustomPlot *parentPlot)
Base class constructor which initializes base class members.
bool selected() const
Definition: qcustomplot.h:1600
void selectionChanged(bool selected)
This signal is emitted when the selection state of this item has changed, either by user interaction ...
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPAxisRect * clipAxisRect() const
bool selectable() const
Definition: qcustomplot.h:1599
QCPItemAnchor * createAnchor(const QString &name, int anchorId)
QList< QCPItemPosition * > mPositions
Definition: qcustomplot.h:1626
The abstract base class for all entries in a QCPLegend.
Definition: qcustomplot.h:2058
void setFont(const QFont &font)
Sets the default font of this specific legend item to font.
void setSelectedTextColor(const QColor &color)
When this legend item is selected, color is used to draw generic text, instead of the normal color se...
QFont font() const
Definition: qcustomplot.h:2074
void setTextColor(const QColor &color)
Sets the default text color of this specific legend item to color.
Q_SLOT void setSelected(bool selected)
Sets whether this specific legend item is selected.
void selectionChanged(bool selected)
This signal is emitted when the selection state of this legend item has changed, either by user inter...
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setSelectedFont(const QFont &font)
When this legend item is selected, font is used to draw generic text, instead of the normal font set ...
Q_SLOT void setSelectable(bool selectable)
Sets whether this specific legend item is selectable.
virtual QCP::Interaction selectionCategory() const
QCPLegend * mParentLegend
Definition: qcustomplot.h:2098
void selectableChanged(bool selectable)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
virtual QRect clipRect() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Layout elements are sensitive to events inside their outer rect.
bool selected() const
Definition: qcustomplot.h:2079
virtual void deselectEvent(bool *selectionStateChanged)
bool selectable() const
Definition: qcustomplot.h:2078
QCPAbstractLegendItem(QCPLegend *parent)
Constructs a QCPAbstractLegendItem and associates it with the QCPLegend parent.
The abstract base class for all data representing objects in a plot.
Definition: qcustomplot.h:1375
void selectableChanged(bool selectable)
This signal is emitted when the selectability of this plottable has changed.
void applyErrorBarsAntialiasingHint(QCPPainter *painter) const
void setAntialiasedFill(bool enabled)
Sets whether fills of this plottable is drawn antialiased or not.
bool selected() const
Definition: qcustomplot.h:1406
friend class QCPPlottableLegendItem
Definition: qcustomplot.h:1485
void rescaleAxes(bool onlyEnlarge=false) const
Rescales the key and value axes associated with this plottable to contain all displayed data,...
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
Q_SLOT void setSelectable(bool selectable)
Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface.
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:1451
QCPAxis * keyAxis() const
Definition: qcustomplot.h:1403
void setAntialiasedScatters(bool enabled)
Sets whether the scatter symbols of this plottable are drawn antialiased or not.
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const =0
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
This function is used to decide whether a click hits a layerable object or not.
void pixelsToCoords(double x, double y, double &key, double &value) const
void selectionChanged(bool selected)
This signal is emitted when the selection state of this plottable has changed, either by user interac...
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:1451
QString name() const
Definition: qcustomplot.h:1395
void applyDefaultAntialiasingHint(QCPPainter *painter) const
SignDomain
Represents negative and positive sign domain for passing to getKeyRange and getValueRange.
Definition: qcustomplot.h:1441
@ sdPositive
The positive sign domain, i.e. numbers greater than zero.
Definition: qcustomplot.h:1443
@ sdBoth
Both sign domains, including zero, i.e. all (rational) numbers.
Definition: qcustomplot.h:1442
@ sdNegative
The negative sign domain, i.e. numbers smaller than zero.
Definition: qcustomplot.h:1441
void setSelectedPen(const QPen &pen)
When the plottable is selected, this pen is used to draw basic lines instead of the normal pen set vi...
virtual void deselectEvent(bool *selectionStateChanged)
virtual bool addToLegend()
Adds this plottable to the legend of the parent QCustomPlot (QCustomPlot::legend).
void setValueAxis(QCPAxis *axis)
The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal to...
void setAntialiasedErrorBars(bool enabled)
Sets whether the error bars of this plottable are drawn antialiased or not.
void setBrush(const QBrush &brush)
The brush is used to draw basic fills of the plottable representation in the plot.
void coordsToPixels(double key, double value, double &x, double &y) const
void setKeyAxis(QCPAxis *axis)
The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal to t...
QBrush brush() const
Definition: qcustomplot.h:1401
void applyFillAntialiasingHint(QCPPainter *painter) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const =0
void rescaleValueAxis(bool onlyEnlarge=false) const
Rescales the value axis of the plottable so the whole plottable is visible.
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const =0
virtual QRect clipRect() const
void setPen(const QPen &pen)
The pen is used to draw basic lines that make up the plottable representation in the plot.
void setName(const QString &name)
The name is the textual representation of this plottable as it is displayed in the legend (QCPLegend)...
QBrush mainBrush() const
void applyScattersAntialiasingHint(QCPPainter *painter) const
virtual bool removeFromLegend() const
Removes the plottable from the legend of the parent QCustomPlot.
bool selectable() const
Definition: qcustomplot.h:1405
void setSelectedBrush(const QBrush &brush)
When the plottable is selected, this brush is used to draw fills instead of the normal brush set via ...
void rescaleKeyAxis(bool onlyEnlarge=false) const
Rescales the key axis of the plottable so the whole plottable is visible.
QCPAxis * valueAxis() const
Definition: qcustomplot.h:1404
QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs an abstract plottable which uses keyAxis as its key axis ("x") and valueAxis as its value ...
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
virtual QCP::Interaction selectionCategory() const
Q_SLOT void setSelected(bool selected)
Sets whether this plottable is selected or not.
virtual void draw(QCPPainter *painter)
QCPLineEnding lowerEnding
Definition: qcustomplot.h:1325
QCache< QString, CachedLabel > mLabelCache
Definition: qcustomplot.h:1361
QCPAxisPainterPrivate(QCustomPlot *parentPlot)
Constructs a QCPAxisPainterPrivate instance.
virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const
virtual int size() const
virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const
virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
virtual ~QCPAxisPainterPrivate()
QCustomPlot * mParentPlot
Definition: qcustomplot.h:1359
virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
QByteArray mLabelParameterHash
Definition: qcustomplot.h:1360
QRect axisSelectionBox() const
Definition: qcustomplot.h:1318
virtual QByteArray generateLabelParameterHash() const
QRect tickLabelsSelectionBox() const
Definition: qcustomplot.h:1319
QVector< QString > tickLabels
Definition: qcustomplot.h:1345
QCPAxis::AxisType type
Definition: qcustomplot.h:1323
QVector< double > tickPositions
Definition: qcustomplot.h:1344
QRect labelSelectionBox() const
Definition: qcustomplot.h:1320
QCPLineEnding upperEnding
Definition: qcustomplot.h:1325
virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
QVector< double > subTickPositions
Definition: qcustomplot.h:1343
Holds multiple axes and arranges them in a rectangular shape.
Definition: qcustomplot.h:1949
QPoint mDragStart
Definition: qcustomplot.h:2032
QList< QCPAbstractItem * > items() const
Returns a list of all the items that are associated with this axis rect.
bool removeAxis(QCPAxis *axis)
Removes the specified axis from the axis rect and deletes it.
QList< QCPAxis * > axes() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QCPRange mDragStartVertRange
Definition: qcustomplot.h:2030
int width() const
Returns the pixel width of this axis rect.
Definition: qcustomplot.h:2005
Qt::Orientations mRangeZoom
Definition: qcustomplot.h:2026
virtual void update(UpdatePhase phase)
This method is called automatically upon replot and doesn't need to be called by users of QCPAxisRect...
QCPLayoutInset * mInsetLayout
Definition: qcustomplot.h:2025
QList< QCPGraph * > graphs() const
Returns a list of all the graphs that are associated with this axis rect.
double mRangeZoomFactorVert
Definition: qcustomplot.h:2028
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:2021
QPointer< QCPAxis > mRangeDragVertAxis
Definition: qcustomplot.h:2027
int right() const
Returns the pixel position of the right border of this axis rect.
Definition: qcustomplot.h:2002
virtual QList< QCPLayoutElement * > elements(bool recursive) const
Returns a list of all child elements in this layout element.
QCPRange mDragStartHorzRange
Definition: qcustomplot.h:2030
int top() const
Returns the pixel position of the top border of this axis rect.
Definition: qcustomplot.h:2003
virtual ~QCPAxisRect()
virtual void mouseMoveEvent(QMouseEvent *event)
This event is called, if the mouse is moved inside the outer rect of this layout element.
QBrush mBackgroundBrush
Definition: qcustomplot.h:2020
QCPAxis * axis(QCPAxis::AxisType type, int index=0) const
Returns the axis with the given index on the axis rect side specified with type.
QList< QCPAbstractPlottable * > plottables() const
Returns a list of all the plottables that are associated with this axis rect.
virtual void wheelEvent(QWheelEvent *event)
This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this layo...
bool mBackgroundScaled
Definition: qcustomplot.h:2023
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
If scaling of the axis background pixmap is enabled (setBackgroundScaled), use this function to defin...
void setupFullAxesBox(bool connectRanges=false)
Convenience function to create an axis on each side that doesn't have any axes yet and set their visi...
void updateAxesOffset(QCPAxis::AxisType type)
void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
Sets the axes whose range will be dragged when setRangeDrag enables mouse range dragging on the QCust...
QCPAxis * rangeZoomAxis(Qt::Orientation orientation)
Returns the range zoom axis of the orientation provided.
QCPAxis * rangeDragAxis(Qt::Orientation orientation)
Returns the range drag axis of the orientation provided.
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:2031
virtual void mousePressEvent(QMouseEvent *event)
This event is called, if the mouse was pressed while being inside the outer rect of this layout eleme...
QList< QCPAxis * > addAxes(QCPAxis::AxisTypes types)
Adds a new axis with addAxis to each axis rect side specified in types.
void setRangeZoom(Qt::Orientations orientations)
Sets which axis orientation may be zoomed by the user with the mouse wheel.
QSize size() const
Returns the pixel size of this axis rect.
Definition: qcustomplot.h:2007
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:2024
int axisCount(QCPAxis::AxisType type) const
Returns the number of axes on the axis rect side specified with type.
void setRangeZoomFactor(double horizontalFactor, double verticalFactor)
Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with setRan...
QPointer< QCPAxis > mRangeZoomVertAxis
Definition: qcustomplot.h:2027
QList< QCPAxis * > axes(QCPAxis::AxisTypes types) const
Returns all axes on the axis rect sides specified with types.
void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
Sets the axes whose range will be zoomed when setRangeZoom enables mouse wheel zooming on the QCustom...
QCPLayoutInset * insetLayout() const
Returns the inset layout of this axis rect.
Definition: qcustomplot.h:1993
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:2031
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:2022
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
Qt::Orientations mRangeDrag
Definition: qcustomplot.h:2026
void drawBackground(QCPPainter *painter)
QCPAxis * addAxis(QCPAxis::AxisType type)
Adds a new axis to the axis rect side specified with type, and returns it.
int height() const
Returns the pixel height of this axis rect.
Definition: qcustomplot.h:2006
int bottom() const
Returns the pixel position of the bottom border of this axis rect.
Definition: qcustomplot.h:2004
double mRangeZoomFactorHorz
Definition: qcustomplot.h:2028
virtual void mouseReleaseEvent(QMouseEvent *event)
This event is called, if the mouse was previously pressed inside the outer rect of this layout elemen...
QPointer< QCPAxis > mRangeZoomHorzAxis
Definition: qcustomplot.h:2027
double rangeZoomFactor(Qt::Orientation orientation)
Returns the range zoom factor of the orientation provided.
void setRangeDrag(Qt::Orientations orientations)
Sets which axis orientation may be range dragged by the user with mouse interaction.
void setBackgroundScaled(bool scaled)
Sets whether the axis background pixmap shall be scaled to fit the axis rect or not.
virtual int calculateAutoMargin(QCP::MarginSide side)
QPointer< QCPAxis > mRangeDragHorzAxis
Definition: qcustomplot.h:2027
void setBackground(const QPixmap &pm)
Sets pm as the axis background pixmap.
virtual void draw(QCPPainter *painter)
int left() const
Returns the pixel position of the left border of this axis rect.
Definition: qcustomplot.h:2001
QHash< QCPAxis::AxisType, QList< QCPAxis * > > mAxes
Definition: qcustomplot.h:2034
Manages a single axis inside a QCustomPlot.
Definition: qcustomplot.h:980
void setSelectedLabelFont(const QFont &font)
Sets the font that is used for the axis label when it is selected.
void setOffset(int offset)
Sets the offset the axis has to its axis rect side.
virtual QCP::Interaction selectionCategory() const
void setTickLabels(bool show)
Sets whether tick labels are displayed.
int padding() const
Definition: qcustomplot.h:1118
void rangeChanged(const QCPRange &newRange)
This signal is emitted when the range of this axis has changed.
void setLowerEnding(const QCPLineEnding &ending)
Sets the style for the lower axis ending.
QString mDateTimeFormat
Definition: qcustomplot.h:1240
LabelType tickLabelType() const
Definition: qcustomplot.h:1095
QCPLineEnding lowerEnding() const
bool autoTickStep() const
Definition: qcustomplot.h:1090
QCPGrid * mGrid
Definition: qcustomplot.h:1261
void moveRange(double diff)
If the scale type (setScaleType) is stLinear, diff is added to the lower and upper bounds of the rang...
void setTickLabelRotation(double degrees)
Sets the rotation of the tick labels.
QPen mTickPen
Definition: qcustomplot.h:1252
QCPRange mRange
Definition: qcustomplot.h:1255
QString numberFormat() const
void setRangeReversed(bool reversed)
Sets whether the axis range (direction) is displayed reversed.
void setNumberPrecision(int precision)
Sets the precision of the tick label numbers.
SelectablePart getPartAt(const QPointF &pos) const
Returns the part of the axis that is hit by pos (in pixels).
int numberPrecision() const
Definition: qcustomplot.h:1102
virtual void draw(QCPPainter *painter)
void setDateTimeSpec(const Qt::TimeSpec &timeSpec)
Sets the time spec that is used for the date time values when setTickLabelType is ltDateTime.
QVector< double > mSubTickVector
Definition: qcustomplot.h:1266
void setSelectedSubTickPen(const QPen &pen)
Sets the pen that is used to draw the subticks when selected.
void setTickLabelFont(const QFont &font)
Sets the font of the tick labels.
QString dateTimeFormat() const
Definition: qcustomplot.h:1099
bool mCachedMarginValid
Definition: qcustomplot.h:1267
void setDateTimeFormat(const QString &format)
Sets the format in which dates and times are displayed as tick labels, if setTickLabelType is ltDateT...
QPen mSubTickPen
Definition: qcustomplot.h:1253
void setLabel(const QString &str)
Sets the text of the axis label that will be shown below/above or next to the axis,...
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
This signal is emitted when the scale type changes, by calls to setScaleType.
ScaleType
Defines the scale of an axis.
Definition: qcustomplot.h:1061
@ stLogarithmic
Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power...
Definition: qcustomplot.h:1062
@ stLinear
Linear scaling.
Definition: qcustomplot.h:1061
QFont mLabelFont
Definition: qcustomplot.h:1231
void setTickLabelColor(const QColor &color)
Sets the color of the tick labels.
void setTickLengthOut(int outside)
Sets the length of the outward ticks in pixels.
QColor mSelectedTickLabelColor
Definition: qcustomplot.h:1239
bool mTickLabels
Definition: qcustomplot.h:1235
int autoTickCount() const
Definition: qcustomplot.h:1088
QList< QCPAbstractItem * > items() const
Returns a list of all the items that are associated with this axis.
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setLabelPadding(int padding)
Sets the distance between the tick labels and the axis label.
QColor mLabelColor
Definition: qcustomplot.h:1232
virtual int calculateMargin()
int mCachedMargin
Definition: qcustomplot.h:1268
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void rescale(bool onlyVisiblePlottables=false)
Changes the axis range such that all plottables associated with this axis are fully visible in that d...
int mAutoTickCount
Definition: qcustomplot.h:1249
LabelType
When automatic tick label generation is enabled (setAutoTickLabels), defines how the coordinate of th...
Definition: qcustomplot.h:1053
@ ltNumber
Tick coordinate is regarded as normal number and will be displayed as such. (see setNumberFormat)
Definition: qcustomplot.h:1053
@ ltDateTime
Tick coordinate is regarded as a date/time (seconds since 1970-01-01T00:00:00 UTC) and will be displa...
Definition: qcustomplot.h:1054
void setSubTickCount(int count)
Sets the number of sub ticks in one (major) tick step.
void setSubTickLengthOut(int outside)
Sets the length of the outward subticks in pixels.
QFont mSelectedTickLabelFont
Definition: qcustomplot.h:1238
double mTickStep
Definition: qcustomplot.h:1248
Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts)
Sets whether the user can (de-)select the parts in selectable by clicking on the QCustomPlot surface.
int mPadding
Definition: qcustomplot.h:1223
virtual void deselectEvent(bool *selectionStateChanged)
double pixelToCoord(double value) const
Transforms value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.
void setTickLabelType(LabelType type)
Sets whether the tick labels display numbers or dates/times.
void setPadding(int padding)
Sets the padding of the axis.
virtual void setupTickVectors()
bool ticks() const
Definition: qcustomplot.h:1092
double tickLabelRotation() const
bool mRangeReversed
Definition: qcustomplot.h:1256
void setSelectedLabelColor(const QColor &color)
Sets the color that is used for the axis label when it is selected.
char mNumberFormatChar
Definition: qcustomplot.h:1243
virtual void generateAutoTicks()
void selectionChanged(const QCPAxis::SelectableParts &parts)
This signal is emitted when the selection state of this axis has changed, either by user interaction ...
void setTickLength(int inside, int outside=0)
Sets the length of the ticks in pixels.
QColor mTickLabelColor
Definition: qcustomplot.h:1239
QCPGrid * grid() const
Returns the QCPGrid instance belonging to this axis.
Definition: qcustomplot.h:1131
void setUpperEnding(const QCPLineEnding &ending)
Sets the style for the upper axis ending.
QFont getTickLabelFont() const
void setLabelColor(const QColor &color)
Sets the color of the axis label.
int labelPadding() const
LabelType mTickLabelType
Definition: qcustomplot.h:1237
QCPAxisRect * mAxisRect
Definition: qcustomplot.h:1221
void scaleRange(double factor, double center)
Scales the range of this axis by factor around the coordinate center.
void setLabelFont(const QFont &font)
Sets the font of the axis label.
bool mAutoTickLabels
Definition: qcustomplot.h:1235
void setScaleLogBase(double base)
If setScaleType is set to stLogarithmic, base will be the logarithm base of the scaling.
void setBasePen(const QPen &pen)
Sets the pen, the axis base line is drawn with.
void setAutoTickCount(int approximateCount)
When setAutoTickStep is true, approximateCount determines how many ticks should be generated in the v...
virtual ~QCPAxis()
QPen mSelectedBasePen
Definition: qcustomplot.h:1226
void setSelectedTickPen(const QPen &pen)
Sets the pen that is used to draw the (major) ticks when selected.
void setSelectedTickLabelFont(const QFont &font)
Sets the font that is used for tick labels when they are selected.
void setTickVector(const QVector< double > &vec)
If you want full control over what ticks (and possibly labels) the axes show, this function is used t...
SelectableParts selectedParts() const
Definition: qcustomplot.h:1120
QPen getBasePen() const
QColor getTickLabelColor() const
double scaleLogBase() const
Definition: qcustomplot.h:1084
SelectableParts mSelectedParts
Definition: qcustomplot.h:1225
void setTickVectorLabels(const QVector< QString > &vec)
If you want full control over what ticks and labels the axes show, this function is used to set a num...
double mScaleLogBaseLogInv
Definition: qcustomplot.h:1258
QColor mSelectedLabelColor
Definition: qcustomplot.h:1232
double tickStep() const
Definition: qcustomplot.h:1103
QPen mSelectedTickPen
Definition: qcustomplot.h:1252
void setAutoTickStep(bool on)
Sets whether the tick step, i.e.
void setSelectedTickLabelColor(const QColor &color)
Sets the color that is used for tick labels when they are selected.
QCPLineEnding upperEnding() const
AxisType axisType() const
Definition: qcustomplot.h:1081
QPen mSelectedSubTickPen
Definition: qcustomplot.h:1253
void selectableChanged(const QCPAxis::SelectableParts &parts)
This signal is emitted when the selectability changes, by calls to setSelectableParts.
static AxisType opposite(AxisType type)
Returns the axis type that describes the opposite axis of an axis with the specified type.
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
void setAutoTickLabels(bool on)
Sets whether the tick labels are generated automatically.
double basePow(double value) const
bool mAutoSubTicks
Definition: qcustomplot.h:1250
bool mAutoTicks
Definition: qcustomplot.h:1250
QPen getSubTickPen() const
bool mAutoTickStep
Definition: qcustomplot.h:1250
QVector< double > mTickVector
Definition: qcustomplot.h:1264
bool mTicks
Definition: qcustomplot.h:1247
void setSubTickLength(int inside, int outside=0)
Sets the length of the subticks in pixels.
SelectableParts mSelectableParts
Definition: qcustomplot.h:1225
bool rangeReversed() const
Definition: qcustomplot.h:1086
Qt::Orientation orientation() const
Returns the orientation of this axis.
Definition: qcustomplot.h:1193
Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts)
Sets the selected state of the respective axis parts described by SelectablePart.
int subTickCount() const
Definition: qcustomplot.h:1108
int mHighestVisibleTick
Definition: qcustomplot.h:1263
double mScaleLogBase
Definition: qcustomplot.h:1258
SelectablePart
Defines the selectable parts of an axis.
Definition: qcustomplot.h:1069
@ spTickLabels
Tick labels (numbers) of this axis (as a whole, not individually)
Definition: qcustomplot.h:1071
@ spAxisLabel
The axis label.
Definition: qcustomplot.h:1072
@ spAxis
The axis backbone and tick marks.
Definition: qcustomplot.h:1070
@ spNone
None of the selectable parts.
Definition: qcustomplot.h:1069
static AxisType marginSideToAxisType(QCP::MarginSide side)
Transforms a margin side to the logically corresponding axis type.
const QCPRange range() const
Definition: qcustomplot.h:1085
void setSubTickLengthIn(int inside)
Sets the length of the inward subticks in pixels.
QList< QCPAbstractPlottable * > plottables() const
Returns a list of all the plottables that have this axis as key or value axis.
QCPAxis(QCPAxisRect *parent, AxisType type)
Constructs an Axis instance of Type type for the axis rect parent.
void setTicks(bool show)
Sets whether tick marks are displayed.
int subTickLengthOut() const
virtual int calculateAutoSubTickCount(double tickStep) const
void setRangeUpper(double upper)
Sets the upper bound of the axis range.
int mNumberPrecision
Definition: qcustomplot.h:1242
int tickLengthIn() const
ScaleType scaleType() const
Definition: qcustomplot.h:1083
int tickLengthOut() const
QList< QCPGraph * > graphs() const
Returns a list of all the graphs that have this axis as key or value axis.
QPen mBasePen
Definition: qcustomplot.h:1226
int mSubTickCount
Definition: qcustomplot.h:1249
ScaleType mScaleType
Definition: qcustomplot.h:1257
void setTickPen(const QPen &pen)
Sets the pen, tick marks will be drawn with.
void setAutoSubTicks(bool on)
Sets whether the number of sub ticks in one tick interval is determined automatically.
QFont mTickLabelFont
Definition: qcustomplot.h:1238
Q_SLOT void setScaleType(QCPAxis::ScaleType type)
Sets whether the axis uses a linear scale or a logarithmic scale.
AxisType
Defines at which side of the axis rect the axis will appear.
Definition: qcustomplot.h:1040
@ atBottom
0x08 Axis is horizontal and on the bottom side of the axis rect
Definition: qcustomplot.h:1043
@ atTop
0x04 Axis is horizontal and on the top side of the axis rect
Definition: qcustomplot.h:1042
@ atRight
0x02 Axis is vertical and on the right side of the axis rect
Definition: qcustomplot.h:1041
@ atLeft
0x01 Axis is vertical and on the left side of the axis rect
Definition: qcustomplot.h:1040
bool autoSubTicks() const
Definition: qcustomplot.h:1091
bool tickLabels() const
Definition: qcustomplot.h:1093
QFont mSelectedLabelFont
Definition: qcustomplot.h:1231
void setNumberFormat(const QString &formatCode)
Sets the number format for the numbers drawn as tick labels (if tick label type is ltNumber).
AxisType mAxisType
Definition: qcustomplot.h:1220
double baseLog(double value) const
QString mLabel
Definition: qcustomplot.h:1230
void setAutoTicks(bool on)
Sets whether the tick positions should be calculated automatically (either from an automatically gene...
QColor getLabelColor() const
Qt::TimeSpec dateTimeSpec() const
Definition: qcustomplot.h:1100
QFont getLabelFont() const
void setSelectedBasePen(const QPen &pen)
Sets the pen that is used to draw the axis base line when selected.
int mLowestVisibleTick
Definition: qcustomplot.h:1263
Q_SLOT void setRange(const QCPRange &range)
Sets the range of the axis.
void visibleTickBounds(int &lowIndex, int &highIndex) const
void setSubTickPen(const QPen &pen)
Sets the pen, subtick marks will be drawn with.
QCPAxisPainterPrivate * mAxisPainter
Definition: qcustomplot.h:1262
QVector< QString > mTickVectorLabels
Definition: qcustomplot.h:1265
int offset() const
bool mNumberBeautifulPowers
Definition: qcustomplot.h:1244
double coordToPixel(double value) const
Transforms value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.
void setTickLabelPadding(int padding)
Sets the distance between the axis base line (including any outward ticks) and the tick labels.
void ticksRequest()
This signal is emitted when setAutoTicks is false and the axis is about to generate tick labels for a...
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
Scales the range of this axis to have a certain scale ratio to otherAxis.
void setTickStep(double step)
If setAutoTickStep is set to false, use this function to set the tick step manually.
Qt::TimeSpec mDateTimeSpec
Definition: qcustomplot.h:1241
int subTickLengthIn() const
int tickLabelPadding() const
void setTickLengthIn(int inside)
Sets the length of the inward ticks in pixels.
QCPAxisRect * axisRect() const
Definition: qcustomplot.h:1082
void setRangeLower(double lower)
Sets the lower bound of the axis range.
QPen getTickPen() const
Holds the data of one single data point (one bar) for QCPBars.
Definition: qcustomplot.h:2712
QCPBarData()
Constructs a bar data point with key and value set to zero.
double value
Definition: qcustomplot.h:2716
double key
Definition: qcustomplot.h:2716
A plottable representing a bar chart in a plot.
Definition: qcustomplot.h:2733
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
QPointer< QCPBars > mBarAbove
Definition: qcustomplot.h:2775
virtual void clearData()
Removes all data points.
QCPBarDataMap * data() const
Definition: qcustomplot.h:2748
void addData(const QCPBarDataMap &dataMap)
Adds the provided data points in dataMap to the current data.
void removeData(double fromKey, double toKey)
Removes all data points with key between fromKey and toKey.
QPolygonF getBarPolygon(double key, double value) const
virtual void draw(QCPPainter *painter)
virtual ~QCPBars()
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a bar chart which uses keyAxis as its key axis ("x") and valueAxis as its value axis ("y")...
void moveBelow(QCPBars *bars)
Moves this bars plottable below bars.
static void connectBars(QCPBars *lower, QCPBars *upper)
double mWidth
Definition: qcustomplot.h:2774
void removeDataAfter(double key)
Removes all data points with key greater than key.
void removeDataBefore(double key)
Removes all data points with key smaller than key.
void setData(QCPBarDataMap *data, bool copy=false)
Replaces the current data with the provided data.
double width() const
Definition: qcustomplot.h:2745
void moveAbove(QCPBars *bars)
Moves this bars plottable above bars.
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QPointer< QCPBars > mBarBelow
Definition: qcustomplot.h:2775
double getBaseValue(double key, bool positive) const
QCPBarDataMap * mData
Definition: qcustomplot.h:2773
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void setWidth(double width)
Sets the width of the bars in plot (key) coordinates.
Defines a color gradient for use with e.g.
Definition: qcustomplot.h:1877
ColorInterpolation mColorInterpolation
Definition: qcustomplot.h:1939
QRgb color(double position, const QCPRange &range, bool logarithmic=false)
void setLevelCount(int n)
Sets the number of discretization levels of the color gradient to n.
void setPeriodic(bool enabled)
Sets whether data points that are outside the configured data range (e.g.
void setColorStopAt(double position, const QColor &color)
Sets the color the gradient will have at the specified position (from 0 to 1).
QCPColorGradient(GradientPreset preset=gpCold)
Constructs a new QCPColorGradient initialized with the colors and color interpolation according to pr...
void setColorStops(const QMap< double, QColor > &colorStops)
Sets at which positions from 0 to 1 which color shall occur.
bool operator==(const QCPColorGradient &other) const
void clearColorStops()
Clears all color stops.
QMap< double, QColor > mColorStops
Definition: qcustomplot.h:1938
QCPColorGradient inverted() const
Returns an inverted gradient.
void loadPreset(GradientPreset preset)
Clears the current color stops and loads the specified preset.
void setColorInterpolation(ColorInterpolation interpolation)
Sets whether the colors in between the configured color stops (see setColorStopAt) shall be interpola...
QMap< double, QColor > colorStops() const
Definition: qcustomplot.h:1915
void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false)
This method is used to quickly convert a data array to colors.
bool mColorBufferInvalidated
Definition: qcustomplot.h:1944
int levelCount() const
Definition: qcustomplot.h:1914
ColorInterpolation
Defines the color spaces in which color interpolation between gradient stops can be performed.
Definition: qcustomplot.h:1885
@ ciRGB
Color channels red, green and blue are linearly interpolated.
Definition: qcustomplot.h:1885
@ ciHSV
Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the ...
Definition: qcustomplot.h:1886
GradientPreset
Defines the available presets that can be loaded with loadPreset.
Definition: qcustomplot.h:1894
@ gpNight
Continuous lightness from black over weak blueish colors to white (suited for non-biased data represe...
Definition: qcustomplot.h:1897
@ gpHues
Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and pha...
Definition: qcustomplot.h:1905
@ gpGeography
Colors suitable to represent different elevations on geographical maps.
Definition: qcustomplot.h:1899
@ gpIon
Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allo...
Definition: qcustomplot.h:1900
@ gpHot
Continuous lightness from black over firey colors to white (suited for non-biased data representation...
Definition: qcustomplot.h:1895
@ gpJet
Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion ...
Definition: qcustomplot.h:1904
@ gpCandy
Blue over pink to white.
Definition: qcustomplot.h:1898
@ gpPolar
Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle ...
Definition: qcustomplot.h:1902
@ gpSpectrum
An approximation of the visible light spectrum (creates banding illusion but allows more precise magn...
Definition: qcustomplot.h:1903
@ gpGrayscale
Continuous lightness from black to white (suited for non-biased data representation)
Definition: qcustomplot.h:1894
@ gpCold
Continuous lightness from black over icey colors to white (suited for non-biased data representation)
Definition: qcustomplot.h:1896
@ gpThermal
Colors suitable for thermal imaging, ranging from dark blue over purple to orange,...
Definition: qcustomplot.h:1901
QVector< QRgb > mColorBuffer
Definition: qcustomplot.h:1943
Holds the two-dimensional data of a QCPColorMap plottable.
Definition: qcustomplot.h:2880
void setKeyRange(const QCPRange &keyRange)
Sets the coordinate range the data shall be distributed over in the key dimension.
void setValueSize(int valueSize)
Resizes the data array to have valueSize cells in the value dimension.
void setSize(int keySize, int valueSize)
Resizes the data array to have keySize cells in the key dimension and valueSize cells in the value di...
QCPRange mDataBounds
Definition: qcustomplot.h:2921
QCPRange keyRange() const
Definition: qcustomplot.h:2890
QCPRange mValueRange
Definition: qcustomplot.h:2917
double data(double key, double value)
void fill(double z)
Sets all cells to the value z.
QCPRange valueRange() const
Definition: qcustomplot.h:2891
int valueSize() const
Definition: qcustomplot.h:2889
void setCell(int keyIndex, int valueIndex, double z)
Sets the data of the cell with indices keyIndex and valueIndex to z.
void clear()
Frees the internal data memory.
QCPRange mKeyRange
Definition: qcustomplot.h:2917
QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange)
Constructs a new QCPColorMapData instance.
void setRange(const QCPRange &keyRange, const QCPRange &valueRange)
Sets the coordinate ranges the data shall be distributed over.
void recalculateDataBounds()
Goes through the data and updates the buffered minimum and maximum data values.
QCPRange dataBounds() const
Definition: qcustomplot.h:2892
int keySize() const
Definition: qcustomplot.h:2888
void setKeySize(int keySize)
Resizes the data array to have keySize cells in the key dimension.
void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
Transforms plot coordinates given by key and value to cell indices of this QCPColorMapData instance.
void setValueRange(const QCPRange &valueRange)
Sets the coordinate range the data shall be distributed over in the value dimension.
bool isEmpty() const
Returns whether this instance carries no data.
Definition: qcustomplot.h:2910
void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
Transforms cell indices given by keyIndex and valueIndex to cell indices of this QCPColorMapData inst...
double cell(int keyIndex, int valueIndex)
void setData(double key, double value, double z)
Sets the data of the cell, which lies at the plot coordinates given by key and value,...
QCPColorMapData & operator=(const QCPColorMapData &other)
Overwrites this color map data instance with the data stored in other.
A plottable representing a two-dimensional color map in a plot.
Definition: qcustomplot.h:2929
QCPColorMapData * data() const
Returns a pointer to the internal data storage of type QCPColorMapData.
Definition: qcustomplot.h:2944
virtual void clearData()
Clears the colormap data by calling QCPColorMapData::clear() on the internal data.
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual void draw(QCPPainter *painter)
void dataRangeChanged(QCPRange newRange)
This signal is emitted when the data range changes.
void setInterpolate(bool enabled)
Sets whether the color map image shall use bicubic interpolation when displaying the color map shrink...
void setData(QCPColorMapData *data, bool copy=false)
Replaces the current data with the provided data.
Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18))
Takes the current appearance of the color map and updates the legend icon, which is used to represent...
virtual void updateMapImage()
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QImage mMapImage
Definition: qcustomplot.h:2984
Q_SLOT void setGradient(const QCPColorGradient &gradient)
Sets the color gradient that is used to represent the data.
void rescaleDataRange(bool recalculateDataBounds=false)
Sets the data range (setDataRange) to span the minimum and maximum values that occur in the current d...
QCPColorMapData * mMapData
Definition: qcustomplot.h:2978
QPointer< QCPColorScale > mColorScale
Definition: qcustomplot.h:2982
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
This signal is emitted when the data scale type changes.
Q_SLOT void setDataRange(const QCPRange &dataRange)
Sets the data range of this color map to dataRange.
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
Sets whether the data is correlated with the color gradient linearly or logarithmically.
QCPColorScale * colorScale() const
Definition: qcustomplot.h:2950
QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a color map with the specified keyAxis and valueAxis.
void setColorScale(QCPColorScale *colorScale)
Associates the color scale colorScale with this color map.
QCPColorGradient mGradient
Definition: qcustomplot.h:2979
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:2977
QCPRange mDataRange
Definition: qcustomplot.h:2976
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void gradientChanged(QCPColorGradient newGradient)
This signal is emitted when the gradient changes.
bool mTightBoundary
Definition: qcustomplot.h:2981
virtual ~QCPColorMap()
bool mMapImageInvalidated
Definition: qcustomplot.h:2986
QCPColorGradient gradient() const
Definition: qcustomplot.h:2949
void setTightBoundary(bool enabled)
Sets whether the outer most data rows and columns are clipped to the specified key and value range (s...
QPixmap mLegendIcon
Definition: qcustomplot.h:2985
QCPRange dataRange() const
Definition: qcustomplot.h:2945
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
bool mInterpolate
Definition: qcustomplot.h:2980
QCPColorScale * mParentColorScale
Definition: qcustomplot.h:2344
Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale)
Creates a new instance, as a child of parentColorScale.
virtual void draw(QCPPainter *painter)
A color scale for use with color coding data such as QCPColorMap.
Definition: qcustomplot.h:2363
virtual void mouseReleaseEvent(QMouseEvent *event)
This event is called, if the mouse was previously pressed inside the outer rect of this layout elemen...
void setType(QCPAxis::AxisType type)
Sets at which side of the color scale the axis is placed, and thus also its orientation.
Q_SLOT void setGradient(const QCPColorGradient &gradient)
Sets the color gradient that will be used to represent data values.
void setRangeDrag(bool enabled)
Sets whether the user can drag the data range (setDataRange).
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:2416
void dataRangeChanged(QCPRange newRange)
This signal is emitted when the data range changes.
bool rangeDrag() const
QPointer< QCPAxis > mColorAxis
Definition: qcustomplot.h:2422
QCPColorGradient gradient() const
Definition: qcustomplot.h:2384
virtual void mouseMoveEvent(QMouseEvent *event)
This event is called, if the mouse is moved inside the outer rect of this layout element.
QString label() const
void rescaleDataRange(bool onlyVisibleMaps)
Changes the data range such that all color maps associated with this color scale are fully mapped to ...
virtual ~QCPColorScale()
QCPRange dataRange() const
Definition: qcustomplot.h:2382
QList< QCPColorMap * > colorMaps() const
Returns a list of all the color maps associated with this color scale.
QCPRange mDataRange
Definition: qcustomplot.h:2415
virtual void mousePressEvent(QMouseEvent *event)
This event is called, if the mouse was pressed while being inside the outer rect of this layout eleme...
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
This signal is emitted when the data scale type changes.
void gradientChanged(QCPColorGradient newGradient)
This signal is emitted when the gradient changes.
QPointer< QCPColorScaleAxisRectPrivate > mAxisRect
Definition: qcustomplot.h:2421
QCPAxis::AxisType mType
Definition: qcustomplot.h:2414
QCPAxis::AxisType type() const
Definition: qcustomplot.h:2381
void setRangeZoom(bool enabled)
Sets whether the user can zoom the data range (setDataRange) by scrolling the mouse wheel.
QCPColorScale(QCustomPlot *parentPlot)
Constructs a new QCPColorScale.
virtual void wheelEvent(QWheelEvent *event)
This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this layo...
virtual void update(UpdatePhase phase)
Updates the layout element and sub-elements.
void setBarWidth(int width)
Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed will ...
Q_SLOT void setDataRange(const QCPRange &dataRange)
Sets the range spanned by the color gradient and that is shown by the axis in the color scale.
bool rangeZoom() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QCPColorGradient mGradient
Definition: qcustomplot.h:2417
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
Sets the scale type of the color scale, i.e.
void setLabel(const QString &str)
Sets the axis label of the color scale.
Holds the data of one single data point for QCPCurve.
Definition: qcustomplot.h:2615
QCPCurveData()
Constructs a curve data point with t, key and value set to zero.
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > *pointData) const
void removeDataAfter(double t)
Removes all data points with curve parameter t greater than t.
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:2685
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual void draw(QCPPainter *painter)
LineStyle
Defines how the curve's line is represented visually in the plot.
Definition: qcustomplot.h:2649
@ lsLine
Data points are connected with a straight line.
Definition: qcustomplot.h:2650
@ lsNone
No line is drawn between data points (e.g. only scatters)
Definition: qcustomplot.h:2649
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a curve which uses keyAxis as its key axis ("x") and valueAxis as its value axis ("y").
void getCurveData(QVector< QPointF > *lineData) const
QPointF outsideCoordsToPixels(double key, double value, int region, QRect axisRect) const
void setLineStyle(LineStyle style)
Sets how the single data points are connected in the plot or how they are represented visually apart ...
void addData(const QCPCurveDataMap &dataMap)
Adds the provided data points in dataMap to the current data.
void setScatterStyle(const QCPScatterStyle &style)
Sets the visual appearance of single data points in the plot.
void setData(QCPCurveDataMap *data, bool copy=false)
Replaces the current data with the provided data.
QCPCurveDataMap * data() const
Definition: qcustomplot.h:2656
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QCPCurveDataMap * mData
Definition: qcustomplot.h:2684
virtual ~QCPCurve()
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void removeData(double fromt, double tot)
Removes all data points with curve parameter t between fromt and tot.
double pointDistance(const QPointF &pixelPoint) const
virtual void clearData()
Removes all data points.
LineStyle mLineStyle
Definition: qcustomplot.h:2686
void removeDataBefore(double t)
Removes all data points with curve parameter t smaller than t.
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:2444
QCPData()
Constructs a data point with key, value and all errors set to zero.
double key
Definition: qcustomplot.h:2448
double valueErrorMinus
Definition: qcustomplot.h:2450
double valueErrorPlus
Definition: qcustomplot.h:2450
double keyErrorPlus
Definition: qcustomplot.h:2449
double value
Definition: qcustomplot.h:2448
double keyErrorMinus
Definition: qcustomplot.h:2449
A plottable representing a graph in a plot.
Definition: qcustomplot.h:2467
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a graph which uses keyAxis as its key axis ("x") and valueAxis as its value axis ("y").
void setErrorBarSize(double size)
Sets the width of the handles at both ends of an error bar in pixels.
void setScatterStyle(const QCPScatterStyle &style)
Sets the visual appearance of single data points in the plot.
void rescaleAxes(bool onlyEnlarge=false) const
Rescales the key and value axes associated with this plottable to contain all displayed data,...
void setData(QCPDataMap *data, bool copy=false)
Replaces the current data with the provided data.
ErrorType errorType() const
Definition: qcustomplot.h:2511
ErrorType mErrorType
Definition: qcustomplot.h:2562
void setChannelFillGraph(QCPGraph *targetGraph)
Sets the target graph for filling the area between this graph and targetGraph with the current brush ...
QPointer< QCPGraph > mChannelFillGraph
Definition: qcustomplot.h:2565
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void removeData(double fromKey, double toKey)
Removes all data points with keys between fromKey and toKey.
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:2561
void setLineStyle(LineStyle ls)
Sets how the single data points are connected in the plot.
void getStepRightPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
int findIndexBelowY(const QVector< QPointF > *data, double y) const
void getPlotData(QVector< QPointF > *lineData, QVector< QCPData > *scatterData) const
virtual void draw(QCPPainter *painter)
void getScatterPlotData(QVector< QCPData > *scatterData) const
void getLinePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QPointF upperFillBasePoint(double upperKey) const
double mErrorBarSize
Definition: qcustomplot.h:2563
QCPDataMap * mData
Definition: qcustomplot.h:2558
LineStyle mLineStyle
Definition: qcustomplot.h:2560
void setDataBothError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError, const QVector< double > &valueError)
Replaces the current data with the provided points in key and value pairs.
int findIndexAboveY(const QVector< QPointF > *data, double y) const
int findIndexBelowX(const QVector< QPointF > *data, double x) const
void addFillBasePoints(QVector< QPointF > *lineData) const
void getStepLeftPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
double pointDistance(const QPointF &pixelPoint) const
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lineData) const
void removeDataBefore(double key)
Removes all data points with keys smaller than key.
void rescaleValueAxis(bool onlyEnlarge=false) const
Rescales the value axis of the plottable so the whole plottable is visible.
QPen mErrorPen
Definition: qcustomplot.h:2559
void addData(const QCPDataMap &dataMap)
Adds the provided data points in dataMap to the current data.
bool mAdaptiveSampling
Definition: qcustomplot.h:2566
int findIndexAboveX(const QVector< QPointF > *data, double x) const
void getImpulsePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
void setErrorBarSkipSymbol(bool enabled)
If enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol b...
void setAdaptiveSampling(bool enabled)
Sets whether adaptive sampling shall be used when plotting this graph.
void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
void getStepCenterPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
void setDataKeyError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError)
Replaces the current data with the provided points in key and value pairs.
void setErrorPen(const QPen &pen)
Sets the pen with which the error bars will be drawn.
void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void setErrorType(ErrorType errorType)
Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data point.
virtual void drawImpulsePlot(QCPPainter *painter, QVector< QPointF > *lineData) const
void setDataValueError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &valueError)
Replaces the current data with the provided points in key and value pairs.
QCPDataMap * data() const
Returns a pointer to the internal data storage of type QCPDataMap.
Definition: qcustomplot.h:2508
QPointF lowerFillBasePoint(double lowerKey) const
bool mErrorBarSkipSymbol
Definition: qcustomplot.h:2564
ErrorType
Defines what kind of error bars are drawn for each data point.
Definition: qcustomplot.h:2497
@ etValue
Error bars for the value dimension of the data point are shown.
Definition: qcustomplot.h:2499
@ etKey
Error bars for the key dimension of the data point are shown.
Definition: qcustomplot.h:2498
@ etBoth
Error bars for both key and value dimensions of the data point are shown.
Definition: qcustomplot.h:2500
@ etNone
No error bars are shown.
Definition: qcustomplot.h:2497
virtual void clearData()
Removes all data points.
LineStyle
Defines how the graph's line is represented visually in the plot.
Definition: qcustomplot.h:2485
@ lsLine
data points are connected by a straight line
Definition: qcustomplot.h:2487
@ lsStepCenter
line is drawn as steps where the step is in between two data points
Definition: qcustomplot.h:2490
@ lsStepRight
line is drawn as steps where the step height is the value of the right data point
Definition: qcustomplot.h:2489
@ lsImpulse
each data point is represented by a line parallel to the value axis, which reaches from the data poin...
Definition: qcustomplot.h:2491
@ lsStepLeft
line is drawn as steps where the step height is the value of the left data point
Definition: qcustomplot.h:2488
@ lsNone
data points are not connected with any lines (e.g.
Definition: qcustomplot.h:2485
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lineData) const
virtual void drawScatterPlot(QCPPainter *painter, QVector< QCPData > *scatterData) const
int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
void removeDataAfter(double key)
Removes all data points with keys greater than key.
void getPreparedData(QVector< QCPData > *lineData, QVector< QCPData > *scatterData) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void rescaleKeyAxis(bool onlyEnlarge=false) const
Rescales the key axis of the plottable so the whole plottable is visible.
virtual ~QCPGraph()
virtual void drawLinePlot(QCPPainter *painter, QVector< QPointF > *lineData) const
void removeFillBasePoints(QVector< QPointF > *lineData) const
Responsible for drawing the grid of a QCPAxis.
Definition: qcustomplot.h:930
QPen mPen
Definition: qcustomplot.h:963
QPen pen() const
Definition: qcustomplot.h:947
void setZeroLinePen(const QPen &pen)
Sets the pen with which zero lines are drawn.
QPen mZeroLinePen
Definition: qcustomplot.h:963
void setAntialiasedZeroLine(bool enabled)
Sets whether zero lines are drawn antialiased.
bool mSubGridVisible
Definition: qcustomplot.h:961
void setAntialiasedSubGrid(bool enabled)
Sets whether sub grid lines are drawn antialiased.
bool mAntialiasedSubGrid
Definition: qcustomplot.h:962
void drawSubGridLines(QCPPainter *painter) const
bool mAntialiasedZeroLine
Definition: qcustomplot.h:962
QCPAxis * mParentAxis
Definition: qcustomplot.h:965
void setSubGridPen(const QPen &pen)
Sets the pen with which sub grid lines are drawn.
void setPen(const QPen &pen)
Sets the pen with which (major) grid lines are drawn.
QPen mSubGridPen
Definition: qcustomplot.h:963
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QCPGrid(QCPAxis *parentAxis)
Creates a QCPGrid instance and sets default values.
virtual void draw(QCPPainter *painter)
void setSubGridVisible(bool visible)
Sets whether grid lines at sub tick marks are drawn.
void drawGridLines(QCPPainter *painter) const
An anchor of an item to which positions can be attached to.
Definition: qcustomplot.h:1490
virtual ~QCPItemAnchor()
QCustomPlot * mParentPlot
Definition: qcustomplot.h:1504
QSet< QCPItemPosition * > mChildren
Definition: qcustomplot.h:1507
QCPAbstractItem * mParentItem
Definition: qcustomplot.h:1505
friend class QCPItemPosition
Definition: qcustomplot.h:1519
void removeChild(QCPItemPosition *pos)
virtual QCPItemPosition * toQCPItemPosition()
Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if...
Definition: qcustomplot.h:1510
void addChild(QCPItemPosition *pos)
virtual QPointF pixelPoint() const
Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.
QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId=-1)
Creates a new QCPItemAnchor.
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the bracket when selected.
QCPItemBracket(QCustomPlot *parentPlot)
Creates a bracket item and sets default values.
virtual QPointF anchorPixelPoint(int anchorId) const
BracketStyle style() const
Definition: qcustomplot.h:3498
QPen pen() const
Definition: qcustomplot.h:3495
void setStyle(BracketStyle style)
Sets the style of the bracket, i.e.
@ bsRound
A brace with round edges.
Definition: qcustomplot.h:3486
@ bsCurly
A curly brace.
Definition: qcustomplot.h:3487
@ bsSquare
A brace with angled edges.
Definition: qcustomplot.h:3485
@ bsCalligraphic
A curly brace with varying stroke width giving a calligraphic impression.
Definition: qcustomplot.h:3488
virtual void draw(QCPPainter *painter)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void setPen(const QPen &pen)
Sets the pen that will be used to draw the bracket.
void setLength(double length)
Sets the length in pixels how far the bracket extends in the direction towards the embraced span of t...
BracketStyle mStyle
Definition: qcustomplot.h:3518
virtual ~QCPItemBracket()
QPen mainPen() const
double length() const
Definition: qcustomplot.h:3497
QCPItemPosition *const left
Definition: qcustomplot.h:3509
QCPItemPosition *const right
Definition: qcustomplot.h:3510
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line.
void setHead(const QCPLineEnding &head)
Sets the line ending style of the head.
QCPItemPosition *const start
Definition: qcustomplot.h:3114
QCPItemPosition *const end
Definition: qcustomplot.h:3117
QCPItemPosition *const endDir
Definition: qcustomplot.h:3116
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line when selected.
QPen mainPen() const
virtual void draw(QCPPainter *painter)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QCPLineEnding head() const
Definition: qcustomplot.h:3102
QCPItemPosition *const startDir
Definition: qcustomplot.h:3115
QCPLineEnding tail() const
Definition: qcustomplot.h:3103
void setTail(const QCPLineEnding &tail)
Sets the line ending style of the tail.
QCPItemCurve(QCustomPlot *parentPlot)
Creates a curve item and sets default values.
virtual ~QCPItemCurve()
QPen pen() const
Definition: qcustomplot.h:3100
QCPLineEnding mTail
Definition: qcustomplot.h:3122
QCPLineEnding mHead
Definition: qcustomplot.h:3122
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3306
QBrush mSelectedBrush
Definition: qcustomplot.h:3323
virtual ~QCPItemEllipse()
void setBrush(const QBrush &brush)
Sets the brush that will be used to fill the ellipse.
QBrush mainBrush() const
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line of the ellipse when selected.
QCPItemEllipse(QCustomPlot *parentPlot)
Creates an ellipse item and sets default values.
QPen pen() const
Definition: qcustomplot.h:3292
QCPItemAnchor *const center
Definition: qcustomplot.h:3316
void setSelectedBrush(const QBrush &brush)
Sets the brush that will be used to fill the ellipse when selected.
QPen mainPen() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QBrush brush() const
Definition: qcustomplot.h:3294
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3307
virtual QPointF anchorPixelPoint(int anchorId) const
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line of the ellipse.
virtual void draw(QCPPainter *painter)
QCPItemPosition *const end
Definition: qcustomplot.h:3070
QCPItemLine(QCustomPlot *parentPlot)
Creates a line item and sets default values.
virtual void draw(QCPPainter *painter)
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line when selected.
QCPLineEnding mHead
Definition: qcustomplot.h:3075
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line.
QCPItemPosition *const start
Definition: qcustomplot.h:3069
QCPLineEnding head() const
Definition: qcustomplot.h:3057
QPen pen() const
Definition: qcustomplot.h:3055
virtual ~QCPItemLine()
QCPLineEnding mTail
Definition: qcustomplot.h:3075
QCPLineEnding tail() const
Definition: qcustomplot.h:3058
void setTail(const QCPLineEnding &tail)
Sets the line ending style of the tail.
QLineF getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void setHead(const QCPLineEnding &head)
Sets the line ending style of the head.
QPen mainPen() const
QPen mSelectedPen
Definition: qcustomplot.h:3074
QPixmap mPixmap
Definition: qcustomplot.h:3378
virtual QPointF anchorPixelPoint(int anchorId) const
QPixmap mScaledPixmap
Definition: qcustomplot.h:3379
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3365
QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const
Qt::AspectRatioMode aspectRatioMode() const
Definition: qcustomplot.h:3352
QPen pen() const
Definition: qcustomplot.h:3353
void setPixmap(const QPixmap &pixmap)
Sets the pixmap that will be displayed.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QPixmap pixmap() const
Definition: qcustomplot.h:3350
virtual ~QCPItemPixmap()
bool scaled() const
Definition: qcustomplot.h:3351
virtual void draw(QCPPainter *painter)
void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false)
Qt::AspectRatioMode mAspectRatioMode
Definition: qcustomplot.h:3381
QCPItemPixmap(QCustomPlot *parentPlot)
Creates a rectangle item and sets default values.
QPen mainPen() const
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3366
void setPen(const QPen &pen)
Sets the pen that will be used to draw a border around the pixmap.
void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio)
Sets whether the pixmap will be scaled to fit the rectangle defined by the topLeft and bottomRight po...
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw a border around the pixmap when selected.
Manages the position of an item.
Definition: qcustomplot.h:1525
void setAxisRect(QCPAxisRect *axisRect)
When setType is ptAxisRectRatio, this function may be used to specify the axis rect the coordinates s...
void setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
When setType is ptPlotCoords, this function may be used to specify the axes the coordinates set with ...
QCPItemAnchor * mParentAnchor
Definition: qcustomplot.h:1572
QCPAxis * valueAxis() const
Definition: qcustomplot.h:1553
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:1569
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:1569
virtual QPointF pixelPoint() const
Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface.
double key() const
Definition: qcustomplot.h:1549
QCPAxis * keyAxis() const
Definition: qcustomplot.h:1552
void setType(PositionType type)
Sets the type of the position.
void setCoords(double key, double value)
Sets the coordinates of this QCPItemPosition.
PositionType
Defines the ways an item position can be specified.
Definition: qcustomplot.h:1533
@ ptAxisRectRatio
Static positioning given by a fraction of the axis rect size (see setAxisRect).
Definition: qcustomplot.h:1537
@ ptAbsolute
Static positioning in pixels, starting from the top left corner of the viewport/widget.
Definition: qcustomplot.h:1533
@ ptViewportRatio
Static positioning given by a fraction of the viewport size.
Definition: qcustomplot.h:1534
@ ptPlotCoords
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
Definition: qcustomplot.h:1540
void setPixelPoint(const QPointF &pixelPoint)
Sets the apparent pixel position.
PositionType type() const
Definition: qcustomplot.h:1547
bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
Sets the parent of this QCPItemPosition to parentAnchor.
PositionType mPositionType
Definition: qcustomplot.h:1568
double value() const
Definition: qcustomplot.h:1550
virtual ~QCPItemPosition()
QPointer< QCPAxisRect > mAxisRect
Definition: qcustomplot.h:1570
QCPItemAnchor * parentAnchor() const
Definition: qcustomplot.h:1548
QCPAxisRect * axisRect() const
virtual void draw(QCPPainter *painter)
QBrush mSelectedBrush
Definition: qcustomplot.h:3174
QBrush mBrush
Definition: qcustomplot.h:3174
QPen pen() const
Definition: qcustomplot.h:3146
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3161
QCPItemRect(QCustomPlot *parentPlot)
Creates a rectangle item and sets default values.
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line of the rectangle.
QBrush brush() const
Definition: qcustomplot.h:3148
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line of the rectangle when selected.
QPen mSelectedPen
Definition: qcustomplot.h:3173
QBrush mainBrush() const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3160
void setBrush(const QBrush &brush)
Sets the brush that will be used to fill the rectangle.
void setSelectedBrush(const QBrush &brush)
Sets the brush that will be used to fill the rectangle when selected.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
virtual QPointF anchorPixelPoint(int anchorId) const
QPen mainPen() const
virtual ~QCPItemRect()
double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
virtual ~QCPItemStraightLine()
virtual void draw(QCPPainter *painter)
QCPItemStraightLine(QCustomPlot *parentPlot)
Creates a straight line item and sets default values.
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line when selected.
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line.
QPen pen() const
Definition: qcustomplot.h:3014
QCPItemPosition *const point1
Definition: qcustomplot.h:3024
QCPItemPosition *const point2
Definition: qcustomplot.h:3025
QLineF getRectClippedStraightLine(const QVector2D &point1, const QVector2D &vec, const QRect &rect) const
QColor color() const
Definition: qcustomplot.h:3209
void setSelectedFont(const QFont &font)
Sets the font of the text that will be used when the item is selected.
QCPItemPosition *const position
Definition: qcustomplot.h:3241
Qt::Alignment positionAlignment() const
Definition: qcustomplot.h:3218
void setBrush(const QBrush &brush)
Sets the brush that will be used do fill the background of the text.
virtual ~QCPItemText()
QBrush mBrush
Definition: qcustomplot.h:3257
QBrush brush() const
Definition: qcustomplot.h:3213
QBrush mSelectedBrush
Definition: qcustomplot.h:3257
void setSelectedPen(const QPen &pen)
Sets the pen that will be used do draw a rectangular border around the text, when the item is selecte...
QString mText
Definition: qcustomplot.h:3259
QPen mainPen() const
void setText(const QString &text)
Sets the text that will be displayed.
virtual QPointF anchorPixelPoint(int anchorId) const
QFont font() const
Definition: qcustomplot.h:3215
void setRotation(double degrees)
Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated arou...
QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
QMargins padding() const
Definition: qcustomplot.h:3221
QFont mSelectedFont
Definition: qcustomplot.h:3258
void setSelectedBrush(const QBrush &brush)
Sets the brush that will be used do fill the background of the text, when the item is selected.
Qt::Alignment mPositionAlignment
Definition: qcustomplot.h:3260
QPen pen() const
Definition: qcustomplot.h:3211
QCPItemText(QCustomPlot *parentPlot)
Creates a text item and sets default values.
void setPositionAlignment(Qt::Alignment alignment)
Sets which point of the text rect shall be aligned with position.
QColor mSelectedColor
Definition: qcustomplot.h:3255
QColor mColor
Definition: qcustomplot.h:3255
virtual void draw(QCPPainter *painter)
QPen mSelectedPen
Definition: qcustomplot.h:3256
void setFont(const QFont &font)
Sets the font of the text.
void setPen(const QPen &pen)
Sets the pen that will be used do draw a rectangular border around the text.
void setColor(const QColor &color)
Sets the color of the text.
void setTextAlignment(Qt::Alignment alignment)
Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft,...
QColor mainColor() const
double mRotation
Definition: qcustomplot.h:3262
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
Qt::Alignment mTextAlignment
Definition: qcustomplot.h:3261
QBrush mainBrush() const
QString text() const
Definition: qcustomplot.h:3217
QMargins mPadding
Definition: qcustomplot.h:3263
void setSelectedColor(const QColor &color)
Sets the color of the text that will be used when the item is selected.
void setPadding(const QMargins &padding)
Sets the distance between the border of the text rectangle and the text.
QFont mainFont() const
void setSelectedBrush(const QBrush &brush)
Sets the brush that will be used to draw any fills of the tracer, when selected.
QBrush mSelectedBrush
Definition: qcustomplot.h:3459
void setBrush(const QBrush &brush)
Sets the brush that will be used to draw any fills of the tracer.
QCPGraph * mGraph
Definition: qcustomplot.h:3462
TracerStyle
The different visual appearances a tracer item can have.
Definition: qcustomplot.h:3415
@ tsPlus
A plus shaped crosshair with limited size.
Definition: qcustomplot.h:3416
@ tsSquare
A square.
Definition: qcustomplot.h:3419
@ tsNone
The tracer is not visible.
Definition: qcustomplot.h:3415
@ tsCircle
A circle.
Definition: qcustomplot.h:3418
@ tsCrosshair
A plus shaped crosshair which spans the complete axis rect.
Definition: qcustomplot.h:3417
void setStyle(TracerStyle style)
Sets the style/visual appearance of the tracer.
virtual ~QCPItemTracer()
double size() const
Definition: qcustomplot.h:3431
void updatePosition()
If the tracer is connected with a graph (setGraph), this function updates the tracer's position to re...
void setGraphKey(double key)
Sets the key of the graph's data point the tracer will be positioned at.
QCPItemPosition *const position
Definition: qcustomplot.h:3454
void setInterpolating(bool enabled)
Sets whether the value of the graph's data points shall be interpolated, when positioning the tracer.
QBrush brush() const
Definition: qcustomplot.h:3429
QPen pen() const
Definition: qcustomplot.h:3427
double mGraphKey
Definition: qcustomplot.h:3463
QBrush mainBrush() const
virtual void draw(QCPPainter *painter)
QPen mainPen() const
QCPGraph * graph() const
Definition: qcustomplot.h:3433
QCPItemTracer(QCustomPlot *parentPlot)
Creates a tracer item and sets default values.
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line of the tracer when selected.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void setSize(double size)
Sets the size of the tracer in pixels, if the style supports setting a size (e.g.
void setGraph(QCPGraph *graph)
Sets the QCPGraph this tracer sticks to.
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line of the tracer.
TracerStyle mStyle
Definition: qcustomplot.h:3461
TracerStyle style() const
Definition: qcustomplot.h:3432
A layer that may contain objects, to control the rendering order.
Definition: qcustomplot.h:365
QList< QCPLayerable * > children() const
Returns a list of all layerables on this layer.
Definition: qcustomplot.h:382
bool mVisible
Definition: qcustomplot.h:394
QCustomPlot * mParentPlot
Definition: qcustomplot.h:390
QString name() const
Definition: qcustomplot.h:380
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:379
void addChild(QCPLayerable *layerable, bool prepend)
QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
Creates a new QCPLayer instance.
QList< QCPLayerable * > mChildren
Definition: qcustomplot.h:393
void setVisible(bool visible)
Sets whether this layer is visible or not.
void removeChild(QCPLayerable *layerable)
bool visible() const
Definition: qcustomplot.h:383
int index() const
Returns the index this layer has in the QCustomPlot.
Definition: qcustomplot.h:381
Base class for all drawable objects.
Definition: qcustomplot.h:408
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QPointer< QCPLayerable > mParentLayerable
Definition: qcustomplot.h:447
bool mAntialiased
Definition: qcustomplot.h:449
void setVisible(bool on)
Sets the visibility of this layerable object.
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:423
void setAntialiased(bool enabled)
Sets whether this object will be drawn antialiased or not.
QCPLayer * layer() const
Definition: qcustomplot.h:425
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPLayerable(QCustomPlot *plot, QString targetLayer="", QCPLayerable *parentLayerable=0)
Creates a new QCPLayerable instance.
void initializeParentPlot(QCustomPlot *parentPlot)
virtual QCP::Interaction selectionCategory() const
void setParentLayerable(QCPLayerable *parentLayerable)
QCustomPlot * mParentPlot
Definition: qcustomplot.h:446
QCPLayer * mLayer
Definition: qcustomplot.h:448
QCPLayerable * parentLayerable() const
Returns the parent layerable of this layerable.
Definition: qcustomplot.h:424
bool realVisibility() const
Returns whether this layerable is visible, taking the visibility of the layerable parent and the visi...
Q_SLOT bool setLayer(QCPLayer *layer)
Sets the layer of this layerable object.
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
void layerChanged(QCPLayer *newLayer)
This signal is emitted when the layer of this layerable changes, i.e.
void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
virtual QRect clipRect() const
friend class QCPAxisRect
Definition: qcustomplot.h:471
virtual void deselectEvent(bool *selectionStateChanged)
virtual void draw(QCPPainter *painter)=0
bool visible() const
Definition: qcustomplot.h:422
bool moveToLayer(QCPLayer *layer, bool prepend)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const =0
The abstract base class for all objects that form the layout system.
Definition: qcustomplot.h:629
virtual int calculateAutoMargin(QCP::MarginSide side)
void setMinimumMargins(const QMargins &margins)
If setAutoMargins is enabled on some or all margins, this function is used to provide minimum values ...
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Layout elements are sensitive to events inside their outer rect.
UpdatePhase
Defines the phases of the update process, that happens just before a replot.
Definition: qcustomplot.h:645
@ upMargins
Phase in which the margins are calculated and set.
Definition: qcustomplot.h:646
@ upLayout
Final phase in which the layout system places the rects of the elements.
Definition: qcustomplot.h:647
@ upPreparation
Phase used for any type of preparation that needs to be done before margin calculation and layout.
Definition: qcustomplot.h:645
virtual ~QCPLayoutElement()
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
QRect rect() const
Returns the inner rect of this layout element.
Definition: qcustomplot.h:656
void setOuterRect(const QRect &rect)
Sets the outer rect of this layout element.
QCPLayout * layout() const
Returns the parent layout of this layout element.
Definition: qcustomplot.h:655
void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
Sets the margin group of the specified margin sides.
QMargins mMinimumMargins
Definition: qcustomplot.h:691
void setMinimumSize(const QSize &size)
Sets the minimum size for the inner rect of this layout element.
void setMaximumSize(const QSize &size)
Sets the maximum size for the inner rect of this layout element.
virtual QList< QCPLayoutElement * > elements(bool recursive) const
Returns a list of all child elements in this layout element.
QCPLayoutElement(QCustomPlot *parentPlot=0)
Creates an instance of QCPLayoutElement and sets default values.
QCPMarginGroup * marginGroup(QCP::MarginSide side) const
Definition: qcustomplot.h:663
void setMargins(const QMargins &margins)
Sets the margins of this layout element.
virtual void update(UpdatePhase phase)
Updates the layout element and sub-elements.
virtual QSize minimumSizeHint() const
Returns the minimum size this layout element (the inner rect) may be compressed to.
virtual QSize maximumSizeHint() const
Returns the maximum size this layout element (the inner rect) may be expanded to.
QMargins mMargins
Definition: qcustomplot.h:691
void setAutoMargins(QCP::MarginSides sides)
Sets on which sides the margin shall be calculated automatically.
QCPLayout * mParentLayout
Definition: qcustomplot.h:688
QHash< QCP::MarginSide, QCPMarginGroup * > mMarginGroups
Definition: qcustomplot.h:693
QMargins margins() const
Definition: qcustomplot.h:658
QCP::MarginSides mAutoMargins
Definition: qcustomplot.h:692
A layout that arranges child elements in a grid.
Definition: qcustomplot.h:757
virtual void updateLayout()
virtual void simplify()
Simplifies the layout by collapsing rows and columns which only contain empty cells.
int rowCount() const
Returns the number of rows in the layout.
int columnCount() const
Returns the number of columns in the layout.
void insertColumn(int newIndex)
Inserts a new column with empty cells at the column index newIndex.
void setRowStretchFactors(const QList< double > &factors)
Sets the stretch factors of all rows.
virtual QList< QCPLayoutElement * > elements(bool recursive) const
Returns a list of all child elements in this layout element.
QList< QList< QCPLayoutElement * > > mElements
Definition: qcustomplot.h:808
QList< double > mRowStretchFactors
Definition: qcustomplot.h:810
virtual QSize maximumSizeHint() const
Returns the maximum size this layout element (the inner rect) may be expanded to.
void setColumnSpacing(int pixels)
Sets the gap that is left blank between columns to pixels.
void insertRow(int newIndex)
Inserts a new row with empty cells at the row index newIndex.
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
QCPLayoutElement * element(int row, int column) const
Returns the element in the cell in row and column.
virtual bool take(QCPLayoutElement *element)
Removes the specified element from the layout and returns true on success.
void setColumnStretchFactors(const QList< double > &factors)
Sets the stretch factors of all columns.
virtual int elementCount() const
Returns the number of elements/cells in the layout.
void setRowStretchFactor(int row, double factor)
Sets the stretch factor of row.
void expandTo(int newRowCount, int newColumnCount)
Expands the layout to have newRowCount rows and newColumnCount columns.
virtual QCPLayoutElement * elementAt(int index) const
Returns the element in the cell with the given index.
void getMaximumRowColSizes(QVector< int > *maxColWidths, QVector< int > *maxRowHeights) const
virtual QSize minimumSizeHint() const
Returns the minimum size this layout element (the inner rect) may be compressed to.
void setRowSpacing(int pixels)
Sets the gap that is left blank between rows to pixels.
bool hasElement(int row, int column)
Returns whether the cell at row and column exists and contains a valid element, i....
QCPLayoutGrid()
Creates an instance of QCPLayoutGrid and sets default values.
QList< double > mColumnStretchFactors
Definition: qcustomplot.h:809
virtual QCPLayoutElement * takeAt(int index)
Removes the element with the given index from the layout and returns it.
bool addElement(int row, int column, QCPLayoutElement *element)
Adds the element to cell with row and column.
void setColumnStretchFactor(int column, double factor)
Sets the stretch factor of column.
virtual ~QCPLayoutGrid()
A layout that places child elements aligned to the border or arbitrarily positioned.
Definition: qcustomplot.h:823
virtual int elementCount() const
Returns the number of elements/cells in the layout.
QCPLayoutInset()
Creates an instance of QCPLayoutInset and sets default values.
QList< Qt::Alignment > mInsetAlignment
Definition: qcustomplot.h:863
QList< InsetPlacement > mInsetPlacement
Definition: qcustomplot.h:862
Qt::Alignment insetAlignment(int index) const
Returns the alignment of the element with the specified index.
void setInsetAlignment(int index, Qt::Alignment alignment)
If the inset placement (setInsetPlacement) is ipBorderAligned, this function is used to set the align...
void setInsetPlacement(int index, InsetPlacement placement)
Sets the inset placement type of the element with the specified index to placement.
InsetPlacement insetPlacement(int index) const
Returns the placement type of the element with the specified index.
virtual void updateLayout()
virtual ~QCPLayoutInset()
virtual QCPLayoutElement * elementAt(int index) const
Returns the element in the cell with the given index.
InsetPlacement
Defines how the placement and sizing is handled for a certain element in a QCPLayoutInset.
Definition: qcustomplot.h:829
@ ipFree
The element may be positioned/sized arbitrarily, see setInsetRect.
Definition: qcustomplot.h:829
@ ipBorderAligned
The element is aligned to one of the layout sides, see setInsetAlignment.
Definition: qcustomplot.h:830
QList< QCPLayoutElement * > mElements
Definition: qcustomplot.h:861
virtual bool take(QCPLayoutElement *element)
Removes the specified element from the layout and returns true on success.
void setInsetRect(int index, const QRectF &rect)
If the inset placement (setInsetPlacement) is ipFree, this function is used to set the position and s...
QList< QRectF > mInsetRect
Definition: qcustomplot.h:864
QRectF insetRect(int index) const
Returns the rect of the element with the specified index.
void addElement(QCPLayoutElement *element, Qt::Alignment alignment)
Adds the specified element to the layout as an inset aligned at the border (setInsetAlignment is init...
virtual QCPLayoutElement * takeAt(int index)
Removes the element with the given index from the layout and returns it.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
The inset layout is sensitive to events only at areas where its (visible) child elements are sensitiv...
The abstract base class for layouts.
Definition: qcustomplot.h:719
void clear()
Removes and deletes all layout elements in this layout.
QCPLayout()
Creates an instance of QCPLayout and sets default values.
virtual void updateLayout()
bool removeAt(int index)
Removes and deletes the element at the provided index.
virtual void update(UpdatePhase phase)
First calls the QCPLayoutElement::update base class implementation to update the margins on this layo...
virtual int elementCount() const =0
Returns the number of elements/cells in the layout.
QVector< int > getSectionSizes(QVector< int > maxSizes, QVector< int > minSizes, QVector< double > stretchFactors, int totalSize) const
virtual void simplify()
Simplifies the layout by collapsing empty cells.
void releaseElement(QCPLayoutElement *el)
virtual QCPLayoutElement * takeAt(int index)=0
Removes the element with the given index from the layout and returns it.
bool remove(QCPLayoutElement *element)
Removes and deletes the provided element.
virtual bool take(QCPLayoutElement *element)=0
Removes the specified element from the layout and returns true on success.
virtual QList< QCPLayoutElement * > elements(bool recursive) const
Returns a list of all child elements in this layout element.
void sizeConstraintsChanged() const
Subclasses call this method to report changed (minimum/maximum) size constraints.
void adoptElement(QCPLayoutElement *el)
virtual QCPLayoutElement * elementAt(int index) const =0
Returns the element in the cell with the given index.
Manages a legend inside a QCustomPlot.
Definition: qcustomplot.h:2146
QCPLegend()
Constructs a new QCPLegend instance with parentPlot as the containing plot and default values.
SelectableParts mSelectableParts
Definition: qcustomplot.h:2240
int iconTextPadding() const
Definition: qcustomplot.h:2186
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QPen getBorderPen() const
void clearItems()
Removes all items from the legend.
Q_SLOT void setSelectedParts(const SelectableParts &selectedParts)
Sets the selected state of the respective legend parts described by SelectablePart.
void setSelectedBorderPen(const QPen &pen)
When the legend box is selected, this pen is used to draw the border instead of the normal pen set vi...
void setIconBorderPen(const QPen &pen)
Sets the pen used to draw a border around each legend icon.
QSize mIconSize
Definition: qcustomplot.h:2238
bool addItem(QCPAbstractLegendItem *item)
Adds item to the legend, if it's not present already.
SelectableParts selectedParts() const
virtual void draw(QCPPainter *painter)
QColor mTextColor
Definition: qcustomplot.h:2237
void setBrush(const QBrush &brush)
Sets the brush of the legend background.
bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
Returns whether the legend contains a QCPPlottableLegendItem which is associated with plottable (e....
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
virtual void deselectEvent(bool *selectionStateChanged)
QPen mBorderPen
Definition: qcustomplot.h:2234
virtual ~QCPLegend()
SelectablePart
Defines the selectable parts of a legend.
Definition: qcustomplot.h:2170
@ spLegendBox
0x001 The legend box (frame)
Definition: qcustomplot.h:2171
@ spNone
0x000 None
Definition: qcustomplot.h:2170
@ spItems
0x002 Legend items individually (see selectedItems)
Definition: qcustomplot.h:2172
QFont mFont
Definition: qcustomplot.h:2236
int itemCount() const
Returns the number of items currently in the legend.
QPen iconBorderPen() const
Definition: qcustomplot.h:2187
QPen mSelectedBorderPen
Definition: qcustomplot.h:2241
void setIconTextPadding(int padding)
Sets the horizontal space in pixels between the legend icon and the text next to it.
QColor mSelectedTextColor
Definition: qcustomplot.h:2244
QPen mSelectedIconBorderPen
Definition: qcustomplot.h:2241
void setSelectedTextColor(const QColor &color)
Sets the default text color that is used by legend items when they are selected.
QPen mIconBorderPen
Definition: qcustomplot.h:2234
void selectionChanged(QCPLegend::SelectableParts parts)
This signal is emitted when the selection state of this legend has changed.
void setBorderPen(const QPen &pen)
Sets the pen, the border of the entire legend is drawn with.
QFont mSelectedFont
Definition: qcustomplot.h:2243
void setSelectedBrush(const QBrush &brush)
When the legend box is selected, this brush is used to draw the legend background instead of the norm...
void selectableChanged(QCPLegend::SelectableParts parts)
int mIconTextPadding
Definition: qcustomplot.h:2239
void setIconSize(const QSize &size)
Sets the size of legend icons.
virtual QCP::Interaction selectionCategory() const
SelectableParts mSelectedParts
Definition: qcustomplot.h:2240
QCPPlottableLegendItem * itemWithPlottable(const QCPAbstractPlottable *plottable) const
Returns the QCPPlottableLegendItem which is associated with plottable (e.g.
QBrush mBrush
Definition: qcustomplot.h:2235
Q_SLOT void setSelectableParts(const SelectableParts &selectableParts)
Sets whether the user can (de-)select the parts in selectable by clicking on the QCustomPlot surface.
void setFont(const QFont &font)
Sets the default font of legend text.
QBrush brush() const
Definition: qcustomplot.h:2182
QBrush getBrush() const
QBrush mSelectedBrush
Definition: qcustomplot.h:2242
void setSelectedFont(const QFont &font)
Sets the default font that is used by legend items when they are selected.
QList< QCPAbstractLegendItem * > selectedItems() const
Returns the legend items that are currently selected.
bool removeItem(int index)
Removes the item with index index from the legend.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Layout elements are sensitive to events inside their outer rect.
QCPAbstractLegendItem * item(int index) const
Returns the item with index i.
SelectableParts selectableParts() const
Definition: qcustomplot.h:2188
bool hasItem(QCPAbstractLegendItem *item) const
Returns whether the legend contains itm.
QPen selectedIconBorderPen() const
Definition: qcustomplot.h:2191
void setSelectedIconBorderPen(const QPen &pen)
Sets the pen legend items will use to draw their icon borders, when they are selected.
void setTextColor(const QColor &color)
Sets the default color of legend text.
QFont font() const
Definition: qcustomplot.h:2183
QSize iconSize() const
Definition: qcustomplot.h:2185
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
Handles the different ending decorations for line-like items.
Definition: qcustomplot.h:872
EndingStyle style() const
Definition: qcustomplot.h:903
void draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
double boundingDistance() const
bool inverted() const
Definition: qcustomplot.h:906
void setWidth(double width)
Sets the width of the ending decoration, if the style supports it.
EndingStyle mStyle
Definition: qcustomplot.h:922
void setStyle(EndingStyle style)
Sets the style of the ending decoration.
void setInverted(bool inverted)
Sets whether the ending decoration shall be inverted.
EndingStyle
Defines the type of ending decoration for line-like items, e.g.
Definition: qcustomplot.h:887
@ esHalfBar
A bar perpendicular to the line, pointing out to only one side (to which side can be changed with set...
Definition: qcustomplot.h:895
@ esSkewedBar
A bar that is skewed (skew controllable via setLength)
Definition: qcustomplot.h:896
@ esBar
A bar perpendicular to the line.
Definition: qcustomplot.h:894
@ esDiamond
A filled diamond (45° rotated square)
Definition: qcustomplot.h:893
@ esFlatArrow
A filled arrow head with a straight/flat back (a triangle)
Definition: qcustomplot.h:888
@ esLineArrow
A non-filled arrow head with open back.
Definition: qcustomplot.h:890
@ esSpikeArrow
A filled arrow head with an indented back.
Definition: qcustomplot.h:889
@ esNone
No ending decoration.
Definition: qcustomplot.h:887
@ esSquare
A filled square.
Definition: qcustomplot.h:892
@ esDisc
A filled circle.
Definition: qcustomplot.h:891
double realLength() const
Starting from the origin of this line ending (which is style specific), returns the length covered by...
void setLength(double length)
Sets the length of the ending decoration, if the style supports it.
double width() const
Definition: qcustomplot.h:904
double mLength
Definition: qcustomplot.h:923
double length() const
Definition: qcustomplot.h:905
QCPLineEnding()
Creates a QCPLineEnding instance with default values (style esNone).
A margin group allows synchronization of margin sides if working with multiple layout elements.
Definition: qcustomplot.h:600
void clear()
Clears this margin group.
void removeChild(QCP::MarginSide side, QCPLayoutElement *element)
QHash< QCP::MarginSide, QList< QCPLayoutElement * > > mChildren
Definition: qcustomplot.h:614
QCPMarginGroup(QCustomPlot *parentPlot)
Creates a new QCPMarginGroup instance in parentPlot.
QList< QCPLayoutElement * > elements(QCP::MarginSide side) const
Returns a list of all layout elements that have their margin side associated with this margin group.
Definition: qcustomplot.h:607
void addChild(QCP::MarginSide side, QCPLayoutElement *element)
bool isEmpty() const
Returns whether this margin group is empty.
int commonMargin(QCP::MarginSide side) const
QPainter subclass used internally.
Definition: qcustomplot.h:312
QStack< bool > mAntialiasingStack
Definition: qcustomplot.h:359
bool begin(QPaintDevice *device)
Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on device...
void drawLine(const QLineF &line)
This is an overloaded member function, provided for convenience. It differs from the above function o...
PainterMode
Defines special modes the painter can operate in.
Definition: qcustomplot.h:319
@ pmNonCosmetic
0x04 Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width...
Definition: qcustomplot.h:322
@ pmNoCaching
0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixma...
Definition: qcustomplot.h:321
@ pmVectorized
0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fix...
Definition: qcustomplot.h:320
QCPPainter()
Creates a new QCPPainter instance and sets default values.
Definition: qcustomplot.cpp:51
bool antialiasing() const
Definition: qcustomplot.h:332
void setModes(PainterModes modes)
Sets the mode of the painter.
void restore()
Restores the painter (see QPainter::restore).
bool mIsAntialiasing
Definition: qcustomplot.h:356
void makeNonCosmetic()
Changes the pen width to 1 if it currently is 0.
void save()
Saves the painter (see QPainter::save).
void setAntialiasing(bool enabled)
Sets whether painting uses antialiasing or not.
PainterModes modes() const
Definition: qcustomplot.h:333
PainterModes mModes
Definition: qcustomplot.h:355
void setMode(PainterMode mode, bool enabled=true)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setPen(const QPen &pen)
Sets the pen of the painter and applies certain fixes to it, depending on the mode of this QCPPainter...
Definition: qcustomplot.cpp:87
A layout element displaying a plot title text.
Definition: qcustomplot.h:2270
void setSelectedTextColor(const QColor &color)
Sets the color of the title text that will be used if the plot title is selected (setSelected).
QString mText
Definition: qcustomplot.h:2312
void setFont(const QFont &font)
Sets the font of the title text.
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QFont font() const
Definition: qcustomplot.h:2287
void selectionChanged(bool selected)
This signal is emitted when the selection state has changed to selected, either by user interaction o...
void setSelectedFont(const QFont &font)
Sets the font of the title text that will be used if the plot title is selected (setSelected).
QFont mainFont() const
QColor mTextColor
Definition: qcustomplot.h:2314
void selectableChanged(bool selectable)
bool selected() const
Definition: qcustomplot.h:2292
void setTextColor(const QColor &color)
Sets the color of the title text.
QRect mTextBoundingRect
Definition: qcustomplot.h:2317
Q_SLOT void setSelectable(bool selectable)
Sets whether the user may select this plot title to selectable.
QColor mSelectedTextColor
Definition: qcustomplot.h:2316
Q_SLOT void setSelected(bool selected)
Sets the selection state of this plot title to selected.
QFont mSelectedFont
Definition: qcustomplot.h:2315
QCPPlotTitle(QCustomPlot *parentPlot)
Creates a new QCPPlotTitle instance and sets default values.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Layout elements are sensitive to events inside their outer rect.
void setText(const QString &text)
Sets the text that will be displayed to text.
QColor mainTextColor() const
QString text() const
Definition: qcustomplot.h:2286
bool selectable() const
Definition: qcustomplot.h:2291
virtual void deselectEvent(bool *selectionStateChanged)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
virtual QSize maximumSizeHint() const
Returns the maximum size this layout element (the inner rect) may be expanded to.
virtual void draw(QCPPainter *painter)
virtual QSize minimumSizeHint() const
Returns the minimum size this layout element (the inner rect) may be compressed to.
A legend item representing a plottable with an icon and the plottable name.
Definition: qcustomplot.h:2122
virtual QSize minimumSizeHint() const
Returns the minimum size this layout element (the inner rect) may be compressed to.
QColor getTextColor() const
virtual void draw(QCPPainter *painter)
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
Creates a new legend item associated with plottable.
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:2132
QPen getIconBorderPen() const
Represents the range an axis is encompassing.
Definition: qcustomplot.h:476
void expand(const QCPRange &otherRange)
Expands this range such that otherRange is contained in the new range.
QCPRange sanitizedForLogScale() const
Returns a sanitized version of the range.
static const double maxRange
Maximum values (negative and positive) the range will accept in range-changing functions.
Definition: qcustomplot.h:509
double size() const
Returns the size of the range, i.e.
QCPRange sanitizedForLinScale() const
Returns a sanitized version of the range.
QCPRange expanded(const QCPRange &otherRange) const
Returns an expanded range that contains this and otherRange.
double lower
Definition: qcustomplot.h:478
static bool validRange(double lower, double upper)
Checks, whether the specified range is within valid bounds, which are defined as QCPRange::maxRange a...
static const double minRange
Minimum range size (upper - lower) the range changing functions will accept.
Definition: qcustomplot.h:508
QCPRange()
Constructs a range with lower and upper set to zero.
double upper
Definition: qcustomplot.h:478
bool contains(double value) const
Returns true when value lies within or exactly on the borders of the range.
double center() const
Returns the center of the range, i.e.
void normalize()
Makes sure lower is numerically smaller than upper.
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:234
double size() const
Definition: qcustomplot.h:274
void drawShape(QCPPainter *painter, QPointF pos) const
Draws the scatter shape with painter at position pos.
void setPixmap(const QPixmap &pixmap)
Sets the pixmap that will be drawn as scatter point to pixmap.
bool isNone() const
Returns whether the scatter shape is ssNone.
Definition: qcustomplot.h:290
void setBrush(const QBrush &brush)
Sets the brush that will be used to fill scatter points to brush.
void setPen(const QPen &pen)
Sets the pen that will be used to draw scatter points to pen.
void setShape(ScatterShape shape)
Sets the shape to shape.
QPainterPath mCustomPath
Definition: qcustomplot.h:303
QCPScatterStyle()
Creates a new QCPScatterStyle instance with size set to 6.
void setCustomPath(const QPainterPath &customPath)
Sets the custom shape that will be drawn as scatter point to customPath.
QPixmap pixmap() const
Definition: qcustomplot.h:278
void setSize(double size)
Sets the size (pixel diameter) of the drawn scatter points to size.
QPen pen() const
Definition: qcustomplot.h:276
ScatterShape
Defines the shape used for scatter points.
Definition: qcustomplot.h:245
@ ssDot
\enumimage{ssDot.png} a single pixel (use ssDisc or ssCircle if you want a round shape with a certain...
Definition: qcustomplot.h:246
@ ssCustom
custom painter operations are performed per scatter (As QPainterPath, see setCustomPath)
Definition: qcustomplot.h:262
@ ssSquare
\enumimage{ssSquare.png} a square
Definition: qcustomplot.h:251
@ ssDisc
\enumimage{ssDisc.png} a circle which is filled with the pen's color (not the brush as with ssCircle)
Definition: qcustomplot.h:250
@ ssPlus
\enumimage{ssPlus.png} a plus
Definition: qcustomplot.h:248
@ ssDiamond
\enumimage{ssDiamond.png} a diamond
Definition: qcustomplot.h:252
@ ssCrossCircle
\enumimage{ssCrossCircle.png} a circle with a cross inside
Definition: qcustomplot.h:258
@ ssPlusSquare
\enumimage{ssPlusSquare.png} a square with a plus inside
Definition: qcustomplot.h:257
@ ssStar
\enumimage{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
Definition: qcustomplot.h:253
@ ssTriangleInverted
\enumimage{ssTriangleInverted.png} an equilateral triangle, standing on corner
Definition: qcustomplot.h:255
@ ssPlusCircle
\enumimage{ssPlusCircle.png} a circle with a plus inside
Definition: qcustomplot.h:259
@ ssCrossSquare
\enumimage{ssCrossSquare.png} a square with a cross inside
Definition: qcustomplot.h:256
@ ssTriangle
\enumimage{ssTriangle.png} an equilateral triangle, standing on baseline
Definition: qcustomplot.h:254
@ ssCircle
\enumimage{ssCircle.png} a circle
Definition: qcustomplot.h:249
@ ssPixmap
a custom pixmap specified by setPixmap, centered on the data point coordinates
Definition: qcustomplot.h:261
@ ssCross
\enumimage{ssCross.png} a cross
Definition: qcustomplot.h:247
@ ssNone
no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines)
Definition: qcustomplot.h:245
@ ssPeace
\enumimage{ssPeace.png} a circle, with one vertical and two downward diagonal lines
Definition: qcustomplot.h:260
QBrush brush() const
Definition: qcustomplot.h:277
QPainterPath customPath() const
Definition: qcustomplot.h:279
ScatterShape shape() const
Definition: qcustomplot.h:275
ScatterShape mShape
Definition: qcustomplot.h:299
void applyTo(QCPPainter *painter, const QPen &defaultPen) const
Applies the pen and the brush of this scatter style to painter.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
double key() const
Definition: qcustomplot.h:2819
void setWidth(double width)
Sets the width of the box in key coordinates.
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual void clearData()
Clears all data in the plottable.
QVector< double > mOutliers
Definition: qcustomplot.h:2855
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void setWhiskerPen(const QPen &pen)
Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis).
double maximum() const
Definition: qcustomplot.h:2824
virtual void drawMedian(QCPPainter *painter) const
void setMedian(double value)
Sets the parameter "median" of the statistical box plot.
void setUpperQuartile(double value)
Sets the parameter "upper Quartile" of the statistical box plot.
void setLowerQuartile(double value)
Sets the parameter "lower Quartile" of the statistical box plot.
virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const
void setMedianPen(const QPen &pen)
Sets the pen used for drawing the median indicator line inside the statistical box.
virtual void draw(QCPPainter *painter)
QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a statistical box which uses keyAxis as its key axis ("x") and valueAxis as its value axis...
void setKey(double key)
Sets the key coordinate of the statistical box.
void setMinimum(double value)
Sets the parameter "minimum" of the statistical box plot.
virtual void drawOutliers(QCPPainter *painter) const
void setWhiskerBarPen(const QPen &pen)
Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at each ...
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
double minimum() const
Definition: qcustomplot.h:2820
double median() const
Definition: qcustomplot.h:2822
void setMaximum(double value)
Sets the parameter "maximum" of the statistical box plot.
void setOutlierStyle(const QCPScatterStyle &style)
Sets the appearance of the outlier data points.
void setWhiskerWidth(double width)
Sets the width of the whiskers (setMinimum, setMaximum) in key coordinates.
void setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
Sets all parameters of the statistical box plot at once.
QCPScatterStyle mOutlierStyle
Definition: qcustomplot.h:2860
double width() const
Definition: qcustomplot.h:2826
virtual void drawWhiskers(QCPPainter *painter) const
void setOutliers(const QVector< double > &values)
Sets a vector of outlier values that will be drawn as scatters.
double lowerQuartile() const
Definition: qcustomplot.h:2821
double upperQuartile() const
Definition: qcustomplot.h:2823
The central class of the library.
Definition: qcustomplot.h:1657
void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
This signal is emitted when a legend (item) is double clicked.
QCPLayer * currentLayer() const
Returns the layer that is set as current layer (see setCurrentLayer).
void drawBackground(QCPPainter *painter)
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:1835
QCPLayer * layer(const QString &name) const
Returns the layer with the specified name.
void beforeReplot()
This signal is emitted immediately before a replot takes place (caused by a call to the slot replot).
Qt::KeyboardModifier mMultiSelectModifier
Definition: qcustomplot.h:1840
virtual QSize minimumSizeHint() const
QCPLayerable * layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const
QList< QCPAxisRect * > axisRects() const
Returns all axis rects in the plot.
QCPAbstractItem * item() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setBackground(const QPixmap &pm)
Sets pm as the viewport background pixmap (see setViewport).
virtual void resizeEvent(QResizeEvent *event)
int itemCount() const
Returns the number of currently existing items in the plot.
QRect viewport() const
Returns the viewport rect of this QCustomPlot instance.
Definition: qcustomplot.h:1695
void toPainter(QCPPainter *painter, int width=0, int height=0)
Renders the plot using the passed painter.
void titleClick(QMouseEvent *event, QCPPlotTitle *title)
This signal is emitted when a plot title is clicked.
QCP::AntialiasedElements mNotAntialiasedElements
Definition: qcustomplot.h:1829
virtual void paintEvent(QPaintEvent *event)
const QCP::Interactions interactions() const
Definition: qcustomplot.h:1703
QPointer< QCPLayoutElement > mMouseEventElement
Definition: qcustomplot.h:1845
QCPAbstractPlottable * plottable(int index)
Returns the plottable with index.
void setBackgroundScaled(bool scaled)
Sets whether the viewport background pixmap shall be scaled to fit the viewport.
QBrush mBackgroundBrush
Definition: qcustomplot.h:1833
void setPlottingHint(QCP::PlottingHint hint, bool enabled=true)
Sets the specified plotting hint to enabled.
void setViewport(const QRect &rect)
Sets the viewport of this QCustomPlot.
bool removeLayer(QCPLayer *layer)
Removes the specified layer and returns true on success.
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
Sets the single interaction of this QCustomPlot to enabled.
QCustomPlot(QWidget *parent=0)
Constructs a QCustomPlot and sets reasonable default values.
RefreshPriority
Defines with what timing the QCustomPlot surface is refreshed after a replot.
Definition: qcustomplot.h:1686
@ rpImmediate
The QCustomPlot surface is immediately refreshed, by calling QWidget::repaint() after the replot.
Definition: qcustomplot.h:1686
@ rpQueued
Queues the refresh such that it is performed at a slightly delayed point in time after the replot,...
Definition: qcustomplot.h:1687
@ rpHint
Whether to use immediate repaint or queued update depends on whether the plotting hint QCP::phForceRe...
Definition: qcustomplot.h:1688
bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1)
Saves a JPG image file to fileName on disc.
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
If scaling of the viewport background pixmap is enabled (setBackgroundScaled), use this function to d...
void setSelectionTolerance(int pixels)
Sets the tolerance that is used to decide whether a click selects an object (e.g.
QCPLegend * legend
A pointer to the default legend of the main axis rect.
Definition: qcustomplot.h:1796
void selectionChangedByUser()
This signal is emitted after the user has changed the selection in the QCustomPlot,...
virtual QSize sizeHint() const
int selectionTolerance() const
Definition: qcustomplot.h:1704
void plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
This signal is emitted when a plottable is clicked.
friend class QCPLayer
Definition: qcustomplot.h:1871
int graphCount() const
Returns the number of currently existing graphs in the plot.
void setInteractions(const QCP::Interactions &interactions)
Sets the possible interactions of this QCustomPlot as an or-combination of QCP::Interaction enums.
int plottableCount() const
Returns the number of currently existing plottables in the plot.
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpHint)
Causes a complete replot into the internal buffer.
QList< QCPAbstractPlottable * > mPlottables
Definition: qcustomplot.h:1825
bool mBackgroundScaled
Definition: qcustomplot.h:1836
QCP::AntialiasedElements antialiasedElements() const
Definition: qcustomplot.h:1700
bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0)
Saves a BMP image file to fileName on disc.
QList< QCPAbstractItem * > mItems
Definition: qcustomplot.h:1827
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
This signal is emitted when an axis is double clicked.
void afterReplot()
This signal is emitted immediately after a replot has taken place (caused by a call to the slot replo...
QCPGraph * addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0)
Creates a new graph inside the plot.
virtual void mouseReleaseEvent(QMouseEvent *event)
bool hasPlottable(QCPAbstractPlottable *plottable) const
Returns whether this QCustomPlot instance contains the plottable.
QList< QCPLayer * > mLayers
Definition: qcustomplot.h:1828
bool setCurrentLayer(const QString &name)
Sets the layer with the specified name to be the current layer.
void mouseMove(QMouseEvent *event)
This signal is emitted when the QCustomPlot receives a mouse move event.
QList< QCPAbstractPlottable * > selectedPlottables() const
Returns a list of the selected plottables.
QPixmap mPaintBuffer
Definition: qcustomplot.h:1843
QCP::AntialiasedElements notAntialiasedElements() const
Definition: qcustomplot.h:1701
LayerInsertMode
Defines how a layer should be inserted relative to an other layer.
Definition: qcustomplot.h:1676
@ limAbove
Layer is inserted above other layer.
Definition: qcustomplot.h:1677
virtual ~QCustomPlot()
bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1)
Saves a PNG image file to fileName on disc.
virtual void mouseDoubleClickEvent(QMouseEvent *event)
void setNoAntialiasingOnDrag(bool enabled)
Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes ranges.
void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
This signal is emitted when a legend (item) is clicked.
virtual void wheelEvent(QWheelEvent *event)
QList< QCPAxis * > selectedAxes() const
Returns the axes that currently have selected parts, i.e.
void updateLayerIndices() const
friend class QCPLegend
Definition: qcustomplot.h:1869
virtual void axisRemoved(QCPAxis *axis)
int axisRectCount() const
Returns the number of axis rects in the plot.
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
Sets the keyboard modifier that will be recognized as multi-select-modifier.
bool removeGraph(QCPGraph *graph)
Removes the specified graph from the plot and, if necessary, from the QCustomPlot::legend.
void setPlottingHints(const QCP::PlottingHints &hints)
Sets the plotting hints for this QCustomPlot instance as an or combination of QCP::PlottingHint.
int clearPlottables()
Removes all plottables from the plot (and the QCustomPlot::legend, if necessary).
QCPAxis * xAxis
A pointer to the primary x Axis (bottom) of the main axis rect of the plot.
Definition: qcustomplot.h:1795
void mouseDoubleClick(QMouseEvent *event)
This signal is emitted when the QCustomPlot receives a mouse double click event.
virtual void legendRemoved(QCPLegend *legend)
Q_SLOT void deselectAll()
Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot.
QCP::PlottingHints mPlottingHints
Definition: qcustomplot.h:1839
QCPLayer * mCurrentLayer
Definition: qcustomplot.h:1838
QCP::AntialiasedElements mAntialiasedElements
Definition: qcustomplot.h:1829
bool addItem(QCPAbstractItem *item)
Adds the specified item to the plot.
QPixmap toPixmap(int width=0, int height=0, double scale=1.0)
Renders the plot to a pixmap and returns it.
QCPGraph * graph() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0, const QString &pdfCreator="", const QString &pdfTitle="")
Saves a PDF with the vectorized plot to the file fileName.
bool mAutoAddPlottableToLegend
Definition: qcustomplot.h:1824
int clearGraphs()
Removes all graphs from the plot (and the QCustomPlot::legend, if necessary).
bool mReplotting
Definition: qcustomplot.h:1846
bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1)
Saves the plot to a rastered image file fileName in the image format format.
bool addPlottable(QCPAbstractPlottable *plottable)
Adds the specified plottable to the plot and, if setAutoAddPlottableToLegend is enabled,...
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:1837
int mSelectionTolerance
Definition: qcustomplot.h:1831
virtual void mousePressEvent(QMouseEvent *event)
int clearItems()
Removes all items from the plot.
void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
This signal is emitted when an axis is clicked.
QCPAbstractItem * itemAt(const QPointF &pos, bool onlySelectable=false) const
Returns the item at the pixel position pos.
QRect mViewport
Definition: qcustomplot.h:1822
QPoint mMousePressPos
Definition: qcustomplot.h:1844
virtual void mouseMoveEvent(QMouseEvent *event)
QCP::PlottingHints plottingHints() const
Definition: qcustomplot.h:1706
void mouseWheel(QWheelEvent *event)
This signal is emitted when the QCustomPlot receives a mouse wheel event.
void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
This signal is emitted when an item is double clicked.
bool mNoAntialiasingOnDrag
Definition: qcustomplot.h:1832
QList< QCPLegend * > selectedLegends() const
Returns the legends that currently have selected parts, i.e.
void mouseRelease(QMouseEvent *event)
This signal is emitted when the QCustomPlot receives a mouse release event.
QCPLayoutGrid * mPlotLayout
Definition: qcustomplot.h:1823
bool noAntialiasingOnDrag() const
Definition: qcustomplot.h:1705
void mousePress(QMouseEvent *event)
This signal is emitted when the QCustomPlot receives a mouse press event.
friend class QCPAxisRect
Definition: qcustomplot.h:1872
QCPAbstractPlottable * plottableAt(const QPointF &pos, bool onlySelectable=false) const
Returns the plottable at the pixel position pos.
QList< QCPGraph * > selectedGraphs() const
Returns a list of the selected graphs.
void titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
This signal is emitted when a plot title is double clicked.
bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove)
Adds a new layer to this QCustomPlot instance.
QCP::Interactions mInteractions
Definition: qcustomplot.h:1830
virtual void draw(QCPPainter *painter)
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
Rescales the axes such that all plottables (like graphs) in the plot are fully visible.
void setAutoAddPlottableToLegend(bool on)
If set to true, adding a plottable (e.g.
QCPAxis * xAxis2
A pointer to the secondary x Axis (top) of the main axis rect of the plot.
Definition: qcustomplot.h:1795
QList< QCPGraph * > mGraphs
Definition: qcustomplot.h:1826
QCPAbstractPlottable * plottable()
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool removeItem(QCPAbstractItem *item)
Removes the specified item from the plot.
void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
Sets which elements are forcibly drawn not antialiased as an or combination of QCP::AntialiasedElemen...
void itemClick(QCPAbstractItem *item, QMouseEvent *event)
This signal is emitted when an item is clicked.
QCPAxisRect * axisRect(int index=0) const
Returns the axis rect with index.
bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove)
Moves the specified layer either above or below otherLayer.
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:1834
void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true)
Sets whether the specified antialiasedElement is forcibly drawn antialiased.
bool hasItem(QCPAbstractItem *item) const
Returns whether this QCustomPlot contains the item.
QCPAxis * yAxis2
A pointer to the secondary y Axis (right) of the main axis rect of the plot.
Definition: qcustomplot.h:1795
void plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
This signal is emitted when a plottable is double clicked.
bool removePlottable(QCPAbstractPlottable *plottable)
Removes the specified plottable from the plot and, if necessary, from the legend (QCustomPlot::legend...
void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
Sets which elements are forcibly drawn antialiased as an or combination of QCP::AntialiasedElement.
QCPAxis * yAxis
A pointer to the primary y Axis (left) of the main axis rect of the plot.
Definition: qcustomplot.h:1795
int layerCount() const
Returns the number of currently existing layers in the plot.
QCPLayoutElement * layoutElementAt(const QPointF &pos) const
Returns the layout element at pixel position pos.
void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true)
Sets whether the specified notAntialiasedElement is forcibly drawn not antialiased.
QList< QCPAbstractItem * > selectedItems() const
Returns a list of the selected items.
int n
bool done
Definition: main.cpp:42
The QCP Namespace contains general enums and QFlags used throughout the QCustomPlot library.
Definition: qcustomplot.h:93
bool isInvalidData(double value)
Definition: qcustomplot.h:171
int getMarginValue(const QMargins &margins, QCP::MarginSide side)
Definition: qcustomplot.h:212
Interaction
Defines the mouse interactions possible with QCustomPlot.
Definition: qcustomplot.h:154
@ iSelectLegend
0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts)
Definition: qcustomplot.h:159
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
Definition: qcustomplot.h:154
@ iSelectPlottables
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable)
Definition: qcustomplot.h:157
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
Definition: qcustomplot.h:155
@ iSelectAxes
0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)
Definition: qcustomplot.h:158
@ iSelectItems
0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see QCPAbstractItem)
Definition: qcustomplot.h:160
@ iMultiSelect
0x004 The user can select multiple objects by holding the modifier set by QCustomPlot::setMultiSelect...
Definition: qcustomplot.h:156
@ iSelectOther
0x080 All other objects are selectable (e.g. your own derived layerables, the plot title,...
Definition: qcustomplot.h:161
PlottingHint
Defines plotting hints that control various aspects of the quality and speed of plotting.
Definition: qcustomplot.h:138
@ phCacheLabels
0x004 axis (tick) labels will be cached as pixmaps, increasing replot performance.
Definition: qcustomplot.h:143
@ phForceRepaint
0x002 causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called w...
Definition: qcustomplot.h:141
@ phFastPolylines
0x001 Graph/Curve lines are drawn with a faster method.
Definition: qcustomplot.h:139
MarginSide
Defines the sides of a rectangular entity to which margins can be applied.
Definition: qcustomplot.h:99
@ msAll
0xFF all margins
Definition: qcustomplot.h:103
@ msBottom
0x08 bottom margin
Definition: qcustomplot.h:102
@ msTop
0x04 top margin
Definition: qcustomplot.h:101
@ msNone
0x00 no margin
Definition: qcustomplot.h:104
@ msRight
0x02 right margin
Definition: qcustomplot.h:100
@ msLeft
0x01 left margin
Definition: qcustomplot.h:99
AntialiasedElement
Defines what objects of a plot can be forcibly drawn antialiased/not antialiased.
Definition: qcustomplot.h:117
@ aeLegendItems
0x0010 Legend items
Definition: qcustomplot.h:121
@ aeZeroLine
0x0400 Zero-lines, see QCPGrid::setZeroLinePen
Definition: qcustomplot.h:127
@ aePlottables
0x0020 Main lines of plottables (excluding error bars, see element aeErrorBars)
Definition: qcustomplot.h:122
@ aeGrid
0x0002 Grid lines
Definition: qcustomplot.h:118
@ aeFills
0x0200 Borders of fills (e.g. under or between graphs)
Definition: qcustomplot.h:126
@ aeErrorBars
0x0100 Error bars
Definition: qcustomplot.h:125
@ aeLegend
0x0008 Legend box
Definition: qcustomplot.h:120
@ aeAll
0xFFFF All elements
Definition: qcustomplot.h:128
@ aeNone
0x0000 No elements
Definition: qcustomplot.h:129
@ aeSubGrid
0x0004 Sub grid lines
Definition: qcustomplot.h:119
@ aeScatters
0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap)
Definition: qcustomplot.h:124
@ aeAxes
0x0001 Axis base line and tick marks
Definition: qcustomplot.h:117
@ aeItems
0x0040 Main lines of items
Definition: qcustomplot.h:123
void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
Definition: qcustomplot.h:192
void append(const size_t index, shared_ptr< Epsilon_neighbours_t > en)
Definition: clustering.cpp:65
bool lower(Value &a, Value &b)
Definition: main.cpp:337
const FSC max
Definition: strain.h:48
const FSC min
Definition: strain.h:49
QMap< double, QCPCurveData > QCPCurveDataMap
Container for storing QCPCurveData items in a sorted fashion.
Definition: qcustomplot.h:2631
QMap< double, QCPData > QCPDataMap
Container for storing QCPData items in a sorted fashion.
Definition: qcustomplot.h:2461
QMap< double, QCPBarData > QCPBarDataMap
Container for storing QCPBarData items in a sorted fashion.
Definition: qcustomplot.h:2727
grid on
Definition: show_eyes_axes.m:5
function white
Definition: show_eyes_axes.m:3
degrees offset
Definition: sine.m:4