Vespucci  1.0.0
qcustomplot.cpp
Go to the documentation of this file.
1 /***************************************************************
2  * Vespucci-QCP, a very very minor modification to QCustomPlot *
3  * Modifications as noted in text (C) 2014 Daniel Foose *
4  * QCP IP notice repeated verbatim below *
5  * *************************************************************/
6 /***************************************************************************
7 ** **
8 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
9 ** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer **
10 ** **
11 ** This program is free software: you can redistribute it and/or modify **
12 ** it under the terms of the GNU General Public License as published by **
13 ** the Free Software Foundation, either version 3 of the License, or **
14 ** (at your option) any later version. **
15 ** **
16 ** This program is distributed in the hope that it will be useful, **
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of **
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
19 ** GNU General Public License for more details. **
20 ** **
21 ** You should have received a copy of the GNU General Public License **
22 ** along with this program. If not, see http://www.gnu.org/licenses/. **
23 ** **
24 ****************************************************************************
25 ** Author: Emanuel Eichhammer **
26 ** Website/Contact: http://www.qcustomplot.com/ **
27 ** Date: 27.12.14 **
28 ** Version: 1.3.0 **
29 ****************************************************************************/
30 
31 #include "qcustomplot.h"
32 
33 
34 
38 
56  QPainter(),
57  mModes(pmDefault),
58  mIsAntialiasing(false)
59 {
60  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
61  // a call to begin() will follow
62 }
63 
70 QCPPainter::QCPPainter(QPaintDevice *device) :
71  QPainter(device),
73  mIsAntialiasing(false)
74 {
75 #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.
76  if (isActive())
77  setRenderHint(QPainter::NonCosmeticDefaultPen);
78 #endif
79 }
80 
82 {
83 }
84 
91 void QCPPainter::setPen(const QPen &pen)
92 {
93  QPainter::setPen(pen);
94  if (mModes.testFlag(pmNonCosmetic))
96 }
97 
105 void QCPPainter::setPen(const QColor &color)
106 {
107  QPainter::setPen(color);
108  if (mModes.testFlag(pmNonCosmetic))
109  makeNonCosmetic();
110 }
111 
119 void QCPPainter::setPen(Qt::PenStyle penStyle)
120 {
121  QPainter::setPen(penStyle);
122  if (mModes.testFlag(pmNonCosmetic))
123  makeNonCosmetic();
124 }
125 
134 void QCPPainter::drawLine(const QLineF &line)
135 {
136  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
137  QPainter::drawLine(line);
138  else
139  QPainter::drawLine(line.toLine());
140 }
141 
148 void QCPPainter::setAntialiasing(bool enabled)
149 {
150  setRenderHint(QPainter::Antialiasing, enabled);
151  if (mIsAntialiasing != enabled)
152  {
153  mIsAntialiasing = enabled;
154  if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
155  {
156  if (mIsAntialiasing)
157  translate(0.5, 0.5);
158  else
159  translate(-0.5, -0.5);
160  }
161  }
162 }
163 
168 void QCPPainter::setModes(QCPPainter::PainterModes modes)
169 {
170  mModes = modes;
171 }
172 
184 bool QCPPainter::begin(QPaintDevice *device)
185 {
186  bool result = QPainter::begin(device);
187 #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.
188  if (result)
189  setRenderHint(QPainter::NonCosmeticDefaultPen);
190 #endif
191  return result;
192 }
193 
200 {
201  if (!enabled && mModes.testFlag(mode))
202  mModes &= ~mode;
203  else if (enabled && !mModes.testFlag(mode))
204  mModes |= mode;
205 }
206 
216 {
218  QPainter::save();
219 }
220 
230 {
231  if (!mAntialiasingStack.isEmpty())
233  else
234  qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
235  QPainter::restore();
236 }
237 
243 {
244  if (qFuzzyIsNull(pen().widthF()))
245  {
246  QPen p = pen();
247  p.setWidth(1);
248  QPainter::setPen(p);
249  }
250 }
251 
252 
256 
318 /* start documentation of inline functions */
319 
339 /* end documentation of inline functions */
340 
348  mSize(6),
349  mShape(ssNone),
350  mPen(Qt::NoPen),
351  mBrush(Qt::NoBrush),
352  mPenDefined(false)
353 {
354 }
355 
364  mSize(size),
365  mShape(shape),
366  mPen(Qt::NoPen),
367  mBrush(Qt::NoBrush),
368  mPenDefined(false)
369 {
370 }
371 
377  mSize(size),
378  mShape(shape),
379  mPen(QPen(color)),
380  mBrush(Qt::NoBrush),
381  mPenDefined(true)
382 {
383 }
384 
389 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
390  mSize(size),
391  mShape(shape),
392  mPen(QPen(color)),
393  mBrush(QBrush(fill)),
394  mPenDefined(true)
395 {
396 }
397 
413 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
414  mSize(size),
415  mShape(shape),
416  mPen(pen),
417  mBrush(brush),
418  mPenDefined(pen.style() != Qt::NoPen)
419 {
420 }
421 
427  mSize(5),
428  mShape(ssPixmap),
429  mPen(Qt::NoPen),
430  mBrush(Qt::NoBrush),
431  mPixmap(pixmap),
432  mPenDefined(false)
433 {
434 }
435 
445 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
446  mSize(size),
447  mShape(ssCustom),
448  mPen(pen),
449  mBrush(brush),
450  mCustomPath(customPath),
451  mPenDefined(pen.style() != Qt::NoPen)
452 {
453 }
454 
461 {
462  mSize = size;
463 }
464 
474 {
475  mShape = shape;
476 }
477 
486 void QCPScatterStyle::setPen(const QPen &pen)
487 {
488  mPenDefined = true;
489  mPen = pen;
490 }
491 
498 void QCPScatterStyle::setBrush(const QBrush &brush)
499 {
500  mBrush = brush;
501 }
502 
510 void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
511 {
513  mPixmap = pixmap;
514 }
515 
522 {
525 }
526 
536 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
537 {
538  painter->setPen(mPenDefined ? mPen : defaultPen);
539  painter->setBrush(mBrush);
540 }
541 
550 void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
551 {
552  drawShape(painter, pos.x(), pos.y());
553 }
554 
558 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
559 {
560  double w = mSize/2.0;
561  switch (mShape)
562  {
563  case ssNone: break;
564  case ssDot:
565  {
566  painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
567  break;
568  }
569  case ssCross:
570  {
571  painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
572  painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
573  break;
574  }
575  case ssPlus:
576  {
577  painter->drawLine(QLineF(x-w, y, x+w, y));
578  painter->drawLine(QLineF( x, y+w, x, y-w));
579  break;
580  }
581  case ssCircle:
582  {
583  painter->drawEllipse(QPointF(x , y), w, w);
584  break;
585  }
586  case ssDisc:
587  {
588  QBrush b = painter->brush();
589  painter->setBrush(painter->pen().color());
590  painter->drawEllipse(QPointF(x , y), w, w);
591  painter->setBrush(b);
592  break;
593  }
594  case ssSquare:
595  {
596  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
597  break;
598  }
599  case ssDiamond:
600  {
601  painter->drawLine(QLineF(x-w, y, x, y-w));
602  painter->drawLine(QLineF( x, y-w, x+w, y));
603  painter->drawLine(QLineF(x+w, y, x, y+w));
604  painter->drawLine(QLineF( x, y+w, x-w, y));
605  break;
606  }
607  case ssStar:
608  {
609  painter->drawLine(QLineF(x-w, y, x+w, y));
610  painter->drawLine(QLineF( x, y+w, x, y-w));
611  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
612  painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
613  break;
614  }
615  case ssTriangle:
616  {
617  painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
618  painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
619  painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w));
620  break;
621  }
622  case ssTriangleInverted:
623  {
624  painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
625  painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
626  painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w));
627  break;
628  }
629  case ssCrossSquare:
630  {
631  painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
632  painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
633  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
634  break;
635  }
636  case ssPlusSquare:
637  {
638  painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
639  painter->drawLine(QLineF( x, y+w, x, y-w));
640  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
641  break;
642  }
643  case ssCrossCircle:
644  {
645  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
646  painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
647  painter->drawEllipse(QPointF(x, y), w, w);
648  break;
649  }
650  case ssPlusCircle:
651  {
652  painter->drawLine(QLineF(x-w, y, x+w, y));
653  painter->drawLine(QLineF( x, y+w, x, y-w));
654  painter->drawEllipse(QPointF(x, y), w, w);
655  break;
656  }
657  case ssPeace:
658  {
659  painter->drawLine(QLineF(x, y-w, x, y+w));
660  painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
661  painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
662  painter->drawEllipse(QPointF(x, y), w, w);
663  break;
664  }
665  case ssPixmap:
666  {
667  painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
668  break;
669  }
670  case ssCustom:
671  {
672  QTransform oldTransform = painter->transform();
673  painter->translate(x, y);
674  painter->scale(mSize/6.0, mSize/6.0);
675  painter->drawPath(mCustomPath);
676  painter->setTransform(oldTransform);
677  break;
678  }
679  }
680 }
681 
682 
686 
729 /* start documentation of inline functions */
730 
745 /* end documentation of inline functions */
746 
755 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
756  QObject(parentPlot),
757  mParentPlot(parentPlot),
758  mName(layerName),
759  mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
760  mVisible(true)
761 {
762  // Note: no need to make sure layerName is unique, because layer
763  // management is done with QCustomPlot functions.
764 }
765 
767 {
768  // If child layerables are still on this layer, detach them, so they don't try to reach back to this
769  // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
770  // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
771  // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
772 
773  while (!mChildren.isEmpty())
774  mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
775 
776  if (mParentPlot->currentLayer() == this)
777  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.";
778 }
779 
789 {
790  mVisible = visible;
791 }
792 
803 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
804 {
805  if (!mChildren.contains(layerable))
806  {
807  if (prepend)
808  mChildren.prepend(layerable);
809  else
810  mChildren.append(layerable);
811  } else
812  qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
813 }
814 
825 {
826  if (!mChildren.removeOne(layerable))
827  qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
828 }
829 
830 
834 
847 /* start documentation of inline functions */
848 
862 /* end documentation of inline functions */
863 /* start documentation of pure virtual functions */
864 
905 /* end documentation of pure virtual functions */
906 /* start documentation of signals */
907 
916 /* end documentation of signals */
917 
936 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
937  QObject(plot),
938  mVisible(true),
939  mParentPlot(plot),
940  mParentLayerable(parentLayerable),
941  mLayer(0),
942  mAntialiased(true)
943 {
944  if (mParentPlot)
945  {
946  if (targetLayer.isEmpty())
948  else if (!setLayer(targetLayer))
949  qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
950  }
951 }
952 
954 {
955  if (mLayer)
956  {
957  mLayer->removeChild(this);
958  mLayer = 0;
959  }
960 }
961 
968 {
969  mVisible = on;
970 }
971 
979 {
980  return moveToLayer(layer, false);
981 }
982 
988 bool QCPLayerable::setLayer(const QString &layerName)
989 {
990  if (!mParentPlot)
991  {
992  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
993  return false;
994  }
995  if (QCPLayer *layer = mParentPlot->layer(layerName))
996  {
997  return setLayer(layer);
998  } else
999  {
1000  qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
1001  return false;
1002  }
1003 }
1004 
1012 {
1013  mAntialiased = enabled;
1014 }
1015 
1030 {
1031  return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1032 }
1033 
1068 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1069 {
1070  Q_UNUSED(pos)
1071  Q_UNUSED(onlySelectable)
1072  Q_UNUSED(details)
1073  return -1.0;
1074 }
1075 
1094 {
1095  if (mParentPlot)
1096  {
1097  qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1098  return;
1099  }
1100 
1101  if (!parentPlot)
1102  qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1103 
1106 }
1107 
1120 {
1122 }
1123 
1133 {
1134  if (layer && !mParentPlot)
1135  {
1136  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1137  return false;
1138  }
1139  if (layer && layer->parentPlot() != mParentPlot)
1140  {
1141  qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1142  return false;
1143  }
1144 
1145  QCPLayer *oldLayer = mLayer;
1146  if (mLayer)
1147  mLayer->removeChild(this);
1148  mLayer = layer;
1149  if (mLayer)
1150  mLayer->addChild(this, prepend);
1151  if (mLayer != oldLayer)
1152  emit layerChanged(mLayer);
1153  return true;
1154 }
1155 
1163 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1164 {
1165  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1166  painter->setAntialiasing(false);
1167  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1168  painter->setAntialiasing(true);
1169  else
1170  painter->setAntialiasing(localAntialiased);
1171 }
1172 
1190 {
1191  Q_UNUSED(parentPlot)
1192 }
1193 
1206 {
1207  return QCP::iSelectOther;
1208 }
1209 
1220 {
1221  if (mParentPlot)
1222  return mParentPlot->viewport();
1223  else
1224  return QRect();
1225 }
1226 
1255 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1256 {
1257  Q_UNUSED(event)
1258  Q_UNUSED(additive)
1259  Q_UNUSED(details)
1260  Q_UNUSED(selectionStateChanged)
1261 }
1262 
1275 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1276 {
1277  Q_UNUSED(selectionStateChanged)
1278 }
1279 
1280 
1284 
1299 const double QCPRange::minRange = 1e-280;
1300 
1309 const double QCPRange::maxRange = 1e250;
1310 
1315  lower(0),
1316  upper(0)
1317 {
1318 }
1319 
1323 QCPRange::QCPRange(double lower, double upper) :
1324  lower(lower),
1325  upper(upper)
1326 {
1327  normalize();
1328 }
1329 
1333 double QCPRange::size() const
1334 {
1335  return upper-lower;
1336 }
1337 
1341 double QCPRange::center() const
1342 {
1343  return (upper+lower)*0.5;
1344 }
1345 
1351 {
1352  if (lower > upper)
1353  qSwap(lower, upper);
1354 }
1355 
1364 void QCPRange::expand(const QCPRange &otherRange)
1365 {
1366  if (lower > otherRange.lower)
1367  lower = otherRange.lower;
1368  if (upper < otherRange.upper)
1369  upper = otherRange.upper;
1370 }
1371 
1372 
1379 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1380 {
1381  QCPRange result = *this;
1382  result.expand(otherRange);
1383  return result;
1384 }
1385 
1399 {
1400  double rangeFac = 1e-3;
1401  QCPRange sanitizedRange(lower, upper);
1402  sanitizedRange.normalize();
1403  // can't have range spanning negative and positive values in log plot, so change range to fix it
1404  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
1405  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
1406  {
1407  // case lower is 0
1408  if (rangeFac < sanitizedRange.upper*rangeFac)
1409  sanitizedRange.lower = rangeFac;
1410  else
1411  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1412  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1413  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
1414  {
1415  // case upper is 0
1416  if (-rangeFac > sanitizedRange.lower*rangeFac)
1417  sanitizedRange.upper = -rangeFac;
1418  else
1419  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1420  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
1421  {
1422  // find out whether negative or positive interval is wider to decide which sign domain will be chosen
1423  if (-sanitizedRange.lower > sanitizedRange.upper)
1424  {
1425  // negative is wider, do same as in case upper is 0
1426  if (-rangeFac > sanitizedRange.lower*rangeFac)
1427  sanitizedRange.upper = -rangeFac;
1428  else
1429  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1430  } else
1431  {
1432  // positive is wider, do same as in case lower is 0
1433  if (rangeFac < sanitizedRange.upper*rangeFac)
1434  sanitizedRange.lower = rangeFac;
1435  else
1436  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1437  }
1438  }
1439  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
1440  return sanitizedRange;
1441 }
1442 
1448 {
1449  QCPRange sanitizedRange(lower, upper);
1450  sanitizedRange.normalize();
1451  return sanitizedRange;
1452 }
1453 
1457 bool QCPRange::contains(double value) const
1458 {
1459  return value >= lower && value <= upper;
1460 }
1461 
1470 bool QCPRange::validRange(double lower, double upper)
1471 {
1472  /*
1473  return (lower > -maxRange &&
1474  upper < maxRange &&
1475  qAbs(lower-upper) > minRange &&
1476  (lower < -minRange || lower > minRange) &&
1477  (upper < -minRange || upper > minRange));
1478  */
1479  return (lower > -maxRange &&
1480  upper < maxRange &&
1481  qAbs(lower-upper) > minRange &&
1482  qAbs(lower-upper) < maxRange);
1483 }
1484 
1494 bool QCPRange::validRange(const QCPRange &range)
1495 {
1496  /*
1497  return (range.lower > -maxRange &&
1498  range.upper < maxRange &&
1499  qAbs(range.lower-range.upper) > minRange &&
1500  qAbs(range.lower-range.upper) < maxRange &&
1501  (range.lower < -minRange || range.lower > minRange) &&
1502  (range.upper < -minRange || range.upper > minRange));
1503  */
1504  return (range.lower > -maxRange &&
1505  range.upper < maxRange &&
1506  qAbs(range.lower-range.upper) > minRange &&
1507  qAbs(range.lower-range.upper) < maxRange);
1508 }
1509 
1510 
1514 
1551 /* start documentation of inline functions */
1552 
1559 /* end documentation of inline functions */
1560 
1565  QObject(parentPlot),
1566  mParentPlot(parentPlot)
1567 {
1568  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
1569  mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
1570  mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
1571  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
1572 }
1573 
1575 {
1576  clear();
1577 }
1578 
1584 {
1585  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1586  while (it.hasNext())
1587  {
1588  it.next();
1589  if (!it.value().isEmpty())
1590  return false;
1591  }
1592  return true;
1593 }
1594 
1600 {
1601  // make all children remove themselves from this margin group:
1602  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1603  while (it.hasNext())
1604  {
1605  it.next();
1606  const QList<QCPLayoutElement*> elements = it.value();
1607  for (int i=elements.size()-1; i>=0; --i)
1608  elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
1609  }
1610 }
1611 
1623 {
1624  // query all automatic margins of the layout elements in this margin group side and find maximum:
1625  int result = 0;
1626  const QList<QCPLayoutElement*> elements = mChildren.value(side);
1627  for (int i=0; i<elements.size(); ++i)
1628  {
1629  if (!elements.at(i)->autoMargins().testFlag(side))
1630  continue;
1631  int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1632  if (m > result)
1633  result = m;
1634  }
1635  return result;
1636 }
1637 
1645 {
1646  if (!mChildren[side].contains(element))
1647  mChildren[side].append(element);
1648  else
1649  qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
1650 }
1651 
1659 {
1660  if (!mChildren[side].removeOne(element))
1661  qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
1662 }
1663 
1664 
1668 
1695 /* start documentation of inline functions */
1696 
1744 /* end documentation of inline functions */
1745 
1750  QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
1751  mParentLayout(0),
1752  mMinimumSize(),
1753  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1754  mRect(0, 0, 0, 0),
1755  mOuterRect(0, 0, 0, 0),
1756  mMargins(0, 0, 0, 0),
1757  mMinimumMargins(0, 0, 0, 0),
1758  mAutoMargins(QCP::msAll)
1759 {
1760 }
1761 
1763 {
1764  setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
1765  // unregister at layout:
1766  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
1767  mParentLayout->take(this);
1768 }
1769 
1782 {
1783  if (mOuterRect != rect)
1784  {
1785  mOuterRect = rect;
1786  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1787  }
1788 }
1789 
1802 {
1803  if (mMargins != margins)
1804  {
1805  mMargins = margins;
1806  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1807  }
1808 }
1809 
1820 {
1821  if (mMinimumMargins != margins)
1822  {
1824  }
1825 }
1826 
1837 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
1838 {
1839  mAutoMargins = sides;
1840 }
1841 
1851 void QCPLayoutElement::setMinimumSize(const QSize &size)
1852 {
1853  if (mMinimumSize != size)
1854  {
1855  mMinimumSize = size;
1856  if (mParentLayout)
1858  }
1859 }
1860 
1865 void QCPLayoutElement::setMinimumSize(int width, int height)
1866 {
1867  setMinimumSize(QSize(width, height));
1868 }
1869 
1874 void QCPLayoutElement::setMaximumSize(const QSize &size)
1875 {
1876  if (mMaximumSize != size)
1877  {
1878  mMaximumSize = size;
1879  if (mParentLayout)
1881  }
1882 }
1883 
1888 void QCPLayoutElement::setMaximumSize(int width, int height)
1889 {
1890  setMaximumSize(QSize(width, height));
1891 }
1892 
1904 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
1905 {
1906  QVector<QCP::MarginSide> sideVector;
1907  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1908  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1909  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1910  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1911 
1912  for (int i=0; i<sideVector.size(); ++i)
1913  {
1914  QCP::MarginSide side = sideVector.at(i);
1915  if (marginGroup(side) != group)
1916  {
1917  QCPMarginGroup *oldGroup = marginGroup(side);
1918  if (oldGroup) // unregister at old group
1919  oldGroup->removeChild(side, this);
1920 
1921  if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
1922  {
1923  mMarginGroups.remove(side);
1924  } else // setting to a new group
1925  {
1926  mMarginGroups[side] = group;
1927  group->addChild(side, this);
1928  }
1929  }
1930  }
1931 }
1932 
1946 {
1947  if (phase == upMargins)
1948  {
1949  if (mAutoMargins != QCP::msNone)
1950  {
1951  // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
1952  QMargins newMargins = mMargins;
1953  foreach (QCP::MarginSide side, QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom)
1954  {
1955  if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
1956  {
1957  if (mMarginGroups.contains(side))
1958  QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
1959  else
1960  QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
1961  // apply minimum margin restrictions:
1962  if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
1963  QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
1964  }
1965  }
1966  setMargins(newMargins);
1967  }
1968  }
1969 }
1970 
1979 {
1980  return mMinimumSize;
1981 }
1982 
1991 {
1992  return mMaximumSize;
1993 }
1994 
2002 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
2003 {
2004  Q_UNUSED(recursive)
2005  return QList<QCPLayoutElement*>();
2006 }
2007 
2019 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
2020 {
2021  Q_UNUSED(details)
2022 
2023  if (onlySelectable)
2024  return -1;
2025 
2026  if (QRectF(mOuterRect).contains(pos))
2027  {
2028  if (mParentPlot)
2029  return mParentPlot->selectionTolerance()*0.99;
2030  else
2031  {
2032  qDebug() << Q_FUNC_INFO << "parent plot not defined";
2033  return -1;
2034  }
2035  } else
2036  return -1;
2037 }
2038 
2045 {
2046  foreach (QCPLayoutElement* el, elements(false))
2047  {
2048  if (!el->parentPlot())
2049  el->initializeParentPlot(parentPlot);
2050  }
2051 }
2052 
2063 {
2065 }
2066 
2070 
2094 /* start documentation of pure virtual functions */
2095 
2138 /* end documentation of pure virtual functions */
2139 
2145 {
2146 }
2147 
2157 {
2158  QCPLayoutElement::update(phase);
2159 
2160  // set child element rects according to layout:
2161  if (phase == upLayout)
2162  updateLayout();
2163 
2164  // propagate update call to child elements:
2165  const int elCount = elementCount();
2166  for (int i=0; i<elCount; ++i)
2167  {
2168  if (QCPLayoutElement *el = elementAt(i))
2169  el->update(phase);
2170  }
2171 }
2172 
2173 /* inherits documentation from base class */
2174 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2175 {
2176  const int c = elementCount();
2177  QList<QCPLayoutElement*> result;
2178 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2179  result.reserve(c);
2180 #endif
2181  for (int i=0; i<c; ++i)
2182  result.append(elementAt(i));
2183  if (recursive)
2184  {
2185  for (int i=0; i<c; ++i)
2186  {
2187  if (result.at(i))
2188  result << result.at(i)->elements(recursive);
2189  }
2190  }
2191  return result;
2192 }
2193 
2202 {
2203 }
2204 
2214 bool QCPLayout::removeAt(int index)
2215 {
2216  if (QCPLayoutElement *el = takeAt(index))
2217  {
2218  delete el;
2219  return true;
2220  } else
2221  return false;
2222 }
2223 
2234 {
2235  if (take(element))
2236  {
2237  delete element;
2238  return true;
2239  } else
2240  return false;
2241 }
2242 
2249 {
2250  for (int i=elementCount()-1; i>=0; --i)
2251  {
2252  if (elementAt(i))
2253  removeAt(i);
2254  }
2255  simplify();
2256 }
2257 
2267 {
2268  if (QWidget *w = qobject_cast<QWidget*>(parent()))
2269  w->updateGeometry();
2270  else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
2271  l->sizeConstraintsChanged();
2272 }
2273 
2287 {
2288 }
2289 
2290 
2304 {
2305  if (el)
2306  {
2307  el->mParentLayout = this;
2308  el->setParentLayerable(this);
2309  el->setParent(this);
2310  if (!el->parentPlot())
2312  } else
2313  qDebug() << Q_FUNC_INFO << "Null element passed";
2314 }
2315 
2327 {
2328  if (el)
2329  {
2330  el->mParentLayout = 0;
2331  el->setParentLayerable(0);
2332  el->setParent(mParentPlot);
2333  // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
2334  } else
2335  qDebug() << Q_FUNC_INFO << "Null element passed";
2336 }
2337 
2367 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
2368 {
2369  if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
2370  {
2371  qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
2372  return QVector<int>();
2373  }
2374  if (stretchFactors.isEmpty())
2375  return QVector<int>();
2376  int sectionCount = stretchFactors.size();
2377  QVector<double> sectionSizes(sectionCount);
2378  // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
2379  int minSizeSum = 0;
2380  for (int i=0; i<sectionCount; ++i)
2381  minSizeSum += minSizes.at(i);
2382  if (totalSize < minSizeSum)
2383  {
2384  // new stretch factors are minimum sizes and minimum sizes are set to zero:
2385  for (int i=0; i<sectionCount; ++i)
2386  {
2387  stretchFactors[i] = minSizes.at(i);
2388  minSizes[i] = 0;
2389  }
2390  }
2391 
2392  QList<int> minimumLockedSections;
2393  QList<int> unfinishedSections;
2394  for (int i=0; i<sectionCount; ++i)
2395  unfinishedSections.append(i);
2396  double freeSize = totalSize;
2397 
2398  int outerIterations = 0;
2399  while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2400  {
2401  ++outerIterations;
2402  int innerIterations = 0;
2403  while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2404  {
2405  ++innerIterations;
2406  // find section that hits its maximum next:
2407  int nextId = -1;
2408  double nextMax = 1e12;
2409  for (int i=0; i<unfinishedSections.size(); ++i)
2410  {
2411  int secId = unfinishedSections.at(i);
2412  double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
2413  if (hitsMaxAt < nextMax)
2414  {
2415  nextMax = hitsMaxAt;
2416  nextId = secId;
2417  }
2418  }
2419  // 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
2420  // actually hits its maximum, without exceeding the total size when we add up all sections)
2421  double stretchFactorSum = 0;
2422  for (int i=0; i<unfinishedSections.size(); ++i)
2423  stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2424  double nextMaxLimit = freeSize/stretchFactorSum;
2425  if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
2426  {
2427  for (int i=0; i<unfinishedSections.size(); ++i)
2428  {
2429  sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2430  freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
2431  }
2432  unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
2433  } else // next maximum isn't hit, just distribute rest of free space on remaining sections
2434  {
2435  for (int i=0; i<unfinishedSections.size(); ++i)
2436  sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2437  unfinishedSections.clear();
2438  }
2439  }
2440  if (innerIterations == sectionCount*2)
2441  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2442 
2443  // now check whether the resulting section sizes violate minimum restrictions:
2444  bool foundMinimumViolation = false;
2445  for (int i=0; i<sectionSizes.size(); ++i)
2446  {
2447  if (minimumLockedSections.contains(i))
2448  continue;
2449  if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
2450  {
2451  sectionSizes[i] = minSizes.at(i); // set it to minimum
2452  foundMinimumViolation = true; // make sure we repeat the whole optimization process
2453  minimumLockedSections.append(i);
2454  }
2455  }
2456  if (foundMinimumViolation)
2457  {
2458  freeSize = totalSize;
2459  for (int i=0; i<sectionCount; ++i)
2460  {
2461  if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
2462  unfinishedSections.append(i);
2463  else
2464  freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
2465  }
2466  // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
2467  for (int i=0; i<unfinishedSections.size(); ++i)
2468  sectionSizes[unfinishedSections.at(i)] = 0;
2469  }
2470  }
2471  if (outerIterations == sectionCount*2)
2472  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2473 
2474  QVector<int> result(sectionCount);
2475  for (int i=0; i<sectionCount; ++i)
2476  result[i] = qRound(sectionSizes.at(i));
2477  return result;
2478 }
2479 
2480 
2484 
2505  mColumnSpacing(5),
2506  mRowSpacing(5)
2507 {
2508 }
2509 
2511 {
2512  // clear all child layout elements. This is important because only the specific layouts know how
2513  // to handle removing elements (clear calls virtual removeAt method to do that).
2514  clear();
2515 }
2516 
2525 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
2526 {
2527  if (row >= 0 && row < mElements.size())
2528  {
2529  if (column >= 0 && column < mElements.first().size())
2530  {
2531  if (QCPLayoutElement *result = mElements.at(row).at(column))
2532  return result;
2533  else
2534  qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
2535  } else
2536  qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
2537  } else
2538  qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
2539  return 0;
2540 }
2541 
2548 {
2549  return mElements.size();
2550 }
2551 
2558 {
2559  if (mElements.size() > 0)
2560  return mElements.first().size();
2561  else
2562  return 0;
2563 }
2564 
2576 {
2577  if (element)
2578  {
2579  if (!hasElement(row, column))
2580  {
2581  if (element->layout()) // remove from old layout first
2582  element->layout()->take(element);
2583  expandTo(row+1, column+1);
2584  mElements[row][column] = element;
2585  adoptElement(element);
2586  return true;
2587  } else
2588  qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2589  } else
2590  qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2591  return false;
2592 }
2593 
2600 bool QCPLayoutGrid::hasElement(int row, int column)
2601 {
2602  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2603  return mElements.at(row).at(column);
2604  else
2605  return false;
2606 }
2607 
2619 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
2620 {
2621  if (column >= 0 && column < columnCount())
2622  {
2623  if (factor > 0)
2624  mColumnStretchFactors[column] = factor;
2625  else
2626  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2627  } else
2628  qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2629 }
2630 
2642 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
2643 {
2644  if (factors.size() == mColumnStretchFactors.size())
2645  {
2646  mColumnStretchFactors = factors;
2647  for (int i=0; i<mColumnStretchFactors.size(); ++i)
2648  {
2649  if (mColumnStretchFactors.at(i) <= 0)
2650  {
2651  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
2652  mColumnStretchFactors[i] = 1;
2653  }
2654  }
2655  } else
2656  qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
2657 }
2658 
2670 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
2671 {
2672  if (row >= 0 && row < rowCount())
2673  {
2674  if (factor > 0)
2675  mRowStretchFactors[row] = factor;
2676  else
2677  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2678  } else
2679  qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2680 }
2681 
2693 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
2694 {
2695  if (factors.size() == mRowStretchFactors.size())
2696  {
2697  mRowStretchFactors = factors;
2698  for (int i=0; i<mRowStretchFactors.size(); ++i)
2699  {
2700  if (mRowStretchFactors.at(i) <= 0)
2701  {
2702  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
2703  mRowStretchFactors[i] = 1;
2704  }
2705  }
2706  } else
2707  qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
2708 }
2709 
2716 {
2717  mColumnSpacing = pixels;
2718 }
2719 
2726 {
2727  mRowSpacing = pixels;
2728 }
2729 
2744 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
2745 {
2746  // add rows as necessary:
2747  while (rowCount() < newRowCount)
2748  {
2749  mElements.append(QList<QCPLayoutElement*>());
2750  mRowStretchFactors.append(1);
2751  }
2752  // go through rows and expand columns as necessary:
2753  int newColCount = qMax(columnCount(), newColumnCount);
2754  for (int i=0; i<rowCount(); ++i)
2755  {
2756  while (mElements.at(i).size() < newColCount)
2757  mElements[i].append(0);
2758  }
2759  while (mColumnStretchFactors.size() < newColCount)
2760  mColumnStretchFactors.append(1);
2761 }
2762 
2769 void QCPLayoutGrid::insertRow(int newIndex)
2770 {
2771  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2772  {
2773  expandTo(1, 1);
2774  return;
2775  }
2776 
2777  if (newIndex < 0)
2778  newIndex = 0;
2779  if (newIndex > rowCount())
2780  newIndex = rowCount();
2781 
2782  mRowStretchFactors.insert(newIndex, 1);
2783  QList<QCPLayoutElement*> newRow;
2784  for (int col=0; col<columnCount(); ++col)
2785  newRow.append((QCPLayoutElement*)0);
2786  mElements.insert(newIndex, newRow);
2787 }
2788 
2796 {
2797  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2798  {
2799  expandTo(1, 1);
2800  return;
2801  }
2802 
2803  if (newIndex < 0)
2804  newIndex = 0;
2805  if (newIndex > columnCount())
2806  newIndex = columnCount();
2807 
2808  mColumnStretchFactors.insert(newIndex, 1);
2809  for (int row=0; row<rowCount(); ++row)
2810  mElements[row].insert(newIndex, (QCPLayoutElement*)0);
2811 }
2812 
2813 /* inherits documentation from base class */
2815 {
2816  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2817  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2818  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2819 
2820  int totalRowSpacing = (rowCount()-1) * mRowSpacing;
2821  int totalColSpacing = (columnCount()-1) * mColumnSpacing;
2822  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
2823  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
2824 
2825  // go through cells and set rects accordingly:
2826  int yOffset = mRect.top();
2827  for (int row=0; row<rowCount(); ++row)
2828  {
2829  if (row > 0)
2830  yOffset += rowHeights.at(row-1)+mRowSpacing;
2831  int xOffset = mRect.left();
2832  for (int col=0; col<columnCount(); ++col)
2833  {
2834  if (col > 0)
2835  xOffset += colWidths.at(col-1)+mColumnSpacing;
2836  if (mElements.at(row).at(col))
2837  mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2838  }
2839  }
2840 }
2841 
2842 /* inherits documentation from base class */
2844 {
2845  return rowCount()*columnCount();
2846 }
2847 
2848 /* inherits documentation from base class */
2850 {
2851  if (index >= 0 && index < elementCount())
2852  return mElements.at(index / columnCount()).at(index % columnCount());
2853  else
2854  return 0;
2855 }
2856 
2857 /* inherits documentation from base class */
2859 {
2860  if (QCPLayoutElement *el = elementAt(index))
2861  {
2862  releaseElement(el);
2863  mElements[index / columnCount()][index % columnCount()] = 0;
2864  return el;
2865  } else
2866  {
2867  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2868  return 0;
2869  }
2870 }
2871 
2872 /* inherits documentation from base class */
2874 {
2875  if (element)
2876  {
2877  for (int i=0; i<elementCount(); ++i)
2878  {
2879  if (elementAt(i) == element)
2880  {
2881  takeAt(i);
2882  return true;
2883  }
2884  }
2885  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2886  } else
2887  qDebug() << Q_FUNC_INFO << "Can't take null element";
2888  return false;
2889 }
2890 
2891 /* inherits documentation from base class */
2892 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
2893 {
2894  QList<QCPLayoutElement*> result;
2895  int colC = columnCount();
2896  int rowC = rowCount();
2897 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2898  result.reserve(colC*rowC);
2899 #endif
2900  for (int row=0; row<rowC; ++row)
2901  {
2902  for (int col=0; col<colC; ++col)
2903  {
2904  result.append(mElements.at(row).at(col));
2905  }
2906  }
2907  if (recursive)
2908  {
2909  int c = result.size();
2910  for (int i=0; i<c; ++i)
2911  {
2912  if (result.at(i))
2913  result << result.at(i)->elements(recursive);
2914  }
2915  }
2916  return result;
2917 }
2918 
2923 {
2924  // remove rows with only empty cells:
2925  for (int row=rowCount()-1; row>=0; --row)
2926  {
2927  bool hasElements = false;
2928  for (int col=0; col<columnCount(); ++col)
2929  {
2930  if (mElements.at(row).at(col))
2931  {
2932  hasElements = true;
2933  break;
2934  }
2935  }
2936  if (!hasElements)
2937  {
2938  mRowStretchFactors.removeAt(row);
2939  mElements.removeAt(row);
2940  if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
2941  mColumnStretchFactors.clear();
2942  }
2943  }
2944 
2945  // remove columns with only empty cells:
2946  for (int col=columnCount()-1; col>=0; --col)
2947  {
2948  bool hasElements = false;
2949  for (int row=0; row<rowCount(); ++row)
2950  {
2951  if (mElements.at(row).at(col))
2952  {
2953  hasElements = true;
2954  break;
2955  }
2956  }
2957  if (!hasElements)
2958  {
2959  mColumnStretchFactors.removeAt(col);
2960  for (int row=0; row<rowCount(); ++row)
2961  mElements[row].removeAt(col);
2962  }
2963  }
2964 }
2965 
2966 /* inherits documentation from base class */
2968 {
2969  QVector<int> minColWidths, minRowHeights;
2970  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2971  QSize result(0, 0);
2972  for (int i=0; i<minColWidths.size(); ++i)
2973  result.rwidth() += minColWidths.at(i);
2974  for (int i=0; i<minRowHeights.size(); ++i)
2975  result.rheight() += minRowHeights.at(i);
2976  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2977  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2978  return result;
2979 }
2980 
2981 /* inherits documentation from base class */
2983 {
2984  QVector<int> maxColWidths, maxRowHeights;
2985  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2986 
2987  QSize result(0, 0);
2988  for (int i=0; i<maxColWidths.size(); ++i)
2989  result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
2990  for (int i=0; i<maxRowHeights.size(); ++i)
2991  result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
2992  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2993  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2994  return result;
2995 }
2996 
3009 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
3010 {
3011  *minColWidths = QVector<int>(columnCount(), 0);
3012  *minRowHeights = QVector<int>(rowCount(), 0);
3013  for (int row=0; row<rowCount(); ++row)
3014  {
3015  for (int col=0; col<columnCount(); ++col)
3016  {
3017  if (mElements.at(row).at(col))
3018  {
3019  QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
3020  QSize min = mElements.at(row).at(col)->minimumSize();
3021  QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
3022  if (minColWidths->at(col) < final.width())
3023  (*minColWidths)[col] = final.width();
3024  if (minRowHeights->at(row) < final.height())
3025  (*minRowHeights)[row] = final.height();
3026  }
3027  }
3028  }
3029 }
3030 
3043 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
3044 {
3045  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3046  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3047  for (int row=0; row<rowCount(); ++row)
3048  {
3049  for (int col=0; col<columnCount(); ++col)
3050  {
3051  if (mElements.at(row).at(col))
3052  {
3053  QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3054  QSize max = mElements.at(row).at(col)->maximumSize();
3055  QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
3056  if (maxColWidths->at(col) > final.width())
3057  (*maxColWidths)[col] = final.width();
3058  if (maxRowHeights->at(row) > final.height())
3059  (*maxRowHeights)[row] = final.height();
3060  }
3061  }
3062  }
3063 }
3064 
3065 
3069 
3087 /* start documentation of inline functions */
3088 
3095 /* end documentation of inline functions */
3096 
3101 {
3102 }
3103 
3105 {
3106  // clear all child layout elements. This is important because only the specific layouts know how
3107  // to handle removing elements (clear calls virtual removeAt method to do that).
3108  clear();
3109 }
3110 
3115 {
3116  if (elementAt(index))
3117  return mInsetPlacement.at(index);
3118  else
3119  {
3120  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3121  return ipFree;
3122  }
3123 }
3124 
3129 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
3130 {
3131  if (elementAt(index))
3132  return mInsetAlignment.at(index);
3133  else
3134  {
3135  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3136  return 0;
3137  }
3138 }
3139 
3144 QRectF QCPLayoutInset::insetRect(int index) const
3145 {
3146  if (elementAt(index))
3147  return mInsetRect.at(index);
3148  else
3149  {
3150  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3151  return QRectF();
3152  }
3153 }
3154 
3161 {
3162  if (elementAt(index))
3163  mInsetPlacement[index] = placement;
3164  else
3165  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3166 }
3167 
3176 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
3177 {
3178  if (elementAt(index))
3179  mInsetAlignment[index] = alignment;
3180  else
3181  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3182 }
3183 
3195 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
3196 {
3197  if (elementAt(index))
3198  mInsetRect[index] = rect;
3199  else
3200  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3201 }
3202 
3203 /* inherits documentation from base class */
3205 {
3206  for (int i=0; i<mElements.size(); ++i)
3207  {
3208  QRect insetRect;
3209  QSize finalMinSize, finalMaxSize;
3210  QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3211  QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3212  finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
3213  finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
3214  finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
3215  finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
3216  if (mInsetPlacement.at(i) == ipFree)
3217  {
3218  insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
3219  rect().y()+rect().height()*mInsetRect.at(i).y(),
3220  rect().width()*mInsetRect.at(i).width(),
3221  rect().height()*mInsetRect.at(i).height());
3222  if (insetRect.size().width() < finalMinSize.width())
3223  insetRect.setWidth(finalMinSize.width());
3224  if (insetRect.size().height() < finalMinSize.height())
3225  insetRect.setHeight(finalMinSize.height());
3226  if (insetRect.size().width() > finalMaxSize.width())
3227  insetRect.setWidth(finalMaxSize.width());
3228  if (insetRect.size().height() > finalMaxSize.height())
3229  insetRect.setHeight(finalMaxSize.height());
3230  } else if (mInsetPlacement.at(i) == ipBorderAligned)
3231  {
3232  insetRect.setSize(finalMinSize);
3233  Qt::Alignment al = mInsetAlignment.at(i);
3234  if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
3235  else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
3236  else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
3237  if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
3238  else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
3239  else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
3240  }
3241  mElements.at(i)->setOuterRect(insetRect);
3242  }
3243 }
3244 
3245 /* inherits documentation from base class */
3247 {
3248  return mElements.size();
3249 }
3250 
3251 /* inherits documentation from base class */
3253 {
3254  if (index >= 0 && index < mElements.size())
3255  return mElements.at(index);
3256  else
3257  return 0;
3258 }
3259 
3260 /* inherits documentation from base class */
3262 {
3263  if (QCPLayoutElement *el = elementAt(index))
3264  {
3265  releaseElement(el);
3266  mElements.removeAt(index);
3267  mInsetPlacement.removeAt(index);
3268  mInsetAlignment.removeAt(index);
3269  mInsetRect.removeAt(index);
3270  return el;
3271  } else
3272  {
3273  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3274  return 0;
3275  }
3276 }
3277 
3278 /* inherits documentation from base class */
3280 {
3281  if (element)
3282  {
3283  for (int i=0; i<elementCount(); ++i)
3284  {
3285  if (elementAt(i) == element)
3286  {
3287  takeAt(i);
3288  return true;
3289  }
3290  }
3291  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3292  } else
3293  qDebug() << Q_FUNC_INFO << "Can't take null element";
3294  return false;
3295 }
3296 
3306 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3307 {
3308  Q_UNUSED(details)
3309  if (onlySelectable)
3310  return -1;
3311 
3312  for (int i=0; i<mElements.size(); ++i)
3313  {
3314  // inset layout shall only return positive selectTest, if actually an inset object is at pos
3315  // else it would block the entire underlying QCPAxisRect with its surface.
3316  if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3317  return mParentPlot->selectionTolerance()*0.99;
3318  }
3319  return -1;
3320 }
3321 
3333 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3334 {
3335  if (element)
3336  {
3337  if (element->layout()) // remove from old layout first
3338  element->layout()->take(element);
3339  mElements.append(element);
3340  mInsetPlacement.append(ipBorderAligned);
3341  mInsetAlignment.append(alignment);
3342  mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3343  adoptElement(element);
3344  } else
3345  qDebug() << Q_FUNC_INFO << "Can't add null element";
3346 }
3347 
3360 {
3361  if (element)
3362  {
3363  if (element->layout()) // remove from old layout first
3364  element->layout()->take(element);
3365  mElements.append(element);
3366  mInsetPlacement.append(ipFree);
3367  mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
3368  mInsetRect.append(rect);
3369  adoptElement(element);
3370  } else
3371  qDebug() << Q_FUNC_INFO << "Can't add null element";
3372 }
3373 
3374 
3378 
3403  mStyle(esNone),
3404  mWidth(8),
3405  mLength(10),
3406  mInverted(false)
3407 {
3408 }
3409 
3414  mStyle(style),
3415  mWidth(width),
3416  mLength(length),
3417  mInverted(inverted)
3418 {
3419 }
3420 
3425 {
3426  mStyle = style;
3427 }
3428 
3436 {
3437  mWidth = width;
3438 }
3439 
3447 {
3448  mLength = length;
3449 }
3450 
3460 {
3461  mInverted = inverted;
3462 }
3463 
3474 {
3475  switch (mStyle)
3476  {
3477  case esNone:
3478  return 0;
3479 
3480  case esFlatArrow:
3481  case esSpikeArrow:
3482  case esLineArrow:
3483  case esSkewedBar:
3484  return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
3485 
3486  case esDisc:
3487  case esSquare:
3488  case esDiamond:
3489  case esBar:
3490  case esHalfBar:
3491  return mWidth*1.42; // items that only have a width -> width*sqrt(2)
3492 
3493  }
3494  return 0;
3495 }
3496 
3509 {
3510  switch (mStyle)
3511  {
3512  case esNone:
3513  case esLineArrow:
3514  case esSkewedBar:
3515  case esBar:
3516  case esHalfBar:
3517  return 0;
3518 
3519  case esFlatArrow:
3520  return mLength;
3521 
3522  case esDisc:
3523  case esSquare:
3524  case esDiamond:
3525  return mWidth*0.5;
3526 
3527  case esSpikeArrow:
3528  return mLength*0.8;
3529  }
3530  return 0;
3531 }
3532 
3538 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
3539 {
3540  if (mStyle == esNone)
3541  return;
3542 
3543  QVector2D lengthVec(dir.normalized());
3544  if (lengthVec.isNull())
3545  lengthVec = QVector2D(1, 0);
3546  QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3547  lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3548  widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3549 
3550  QPen penBackup = painter->pen();
3551  QBrush brushBackup = painter->brush();
3552  QPen miterPen = penBackup;
3553  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3554  QBrush brush(painter->pen().color(), Qt::SolidPattern);
3555  switch (mStyle)
3556  {
3557  case esNone: break;
3558  case esFlatArrow:
3559  {
3560  QPointF points[3] = {pos.toPointF(),
3561  (pos-lengthVec+widthVec).toPointF(),
3562  (pos-lengthVec-widthVec).toPointF()
3563  };
3564  painter->setPen(miterPen);
3565  painter->setBrush(brush);
3566  painter->drawConvexPolygon(points, 3);
3567  painter->setBrush(brushBackup);
3568  painter->setPen(penBackup);
3569  break;
3570  }
3571  case esSpikeArrow:
3572  {
3573  QPointF points[4] = {pos.toPointF(),
3574  (pos-lengthVec+widthVec).toPointF(),
3575  (pos-lengthVec*0.8f).toPointF(),
3576  (pos-lengthVec-widthVec).toPointF()
3577  };
3578  painter->setPen(miterPen);
3579  painter->setBrush(brush);
3580  painter->drawConvexPolygon(points, 4);
3581  painter->setBrush(brushBackup);
3582  painter->setPen(penBackup);
3583  break;
3584  }
3585  case esLineArrow:
3586  {
3587  QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
3588  pos.toPointF(),
3589  (pos-lengthVec-widthVec).toPointF()
3590  };
3591  painter->setPen(miterPen);
3592  painter->drawPolyline(points, 3);
3593  painter->setPen(penBackup);
3594  break;
3595  }
3596  case esDisc:
3597  {
3598  painter->setBrush(brush);
3599  painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
3600  painter->setBrush(brushBackup);
3601  break;
3602  }
3603  case esSquare:
3604  {
3605  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3606  QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
3607  (pos-widthVecPerp-widthVec).toPointF(),
3608  (pos+widthVecPerp-widthVec).toPointF(),
3609  (pos+widthVecPerp+widthVec).toPointF()
3610  };
3611  painter->setPen(miterPen);
3612  painter->setBrush(brush);
3613  painter->drawConvexPolygon(points, 4);
3614  painter->setBrush(brushBackup);
3615  painter->setPen(penBackup);
3616  break;
3617  }
3618  case esDiamond:
3619  {
3620  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3621  QPointF points[4] = {(pos-widthVecPerp).toPointF(),
3622  (pos-widthVec).toPointF(),
3623  (pos+widthVecPerp).toPointF(),
3624  (pos+widthVec).toPointF()
3625  };
3626  painter->setPen(miterPen);
3627  painter->setBrush(brush);
3628  painter->drawConvexPolygon(points, 4);
3629  painter->setBrush(brushBackup);
3630  painter->setPen(penBackup);
3631  break;
3632  }
3633  case esBar:
3634  {
3635  painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
3636  break;
3637  }
3638  case esHalfBar:
3639  {
3640  painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
3641  break;
3642  }
3643  case esSkewedBar:
3644  {
3645  if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3646  {
3647  // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3648  painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3649  (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3650  } else
3651  {
3652  // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3653  painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3654  (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
3655  }
3656  break;
3657  }
3658  }
3659 }
3660 
3666 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
3667 {
3668  draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3669 }
3670 
3671 
3675 
3695  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3696  mParentAxis(parentAxis)
3697 {
3698  // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
3699  setParent(parentAxis);
3700  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
3701  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
3702  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
3703  setSubGridVisible(false);
3704  setAntialiased(false);
3705  setAntialiasedSubGrid(false);
3706  setAntialiasedZeroLine(false);
3707 }
3708 
3715 {
3717 }
3718 
3723 {
3724  mAntialiasedSubGrid = enabled;
3725 }
3726 
3731 {
3732  mAntialiasedZeroLine = enabled;
3733 }
3734 
3738 void QCPGrid::setPen(const QPen &pen)
3739 {
3740  mPen = pen;
3741 }
3742 
3746 void QCPGrid::setSubGridPen(const QPen &pen)
3747 {
3748  mSubGridPen = pen;
3749 }
3750 
3757 void QCPGrid::setZeroLinePen(const QPen &pen)
3758 {
3759  mZeroLinePen = pen;
3760 }
3761 
3776 {
3778 }
3779 
3786 {
3787  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3788 
3789  if (mSubGridVisible)
3790  drawSubGridLines(painter);
3791  drawGridLines(painter);
3792 }
3793 
3801 {
3802  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3803 
3804  int lowTick = mParentAxis->mLowestVisibleTick;
3805  int highTick = mParentAxis->mHighestVisibleTick;
3806  double t; // helper variable, result of coordinate-to-pixel transforms
3807  if (mParentAxis->orientation() == Qt::Horizontal)
3808  {
3809  // draw zeroline:
3810  int zeroLineIndex = -1;
3811  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3812  {
3814  painter->setPen(mZeroLinePen);
3815  double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
3816  for (int i=lowTick; i <= highTick; ++i)
3817  {
3818  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3819  {
3820  zeroLineIndex = i;
3821  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3822  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3823  break;
3824  }
3825  }
3826  }
3827  // draw grid lines:
3829  painter->setPen(mPen);
3830  for (int i=lowTick; i <= highTick; ++i)
3831  {
3832  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3833  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3834  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3835  }
3836  } else
3837  {
3838  // draw zeroline:
3839  int zeroLineIndex = -1;
3840  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3841  {
3843  painter->setPen(mZeroLinePen);
3844  double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
3845  for (int i=lowTick; i <= highTick; ++i)
3846  {
3847  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3848  {
3849  zeroLineIndex = i;
3850  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3851  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3852  break;
3853  }
3854  }
3855  }
3856  // draw grid lines:
3858  painter->setPen(mPen);
3859  for (int i=lowTick; i <= highTick; ++i)
3860  {
3861  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3862  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3863  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3864  }
3865  }
3866 }
3867 
3875 {
3876  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3877 
3879  double t; // helper variable, result of coordinate-to-pixel transforms
3880  painter->setPen(mSubGridPen);
3881  if (mParentAxis->orientation() == Qt::Horizontal)
3882  {
3883  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3884  {
3886  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3887  }
3888  } else
3889  {
3890  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3891  {
3893  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3894  }
3895  }
3896 }
3897 
3898 
3902 
3921 /* start of documentation of inline functions */
3922 
3944 /* end of documentation of inline functions */
3945 /* start of documentation of signals */
3946 
3992 /* end of documentation of signals */
3993 
4002  QCPLayerable(parent->parentPlot(), QString(), parent),
4003  // axis base:
4004  mAxisType(type),
4005  mAxisRect(parent),
4006  mPadding(5),
4007  mOrientation(orientation(type)),
4008  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
4009  mSelectedParts(spNone),
4010  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4011  mSelectedBasePen(QPen(Qt::blue, 2)),
4012  // axis label:
4013  mLabel(),
4014  mLabelFont(mParentPlot->font()),
4015  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4016  mLabelColor(Qt::black),
4017  mSelectedLabelColor(Qt::blue),
4018  // tick labels:
4019  mTickLabels(true),
4020  mAutoTickLabels(true),
4021  mTickLabelType(ltNumber),
4022  mTickLabelFont(mParentPlot->font()),
4023  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4024  mTickLabelColor(Qt::black),
4025  mSelectedTickLabelColor(Qt::blue),
4026  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
4027  mDateTimeSpec(Qt::LocalTime),
4028  mNumberPrecision(6),
4029  mNumberFormatChar('g'),
4030  mNumberBeautifulPowers(true),
4031  // ticks and subticks:
4032  mTicks(true),
4033  mTickStep(1),
4034  mSubTickCount(4),
4035  mAutoTickCount(6),
4036  mAutoTicks(true),
4037  mAutoTickStep(true),
4038  mAutoSubTicks(true),
4039  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4040  mSelectedTickPen(QPen(Qt::blue, 2)),
4041  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4042  mSelectedSubTickPen(QPen(Qt::blue, 2)),
4043  // scale and range:
4044  mRange(0, 5),
4045  mRangeReversed(false),
4046  mScaleType(stLinear),
4047  mScaleLogBase(10),
4048  mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4049  // internal members:
4050  mGrid(new QCPGrid(this)),
4051  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4052  mLowestVisibleTick(0),
4053  mHighestVisibleTick(-1),
4054  mCachedMarginValid(false),
4055  mCachedMargin(0)
4056 {
4057  mGrid->setVisible(false);
4058  setAntialiased(false);
4059  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
4060 
4061  if (type == atTop)
4062  {
4064  setLabelPadding(6);
4065  } else if (type == atRight)
4066  {
4068  setLabelPadding(12);
4069  } else if (type == atBottom)
4070  {
4072  setLabelPadding(3);
4073  } else if (type == atLeft)
4074  {
4076  setLabelPadding(10);
4077  }
4078 }
4079 
4081 {
4082  delete mAxisPainter;
4083 }
4084 
4085 /* No documentation as it is a property getter */
4087 {
4089 }
4090 
4091 /* No documentation as it is a property getter */
4093 {
4095 }
4096 
4097 /* No documentation as it is a property getter */
4099 {
4100  return mAxisPainter->tickLabelSide;
4101 }
4102 
4103 /* No documentation as it is a property getter */
4104 QString QCPAxis::numberFormat() const
4105 {
4106  QString result;
4107  result.append(mNumberFormatChar);
4109  {
4110  result.append(QLatin1Char('b'));
4112  result.append(QLatin1Char('c'));
4113  }
4114  return result;
4115 }
4116 
4117 /* No documentation as it is a property getter */
4119 {
4120  return mAxisPainter->tickLengthIn;
4121 }
4122 
4123 /* No documentation as it is a property getter */
4125 {
4126  return mAxisPainter->tickLengthOut;
4127 }
4128 
4129 /* No documentation as it is a property getter */
4131 {
4132  return mAxisPainter->subTickLengthIn;
4133 }
4134 
4135 /* No documentation as it is a property getter */
4137 {
4139 }
4140 
4141 /* No documentation as it is a property getter */
4143 {
4144  return mAxisPainter->labelPadding;
4145 }
4146 
4147 /* No documentation as it is a property getter */
4148 int QCPAxis::offset() const
4149 {
4150  return mAxisPainter->offset;
4151 }
4152 
4153 /* No documentation as it is a property getter */
4155 {
4156  return mAxisPainter->lowerEnding;
4157 }
4158 
4159 /* No documentation as it is a property getter */
4161 {
4162  return mAxisPainter->upperEnding;
4163 }
4164 
4179 {
4180  if (mScaleType != type)
4181  {
4182  mScaleType = type;
4183  if (mScaleType == stLogarithmic)
4185  mCachedMarginValid = false;
4187  }
4188 }
4189 
4197 void QCPAxis::setScaleLogBase(double base)
4198 {
4199  if (base > 1)
4200  {
4201  mScaleLogBase = base;
4202  mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4203  mCachedMarginValid = false;
4204  } else
4205  qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
4206 }
4207 
4217 {
4218  if (range.lower == mRange.lower && range.upper == mRange.upper)
4219  return;
4220 
4221  if (!QCPRange::validRange(range)) return;
4222  QCPRange oldRange = mRange;
4223  if (mScaleType == stLogarithmic)
4224  {
4225  mRange = range.sanitizedForLogScale();
4226  } else
4227  {
4228  mRange = range.sanitizedForLinScale();
4229  }
4230  mCachedMarginValid = false;
4231  emit rangeChanged(mRange);
4232  emit rangeChanged(mRange, oldRange);
4233 }
4234 
4245 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4246 {
4247  if (mSelectableParts != selectable)
4248  {
4249  mSelectableParts = selectable;
4251  }
4252 }
4253 
4269 void QCPAxis::setSelectedParts(const SelectableParts &selected)
4270 {
4271  if (mSelectedParts != selected)
4272  {
4273  mSelectedParts = selected;
4275  }
4276 }
4277 
4287 void QCPAxis::setRange(double lower, double upper)
4288 {
4289  if (lower == mRange.lower && upper == mRange.upper)
4290  return;
4291 
4292  if (!QCPRange::validRange(lower, upper)) return;
4293  QCPRange oldRange = mRange;
4294  mRange.lower = lower;
4295  mRange.upper = upper;
4296  if (mScaleType == stLogarithmic)
4297  {
4299  } else
4300  {
4302  }
4303  mCachedMarginValid = false;
4304  emit rangeChanged(mRange);
4305  emit rangeChanged(mRange, oldRange);
4306 }
4307 
4319 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
4320 {
4321  if (alignment == Qt::AlignLeft)
4322  setRange(position, position+size);
4323  else if (alignment == Qt::AlignRight)
4324  setRange(position-size, position);
4325  else // alignment == Qt::AlignCenter
4326  setRange(position-size/2.0, position+size/2.0);
4327 }
4328 
4333 void QCPAxis::setRangeLower(double lower)
4334 {
4335  if (mRange.lower == lower)
4336  return;
4337 
4338  QCPRange oldRange = mRange;
4339  mRange.lower = lower;
4340  if (mScaleType == stLogarithmic)
4341  {
4343  } else
4344  {
4346  }
4347  mCachedMarginValid = false;
4348  emit rangeChanged(mRange);
4349  emit rangeChanged(mRange, oldRange);
4350 }
4351 
4356 void QCPAxis::setRangeUpper(double upper)
4357 {
4358  if (mRange.upper == upper)
4359  return;
4360 
4361  QCPRange oldRange = mRange;
4362  mRange.upper = upper;
4363  if (mScaleType == stLogarithmic)
4364  {
4366  } else
4367  {
4369  }
4370  mCachedMarginValid = false;
4371  emit rangeChanged(mRange);
4372  emit rangeChanged(mRange, oldRange);
4373 }
4374 
4384 void QCPAxis::setRangeReversed(bool reversed)
4385 {
4386  if (mRangeReversed != reversed)
4387  {
4388  mRangeReversed = reversed;
4389  mCachedMarginValid = false;
4390  }
4391 }
4392 
4409 {
4410  if (mAutoTicks != on)
4411  {
4412  mAutoTicks = on;
4413  mCachedMarginValid = false;
4414  }
4415 }
4416 
4428 void QCPAxis::setAutoTickCount(int approximateCount)
4429 {
4430  if (mAutoTickCount != approximateCount)
4431  {
4432  if (approximateCount > 0)
4433  {
4434  mAutoTickCount = approximateCount;
4435  mCachedMarginValid = false;
4436  } else
4437  qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
4438  }
4439 }
4440 
4457 {
4458  if (mAutoTickLabels != on)
4459  {
4460  mAutoTickLabels = on;
4461  mCachedMarginValid = false;
4462  }
4463 }
4464 
4478 {
4479  if (mAutoTickStep != on)
4480  {
4481  mAutoTickStep = on;
4482  mCachedMarginValid = false;
4483  }
4484 }
4485 
4496 {
4497  if (mAutoSubTicks != on)
4498  {
4499  mAutoSubTicks = on;
4500  mCachedMarginValid = false;
4501  }
4502 }
4503 
4510 void QCPAxis::setTicks(bool show)
4511 {
4512  if (mTicks != show)
4513  {
4514  mTicks = show;
4515  mCachedMarginValid = false;
4516  }
4517 }
4518 
4522 void QCPAxis::setTickLabels(bool show)
4523 {
4524  if (mTickLabels != show)
4525  {
4526  mTickLabels = show;
4527  mCachedMarginValid = false;
4528  }
4529 }
4530 
4536 {
4537  if (mAxisPainter->tickLabelPadding != padding)
4538  {
4540  mCachedMarginValid = false;
4541  }
4542 }
4543 
4566 {
4567  if (mTickLabelType != type)
4568  {
4569  mTickLabelType = type;
4570  mCachedMarginValid = false;
4571  }
4572 }
4573 
4579 void QCPAxis::setTickLabelFont(const QFont &font)
4580 {
4581  if (font != mTickLabelFont)
4582  {
4583  mTickLabelFont = font;
4584  mCachedMarginValid = false;
4585  }
4586 }
4587 
4593 void QCPAxis::setTickLabelColor(const QColor &color)
4594 {
4595  if (color != mTickLabelColor)
4596  {
4597  mTickLabelColor = color;
4598  mCachedMarginValid = false;
4599  }
4600 }
4601 
4611 void QCPAxis::setTickLabelRotation(double degrees)
4612 {
4613  if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4614  {
4615  mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4616  mCachedMarginValid = false;
4617  }
4618 }
4619 
4628 {
4629  mAxisPainter->tickLabelSide = side;
4630  mCachedMarginValid = false;
4631 }
4632 
4641 void QCPAxis::setDateTimeFormat(const QString &format)
4642 {
4643  if (mDateTimeFormat != format)
4644  {
4645  mDateTimeFormat = format;
4646  mCachedMarginValid = false;
4647  }
4648 }
4649 
4660 void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
4661 {
4662  mDateTimeSpec = timeSpec;
4663 }
4664 
4701 void QCPAxis::setNumberFormat(const QString &formatCode)
4702 {
4703  if (formatCode.isEmpty())
4704  {
4705  qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4706  return;
4707  }
4708  mCachedMarginValid = false;
4709 
4710  // interpret first char as number format char:
4711  QString allowedFormatChars(QLatin1String("eEfgG"));
4712  if (allowedFormatChars.contains(formatCode.at(0)))
4713  {
4714  mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
4715  } else
4716  {
4717  qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4718  return;
4719  }
4720  if (formatCode.length() < 2)
4721  {
4722  mNumberBeautifulPowers = false;
4724  return;
4725  }
4726 
4727  // interpret second char as indicator for beautiful decimal powers:
4728  if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
4729  {
4730  mNumberBeautifulPowers = true;
4731  } else
4732  {
4733  qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4734  return;
4735  }
4736  if (formatCode.length() < 3)
4737  {
4739  return;
4740  }
4741 
4742  // interpret third char as indicator for dot or cross multiplication symbol:
4743  if (formatCode.at(2) == QLatin1Char('c'))
4744  {
4746  } else if (formatCode.at(2) == QLatin1Char('d'))
4747  {
4749  } else
4750  {
4751  qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4752  return;
4753  }
4754 }
4755 
4767 void QCPAxis::setNumberPrecision(int precision)
4768 {
4769  if (mNumberPrecision != precision)
4770  {
4771  mNumberPrecision = precision;
4772  mCachedMarginValid = false;
4773  }
4774 }
4775 
4781 void QCPAxis::setTickStep(double step)
4782 {
4783  if (mTickStep != step)
4784  {
4785  mTickStep = step;
4786  mCachedMarginValid = false;
4787  }
4788 }
4789 
4803 void QCPAxis::setTickVector(const QVector<double> &vec)
4804 {
4805  // don't check whether mTickVector != vec here, because it takes longer than we would save
4806  mTickVector = vec;
4807  mCachedMarginValid = false;
4808 }
4809 
4821 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4822 {
4823  // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
4824  mTickVectorLabels = vec;
4825  mCachedMarginValid = false;
4826 }
4827 
4836 void QCPAxis::setTickLength(int inside, int outside)
4837 {
4838  setTickLengthIn(inside);
4839  setTickLengthOut(outside);
4840 }
4841 
4849 {
4850  if (mAxisPainter->tickLengthIn != inside)
4851  {
4852  mAxisPainter->tickLengthIn = inside;
4853  }
4854 }
4855 
4863 void QCPAxis::setTickLengthOut(int outside)
4864 {
4865  if (mAxisPainter->tickLengthOut != outside)
4866  {
4867  mAxisPainter->tickLengthOut = outside;
4868  mCachedMarginValid = false; // only outside tick length can change margin
4869  }
4870 }
4871 
4884 {
4885  mSubTickCount = count;
4886 }
4887 
4896 void QCPAxis::setSubTickLength(int inside, int outside)
4897 {
4898  setSubTickLengthIn(inside);
4899  setSubTickLengthOut(outside);
4900 }
4901 
4909 {
4910  if (mAxisPainter->subTickLengthIn != inside)
4911  {
4912  mAxisPainter->subTickLengthIn = inside;
4913  }
4914 }
4915 
4924 {
4925  if (mAxisPainter->subTickLengthOut != outside)
4926  {
4927  mAxisPainter->subTickLengthOut = outside;
4928  mCachedMarginValid = false; // only outside tick length can change margin
4929  }
4930 }
4931 
4937 void QCPAxis::setBasePen(const QPen &pen)
4938 {
4939  mBasePen = pen;
4940 }
4941 
4947 void QCPAxis::setTickPen(const QPen &pen)
4948 {
4949  mTickPen = pen;
4950 }
4951 
4957 void QCPAxis::setSubTickPen(const QPen &pen)
4958 {
4959  mSubTickPen = pen;
4960 }
4961 
4967 void QCPAxis::setLabelFont(const QFont &font)
4968 {
4969  if (mLabelFont != font)
4970  {
4971  mLabelFont = font;
4972  mCachedMarginValid = false;
4973  }
4974 }
4975 
4981 void QCPAxis::setLabelColor(const QColor &color)
4982 {
4983  mLabelColor = color;
4984 }
4985 
4990 void QCPAxis::setLabel(const QString &str)
4991 {
4992  if (mLabel != str)
4993  {
4994  mLabel = str;
4995  mCachedMarginValid = false;
4996  }
4997 }
4998 
5005 {
5006  if (mAxisPainter->labelPadding != padding)
5007  {
5009  mCachedMarginValid = false;
5010  }
5011 }
5012 
5024 {
5025  if (mPadding != padding)
5026  {
5027  mPadding = padding;
5028  mCachedMarginValid = false;
5029  }
5030 }
5031 
5041 {
5043 }
5044 
5050 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
5051 {
5052  if (font != mSelectedTickLabelFont)
5053  {
5054  mSelectedTickLabelFont = font;
5055  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5056  }
5057 }
5058 
5064 void QCPAxis::setSelectedLabelFont(const QFont &font)
5065 {
5066  mSelectedLabelFont = font;
5067  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5068 }
5069 
5075 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
5076 {
5077  if (color != mSelectedTickLabelColor)
5078  {
5079  mSelectedTickLabelColor = color;
5080  }
5081 }
5082 
5088 void QCPAxis::setSelectedLabelColor(const QColor &color)
5089 {
5090  mSelectedLabelColor = color;
5091 }
5092 
5098 void QCPAxis::setSelectedBasePen(const QPen &pen)
5099 {
5100  mSelectedBasePen = pen;
5101 }
5102 
5108 void QCPAxis::setSelectedTickPen(const QPen &pen)
5109 {
5110  mSelectedTickPen = pen;
5111 }
5112 
5118 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
5119 {
5120  mSelectedSubTickPen = pen;
5121 }
5122 
5134 {
5135  mAxisPainter->lowerEnding = ending;
5136 }
5137 
5149 {
5150  mAxisPainter->upperEnding = ending;
5151 }
5152 
5161 {
5162  QCPRange oldRange = mRange;
5163  if (mScaleType == stLinear)
5164  {
5165  mRange.lower += diff;
5166  mRange.upper += diff;
5167  } else // mScaleType == stLogarithmic
5168  {
5169  mRange.lower *= diff;
5170  mRange.upper *= diff;
5171  }
5172  mCachedMarginValid = false;
5173  emit rangeChanged(mRange);
5174  emit rangeChanged(mRange, oldRange);
5175 }
5176 
5183 void QCPAxis::scaleRange(double factor, double center)
5184 {
5185  QCPRange oldRange = mRange;
5186  if (mScaleType == stLinear)
5187  {
5188  QCPRange newRange;
5189  newRange.lower = (mRange.lower-center)*factor + center;
5190  newRange.upper = (mRange.upper-center)*factor + center;
5191  if (QCPRange::validRange(newRange))
5192  mRange = newRange.sanitizedForLinScale();
5193  } else // mScaleType == stLogarithmic
5194  {
5195  if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
5196  {
5197  QCPRange newRange;
5198  newRange.lower = qPow(mRange.lower/center, factor)*center;
5199  newRange.upper = qPow(mRange.upper/center, factor)*center;
5200  if (QCPRange::validRange(newRange))
5201  mRange = newRange.sanitizedForLogScale();
5202  } else
5203  qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
5204  }
5205  mCachedMarginValid = false;
5206  emit rangeChanged(mRange);
5207  emit rangeChanged(mRange, oldRange);
5208 }
5209 
5223 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
5224 {
5225  int otherPixelSize, ownPixelSize;
5226 
5227  if (otherAxis->orientation() == Qt::Horizontal)
5228  otherPixelSize = otherAxis->axisRect()->width();
5229  else
5230  otherPixelSize = otherAxis->axisRect()->height();
5231 
5232  if (orientation() == Qt::Horizontal)
5233  ownPixelSize = axisRect()->width();
5234  else
5235  ownPixelSize = axisRect()->height();
5236 
5237  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
5238  setRange(range().center(), newRangeSize, Qt::AlignCenter);
5239 }
5240 
5247 void QCPAxis::rescale(bool onlyVisiblePlottables)
5248 {
5249  QList<QCPAbstractPlottable*> p = plottables();
5250  QCPRange newRange;
5251  bool haveRange = false;
5252  for (int i=0; i<p.size(); ++i)
5253  {
5254  if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5255  continue;
5256  QCPRange plottableRange;
5257  bool currentFoundRange;
5259  if (mScaleType == stLogarithmic)
5261  if (p.at(i)->keyAxis() == this)
5262  plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5263  else
5264  plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5265  if (currentFoundRange)
5266  {
5267  if (!haveRange)
5268  newRange = plottableRange;
5269  else
5270  newRange.expand(plottableRange);
5271  haveRange = true;
5272  }
5273  }
5274  if (haveRange)
5275  {
5276  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
5277  {
5278  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
5279  if (mScaleType == stLinear)
5280  {
5281  newRange.lower = center-mRange.size()/2.0;
5282  newRange.upper = center+mRange.size()/2.0;
5283  } else // mScaleType == stLogarithmic
5284  {
5285  newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
5286  newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
5287  }
5288  }
5289  setRange(newRange);
5290  }
5291 }
5292 
5296 double QCPAxis::pixelToCoord(double value) const
5297 {
5298  if (orientation() == Qt::Horizontal)
5299  {
5300  if (mScaleType == stLinear)
5301  {
5302  if (!mRangeReversed)
5303  return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
5304  else
5305  return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
5306  } else // mScaleType == stLogarithmic
5307  {
5308  if (!mRangeReversed)
5309  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
5310  else
5311  return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
5312  }
5313  } else // orientation() == Qt::Vertical
5314  {
5315  if (mScaleType == stLinear)
5316  {
5317  if (!mRangeReversed)
5318  return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
5319  else
5320  return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
5321  } else // mScaleType == stLogarithmic
5322  {
5323  if (!mRangeReversed)
5324  return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
5325  else
5326  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
5327  }
5328  }
5329 }
5330 
5334 double QCPAxis::coordToPixel(double value) const
5335 {
5336  if (orientation() == Qt::Horizontal)
5337  {
5338  if (mScaleType == stLinear)
5339  {
5340  if (!mRangeReversed)
5341  return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5342  else
5343  return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5344  } else // mScaleType == stLogarithmic
5345  {
5346  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5347  return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
5348  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5349  return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
5350  else
5351  {
5352  if (!mRangeReversed)
5354  else
5356  }
5357  }
5358  } else // orientation() == Qt::Vertical
5359  {
5360  if (mScaleType == stLinear)
5361  {
5362  if (!mRangeReversed)
5363  return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
5364  else
5365  return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5366  } else // mScaleType == stLogarithmic
5367  {
5368  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5369  return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5370  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5371  return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
5372  else
5373  {
5374  if (!mRangeReversed)
5376  else
5378  }
5379  }
5380  }
5381 }
5382 
5393 {
5394  if (!mVisible)
5395  return spNone;
5396 
5397  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5398  return spAxis;
5399  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5400  return spTickLabels;
5401  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5402  return spAxisLabel;
5403  else
5404  return spNone;
5405 }
5406 
5407 /* inherits documentation from base class */
5408 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5409 {
5410  if (!mParentPlot) return -1;
5411  SelectablePart part = getPartAt(pos);
5412  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5413  return -1;
5414 
5415  if (details)
5416  details->setValue(part);
5417  return mParentPlot->selectionTolerance()*0.99;
5418 }
5419 
5427 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
5428 {
5429  QList<QCPAbstractPlottable*> result;
5430  if (!mParentPlot) return result;
5431 
5432  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
5433  {
5434  if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
5435  result.append(mParentPlot->mPlottables.at(i));
5436  }
5437  return result;
5438 }
5439 
5445 QList<QCPGraph*> QCPAxis::graphs() const
5446 {
5447  QList<QCPGraph*> result;
5448  if (!mParentPlot) return result;
5449 
5450  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
5451  {
5452  if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
5453  result.append(mParentPlot->mGraphs.at(i));
5454  }
5455  return result;
5456 }
5457 
5464 QList<QCPAbstractItem*> QCPAxis::items() const
5465 {
5466  QList<QCPAbstractItem*> result;
5467  if (!mParentPlot) return result;
5468 
5469  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
5470  {
5471  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
5472  for (int posId=0; posId<positions.size(); ++posId)
5473  {
5474  if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
5475  {
5476  result.append(mParentPlot->mItems.at(itemId));
5477  break;
5478  }
5479  }
5480  }
5481  return result;
5482 }
5483 
5489 {
5490  switch (side)
5491  {
5492  case QCP::msLeft: return atLeft;
5493  case QCP::msRight: return atRight;
5494  case QCP::msTop: return atTop;
5495  case QCP::msBottom: return atBottom;
5496  default: break;
5497  }
5498  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5499  return atLeft;
5500 }
5501 
5506 {
5507  switch (type)
5508  {
5509  case atLeft: return atRight; break;
5510  case atRight: return atLeft; break;
5511  case atBottom: return atTop; break;
5512  case atTop: return atBottom; break;
5513  default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
5514  }
5515 }
5516 
5525 {
5526  if (!mParentPlot) return;
5527  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
5528 
5529  // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
5530  if (mAutoTicks)
5531  {
5533  } else
5534  {
5535  emit ticksRequest();
5536  }
5537 
5539  if (mTickVector.isEmpty())
5540  {
5541  mSubTickVector.clear();
5542  return;
5543  }
5544 
5545  // generate subticks between ticks:
5546  mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
5547  if (mSubTickCount > 0)
5548  {
5549  double subTickStep = 0;
5550  double subTickPosition = 0;
5551  int subTickIndex = 0;
5552  bool done = false;
5555  for (int i=lowTick+1; i<=highTick; ++i)
5556  {
5557  subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
5558  for (int k=1; k<=mSubTickCount; ++k)
5559  {
5560  subTickPosition = mTickVector.at(i-1) + k*subTickStep;
5561  if (subTickPosition < mRange.lower)
5562  continue;
5563  if (subTickPosition > mRange.upper)
5564  {
5565  done = true;
5566  break;
5567  }
5568  mSubTickVector[subTickIndex] = subTickPosition;
5569  subTickIndex++;
5570  }
5571  if (done) break;
5572  }
5573  mSubTickVector.resize(subTickIndex);
5574  }
5575 
5576  // generate tick labels according to tick positions:
5577  if (mAutoTickLabels)
5578  {
5579  int vecsize = mTickVector.size();
5580  mTickVectorLabels.resize(vecsize);
5581  if (mTickLabelType == ltNumber)
5582  {
5583  for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5584  mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision);
5585  } else if (mTickLabelType == ltDateTime)
5586  {
5587  for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5588  {
5589 #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")
5590  mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5591 #else
5592  mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5593 #endif
5594  }
5595  }
5596  } else // mAutoTickLabels == false
5597  {
5598  if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
5599  {
5600  emit ticksRequest();
5601  }
5602  // make sure provided tick label vector has correct (minimal) length:
5603  if (mTickVectorLabels.size() < mTickVector.size())
5604  mTickVectorLabels.resize(mTickVector.size());
5605  }
5606 }
5607 
5618 {
5619  if (mScaleType == stLinear)
5620  {
5621  if (mAutoTickStep)
5622  {
5623  // Generate tick positions according to linear scaling:
5624  mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
5625  double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5626  double tickStepMantissa = mTickStep/magnitudeFactor;
5627  if (tickStepMantissa < 5)
5628  {
5629  // round digit after decimal point to 0.5
5630  mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
5631  } else
5632  {
5633  // round to first digit in multiples of 2
5634  mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
5635  }
5636  }
5637  if (mAutoSubTicks)
5639  // Generate tick positions according to mTickStep:
5640  qint64 firstStep = floor(mRange.lower/mTickStep); // do not use qFloor here, or we'll lose 64 bit precision
5641  qint64 lastStep = ceil(mRange.upper/mTickStep); // do not use qCeil here, or we'll lose 64 bit precision
5642  int tickcount = lastStep-firstStep+1;
5643  if (tickcount < 0) tickcount = 0;
5644  mTickVector.resize(tickcount);
5645  for (int i=0; i<tickcount; ++i)
5646  mTickVector[i] = (firstStep+i)*mTickStep;
5647  } else // mScaleType == stLogarithmic
5648  {
5649  // Generate tick positions according to logbase scaling:
5650  if (mRange.lower > 0 && mRange.upper > 0) // positive range
5651  {
5652  double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
5653  double currentMag = lowerMag;
5654  mTickVector.clear();
5655  mTickVector.append(currentMag);
5656  while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5657  {
5658  currentMag *= mScaleLogBase;
5659  mTickVector.append(currentMag);
5660  }
5661  } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5662  {
5663  double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
5664  double currentMag = lowerMag;
5665  mTickVector.clear();
5666  mTickVector.append(currentMag);
5667  while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5668  {
5669  currentMag /= mScaleLogBase;
5670  mTickVector.append(currentMag);
5671  }
5672  } else // invalid range for logarithmic scale, because lower and upper have different sign
5673  {
5674  mTickVector.clear();
5675  qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
5676  }
5677  }
5678 }
5679 
5694 {
5695  int result = mSubTickCount; // default to current setting, if no proper value can be found
5696 
5697  // get mantissa of tickstep:
5698  double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5699  double tickStepMantissa = tickStep/magnitudeFactor;
5700 
5701  // separate integer and fractional part of mantissa:
5702  double epsilon = 0.01;
5703  double intPartf;
5704  int intPart;
5705  double fracPart = modf(tickStepMantissa, &intPartf);
5706  intPart = intPartf;
5707 
5708  // handle cases with (almost) integer mantissa:
5709  if (fracPart < epsilon || 1.0-fracPart < epsilon)
5710  {
5711  if (1.0-fracPart < epsilon)
5712  ++intPart;
5713  switch (intPart)
5714  {
5715  case 1: result = 4; break; // 1.0 -> 0.2 substep
5716  case 2: result = 3; break; // 2.0 -> 0.5 substep
5717  case 3: result = 2; break; // 3.0 -> 1.0 substep
5718  case 4: result = 3; break; // 4.0 -> 1.0 substep
5719  case 5: result = 4; break; // 5.0 -> 1.0 substep
5720  case 6: result = 2; break; // 6.0 -> 2.0 substep
5721  case 7: result = 6; break; // 7.0 -> 1.0 substep
5722  case 8: result = 3; break; // 8.0 -> 2.0 substep
5723  case 9: result = 2; break; // 9.0 -> 3.0 substep
5724  }
5725  } else
5726  {
5727  // handle cases with significantly fractional mantissa:
5728  if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5729  {
5730  switch (intPart)
5731  {
5732  case 1: result = 2; break; // 1.5 -> 0.5 substep
5733  case 2: result = 4; break; // 2.5 -> 0.5 substep
5734  case 3: result = 4; break; // 3.5 -> 0.7 substep
5735  case 4: result = 2; break; // 4.5 -> 1.5 substep
5736  case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
5737  case 6: result = 4; break; // 6.5 -> 1.3 substep
5738  case 7: result = 2; break; // 7.5 -> 2.5 substep
5739  case 8: result = 4; break; // 8.5 -> 1.7 substep
5740  case 9: result = 4; break; // 9.5 -> 1.9 substep
5741  }
5742  }
5743  // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5744  }
5745 
5746  return result;
5747 }
5748 
5749 /* inherits documentation from base class */
5750 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
5751 {
5752  Q_UNUSED(event)
5753  SelectablePart part = details.value<SelectablePart>();
5754  if (mSelectableParts.testFlag(part))
5755  {
5756  SelectableParts selBefore = mSelectedParts;
5757  setSelectedParts(additive ? mSelectedParts^part : part);
5758  if (selectionStateChanged)
5759  *selectionStateChanged = mSelectedParts != selBefore;
5760  }
5761 }
5762 
5763 /* inherits documentation from base class */
5764 void QCPAxis::deselectEvent(bool *selectionStateChanged)
5765 {
5766  SelectableParts selBefore = mSelectedParts;
5768  if (selectionStateChanged)
5769  *selectionStateChanged = mSelectedParts != selBefore;
5770 }
5771 
5786 {
5788 }
5789 
5796 {
5797  const int lowTick = mLowestVisibleTick;
5798  const int highTick = mHighestVisibleTick;
5799  QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5800  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5801  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5802  tickPositions.reserve(highTick-lowTick+1);
5803  tickLabels.reserve(highTick-lowTick+1);
5804  subTickPositions.reserve(mSubTickVector.size());
5805 
5806  if (mTicks)
5807  {
5808  for (int i=lowTick; i<=highTick; ++i)
5809  {
5810  tickPositions.append(coordToPixel(mTickVector.at(i)));
5811  if (mTickLabels)
5812  tickLabels.append(mTickVectorLabels.at(i));
5813  }
5814 
5815  if (mSubTickCount > 0)
5816  {
5817  const int subTickCount = mSubTickVector.size();
5818  for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
5819  subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5820  }
5821  }
5822  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
5823  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
5838  mAxisPainter->tickPositions = tickPositions;
5840  mAxisPainter->subTickPositions = subTickPositions;
5841  mAxisPainter->draw(painter);
5842 }
5843 
5860 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5861 {
5862  bool lowFound = false;
5863  bool highFound = false;
5864  lowIndex = 0;
5865  highIndex = -1;
5866 
5867  for (int i=0; i < mTickVector.size(); ++i)
5868  {
5869  if (mTickVector.at(i) >= mRange.lower)
5870  {
5871  lowFound = true;
5872  lowIndex = i;
5873  break;
5874  }
5875  }
5876  for (int i=mTickVector.size()-1; i >= 0; --i)
5877  {
5878  if (mTickVector.at(i) <= mRange.upper)
5879  {
5880  highFound = true;
5881  highIndex = i;
5882  break;
5883  }
5884  }
5885 
5886  if (!lowFound && highFound)
5887  lowIndex = highIndex+1;
5888  else if (lowFound && !highFound)
5889  highIndex = lowIndex-1;
5890 }
5891 
5900 double QCPAxis::baseLog(double value) const
5901 {
5902  return qLn(value)*mScaleLogBaseLogInv;
5903 }
5904 
5912 double QCPAxis::basePow(double value) const
5913 {
5914  return qPow(mScaleLogBase, value);
5915 }
5916 
5923 {
5924  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5925 }
5926 
5933 {
5934  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5935 }
5936 
5943 {
5945 }
5946 
5953 {
5955 }
5956 
5963 {
5965 }
5966 
5973 {
5975 }
5976 
5983 {
5985 }
5986 
6002 {
6003  if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
6004  return 0;
6005 
6006  if (mCachedMarginValid)
6007  return mCachedMargin;
6008 
6009  // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6010  int margin = 0;
6011 
6012  int lowTick, highTick;
6013  visibleTickBounds(lowTick, highTick);
6014  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
6015  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6016  tickPositions.reserve(highTick-lowTick+1);
6017  tickLabels.reserve(highTick-lowTick+1);
6018  if (mTicks)
6019  {
6020  for (int i=lowTick; i<=highTick; ++i)
6021  {
6022  tickPositions.append(coordToPixel(mTickVector.at(i)));
6023  if (mTickLabels)
6024  tickLabels.append(mTickVectorLabels.at(i));
6025  }
6026  }
6027  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6028  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6035  mAxisPainter->tickPositions = tickPositions;
6037  margin += mAxisPainter->size();
6038  margin += mPadding;
6039 
6040  mCachedMargin = margin;
6041  mCachedMarginValid = true;
6042  return margin;
6043 }
6044 
6045 /* inherits documentation from base class */
6047 {
6048  return QCP::iSelectAxes;
6049 }
6050 
6051 
6055 
6073  type(QCPAxis::atLeft),
6074  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6075  lowerEnding(QCPLineEnding::esNone),
6076  upperEnding(QCPLineEnding::esNone),
6077  labelPadding(0),
6078  tickLabelPadding(0),
6079  tickLabelRotation(0),
6081  substituteExponent(true),
6082  numberMultiplyCross(false),
6083  tickLengthIn(5),
6084  tickLengthOut(0),
6085  subTickLengthIn(2),
6086  subTickLengthOut(0),
6087  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6088  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6089  offset(0),
6090  abbreviateDecimalPowers(false),
6091  reversedEndings(false),
6092  mParentPlot(parentPlot),
6093  mLabelCache(16) // cache at most 16 (tick) labels
6094 {
6095 }
6096 
6098 {
6099 }
6100 
6109 {
6110  QByteArray newHash = generateLabelParameterHash();
6111  if (newHash != mLabelParameterHash)
6112  {
6113  mLabelCache.clear();
6114  mLabelParameterHash = newHash;
6115  }
6116 
6117  QPoint origin;
6118  switch (type)
6119  {
6120  case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
6121  case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
6122  case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break;
6123  case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
6124  }
6125 
6126  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
6127  switch (type)
6128  {
6129  case QCPAxis::atTop: yCor = -1; break;
6130  case QCPAxis::atRight: xCor = 1; break;
6131  default: break;
6132  }
6133 
6134  int margin = 0;
6135  // draw baseline:
6136  QLineF baseLine;
6137  painter->setPen(basePen);
6138  if (QCPAxis::orientation(type) == Qt::Horizontal)
6139  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
6140  else
6141  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
6142  if (reversedEndings)
6143  baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
6144  painter->drawLine(baseLine);
6145 
6146  // draw ticks:
6147  if (!tickPositions.isEmpty())
6148  {
6149  painter->setPen(tickPen);
6150  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
6151  if (QCPAxis::orientation(type) == Qt::Horizontal)
6152  {
6153  for (int i=0; i<tickPositions.size(); ++i)
6154  painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
6155  } else
6156  {
6157  for (int i=0; i<tickPositions.size(); ++i)
6158  painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
6159  }
6160  }
6161 
6162  // draw subticks:
6163  if (!subTickPositions.isEmpty())
6164  {
6165  painter->setPen(subTickPen);
6166  // direction of ticks ("inward" is right for left axis and left for right axis)
6167  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6168  if (QCPAxis::orientation(type) == Qt::Horizontal)
6169  {
6170  for (int i=0; i<subTickPositions.size(); ++i)
6171  painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
6172  } else
6173  {
6174  for (int i=0; i<subTickPositions.size(); ++i)
6175  painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
6176  }
6177  }
6178  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6179 
6180  // draw axis base endings:
6181  bool antialiasingBackup = painter->antialiasing();
6182  painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
6183  painter->setBrush(QBrush(basePen.color()));
6184  QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6186  lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6188  upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
6189  painter->setAntialiasing(antialiasingBackup);
6190 
6191  // tick labels:
6192  QRect oldClipRect;
6193  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
6194  {
6195  oldClipRect = painter->clipRegion().boundingRect();
6196  painter->setClipRect(axisRect);
6197  }
6198  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
6199  if (!tickLabels.isEmpty())
6200  {
6202  margin += tickLabelPadding;
6203  painter->setFont(tickLabelFont);
6204  painter->setPen(QPen(tickLabelColor));
6205  const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6206  int distanceToAxis = margin;
6208  distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6209  for (int i=0; i<maxLabelIndex; ++i)
6210  placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
6212  margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
6213  }
6215  painter->setClipRect(oldClipRect);
6216 
6217  // axis label:
6218  QRect labelBounds;
6219  if (!label.isEmpty())
6220  {
6221  margin += labelPadding;
6222  painter->setFont(labelFont);
6223  painter->setPen(QPen(labelColor));
6224  labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
6225  if (type == QCPAxis::atLeft)
6226  {
6227  QTransform oldTransform = painter->transform();
6228  painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
6229  painter->rotate(-90);
6230  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6231  painter->setTransform(oldTransform);
6232  }
6233  else if (type == QCPAxis::atRight)
6234  {
6235  QTransform oldTransform = painter->transform();
6236  painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
6237  painter->rotate(90);
6238  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6239  painter->setTransform(oldTransform);
6240  }
6241  else if (type == QCPAxis::atTop)
6242  painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6243  else if (type == QCPAxis::atBottom)
6244  painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6245  }
6246 
6247  // set selection boxes:
6248  int selectionTolerance = 0;
6249  if (mParentPlot)
6250  selectionTolerance = mParentPlot->selectionTolerance();
6251  else
6252  qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6253  int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6254  int selAxisInSize = selectionTolerance;
6255  int selTickLabelSize;
6256  int selTickLabelOffset;
6258  {
6259  selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6260  selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
6261  } else
6262  {
6263  selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6264  selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6265  }
6266  int selLabelSize = labelBounds.height();
6267  int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
6268  if (type == QCPAxis::atLeft)
6269  {
6270  mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
6271  mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
6272  mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
6273  } else if (type == QCPAxis::atRight)
6274  {
6275  mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
6276  mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
6277  mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
6278  } else if (type == QCPAxis::atTop)
6279  {
6280  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
6281  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
6282  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
6283  } else if (type == QCPAxis::atBottom)
6284  {
6285  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
6286  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
6287  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
6288  }
6289  mAxisSelectionBox = mAxisSelectionBox.normalized();
6291  mLabelSelectionBox = mLabelSelectionBox.normalized();
6292  // draw hitboxes for debug purposes:
6293  //painter->setBrush(Qt::NoBrush);
6294  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
6295 }
6296 
6303 {
6304  int result = 0;
6305 
6306  // get length of tick marks pointing outwards:
6307  if (!tickPositions.isEmpty())
6308  result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6309 
6310  // calculate size of tick labels:
6312  {
6313  QSize tickLabelsSize(0, 0);
6314  if (!tickLabels.isEmpty())
6315  {
6316  for (int i=0; i<tickLabels.size(); ++i)
6317  getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6318  result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6319  result += tickLabelPadding;
6320  }
6321  }
6322 
6323  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6324  if (!label.isEmpty())
6325  {
6326  QFontMetrics fontMetrics(labelFont);
6327  QRect bounds;
6328  bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6329  result += bounds.height() + labelPadding;
6330  }
6331 
6332  return result;
6333 }
6334 
6342 {
6343  mLabelCache.clear();
6344 }
6345 
6354 {
6355  QByteArray result;
6356  result.append(QByteArray::number(tickLabelRotation));
6357  result.append(QByteArray::number((int)tickLabelSide));
6358  result.append(QByteArray::number((int)substituteExponent));
6359  result.append(QByteArray::number((int)numberMultiplyCross));
6360  result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
6361  result.append(tickLabelFont.toString().toLatin1());
6362  return result;
6363 }
6364 
6384 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
6385 {
6386  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
6387  if (text.isEmpty()) return;
6388  QSize finalSize;
6389  QPointF labelAnchor;
6390  switch (type)
6391  {
6392  case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
6393  case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
6394  case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
6395  case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
6396  }
6397  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
6398  {
6399  if (!mLabelCache.contains(text)) // no cached label exists, create it
6400  {
6401  CachedLabel *newCachedLabel = new CachedLabel;
6402  TickLabelData labelData = getTickLabelData(painter->font(), text);
6403  newCachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
6404  newCachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6405  newCachedLabel->pixmap.fill(Qt::transparent);
6406  QCPPainter cachePainter(&newCachedLabel->pixmap);
6407  cachePainter.setPen(painter->pen());
6408  drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6409  mLabelCache.insert(text, newCachedLabel, 1);
6410  }
6411  // draw cached label:
6412  const CachedLabel *cachedLabel = mLabelCache.object(text);
6413  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6415  {
6416  if (QCPAxis::orientation(type) == Qt::Horizontal)
6417  {
6418  if (labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() ||
6419  labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left())
6420  return;
6421  } else
6422  {
6423  if (labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() >viewportRect.bottom() ||
6424  labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top())
6425  return;
6426  }
6427  }
6428  painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
6429  finalSize = cachedLabel->pixmap.size();
6430  } else // label caching disabled, draw text directly on surface:
6431  {
6432  TickLabelData labelData = getTickLabelData(painter->font(), text);
6433  QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6434  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6436  {
6437  if (QCPAxis::orientation(type) == Qt::Horizontal)
6438  {
6439  if (finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() ||
6440  finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left())
6441  return;
6442  } else
6443  {
6444  if (finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() ||
6445  finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top())
6446  return;
6447  }
6448  }
6449  drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6450  finalSize = labelData.rotatedTotalBounds.size();
6451  }
6452 
6453  // expand passed tickLabelsSize if current tick label is larger:
6454  if (finalSize.width() > tickLabelsSize->width())
6455  tickLabelsSize->setWidth(finalSize.width());
6456  if (finalSize.height() > tickLabelsSize->height())
6457  tickLabelsSize->setHeight(finalSize.height());
6458 }
6459 
6469 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
6470 {
6471  // backup painter settings that we're about to change:
6472  QTransform oldTransform = painter->transform();
6473  QFont oldFont = painter->font();
6474 
6475  // transform painter to position/rotation:
6476  painter->translate(x, y);
6477  if (!qFuzzyIsNull(tickLabelRotation))
6478  painter->rotate(tickLabelRotation);
6479 
6480  // draw text:
6481  if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6482  {
6483  painter->setFont(labelData.baseFont);
6484  painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6485  painter->setFont(labelData.expFont);
6486  painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
6487  } else
6488  {
6489  painter->setFont(labelData.baseFont);
6490  painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6491  }
6492 
6493  // reset painter settings to what it was before:
6494  painter->setTransform(oldTransform);
6495  painter->setFont(oldFont);
6496 }
6497 
6507 {
6508  TickLabelData result;
6509 
6510  // determine whether beautiful decimal powers should be used
6511  bool useBeautifulPowers = false;
6512  int ePos = -1;
6513  if (substituteExponent)
6514  {
6515  ePos = text.indexOf(QLatin1Char('e'));
6516  if (ePos > -1)
6517  useBeautifulPowers = true;
6518  }
6519 
6520  // calculate text bounding rects and do string preparation for beautiful decimal powers:
6521  result.baseFont = font;
6522  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
6523  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
6524  if (useBeautifulPowers)
6525  {
6526  // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6527  result.basePart = text.left(ePos);
6528  // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6529  if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
6530  result.basePart = QLatin1String("10");
6531  else
6532  result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
6533  result.expPart = text.mid(ePos+1);
6534  // clip "+" and leading zeros off expPart:
6535  while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
6536  result.expPart.remove(1, 1);
6537  if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
6538  result.expPart.remove(0, 1);
6539  // prepare smaller font for exponent:
6540  result.expFont = font;
6541  result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6542  // calculate bounding rects of base part, exponent part and total one:
6543  result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6544  result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6545  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
6546  } else // useBeautifulPowers == false
6547  {
6548  result.basePart = text;
6549  result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6550  }
6551  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
6552 
6553  // calculate possibly different bounding rect after rotation:
6554  result.rotatedTotalBounds = result.totalBounds;
6555  if (!qFuzzyIsNull(tickLabelRotation))
6556  {
6557  QTransform transform;
6558  transform.rotate(tickLabelRotation);
6559  result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6560  }
6561 
6562  return result;
6563 }
6564 
6576 {
6577  /*
6578  calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6579  explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6580  horizontally under the corresponding tick is always on the label side that is closer to the
6581  axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6582  is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6583  will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6584  time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6585  labels.
6586  */
6587  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6588  bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6589  double radians = tickLabelRotation/180.0*M_PI;
6590  int x=0, y=0;
6591  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
6592  {
6593  if (doRotation)
6594  {
6595  if (tickLabelRotation > 0)
6596  {
6597  x = -qCos(radians)*labelData.totalBounds.width();
6598  y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6599  } else
6600  {
6601  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6602  y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6603  }
6604  } else
6605  {
6606  x = -labelData.totalBounds.width();
6607  y = -labelData.totalBounds.height()/2.0;
6608  }
6609  } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
6610  {
6611  if (doRotation)
6612  {
6613  if (tickLabelRotation > 0)
6614  {
6615  x = +qSin(radians)*labelData.totalBounds.height();
6616  y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6617  } else
6618  {
6619  x = 0;
6620  y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6621  }
6622  } else
6623  {
6624  x = 0;
6625  y = -labelData.totalBounds.height()/2.0;
6626  }
6627  } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
6628  {
6629  if (doRotation)
6630  {
6631  if (tickLabelRotation > 0)
6632  {
6633  x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6634  y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6635  } else
6636  {
6637  x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6638  y = -qCos(-radians)*labelData.totalBounds.height();
6639  }
6640  } else
6641  {
6642  x = -labelData.totalBounds.width()/2.0;
6643  y = -labelData.totalBounds.height();
6644  }
6645  } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
6646  {
6647  if (doRotation)
6648  {
6649  if (tickLabelRotation > 0)
6650  {
6651  x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6652  y = 0;
6653  } else
6654  {
6655  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6656  y = +qSin(-radians)*labelData.totalBounds.width();
6657  }
6658  } else
6659  {
6660  x = -labelData.totalBounds.width()/2.0;
6661  y = 0;
6662  }
6663  }
6664 
6665  return QPointF(x, y);
6666 }
6667 
6675 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
6676 {
6677  // note: this function must return the same tick label sizes as the placeTickLabel function.
6678  QSize finalSize;
6679  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6680  {
6681  const CachedLabel *cachedLabel = mLabelCache.object(text);
6682  finalSize = cachedLabel->pixmap.size();
6683  } else // label caching disabled or no label with this text cached:
6684  {
6685  TickLabelData labelData = getTickLabelData(font, text);
6686  finalSize = labelData.rotatedTotalBounds.size();
6687  }
6688 
6689  // expand passed tickLabelsSize if current tick label is larger:
6690  if (finalSize.width() > tickLabelsSize->width())
6691  tickLabelsSize->setWidth(finalSize.width());
6692  if (finalSize.height() > tickLabelsSize->height())
6693  tickLabelsSize->setHeight(finalSize.height());
6694 }
6695 
6696 
6700 
6769 /* start of documentation of pure virtual functions */
6770 
6816 /* end of documentation of pure virtual functions */
6817 /* start of documentation of signals */
6818 
6832 /* end of documentation of signals */
6833 
6846  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
6847  mName(),
6848  mAntialiasedFill(true),
6849  mAntialiasedScatters(true),
6850  mAntialiasedErrorBars(false),
6851  mPen(Qt::black),
6852  mSelectedPen(Qt::black),
6853  mBrush(Qt::NoBrush),
6854  mSelectedBrush(Qt::NoBrush),
6855  mKeyAxis(keyAxis),
6856  mValueAxis(valueAxis),
6857  mSelectable(true),
6858  mSelected(false)
6859 {
6860  if (keyAxis->parentPlot() != valueAxis->parentPlot())
6861  qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
6862  if (keyAxis->orientation() == valueAxis->orientation())
6863  qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
6864 }
6865 
6871 {
6872  mName = name;
6873 }
6874 
6882 {
6883  mAntialiasedFill = enabled;
6884 }
6885 
6893 {
6894  mAntialiasedScatters = enabled;
6895 }
6896 
6904 {
6905  mAntialiasedErrorBars = enabled;
6906 }
6907 
6908 
6919 {
6920  mPen = pen;
6921 }
6922 
6930 {
6931  mSelectedPen = pen;
6932 }
6933 
6944 {
6945  mBrush = brush;
6946 }
6947 
6955 {
6957 }
6958 
6971 {
6972  mKeyAxis = axis;
6973 }
6974 
6987 {
6988  mValueAxis = axis;
6989 }
6990 
7001 {
7002  if (mSelectable != selectable)
7003  {
7006  }
7007 }
7008 
7024 {
7025  if (mSelected != selected)
7026  {
7027  mSelected = selected;
7029  }
7030 }
7031 
7045 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
7046 {
7047  rescaleKeyAxis(onlyEnlarge);
7048  rescaleValueAxis(onlyEnlarge);
7049 }
7050 
7056 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
7057 {
7058  QCPAxis *keyAxis = mKeyAxis.data();
7059  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
7060 
7061  SignDomain signDomain = sdBoth;
7062  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
7063  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7064 
7065  bool foundRange;
7066  QCPRange newRange = getKeyRange(foundRange, signDomain);
7067  if (foundRange)
7068  {
7069  if (onlyEnlarge)
7070  newRange.expand(keyAxis->range());
7071  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
7072  {
7073  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
7074  if (keyAxis->scaleType() == QCPAxis::stLinear)
7075  {
7076  newRange.lower = center-keyAxis->range().size()/2.0;
7077  newRange.upper = center+keyAxis->range().size()/2.0;
7078  } else // scaleType() == stLogarithmic
7079  {
7080  newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7081  newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7082  }
7083  }
7084  keyAxis->setRange(newRange);
7085  }
7086 }
7087 
7096 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
7097 {
7098  QCPAxis *valueAxis = mValueAxis.data();
7099  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
7100 
7101  SignDomain signDomain = sdBoth;
7102  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
7103  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7104 
7105  bool foundRange;
7106  QCPRange newRange = getValueRange(foundRange, signDomain);
7107  if (foundRange)
7108  {
7109  if (onlyEnlarge)
7110  newRange.expand(valueAxis->range());
7111  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
7112  {
7113  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
7114  if (valueAxis->scaleType() == QCPAxis::stLinear)
7115  {
7116  newRange.lower = center-valueAxis->range().size()/2.0;
7117  newRange.upper = center+valueAxis->range().size()/2.0;
7118  } else // scaleType() == stLogarithmic
7119  {
7120  newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7121  newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7122  }
7123  }
7124  valueAxis->setRange(newRange);
7125  }
7126 }
7127 
7141 {
7142  if (!mParentPlot || !mParentPlot->legend)
7143  return false;
7144 
7146  {
7148  return true;
7149  } else
7150  return false;
7151 }
7152 
7164 {
7165  if (!mParentPlot->legend)
7166  return false;
7167 
7169  return mParentPlot->legend->removeItem(lip);
7170  else
7171  return false;
7172 }
7173 
7174 /* inherits documentation from base class */
7176 {
7177  if (mKeyAxis && mValueAxis)
7178  return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
7179  else
7180  return QRect();
7181 }
7182 
7183 /* inherits documentation from base class */
7185 {
7186  return QCP::iSelectPlottables;
7187 }
7188 
7199 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
7200 {
7201  QCPAxis *keyAxis = mKeyAxis.data();
7202  QCPAxis *valueAxis = mValueAxis.data();
7203  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7204 
7205  if (keyAxis->orientation() == Qt::Horizontal)
7206  {
7207  x = keyAxis->coordToPixel(key);
7208  y = valueAxis->coordToPixel(value);
7209  } else
7210  {
7211  y = keyAxis->coordToPixel(key);
7212  x = valueAxis->coordToPixel(value);
7213  }
7214 }
7215 
7221 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
7222 {
7223  QCPAxis *keyAxis = mKeyAxis.data();
7224  QCPAxis *valueAxis = mValueAxis.data();
7225  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
7226 
7227  if (keyAxis->orientation() == Qt::Horizontal)
7228  return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7229  else
7230  return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7231 }
7232 
7243 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
7244 {
7245  QCPAxis *keyAxis = mKeyAxis.data();
7246  QCPAxis *valueAxis = mValueAxis.data();
7247  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7248 
7249  if (keyAxis->orientation() == Qt::Horizontal)
7250  {
7251  key = keyAxis->pixelToCoord(x);
7252  value = valueAxis->pixelToCoord(y);
7253  } else
7254  {
7255  key = keyAxis->pixelToCoord(y);
7256  value = valueAxis->pixelToCoord(x);
7257  }
7258 }
7259 
7265 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
7266 {
7267  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7268 }
7269 
7276 {
7277  return mSelected ? mSelectedPen : mPen;
7278 }
7279 
7286 {
7287  return mSelected ? mSelectedBrush : mBrush;
7288 }
7289 
7304 {
7306 }
7307 
7320 {
7322 }
7323 
7336 {
7338 }
7339 
7352 {
7354 }
7355 
7366 double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
7367 {
7368  QVector2D a(start);
7369  QVector2D b(end);
7370  QVector2D p(point);
7371  QVector2D v(b-a);
7372 
7373  double vLengthSqr = v.lengthSquared();
7374  if (!qFuzzyIsNull(vLengthSqr))
7375  {
7376  double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
7377  if (mu < 0)
7378  return (a-p).lengthSquared();
7379  else if (mu > 1)
7380  return (b-p).lengthSquared();
7381  else
7382  return ((a + mu*v)-p).lengthSquared();
7383  } else
7384  return (a-p).lengthSquared();
7385 }
7386 
7387 /* inherits documentation from base class */
7388 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
7389 {
7390  Q_UNUSED(event)
7391  Q_UNUSED(details)
7392  if (mSelectable)
7393  {
7394  bool selBefore = mSelected;
7395  setSelected(additive ? !mSelected : true);
7396  if (selectionStateChanged)
7397  *selectionStateChanged = mSelected != selBefore;
7398  }
7399 }
7400 
7401 /* inherits documentation from base class */
7402 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
7403 {
7404  if (mSelectable)
7405  {
7406  bool selBefore = mSelected;
7407  setSelected(false);
7408  if (selectionStateChanged)
7409  *selectionStateChanged = mSelected != selBefore;
7410  }
7411 }
7412 
7413 
7417 
7439 /* start documentation of inline functions */
7440 
7451 /* end documentation of inline functions */
7452 
7458 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
7459  mName(name),
7460  mParentPlot(parentPlot),
7461  mParentItem(parentItem),
7462  mAnchorId(anchorId)
7463 {
7464 }
7465 
7467 {
7468  // unregister as parent at children:
7469  foreach (QCPItemPosition *child, mChildrenX.toList())
7470  {
7471  if (child->parentAnchorX() == this)
7472  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7473  }
7474  foreach (QCPItemPosition *child, mChildrenY.toList())
7475  {
7476  if (child->parentAnchorY() == this)
7477  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7478  }
7479 }
7480 
7488 {
7489  if (mParentItem)
7490  {
7491  if (mAnchorId > -1)
7492  {
7494  } else
7495  {
7496  qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7497  return QPointF();
7498  }
7499  } else
7500  {
7501  qDebug() << Q_FUNC_INFO << "no parent item set";
7502  return QPointF();
7503  }
7504 }
7505 
7515 {
7516  if (!mChildrenX.contains(pos))
7517  mChildrenX.insert(pos);
7518  else
7519  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7520 }
7521 
7529 {
7530  if (!mChildrenX.remove(pos))
7531  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7532 }
7533 
7543 {
7544  if (!mChildrenY.contains(pos))
7545  mChildrenY.insert(pos);
7546  else
7547  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7548 }
7549 
7557 {
7558  if (!mChildrenY.remove(pos))
7559  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7560 }
7561 
7562 
7566 
7601 /* start documentation of inline functions */
7602 
7624 /* end documentation of inline functions */
7625 
7631 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
7632  QCPItemAnchor(parentPlot, parentItem, name),
7633  mPositionTypeX(ptAbsolute),
7634  mPositionTypeY(ptAbsolute),
7635  mKey(0),
7636  mValue(0),
7637  mParentAnchorX(0),
7638  mParentAnchorY(0)
7639 {
7640 }
7641 
7643 {
7644  // unregister as parent at children:
7645  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
7646  // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
7647  foreach (QCPItemPosition *child, mChildrenX.toList())
7648  {
7649  if (child->parentAnchorX() == this)
7650  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7651  }
7652  foreach (QCPItemPosition *child, mChildrenY.toList())
7653  {
7654  if (child->parentAnchorY() == this)
7655  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7656  }
7657  // unregister as child in parent:
7658  if (mParentAnchorX)
7660  if (mParentAnchorY)
7662 }
7663 
7664 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
7666 {
7667  return mAxisRect.data();
7668 }
7669 
7696 {
7697  setTypeX(type);
7698  setTypeY(type);
7699 }
7700 
7709 {
7710  if (mPositionTypeX != type)
7711  {
7712  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7713  // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7714  bool retainPixelPosition = true;
7715  if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7716  retainPixelPosition = false;
7717  if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7718  retainPixelPosition = false;
7719 
7720  QPointF pixel;
7721  if (retainPixelPosition)
7722  pixel = pixelPoint();
7723 
7724  mPositionTypeX = type;
7725 
7726  if (retainPixelPosition)
7727  setPixelPoint(pixel);
7728  }
7729 }
7730 
7739 {
7740  if (mPositionTypeY != type)
7741  {
7742  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7743  // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7744  bool retainPixelPosition = true;
7745  if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7746  retainPixelPosition = false;
7747  if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7748  retainPixelPosition = false;
7749 
7750  QPointF pixel;
7751  if (retainPixelPosition)
7752  pixel = pixelPoint();
7753 
7754  mPositionTypeY = type;
7755 
7756  if (retainPixelPosition)
7757  setPixelPoint(pixel);
7758  }
7759 }
7760 
7780 {
7781  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
7782  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
7783  return successX && successY;
7784 }
7785 
7794 {
7795  // make sure self is not assigned as parent:
7796  if (parentAnchor == this)
7797  {
7798  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7799  return false;
7800  }
7801  // make sure no recursive parent-child-relationships are created:
7802  QCPItemAnchor *currentParent = parentAnchor;
7803  while (currentParent)
7804  {
7805  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7806  {
7807  // is a QCPItemPosition, might have further parent, so keep iterating
7808  if (currentParentPos == this)
7809  {
7810  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7811  return false;
7812  }
7813  currentParent = currentParentPos->parentAnchorX();
7814  } else
7815  {
7816  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7817  // same, to prevent a position being child of an anchor which itself depends on the position,
7818  // because they're both on the same item:
7819  if (currentParent->mParentItem == mParentItem)
7820  {
7821  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7822  return false;
7823  }
7824  break;
7825  }
7826  }
7827 
7828  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7831 
7832  // save pixel position:
7833  QPointF pixelP;
7834  if (keepPixelPosition)
7835  pixelP = pixelPoint();
7836  // unregister at current parent anchor:
7837  if (mParentAnchorX)
7839  // register at new parent anchor:
7840  if (parentAnchor)
7841  parentAnchor->addChildX(this);
7843  // restore pixel position under new parent:
7844  if (keepPixelPosition)
7845  setPixelPoint(pixelP);
7846  else
7847  setCoords(0, coords().y());
7848  return true;
7849 }
7850 
7859 {
7860  // make sure self is not assigned as parent:
7861  if (parentAnchor == this)
7862  {
7863  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7864  return false;
7865  }
7866  // make sure no recursive parent-child-relationships are created:
7867  QCPItemAnchor *currentParent = parentAnchor;
7868  while (currentParent)
7869  {
7870  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7871  {
7872  // is a QCPItemPosition, might have further parent, so keep iterating
7873  if (currentParentPos == this)
7874  {
7875  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7876  return false;
7877  }
7878  currentParent = currentParentPos->parentAnchorY();
7879  } else
7880  {
7881  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7882  // same, to prevent a position being child of an anchor which itself depends on the position,
7883  // because they're both on the same item:
7884  if (currentParent->mParentItem == mParentItem)
7885  {
7886  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7887  return false;
7888  }
7889  break;
7890  }
7891  }
7892 
7893  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7896 
7897  // save pixel position:
7898  QPointF pixelP;
7899  if (keepPixelPosition)
7900  pixelP = pixelPoint();
7901  // unregister at current parent anchor:
7902  if (mParentAnchorY)
7904  // register at new parent anchor:
7905  if (parentAnchor)
7906  parentAnchor->addChildY(this);
7908  // restore pixel position under new parent:
7909  if (keepPixelPosition)
7910  setPixelPoint(pixelP);
7911  else
7912  setCoords(coords().x(), 0);
7913  return true;
7914 }
7915 
7934 {
7935  mKey = key;
7936  mValue = value;
7937 }
7938 
7944 void QCPItemPosition::setCoords(const QPointF &pos)
7945 {
7946  setCoords(pos.x(), pos.y());
7947 }
7948 
7956 {
7957  QPointF result;
7958 
7959  // determine X:
7960  switch (mPositionTypeX)
7961  {
7962  case ptAbsolute:
7963  {
7964  result.rx() = mKey;
7965  if (mParentAnchorX)
7966  result.rx() += mParentAnchorX->pixelPoint().x();
7967  break;
7968  }
7969  case ptViewportRatio:
7970  {
7971  result.rx() = mKey*mParentPlot->viewport().width();
7972  if (mParentAnchorX)
7973  result.rx() += mParentAnchorX->pixelPoint().x();
7974  else
7975  result.rx() += mParentPlot->viewport().left();
7976  break;
7977  }
7978  case ptAxisRectRatio:
7979  {
7980  if (mAxisRect)
7981  {
7982  result.rx() = mKey*mAxisRect.data()->width();
7983  if (mParentAnchorX)
7984  result.rx() += mParentAnchorX->pixelPoint().x();
7985  else
7986  result.rx() += mAxisRect.data()->left();
7987  } else
7988  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
7989  break;
7990  }
7991  case ptPlotCoords:
7992  {
7993  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
7994  result.rx() = mKeyAxis.data()->coordToPixel(mKey);
7995  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
7996  result.rx() = mValueAxis.data()->coordToPixel(mValue);
7997  else
7998  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
7999  break;
8000  }
8001  }
8002 
8003  // determine Y:
8004  switch (mPositionTypeY)
8005  {
8006  case ptAbsolute:
8007  {
8008  result.ry() = mValue;
8009  if (mParentAnchorY)
8010  result.ry() += mParentAnchorY->pixelPoint().y();
8011  break;
8012  }
8013  case ptViewportRatio:
8014  {
8015  result.ry() = mValue*mParentPlot->viewport().height();
8016  if (mParentAnchorY)
8017  result.ry() += mParentAnchorY->pixelPoint().y();
8018  else
8019  result.ry() += mParentPlot->viewport().top();
8020  break;
8021  }
8022  case ptAxisRectRatio:
8023  {
8024  if (mAxisRect)
8025  {
8026  result.ry() = mValue*mAxisRect.data()->height();
8027  if (mParentAnchorY)
8028  result.ry() += mParentAnchorY->pixelPoint().y();
8029  else
8030  result.ry() += mAxisRect.data()->top();
8031  } else
8032  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8033  break;
8034  }
8035  case ptPlotCoords:
8036  {
8037  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8038  result.ry() = mKeyAxis.data()->coordToPixel(mKey);
8039  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8040  result.ry() = mValueAxis.data()->coordToPixel(mValue);
8041  else
8042  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8043  break;
8044  }
8045  }
8046 
8047  return result;
8048 }
8049 
8056 {
8057  mKeyAxis = keyAxis;
8059 }
8060 
8067 {
8068  mAxisRect = axisRect;
8069 }
8070 
8082 {
8083  double x = pixelPoint.x();
8084  double y = pixelPoint.y();
8085 
8086  switch (mPositionTypeX)
8087  {
8088  case ptAbsolute:
8089  {
8090  if (mParentAnchorX)
8091  x -= mParentAnchorX->pixelPoint().x();
8092  break;
8093  }
8094  case ptViewportRatio:
8095  {
8096  if (mParentAnchorX)
8097  x -= mParentAnchorX->pixelPoint().x();
8098  else
8099  x -= mParentPlot->viewport().left();
8100  x /= (double)mParentPlot->viewport().width();
8101  break;
8102  }
8103  case ptAxisRectRatio:
8104  {
8105  if (mAxisRect)
8106  {
8107  if (mParentAnchorX)
8108  x -= mParentAnchorX->pixelPoint().x();
8109  else
8110  x -= mAxisRect.data()->left();
8111  x /= (double)mAxisRect.data()->width();
8112  } else
8113  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
8114  break;
8115  }
8116  case ptPlotCoords:
8117  {
8118  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8119  x = mKeyAxis.data()->pixelToCoord(x);
8120  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
8121  y = mValueAxis.data()->pixelToCoord(x);
8122  else
8123  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
8124  break;
8125  }
8126  }
8127 
8128  switch (mPositionTypeY)
8129  {
8130  case ptAbsolute:
8131  {
8132  if (mParentAnchorY)
8133  y -= mParentAnchorY->pixelPoint().y();
8134  break;
8135  }
8136  case ptViewportRatio:
8137  {
8138  if (mParentAnchorY)
8139  y -= mParentAnchorY->pixelPoint().y();
8140  else
8141  y -= mParentPlot->viewport().top();
8142  y /= (double)mParentPlot->viewport().height();
8143  break;
8144  }
8145  case ptAxisRectRatio:
8146  {
8147  if (mAxisRect)
8148  {
8149  if (mParentAnchorY)
8150  y -= mParentAnchorY->pixelPoint().y();
8151  else
8152  y -= mAxisRect.data()->top();
8153  y /= (double)mAxisRect.data()->height();
8154  } else
8155  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8156  break;
8157  }
8158  case ptPlotCoords:
8159  {
8160  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8161  x = mKeyAxis.data()->pixelToCoord(y);
8162  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8163  y = mValueAxis.data()->pixelToCoord(y);
8164  else
8165  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8166  break;
8167  }
8168  }
8169 
8170  setCoords(x, y);
8171 }
8172 
8173 
8177 
8317 /* start of documentation of inline functions */
8318 
8334 /* end of documentation of inline functions */
8335 /* start documentation of pure virtual functions */
8336 
8347 /* end documentation of pure virtual functions */
8348 /* start documentation of signals */
8349 
8355 /* end documentation of signals */
8356 
8361  QCPLayerable(parentPlot),
8362  mClipToAxisRect(false),
8363  mSelectable(true),
8364  mSelected(false)
8365 {
8366  QList<QCPAxisRect*> rects = parentPlot->axisRects();
8367  if (rects.size() > 0)
8368  {
8369  setClipToAxisRect(true);
8370  setClipAxisRect(rects.first());
8371  }
8372 }
8373 
8375 {
8376  // don't delete mPositions because every position is also an anchor and thus in mAnchors
8377  qDeleteAll(mAnchors);
8378 }
8379 
8380 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
8382 {
8383  return mClipAxisRect.data();
8384 }
8385 
8393 {
8394  mClipToAxisRect = clip;
8395  if (mClipToAxisRect)
8397 }
8398 
8406 {
8407  mClipAxisRect = rect;
8408  if (mClipToAxisRect)
8410 }
8411 
8422 {
8423  if (mSelectable != selectable)
8424  {
8427  }
8428 }
8429 
8445 {
8446  if (mSelected != selected)
8447  {
8448  mSelected = selected;
8450  }
8451 }
8452 
8463 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
8464 {
8465  for (int i=0; i<mPositions.size(); ++i)
8466  {
8467  if (mPositions.at(i)->name() == name)
8468  return mPositions.at(i);
8469  }
8470  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8471  return 0;
8472 }
8473 
8484 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8485 {
8486  for (int i=0; i<mAnchors.size(); ++i)
8487  {
8488  if (mAnchors.at(i)->name() == name)
8489  return mAnchors.at(i);
8490  }
8491  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8492  return 0;
8493 }
8494 
8503 bool QCPAbstractItem::hasAnchor(const QString &name) const
8504 {
8505  for (int i=0; i<mAnchors.size(); ++i)
8506  {
8507  if (mAnchors.at(i)->name() == name)
8508  return true;
8509  }
8510  return false;
8511 }
8512 
8523 {
8525  return mClipAxisRect.data()->rect();
8526  else
8527  return mParentPlot->viewport();
8528 }
8529 
8544 {
8546 }
8547 
8560 double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8561 {
8562  QVector2D a(start);
8563  QVector2D b(end);
8564  QVector2D p(point);
8565  QVector2D v(b-a);
8566 
8567  double vLengthSqr = v.lengthSquared();
8568  if (!qFuzzyIsNull(vLengthSqr))
8569  {
8570  double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8571  if (mu < 0)
8572  return (a-p).lengthSquared();
8573  else if (mu > 1)
8574  return (b-p).lengthSquared();
8575  else
8576  return ((a + mu*v)-p).lengthSquared();
8577  } else
8578  return (a-p).lengthSquared();
8579 }
8580 
8598 double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
8599 {
8600  double result = -1;
8601 
8602  // distance to border:
8603  QList<QLineF> lines;
8604  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
8605  << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
8606  double minDistSqr = std::numeric_limits<double>::max();
8607  for (int i=0; i<lines.size(); ++i)
8608  {
8609  double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8610  if (distSqr < minDistSqr)
8611  minDistSqr = distSqr;
8612  }
8613  result = qSqrt(minDistSqr);
8614 
8615  // filled rect, allow click inside to count as hit:
8616  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
8617  {
8618  if (rect.contains(pos))
8619  result = mParentPlot->selectionTolerance()*0.99;
8620  }
8621  return result;
8622 }
8623 
8634 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
8635 {
8636  qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
8637  return QPointF();
8638 }
8639 
8655 {
8656  if (hasAnchor(name))
8657  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8658  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8659  mPositions.append(newPosition);
8660  mAnchors.append(newPosition); // every position is also an anchor
8661  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8662  newPosition->setType(QCPItemPosition::ptPlotCoords);
8663  if (mParentPlot->axisRect())
8664  newPosition->setAxisRect(mParentPlot->axisRect());
8665  newPosition->setCoords(0, 0);
8666  return newPosition;
8667 }
8668 
8688 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
8689 {
8690  if (hasAnchor(name))
8691  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8692  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
8693  mAnchors.append(newAnchor);
8694  return newAnchor;
8695 }
8696 
8697 /* inherits documentation from base class */
8698 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8699 {
8700  Q_UNUSED(event)
8701  Q_UNUSED(details)
8702  if (mSelectable)
8703  {
8704  bool selBefore = mSelected;
8705  setSelected(additive ? !mSelected : true);
8706  if (selectionStateChanged)
8707  *selectionStateChanged = mSelected != selBefore;
8708  }
8709 }
8710 
8711 /* inherits documentation from base class */
8712 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
8713 {
8714  if (mSelectable)
8715  {
8716  bool selBefore = mSelected;
8717  setSelected(false);
8718  if (selectionStateChanged)
8719  *selectionStateChanged = mSelected != selBefore;
8720  }
8721 }
8722 
8723 /* inherits documentation from base class */
8725 {
8726  return QCP::iSelectItems;
8727 }
8728 
8729 
8734 
8747 /* start of documentation of inline functions */
8748 
8770 /* end of documentation of inline functions */
8771 /* start of documentation of signals */
8772 
8960 /* end of documentation of signals */
8961 /* start of documentation of public members */
8962 
9034 /* end of documentation of public members */
9035 
9039 QCustomPlot::QCustomPlot(QWidget *parent) :
9040  QWidget(parent),
9041  xAxis(0),
9042  yAxis(0),
9043  xAxis2(0),
9044  yAxis2(0),
9045  legend(0),
9046  mPlotLayout(0),
9047  mAutoAddPlottableToLegend(true),
9048  mAntialiasedElements(QCP::aeNone),
9049  mNotAntialiasedElements(QCP::aeNone),
9050  mInteractions(0),
9051  mSelectionTolerance(8),
9052  mNoAntialiasingOnDrag(false),
9053  mBackgroundBrush(Qt::white, Qt::SolidPattern),
9054  mBackgroundScaled(true),
9055  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9056  mCurrentLayer(0),
9057  mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
9058  mMultiSelectModifier(Qt::ControlModifier),
9059  mPaintBuffer(size()),
9060  mMouseEventElement(0),
9061  mReplotting(false)
9062 {
9063  setAttribute(Qt::WA_NoMousePropagation);
9064  setAttribute(Qt::WA_OpaquePaintEvent);
9065  setMouseTracking(true);
9066  QLocale currentLocale = locale();
9067  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9068  setLocale(currentLocale);
9069 
9070  // create initial layers:
9071  mLayers.append(new QCPLayer(this, QLatin1String("background")));
9072  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
9073  mLayers.append(new QCPLayer(this, QLatin1String("main")));
9074  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
9075  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
9077  setCurrentLayer(QLatin1String("main"));
9078 
9079  // create initial layout, axis rect and legend:
9080  mPlotLayout = new QCPLayoutGrid;
9082  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9083  mPlotLayout->setLayer(QLatin1String("main"));
9084  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9085  mPlotLayout->addElement(0, 0, defaultAxisRect);
9086  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9087  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9088  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9089  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9090  legend = new QCPLegend;
9091  legend->setVisible(false);
9092  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
9093  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9094 
9095  defaultAxisRect->setLayer(QLatin1String("background"));
9096  xAxis->setLayer(QLatin1String("axes"));
9097  yAxis->setLayer(QLatin1String("axes"));
9098  xAxis2->setLayer(QLatin1String("axes"));
9099  yAxis2->setLayer(QLatin1String("axes"));
9100  xAxis->grid()->setLayer(QLatin1String("grid"));
9101  yAxis->grid()->setLayer(QLatin1String("grid"));
9102  xAxis2->grid()->setLayer(QLatin1String("grid"));
9103  yAxis2->grid()->setLayer(QLatin1String("grid"));
9104  legend->setLayer(QLatin1String("legend"));
9105 
9106  setViewport(rect()); // needs to be called after mPlotLayout has been created
9107 
9108  replot();
9109 }
9110 
9112 {
9113  clearPlottables();
9114  clearItems();
9115 
9116  if (mPlotLayout)
9117  {
9118  delete mPlotLayout;
9119  mPlotLayout = 0;
9120  }
9121 
9122  mCurrentLayer = 0;
9123  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
9124  mLayers.clear();
9125 }
9126 
9144 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
9145 {
9147 
9148  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9150  mNotAntialiasedElements |= ~mAntialiasedElements;
9151 }
9152 
9160 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
9161 {
9162  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9163  mAntialiasedElements &= ~antialiasedElement;
9164  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9165  mAntialiasedElements |= antialiasedElement;
9166 
9167  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9169  mNotAntialiasedElements |= ~mAntialiasedElements;
9170 }
9171 
9191 {
9193 
9194  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9196  mAntialiasedElements |= ~mNotAntialiasedElements;
9197 }
9198 
9206 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
9207 {
9208  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9209  mNotAntialiasedElements &= ~notAntialiasedElement;
9210  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9211  mNotAntialiasedElements |= notAntialiasedElement;
9212 
9213  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9215  mAntialiasedElements |= ~mNotAntialiasedElements;
9216 }
9217 
9225 {
9227 }
9228 
9283 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
9284 {
9286 }
9287 
9295 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
9296 {
9297  if (!enabled && mInteractions.testFlag(interaction))
9298  mInteractions &= ~interaction;
9299  else if (enabled && !mInteractions.testFlag(interaction))
9300  mInteractions |= interaction;
9301 }
9302 
9317 {
9318  mSelectionTolerance = pixels;
9319 }
9320 
9331 {
9332  mNoAntialiasingOnDrag = enabled;
9333 }
9334 
9340 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
9341 {
9342  mPlottingHints = hints;
9343 }
9344 
9351 {
9352  QCP::PlottingHints newHints = mPlottingHints;
9353  if (!enabled)
9354  newHints &= ~hint;
9355  else
9356  newHints |= hint;
9357 
9358  if (newHints != mPlottingHints)
9359  setPlottingHints(newHints);
9360 }
9361 
9372 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
9373 {
9374  mMultiSelectModifier = modifier;
9375 }
9376 
9384 void QCustomPlot::setViewport(const QRect &rect)
9385 {
9386  mViewport = rect;
9387  if (mPlotLayout)
9389 }
9390 
9406 void QCustomPlot::setBackground(const QPixmap &pm)
9407 {
9408  mBackgroundPixmap = pm;
9409  mScaledBackgroundPixmap = QPixmap();
9410 }
9411 
9425 void QCustomPlot::setBackground(const QBrush &brush)
9426 {
9427  mBackgroundBrush = brush;
9428 }
9429 
9437 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
9438 {
9439  mBackgroundPixmap = pm;
9440  mScaledBackgroundPixmap = QPixmap();
9441  mBackgroundScaled = scaled;
9442  mBackgroundScaledMode = mode;
9443 }
9444 
9456 {
9457  mBackgroundScaled = scaled;
9458 }
9459 
9466 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
9467 {
9468  mBackgroundScaledMode = mode;
9469 }
9470 
9480 {
9481  if (index >= 0 && index < mPlottables.size())
9482  {
9483  return mPlottables.at(index);
9484  } else
9485  {
9486  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9487  return 0;
9488  }
9489 }
9490 
9499 {
9500  if (!mPlottables.isEmpty())
9501  {
9502  return mPlottables.last();
9503  } else
9504  return 0;
9505 }
9506 
9518 {
9519  if (mPlottables.contains(plottable))
9520  {
9521  qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
9522  return false;
9523  }
9524  if (plottable->parentPlot() != this)
9525  {
9526  qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
9527  return false;
9528  }
9529 
9530  mPlottables.append(plottable);
9531  // possibly add plottable to legend:
9533  plottable->addToLegend();
9534  // special handling for QCPGraphs to maintain the simple graph interface:
9535  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9536  mGraphs.append(graph);
9537  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
9538  plottable->setLayer(currentLayer());
9539  return true;
9540 }
9541 
9550 {
9551  if (!mPlottables.contains(plottable))
9552  {
9553  qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
9554  return false;
9555  }
9556 
9557  // remove plottable from legend:
9558  plottable->removeFromLegend();
9559  // special handling for QCPGraphs to maintain the simple graph interface:
9560  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9561  mGraphs.removeOne(graph);
9562  // remove plottable:
9563  delete plottable;
9564  mPlottables.removeOne(plottable);
9565  return true;
9566 }
9567 
9573 {
9574  if (index >= 0 && index < mPlottables.size())
9575  return removePlottable(mPlottables[index]);
9576  else
9577  {
9578  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9579  return false;
9580  }
9581 }
9582 
9591 {
9592  int c = mPlottables.size();
9593  for (int i=c-1; i >= 0; --i)
9595  return c;
9596 }
9597 
9604 {
9605  return mPlottables.size();
9606 }
9607 
9615 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9616 {
9617  QList<QCPAbstractPlottable*> result;
9619  {
9620  if (plottable->selected())
9621  result.append(plottable);
9622  }
9623  return result;
9624 }
9625 
9638 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
9639 {
9640  QCPAbstractPlottable *resultPlottable = 0;
9641  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9642 
9644  {
9645  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
9646  continue;
9647  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
9648  {
9649  double currentDistance = plottable->selectTest(pos, false);
9650  if (currentDistance >= 0 && currentDistance < resultDistance)
9651  {
9652  resultPlottable = plottable;
9653  resultDistance = currentDistance;
9654  }
9655  }
9656  }
9657 
9658  return resultPlottable;
9659 }
9660 
9667 {
9668  return mPlottables.contains(plottable);
9669 }
9670 
9679 QCPGraph *QCustomPlot::graph(int index) const
9680 {
9681  if (index >= 0 && index < mGraphs.size())
9682  {
9683  return mGraphs.at(index);
9684  } else
9685  {
9686  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9687  return 0;
9688  }
9689 }
9690 
9699 {
9700  if (!mGraphs.isEmpty())
9701  {
9702  return mGraphs.last();
9703  } else
9704  return 0;
9705 }
9706 
9720 {
9721  if (!keyAxis) keyAxis = xAxis;
9722  if (!valueAxis) valueAxis = yAxis;
9723  if (!keyAxis || !valueAxis)
9724  {
9725  qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
9726  return 0;
9727  }
9728  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
9729  {
9730  qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
9731  return 0;
9732  }
9733 
9734  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9735  if (addPlottable(newGraph))
9736  {
9737  newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
9738  return newGraph;
9739  } else
9740  {
9741  delete newGraph;
9742  return 0;
9743  }
9744 }
9745 
9756 {
9757  return removePlottable(graph);
9758 }
9759 
9765 {
9766  if (index >= 0 && index < mGraphs.size())
9767  return removeGraph(mGraphs[index]);
9768  else
9769  return false;
9770 }
9771 
9780 {
9781  int c = mGraphs.size();
9782  for (int i=c-1; i >= 0; --i)
9783  removeGraph(mGraphs[i]);
9784  return c;
9785 }
9786 
9793 {
9794  return mGraphs.size();
9795 }
9796 
9805 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9806 {
9807  QList<QCPGraph*> result;
9808  foreach (QCPGraph *graph, mGraphs)
9809  {
9810  if (graph->selected())
9811  result.append(graph);
9812  }
9813  return result;
9814 }
9815 
9825 {
9826  if (index >= 0 && index < mItems.size())
9827  {
9828  return mItems.at(index);
9829  } else
9830  {
9831  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9832  return 0;
9833  }
9834 }
9835 
9844 {
9845  if (!mItems.isEmpty())
9846  {
9847  return mItems.last();
9848  } else
9849  return 0;
9850 }
9851 
9861 {
9862  if (!mItems.contains(item) && item->parentPlot() == this)
9863  {
9864  mItems.append(item);
9865  return true;
9866  } else
9867  {
9868  qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
9869  return false;
9870  }
9871 }
9872 
9881 {
9882  if (mItems.contains(item))
9883  {
9884  delete item;
9885  mItems.removeOne(item);
9886  return true;
9887  } else
9888  {
9889  qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
9890  return false;
9891  }
9892 }
9893 
9899 {
9900  if (index >= 0 && index < mItems.size())
9901  return removeItem(mItems[index]);
9902  else
9903  {
9904  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9905  return false;
9906  }
9907 }
9908 
9917 {
9918  int c = mItems.size();
9919  for (int i=c-1; i >= 0; --i)
9920  removeItem(mItems[i]);
9921  return c;
9922 }
9923 
9930 {
9931  return mItems.size();
9932 }
9933 
9939 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9940 {
9941  QList<QCPAbstractItem*> result;
9942  foreach (QCPAbstractItem *item, mItems)
9943  {
9944  if (item->selected())
9945  result.append(item);
9946  }
9947  return result;
9948 }
9949 
9963 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
9964 {
9965  QCPAbstractItem *resultItem = 0;
9966  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9967 
9968  foreach (QCPAbstractItem *item, mItems)
9969  {
9970  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
9971  continue;
9972  if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
9973  {
9974  double currentDistance = item->selectTest(pos, false);
9975  if (currentDistance >= 0 && currentDistance < resultDistance)
9976  {
9977  resultItem = item;
9978  resultDistance = currentDistance;
9979  }
9980  }
9981  }
9982 
9983  return resultItem;
9984 }
9985 
9992 {
9993  return mItems.contains(item);
9994 }
9995 
10004 QCPLayer *QCustomPlot::layer(const QString &name) const
10005 {
10006  foreach (QCPLayer *layer, mLayers)
10007  {
10008  if (layer->name() == name)
10009  return layer;
10010  }
10011  return 0;
10012 }
10013 
10020 QCPLayer *QCustomPlot::layer(int index) const
10021 {
10022  if (index >= 0 && index < mLayers.size())
10023  {
10024  return mLayers.at(index);
10025  } else
10026  {
10027  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10028  return 0;
10029  }
10030 }
10031 
10036 {
10037  return mCurrentLayer;
10038 }
10039 
10050 bool QCustomPlot::setCurrentLayer(const QString &name)
10051 {
10052  if (QCPLayer *newCurrentLayer = layer(name))
10053  {
10054  return setCurrentLayer(newCurrentLayer);
10055  } else
10056  {
10057  qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10058  return false;
10059  }
10060 }
10061 
10071 {
10072  if (!mLayers.contains(layer))
10073  {
10074  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10075  return false;
10076  }
10077 
10078  mCurrentLayer = layer;
10079  return true;
10080 }
10081 
10088 {
10089  return mLayers.size();
10090 }
10091 
10105 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10106 {
10107  if (!otherLayer)
10108  otherLayer = mLayers.last();
10109  if (!mLayers.contains(otherLayer))
10110  {
10111  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10112  return false;
10113  }
10114  if (layer(name))
10115  {
10116  qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10117  return false;
10118  }
10119 
10120  QCPLayer *newLayer = new QCPLayer(this, name);
10121  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
10123  return true;
10124 }
10125 
10141 {
10142  if (!mLayers.contains(layer))
10143  {
10144  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10145  return false;
10146  }
10147  if (mLayers.size() < 2)
10148  {
10149  qDebug() << Q_FUNC_INFO << "can't remove last layer";
10150  return false;
10151  }
10152 
10153  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
10154  int removedIndex = layer->index();
10155  bool isFirstLayer = removedIndex==0;
10156  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
10157  QList<QCPLayerable*> children = layer->children();
10158  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
10159  {
10160  for (int i=children.size()-1; i>=0; --i)
10161  children.at(i)->moveToLayer(targetLayer, true);
10162  } else // append normally
10163  {
10164  for (int i=0; i<children.size(); ++i)
10165  children.at(i)->moveToLayer(targetLayer, false);
10166  }
10167  // if removed layer is current layer, change current layer to layer below/above:
10168  if (layer == mCurrentLayer)
10169  setCurrentLayer(targetLayer);
10170  // remove layer:
10171  delete layer;
10172  mLayers.removeOne(layer);
10174  return true;
10175 }
10176 
10187 {
10188  if (!mLayers.contains(layer))
10189  {
10190  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10191  return false;
10192  }
10193  if (!mLayers.contains(otherLayer))
10194  {
10195  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10196  return false;
10197  }
10198 
10199  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
10201  return true;
10202 }
10203 
10214 {
10215  return axisRects().size();
10216 }
10217 
10228 {
10229  const QList<QCPAxisRect*> rectList = axisRects();
10230  if (index >= 0 && index < rectList.size())
10231  {
10232  return rectList.at(index);
10233  } else
10234  {
10235  qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10236  return 0;
10237  }
10238 }
10239 
10245 QList<QCPAxisRect*> QCustomPlot::axisRects() const
10246 {
10247  QList<QCPAxisRect*> result;
10248  QStack<QCPLayoutElement*> elementStack;
10249  if (mPlotLayout)
10250  elementStack.push(mPlotLayout);
10251 
10252  while (!elementStack.isEmpty())
10253  {
10254  foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
10255  {
10256  if (element)
10257  {
10258  elementStack.push(element);
10259  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10260  result.append(ar);
10261  }
10262  }
10263  }
10264 
10265  return result;
10266 }
10267 
10278 {
10279  QCPLayoutElement *currentElement = mPlotLayout;
10280  bool searchSubElements = true;
10281  while (searchSubElements && currentElement)
10282  {
10283  searchSubElements = false;
10284  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10285  {
10286  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10287  {
10288  currentElement = subElement;
10289  searchSubElements = true;
10290  break;
10291  }
10292  }
10293  }
10294  return currentElement;
10295 }
10296 
10304 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10305 {
10306  QList<QCPAxis*> result, allAxes;
10307  foreach (QCPAxisRect *rect, axisRects())
10308  allAxes << rect->axes();
10309 
10310  foreach (QCPAxis *axis, allAxes)
10311  {
10312  if (axis->selectedParts() != QCPAxis::spNone)
10313  result.append(axis);
10314  }
10315 
10316  return result;
10317 }
10318 
10326 QList<QCPLegend*> QCustomPlot::selectedLegends() const
10327 {
10328  QList<QCPLegend*> result;
10329 
10330  QStack<QCPLayoutElement*> elementStack;
10331  if (mPlotLayout)
10332  elementStack.push(mPlotLayout);
10333 
10334  while (!elementStack.isEmpty())
10335  {
10336  foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
10337  {
10338  if (subElement)
10339  {
10340  elementStack.push(subElement);
10341  if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10342  {
10343  if (leg->selectedParts() != QCPLegend::spNone)
10344  result.append(leg);
10345  }
10346  }
10347  }
10348  }
10349 
10350  return result;
10351 }
10352 
10363 {
10364  foreach (QCPLayer *layer, mLayers)
10365  {
10366  foreach (QCPLayerable *layerable, layer->children())
10367  layerable->deselectEvent(0);
10368  }
10369 }
10370 
10385 {
10386  if (mReplotting) // incase signals loop back to replot slot
10387  return;
10388  mReplotting = true;
10389  emit beforeReplot();
10390 
10391  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10392  QCPPainter painter;
10393  painter.begin(&mPaintBuffer);
10394  if (painter.isActive())
10395  {
10396  painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10397  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10398  painter.fillRect(mViewport, mBackgroundBrush);
10399  draw(&painter);
10400  painter.end();
10401  if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10402  repaint();
10403  else
10404  update();
10405  } else // might happen if QCustomPlot has width or height zero
10406  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
10407 
10408  emit afterReplot();
10409  mReplotting = false;
10410 }
10411 
10420 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10421 {
10422  QList<QCPAxis*> allAxes;
10423  foreach (QCPAxisRect *rect, axisRects())
10424  allAxes << rect->axes();
10425 
10426  foreach (QCPAxis *axis, allAxes)
10427  axis->rescale(onlyVisiblePlottables);
10428 }
10429 
10467 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10468 {
10469  bool success = false;
10470 #ifdef QT_NO_PRINTER
10471  Q_UNUSED(fileName)
10472  Q_UNUSED(noCosmeticPen)
10473  Q_UNUSED(width)
10474  Q_UNUSED(height)
10475  qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
10476 #else
10477  int newWidth, newHeight;
10478  if (width == 0 || height == 0)
10479  {
10480  newWidth = this->width();
10481  newHeight = this->height();
10482  } else
10483  {
10484  newWidth = width;
10485  newHeight = height;
10486  }
10487 
10488  QPrinter printer(QPrinter::ScreenResolution);
10489  printer.setOutputFileName(fileName);
10490  printer.setOutputFormat(QPrinter::PdfFormat);
10491  printer.setColorMode(QPrinter::Color);
10492  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10493  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10494  QRect oldViewport = viewport();
10495  setViewport(QRect(0, 0, newWidth, newHeight));
10496 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
10497  printer.setFullPage(true);
10498  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10499 #else
10500  QPageLayout pageLayout;
10501  pageLayout.setMode(QPageLayout::FullPageMode);
10502  pageLayout.setOrientation(QPageLayout::Portrait);
10503  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
10504  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
10505  printer.setPageLayout(pageLayout);
10506 #endif
10507  QCPPainter printpainter;
10508  if (printpainter.begin(&printer))
10509  {
10510  printpainter.setMode(QCPPainter::pmVectorized);
10511  printpainter.setMode(QCPPainter::pmNoCaching);
10512  printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10513  printpainter.setWindow(mViewport);
10514  if (mBackgroundBrush.style() != Qt::NoBrush &&
10515  mBackgroundBrush.color() != Qt::white &&
10516  mBackgroundBrush.color() != Qt::transparent &&
10517  mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
10518  printpainter.fillRect(viewport(), mBackgroundBrush);
10519  draw(&printpainter);
10520  printpainter.end();
10521  success = true;
10522  }
10523  setViewport(oldViewport);
10524 #endif // QT_NO_PRINTER
10525  return success;
10526 }
10527 
10565 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10566 {
10567  return saveRastered(fileName, width, height, scale, "PNG", quality);
10568 }
10569 
10604 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
10605 {
10606  return saveRastered(fileName, width, height, scale, "JPG", quality);
10607 }
10608 
10640 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
10641 {
10642  return saveRastered(fileName, width, height, scale, "BMP");
10643 }
10644 
10654 {
10655  return mPlotLayout->minimumSizeHint();
10656 }
10657 
10664 {
10665  return mPlotLayout->minimumSizeHint();
10666 }
10667 
10673 void QCustomPlot::paintEvent(QPaintEvent *event)
10674 {
10675  Q_UNUSED(event);
10676  QPainter painter(this);
10677  painter.drawPixmap(0, 0, mPaintBuffer);
10678 }
10679 
10686 void QCustomPlot::resizeEvent(QResizeEvent *event)
10687 {
10688  // resize and repaint the buffer:
10689  mPaintBuffer = QPixmap(event->size());
10690  setViewport(rect());
10691  replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10692 }
10693 
10703 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
10704 {
10705  emit mouseDoubleClick(event);
10706 
10707  QVariant details;
10708  QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10709 
10710  // emit specialized object double click signals:
10711  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10712  emit plottableDoubleClick(ap, event);
10713  else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10714  emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10715  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10716  emit itemDoubleClick(ai, event);
10717  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10718  emit legendDoubleClick(lg, 0, event);
10719  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10720  emit legendDoubleClick(li->parentLegend(), li, event);
10721  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10722  emit titleDoubleClick(event, pt);
10723 
10724  // call double click event of affected layout element:
10725  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10726  el->mouseDoubleClickEvent(event);
10727 
10728  // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
10729  if (mMouseEventElement)
10730  {
10731  mMouseEventElement->mouseReleaseEvent(event);
10732  mMouseEventElement = 0;
10733  }
10734 
10735  //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
10736 }
10737 
10745 void QCustomPlot::mousePressEvent(QMouseEvent *event)
10746 {
10747  emit mousePress(event);
10748  mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
10749 
10750  // call event of affected layout element:
10751  mMouseEventElement = layoutElementAt(event->pos());
10752  if (mMouseEventElement)
10753  mMouseEventElement->mousePressEvent(event);
10754 
10755  QWidget::mousePressEvent(event);
10756 }
10757 
10767 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
10768 {
10769  emit mouseMove(event);
10770 
10771  // call event of affected layout element:
10772  if (mMouseEventElement)
10773  mMouseEventElement->mouseMoveEvent(event);
10774 
10775  QWidget::mouseMoveEvent(event);
10776 }
10777 
10792 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
10793 {
10794  emit mouseRelease(event);
10795  bool doReplot = false;
10796 
10797  if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
10798  {
10799  if (event->button() == Qt::LeftButton)
10800  {
10801  // handle selection mechanism:
10802  QVariant details;
10803  QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10804  bool selectionStateChanged = false;
10805  bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
10806  // deselect all other layerables if not additive selection:
10807  if (!additive)
10808  {
10809  foreach (QCPLayer *layer, mLayers)
10810  {
10811  foreach (QCPLayerable *layerable, layer->children())
10812  {
10813  if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
10814  {
10815  bool selChanged = false;
10816  layerable->deselectEvent(&selChanged);
10817  selectionStateChanged |= selChanged;
10818  }
10819  }
10820  }
10821  }
10822  if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10823  {
10824  // a layerable was actually clicked, call its selectEvent:
10825  bool selChanged = false;
10826  clickedLayerable->selectEvent(event, additive, details, &selChanged);
10827  selectionStateChanged |= selChanged;
10828  }
10829  doReplot = true;
10830  if (selectionStateChanged)
10831  emit selectionChangedByUser();
10832  }
10833 
10834  // emit specialized object click signals:
10835  QVariant details;
10836  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
10837  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10838  emit plottableClick(ap, event);
10839  else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10840  emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10841  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10842  emit itemClick(ai, event);
10843  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10844  emit legendClick(lg, 0, event);
10845  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10846  emit legendClick(li->parentLegend(), li, event);
10847  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10848  emit titleClick(event, pt);
10849  }
10850 
10851  // call event of affected layout element:
10852  if (mMouseEventElement)
10853  {
10854  mMouseEventElement->mouseReleaseEvent(event);
10855  mMouseEventElement = 0;
10856  }
10857 
10858  if (doReplot || noAntialiasingOnDrag())
10859  replot();
10860 
10861  QWidget::mouseReleaseEvent(event);
10862 }
10863 
10870 void QCustomPlot::wheelEvent(QWheelEvent *event)
10871 {
10872  emit mouseWheel(event);
10873 
10874  // call event of affected layout element:
10875  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10876  el->wheelEvent(event);
10877 
10878  QWidget::wheelEvent(event);
10879 }
10880 
10889 {
10890  // run through layout phases:
10894 
10895  // draw viewport background pixmap:
10896  drawBackground(painter);
10897 
10898  // draw all layered objects (grid, axes, plottables, items, legend,...):
10899  foreach (QCPLayer *layer, mLayers)
10900  {
10901  foreach (QCPLayerable *child, layer->children())
10902  {
10903  if (child->realVisibility())
10904  {
10905  painter->save();
10906  painter->setClipRect(child->clipRect().translated(0, -1));
10907  child->applyDefaultAntialiasingHint(painter);
10908  child->draw(painter);
10909  painter->restore();
10910  }
10911  }
10912  }
10913 
10914  /* Debug code to draw all layout element rects
10915  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
10916  {
10917  painter->setBrush(Qt::NoBrush);
10918  painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
10919  painter->drawRect(el->rect());
10920  painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
10921  painter->drawRect(el->outerRect());
10922  }
10923  */
10924 }
10925 
10944 {
10945  // Note: background color is handled in individual replot/save functions
10946 
10947  // draw background pixmap (on top of fill, if brush specified):
10948  if (!mBackgroundPixmap.isNull())
10949  {
10950  if (mBackgroundScaled)
10951  {
10952  // check whether mScaledBackground needs to be updated:
10953  QSize scaledSize(mBackgroundPixmap.size());
10954  scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
10955  if (mScaledBackgroundPixmap.size() != scaledSize)
10956  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
10957  painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
10958  } else
10959  {
10960  painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
10961  }
10962  }
10963 }
10964 
10965 
10972 {
10973  if (xAxis == axis)
10974  xAxis = 0;
10975  if (xAxis2 == axis)
10976  xAxis2 = 0;
10977  if (yAxis == axis)
10978  yAxis = 0;
10979  if (yAxis2 == axis)
10980  yAxis2 = 0;
10981 
10982  // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
10983 }
10984 
10991 {
10992  if (this->legend == legend)
10993  this->legend = 0;
10994 }
10995 
11003 {
11004  for (int i=0; i<mLayers.size(); ++i)
11005  mLayers.at(i)->mIndex = i;
11006 }
11007 
11020 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
11021 {
11022  for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
11023  {
11024  const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
11025  double minimumDistance = selectionTolerance()*1.1;
11026  QCPLayerable *minimumDistanceLayerable = 0;
11027  for (int i=layerables.size()-1; i>=0; --i)
11028  {
11029  if (!layerables.at(i)->realVisibility())
11030  continue;
11031  QVariant details;
11032  double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11033  if (dist >= 0 && dist < minimumDistance)
11034  {
11035  minimumDistance = dist;
11036  minimumDistanceLayerable = layerables.at(i);
11037  if (selectionDetails) *selectionDetails = details;
11038  }
11039  }
11040  if (minimumDistance < selectionTolerance())
11041  return minimumDistanceLayerable;
11042  }
11043  return 0;
11044 }
11045 
11057 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
11058 {
11059  QPixmap buffer = toPixmap(width, height, scale);
11060  if (!buffer.isNull())
11061  return buffer.save(fileName, format, quality);
11062  else
11063  return false;
11064 }
11065 
11074 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
11075 {
11076  // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11077  int newWidth, newHeight;
11078  if (width == 0 || height == 0)
11079  {
11080  newWidth = this->width();
11081  newHeight = this->height();
11082  } else
11083  {
11084  newWidth = width;
11085  newHeight = height;
11086  }
11087  int scaledWidth = qRound(scale*newWidth);
11088  int scaledHeight = qRound(scale*newHeight);
11089 
11090  QPixmap result(scaledWidth, scaledHeight);
11091  result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
11092  QCPPainter painter;
11093  painter.begin(&result);
11094  if (painter.isActive())
11095  {
11096  QRect oldViewport = viewport();
11097  setViewport(QRect(0, 0, newWidth, newHeight));
11099  if (!qFuzzyCompare(scale, 1.0))
11100  {
11101  if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
11103  painter.scale(scale, scale);
11104  }
11105  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
11106  painter.fillRect(mViewport, mBackgroundBrush);
11107  draw(&painter);
11108  setViewport(oldViewport);
11109  painter.end();
11110  } else // might happen if pixmap has width or height zero
11111  {
11112  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11113  return QPixmap();
11114  }
11115  return result;
11116 }
11117 
11130 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
11131 {
11132  // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11133  int newWidth, newHeight;
11134  if (width == 0 || height == 0)
11135  {
11136  newWidth = this->width();
11137  newHeight = this->height();
11138  } else
11139  {
11140  newWidth = width;
11141  newHeight = height;
11142  }
11143 
11144  if (painter->isActive())
11145  {
11146  QRect oldViewport = viewport();
11147  setViewport(QRect(0, 0, newWidth, newHeight));
11148  painter->setMode(QCPPainter::pmNoCaching);
11149  // warning: the following is different in toPixmap, because a solid background color is applied there via QPixmap::fill
11150  // here, we need to do this via QPainter::fillRect.
11151  if (mBackgroundBrush.style() != Qt::NoBrush)
11152  painter->fillRect(mViewport, mBackgroundBrush);
11153  draw(painter);
11154  setViewport(oldViewport);
11155  } else
11156  qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11157 }
11158 
11159 
11163 
11197  mLevelCount(350),
11198  mColorInterpolation(ciRGB),
11199  mPeriodic(false),
11200  mColorBufferInvalidated(true)
11201 {
11202  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11203  loadPreset(preset);
11204 }
11205 
11206 /* undocumented operator */
11208 {
11209  return ((other.mLevelCount == this->mLevelCount) &&
11210  (other.mColorInterpolation == this->mColorInterpolation) &&
11211  (other.mPeriodic == this->mPeriodic) &&
11212  (other.mColorStops == this->mColorStops));
11213 }
11214 
11222 {
11223  if (n < 2)
11224  {
11225  qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11226  n = 2;
11227  }
11228  if (n != mLevelCount)
11229  {
11230  mLevelCount = n;
11231  mColorBufferInvalidated = true;
11232  }
11233 }
11234 
11245 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11246 {
11248  mColorBufferInvalidated = true;
11249 }
11250 
11258 {
11259  mColorStops.insert(position, color);
11260  mColorBufferInvalidated = true;
11261 }
11262 
11271 {
11272  if (interpolation != mColorInterpolation)
11273  {
11274  mColorInterpolation = interpolation;
11275  mColorBufferInvalidated = true;
11276  }
11277 }
11278 
11295 {
11296  mPeriodic = enabled;
11297 }
11298 
11311 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
11312 {
11313  // If you change something here, make sure to also adapt ::color()
11314  if (!data)
11315  {
11316  qDebug() << Q_FUNC_INFO << "null pointer given as data";
11317  return;
11318  }
11319  if (!scanLine)
11320  {
11321  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11322  return;
11323  }
11326 
11327  if (!logarithmic)
11328  {
11329  const double posToIndexFactor = (mLevelCount-1)/range.size();
11330  if (mPeriodic)
11331  {
11332  for (int i=0; i<n; ++i)
11333  {
11334  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
11335  if (index < 0)
11336  index += mLevelCount;
11337  scanLine[i] = mColorBuffer.at(index);
11338  }
11339  } else
11340  {
11341  for (int i=0; i<n; ++i)
11342  {
11343  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
11344  if (index < 0)
11345  index = 0;
11346  else if (index >= mLevelCount)
11347  index = mLevelCount-1;
11348  scanLine[i] = mColorBuffer.at(index);
11349  }
11350  }
11351  } else // logarithmic == true
11352  {
11353  if (mPeriodic)
11354  {
11355  for (int i=0; i<n; ++i)
11356  {
11357  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
11358  if (index < 0)
11359  index += mLevelCount;
11360  scanLine[i] = mColorBuffer.at(index);
11361  }
11362  } else
11363  {
11364  for (int i=0; i<n; ++i)
11365  {
11366  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11367  if (index < 0)
11368  index = 0;
11369  else if (index >= mLevelCount)
11370  index = mLevelCount-1;
11371  scanLine[i] = mColorBuffer.at(index);
11372  }
11373  }
11374  }
11375 }
11376 
11386 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11387 {
11388  // If you change something here, make sure to also adapt ::colorize()
11391  int index = 0;
11392  if (!logarithmic)
11393  index = (position-range.lower)*(mLevelCount-1)/range.size();
11394  else
11395  index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11396  if (mPeriodic)
11397  {
11398  index = index % mLevelCount;
11399  if (index < 0)
11400  index += mLevelCount;
11401  } else
11402  {
11403  if (index < 0)
11404  index = 0;
11405  else if (index >= mLevelCount)
11406  index = mLevelCount-1;
11407  }
11408  return mColorBuffer.at(index);
11409 }
11410 
11419 {
11420  clearColorStops();
11421  switch (preset)
11422  {
11423  case gpGrayscale:
11425  setColorStopAt(0, Qt::black);
11426  setColorStopAt(1, Qt::white);
11427  break;
11428  case gpHot:
11430  setColorStopAt(0, QColor(50, 0, 0));
11431  setColorStopAt(0.2, QColor(180, 10, 0));
11432  setColorStopAt(0.4, QColor(245, 50, 0));
11433  setColorStopAt(0.6, QColor(255, 150, 10));
11434  setColorStopAt(0.8, QColor(255, 255, 50));
11435  setColorStopAt(1, QColor(255, 255, 255));
11436  break;
11437  case gpCold:
11439  setColorStopAt(0, QColor(0, 0, 50));
11440  setColorStopAt(0.2, QColor(0, 10, 180));
11441  setColorStopAt(0.4, QColor(0, 50, 245));
11442  setColorStopAt(0.6, QColor(10, 150, 255));
11443  setColorStopAt(0.8, QColor(50, 255, 255));
11444  setColorStopAt(1, QColor(255, 255, 255));
11445  break;
11446  case gpNight:
11448  setColorStopAt(0, QColor(10, 20, 30));
11449  setColorStopAt(1, QColor(250, 255, 250));
11450  break;
11451  case gpCandy:
11453  setColorStopAt(0, QColor(0, 0, 255));
11454  setColorStopAt(1, QColor(255, 250, 250));
11455  break;
11456  case gpGeography:
11458  setColorStopAt(0, QColor(70, 170, 210));
11459  setColorStopAt(0.20, QColor(90, 160, 180));
11460  setColorStopAt(0.25, QColor(45, 130, 175));
11461  setColorStopAt(0.30, QColor(100, 140, 125));
11462  setColorStopAt(0.5, QColor(100, 140, 100));
11463  setColorStopAt(0.6, QColor(130, 145, 120));
11464  setColorStopAt(0.7, QColor(140, 130, 120));
11465  setColorStopAt(0.9, QColor(180, 190, 190));
11466  setColorStopAt(1, QColor(210, 210, 230));
11467  break;
11468  case gpIon:
11470  setColorStopAt(0, QColor(50, 10, 10));
11471  setColorStopAt(0.45, QColor(0, 0, 255));
11472  setColorStopAt(0.8, QColor(0, 255, 255));
11473  setColorStopAt(1, QColor(0, 255, 0));
11474  break;
11475  case gpThermal:
11477  setColorStopAt(0, QColor(0, 0, 50));
11478  setColorStopAt(0.15, QColor(20, 0, 120));
11479  setColorStopAt(0.33, QColor(200, 30, 140));
11480  setColorStopAt(0.6, QColor(255, 100, 0));
11481  setColorStopAt(0.85, QColor(255, 255, 40));
11482  setColorStopAt(1, QColor(255, 255, 255));
11483  break;
11484  case gpPolar:
11486  setColorStopAt(0, QColor(50, 255, 255));
11487  setColorStopAt(0.18, QColor(10, 70, 255));
11488  setColorStopAt(0.28, QColor(10, 10, 190));
11489  setColorStopAt(0.5, QColor(0, 0, 0));
11490  setColorStopAt(0.72, QColor(190, 10, 10));
11491  setColorStopAt(0.82, QColor(255, 70, 10));
11492  setColorStopAt(1, QColor(255, 255, 50));
11493  break;
11494  case gpSpectrum:
11496  setColorStopAt(0, QColor(50, 0, 50));
11497  setColorStopAt(0.15, QColor(0, 0, 255));
11498  setColorStopAt(0.35, QColor(0, 255, 255));
11499  setColorStopAt(0.6, QColor(255, 255, 0));
11500  setColorStopAt(0.75, QColor(255, 30, 0));
11501  setColorStopAt(1, QColor(50, 0, 0));
11502  break;
11503  case gpJet:
11505  setColorStopAt(0, QColor(0, 0, 100));
11506  setColorStopAt(0.15, QColor(0, 50, 255));
11507  setColorStopAt(0.35, QColor(0, 255, 255));
11508  setColorStopAt(0.65, QColor(255, 255, 0));
11509  setColorStopAt(0.85, QColor(255, 30, 0));
11510  setColorStopAt(1, QColor(100, 0, 0));
11511  break;
11512  case gpHues:
11514  setColorStopAt(0, QColor(255, 0, 0));
11515  setColorStopAt(1.0/3.0, QColor(0, 0, 255));
11516  setColorStopAt(2.0/3.0, QColor(0, 255, 0));
11517  setColorStopAt(1, QColor(255, 0, 0));
11518  break;
11519  case cbBuGn:
11521  setColorStopAt(1, QColor(247,252,253));
11522  setColorStopAt(7.0/8.0, QColor(229,245,249));
11523  setColorStopAt(6.0/8.0, QColor(204,236,230));
11524  setColorStopAt(5.0/8.0, QColor(153,216,201));
11525  setColorStopAt(4.0/8.0, QColor(102,194,164));
11526  setColorStopAt(3.0/8.0, QColor(65,174,118));
11527  setColorStopAt(2.0/8.0, QColor(35,139,69));
11528  setColorStopAt(1.0/8.0, QColor(0,109,44));
11529  setColorStopAt(0, QColor(0,68,27));
11530  break;
11531  case cbBuPu:
11533  setColorStopAt(1, QColor(247,252,253));
11534  setColorStopAt(7.0/8.0, QColor(224,236,244));
11535  setColorStopAt(6.0/8.0, QColor(191,211,230));
11536  setColorStopAt(5.0/8.0, QColor(158,188,218));
11537  setColorStopAt(4.0/8.0, QColor(140,150,198));
11538  setColorStopAt(3.0/8.0, QColor(140,107,177));
11539  setColorStopAt(2.0/8.0, QColor(136,65,157));
11540  setColorStopAt(1.0/8.0, QColor(129,15,124));
11541  setColorStopAt(0, QColor(77,0,75));
11542  break;
11543  case cbGnBu:
11545  setColorStopAt(1, QColor(247,252,240));
11546  setColorStopAt(7.0/8.0, QColor(224,243,219));
11547  setColorStopAt(6.0/8.0, QColor(204,235,197));
11548  setColorStopAt(5.0/8.0, QColor(168,221,181));
11549  setColorStopAt(4.0/8.0, QColor(123,204,196));
11550  setColorStopAt(3.0/8.0, QColor(78,179,211));
11551  setColorStopAt(2.0/8.0, QColor(43,140,190));
11552  setColorStopAt(1.0/8.0, QColor(8,104,172));
11553  setColorStopAt(0, QColor(8,64,129));
11554  break;
11555  case cbOrRd:
11557  setColorStopAt(1, QColor(255,247,236));
11558  setColorStopAt(7.0/8.0, QColor(254,232,200));
11559  setColorStopAt(6.0/8.0, QColor(253,212,158));
11560  setColorStopAt(5.0/8.0, QColor(253,187,132));
11561  setColorStopAt(4.0/8.0, QColor(252,141,89));
11562  setColorStopAt(3.0/8.0, QColor(239,101,72));
11563  setColorStopAt(2.0/8.0, QColor(215,48,31));
11564  setColorStopAt(1.0/8.0, QColor(179,0,0));
11565  setColorStopAt(0, QColor(127,0,0));
11566  break;
11567  case cbPuBu:
11569  setColorStopAt(1, QColor(255,247,251));
11570  setColorStopAt(7.0/8.0, QColor(236,231,242));
11571  setColorStopAt(6.0/8.0, QColor(208,209,230));
11572  setColorStopAt(5.0/8.0, QColor(166,189,219));
11573  setColorStopAt(4.0/8.0, QColor(116,169,207));
11574  setColorStopAt(3.0/8.0, QColor(54,144,192));
11575  setColorStopAt(2.0/8.0, QColor(5,112,176));
11576  setColorStopAt(1.0/8.0, QColor(4,90,141));
11577  setColorStopAt(0, QColor(2,56,88));
11578  break;
11579  case cbPuBuGn:
11581  setColorStopAt(1, QColor(255,247,251));
11582  setColorStopAt(7.0/8.0, QColor(236,226,240));
11583  setColorStopAt(6.0/8.0, QColor(208,209,230));
11584  setColorStopAt(5.0/8.0, QColor(166,189,219));
11585  setColorStopAt(4.0/8.0, QColor(103,169,207));
11586  setColorStopAt(3.0/8.0, QColor(54,144,192));
11587  setColorStopAt(2.0/8.0, QColor(2,129,138));
11588  setColorStopAt(1.0/8.0, QColor(1,108,89));
11589  setColorStopAt(0, QColor(1,70,54));
11590  break;
11591  case cbPuRd:
11593  setColorStopAt(1, QColor(247,244,249));
11594  setColorStopAt(7.0/8.0, QColor(231,225,239));
11595  setColorStopAt(6.0/8.0, QColor(212,185,218));
11596  setColorStopAt(5.0/8.0, QColor(201,148,199));
11597  setColorStopAt(4.0/8.0, QColor(223,101,176));
11598  setColorStopAt(3.0/8.0, QColor(231,41,138));
11599  setColorStopAt(2.0/8.0, QColor(206,18,86));
11600  setColorStopAt(1.0/8.0, QColor(152,0,67));
11601  setColorStopAt(0, QColor(103,0,31));
11602  break;
11603  case cbRdPu:
11605  setColorStopAt(1, QColor(255,247,243));
11606  setColorStopAt(7.0/8.0, QColor(253,224,221));
11607  setColorStopAt(6.0/8.0, QColor(252,197,192));
11608  setColorStopAt(5.0/8.0, QColor(250,159,181));
11609  setColorStopAt(4.0/8.0, QColor(247,104,161));
11610  setColorStopAt(3.0/8.0, QColor(221,52,151));
11611  setColorStopAt(2.0/8.0, QColor(174,1,126));
11612  setColorStopAt(1.0/8.0, QColor(122,1,119));
11613  setColorStopAt(0, QColor(73,0,106));
11614  break;
11615  case cbYlGn:
11617  setColorStopAt(1, QColor(255,255,229));
11618  setColorStopAt(7.0/8.0, QColor(247,252,185));
11619  setColorStopAt(6.0/8.0, QColor(217,240,163));
11620  setColorStopAt(5.0/8.0, QColor(173,221,142));
11621  setColorStopAt(4.0/8.0, QColor(120,198,121));
11622  setColorStopAt(3.0/8.0, QColor(65,171,93));
11623  setColorStopAt(2.0/8.0, QColor(35,132,67));
11624  setColorStopAt(1.0/8.0, QColor(0,104,55));
11625  setColorStopAt(0, QColor(0,69,41));
11626  break;
11627  case cbYlGnBu:
11629  setColorStopAt(1, QColor(255,255,217));
11630  setColorStopAt(7.0/8.0, QColor(237,248,177));
11631  setColorStopAt(6.0/8.0, QColor(199,233,180));
11632  setColorStopAt(5.0/8.0, QColor(127,205,187));
11633  setColorStopAt(4.0/8.0, QColor(65,182,196));
11634  setColorStopAt(3.0/8.0, QColor(29,145,192));
11635  setColorStopAt(2.0/8.0, QColor(34,94,168));
11636  setColorStopAt(1.0/8.0, QColor(37,52,148));
11637  setColorStopAt(0, QColor(8,29,88));
11638  break;
11639  case cbYlOrBr:
11641  setColorStopAt(1, QColor(255,255,229));
11642  setColorStopAt(7.0/8.0, QColor(255,247,188));
11643  setColorStopAt(6.0/8.0, QColor(254,227,145));
11644  setColorStopAt(5.0/8.0, QColor(254,196,79));
11645  setColorStopAt(4.0/8.0, QColor(254,153,41));
11646  setColorStopAt(3.0/8.0, QColor(236,112,20));
11647  setColorStopAt(2.0/8.0, QColor(204,76,2));
11648  setColorStopAt(1.0/8.0, QColor(153,52,4));
11649  setColorStopAt(0, QColor(102,37,6));
11650  break;
11651 
11652  case cbYlOrRd:
11654  setColorStopAt(1, QColor(255,255,204));
11655  setColorStopAt(7.0/8.0, QColor(255,237,160));
11656  setColorStopAt(6.0/8.0, QColor(254,217,118));
11657  setColorStopAt(5.0/8.0, QColor(254,178,76));
11658  setColorStopAt(4.0/8.0, QColor(253,141,60));
11659  setColorStopAt(3.0/8.0, QColor(252,78,42));
11660  setColorStopAt(2.0/8.0, QColor(227,26,28));
11661  setColorStopAt(1.0/8.0, QColor(189,0,38));
11662  setColorStopAt(0, QColor(128,0,38));
11663  break;
11664  case cbBlues:
11666  setColorStopAt(1, QColor(247,251,255));
11667  setColorStopAt(7.0/8.0, QColor(222,235,247));
11668  setColorStopAt(6.0/8.0, QColor(198,219,239));
11669  setColorStopAt(5.0/8.0, QColor(158,202,225));
11670  setColorStopAt(4.0/8.0, QColor(107,174,214));
11671  setColorStopAt(3.0/8.0, QColor(66,146,198));
11672  setColorStopAt(2.0/8.0, QColor(33,113,181));
11673  setColorStopAt(1.0/8.0, QColor(8,81,156));
11674  setColorStopAt(0, QColor(8,48,107));
11675  break;
11676  case cbGreens:
11678  setColorStopAt(1, QColor(247,252,245));
11679  setColorStopAt(7.0/8.0, QColor(229,245,224));
11680  setColorStopAt(6.0/8.0, QColor(199,233,192));
11681  setColorStopAt(5.0/8.0, QColor(161,217,155));
11682  setColorStopAt(4.0/8.0, QColor(116,196,118));
11683  setColorStopAt(3.0/8.0, QColor(65,171,93));
11684  setColorStopAt(2.0/8.0, QColor(35,139,69));
11685  setColorStopAt(1.0/8.0, QColor(0,109,44));
11686  setColorStopAt(0, QColor(0,68,27));
11687  break;
11688  case cbGreys:
11690  setColorStopAt(1, QColor(255,255,255));
11691  setColorStopAt(7.0/8.0, QColor(240,240,240));
11692  setColorStopAt(6.0/8.0, QColor(217,217,217));
11693  setColorStopAt(5.0/8.0, QColor(189,189,189));
11694  setColorStopAt(4.0/8.0, QColor(150,150,150));
11695  setColorStopAt(3.0/8.0, QColor(115,115,115));
11696  setColorStopAt(2.0/8.0, QColor(82,82,82));
11697  setColorStopAt(1.0/8.0, QColor(37,37,37));
11698  setColorStopAt(0, QColor(0,0,0));
11699  break;
11700  case cbOranges:
11702  setColorStopAt(1, QColor(255,245,235));
11703  setColorStopAt(7.0/8.0, QColor(254,230,206));
11704  setColorStopAt(6.0/8.0, QColor(253,208,162));
11705  setColorStopAt(5.0/8.0, QColor(253,174,107));
11706  setColorStopAt(4.0/8.0, QColor(253,141,60));
11707  setColorStopAt(3.0/8.0, QColor(241,105,19));
11708  setColorStopAt(2.0/8.0, QColor(217,72,1));
11709  setColorStopAt(1.0/8.0, QColor(166,54,3));
11710  setColorStopAt(0, QColor(127,39,4));
11711  break;
11712  case cbPurples:
11714  setColorStopAt(1, QColor(252,251,253));
11715  setColorStopAt(7.0/8.0, QColor(239,237,245));
11716  setColorStopAt(6.0/8.0, QColor(218,218,235));
11717  setColorStopAt(5.0/8.0, QColor(188,189,220));
11718  setColorStopAt(4.0/8.0, QColor(158,154,200));
11719  setColorStopAt(3.0/8.0, QColor(128,125,186));
11720  setColorStopAt(2.0/8.0, QColor(106,81,163));
11721  setColorStopAt(1.0/8.0, QColor(84,39,143));
11722  setColorStopAt(0, QColor(63,0,125));
11723  break;
11724  case cbReds:
11726  setColorStopAt(1, QColor(255,245,240));
11727  setColorStopAt(7.0/8.0, QColor(254,224,210));
11728  setColorStopAt(6.0/8.0, QColor(252,187,161));
11729  setColorStopAt(5.0/8.0, QColor(252,146,114));
11730  setColorStopAt(4.0/8.0, QColor(251,106,74));
11731  setColorStopAt(3.0/8.0, QColor(239,59,44));
11732  setColorStopAt(2.0/8.0, QColor(203,24,29));
11733  setColorStopAt(1.0/8.0, QColor(165,15,21));
11734  setColorStopAt(0, QColor(103,0,13));
11735  break;
11736  case cbBrBG:
11738  setColorStopAt(1, QColor(84,48,5));
11739  setColorStopAt(9.0/10.0, QColor(140,81,10));
11740  setColorStopAt(8.0/10.0, QColor(191,129,45));
11741  setColorStopAt(7.0/10.0, QColor(223,194,125));
11742  setColorStopAt(6.0/10.0, QColor(246,232,195));
11743  setColorStopAt(5.0/10.0, QColor(245,245,245));
11744  setColorStopAt(4.0/10.0, QColor(199,234,229));
11745  setColorStopAt(3.0/10.0, QColor(128,205,193));
11746  setColorStopAt(2.0/10.0, QColor(53,151,143));
11747  setColorStopAt(1.0/10.0, QColor(1,102,94));
11748  setColorStopAt(0, QColor(0,60,48));
11749  break;
11750  case cbPiYG:
11752  setColorStopAt(1, QColor(142,1,82));
11753  setColorStopAt(9.0/10.0, QColor(197,27,125));
11754  setColorStopAt(8.0/10.0, QColor(222,119,174));
11755  setColorStopAt(7.0/10.0, QColor(241,182,218));
11756  setColorStopAt(6.0/10.0, QColor(253,224,239));
11757  setColorStopAt(5.0/10.0, QColor(247,247,247));
11758  setColorStopAt(4.0/10.0, QColor(230,245,208));
11759  setColorStopAt(3.0/10.0, QColor(184,225,134));
11760  setColorStopAt(2.0/10.0, QColor(127,188,65));
11761  setColorStopAt(1.0/10.0, QColor(77,146,33));
11762  setColorStopAt(0, QColor(39,100,25));
11763  break;
11764  case cbPRGn:
11766  setColorStopAt(1, QColor(64,0,75));
11767  setColorStopAt(9.0/10.0, QColor(118,42,131));
11768  setColorStopAt(8.0/10.0, QColor(153,112,171));
11769  setColorStopAt(7.0/10.0, QColor(194,165,207));
11770  setColorStopAt(6.0/10.0, QColor(231,212,232));
11771  setColorStopAt(5.0/10.0, QColor(247,247,247));
11772  setColorStopAt(4.0/10.0, QColor(217,240,211));
11773  setColorStopAt(3.0/10.0, QColor(166,219,160));
11774  setColorStopAt(2.0/10.0, QColor(90,174,97));
11775  setColorStopAt(1.0/10.0, QColor(27,120,55));
11776  setColorStopAt(0, QColor(0,68,27));
11777  break;
11778  case cbPuOr:
11780  setColorStopAt(1, QColor(127,59,8));
11781  setColorStopAt(9.0/10.0, QColor(179,88,6));
11782  setColorStopAt(8.0/10.0, QColor(224,130,20));
11783  setColorStopAt(7.0/10.0, QColor(253,184,99));
11784  setColorStopAt(6.0/10.0, QColor(254,224,182));
11785  setColorStopAt(5.0/10.0, QColor(247,247,247));
11786  setColorStopAt(4.0/10.0, QColor(216,218,235));
11787  setColorStopAt(3.0/10.0, QColor(178,171,210));
11788  setColorStopAt(2.0/10.0, QColor(128,115,172));
11789  setColorStopAt(1.0/10.0, QColor(84,39,136));
11790  setColorStopAt(0, QColor(45,0,75));
11791  break;
11792  case cbRdBu:
11794  setColorStopAt(1, QColor(103,0,31));
11795  setColorStopAt(9.0/10.0, QColor(178,24,43));
11796  setColorStopAt(8.0/10.0, QColor(214,96,77));
11797  setColorStopAt(7.0/10.0, QColor(244,165,130));
11798  setColorStopAt(6.0/10.0, QColor(253,219,199));
11799  setColorStopAt(5.0/10.0, QColor(247,247,247));
11800  setColorStopAt(4.0/10.0, QColor(209,229,240));
11801  setColorStopAt(3.0/10.0, QColor(146,197,222));
11802  setColorStopAt(2.0/10.0, QColor(67,147,195));
11803  setColorStopAt(1.0/10.0, QColor(33,102,172));
11804  setColorStopAt(0, QColor(5,48,97));
11805  break;
11806  case cbRdGy:
11808  setColorStopAt(1, QColor(103,0,31));
11809  setColorStopAt(9.0/10.0, QColor(178,24,43));
11810  setColorStopAt(8.0/10.0, QColor(214,96,77));
11811  setColorStopAt(7.0/10.0, QColor(244,165,130));
11812  setColorStopAt(6.0/10.0, QColor(253,219,199));
11813  setColorStopAt(5.0/10.0, QColor(255,255,255));
11814  setColorStopAt(4.0/10.0, QColor(224,224,224));
11815  setColorStopAt(3.0/10.0, QColor(186,186,186));
11816  setColorStopAt(2.0/10.0, QColor(135,135,135));
11817  setColorStopAt(1.0/10.0, QColor(77,77,77));
11818  setColorStopAt(0, QColor(26,26,26));
11819  break;
11820  case cbRdYlBu:
11822  setColorStopAt(1, QColor(165,0,38));
11823  setColorStopAt(9.0/10.0, QColor(215,48,39));
11824  setColorStopAt(8.0/10.0, QColor(244,109,67));
11825  setColorStopAt(7.0/10.0, QColor(253,174,97));
11826  setColorStopAt(6.0/10.0, QColor(254,224,144));
11827  setColorStopAt(5.0/10.0, QColor(255,255,191));
11828  setColorStopAt(4.0/10.0, QColor(224,243,248));
11829  setColorStopAt(3.0/10.0, QColor(171,217,233));
11830  setColorStopAt(2.0/10.0, QColor(116,173,209));
11831  setColorStopAt(1.0/10.0, QColor(69,117,180));
11832  setColorStopAt(0, QColor(49,54,149));
11833  break;
11834  case cbRdYlGn:
11836  setColorStopAt(1, QColor(165,0,38));
11837  setColorStopAt(9.0/10.0, QColor(215,48,39));
11838  setColorStopAt(8.0/10.0, QColor(244,109,67));
11839  setColorStopAt(7.0/10.0, QColor(253,174,97));
11840  setColorStopAt(6.0/10.0, QColor(254,224,139));
11841  setColorStopAt(5.0/10.0, QColor(255,255,191));
11842  setColorStopAt(4.0/10.0, QColor(217,239,139));
11843  setColorStopAt(3.0/10.0, QColor(166,217,106));
11844  setColorStopAt(2.0/10.0, QColor(102,189,99));
11845  setColorStopAt(1.0/10.0, QColor(26,152,80));
11846  setColorStopAt(0, QColor(0,104,55));
11847  break;
11848  case cbSpectral:
11850  setColorStopAt(1, QColor(158,1,66));
11851  setColorStopAt(9.0/10.0, QColor(213,62,79));
11852  setColorStopAt(8.0/10.0, QColor(244,109,67));
11853  setColorStopAt(7.0/10.0, QColor(253,174,97));
11854  setColorStopAt(6.0/10.0, QColor(254,224,139));
11855  setColorStopAt(5.0/10.0, QColor(255,255,191));
11856  setColorStopAt(4.0/10.0, QColor(230,245,152));
11857  setColorStopAt(3.0/10.0, QColor(171,221,164));
11858  setColorStopAt(2.0/10.0, QColor(102,194,165));
11859  setColorStopAt(1.0/10.0, QColor(50,136,189));
11860  setColorStopAt(0, QColor(94,79,162));
11861  break;
11862  case cbCluster9:
11864  setLevelCount(9);
11865  setColorStopAt(1, QColor(228,26,28));
11866  setColorStopAt(7.0/8.0, QColor(55,126,184));
11867  setColorStopAt(6.0/8.0, QColor(77,175,74));
11868  setColorStopAt(5.0/8.0, QColor(152,78,163));
11869  setColorStopAt(4.0/8.0, QColor(255,127,0));
11870  setColorStopAt(3.0/8.0, QColor(255,255,51));
11871  setColorStopAt(2.0/8.0, QColor(166,86,40));
11872  setColorStopAt(1.0/8.0, QColor(247,129,191));
11873  setColorStopAt(0, QColor(153,153,153));
11874  break;
11875  case cbCluster8:
11877  setLevelCount(8);
11878  setColorStopAt(1, QColor(228,26,28));
11879  setColorStopAt(6.0/7.0, QColor(55,126,184));
11880  setColorStopAt(5.0/7.0, QColor(77,175,74));
11881  setColorStopAt(4.0/7.0, QColor(152,78,163));
11882  setColorStopAt(3.0/7.0, QColor(255,127,0));
11883  setColorStopAt(2.0/7.0, QColor(255,255,51));
11884  setColorStopAt(1.0/7.0, QColor(166,86,40));
11885  setColorStopAt(0, QColor(247,129,191));
11886  break;
11887  case cbCluster7:
11889  setLevelCount(7);
11890  setColorStopAt(1, QColor(228,26,28));
11891  setColorStopAt(5.0/6.0, QColor(55,126,184));
11892  setColorStopAt(4.0/6.0, QColor(77,175,74));
11893  setColorStopAt(3.0/6.0, QColor(152,78,163));
11894  setColorStopAt(2.0/6.0, QColor(255,127,0));
11895  setColorStopAt(1.0/6.0, QColor(255,255,51));
11896  setColorStopAt(0, QColor(166,86,40));
11897  break;
11898  case cbCluster6:
11900  setLevelCount(6);
11901  setColorStopAt(1, QColor(228,26,28));
11902  setColorStopAt(4.0/5.0, QColor(55,126,184));
11903  setColorStopAt(3.0/5.0, QColor(77,175,74));
11904  setColorStopAt(2.0/5.0, QColor(152,78,163));
11905  setColorStopAt(1.0/5.0, QColor(255,127,0));
11906  setColorStopAt(0, QColor(255,255,51));
11907  break;
11908  case cbCluster5:
11910  setLevelCount(5);
11911  setColorStopAt(1, QColor(228,26,28));
11912  setColorStopAt(3.0/4.0, QColor(55,126,184));
11913  setColorStopAt(2.0/4.0, QColor(77,175,74));
11914  setColorStopAt(1.0/4.0, QColor(152,78,163));
11915  setColorStopAt(0, QColor(255,127,0));
11916  break;
11917  case cbCluster4:
11919  setLevelCount(4);
11920  setColorStopAt(1, QColor(228,26,28));
11921  setColorStopAt(2.0/3.0, QColor(55,126,184));
11922  setColorStopAt(1.0/3.0, QColor(77,175,74));
11923  setColorStopAt(0, QColor(152,78,163));
11924  break;
11925  case cbCluster3:
11927  setLevelCount(3);
11928  setColorStopAt(1, QColor(228,26,28));
11929  setColorStopAt(1.0/2.0, QColor(55,126,184));
11930  setColorStopAt(0, QColor(77,175,74));
11931  break;
11932  case cbCluster2:
11934  setLevelCount(2);
11935  setColorStopAt(1, QColor(228,26,28));
11936  setColorStopAt(0, QColor(55,126,184));
11937  break;
11938  case vSpectral:
11940  setColorStopAt(0.0/64.0, QColor(0,0,0));
11941  setColorStopAt(1.0/64.0, QColor(0,0,36));
11942  setColorStopAt(2.0/64.0, QColor(0,0,73));
11943  setColorStopAt(3.0/64.0, QColor(0,0,109));
11944  setColorStopAt(4.0/64.0, QColor(0,0,146));
11945  setColorStopAt(5.0/64.0, QColor(0,0,182));
11946  setColorStopAt(6.0/64.0, QColor(0,0,219));
11947  setColorStopAt(7.0/64.0, QColor(0,0,255));
11948  setColorStopAt(8.0/64.0, QColor(0,16,255));
11949  setColorStopAt(9.0/64.0, QColor(0,32,255));
11950  setColorStopAt(10.0/64.0, QColor(0,48,255));
11951  setColorStopAt(11.0/64.0, QColor(0,64,255));
11952  setColorStopAt(12.0/64.0, QColor(0,80,255));
11953  setColorStopAt(13.0/64.0, QColor(0,96,255));
11954  setColorStopAt(14.0/64.0, QColor(0,111,255));
11955  setColorStopAt(15.0/64.0, QColor(0,128,255));
11956  setColorStopAt(16.0/64.0, QColor(0,143,255));
11957  setColorStopAt(17.0/64.0, QColor(0,159,255));
11958  setColorStopAt(18.0/64.0, QColor(0,175,255));
11959  setColorStopAt(19.0/64.0, QColor(0,191,255));
11960  setColorStopAt(20.0/64.0, QColor(0,207,255));
11961  setColorStopAt(21.0/64.0, QColor(0,223,255));
11962  setColorStopAt(22.0/64.0, QColor(0,239,255));
11963  setColorStopAt(23.0/64.0, QColor(0,255,255));
11964  setColorStopAt(24.0/64.0, QColor(16,255,239));
11965  setColorStopAt(25.0/64.0, QColor(32,255,223));
11966  setColorStopAt(26.0/64.0, QColor(48,255,207));
11967  setColorStopAt(27.0/64.0, QColor(64,255,191));
11968  setColorStopAt(28.0/64.0, QColor(80,255,175));
11969  setColorStopAt(29.0/64.0, QColor(96,255,159));
11970  setColorStopAt(30.0/64.0, QColor(111,255,143));
11971  setColorStopAt(31.0/64.0, QColor(128,255,128));
11972  setColorStopAt(32.0/64.0, QColor(143,255,111));
11973  setColorStopAt(33.0/64.0, QColor(159,255,96));
11974  setColorStopAt(34.0/64.0, QColor(175,255,80));
11975  setColorStopAt(35.0/64.0, QColor(191,255,64));
11976  setColorStopAt(36.0/64.0, QColor(207,255,48));
11977  setColorStopAt(37.0/64.0, QColor(223,255,32));
11978  setColorStopAt(38.0/64.0, QColor(239,255,16));
11979  setColorStopAt(39.0/64.0, QColor(255,255,0));
11980  setColorStopAt(40.0/64.0, QColor(255,239,0));
11981  setColorStopAt(41.0/64.0, QColor(255,223,0));
11982  setColorStopAt(42.0/64.0, QColor(255,207,0));
11983  setColorStopAt(43.0/64.0, QColor(255,191,0));
11984  setColorStopAt(44.0/64.0, QColor(255,175,0));
11985  setColorStopAt(45.0/64.0, QColor(255,159,0));
11986  setColorStopAt(46.0/64.0, QColor(255,143,0));
11987  setColorStopAt(47.0/64.0, QColor(255,128,0));
11988  setColorStopAt(48.0/64.0, QColor(255,111,0));
11989  setColorStopAt(49.0/64.0, QColor(255,96,0));
11990  setColorStopAt(50.0/64.0, QColor(255,80,0));
11991  setColorStopAt(51.0/64.0, QColor(255,64,0));
11992  setColorStopAt(52.0/64.0, QColor(255,48,0));
11993  setColorStopAt(53.0/64.0, QColor(255,32,0));
11994  setColorStopAt(54.0/64.0, QColor(255,16,0));
11995  setColorStopAt(55.0/64.0, QColor(255,0,0));
11996  setColorStopAt(56.0/64.0, QColor(255,32,32));
11997  setColorStopAt(57.0/64.0, QColor(255,32,32));
11998  setColorStopAt(58.0/64.0, QColor(255,64,64));
11999  setColorStopAt(59.0/64.0, QColor(255,96,96));
12000  setColorStopAt(60.0/64.0, QColor(255,128,128));
12001  setColorStopAt(61.0/64.0, QColor(255,159,159));
12002  setColorStopAt(62.0/64.0, QColor(255,191,191));
12003  setColorStopAt(63.0/64.0, QColor(255,223,223));
12004  setColorStopAt(64.0/64.0, QColor(255,255,255));
12005  break;
12006  }
12007 }
12008 
12015 {
12016  mColorStops.clear();
12017  mColorBufferInvalidated = true;
12018 }
12019 
12027 {
12028  QCPColorGradient result(*this);
12029  result.clearColorStops();
12030  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
12031  result.setColorStopAt(1.0-it.key(), it.value());
12032  return result;
12033 }
12034 
12041 {
12042  if (mColorBuffer.size() != mLevelCount)
12043  mColorBuffer.resize(mLevelCount);
12044  if (mColorStops.size() > 1)
12045  {
12046  double indexToPosFactor = 1.0/(double)(mLevelCount-1);
12047  for (int i=0; i<mLevelCount; ++i)
12048  {
12049  double position = i*indexToPosFactor;
12050  QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
12051  if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
12052  {
12053  mColorBuffer[i] = (it-1).value().rgb();
12054  } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
12055  {
12056  mColorBuffer[i] = it.value().rgb();
12057  } else // position is in between stops (or on an intermediate stop), interpolate color
12058  {
12059  QMap<double, QColor>::const_iterator high = it;
12060  QMap<double, QColor>::const_iterator low = it-1;
12061  double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
12062  switch (mColorInterpolation)
12063  {
12064  case ciRGB:
12065  {
12066  mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
12067  (1-t)*low.value().green() + t*high.value().green(),
12068  (1-t)*low.value().blue() + t*high.value().blue());
12069  break;
12070  }
12071  case ciHSV:
12072  {
12073  QColor lowHsv = low.value().toHsv();
12074  QColor highHsv = high.value().toHsv();
12075  double hue = 0;
12076  double hueDiff = highHsv.hueF()-lowHsv.hueF();
12077  if (hueDiff > 0.5)
12078  hue = lowHsv.hueF() - t*(1.0-hueDiff);
12079  else if (hueDiff < -0.5)
12080  hue = lowHsv.hueF() + t*(1.0+hueDiff);
12081  else
12082  hue = lowHsv.hueF() + t*hueDiff;
12083  if (hue < 0) hue += 1.0;
12084  else if (hue >= 1.0) hue -= 1.0;
12085  mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
12086  break;
12087  }
12088  }
12089  }
12090  }
12091  } else if (mColorStops.size() == 1)
12092  {
12093  mColorBuffer.fill(mColorStops.constBegin().value().rgb());
12094  } else // mColorStops is empty, fill color buffer with black
12095  {
12096  mColorBuffer.fill(qRgb(0, 0, 0));
12097  }
12098  mColorBufferInvalidated = false;
12099 }
12100 
12101 
12105 
12143 /* start documentation of inline functions */
12144 
12225 /* end documentation of inline functions */
12226 
12231 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
12232  QCPLayoutElement(parentPlot),
12233  mBackgroundBrush(Qt::NoBrush),
12234  mBackgroundScaled(true),
12235  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
12236  mInsetLayout(new QCPLayoutInset),
12237  mRangeDrag(Qt::Horizontal|Qt::Vertical),
12238  mRangeZoom(Qt::Horizontal|Qt::Vertical),
12239  mRangeZoomFactorHorz(0.85),
12240  mRangeZoomFactorVert(0.85),
12241  mDragging(false)
12242 {
12245  mInsetLayout->setParent(this);
12246 
12247  setMinimumSize(50, 50);
12248  setMinimumMargins(QMargins(15, 15, 15, 15));
12249  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
12250  mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
12251  mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
12252  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
12253 
12254  if (setupDefaultAxes)
12255  {
12256  QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
12257  QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
12258  QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
12259  QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
12260  setRangeDragAxes(xAxis, yAxis);
12261  setRangeZoomAxes(xAxis, yAxis);
12262  xAxis2->setVisible(false);
12263  yAxis2->setVisible(false);
12264  xAxis->grid()->setVisible(true);
12265  yAxis->grid()->setVisible(true);
12266  xAxis2->grid()->setVisible(false);
12267  yAxis2->grid()->setVisible(false);
12268  xAxis2->grid()->setZeroLinePen(Qt::NoPen);
12269  yAxis2->grid()->setZeroLinePen(Qt::NoPen);
12270  xAxis2->grid()->setVisible(false);
12271  yAxis2->grid()->setVisible(false);
12272  }
12273 }
12274 
12276 {
12277  delete mInsetLayout;
12278  mInsetLayout = 0;
12279 
12280  QList<QCPAxis*> axesList = axes();
12281  for (int i=0; i<axesList.size(); ++i)
12282  removeAxis(axesList.at(i));
12283 }
12284 
12291 {
12292  return mAxes.value(type).size();
12293 }
12294 
12301 {
12302  QList<QCPAxis*> ax(mAxes.value(type));
12303  if (index >= 0 && index < ax.size())
12304  {
12305  return ax.at(index);
12306  } else
12307  {
12308  qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
12309  return 0;
12310  }
12311 }
12312 
12321 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
12322 {
12323  QList<QCPAxis*> result;
12324  if (types.testFlag(QCPAxis::atLeft))
12325  result << mAxes.value(QCPAxis::atLeft);
12326  if (types.testFlag(QCPAxis::atRight))
12327  result << mAxes.value(QCPAxis::atRight);
12328  if (types.testFlag(QCPAxis::atTop))
12329  result << mAxes.value(QCPAxis::atTop);
12330  if (types.testFlag(QCPAxis::atBottom))
12331  result << mAxes.value(QCPAxis::atBottom);
12332  return result;
12333 }
12334 
12339 QList<QCPAxis*> QCPAxisRect::axes() const
12340 {
12341  QList<QCPAxis*> result;
12342  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
12343  while (it.hasNext())
12344  {
12345  it.next();
12346  result << it.value();
12347  }
12348  return result;
12349 }
12350 
12371 {
12372  QCPAxis *newAxis = axis;
12373  if (!newAxis)
12374  {
12375  newAxis = new QCPAxis(this, type);
12376  } else // user provided existing axis instance, do some sanity checks
12377  {
12378  if (newAxis->axisType() != type)
12379  {
12380  qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
12381  return 0;
12382  }
12383  if (newAxis->axisRect() != this)
12384  {
12385  qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
12386  return 0;
12387  }
12388  if (axes().contains(newAxis))
12389  {
12390  qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
12391  return 0;
12392  }
12393  }
12394  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
12395  {
12396  bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
12397  newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
12398  newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
12399  }
12400  mAxes[type].append(newAxis);
12401  return newAxis;
12402 }
12403 
12412 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
12413 {
12414  QList<QCPAxis*> result;
12415  if (types.testFlag(QCPAxis::atLeft))
12416  result << addAxis(QCPAxis::atLeft);
12417  if (types.testFlag(QCPAxis::atRight))
12418  result << addAxis(QCPAxis::atRight);
12419  if (types.testFlag(QCPAxis::atTop))
12420  result << addAxis(QCPAxis::atTop);
12421  if (types.testFlag(QCPAxis::atBottom))
12422  result << addAxis(QCPAxis::atBottom);
12423  return result;
12424 }
12425 
12434 {
12435  // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
12436  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
12437  while (it.hasNext())
12438  {
12439  it.next();
12440  if (it.value().contains(axis))
12441  {
12442  mAxes[it.key()].removeOne(axis);
12443  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)
12444  parentPlot()->axisRemoved(axis);
12445  delete axis;
12446  return true;
12447  }
12448  }
12449  qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
12450  return false;
12451 }
12452 
12479 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
12480 {
12481  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
12482  if (axisCount(QCPAxis::atBottom) == 0)
12483  xAxis = addAxis(QCPAxis::atBottom);
12484  else
12485  xAxis = axis(QCPAxis::atBottom);
12486 
12487  if (axisCount(QCPAxis::atLeft) == 0)
12488  yAxis = addAxis(QCPAxis::atLeft);
12489  else
12490  yAxis = axis(QCPAxis::atLeft);
12491 
12492  if (axisCount(QCPAxis::atTop) == 0)
12493  xAxis2 = addAxis(QCPAxis::atTop);
12494  else
12495  xAxis2 = axis(QCPAxis::atTop);
12496 
12497  if (axisCount(QCPAxis::atRight) == 0)
12498  yAxis2 = addAxis(QCPAxis::atRight);
12499  else
12500  yAxis2 = axis(QCPAxis::atRight);
12501 
12502  xAxis->setVisible(true);
12503  yAxis->setVisible(true);
12504  xAxis2->setVisible(true);
12505  yAxis2->setVisible(true);
12506  xAxis2->setTickLabels(false);
12507  yAxis2->setTickLabels(false);
12508 
12509  xAxis2->setRange(xAxis->range());
12510  xAxis2->setRangeReversed(xAxis->rangeReversed());
12511  xAxis2->setScaleType(xAxis->scaleType());
12512  xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12513  xAxis2->setTicks(xAxis->ticks());
12514  xAxis2->setAutoTickCount(xAxis->autoTickCount());
12515  xAxis2->setSubTickCount(xAxis->subTickCount());
12516  xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12517  xAxis2->setTickStep(xAxis->tickStep());
12518  xAxis2->setAutoTickStep(xAxis->autoTickStep());
12519  xAxis2->setNumberFormat(xAxis->numberFormat());
12520  xAxis2->setNumberPrecision(xAxis->numberPrecision());
12521  xAxis2->setTickLabelType(xAxis->tickLabelType());
12522  xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12523  xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12524 
12525  yAxis2->setRange(yAxis->range());
12526  yAxis2->setRangeReversed(yAxis->rangeReversed());
12527  yAxis2->setScaleType(yAxis->scaleType());
12528  yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12529  yAxis2->setTicks(yAxis->ticks());
12530  yAxis2->setAutoTickCount(yAxis->autoTickCount());
12531  yAxis2->setSubTickCount(yAxis->subTickCount());
12532  yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12533  yAxis2->setTickStep(yAxis->tickStep());
12534  yAxis2->setAutoTickStep(yAxis->autoTickStep());
12535  yAxis2->setNumberFormat(yAxis->numberFormat());
12536  yAxis2->setNumberPrecision(yAxis->numberPrecision());
12537  yAxis2->setTickLabelType(yAxis->tickLabelType());
12538  yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12539  yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12540 
12541  if (connectRanges)
12542  {
12543  connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
12544  connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
12545  }
12546 }
12547 
12556 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
12557 {
12558  // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
12559  QList<QCPAbstractPlottable*> result;
12560  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
12561  {
12562  if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12563  result.append(mParentPlot->mPlottables.at(i));
12564  }
12565  return result;
12566 }
12567 
12576 QList<QCPGraph*> QCPAxisRect::graphs() const
12577 {
12578  // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
12579  QList<QCPGraph*> result;
12580  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
12581  {
12582  if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12583  result.append(mParentPlot->mGraphs.at(i));
12584  }
12585  return result;
12586 }
12587 
12598 QList<QCPAbstractItem *> QCPAxisRect::items() const
12599 {
12600  // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
12601  // and miss those items that have this axis rect as clipAxisRect.
12602  QList<QCPAbstractItem*> result;
12603  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
12604  {
12605  if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
12606  {
12607  result.append(mParentPlot->mItems.at(itemId));
12608  continue;
12609  }
12610  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
12611  for (int posId=0; posId<positions.size(); ++posId)
12612  {
12613  if (positions.at(posId)->axisRect() == this ||
12614  positions.at(posId)->keyAxis()->axisRect() == this ||
12615  positions.at(posId)->valueAxis()->axisRect() == this)
12616  {
12617  result.append(mParentPlot->mItems.at(itemId));
12618  break;
12619  }
12620  }
12621  }
12622  return result;
12623 }
12624 
12634 {
12635  QCPLayoutElement::update(phase);
12636 
12637  switch (phase)
12638  {
12639  case upPreparation:
12640  {
12641  QList<QCPAxis*> allAxes = axes();
12642  for (int i=0; i<allAxes.size(); ++i)
12643  allAxes.at(i)->setupTickVectors();
12644  break;
12645  }
12646  case upLayout:
12647  {
12649  break;
12650  }
12651  default: break;
12652  }
12653 
12654  // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
12655  mInsetLayout->update(phase);
12656 }
12657 
12658 /* inherits documentation from base class */
12659 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
12660 {
12661  QList<QCPLayoutElement*> result;
12662  if (mInsetLayout)
12663  {
12664  result << mInsetLayout;
12665  if (recursive)
12666  result << mInsetLayout->elements(recursive);
12667  }
12668  return result;
12669 }
12670 
12671 /* inherits documentation from base class */
12673 {
12674  painter->setAntialiasing(false);
12675 }
12676 
12677 /* inherits documentation from base class */
12679 {
12680  drawBackground(painter);
12681 }
12682 
12698 void QCPAxisRect::setBackground(const QPixmap &pm)
12699 {
12700  mBackgroundPixmap = pm;
12701  mScaledBackgroundPixmap = QPixmap();
12702 }
12703 
12717 void QCPAxisRect::setBackground(const QBrush &brush)
12718 {
12719  mBackgroundBrush = brush;
12720 }
12721 
12729 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
12730 {
12731  mBackgroundPixmap = pm;
12732  mScaledBackgroundPixmap = QPixmap();
12733  mBackgroundScaled = scaled;
12734  mBackgroundScaledMode = mode;
12735 }
12736 
12748 {
12749  mBackgroundScaled = scaled;
12750 }
12751 
12757 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12758 {
12759  mBackgroundScaledMode = mode;
12760 }
12761 
12767 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12768 {
12769  return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12770 }
12771 
12777 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12778 {
12779  return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12780 }
12781 
12787 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12788 {
12789  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12790 }
12791 
12808 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12809 {
12810  mRangeDrag = orientations;
12811 }
12812 
12828 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12829 {
12830  mRangeZoom = orientations;
12831 }
12832 
12839 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
12840 {
12841  mRangeDragHorzAxis = horizontal;
12842  mRangeDragVertAxis = vertical;
12843 }
12844 
12852 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
12853 {
12854  mRangeZoomHorzAxis = horizontal;
12855  mRangeZoomVertAxis = vertical;
12856 }
12857 
12868 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12869 {
12870  mRangeZoomFactorHorz = horizontalFactor;
12871  mRangeZoomFactorVert = verticalFactor;
12872 }
12873 
12879 {
12880  mRangeZoomFactorHorz = factor;
12881  mRangeZoomFactorVert = factor;
12882 }
12883 
12903 {
12904  // draw background fill:
12905  if (mBackgroundBrush != Qt::NoBrush)
12906  painter->fillRect(mRect, mBackgroundBrush);
12907 
12908  // draw background pixmap (on top of fill, if brush specified):
12909  if (!mBackgroundPixmap.isNull())
12910  {
12911  if (mBackgroundScaled)
12912  {
12913  // check whether mScaledBackground needs to be updated:
12914  QSize scaledSize(mBackgroundPixmap.size());
12915  scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12916  if (mScaledBackgroundPixmap.size() != scaledSize)
12917  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12918  painter->drawPixmap(mRect.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
12919  } else
12920  {
12921  painter->drawPixmap(mRect.topLeft(), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
12922  }
12923  }
12924 }
12925 
12937 {
12938  const QList<QCPAxis*> axesList = mAxes.value(type);
12939  if (axesList.isEmpty())
12940  return;
12941 
12942  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
12943  for (int i=1; i<axesList.size(); ++i)
12944  {
12945  int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
12946  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)
12947  {
12948  if (!isFirstVisible)
12949  offset += axesList.at(i)->tickLengthIn();
12950  isFirstVisible = false;
12951  }
12952  axesList.at(i)->setOffset(offset);
12953  }
12954 }
12955 
12956 /* inherits documentation from base class */
12958 {
12959  if (!mAutoMargins.testFlag(side))
12960  qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12961 
12963 
12964  // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
12965  const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
12966  if (axesList.size() > 0)
12967  return axesList.last()->offset() + axesList.last()->calculateMargin();
12968  else
12969  return 0;
12970 }
12971 
12983 void QCPAxisRect::mousePressEvent(QMouseEvent *event)
12984 {
12985  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)
12986  if (event->buttons() & Qt::LeftButton)
12987  {
12988  mDragging = true;
12989  // initialize antialiasing backup in case we start dragging:
12991  {
12994  }
12995  // Mouse range dragging interaction:
12996  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12997  {
12998  if (mRangeDragHorzAxis)
12999  mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
13000  if (mRangeDragVertAxis)
13001  mDragStartVertRange = mRangeDragVertAxis.data()->range();
13002  }
13003  }
13004 }
13005 
13013 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
13014 {
13015  // Mouse range dragging interaction:
13016  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
13017  {
13018  if (mRangeDrag.testFlag(Qt::Horizontal))
13019  {
13020  if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
13021  {
13022  if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
13023  {
13024  double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
13025  rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
13026  } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
13027  {
13028  double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
13029  rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
13030  }
13031  }
13032  }
13033  if (mRangeDrag.testFlag(Qt::Vertical))
13034  {
13035  if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
13036  {
13037  if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
13038  {
13039  double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
13040  rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
13041  } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
13042  {
13043  double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
13044  rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
13045  }
13046  }
13047  }
13048  if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
13049  {
13052  mParentPlot->replot();
13053  }
13054  }
13055 }
13056 
13057 /* inherits documentation from base class */
13058 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
13059 {
13060  Q_UNUSED(event)
13061  mDragging = false;
13063  {
13066  }
13067 }
13068 
13083 void QCPAxisRect::wheelEvent(QWheelEvent *event)
13084 {
13085  // Mouse range zooming interaction:
13086  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
13087  {
13088  if (mRangeZoom != 0)
13089  {
13090  double factor;
13091  double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
13092  if (mRangeZoom.testFlag(Qt::Horizontal))
13093  {
13094  factor = qPow(mRangeZoomFactorHorz, wheelSteps);
13095  if (mRangeZoomHorzAxis.data())
13096  mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
13097  }
13098  if (mRangeZoom.testFlag(Qt::Vertical))
13099  {
13100  factor = qPow(mRangeZoomFactorVert, wheelSteps);
13101  if (mRangeZoomVertAxis.data())
13102  mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
13103  }
13104  mParentPlot->replot();
13105  }
13106  }
13107 }
13108 
13109 
13113 
13138 /* start of documentation of signals */
13139 
13146 /* end of documentation of signals */
13147 
13153  QCPLayoutElement(parent->parentPlot()),
13154  mParentLegend(parent),
13155  mFont(parent->font()),
13156  mTextColor(parent->textColor()),
13157  mSelectedFont(parent->selectedFont()),
13158  mSelectedTextColor(parent->selectedTextColor()),
13159  mSelectable(true),
13160  mSelected(false)
13161 {
13162  setLayer(QLatin1String("legend"));
13163  setMargins(QMargins(8, 2, 8, 2));
13164 }
13165 
13172 {
13173  mFont = font;
13174 }
13175 
13181 void QCPAbstractLegendItem::setTextColor(const QColor &color)
13182 {
13183  mTextColor = color;
13184 }
13185 
13193 {
13194  mSelectedFont = font;
13195 }
13196 
13204 {
13205  mSelectedTextColor = color;
13206 }
13207 
13214 {
13215  if (mSelectable != selectable)
13216  {
13219  }
13220 }
13221 
13231 {
13232  if (mSelected != selected)
13233  {
13234  mSelected = selected;
13236  }
13237 }
13238 
13239 /* inherits documentation from base class */
13240 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13241 {
13242  Q_UNUSED(details)
13243  if (!mParentPlot) return -1;
13244  if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
13245  return -1;
13246 
13247  if (mRect.contains(pos.toPoint()))
13248  return mParentPlot->selectionTolerance()*0.99;
13249  else
13250  return -1;
13251 }
13252 
13253 /* inherits documentation from base class */
13255 {
13257 }
13258 
13259 /* inherits documentation from base class */
13261 {
13262  return mOuterRect;
13263 }
13264 
13265 /* inherits documentation from base class */
13266 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13267 {
13268  Q_UNUSED(event)
13269  Q_UNUSED(details)
13271  {
13272  bool selBefore = mSelected;
13273  setSelected(additive ? !mSelected : true);
13274  if (selectionStateChanged)
13275  *selectionStateChanged = mSelected != selBefore;
13276  }
13277 }
13278 
13279 /* inherits documentation from base class */
13280 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
13281 {
13283  {
13284  bool selBefore = mSelected;
13285  setSelected(false);
13286  if (selectionStateChanged)
13287  *selectionStateChanged = mSelected != selBefore;
13288  }
13289 }
13290 
13294 
13329  QCPAbstractLegendItem(parent),
13330  mPlottable(plottable)
13331 {
13332 }
13333 
13340 {
13342 }
13343 
13350 {
13352 }
13353 
13360 {
13361  return mSelected ? mSelectedFont : mFont;
13362 }
13363 
13371 {
13372  if (!mPlottable) return;
13373  painter->setFont(getFont());
13374  painter->setPen(QPen(getTextColor()));
13375  QSizeF iconSize = mParentLegend->iconSize();
13376  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
13377  QRectF iconRect(mRect.topLeft(), iconSize);
13378  int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
13379  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
13380  // draw icon:
13381  painter->save();
13382  painter->setClipRect(iconRect, Qt::IntersectClip);
13383  mPlottable->drawLegendIcon(painter, iconRect);
13384  painter->restore();
13385  // draw icon border:
13386  if (getIconBorderPen().style() != Qt::NoPen)
13387  {
13388  painter->setPen(getIconBorderPen());
13389  painter->setBrush(Qt::NoBrush);
13390  painter->drawRect(iconRect);
13391  }
13392 }
13393 
13400 {
13401  if (!mPlottable) return QSize();
13402  QSize result(0, 0);
13403  QRect textRect;
13404  QFontMetrics fontMetrics(getFont());
13405  QSize iconSize = mParentLegend->iconSize();
13406  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
13407  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
13408  result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
13409  return result;
13410 }
13411 
13412 
13416 
13441 /* start of documentation of signals */
13442 
13450 /* end of documentation of signals */
13451 
13459 {
13460  setRowSpacing(0);
13461  setColumnSpacing(10);
13462  setMargins(QMargins(2, 3, 2, 2));
13463  setAntialiased(false);
13464  setIconSize(32, 18);
13465 
13466  setIconTextPadding(7);
13467 
13468  setSelectableParts(spLegendBox | spItems);
13469  setSelectedParts(spNone);
13470 
13471  setBorderPen(QPen(Qt::black));
13472  setSelectedBorderPen(QPen(Qt::blue, 2));
13473  setIconBorderPen(Qt::NoPen);
13474  setSelectedIconBorderPen(QPen(Qt::blue, 2));
13475  setBrush(Qt::white);
13476  setSelectedBrush(Qt::white);
13477  setTextColor(Qt::black);
13478  setSelectedTextColor(Qt::blue);
13479 }
13480 
13482 {
13483  clearItems();
13484  if (qobject_cast<QCustomPlot*>(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot)
13485  mParentPlot->legendRemoved(this);
13486 }
13487 
13488 /* no doc for getter, see setSelectedParts */
13489 QCPLegend::SelectableParts QCPLegend::selectedParts() const
13490 {
13491  // check whether any legend elements selected, if yes, add spItems to return value
13492  bool hasSelectedItems = false;
13493  for (int i=0; i<itemCount(); ++i)
13494  {
13495  if (item(i) && item(i)->selected())
13496  {
13497  hasSelectedItems = true;
13498  break;
13499  }
13500  }
13501  if (hasSelectedItems)
13502  return mSelectedParts | spItems;
13503  else
13504  return mSelectedParts & ~spItems;
13505 }
13506 
13510 void QCPLegend::setBorderPen(const QPen &pen)
13511 {
13512  mBorderPen = pen;
13513 }
13514 
13518 void QCPLegend::setBrush(const QBrush &brush)
13519 {
13520  mBrush = brush;
13521 }
13522 
13532 void QCPLegend::setFont(const QFont &font)
13533 {
13534  mFont = font;
13535  for (int i=0; i<itemCount(); ++i)
13536  {
13537  if (item(i))
13538  item(i)->setFont(mFont);
13539  }
13540 }
13541 
13551 void QCPLegend::setTextColor(const QColor &color)
13552 {
13553  mTextColor = color;
13554  for (int i=0; i<itemCount(); ++i)
13555  {
13556  if (item(i))
13557  item(i)->setTextColor(color);
13558  }
13559 }
13560 
13565 void QCPLegend::setIconSize(const QSize &size)
13566 {
13567  mIconSize = size;
13568 }
13569 
13572 void QCPLegend::setIconSize(int width, int height)
13573 {
13574  mIconSize.setWidth(width);
13575  mIconSize.setHeight(height);
13576 }
13577 
13584 {
13585  mIconTextPadding = padding;
13586 }
13587 
13594 void QCPLegend::setIconBorderPen(const QPen &pen)
13595 {
13596  mIconBorderPen = pen;
13597 }
13598 
13609 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13610 {
13611  if (mSelectableParts != selectable)
13612  {
13613  mSelectableParts = selectable;
13614  emit selectableChanged(mSelectableParts);
13615  }
13616 }
13617 
13639 void QCPLegend::setSelectedParts(const SelectableParts &selected)
13640 {
13641  SelectableParts newSelected = selected;
13642  mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
13643 
13644  if (mSelectedParts != newSelected)
13645  {
13646  if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
13647  {
13648  qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
13649  newSelected &= ~spItems;
13650  }
13651  if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
13652  {
13653  for (int i=0; i<itemCount(); ++i)
13654  {
13655  if (item(i))
13656  item(i)->setSelected(false);
13657  }
13658  }
13659  mSelectedParts = newSelected;
13660  emit selectionChanged(mSelectedParts);
13661  }
13662 }
13663 
13670 void QCPLegend::setSelectedBorderPen(const QPen &pen)
13671 {
13672  mSelectedBorderPen = pen;
13673 }
13674 
13681 {
13682  mSelectedIconBorderPen = pen;
13683 }
13684 
13691 void QCPLegend::setSelectedBrush(const QBrush &brush)
13692 {
13693  mSelectedBrush = brush;
13694 }
13695 
13704 {
13705  mSelectedFont = font;
13706  for (int i=0; i<itemCount(); ++i)
13707  {
13708  if (item(i))
13709  item(i)->setSelectedFont(font);
13710  }
13711 }
13712 
13720 void QCPLegend::setSelectedTextColor(const QColor &color)
13721 {
13722  mSelectedTextColor = color;
13723  for (int i=0; i<itemCount(); ++i)
13724  {
13725  if (item(i))
13726  item(i)->setSelectedTextColor(color);
13727  }
13728 }
13729 
13736 {
13737  return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13738 }
13739 
13747 {
13748  for (int i=0; i<itemCount(); ++i)
13749  {
13750  if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
13751  {
13752  if (pli->plottable() == plottable)
13753  return pli;
13754  }
13755  }
13756  return 0;
13757 }
13758 
13764 {
13765  return elementCount();
13766 }
13767 
13772 {
13773  for (int i=0; i<itemCount(); ++i)
13774  {
13775  if (item == this->item(i))
13776  return true;
13777  }
13778  return false;
13779 }
13780 
13788 {
13789  return itemWithPlottable(plottable);
13790 }
13791 
13800 {
13801  if (!hasItem(item))
13802  {
13803  return addElement(rowCount(), 0, item);
13804  } else
13805  return false;
13806 }
13807 
13815 bool QCPLegend::removeItem(int index)
13816 {
13817  if (QCPAbstractLegendItem *ali = item(index))
13818  {
13819  bool success = remove(ali);
13820  simplify();
13821  return success;
13822  } else
13823  return false;
13824 }
13825 
13835 {
13836  bool success = remove(item);
13837  simplify();
13838  return success;
13839 }
13840 
13845 {
13846  for (int i=itemCount()-1; i>=0; --i)
13847  removeItem(i);
13848 }
13849 
13856 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
13857 {
13858  QList<QCPAbstractLegendItem*> result;
13859  for (int i=0; i<itemCount(); ++i)
13860  {
13861  if (QCPAbstractLegendItem *ali = item(i))
13862  {
13863  if (ali->selected())
13864  result.append(ali);
13865  }
13866  }
13867  return result;
13868 }
13869 
13884 {
13886 }
13887 
13894 {
13895  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
13896 }
13897 
13903 QBrush QCPLegend::getBrush() const
13904 {
13905  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13906 }
13907 
13914 {
13915  // draw background rect:
13916  painter->setBrush(getBrush());
13917  painter->setPen(getBorderPen());
13918  painter->drawRect(mOuterRect);
13919 }
13920 
13921 /* inherits documentation from base class */
13922 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13923 {
13924  if (!mParentPlot) return -1;
13925  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
13926  return -1;
13927 
13928  if (mOuterRect.contains(pos.toPoint()))
13929  {
13930  if (details) details->setValue(spLegendBox);
13931  return mParentPlot->selectionTolerance()*0.99;
13932  }
13933  return -1;
13934 }
13935 
13936 /* inherits documentation from base class */
13937 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13938 {
13939  Q_UNUSED(event)
13940  mSelectedParts = selectedParts(); // in case item selection has changed
13941  if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
13942  {
13943  SelectableParts selBefore = mSelectedParts;
13944  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)
13945  if (selectionStateChanged)
13946  *selectionStateChanged = mSelectedParts != selBefore;
13947  }
13948 }
13949 
13950 /* inherits documentation from base class */
13951 void QCPLegend::deselectEvent(bool *selectionStateChanged)
13952 {
13953  mSelectedParts = selectedParts(); // in case item selection has changed
13954  if (mSelectableParts.testFlag(spLegendBox))
13955  {
13956  SelectableParts selBefore = mSelectedParts;
13957  setSelectedParts(selectedParts() & ~spLegendBox);
13958  if (selectionStateChanged)
13959  *selectionStateChanged = mSelectedParts != selBefore;
13960  }
13961 }
13962 
13963 /* inherits documentation from base class */
13965 {
13966  return QCP::iSelectLegend;
13967 }
13968 
13969 /* inherits documentation from base class */
13971 {
13972  return QCP::iSelectLegend;
13973 }
13974 
13975 /* inherits documentation from base class */
13977 {
13978  Q_UNUSED(parentPlot)
13979 }
13980 
13981 
13985 
14004 /* start documentation of signals */
14005 
14014 /* end documentation of signals */
14015 
14022  QCPLayoutElement(parentPlot),
14023  mFont(QFont(QLatin1String("sans serif"), 13*1.5, QFont::Bold)),
14024  mTextColor(Qt::black),
14025  mSelectedFont(QFont(QLatin1String("sans serif"), 13*1.6, QFont::Bold)),
14026  mSelectedTextColor(Qt::blue),
14027  mSelectable(false),
14028  mSelected(false)
14029 {
14030  if (parentPlot)
14031  {
14032  setLayer(parentPlot->currentLayer());
14033  mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
14034  mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
14035  }
14036  setMargins(QMargins(5, 5, 5, 0));
14037 }
14038 
14044  QCPLayoutElement(parentPlot),
14045  mText(text),
14046  mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
14047  mTextColor(Qt::black),
14048  mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
14049  mSelectedTextColor(Qt::blue),
14050  mSelectable(false),
14051  mSelected(false)
14052 {
14053  setLayer(QLatin1String("axes"));
14054  setMargins(QMargins(5, 5, 5, 0));
14055 }
14056 
14062 void QCPPlotTitle::setText(const QString &text)
14063 {
14064  mText = text;
14065 }
14066 
14072 void QCPPlotTitle::setFont(const QFont &font)
14073 {
14074  mFont = font;
14075 }
14076 
14082 void QCPPlotTitle::setTextColor(const QColor &color)
14083 {
14084  mTextColor = color;
14085 }
14086 
14093 {
14094  mSelectedFont = font;
14095 }
14096 
14102 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
14103 {
14104  mSelectedTextColor = color;
14105 }
14106 
14114 {
14115  if (mSelectable != selectable)
14116  {
14119  }
14120 }
14121 
14130 {
14131  if (mSelected != selected)
14132  {
14133  mSelected = selected;
14135  }
14136 }
14137 
14138 /* inherits documentation from base class */
14140 {
14142 }
14143 
14144 /* inherits documentation from base class */
14146 {
14147  painter->setFont(mainFont());
14148  painter->setPen(QPen(mainTextColor()));
14149  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
14150 }
14151 
14152 /* inherits documentation from base class */
14154 {
14155  QFontMetrics metrics(mFont);
14156  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
14157  result.rwidth() += mMargins.left() + mMargins.right();
14158  result.rheight() += mMargins.top() + mMargins.bottom();
14159  return result;
14160 }
14161 
14162 /* inherits documentation from base class */
14164 {
14165  QFontMetrics metrics(mFont);
14166  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
14167  result.rheight() += mMargins.top() + mMargins.bottom();
14168  result.setWidth(QWIDGETSIZE_MAX);
14169  return result;
14170 }
14171 
14172 /* inherits documentation from base class */
14173 void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
14174 {
14175  Q_UNUSED(event)
14176  Q_UNUSED(details)
14177  if (mSelectable)
14178  {
14179  bool selBefore = mSelected;
14180  setSelected(additive ? !mSelected : true);
14181  if (selectionStateChanged)
14182  *selectionStateChanged = mSelected != selBefore;
14183  }
14184 }
14185 
14186 /* inherits documentation from base class */
14187 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
14188 {
14189  if (mSelectable)
14190  {
14191  bool selBefore = mSelected;
14192  setSelected(false);
14193  if (selectionStateChanged)
14194  *selectionStateChanged = mSelected != selBefore;
14195  }
14196 }
14197 
14198 /* inherits documentation from base class */
14199 double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
14200 {
14201  Q_UNUSED(details)
14202  if (onlySelectable && !mSelectable)
14203  return -1;
14204 
14205  if (mTextBoundingRect.contains(pos.toPoint()))
14206  return mParentPlot->selectionTolerance()*0.99;
14207  else
14208  return -1;
14209 }
14210 
14217 {
14218  return mSelected ? mSelectedFont : mFont;
14219 }
14220 
14227 {
14229 }
14230 
14231 
14235 
14284 /* start documentation of inline functions */
14285 
14299 /* end documentation of signals */
14300 /* start documentation of signals */
14301 
14323 /* end documentation of signals */
14324 
14329  QCPLayoutElement(parentPlot),
14330  mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
14331  mDataScaleType(QCPAxis::stLinear),
14332  mBarWidth(20),
14333  mAxisRect(new QCPColorScaleAxisRectPrivate(this))
14334 {
14335  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)
14337  setDataRange(QCPRange(0, 6));
14338 }
14339 
14341 {
14342  delete mAxisRect;
14343 }
14344 
14345 /* undocumented getter */
14346 QString QCPColorScale::label() const
14347 {
14348  if (!mColorAxis)
14349  {
14350  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
14351  return QString();
14352  }
14353 
14354  return mColorAxis.data()->label();
14355 }
14356 
14357 /* undocumented getter */
14359 {
14360  if (!mAxisRect)
14361  {
14362  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14363  return false;
14364  }
14365 
14366  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
14367  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
14368  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
14369 }
14370 
14371 /* undocumented getter */
14373 {
14374  if (!mAxisRect)
14375  {
14376  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14377  return false;
14378  }
14379 
14380  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
14381  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
14382  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
14383 }
14384 
14393 {
14394  if (!mAxisRect)
14395  {
14396  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14397  return;
14398  }
14399  if (mType != type)
14400  {
14401  mType = type;
14402  QCPRange rangeTransfer(0, 6);
14403  double logBaseTransfer = 10;
14404  QString labelTransfer;
14405  // revert some settings on old axis:
14406  if (mColorAxis)
14407  {
14408  rangeTransfer = mColorAxis.data()->range();
14409  labelTransfer = mColorAxis.data()->label();
14410  logBaseTransfer = mColorAxis.data()->scaleLogBase();
14411  mColorAxis.data()->setLabel(QString());
14412  disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
14413  disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
14414  }
14415  foreach (QCPAxis::AxisType atype, QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop)
14416  {
14417  mAxisRect.data()->axis(atype)->setTicks(atype == mType);
14418  mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
14419  }
14420  // set new mColorAxis pointer:
14421  mColorAxis = mAxisRect.data()->axis(mType);
14422  // transfer settings to new axis:
14423  mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
14424  mColorAxis.data()->setLabel(labelTransfer);
14425  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
14426  connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
14427  connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
14428  mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
14429  QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
14430  }
14431 }
14432 
14443 {
14444  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
14445  {
14447  if (mColorAxis)
14448  mColorAxis.data()->setRange(mDataRange);
14450  }
14451 }
14452 
14464 {
14465  if (mDataScaleType != scaleType)
14466  {
14467  mDataScaleType = scaleType;
14468  if (mColorAxis)
14469  mColorAxis.data()->setScaleType(mDataScaleType);
14473  }
14474 }
14475 
14484 {
14485  if (mGradient != gradient)
14486  {
14487  mGradient = gradient;
14488  if (mAxisRect)
14489  mAxisRect.data()->mGradientImageInvalidated = true;
14490  emit gradientChanged(mGradient);
14491  }
14492 }
14493 
14498 void QCPColorScale::setLabel(const QString &str)
14499 {
14500  if (!mColorAxis)
14501  {
14502  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
14503  return;
14504  }
14505 
14506  mColorAxis.data()->setLabel(str);
14507 }
14508 
14514 {
14515  mBarWidth = width;
14516 }
14517 
14525 {
14526  if (!mAxisRect)
14527  {
14528  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14529  return;
14530  }
14531 
14532  if (enabled)
14533  mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14534  else
14535  mAxisRect.data()->setRangeDrag(0);
14536 }
14537 
14545 {
14546  if (!mAxisRect)
14547  {
14548  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14549  return;
14550  }
14551 
14552  if (enabled)
14553  mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14554  else
14555  mAxisRect.data()->setRangeZoom(0);
14556 }
14557 
14561 QList<QCPColorMap*> QCPColorScale::colorMaps() const
14562 {
14563  QList<QCPColorMap*> result;
14564  for (int i=0; i<mParentPlot->plottableCount(); ++i)
14565  {
14566  if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
14567  if (cm->colorScale() == this)
14568  result.append(cm);
14569  }
14570  return result;
14571 }
14572 
14579 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
14580 {
14581  QList<QCPColorMap*> maps = colorMaps();
14582  QCPRange newRange;
14583  bool haveRange = false;
14584  int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
14586  sign = (mDataRange.upper < 0 ? -1 : 1);
14587  for (int i=0; i<maps.size(); ++i)
14588  {
14589  if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
14590  continue;
14591  QCPRange mapRange;
14592  if (maps.at(i)->colorScale() == this)
14593  {
14594  bool currentFoundRange = true;
14595  mapRange = maps.at(i)->data()->dataBounds();
14596  if (sign == 1)
14597  {
14598  if (mapRange.lower <= 0 && mapRange.upper > 0)
14599  mapRange.lower = mapRange.upper*1e-3;
14600  else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14601  currentFoundRange = false;
14602  } else if (sign == -1)
14603  {
14604  if (mapRange.upper >= 0 && mapRange.lower < 0)
14605  mapRange.upper = mapRange.lower*1e-3;
14606  else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14607  currentFoundRange = false;
14608  }
14609  if (currentFoundRange)
14610  {
14611  if (!haveRange)
14612  newRange = mapRange;
14613  else
14614  newRange.expand(mapRange);
14615  haveRange = true;
14616  }
14617  }
14618  }
14619  if (haveRange)
14620  {
14621  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
14622  {
14623  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
14625  {
14626  newRange.lower = center-mDataRange.size()/2.0;
14627  newRange.upper = center+mDataRange.size()/2.0;
14628  } else // mScaleType == stLogarithmic
14629  {
14630  newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
14631  newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
14632  }
14633  }
14634  setDataRange(newRange);
14635  }
14636 }
14637 
14638 /* inherits documentation from base class */
14640 {
14641  QCPLayoutElement::update(phase);
14642  if (!mAxisRect)
14643  {
14644  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14645  return;
14646  }
14647 
14648  mAxisRect.data()->update(phase);
14649 
14650  switch (phase)
14651  {
14652  case upMargins:
14653  {
14655  {
14656  setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14657  setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14658  } else
14659  {
14660  setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
14661  setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
14662  }
14663  break;
14664  }
14665  case upLayout:
14666  {
14667  mAxisRect.data()->setOuterRect(rect());
14668  break;
14669  }
14670  default: break;
14671  }
14672 }
14673 
14674 /* inherits documentation from base class */
14676 {
14677  painter->setAntialiasing(false);
14678 }
14679 
14680 /* inherits documentation from base class */
14681 void QCPColorScale::mousePressEvent(QMouseEvent *event)
14682 {
14683  if (!mAxisRect)
14684  {
14685  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14686  return;
14687  }
14688  mAxisRect.data()->mousePressEvent(event);
14689 }
14690 
14691 /* inherits documentation from base class */
14692 void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
14693 {
14694  if (!mAxisRect)
14695  {
14696  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14697  return;
14698  }
14699  mAxisRect.data()->mouseMoveEvent(event);
14700 }
14701 
14702 /* inherits documentation from base class */
14703 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
14704 {
14705  if (!mAxisRect)
14706  {
14707  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14708  return;
14709  }
14710  mAxisRect.data()->mouseReleaseEvent(event);
14711 }
14712 
14713 /* inherits documentation from base class */
14714 void QCPColorScale::wheelEvent(QWheelEvent *event)
14715 {
14716  if (!mAxisRect)
14717  {
14718  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14719  return;
14720  }
14721  mAxisRect.data()->wheelEvent(event);
14722 }
14723 
14727 
14743  QCPAxisRect(parentColorScale->parentPlot(), true),
14744  mParentColorScale(parentColorScale),
14745  mGradientImageInvalidated(true)
14746 {
14747  setParentLayerable(parentColorScale);
14748  setMinimumMargins(QMargins(0, 0, 0, 0));
14749  foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14750  {
14751  axis(type)->setVisible(true);
14752  axis(type)->grid()->setVisible(false);
14753  axis(type)->setPadding(0);
14754  connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14755  connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14756  }
14757 
14758  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14759  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14760  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14761  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14762  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14763  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14764  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14765  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14766 
14767  // make layer transfers of color scale transfer to axis rect and axes
14768  // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
14769  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
14770  foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14771  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
14772 }
14773 
14779 {
14782 
14783  bool mirrorHorz = false;
14784  bool mirrorVert = false;
14786  {
14787  mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
14788  mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
14789  }
14790 
14791  painter->drawImage(rect(), mGradientImage.mirrored(mirrorHorz, mirrorVert));
14792  QCPAxisRect::draw(painter);
14793 }
14794 
14801 {
14802  if (rect().isEmpty())
14803  return;
14804 
14806  int w, h;
14807  QVector<double> data(n);
14808  for (int i=0; i<n; ++i)
14809  data[i] = i;
14811  {
14812  w = n;
14813  h = rect().height();
14814  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14815  QVector<QRgb*> pixels;
14816  for (int y=0; y<h; ++y)
14817  pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
14818  mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
14819  for (int y=1; y<h; ++y)
14820  memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
14821  } else
14822  {
14823  w = rect().width();
14824  h = n;
14825  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14826  for (int y=0; y<h; ++y)
14827  {
14828  QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
14829  const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
14830  for (int x=0; x<w; ++x)
14831  pixels[x] = lineColor;
14832  }
14833  }
14834  mGradientImageInvalidated = false;
14835 }
14836 
14842 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
14843 {
14844  // axis bases of four axes shall always (de-)selected synchronously:
14845  foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14846  {
14847  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14848  if (senderAxis->axisType() == type)
14849  continue;
14850 
14851  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14852  {
14853  if (selectedParts.testFlag(QCPAxis::spAxis))
14854  axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
14855  else
14856  axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
14857  }
14858  }
14859 }
14860 
14866 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
14867 {
14868  // synchronize axis base selectability:
14869  foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14870  {
14871  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14872  if (senderAxis->axisType() == type)
14873  continue;
14874 
14875  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14876  {
14877  if (selectableParts.testFlag(QCPAxis::spAxis))
14878  axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
14879  else
14880  axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
14881  }
14882  }
14883 }
14884 
14885 
14889 
14910  key(0),
14911  value(0),
14912  keyErrorPlus(0),
14913  keyErrorMinus(0),
14914  valueErrorPlus(0),
14915  valueErrorMinus(0)
14916 {
14917 }
14918 
14922 QCPData::QCPData(double key, double value) :
14923  key(key),
14924  value(value),
14925  keyErrorPlus(0),
14926  keyErrorMinus(0),
14927  valueErrorPlus(0),
14928  valueErrorMinus(0)
14929 {
14930 }
14931 
14932 
14936 
14971 /* start of documentation of inline functions */
14972 
14980 /* end of documentation of inline functions */
14981 
14993 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
14994  QCPAbstractPlottable(keyAxis, valueAxis)
14995 {
14996  mData = new QCPDataMap;
14997 
14998  setPen(QPen(Qt::blue, 0));
14999  setErrorPen(QPen(Qt::black));
15000  setBrush(Qt::NoBrush);
15001  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
15002  setSelectedBrush(Qt::NoBrush);
15003 
15006  setErrorBarSize(6);
15007  setErrorBarSkipSymbol(true);
15009  setAdaptiveSampling(true);
15010 }
15011 
15013 {
15014  delete mData;
15015 }
15016 
15028 {
15029  if (mData == data)
15030  {
15031  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
15032  return;
15033  }
15034  if (copy)
15035  {
15036  *mData = *data;
15037  } else
15038  {
15039  delete mData;
15040  mData = data;
15041  }
15042 }
15043 
15050 void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
15051 {
15052  mData->clear();
15053  int n = key.size();
15054  n = qMin(n, value.size());
15055  QCPData newData;
15056  for (int i=0; i<n; ++i)
15057  {
15058  newData.key = key[i];
15059  newData.value = value[i];
15060  mData->insertMulti(newData.key, newData);
15061  }
15062 }
15063 
15073 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
15074 {
15075  mData->clear();
15076  int n = key.size();
15077  n = qMin(n, value.size());
15078  n = qMin(n, valueError.size());
15079  QCPData newData;
15080  for (int i=0; i<n; ++i)
15081  {
15082  newData.key = key[i];
15083  newData.value = value[i];
15084  newData.valueErrorMinus = valueError[i];
15085  newData.valueErrorPlus = valueError[i];
15086  mData->insertMulti(key[i], newData);
15087  }
15088 }
15089 
15099 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
15100 {
15101  mData->clear();
15102  int n = key.size();
15103  n = qMin(n, value.size());
15104  n = qMin(n, valueErrorMinus.size());
15105  n = qMin(n, valueErrorPlus.size());
15106  QCPData newData;
15107  for (int i=0; i<n; ++i)
15108  {
15109  newData.key = key[i];
15110  newData.value = value[i];
15111  newData.valueErrorMinus = valueErrorMinus[i];
15112  newData.valueErrorPlus = valueErrorPlus[i];
15113  mData->insertMulti(key[i], newData);
15114  }
15115 }
15116 
15126 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
15127 {
15128  mData->clear();
15129  int n = key.size();
15130  n = qMin(n, value.size());
15131  n = qMin(n, keyError.size());
15132  QCPData newData;
15133  for (int i=0; i<n; ++i)
15134  {
15135  newData.key = key[i];
15136  newData.value = value[i];
15137  newData.keyErrorMinus = keyError[i];
15138  newData.keyErrorPlus = keyError[i];
15139  mData->insertMulti(key[i], newData);
15140  }
15141 }
15142 
15152 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
15153 {
15154  mData->clear();
15155  int n = key.size();
15156  n = qMin(n, value.size());
15157  n = qMin(n, keyErrorMinus.size());
15158  n = qMin(n, keyErrorPlus.size());
15159  QCPData newData;
15160  for (int i=0; i<n; ++i)
15161  {
15162  newData.key = key[i];
15163  newData.value = value[i];
15164  newData.keyErrorMinus = keyErrorMinus[i];
15165  newData.keyErrorPlus = keyErrorPlus[i];
15166  mData->insertMulti(key[i], newData);
15167  }
15168 }
15169 
15179 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
15180 {
15181  mData->clear();
15182  int n = key.size();
15183  n = qMin(n, value.size());
15184  n = qMin(n, valueError.size());
15185  n = qMin(n, keyError.size());
15186  QCPData newData;
15187  for (int i=0; i<n; ++i)
15188  {
15189  newData.key = key[i];
15190  newData.value = value[i];
15191  newData.keyErrorMinus = keyError[i];
15192  newData.keyErrorPlus = keyError[i];
15193  newData.valueErrorMinus = valueError[i];
15194  newData.valueErrorPlus = valueError[i];
15195  mData->insertMulti(key[i], newData);
15196  }
15197 }
15198 
15208 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)
15209 {
15210  mData->clear();
15211  int n = key.size();
15212  n = qMin(n, value.size());
15213  n = qMin(n, valueErrorMinus.size());
15214  n = qMin(n, valueErrorPlus.size());
15215  n = qMin(n, keyErrorMinus.size());
15216  n = qMin(n, keyErrorPlus.size());
15217  QCPData newData;
15218  for (int i=0; i<n; ++i)
15219  {
15220  newData.key = key[i];
15221  newData.value = value[i];
15222  newData.keyErrorMinus = keyErrorMinus[i];
15223  newData.keyErrorPlus = keyErrorPlus[i];
15224  newData.valueErrorMinus = valueErrorMinus[i];
15225  newData.valueErrorPlus = valueErrorPlus[i];
15226  mData->insertMulti(key[i], newData);
15227  }
15228 }
15229 
15230 
15238 {
15239  mLineStyle = ls;
15240 }
15241 
15249 {
15250  mScatterStyle = style;
15251 }
15252 
15262 {
15264 }
15265 
15270 void QCPGraph::setErrorPen(const QPen &pen)
15271 {
15272  mErrorPen = pen;
15273 }
15274 
15278 void QCPGraph::setErrorBarSize(double size)
15279 {
15280  mErrorBarSize = size;
15281 }
15282 
15295 {
15296  mErrorBarSkipSymbol = enabled;
15297 }
15298 
15309 {
15310  // prevent setting channel target to this graph itself:
15311  if (targetGraph == this)
15312  {
15313  qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
15314  mChannelFillGraph = 0;
15315  return;
15316  }
15317  // prevent setting channel target to a graph not in the plot:
15318  if (targetGraph && targetGraph->mParentPlot != mParentPlot)
15319  {
15320  qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
15321  mChannelFillGraph = 0;
15322  return;
15323  }
15324 
15325  mChannelFillGraph = targetGraph;
15326 }
15327 
15360 {
15361  mAdaptiveSampling = enabled;
15362 }
15363 
15372 void QCPGraph::addData(const QCPDataMap &dataMap)
15373 {
15374  mData->unite(dataMap);
15375 }
15376 
15386 {
15387  mData->insertMulti(data.key, data);
15388 }
15389 
15398 void QCPGraph::addData(double key, double value)
15399 {
15400  QCPData newData;
15401  newData.key = key;
15402  newData.value = value;
15403  mData->insertMulti(newData.key, newData);
15404 }
15405 
15414 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
15415 {
15416  int n = qMin(keys.size(), values.size());
15417  QCPData newData;
15418  for (int i=0; i<n; ++i)
15419  {
15420  newData.key = keys[i];
15421  newData.value = values[i];
15422  mData->insertMulti(newData.key, newData);
15423  }
15424 }
15425 
15431 {
15432  QCPDataMap::iterator it = mData->begin();
15433  while (it != mData->end() && it.key() < key)
15434  it = mData->erase(it);
15435 }
15436 
15442 {
15443  if (mData->isEmpty()) return;
15444  QCPDataMap::iterator it = mData->upperBound(key);
15445  while (it != mData->end())
15446  it = mData->erase(it);
15447 }
15448 
15456 void QCPGraph::removeData(double fromKey, double toKey)
15457 {
15458  if (fromKey >= toKey || mData->isEmpty()) return;
15459  QCPDataMap::iterator it = mData->upperBound(fromKey);
15460  QCPDataMap::iterator itEnd = mData->upperBound(toKey);
15461  while (it != itEnd)
15462  it = mData->erase(it);
15463 }
15464 
15473 void QCPGraph::removeData(double key)
15474 {
15475  mData->remove(key);
15476 }
15477 
15483 {
15484  mData->clear();
15485 }
15486 
15487 /* inherits documentation from base class */
15488 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
15489 {
15490  Q_UNUSED(details)
15491  if ((onlySelectable && !mSelectable) || mData->isEmpty())
15492  return -1;
15493  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
15494 
15495  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
15496  return pointDistance(pos);
15497  else
15498  return -1;
15499 }
15500 
15508 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
15509 {
15510  rescaleKeyAxis(onlyEnlarge, includeErrorBars);
15511  rescaleValueAxis(onlyEnlarge, includeErrorBars);
15512 }
15513 
15521 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
15522 {
15523  // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
15524  // that getKeyRange is passed the includeErrorBars value.
15525  if (mData->isEmpty()) return;
15526 
15527  QCPAxis *keyAxis = mKeyAxis.data();
15528  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15529 
15530  SignDomain signDomain = sdBoth;
15531  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
15532  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15533 
15534  bool foundRange;
15535  QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15536 
15537  if (foundRange)
15538  {
15539  if (onlyEnlarge)
15540  {
15541  if (keyAxis->range().lower < newRange.lower)
15542  newRange.lower = keyAxis->range().lower;
15543  if (keyAxis->range().upper > newRange.upper)
15544  newRange.upper = keyAxis->range().upper;
15545  }
15546  keyAxis->setRange(newRange);
15547  }
15548 }
15549 
15557 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
15558 {
15559  // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
15560  // is that getValueRange is passed the includeErrorBars value.
15561  if (mData->isEmpty()) return;
15562 
15563  QCPAxis *valueAxis = mValueAxis.data();
15564  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
15565 
15566  SignDomain signDomain = sdBoth;
15567  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
15568  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15569 
15570  bool foundRange;
15571  QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15572 
15573  if (foundRange)
15574  {
15575  if (onlyEnlarge)
15576  {
15577  if (valueAxis->range().lower < newRange.lower)
15578  newRange.lower = valueAxis->range().lower;
15579  if (valueAxis->range().upper > newRange.upper)
15580  newRange.upper = valueAxis->range().upper;
15581  }
15582  valueAxis->setRange(newRange);
15583  }
15584 }
15585 
15586 /* inherits documentation from base class */
15588 {
15589  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15590  if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15591  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15592 
15593  // allocate line and (if necessary) point vectors:
15594  QVector<QPointF> *lineData = new QVector<QPointF>;
15595  QVector<QCPData> *scatterData = 0;
15596  if (!mScatterStyle.isNone())
15597  scatterData = new QVector<QCPData>;
15598 
15599  // fill vectors with data appropriate to plot style:
15600  getPlotData(lineData, scatterData);
15601 
15602  // check data validity if flag set:
15603 #ifdef QCUSTOMPLOT_CHECK_DATA
15604  QCPDataMap::const_iterator it;
15605  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
15606  {
15607  if (QCP::isInvalidData(it.value().key, it.value().value) ||
15608  QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15609  QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
15610  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
15611  }
15612 #endif
15613 
15614  // draw fill of graph:
15615  drawFill(painter, lineData);
15616 
15617  // draw line:
15618  if (mLineStyle == lsImpulse)
15619  drawImpulsePlot(painter, lineData);
15620  else if (mLineStyle != lsNone)
15621  drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15622 
15623  // draw scatters:
15624  if (scatterData)
15625  drawScatterPlot(painter, scatterData);
15626 
15627  // free allocated line and point vectors:
15628  delete lineData;
15629  if (scatterData)
15630  delete scatterData;
15631 }
15632 
15633 /* inherits documentation from base class */
15634 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
15635 {
15636  // draw fill:
15637  if (mBrush.style() != Qt::NoBrush)
15638  {
15639  applyFillAntialiasingHint(painter);
15640  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
15641  }
15642  // draw line vertically centered:
15643  if (mLineStyle != lsNone)
15644  {
15646  painter->setPen(mPen);
15647  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
15648  }
15649  // draw scatter symbol:
15650  if (!mScatterStyle.isNone())
15651  {
15653  // scale scatter pixmap if it's too large to fit in legend icon rect:
15654  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
15655  {
15656  QCPScatterStyle scaledStyle(mScatterStyle);
15657  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15658  scaledStyle.applyTo(painter, mPen);
15659  scaledStyle.drawShape(painter, QRectF(rect).center());
15660  } else
15661  {
15662  mScatterStyle.applyTo(painter, mPen);
15663  mScatterStyle.drawShape(painter, QRectF(rect).center());
15664  }
15665  }
15666 }
15667 
15686 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
15687 {
15688  switch(mLineStyle)
15689  {
15690  case lsNone: getScatterPlotData(scatterData); break;
15691  case lsLine: getLinePlotData(lineData, scatterData); break;
15692  case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
15693  case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
15694  case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
15695  case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
15696  }
15697 }
15698 
15710 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15711 {
15712  getPreparedData(0, scatterData);
15713 }
15714 
15726 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15727 {
15728  QCPAxis *keyAxis = mKeyAxis.data();
15729  QCPAxis *valueAxis = mValueAxis.data();
15730  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15731  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15732 
15733  QVector<QCPData> lineData;
15734  getPreparedData(&lineData, scatterData);
15735  linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15736  linePixelData->resize(lineData.size());
15737 
15738  // transform lineData points to pixels:
15739  if (keyAxis->orientation() == Qt::Vertical)
15740  {
15741  for (int i=0; i<lineData.size(); ++i)
15742  {
15743  (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15744  (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15745  }
15746  } else // key axis is horizontal
15747  {
15748  for (int i=0; i<lineData.size(); ++i)
15749  {
15750  (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15751  (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15752  }
15753  }
15754 }
15755 
15767 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15768 {
15769  QCPAxis *keyAxis = mKeyAxis.data();
15770  QCPAxis *valueAxis = mValueAxis.data();
15771  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15772  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15773 
15774  QVector<QCPData> lineData;
15775  getPreparedData(&lineData, scatterData);
15776  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15777  linePixelData->resize(lineData.size()*2);
15778 
15779  // calculate steps from lineData and transform to pixel coordinates:
15780  if (keyAxis->orientation() == Qt::Vertical)
15781  {
15782  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15783  double key;
15784  for (int i=0; i<lineData.size(); ++i)
15785  {
15786  key = keyAxis->coordToPixel(lineData.at(i).key);
15787  (*linePixelData)[i*2+0].setX(lastValue);
15788  (*linePixelData)[i*2+0].setY(key);
15789  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15790  (*linePixelData)[i*2+1].setX(lastValue);
15791  (*linePixelData)[i*2+1].setY(key);
15792  }
15793  } else // key axis is horizontal
15794  {
15795  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15796  double key;
15797  for (int i=0; i<lineData.size(); ++i)
15798  {
15799  key = keyAxis->coordToPixel(lineData.at(i).key);
15800  (*linePixelData)[i*2+0].setX(key);
15801  (*linePixelData)[i*2+0].setY(lastValue);
15802  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15803  (*linePixelData)[i*2+1].setX(key);
15804  (*linePixelData)[i*2+1].setY(lastValue);
15805  }
15806  }
15807 }
15808 
15820 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15821 {
15822  QCPAxis *keyAxis = mKeyAxis.data();
15823  QCPAxis *valueAxis = mValueAxis.data();
15824  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15825  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15826 
15827  QVector<QCPData> lineData;
15828  getPreparedData(&lineData, scatterData);
15829  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15830  linePixelData->resize(lineData.size()*2);
15831 
15832  // calculate steps from lineData and transform to pixel coordinates:
15833  if (keyAxis->orientation() == Qt::Vertical)
15834  {
15835  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15836  double value;
15837  for (int i=0; i<lineData.size(); ++i)
15838  {
15839  value = valueAxis->coordToPixel(lineData.at(i).value);
15840  (*linePixelData)[i*2+0].setX(value);
15841  (*linePixelData)[i*2+0].setY(lastKey);
15842  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15843  (*linePixelData)[i*2+1].setX(value);
15844  (*linePixelData)[i*2+1].setY(lastKey);
15845  }
15846  } else // key axis is horizontal
15847  {
15848  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15849  double value;
15850  for (int i=0; i<lineData.size(); ++i)
15851  {
15852  value = valueAxis->coordToPixel(lineData.at(i).value);
15853  (*linePixelData)[i*2+0].setX(lastKey);
15854  (*linePixelData)[i*2+0].setY(value);
15855  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15856  (*linePixelData)[i*2+1].setX(lastKey);
15857  (*linePixelData)[i*2+1].setY(value);
15858  }
15859  }
15860 }
15861 
15873 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15874 {
15875  QCPAxis *keyAxis = mKeyAxis.data();
15876  QCPAxis *valueAxis = mValueAxis.data();
15877  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15878  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15879 
15880  QVector<QCPData> lineData;
15881  getPreparedData(&lineData, scatterData);
15882  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15883  linePixelData->resize(lineData.size()*2);
15884  // calculate steps from lineData and transform to pixel coordinates:
15885  if (keyAxis->orientation() == Qt::Vertical)
15886  {
15887  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15888  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15889  double key;
15890  (*linePixelData)[0].setX(lastValue);
15891  (*linePixelData)[0].setY(lastKey);
15892  for (int i=1; i<lineData.size(); ++i)
15893  {
15894  key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15895  (*linePixelData)[i*2-1].setX(lastValue);
15896  (*linePixelData)[i*2-1].setY(key);
15897  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15898  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15899  (*linePixelData)[i*2+0].setX(lastValue);
15900  (*linePixelData)[i*2+0].setY(key);
15901  }
15902  (*linePixelData)[lineData.size()*2-1].setX(lastValue);
15903  (*linePixelData)[lineData.size()*2-1].setY(lastKey);
15904  } else // key axis is horizontal
15905  {
15906  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15907  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15908  double key;
15909  (*linePixelData)[0].setX(lastKey);
15910  (*linePixelData)[0].setY(lastValue);
15911  for (int i=1; i<lineData.size(); ++i)
15912  {
15913  key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15914  (*linePixelData)[i*2-1].setX(key);
15915  (*linePixelData)[i*2-1].setY(lastValue);
15916  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15917  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15918  (*linePixelData)[i*2+0].setX(key);
15919  (*linePixelData)[i*2+0].setY(lastValue);
15920  }
15921  (*linePixelData)[lineData.size()*2-1].setX(lastKey);
15922  (*linePixelData)[lineData.size()*2-1].setY(lastValue);
15923  }
15924 
15925 }
15926 
15938 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15939 {
15940  QCPAxis *keyAxis = mKeyAxis.data();
15941  QCPAxis *valueAxis = mValueAxis.data();
15942  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15943  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15944 
15945  QVector<QCPData> lineData;
15946  getPreparedData(&lineData, scatterData);
15947  linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
15948 
15949  // transform lineData points to pixels:
15950  if (keyAxis->orientation() == Qt::Vertical)
15951  {
15952  double zeroPointX = valueAxis->coordToPixel(0);
15953  double key;
15954  for (int i=0; i<lineData.size(); ++i)
15955  {
15956  key = keyAxis->coordToPixel(lineData.at(i).key);
15957  (*linePixelData)[i*2+0].setX(zeroPointX);
15958  (*linePixelData)[i*2+0].setY(key);
15959  (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
15960  (*linePixelData)[i*2+1].setY(key);
15961  }
15962  } else // key axis is horizontal
15963  {
15964  double zeroPointY = valueAxis->coordToPixel(0);
15965  double key;
15966  for (int i=0; i<lineData.size(); ++i)
15967  {
15968  key = keyAxis->coordToPixel(lineData.at(i).key);
15969  (*linePixelData)[i*2+0].setX(key);
15970  (*linePixelData)[i*2+0].setY(zeroPointY);
15971  (*linePixelData)[i*2+1].setX(key);
15972  (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
15973  }
15974  }
15975 }
15976 
15990 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
15991 {
15992  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
15993  if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
15994 
15995  applyFillAntialiasingHint(painter);
15996  if (!mChannelFillGraph)
15997  {
15998  // draw base fill under graph, fill goes all the way to the zero-value-line:
15999  addFillBasePoints(lineData);
16000  painter->setPen(Qt::NoPen);
16001  painter->setBrush(mainBrush());
16002  painter->drawPolygon(QPolygonF(*lineData));
16003  removeFillBasePoints(lineData);
16004  } else
16005  {
16006  // draw channel fill between this graph and mChannelFillGraph:
16007  painter->setPen(Qt::NoPen);
16008  painter->setBrush(mainBrush());
16009  painter->drawPolygon(getChannelFillPolygon(lineData));
16010  }
16011 }
16012 
16022 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
16023 {
16024  QCPAxis *keyAxis = mKeyAxis.data();
16025  QCPAxis *valueAxis = mValueAxis.data();
16026  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
16027 
16028  // draw error bars:
16029  if (mErrorType != etNone)
16030  {
16032  painter->setPen(mErrorPen);
16033  if (keyAxis->orientation() == Qt::Vertical)
16034  {
16035  for (int i=0; i<scatterData->size(); ++i)
16036  drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
16037  } else
16038  {
16039  for (int i=0; i<scatterData->size(); ++i)
16040  drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
16041  }
16042  }
16043 
16044  // draw scatter point symbols:
16046  mScatterStyle.applyTo(painter, mPen);
16047  if (keyAxis->orientation() == Qt::Vertical)
16048  {
16049  for (int i=0; i<scatterData->size(); ++i)
16050  if (!qIsNaN(scatterData->at(i).value))
16051  mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
16052  } else
16053  {
16054  for (int i=0; i<scatterData->size(); ++i)
16055  if (!qIsNaN(scatterData->at(i).value))
16056  mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
16057  }
16058 }
16059 
16069 void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
16070 {
16071  // draw line of graph:
16072  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
16073  {
16075  painter->setPen(mainPen());
16076  painter->setBrush(Qt::NoBrush);
16077 
16078  /* Draws polyline in batches, currently not used:
16079  int p = 0;
16080  while (p < lineData->size())
16081  {
16082  int batch = qMin(25, lineData->size()-p);
16083  if (p != 0)
16084  {
16085  ++batch;
16086  --p; // to draw the connection lines between two batches
16087  }
16088  painter->drawPolyline(lineData->constData()+p, batch);
16089  p += batch;
16090  }
16091  */
16092 
16093  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
16094  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
16095  painter->pen().style() == Qt::SolidLine &&
16096  !painter->modes().testFlag(QCPPainter::pmVectorized)&&
16097  !painter->modes().testFlag(QCPPainter::pmNoCaching))
16098  {
16099  int i = 1;
16100  int lineDataSize = lineData->size();
16101  while (i < lineDataSize)
16102  {
16103  if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
16104  painter->drawLine(lineData->at(i-1), lineData->at(i));
16105  else
16106  ++i;
16107  ++i;
16108  }
16109  } else
16110  {
16111  int segmentStart = 0;
16112  int i = 0;
16113  int lineDataSize = lineData->size();
16114  while (i < lineDataSize)
16115  {
16116  if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
16117  {
16118  painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
16119  segmentStart = i+1;
16120  }
16121  ++i;
16122  }
16123  // draw last segment:
16124  painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); // lineDataSize, because we do want to include the last point
16125  }
16126  }
16127 }
16128 
16136 void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
16137 {
16138  // draw impulses:
16139  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
16140  {
16142  QPen pen = mainPen();
16143  pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
16144  painter->setPen(pen);
16145  painter->setBrush(Qt::NoBrush);
16146  painter->drawLines(*lineData);
16147  }
16148 }
16149 
16162 void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
16163 {
16164  QCPAxis *keyAxis = mKeyAxis.data();
16165  QCPAxis *valueAxis = mValueAxis.data();
16166  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
16167  // get visible data range:
16168  QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
16169  getVisibleDataBounds(lower, upper);
16170  if (lower == mData->constEnd() || upper == mData->constEnd())
16171  return;
16172 
16173  // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
16174  int maxCount = std::numeric_limits<int>::max();
16175  if (mAdaptiveSampling)
16176  {
16177  int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
16178  maxCount = 2*keyPixelSpan+2;
16179  }
16180  int dataCount = countDataInBounds(lower, upper, maxCount);
16181 
16182  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
16183  {
16184  if (lineData)
16185  {
16186  QCPDataMap::const_iterator it = lower;
16187  QCPDataMap::const_iterator upperEnd = upper+1;
16188  double minValue = it.value().value;
16189  double maxValue = it.value().value;
16190  QCPDataMap::const_iterator currentIntervalFirstPoint = it;
16191  int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
16192  int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
16193  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
16194  double lastIntervalEndKey = currentIntervalStartKey;
16195  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
16196  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
16197  int intervalDataCount = 1;
16198  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
16199  while (it != upperEnd)
16200  {
16201  if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
16202  {
16203  if (it.value().value < minValue)
16204  minValue = it.value().value;
16205  else if (it.value().value > maxValue)
16206  maxValue = it.value().value;
16207  ++intervalDataCount;
16208  } else // new pixel interval started
16209  {
16210  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
16211  {
16212  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
16213  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
16214  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
16215  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
16216  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
16217  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
16218  } else
16219  lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
16220  lastIntervalEndKey = (it-1).value().key;
16221  minValue = it.value().value;
16222  maxValue = it.value().value;
16223  currentIntervalFirstPoint = it;
16224  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
16225  if (keyEpsilonVariable)
16226  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
16227  intervalDataCount = 1;
16228  }
16229  ++it;
16230  }
16231  // handle last interval:
16232  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
16233  {
16234  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
16235  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
16236  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
16237  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
16238  } else
16239  lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
16240  }
16241 
16242  if (scatterData)
16243  {
16244  double valueMaxRange = valueAxis->range().upper;
16245  double valueMinRange = valueAxis->range().lower;
16246  QCPDataMap::const_iterator it = lower;
16247  QCPDataMap::const_iterator upperEnd = upper+1;
16248  double minValue = it.value().value;
16249  double maxValue = it.value().value;
16250  QCPDataMap::const_iterator minValueIt = it;
16251  QCPDataMap::const_iterator maxValueIt = it;
16252  QCPDataMap::const_iterator currentIntervalStart = it;
16253  int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
16254  int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
16255  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
16256  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
16257  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
16258  int intervalDataCount = 1;
16259  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
16260  while (it != upperEnd)
16261  {
16262  if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
16263  {
16264  if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
16265  {
16266  minValue = it.value().value;
16267  minValueIt = it;
16268  } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
16269  {
16270  maxValue = it.value().value;
16271  maxValueIt = it;
16272  }
16273  ++intervalDataCount;
16274  } else // new pixel started
16275  {
16276  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
16277  {
16278  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
16279  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
16280  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
16281  QCPDataMap::const_iterator intervalIt = currentIntervalStart;
16282  int c = 0;
16283  while (intervalIt != it)
16284  {
16285  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
16286  scatterData->append(intervalIt.value());
16287  ++c;
16288  ++intervalIt;
16289  }
16290  } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
16291  scatterData->append(currentIntervalStart.value());
16292  minValue = it.value().value;
16293  maxValue = it.value().value;
16294  currentIntervalStart = it;
16295  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
16296  if (keyEpsilonVariable)
16297  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
16298  intervalDataCount = 1;
16299  }
16300  ++it;
16301  }
16302  // handle last interval:
16303  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
16304  {
16305  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
16306  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
16307  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
16308  QCPDataMap::const_iterator intervalIt = currentIntervalStart;
16309  int c = 0;
16310  while (intervalIt != it)
16311  {
16312  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
16313  scatterData->append(intervalIt.value());
16314  ++c;
16315  ++intervalIt;
16316  }
16317  } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
16318  scatterData->append(currentIntervalStart.value());
16319  }
16320  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
16321  {
16322  QVector<QCPData> *dataVector = 0;
16323  if (lineData)
16324  dataVector = lineData;
16325  else if (scatterData)
16326  dataVector = scatterData;
16327  if (dataVector)
16328  {
16329  QCPDataMap::const_iterator it = lower;
16330  QCPDataMap::const_iterator upperEnd = upper+1;
16331  dataVector->reserve(dataCount+2); // +2 for possible fill end points
16332  while (it != upperEnd)
16333  {
16334  dataVector->append(it.value());
16335  ++it;
16336  }
16337  }
16338  if (lineData && scatterData)
16339  *scatterData = *dataVector;
16340  }
16341 }
16342 
16350 void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
16351 {
16352  if (qIsNaN(data.value))
16353  return;
16354  QCPAxis *keyAxis = mKeyAxis.data();
16355  QCPAxis *valueAxis = mValueAxis.data();
16356  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
16357 
16358  double a, b; // positions of error bar bounds in pixels
16359  double barWidthHalf = mErrorBarSize*0.5;
16360  double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
16361 
16362  if (keyAxis->orientation() == Qt::Vertical)
16363  {
16364  // draw key error vertically and value error horizontally
16365  if (mErrorType == etKey || mErrorType == etBoth)
16366  {
16367  a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
16368  b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
16369  if (keyAxis->rangeReversed())
16370  qSwap(a,b);
16371  // draw spine:
16372  if (mErrorBarSkipSymbol)
16373  {
16374  if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
16375  painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
16376  if (y-b > skipSymbolMargin)
16377  painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
16378  } else
16379  painter->drawLine(QLineF(x, a, x, b));
16380  // draw handles:
16381  painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
16382  painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
16383  }
16384  if (mErrorType == etValue || mErrorType == etBoth)
16385  {
16386  a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
16387  b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
16388  if (valueAxis->rangeReversed())
16389  qSwap(a,b);
16390  // draw spine:
16391  if (mErrorBarSkipSymbol)
16392  {
16393  if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
16394  painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
16395  if (b-x > skipSymbolMargin)
16396  painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
16397  } else
16398  painter->drawLine(QLineF(a, y, b, y));
16399  // draw handles:
16400  painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
16401  painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
16402  }
16403  } else // mKeyAxis->orientation() is Qt::Horizontal
16404  {
16405  // draw value error vertically and key error horizontally
16406  if (mErrorType == etKey || mErrorType == etBoth)
16407  {
16408  a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
16409  b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
16410  if (keyAxis->rangeReversed())
16411  qSwap(a,b);
16412  // draw spine:
16413  if (mErrorBarSkipSymbol)
16414  {
16415  if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
16416  painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
16417  if (b-x > skipSymbolMargin)
16418  painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
16419  } else
16420  painter->drawLine(QLineF(a, y, b, y));
16421  // draw handles:
16422  painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
16423  painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
16424  }
16425  if (mErrorType == etValue || mErrorType == etBoth)
16426  {
16427  a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
16428  b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
16429  if (valueAxis->rangeReversed())
16430  qSwap(a,b);
16431  // draw spine:
16432  if (mErrorBarSkipSymbol)
16433  {
16434  if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
16435  painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
16436  if (y-b > skipSymbolMargin)
16437  painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
16438  } else
16439  painter->drawLine(QLineF(x, a, x, b));
16440  // draw handles:
16441  painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
16442  painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
16443  }
16444  }
16445 }
16446 
16461 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
16462 {
16463  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16464  if (mData->isEmpty())
16465  {
16466  lower = mData->constEnd();
16467  upper = mData->constEnd();
16468  return;
16469  }
16470 
16471  // get visible data range as QMap iterators
16472  QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
16473  QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
16474  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
16475  bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
16476 
16477  lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
16478  upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
16479 }
16480 
16491 int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
16492 {
16493  if (upper == mData->constEnd() && lower == mData->constEnd())
16494  return 0;
16495  QCPDataMap::const_iterator it = lower;
16496  int count = 1;
16497  while (it != upper && count < maxCount)
16498  {
16499  ++it;
16500  ++count;
16501  }
16502  return count;
16503 }
16504 
16520 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
16521 {
16522  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16523 
16524  // append points that close the polygon fill at the key axis:
16525  if (mKeyAxis.data()->orientation() == Qt::Vertical)
16526  {
16527  *lineData << upperFillBasePoint(lineData->last().y());
16528  *lineData << lowerFillBasePoint(lineData->first().y());
16529  } else
16530  {
16531  *lineData << upperFillBasePoint(lineData->last().x());
16532  *lineData << lowerFillBasePoint(lineData->first().x());
16533  }
16534 }
16535 
16542 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
16543 {
16544  lineData->remove(lineData->size()-2, 2);
16545 }
16546 
16561 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
16562 {
16563  QCPAxis *keyAxis = mKeyAxis.data();
16564  QCPAxis *valueAxis = mValueAxis.data();
16565  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16566 
16567  QPointF point;
16568  if (valueAxis->scaleType() == QCPAxis::stLinear)
16569  {
16570  if (keyAxis->axisType() == QCPAxis::atLeft)
16571  {
16572  point.setX(valueAxis->coordToPixel(0));
16573  point.setY(lowerKey);
16574  } else if (keyAxis->axisType() == QCPAxis::atRight)
16575  {
16576  point.setX(valueAxis->coordToPixel(0));
16577  point.setY(lowerKey);
16578  } else if (keyAxis->axisType() == QCPAxis::atTop)
16579  {
16580  point.setX(lowerKey);
16581  point.setY(valueAxis->coordToPixel(0));
16582  } else if (keyAxis->axisType() == QCPAxis::atBottom)
16583  {
16584  point.setX(lowerKey);
16585  point.setY(valueAxis->coordToPixel(0));
16586  }
16587  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16588  {
16589  // In logarithmic scaling we can't just draw to value zero so we just fill all the way
16590  // to the axis which is in the direction towards zero
16591  if (keyAxis->orientation() == Qt::Vertical)
16592  {
16593  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16594  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16595  point.setX(keyAxis->axisRect()->right());
16596  else
16597  point.setX(keyAxis->axisRect()->left());
16598  point.setY(lowerKey);
16599  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16600  {
16601  point.setX(lowerKey);
16602  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16603  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16604  point.setY(keyAxis->axisRect()->top());
16605  else
16606  point.setY(keyAxis->axisRect()->bottom());
16607  }
16608  }
16609  return point;
16610 }
16611 
16626 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
16627 {
16628  QCPAxis *keyAxis = mKeyAxis.data();
16629  QCPAxis *valueAxis = mValueAxis.data();
16630  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16631 
16632  QPointF point;
16633  if (valueAxis->scaleType() == QCPAxis::stLinear)
16634  {
16635  if (keyAxis->axisType() == QCPAxis::atLeft)
16636  {
16637  point.setX(valueAxis->coordToPixel(0));
16638  point.setY(upperKey);
16639  } else if (keyAxis->axisType() == QCPAxis::atRight)
16640  {
16641  point.setX(valueAxis->coordToPixel(0));
16642  point.setY(upperKey);
16643  } else if (keyAxis->axisType() == QCPAxis::atTop)
16644  {
16645  point.setX(upperKey);
16646  point.setY(valueAxis->coordToPixel(0));
16647  } else if (keyAxis->axisType() == QCPAxis::atBottom)
16648  {
16649  point.setX(upperKey);
16650  point.setY(valueAxis->coordToPixel(0));
16651  }
16652  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16653  {
16654  // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
16655  // to the axis which is in the direction towards 0
16656  if (keyAxis->orientation() == Qt::Vertical)
16657  {
16658  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16659  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16660  point.setX(keyAxis->axisRect()->right());
16661  else
16662  point.setX(keyAxis->axisRect()->left());
16663  point.setY(upperKey);
16664  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16665  {
16666  point.setX(upperKey);
16667  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16668  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16669  point.setY(keyAxis->axisRect()->top());
16670  else
16671  point.setY(keyAxis->axisRect()->bottom());
16672  }
16673  }
16674  return point;
16675 }
16676 
16686 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
16687 {
16688  if (!mChannelFillGraph)
16689  return QPolygonF();
16690 
16691  QCPAxis *keyAxis = mKeyAxis.data();
16692  QCPAxis *valueAxis = mValueAxis.data();
16693  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
16694  if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
16695 
16696  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
16697  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)
16698 
16699  if (lineData->isEmpty()) return QPolygonF();
16700  QVector<QPointF> otherData;
16701  mChannelFillGraph.data()->getPlotData(&otherData, 0);
16702  if (otherData.isEmpty()) return QPolygonF();
16703  QVector<QPointF> thisData;
16704  thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
16705  for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
16706  thisData << lineData->at(i);
16707 
16708  // pointers to be able to swap them, depending which data range needs cropping:
16709  QVector<QPointF> *staticData = &thisData;
16710  QVector<QPointF> *croppedData = &otherData;
16711 
16712  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
16713  if (keyAxis->orientation() == Qt::Horizontal)
16714  {
16715  // x is key
16716  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16717  if (staticData->first().x() > staticData->last().x())
16718  {
16719  int size = staticData->size();
16720  for (int i=0; i<size/2; ++i)
16721  qSwap((*staticData)[i], (*staticData)[size-1-i]);
16722  }
16723  if (croppedData->first().x() > croppedData->last().x())
16724  {
16725  int size = croppedData->size();
16726  for (int i=0; i<size/2; ++i)
16727  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16728  }
16729  // crop lower bound:
16730  if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
16731  qSwap(staticData, croppedData);
16732  int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16733  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16734  croppedData->remove(0, lowBound);
16735  // set lowest point of cropped data to fit exactly key position of first static data
16736  // point via linear interpolation:
16737  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16738  double slope;
16739  if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
16740  slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
16741  else
16742  slope = 0;
16743  (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
16744  (*croppedData)[0].setX(staticData->first().x());
16745 
16746  // crop upper bound:
16747  if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
16748  qSwap(staticData, croppedData);
16749  int highBound = findIndexAboveX(croppedData, staticData->last().x());
16750  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16751  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16752  // set highest point of cropped data to fit exactly key position of last static data
16753  // point via linear interpolation:
16754  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16755  int li = croppedData->size()-1; // last index
16756  if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
16757  slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
16758  else
16759  slope = 0;
16760  (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
16761  (*croppedData)[li].setX(staticData->last().x());
16762  } else // mKeyAxis->orientation() == Qt::Vertical
16763  {
16764  // y is key
16765  // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
16766  // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
16767  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16768  if (staticData->first().y() < staticData->last().y())
16769  {
16770  int size = staticData->size();
16771  for (int i=0; i<size/2; ++i)
16772  qSwap((*staticData)[i], (*staticData)[size-1-i]);
16773  }
16774  if (croppedData->first().y() < croppedData->last().y())
16775  {
16776  int size = croppedData->size();
16777  for (int i=0; i<size/2; ++i)
16778  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16779  }
16780  // crop lower bound:
16781  if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
16782  qSwap(staticData, croppedData);
16783  int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16784  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16785  croppedData->remove(0, lowBound);
16786  // set lowest point of cropped data to fit exactly key position of first static data
16787  // point via linear interpolation:
16788  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16789  double slope;
16790  if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
16791  slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
16792  else
16793  slope = 0;
16794  (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
16795  (*croppedData)[0].setY(staticData->first().y());
16796 
16797  // crop upper bound:
16798  if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
16799  qSwap(staticData, croppedData);
16800  int highBound = findIndexBelowY(croppedData, staticData->last().y());
16801  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16802  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16803  // set highest point of cropped data to fit exactly key position of last static data
16804  // point via linear interpolation:
16805  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16806  int li = croppedData->size()-1; // last index
16807  if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
16808  slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
16809  else
16810  slope = 0;
16811  (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
16812  (*croppedData)[li].setY(staticData->last().y());
16813  }
16814 
16815  // return joined:
16816  for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
16817  thisData << otherData.at(i);
16818  return QPolygonF(thisData);
16819 }
16820 
16828 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
16829 {
16830  for (int i=data->size()-1; i>=0; --i)
16831  {
16832  if (data->at(i).x() < x)
16833  {
16834  if (i<data->size()-1)
16835  return i+1;
16836  else
16837  return data->size()-1;
16838  }
16839  }
16840  return -1;
16841 }
16842 
16850 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
16851 {
16852  for (int i=0; i<data->size(); ++i)
16853  {
16854  if (data->at(i).x() > x)
16855  {
16856  if (i>0)
16857  return i-1;
16858  else
16859  return 0;
16860  }
16861  }
16862  return -1;
16863 }
16864 
16872 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
16873 {
16874  for (int i=0; i<data->size(); ++i)
16875  {
16876  if (data->at(i).y() < y)
16877  {
16878  if (i>0)
16879  return i-1;
16880  else
16881  return 0;
16882  }
16883  }
16884  return -1;
16885 }
16886 
16897 double QCPGraph::pointDistance(const QPointF &pixelPoint) const
16898 {
16899  if (mData->isEmpty())
16900  {
16901  qDebug() << Q_FUNC_INFO << "requested point distance on graph" << mName << "without data";
16902  return 500;
16903  }
16904  if (mData->size() == 1)
16905  {
16906  QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
16907  return QVector2D(dataPoint-pixelPoint).length();
16908  }
16909 
16910  if (mLineStyle == lsNone && mScatterStyle.isNone())
16911  return 500;
16912 
16913  // calculate minimum distances to graph representation:
16914  if (mLineStyle == lsNone)
16915  {
16916  // no line displayed, only calculate distance to scatter points:
16917  QVector<QCPData> *scatterData = new QVector<QCPData>;
16918  getScatterPlotData(scatterData);
16919  double minDistSqr = std::numeric_limits<double>::max();
16920  QPointF ptA;
16921  QPointF ptB = coordsToPixels(scatterData->at(0).key, scatterData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
16922  for (int i=1; i<scatterData->size(); ++i)
16923  {
16924  ptA = ptB;
16925  ptB = coordsToPixels(scatterData->at(i).key, scatterData->at(i).value);
16926  double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
16927  if (currentDistSqr < minDistSqr)
16928  minDistSqr = currentDistSqr;
16929  }
16930  delete scatterData;
16931  return qSqrt(minDistSqr);
16932  } else
16933  {
16934  // line displayed calculate distance to line segments:
16935  QVector<QPointF> *lineData = new QVector<QPointF>;
16936  getPlotData(lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
16937  double minDistSqr = std::numeric_limits<double>::max();
16938  if (mLineStyle == lsImpulse)
16939  {
16940  // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
16941  for (int i=0; i<lineData->size()-1; i+=2) // iterate pairs
16942  {
16943  double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
16944  if (currentDistSqr < minDistSqr)
16945  minDistSqr = currentDistSqr;
16946  }
16947  } else
16948  {
16949  // all other line plots (line and step) connect points directly:
16950  for (int i=0; i<lineData->size()-1; ++i)
16951  {
16952  double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
16953  if (currentDistSqr < minDistSqr)
16954  minDistSqr = currentDistSqr;
16955  }
16956  }
16957  delete lineData;
16958  return qSqrt(minDistSqr);
16959  }
16960 }
16961 
16970 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
16971 {
16972  for (int i=data->size()-1; i>=0; --i)
16973  {
16974  if (data->at(i).y() > y)
16975  {
16976  if (i<data->size()-1)
16977  return i+1;
16978  else
16979  return data->size()-1;
16980  }
16981  }
16982  return -1;
16983 }
16984 
16985 /* inherits documentation from base class */
16986 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
16987 {
16988  // just call the specialized version which takes an additional argument whether error bars
16989  // should also be taken into consideration for range calculation. We set this to true here.
16990  return getKeyRange(foundRange, inSignDomain, true);
16991 }
16992 
16993 /* inherits documentation from base class */
16994 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
16995 {
16996  // just call the specialized version which takes an additional argument whether error bars
16997  // should also be taken into consideration for range calculation. We set this to true here.
16998  return getValueRange(foundRange, inSignDomain, true);
16999 }
17000 
17007 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
17008 {
17009  QCPRange range;
17010  bool haveLower = false;
17011  bool haveUpper = false;
17012 
17013  double current, currentErrorMinus, currentErrorPlus;
17014 
17015  if (inSignDomain == sdBoth) // range may be anywhere
17016  {
17017  QCPDataMap::const_iterator it = mData->constBegin();
17018  while (it != mData->constEnd())
17019  {
17020  current = it.value().key;
17021  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
17022  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
17023  if (current-currentErrorMinus < range.lower || !haveLower)
17024  {
17025  range.lower = current-currentErrorMinus;
17026  haveLower = true;
17027  }
17028  if (current+currentErrorPlus > range.upper || !haveUpper)
17029  {
17030  range.upper = current+currentErrorPlus;
17031  haveUpper = true;
17032  }
17033  ++it;
17034  }
17035  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
17036  {
17037  QCPDataMap::const_iterator it = mData->constBegin();
17038  while (it != mData->constEnd())
17039  {
17040  current = it.value().key;
17041  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
17042  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
17043  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
17044  {
17045  range.lower = current-currentErrorMinus;
17046  haveLower = true;
17047  }
17048  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
17049  {
17050  range.upper = current+currentErrorPlus;
17051  haveUpper = true;
17052  }
17053  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
17054  {
17055  if ((current < range.lower || !haveLower) && current < 0)
17056  {
17057  range.lower = current;
17058  haveLower = true;
17059  }
17060  if ((current > range.upper || !haveUpper) && current < 0)
17061  {
17062  range.upper = current;
17063  haveUpper = true;
17064  }
17065  }
17066  ++it;
17067  }
17068  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
17069  {
17070  QCPDataMap::const_iterator it = mData->constBegin();
17071  while (it != mData->constEnd())
17072  {
17073  current = it.value().key;
17074  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
17075  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
17076  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
17077  {
17078  range.lower = current-currentErrorMinus;
17079  haveLower = true;
17080  }
17081  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
17082  {
17083  range.upper = current+currentErrorPlus;
17084  haveUpper = true;
17085  }
17086  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
17087  {
17088  if ((current < range.lower || !haveLower) && current > 0)
17089  {
17090  range.lower = current;
17091  haveLower = true;
17092  }
17093  if ((current > range.upper || !haveUpper) && current > 0)
17094  {
17095  range.upper = current;
17096  haveUpper = true;
17097  }
17098  }
17099  ++it;
17100  }
17101  }
17102 
17103  foundRange = haveLower && haveUpper;
17104  return range;
17105 }
17106 
17113 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
17114 {
17115  QCPRange range;
17116  bool haveLower = false;
17117  bool haveUpper = false;
17118 
17119  double current, currentErrorMinus, currentErrorPlus;
17120 
17121  if (inSignDomain == sdBoth) // range may be anywhere
17122  {
17123  QCPDataMap::const_iterator it = mData->constBegin();
17124  while (it != mData->constEnd())
17125  {
17126  current = it.value().value;
17127  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
17128  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
17129  if (current-currentErrorMinus < range.lower || !haveLower)
17130  {
17131  range.lower = current-currentErrorMinus;
17132  haveLower = true;
17133  }
17134  if (current+currentErrorPlus > range.upper || !haveUpper)
17135  {
17136  range.upper = current+currentErrorPlus;
17137  haveUpper = true;
17138  }
17139  ++it;
17140  }
17141  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
17142  {
17143  QCPDataMap::const_iterator it = mData->constBegin();
17144  while (it != mData->constEnd())
17145  {
17146  current = it.value().value;
17147  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
17148  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
17149  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
17150  {
17151  range.lower = current-currentErrorMinus;
17152  haveLower = true;
17153  }
17154  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
17155  {
17156  range.upper = current+currentErrorPlus;
17157  haveUpper = true;
17158  }
17159  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
17160  {
17161  if ((current < range.lower || !haveLower) && current < 0)
17162  {
17163  range.lower = current;
17164  haveLower = true;
17165  }
17166  if ((current > range.upper || !haveUpper) && current < 0)
17167  {
17168  range.upper = current;
17169  haveUpper = true;
17170  }
17171  }
17172  ++it;
17173  }
17174  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
17175  {
17176  QCPDataMap::const_iterator it = mData->constBegin();
17177  while (it != mData->constEnd())
17178  {
17179  current = it.value().value;
17180  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
17181  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
17182  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
17183  {
17184  range.lower = current-currentErrorMinus;
17185  haveLower = true;
17186  }
17187  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
17188  {
17189  range.upper = current+currentErrorPlus;
17190  haveUpper = true;
17191  }
17192  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
17193  {
17194  if ((current < range.lower || !haveLower) && current > 0)
17195  {
17196  range.lower = current;
17197  haveLower = true;
17198  }
17199  if ((current > range.upper || !haveUpper) && current > 0)
17200  {
17201  range.upper = current;
17202  haveUpper = true;
17203  }
17204  }
17205  ++it;
17206  }
17207  }
17208 
17209  foundRange = haveLower && haveUpper;
17210  return range;
17211 }
17212 
17213 
17217 
17235  t(0),
17236  key(0),
17237  value(0)
17238 {
17239 }
17240 
17244 QCPCurveData::QCPCurveData(double t, double key, double value) :
17245  t(t),
17246  key(key),
17247  value(value)
17248 {
17249 }
17250 
17251 
17255 
17297 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
17298  QCPAbstractPlottable(keyAxis, valueAxis)
17299 {
17300  mData = new QCPCurveDataMap;
17301  mPen.setColor(Qt::blue);
17302  mPen.setStyle(Qt::SolidLine);
17303  mBrush.setColor(Qt::blue);
17304  mBrush.setStyle(Qt::NoBrush);
17305  mSelectedPen = mPen;
17306  mSelectedPen.setWidthF(2.5);
17307  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
17309 
17312 }
17313 
17315 {
17316  delete mData;
17317 }
17318 
17327 {
17328  if (mData == data)
17329  {
17330  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
17331  return;
17332  }
17333  if (copy)
17334  {
17335  *mData = *data;
17336  } else
17337  {
17338  delete mData;
17339  mData = data;
17340  }
17341 }
17342 
17349 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
17350 {
17351  mData->clear();
17352  int n = t.size();
17353  n = qMin(n, key.size());
17354  n = qMin(n, value.size());
17355  QCPCurveData newData;
17356  for (int i=0; i<n; ++i)
17357  {
17358  newData.t = t[i];
17359  newData.key = key[i];
17360  newData.value = value[i];
17361  mData->insertMulti(newData.t, newData);
17362  }
17363 }
17364 
17370 void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
17371 {
17372  mData->clear();
17373  int n = key.size();
17374  n = qMin(n, value.size());
17375  QCPCurveData newData;
17376  for (int i=0; i<n; ++i)
17377  {
17378  newData.t = i; // no t vector given, so we assign t the index of the key/value pair
17379  newData.key = key[i];
17380  newData.value = value[i];
17381  mData->insertMulti(newData.t, newData);
17382  }
17383 }
17384 
17393 {
17394  mScatterStyle = style;
17395 }
17396 
17405 {
17406  mLineStyle = style;
17407 }
17408 
17414 {
17415  mData->unite(dataMap);
17416 }
17417 
17423 {
17424  mData->insertMulti(data.t, data);
17425 }
17426 
17431 void QCPCurve::addData(double t, double key, double value)
17432 {
17433  QCPCurveData newData;
17434  newData.t = t;
17435  newData.key = key;
17436  newData.value = value;
17437  mData->insertMulti(newData.t, newData);
17438 }
17439 
17448 void QCPCurve::addData(double key, double value)
17449 {
17450  QCPCurveData newData;
17451  if (!mData->isEmpty())
17452  newData.t = (mData->constEnd()-1).key()+1;
17453  else
17454  newData.t = 0;
17455  newData.key = key;
17456  newData.value = value;
17457  mData->insertMulti(newData.t, newData);
17458 }
17459 
17464 void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
17465 {
17466  int n = ts.size();
17467  n = qMin(n, keys.size());
17468  n = qMin(n, values.size());
17469  QCPCurveData newData;
17470  for (int i=0; i<n; ++i)
17471  {
17472  newData.t = ts[i];
17473  newData.key = keys[i];
17474  newData.value = values[i];
17475  mData->insertMulti(newData.t, newData);
17476  }
17477 }
17478 
17484 {
17485  QCPCurveDataMap::iterator it = mData->begin();
17486  while (it != mData->end() && it.key() < t)
17487  it = mData->erase(it);
17488 }
17489 
17495 {
17496  if (mData->isEmpty()) return;
17497  QCPCurveDataMap::iterator it = mData->upperBound(t);
17498  while (it != mData->end())
17499  it = mData->erase(it);
17500 }
17501 
17509 void QCPCurve::removeData(double fromt, double tot)
17510 {
17511  if (fromt >= tot || mData->isEmpty()) return;
17512  QCPCurveDataMap::iterator it = mData->upperBound(fromt);
17513  QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
17514  while (it != itEnd)
17515  it = mData->erase(it);
17516 }
17517 
17527 void QCPCurve::removeData(double t)
17528 {
17529  mData->remove(t);
17530 }
17531 
17537 {
17538  mData->clear();
17539 }
17540 
17541 /* inherits documentation from base class */
17542 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17543 {
17544  Q_UNUSED(details)
17545  if ((onlySelectable && !mSelectable) || mData->isEmpty())
17546  return -1;
17547  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17548 
17549  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17550  return pointDistance(pos);
17551  else
17552  return -1;
17553 }
17554 
17555 /* inherits documentation from base class */
17557 {
17558  if (mData->isEmpty()) return;
17559 
17560  // allocate line vector:
17561  QVector<QPointF> *lineData = new QVector<QPointF>;
17562 
17563  // fill with curve data:
17564  getCurveData(lineData);
17565 
17566  // check data validity if flag set:
17567 #ifdef QCUSTOMPLOT_CHECK_DATA
17568  QCPCurveDataMap::const_iterator it;
17569  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17570  {
17571  if (QCP::isInvalidData(it.value().t) ||
17572  QCP::isInvalidData(it.value().key, it.value().value))
17573  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
17574  }
17575 #endif
17576 
17577  // draw curve fill:
17578  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17579  {
17580  applyFillAntialiasingHint(painter);
17581  painter->setPen(Qt::NoPen);
17582  painter->setBrush(mainBrush());
17583  painter->drawPolygon(QPolygonF(*lineData));
17584  }
17585 
17586  // draw curve line:
17587  if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17588  {
17590  painter->setPen(mainPen());
17591  painter->setBrush(Qt::NoBrush);
17592  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
17593  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17594  painter->pen().style() == Qt::SolidLine &&
17595  !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17596  !painter->modes().testFlag(QCPPainter::pmNoCaching))
17597  {
17598  int i = 1;
17599  int lineDataSize = lineData->size();
17600  while (i < lineDataSize)
17601  {
17602  if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17603  painter->drawLine(lineData->at(i-1), lineData->at(i));
17604  else
17605  ++i;
17606  ++i;
17607  }
17608  } else
17609  {
17610  int segmentStart = 0;
17611  int i = 0;
17612  int lineDataSize = lineData->size();
17613  while (i < lineDataSize)
17614  {
17615  if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17616  {
17617  painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
17618  segmentStart = i+1;
17619  }
17620  ++i;
17621  }
17622  // draw last segment:
17623  painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); // lineDataSize, because we do want to include the last point
17624  }
17625  }
17626 
17627  // draw scatters:
17628  if (!mScatterStyle.isNone())
17629  drawScatterPlot(painter, lineData);
17630 
17631  // free allocated line data:
17632  delete lineData;
17633 }
17634 
17635 /* inherits documentation from base class */
17636 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17637 {
17638  // draw fill:
17639  if (mBrush.style() != Qt::NoBrush)
17640  {
17641  applyFillAntialiasingHint(painter);
17642  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
17643  }
17644  // draw line vertically centered:
17645  if (mLineStyle != lsNone)
17646  {
17648  painter->setPen(mPen);
17649  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
17650  }
17651  // draw scatter symbol:
17652  if (!mScatterStyle.isNone())
17653  {
17655  // scale scatter pixmap if it's too large to fit in legend icon rect:
17656  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
17657  {
17658  QCPScatterStyle scaledStyle(mScatterStyle);
17659  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17660  scaledStyle.applyTo(painter, mPen);
17661  scaledStyle.drawShape(painter, QRectF(rect).center());
17662  } else
17663  {
17664  mScatterStyle.applyTo(painter, mPen);
17665  mScatterStyle.drawShape(painter, QRectF(rect).center());
17666  }
17667  }
17668 }
17669 
17675 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
17676 {
17677  // draw scatter point symbols:
17679  mScatterStyle.applyTo(painter, mPen);
17680  for (int i=0; i<pointData->size(); ++i)
17681  if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
17682  mScatterStyle.drawShape(painter, pointData->at(i));
17683 }
17684 
17698 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
17699 {
17700  QCPAxis *keyAxis = mKeyAxis.data();
17701  QCPAxis *valueAxis = mValueAxis.data();
17702  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17703 
17704  // add margins to rect to compensate for stroke width
17705  double strokeMargin = qMax(qreal(1.0), qreal(mainPen().widthF()*0.75)); // stroke radius + 50% safety
17706  if (!mScatterStyle.isNone())
17707  strokeMargin = qMax(strokeMargin, mScatterStyle.size());
17708  double rectLeft = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17709  double rectRight = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17710  double rectBottom = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)+strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17711  double rectTop = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)-strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17712  int currentRegion;
17713  QCPCurveDataMap::const_iterator it = mData->constBegin();
17714  QCPCurveDataMap::const_iterator prevIt = mData->constEnd()-1;
17715  int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom);
17716  QVector<QPointF> trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right)
17717  while (it != mData->constEnd())
17718  {
17719  currentRegion = getRegion(it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17720  if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
17721  {
17722  if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
17723  {
17724  QPointF crossA, crossB;
17725  if (prevRegion == 5) // we're coming from R, so add this point optimized
17726  {
17727  lineData->append(getOptimizedPoint(currentRegion, it.value().key, it.value().value, prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom));
17728  // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point
17729  *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17730  } else if (mayTraverse(prevRegion, currentRegion) &&
17731  getTraverse(prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom, crossA, crossB))
17732  {
17733  // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
17734  QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
17735  getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop, rectRight, rectBottom, beforeTraverseCornerPoints, afterTraverseCornerPoints);
17736  if (it != mData->constBegin())
17737  {
17738  *lineData << beforeTraverseCornerPoints;
17739  lineData->append(crossA);
17740  lineData->append(crossB);
17741  *lineData << afterTraverseCornerPoints;
17742  } else
17743  {
17744  lineData->append(crossB);
17745  *lineData << afterTraverseCornerPoints;
17746  trailingPoints << beforeTraverseCornerPoints << crossA ;
17747  }
17748  } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s)
17749  {
17750  *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17751  }
17752  } else // segment does end in R, so we add previous point optimized and this point at original position
17753  {
17754  if (it == mData->constBegin()) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end
17755  trailingPoints << getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17756  else
17757  lineData->append(getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom));
17758  lineData->append(coordsToPixels(it.value().key, it.value().value));
17759  }
17760  } else // region didn't change
17761  {
17762  if (currentRegion == 5) // still in R, keep adding original points
17763  {
17764  lineData->append(coordsToPixels(it.value().key, it.value().value));
17765  } else // still outside R, no need to add anything
17766  {
17767  // see how this is not doing anything? That's the main optimization...
17768  }
17769  }
17770  prevIt = it;
17771  prevRegion = currentRegion;
17772  ++it;
17773  }
17774  *lineData << trailingPoints;
17775 }
17776 
17795 int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17796 {
17797  if (x < rectLeft) // region 123
17798  {
17799  if (y > rectTop)
17800  return 1;
17801  else if (y < rectBottom)
17802  return 3;
17803  else
17804  return 2;
17805  } else if (x > rectRight) // region 789
17806  {
17807  if (y > rectTop)
17808  return 7;
17809  else if (y < rectBottom)
17810  return 9;
17811  else
17812  return 8;
17813  } else // region 456
17814  {
17815  if (y > rectTop)
17816  return 4;
17817  else if (y < rectBottom)
17818  return 6;
17819  else
17820  return 5;
17821  }
17822 }
17823 
17839 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17840 {
17841  double intersectKey = rectLeft; // initial value is just fail-safe
17842  double intersectValue = rectTop; // initial value is just fail-safe
17843  switch (otherRegion)
17844  {
17845  case 1: // top and left edge
17846  {
17847  intersectValue = rectTop;
17848  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17849  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17850  {
17851  intersectKey = rectLeft;
17852  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17853  }
17854  break;
17855  }
17856  case 2: // left edge
17857  {
17858  intersectKey = rectLeft;
17859  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17860  break;
17861  }
17862  case 3: // bottom and left edge
17863  {
17864  intersectValue = rectBottom;
17865  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17866  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17867  {
17868  intersectKey = rectLeft;
17869  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17870  }
17871  break;
17872  }
17873  case 4: // top edge
17874  {
17875  intersectValue = rectTop;
17876  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17877  break;
17878  }
17879  case 5:
17880  {
17881  break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
17882  }
17883  case 6: // bottom edge
17884  {
17885  intersectValue = rectBottom;
17886  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17887  break;
17888  }
17889  case 7: // top and right edge
17890  {
17891  intersectValue = rectTop;
17892  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17893  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17894  {
17895  intersectKey = rectRight;
17896  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17897  }
17898  break;
17899  }
17900  case 8: // right edge
17901  {
17902  intersectKey = rectRight;
17903  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17904  break;
17905  }
17906  case 9: // bottom and right edge
17907  {
17908  intersectValue = rectBottom;
17909  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17910  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17911  {
17912  intersectKey = rectRight;
17913  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17914  }
17915  break;
17916  }
17917  }
17918  return coordsToPixels(intersectKey, intersectValue);
17919 }
17920 
17939 QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17940 {
17941  QVector<QPointF> result;
17942  switch (prevRegion)
17943  {
17944  case 1:
17945  {
17946  switch (currentRegion)
17947  {
17948  case 2: { result << coordsToPixels(rectLeft, rectTop); break; }
17949  case 4: { result << coordsToPixels(rectLeft, rectTop); break; }
17950  case 3: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); break; }
17951  case 7: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); break; }
17952  case 6: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17953  case 8: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17954  case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17955  if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17956  { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17957  else
17958  { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17959  break;
17960  }
17961  }
17962  break;
17963  }
17964  case 2:
17965  {
17966  switch (currentRegion)
17967  {
17968  case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17969  case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17970  case 4: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17971  case 6: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17972  case 7: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17973  case 9: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17974  }
17975  break;
17976  }
17977  case 3:
17978  {
17979  switch (currentRegion)
17980  {
17981  case 2: { result << coordsToPixels(rectLeft, rectBottom); break; }
17982  case 6: { result << coordsToPixels(rectLeft, rectBottom); break; }
17983  case 1: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); break; }
17984  case 9: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); break; }
17985  case 4: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17986  case 8: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17987  case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17988  if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17989  { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17990  else
17991  { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17992  break;
17993  }
17994  }
17995  break;
17996  }
17997  case 4:
17998  {
17999  switch (currentRegion)
18000  {
18001  case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
18002  case 7: { result << coordsToPixels(rectRight, rectTop); break; }
18003  case 2: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
18004  case 8: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
18005  case 3: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
18006  case 9: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
18007  }
18008  break;
18009  }
18010  case 5:
18011  {
18012  switch (currentRegion)
18013  {
18014  case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
18015  case 7: { result << coordsToPixels(rectRight, rectTop); break; }
18016  case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
18017  case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
18018  }
18019  break;
18020  }
18021  case 6:
18022  {
18023  switch (currentRegion)
18024  {
18025  case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
18026  case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
18027  case 2: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
18028  case 8: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
18029  case 1: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
18030  case 7: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
18031  }
18032  break;
18033  }
18034  case 7:
18035  {
18036  switch (currentRegion)
18037  {
18038  case 4: { result << coordsToPixels(rectRight, rectTop); break; }
18039  case 8: { result << coordsToPixels(rectRight, rectTop); break; }
18040  case 1: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); break; }
18041  case 9: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); break; }
18042  case 2: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
18043  case 6: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
18044  case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
18045  if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
18046  { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
18047  else
18048  { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
18049  break;
18050  }
18051  }
18052  break;
18053  }
18054  case 8:
18055  {
18056  switch (currentRegion)
18057  {
18058  case 7: { result << coordsToPixels(rectRight, rectTop); break; }
18059  case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
18060  case 4: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
18061  case 6: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
18062  case 1: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
18063  case 3: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
18064  }
18065  break;
18066  }
18067  case 9:
18068  {
18069  switch (currentRegion)
18070  {
18071  case 6: { result << coordsToPixels(rectRight, rectBottom); break; }
18072  case 8: { result << coordsToPixels(rectRight, rectBottom); break; }
18073  case 3: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); break; }
18074  case 7: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); break; }
18075  case 2: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
18076  case 4: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
18077  case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
18078  if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
18079  { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
18080  else
18081  { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
18082  break;
18083  }
18084  }
18085  break;
18086  }
18087  }
18088  return result;
18089 }
18090 
18103 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
18104 {
18105  switch (prevRegion)
18106  {
18107  case 1:
18108  {
18109  switch (currentRegion)
18110  {
18111  case 4:
18112  case 7:
18113  case 2:
18114  case 3: return false;
18115  default: return true;
18116  }
18117  }
18118  case 2:
18119  {
18120  switch (currentRegion)
18121  {
18122  case 1:
18123  case 3: return false;
18124  default: return true;
18125  }
18126  }
18127  case 3:
18128  {
18129  switch (currentRegion)
18130  {
18131  case 1:
18132  case 2:
18133  case 6:
18134  case 9: return false;
18135  default: return true;
18136  }
18137  }
18138  case 4:
18139  {
18140  switch (currentRegion)
18141  {
18142  case 1:
18143  case 7: return false;
18144  default: return true;
18145  }
18146  }
18147  case 5: return false; // should never occur
18148  case 6:
18149  {
18150  switch (currentRegion)
18151  {
18152  case 3:
18153  case 9: return false;
18154  default: return true;
18155  }
18156  }
18157  case 7:
18158  {
18159  switch (currentRegion)
18160  {
18161  case 1:
18162  case 4:
18163  case 8:
18164  case 9: return false;
18165  default: return true;
18166  }
18167  }
18168  case 8:
18169  {
18170  switch (currentRegion)
18171  {
18172  case 7:
18173  case 9: return false;
18174  default: return true;
18175  }
18176  }
18177  case 9:
18178  {
18179  switch (currentRegion)
18180  {
18181  case 3:
18182  case 6:
18183  case 8:
18184  case 7: return false;
18185  default: return true;
18186  }
18187  }
18188  default: return true;
18189  }
18190 }
18191 
18192 
18206 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
18207 {
18208  QList<QPointF> intersections; // x of QPointF corresponds to key and y to value
18209  if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
18210  {
18211  // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
18212  intersections.append(QPointF(key, rectBottom)); // direction will be taken care of at end of method
18213  intersections.append(QPointF(key, rectTop));
18214  } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
18215  {
18216  // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
18217  intersections.append(QPointF(rectLeft, value)); // direction will be taken care of at end of method
18218  intersections.append(QPointF(rectRight, value));
18219  } else // line is skewed
18220  {
18221  double gamma;
18222  double keyPerValue = (key-prevKey)/(value-prevValue);
18223  // check top of rect:
18224  gamma = prevKey + (rectTop-prevValue)*keyPerValue;
18225  if (gamma >= rectLeft && gamma <= rectRight)
18226  intersections.append(QPointF(gamma, rectTop));
18227  // check bottom of rect:
18228  gamma = prevKey + (rectBottom-prevValue)*keyPerValue;
18229  if (gamma >= rectLeft && gamma <= rectRight)
18230  intersections.append(QPointF(gamma, rectBottom));
18231  double valuePerKey = 1.0/keyPerValue;
18232  // check left of rect:
18233  gamma = prevValue + (rectLeft-prevKey)*valuePerKey;
18234  if (gamma >= rectBottom && gamma <= rectTop)
18235  intersections.append(QPointF(rectLeft, gamma));
18236  // check right of rect:
18237  gamma = prevValue + (rectRight-prevKey)*valuePerKey;
18238  if (gamma >= rectBottom && gamma <= rectTop)
18239  intersections.append(QPointF(rectRight, gamma));
18240  }
18241 
18242  // handle cases where found points isn't exactly 2:
18243  if (intersections.size() > 2)
18244  {
18245  // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
18246  double distSqrMax = 0;
18247  QPointF pv1, pv2;
18248  for (int i=0; i<intersections.size()-1; ++i)
18249  {
18250  for (int k=i+1; k<intersections.size(); ++k)
18251  {
18252  QPointF distPoint = intersections.at(i)-intersections.at(k);
18253  double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
18254  if (distSqr > distSqrMax)
18255  {
18256  pv1 = intersections.at(i);
18257  pv2 = intersections.at(k);
18258  distSqrMax = distSqr;
18259  }
18260  }
18261  }
18262  intersections = QList<QPointF>() << pv1 << pv2;
18263  } else if (intersections.size() != 2)
18264  {
18265  // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
18266  return false;
18267  }
18268 
18269  // possibly re-sort points so optimized point segment has same direction as original segment:
18270  if ((key-prevKey)*(intersections.at(1).x()-intersections.at(0).x()) + (value-prevValue)*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction
18271  intersections.move(0, 1);
18272  crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
18273  crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
18274  return true;
18275 }
18276 
18302 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
18303 {
18304  switch (prevRegion)
18305  {
18306  case 1:
18307  {
18308  switch (currentRegion)
18309  {
18310  case 6: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
18311  case 9: { beforeTraverse << coordsToPixels(rectLeft, rectTop); afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
18312  case 8: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
18313  }
18314  break;
18315  }
18316  case 2:
18317  {
18318  switch (currentRegion)
18319  {
18320  case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
18321  case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
18322  }
18323  break;
18324  }
18325  case 3:
18326  {
18327  switch (currentRegion)
18328  {
18329  case 4: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
18330  case 7: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); afterTraverse << coordsToPixels(rectRight, rectTop); break; }
18331  case 8: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
18332  }
18333  break;
18334  }
18335  case 4:
18336  {
18337  switch (currentRegion)
18338  {
18339  case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
18340  case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
18341  }
18342  break;
18343  }
18344  case 5: { break; } // shouldn't happen because this method only handles full traverses
18345  case 6:
18346  {
18347  switch (currentRegion)
18348  {
18349  case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
18350  case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
18351  }
18352  break;
18353  }
18354  case 7:
18355  {
18356  switch (currentRegion)
18357  {
18358  case 2: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
18359  case 3: { beforeTraverse << coordsToPixels(rectRight, rectTop); afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
18360  case 6: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
18361  }
18362  break;
18363  }
18364  case 8:
18365  {
18366  switch (currentRegion)
18367  {
18368  case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
18369  case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
18370  }
18371  break;
18372  }
18373  case 9:
18374  {
18375  switch (currentRegion)
18376  {
18377  case 2: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
18378  case 1: { beforeTraverse << coordsToPixels(rectRight, rectBottom); afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
18379  case 4: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
18380  }
18381  break;
18382  }
18383  }
18384 }
18385 
18392 double QCPCurve::pointDistance(const QPointF &pixelPoint) const
18393 {
18394  if (mData->isEmpty())
18395  {
18396  qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
18397  return 500;
18398  }
18399  if (mData->size() == 1)
18400  {
18401  QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
18402  return QVector2D(dataPoint-pixelPoint).length();
18403  }
18404 
18405  // calculate minimum distance to line segments:
18406  QVector<QPointF> *lineData = new QVector<QPointF>;
18407  getCurveData(lineData);
18408  double minDistSqr = std::numeric_limits<double>::max();
18409  for (int i=0; i<lineData->size()-1; ++i)
18410  {
18411  double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
18412  if (currentDistSqr < minDistSqr)
18413  minDistSqr = currentDistSqr;
18414  }
18415  delete lineData;
18416  return qSqrt(minDistSqr);
18417 }
18418 
18419 /* inherits documentation from base class */
18420 QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
18421 {
18422  QCPRange range;
18423  bool haveLower = false;
18424  bool haveUpper = false;
18425 
18426  double current;
18427 
18428  QCPCurveDataMap::const_iterator it = mData->constBegin();
18429  while (it != mData->constEnd())
18430  {
18431  current = it.value().key;
18432  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
18433  {
18434  if (current < range.lower || !haveLower)
18435  {
18436  range.lower = current;
18437  haveLower = true;
18438  }
18439  if (current > range.upper || !haveUpper)
18440  {
18441  range.upper = current;
18442  haveUpper = true;
18443  }
18444  }
18445  ++it;
18446  }
18447 
18448  foundRange = haveLower && haveUpper;
18449  return range;
18450 }
18451 
18452 /* inherits documentation from base class */
18453 QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
18454 {
18455  QCPRange range;
18456  bool haveLower = false;
18457  bool haveUpper = false;
18458 
18459  double current;
18460 
18461  QCPCurveDataMap::const_iterator it = mData->constBegin();
18462  while (it != mData->constEnd())
18463  {
18464  current = it.value().value;
18465  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
18466  {
18467  if (current < range.lower || !haveLower)
18468  {
18469  range.lower = current;
18470  haveLower = true;
18471  }
18472  if (current > range.upper || !haveUpper)
18473  {
18474  range.upper = current;
18475  haveUpper = true;
18476  }
18477  }
18478  ++it;
18479  }
18480 
18481  foundRange = haveLower && haveUpper;
18482  return range;
18483 }
18484 
18485 
18489 
18523 /* start of documentation of inline functions */
18524 
18551 /* end of documentation of inline functions */
18552 
18557  QObject(parentPlot),
18558  mParentPlot(parentPlot),
18559  mSpacingType(stAbsolute),
18560  mSpacing(4)
18561 {
18562 }
18563 
18565 {
18566  clear();
18567 }
18568 
18577 {
18579 }
18580 
18588 {
18589  mSpacing = spacing;
18590 }
18591 
18598 QCPBars *QCPBarsGroup::bars(int index) const
18599 {
18600  if (index >= 0 && index < mBars.size())
18601  {
18602  return mBars.at(index);
18603  } else
18604  {
18605  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
18606  return 0;
18607  }
18608 }
18609 
18616 {
18617  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
18618  bars->setBarsGroup(0); // removes itself via removeBars
18619 }
18620 
18628 {
18629  if (!bars)
18630  {
18631  qDebug() << Q_FUNC_INFO << "bars is 0";
18632  return;
18633  }
18634 
18635  if (!mBars.contains(bars))
18636  bars->setBarsGroup(this);
18637  else
18638  qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
18639 }
18640 
18651 {
18652  if (!bars)
18653  {
18654  qDebug() << Q_FUNC_INFO << "bars is 0";
18655  return;
18656  }
18657 
18658  // first append to bars list normally:
18659  if (!mBars.contains(bars))
18660  bars->setBarsGroup(this);
18661  // then move to according position:
18662  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
18663 }
18664 
18671 {
18672  if (!bars)
18673  {
18674  qDebug() << Q_FUNC_INFO << "bars is 0";
18675  return;
18676  }
18677 
18678  if (mBars.contains(bars))
18679  bars->setBarsGroup(0);
18680  else
18681  qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
18682 }
18683 
18692 {
18693  if (!mBars.contains(bars))
18694  mBars.append(bars);
18695 }
18696 
18705 {
18706  mBars.removeOne(bars);
18707 }
18708 
18715 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
18716 {
18717  // find list of all base bars in case some mBars are stacked:
18718  QList<const QCPBars*> baseBars;
18719  foreach (const QCPBars *b, mBars)
18720  {
18721  while (b->barBelow())
18722  b = b->barBelow();
18723  if (!baseBars.contains(b))
18724  baseBars.append(b);
18725  }
18726  // find base bar this "bars" is stacked on:
18727  const QCPBars *thisBase = bars;
18728  while (thisBase->barBelow())
18729  thisBase = thisBase->barBelow();
18730 
18731  // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
18732  double result = 0;
18733  int index = baseBars.indexOf(thisBase);
18734  if (index >= 0)
18735  {
18736  int startIndex;
18737  double lowerPixelWidth, upperPixelWidth;
18738  if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
18739  {
18740  return result;
18741  } else if (index < (baseBars.size()-1)/2.0) // bar is to the left of center
18742  {
18743  if (baseBars.size() % 2 == 0) // even number of bars
18744  {
18745  startIndex = baseBars.size()/2-1;
18746  result -= getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18747  } else // uneven number of bars
18748  {
18749  startIndex = (baseBars.size()-1)/2-1;
18750  baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18751  result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18752  result -= getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18753  }
18754  for (int i=startIndex; i>index; --i) // add widths and spacings of bars in between center and our bars
18755  {
18756  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18757  result -= qAbs(upperPixelWidth-lowerPixelWidth);
18758  result -= getPixelSpacing(baseBars.at(i), keyCoord);
18759  }
18760  // finally half of our bars width:
18761  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18762  result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18763  } else // bar is to the right of center
18764  {
18765  if (baseBars.size() % 2 == 0) // even number of bars
18766  {
18767  startIndex = baseBars.size()/2;
18768  result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18769  } else // uneven number of bars
18770  {
18771  startIndex = (baseBars.size()-1)/2+1;
18772  baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18773  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18774  result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18775  }
18776  for (int i=startIndex; i<index; ++i) // add widths and spacings of bars in between center and our bars
18777  {
18778  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18779  result += qAbs(upperPixelWidth-lowerPixelWidth);
18780  result += getPixelSpacing(baseBars.at(i), keyCoord);
18781  }
18782  // finally half of our bars width:
18783  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18784  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18785  }
18786  }
18787  return result;
18788 }
18789 
18800 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
18801 {
18802  switch (mSpacingType)
18803  {
18804  case stAbsolute:
18805  {
18806  return mSpacing;
18807  }
18808  case stAxisRectRatio:
18809  {
18810  if (bars->keyAxis()->orientation() == Qt::Horizontal)
18811  return bars->keyAxis()->axisRect()->width()*mSpacing;
18812  else
18813  return bars->keyAxis()->axisRect()->height()*mSpacing;
18814  }
18815  case stPlotCoords:
18816  {
18817  double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
18818  return bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel;
18819  }
18820  }
18821  return 0;
18822 }
18823 
18824 
18828 
18845  key(0),
18846  value(0)
18847 {
18848 }
18849 
18854  key(key),
18855  value(value)
18856 {
18857 }
18858 
18859 
18863 
18903 /* start of documentation of inline functions */
18904 
18919 /* end of documentation of inline functions */
18920 
18930 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
18931  QCPAbstractPlottable(keyAxis, valueAxis),
18932  mData(new QCPBarDataMap),
18933  mWidth(0.75),
18934  mWidthType(wtPlotCoords),
18935  mBarsGroup(0),
18936  mBaseValue(0)
18937 {
18938  // modify inherited properties from abstract plottable:
18939  mPen.setColor(Qt::blue);
18940  mPen.setStyle(Qt::SolidLine);
18941  mBrush.setColor(QColor(40, 50, 255, 30));
18942  mBrush.setStyle(Qt::SolidPattern);
18943  mSelectedPen = mPen;
18944  mSelectedPen.setWidthF(2.5);
18945  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
18947 }
18948 
18950 {
18951  setBarsGroup(0);
18952  if (mBarBelow || mBarAbove)
18953  connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
18954  delete mData;
18955 }
18956 
18964 {
18965  mWidth = width;
18966 }
18967 
18977 {
18979 }
18980 
18988 {
18989  // deregister at old group:
18990  if (mBarsGroup)
18991  mBarsGroup->unregisterBars(this);
18993  // register at new group:
18994  if (mBarsGroup)
18995  mBarsGroup->registerBars(this);
18996 }
18997 
19011 {
19013 }
19014 
19023 {
19024  if (mData == data)
19025  {
19026  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
19027  return;
19028  }
19029  if (copy)
19030  {
19031  *mData = *data;
19032  } else
19033  {
19034  delete mData;
19035  mData = data;
19036  }
19037 }
19038 
19045 void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
19046 {
19047  mData->clear();
19048  int n = key.size();
19049  n = qMin(n, value.size());
19050  QCPBarData newData;
19051  for (int i=0; i<n; ++i)
19052  {
19053  newData.key = key[i];
19054  newData.value = value[i];
19055  mData->insertMulti(newData.key, newData);
19056  }
19057 }
19058 
19074 {
19075  if (bars == this) return;
19076  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
19077  {
19078  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
19079  return;
19080  }
19081  // remove from stacking:
19082  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
19083  // if new bar given, insert this bar below it:
19084  if (bars)
19085  {
19086  if (bars->mBarBelow)
19087  connectBars(bars->mBarBelow.data(), this);
19088  connectBars(this, bars);
19089  }
19090 }
19091 
19107 {
19108  if (bars == this) return;
19109  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
19110  {
19111  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
19112  return;
19113  }
19114  // remove from stacking:
19115  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
19116  // if new bar given, insert this bar above it:
19117  if (bars)
19118  {
19119  if (bars->mBarAbove)
19120  connectBars(this, bars->mBarAbove.data());
19121  connectBars(bars, this);
19122  }
19123 }
19124 
19129 void QCPBars::addData(const QCPBarDataMap &dataMap)
19130 {
19131  mData->unite(dataMap);
19132 }
19133 
19139 {
19140  mData->insertMulti(data.key, data);
19141 }
19142 
19147 void QCPBars::addData(double key, double value)
19148 {
19149  QCPBarData newData;
19150  newData.key = key;
19151  newData.value = value;
19152  mData->insertMulti(newData.key, newData);
19153 }
19154 
19159 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
19160 {
19161  int n = keys.size();
19162  n = qMin(n, values.size());
19163  QCPBarData newData;
19164  for (int i=0; i<n; ++i)
19165  {
19166  newData.key = keys[i];
19167  newData.value = values[i];
19168  mData->insertMulti(newData.key, newData);
19169  }
19170 }
19171 
19177 {
19178  QCPBarDataMap::iterator it = mData->begin();
19179  while (it != mData->end() && it.key() < key)
19180  it = mData->erase(it);
19181 }
19182 
19188 {
19189  if (mData->isEmpty()) return;
19190  QCPBarDataMap::iterator it = mData->upperBound(key);
19191  while (it != mData->end())
19192  it = mData->erase(it);
19193 }
19194 
19202 void QCPBars::removeData(double fromKey, double toKey)
19203 {
19204  if (fromKey >= toKey || mData->isEmpty()) return;
19205  QCPBarDataMap::iterator it = mData->upperBound(fromKey);
19206  QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
19207  while (it != itEnd)
19208  it = mData->erase(it);
19209 }
19210 
19219 void QCPBars::removeData(double key)
19220 {
19221  mData->remove(key);
19222 }
19223 
19229 {
19230  mData->clear();
19231 }
19232 
19233 /* inherits documentation from base class */
19234 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19235 {
19236  Q_UNUSED(details)
19237  if (onlySelectable && !mSelectable)
19238  return -1;
19239  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19240 
19241  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19242  {
19243  QCPBarDataMap::ConstIterator it;
19244  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
19245  {
19246  if (getBarPolygon(it.value().key, it.value().value).boundingRect().contains(pos))
19247  return mParentPlot->selectionTolerance()*0.99;
19248  }
19249  }
19250  return -1;
19251 }
19252 
19253 /* inherits documentation from base class */
19255 {
19256  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
19257  if (mData->isEmpty()) return;
19258 
19259  QCPBarDataMap::const_iterator it, lower, upperEnd;
19260  getVisibleDataBounds(lower, upperEnd);
19261  for (it = lower; it != upperEnd; ++it)
19262  {
19263  // check data validity if flag set:
19264 #ifdef QCUSTOMPLOT_CHECK_DATA
19265  if (QCP::isInvalidData(it.value().key, it.value().value))
19266  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
19267 #endif
19268  QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
19269  // draw bar fill:
19270  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
19271  {
19272  applyFillAntialiasingHint(painter);
19273  painter->setPen(Qt::NoPen);
19274  painter->setBrush(mainBrush());
19275  painter->drawPolygon(barPolygon);
19276  }
19277  // draw bar line:
19278  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
19279  {
19281  painter->setPen(mainPen());
19282  painter->setBrush(Qt::NoBrush);
19283  painter->drawPolyline(barPolygon);
19284  }
19285  }
19286 }
19287 
19288 /* inherits documentation from base class */
19289 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19290 {
19291  // draw filled rect:
19293  painter->setBrush(mBrush);
19294  painter->setPen(mPen);
19295  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
19296  r.moveCenter(rect.center());
19297  painter->drawRect(r);
19298 }
19299 
19314 void QCPBars::getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
19315 {
19316  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
19317  if (mData->isEmpty())
19318  {
19319  lower = mData->constEnd();
19320  upperEnd = mData->constEnd();
19321  return;
19322  }
19323 
19324  // get visible data range as QMap iterators
19325  lower = mData->lowerBound(mKeyAxis.data()->range().lower);
19326  upperEnd = mData->upperBound(mKeyAxis.data()->range().upper);
19327  double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
19328  double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
19329  bool isVisible = false;
19330  // walk left from lbound to find lower bar that actually is completely outside visible pixel range:
19331  QCPBarDataMap::const_iterator it = lower;
19332  while (it != mData->constBegin())
19333  {
19334  --it;
19335  QRectF barBounds = getBarPolygon(it.value().key, it.value().value).boundingRect();
19336  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
19337  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.left() <= lowerPixelBound));
19338  else // keyaxis is vertical
19339  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= lowerPixelBound));
19340  if (isVisible)
19341  lower = it;
19342  else
19343  break;
19344  }
19345  // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
19346  it = upperEnd;
19347  while (it != mData->constEnd())
19348  {
19349  QRectF barBounds = getBarPolygon(upperEnd.value().key, upperEnd.value().value).boundingRect();
19350  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
19351  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.right() >= upperPixelBound));
19352  else // keyaxis is vertical
19353  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.top() <= upperPixelBound));
19354  if (isVisible)
19355  upperEnd = it+1;
19356  else
19357  break;
19358  ++it;
19359  }
19360 }
19361 
19368 QPolygonF QCPBars::getBarPolygon(double key, double value) const
19369 {
19370  QCPAxis *keyAxis = mKeyAxis.data();
19371  QCPAxis *valueAxis = mValueAxis.data();
19372  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
19373 
19374  QPolygonF result;
19375  double lowerPixelWidth, upperPixelWidth;
19376  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
19377  double base = getStackedBaseValue(key, value >= 0);
19378  double basePixel = valueAxis->coordToPixel(base);
19379  double valuePixel = valueAxis->coordToPixel(base+value);
19380  double keyPixel = keyAxis->coordToPixel(key);
19381  if (mBarsGroup)
19382  keyPixel += mBarsGroup->keyPixelOffset(this, key);
19383  if (keyAxis->orientation() == Qt::Horizontal)
19384  {
19385  result << QPointF(keyPixel+lowerPixelWidth, basePixel);
19386  result << QPointF(keyPixel+lowerPixelWidth, valuePixel);
19387  result << QPointF(keyPixel+upperPixelWidth, valuePixel);
19388  result << QPointF(keyPixel+upperPixelWidth, basePixel);
19389  } else
19390  {
19391  result << QPointF(basePixel, keyPixel+lowerPixelWidth);
19392  result << QPointF(valuePixel, keyPixel+lowerPixelWidth);
19393  result << QPointF(valuePixel, keyPixel+upperPixelWidth);
19394  result << QPointF(basePixel, keyPixel+upperPixelWidth);
19395  }
19396  return result;
19397 }
19398 
19408 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
19409 {
19410  switch (mWidthType)
19411  {
19412  case wtAbsolute:
19413  {
19414  upper = mWidth*0.5;
19415  lower = -upper;
19416  if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
19417  qSwap(lower, upper);
19418  break;
19419  }
19420  case wtAxisRectRatio:
19421  {
19422  if (mKeyAxis && mKeyAxis.data()->axisRect())
19423  {
19424  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
19425  upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5;
19426  else
19427  upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5;
19428  lower = -upper;
19429  if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
19430  qSwap(lower, upper);
19431  } else
19432  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
19433  break;
19434  }
19435  case wtPlotCoords:
19436  {
19437  if (mKeyAxis)
19438  {
19439  double keyPixel = mKeyAxis.data()->coordToPixel(key);
19440  upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
19441  lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
19442  // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
19443  // coordinate transform which includes range direction
19444  } else
19445  qDebug() << Q_FUNC_INFO << "No key axis defined";
19446  break;
19447  }
19448  }
19449 }
19450 
19460 double QCPBars::getStackedBaseValue(double key, bool positive) const
19461 {
19462  if (mBarBelow)
19463  {
19464  double max = 0; // don't use mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
19465  // find bars of mBarBelow that are approximately at key and find largest one:
19466  double epsilon = qAbs(key)*1e-6; // should be safe even when changed to use float at some point
19467  if (key == 0)
19468  epsilon = 1e-6;
19469  QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-epsilon);
19470  QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+epsilon);
19471  while (it != itEnd)
19472  {
19473  if ((positive && it.value().value > max) ||
19474  (!positive && it.value().value < max))
19475  max = it.value().value;
19476  ++it;
19477  }
19478  // recurse down the bar-stack to find the total height:
19479  return max + mBarBelow.data()->getStackedBaseValue(key, positive);
19480  } else
19481  return mBaseValue;
19482 }
19483 
19493 {
19494  if (!lower && !upper) return;
19495 
19496  if (!lower) // disconnect upper at bottom
19497  {
19498  // disconnect old bar below upper:
19499  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19500  upper->mBarBelow.data()->mBarAbove = 0;
19501  upper->mBarBelow = 0;
19502  } else if (!upper) // disconnect lower at top
19503  {
19504  // disconnect old bar above lower:
19505  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19506  lower->mBarAbove.data()->mBarBelow = 0;
19507  lower->mBarAbove = 0;
19508  } else // connect lower and upper
19509  {
19510  // disconnect old bar above lower:
19511  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19512  lower->mBarAbove.data()->mBarBelow = 0;
19513  // disconnect old bar below upper:
19514  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19515  upper->mBarBelow.data()->mBarAbove = 0;
19516  lower->mBarAbove = upper;
19517  upper->mBarBelow = lower;
19518  }
19519 }
19520 
19521 /* inherits documentation from base class */
19522 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19523 {
19524  QCPRange range;
19525  bool haveLower = false;
19526  bool haveUpper = false;
19527 
19528  double current;
19529  QCPBarDataMap::const_iterator it = mData->constBegin();
19530  while (it != mData->constEnd())
19531  {
19532  current = it.value().key;
19533  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19534  {
19535  if (current < range.lower || !haveLower)
19536  {
19537  range.lower = current;
19538  haveLower = true;
19539  }
19540  if (current > range.upper || !haveUpper)
19541  {
19542  range.upper = current;
19543  haveUpper = true;
19544  }
19545  }
19546  ++it;
19547  }
19548  // determine exact range of bars by including bar width and barsgroup offset:
19549  if (haveLower && mKeyAxis)
19550  {
19551  double lowerPixelWidth, upperPixelWidth, keyPixel;
19552  getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
19553  keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
19554  if (mBarsGroup)
19555  keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
19556  range.lower = mKeyAxis.data()->pixelToCoord(keyPixel);
19557  }
19558  if (haveUpper && mKeyAxis)
19559  {
19560  double lowerPixelWidth, upperPixelWidth, keyPixel;
19561  getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
19562  keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
19563  if (mBarsGroup)
19564  keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
19565  range.upper = mKeyAxis.data()->pixelToCoord(keyPixel);
19566  }
19567  foundRange = haveLower && haveUpper;
19568  return range;
19569 }
19570 
19571 /* inherits documentation from base class */
19572 QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19573 {
19574  QCPRange range;
19575  range.lower = mBaseValue;
19576  range.upper = mBaseValue;
19577  bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
19578  bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
19579  double current;
19580 
19581  QCPBarDataMap::const_iterator it = mData->constBegin();
19582  while (it != mData->constEnd())
19583  {
19584  current = it.value().value + getStackedBaseValue(it.value().key, it.value().value >= 0);
19585  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19586  {
19587  if (current < range.lower || !haveLower)
19588  {
19589  range.lower = current;
19590  haveLower = true;
19591  }
19592  if (current > range.upper || !haveUpper)
19593  {
19594  range.upper = current;
19595  haveUpper = true;
19596  }
19597  }
19598  ++it;
19599  }
19600 
19601  foundRange = true; // return true because bar charts always have the 0-line visible
19602  return range;
19603 }
19604 
19605 
19609 
19673  QCPAbstractPlottable(keyAxis, valueAxis),
19674  mKey(0),
19675  mMinimum(0),
19676  mLowerQuartile(0),
19677  mMedian(0),
19678  mUpperQuartile(0),
19679  mMaximum(0)
19680 {
19682  setWhiskerWidth(0.2);
19683  setWidth(0.5);
19684 
19685  setPen(QPen(Qt::black));
19686  setSelectedPen(QPen(Qt::blue, 2.5));
19687  setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
19688  setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
19689  setWhiskerBarPen(QPen(Qt::black));
19690  setBrush(Qt::NoBrush);
19691  setSelectedBrush(Qt::NoBrush);
19692 }
19693 
19698 {
19699  mKey = key;
19700 }
19701 
19709 {
19710  mMinimum = value;
19711 }
19712 
19721 {
19722  mLowerQuartile = value;
19723 }
19724 
19733 {
19734  mMedian = value;
19735 }
19736 
19745 {
19746  mUpperQuartile = value;
19747 }
19748 
19756 {
19757  mMaximum = value;
19758 }
19759 
19767 void QCPStatisticalBox::setOutliers(const QVector<double> &values)
19768 {
19769  mOutliers = values;
19770 }
19771 
19777 void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
19778 {
19779  setKey(key);
19780  setMinimum(minimum);
19781  setLowerQuartile(lowerQuartile);
19782  setMedian(median);
19783  setUpperQuartile(upperQuartile);
19784  setMaximum(maximum);
19785 }
19786 
19793 {
19794  mWidth = width;
19795 }
19796 
19803 {
19804  mWhiskerWidth = width;
19805 }
19806 
19816 {
19817  mWhiskerPen = pen;
19818 }
19819 
19827 {
19828  mWhiskerBarPen = pen;
19829 }
19830 
19835 {
19836  mMedianPen = pen;
19837 }
19838 
19845 {
19846  mOutlierStyle = style;
19847 }
19848 
19849 /* inherits documentation from base class */
19851 {
19852  setOutliers(QVector<double>());
19853  setKey(0);
19854  setMinimum(0);
19855  setLowerQuartile(0);
19856  setMedian(0);
19857  setUpperQuartile(0);
19858  setMaximum(0);
19859 }
19860 
19861 /* inherits documentation from base class */
19862 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19863 {
19864  Q_UNUSED(details)
19865  if (onlySelectable && !mSelectable)
19866  return -1;
19867  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19868 
19869  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19870  {
19871  double posKey, posValue;
19872  pixelsToCoords(pos, posKey, posValue);
19873  // quartile box:
19874  QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19875  QCPRange valueRange(mLowerQuartile, mUpperQuartile);
19876  if (keyRange.contains(posKey) && valueRange.contains(posValue))
19877  return mParentPlot->selectionTolerance()*0.99;
19878 
19879  // min/max whiskers:
19880  if (QCPRange(mMinimum, mMaximum).contains(posValue))
19881  return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
19882  }
19883  return -1;
19884 }
19885 
19886 /* inherits documentation from base class */
19888 {
19889  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
19890 
19891  // check data validity if flag set:
19892 #ifdef QCUSTOMPLOT_CHECK_DATA
19896  qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
19897  for (int i=0; i<mOutliers.size(); ++i)
19898  if (QCP::isInvalidData(mOutliers.at(i)))
19899  qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
19900 #endif
19901 
19902  QRectF quartileBox;
19903  drawQuartileBox(painter, &quartileBox);
19904 
19905  painter->save();
19906  painter->setClipRect(quartileBox, Qt::IntersectClip);
19907  drawMedian(painter);
19908  painter->restore();
19909 
19910  drawWhiskers(painter);
19911  drawOutliers(painter);
19912 }
19913 
19914 /* inherits documentation from base class */
19915 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19916 {
19917  // draw filled rect:
19919  painter->setPen(mPen);
19920  painter->setBrush(mBrush);
19921  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
19922  r.moveCenter(rect.center());
19923  painter->drawRect(r);
19924 }
19925 
19932 void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
19933 {
19934  QRectF box;
19935  box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
19936  box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
19938  painter->setPen(mainPen());
19939  painter->setBrush(mainBrush());
19940  painter->drawRect(box);
19941  if (quartileBox)
19942  *quartileBox = box;
19943 }
19944 
19950 {
19951  QLineF medianLine;
19952  medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
19953  medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
19955  painter->setPen(mMedianPen);
19956  painter->drawLine(medianLine);
19957 }
19958 
19964 {
19965  QLineF backboneMin, backboneMax, barMin, barMax;
19966  backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
19967  backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
19971  painter->setPen(mWhiskerPen);
19972  painter->drawLine(backboneMin);
19973  painter->drawLine(backboneMax);
19974  painter->setPen(mWhiskerBarPen);
19975  painter->drawLine(barMin);
19976  painter->drawLine(barMax);
19977 }
19978 
19984 {
19986  mOutlierStyle.applyTo(painter, mPen);
19987  for (int i=0; i<mOutliers.size(); ++i)
19989 }
19990 
19991 /* inherits documentation from base class */
19992 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19993 {
19994  foundRange = true;
19995  if (inSignDomain == sdBoth)
19996  {
19997  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19998  } else if (inSignDomain == sdNegative)
19999  {
20000  if (mKey+mWidth*0.5 < 0)
20001  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
20002  else if (mKey < 0)
20003  return QCPRange(mKey-mWidth*0.5, mKey);
20004  else
20005  {
20006  foundRange = false;
20007  return QCPRange();
20008  }
20009  } else if (inSignDomain == sdPositive)
20010  {
20011  if (mKey-mWidth*0.5 > 0)
20012  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
20013  else if (mKey > 0)
20014  return QCPRange(mKey, mKey+mWidth*0.5);
20015  else
20016  {
20017  foundRange = false;
20018  return QCPRange();
20019  }
20020  }
20021  foundRange = false;
20022  return QCPRange();
20023 }
20024 
20025 /* inherits documentation from base class */
20026 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
20027 {
20028  QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
20029  values.reserve(mOutliers.size() + 5);
20030  values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
20031  values << mOutliers;
20032  // go through values and find the ones in legal range:
20033  bool haveUpper = false;
20034  bool haveLower = false;
20035  double upper = 0;
20036  double lower = 0;
20037  for (int i=0; i<values.size(); ++i)
20038  {
20039  if ((inSignDomain == sdNegative && values.at(i) < 0) ||
20040  (inSignDomain == sdPositive && values.at(i) > 0) ||
20041  (inSignDomain == sdBoth))
20042  {
20043  if (values.at(i) > upper || !haveUpper)
20044  {
20045  upper = values.at(i);
20046  haveUpper = true;
20047  }
20048  if (values.at(i) < lower || !haveLower)
20049  {
20050  lower = values.at(i);
20051  haveLower = true;
20052  }
20053  }
20054  }
20055  // return the bounds if we found some sensible values:
20056  if (haveLower && haveUpper)
20057  {
20058  foundRange = true;
20059  return QCPRange(lower, upper);
20060  } else // might happen if all values are in other sign domain
20061  {
20062  foundRange = false;
20063  return QCPRange();
20064  }
20065 }
20066 
20067 
20071 
20100 /* start of documentation of inline functions */
20101 
20108 /* end of documentation of inline functions */
20109 
20117 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
20118  mKeySize(0),
20119  mValueSize(0),
20120  mKeyRange(keyRange),
20121  mValueRange(valueRange),
20122  mIsEmpty(true),
20123  mData(0),
20124  mDataModified(true)
20125 {
20126  setSize(keySize, valueSize);
20127  fill(0);
20128 }
20129 
20131 {
20132  if (mData)
20133  delete[] mData;
20134 }
20135 
20140  mKeySize(0),
20141  mValueSize(0),
20142  mIsEmpty(true),
20143  mData(0),
20144  mDataModified(true)
20145 {
20146  *this = other;
20147 }
20148 
20153 {
20154  if (&other != this)
20155  {
20156  const int keySize = other.keySize();
20157  const int valueSize = other.valueSize();
20158  setSize(keySize, valueSize);
20159  setRange(other.keyRange(), other.valueRange());
20160  if (!mIsEmpty)
20161  memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
20162  mDataBounds = other.mDataBounds;
20163  mDataModified = true;
20164  }
20165  return *this;
20166 }
20167 
20168 /* undocumented getter */
20169 double QCPColorMapData::data(double key, double value)
20170 {
20171  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
20172  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
20173  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
20174  return mData[valueCell*mKeySize + keyCell];
20175  else
20176  return 0;
20177 }
20178 
20179 /* undocumented getter */
20180 double QCPColorMapData::cell(int keyIndex, int valueIndex)
20181 {
20182  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
20183  return mData[valueIndex*mKeySize + keyIndex];
20184  else
20185  return 0;
20186 }
20187 
20201 {
20202  if (keySize != mKeySize || valueSize != mValueSize)
20203  {
20204  mKeySize = keySize;
20206  if (mData)
20207  delete[] mData;
20208  mIsEmpty = mKeySize == 0 || mValueSize == 0;
20209  if (!mIsEmpty)
20210  {
20211 #ifdef __EXCEPTIONS
20212  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
20213 #endif
20214  mData = new double[mKeySize*mValueSize];
20215 #ifdef __EXCEPTIONS
20216  } catch (...) { mData = 0; }
20217 #endif
20218  if (mData)
20219  fill(0);
20220  else
20221  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
20222  } else
20223  mData = 0;
20224  mDataModified = true;
20225  }
20226 }
20227 
20239 {
20240  setSize(keySize, mValueSize);
20241 }
20242 
20254 {
20255  setSize(mKeySize, valueSize);
20256 }
20257 
20269 {
20270  setKeyRange(keyRange);
20271  setValueRange(valueRange);
20272 }
20273 
20285 {
20286  mKeyRange = keyRange;
20287 }
20288 
20300 {
20302 }
20303 
20310 void QCPColorMapData::setData(double key, double value, double z)
20311 {
20312  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
20313  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
20314  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
20315  {
20316  mData[valueCell*mKeySize + keyCell] = z;
20317  if (z < mDataBounds.lower)
20318  mDataBounds.lower = z;
20319  if (z > mDataBounds.upper)
20320  mDataBounds.upper = z;
20321  mDataModified = true;
20322  }
20323 }
20324 
20336 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
20337 {
20338  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
20339  {
20340  mData[valueIndex*mKeySize + keyIndex] = z;
20341  if (z < mDataBounds.lower)
20342  mDataBounds.lower = z;
20343  if (z > mDataBounds.upper)
20344  mDataBounds.upper = z;
20345  mDataModified = true;
20346  }
20347 }
20348 
20363 {
20364  if (mKeySize > 0 && mValueSize > 0)
20365  {
20366  double minHeight = mData[0];
20367  double maxHeight = mData[0];
20368  const int dataCount = mValueSize*mKeySize;
20369  for (int i=0; i<dataCount; ++i)
20370  {
20371  if (mData[i] > maxHeight)
20372  maxHeight = mData[i];
20373  if (mData[i] < minHeight)
20374  minHeight = mData[i];
20375  }
20376  mDataBounds.lower = minHeight;
20377  mDataBounds.upper = maxHeight;
20378  }
20379 }
20380 
20387 {
20388  setSize(0, 0);
20389 }
20390 
20395 {
20396  const int dataCount = mValueSize*mKeySize;
20397  for (int i=0; i<dataCount; ++i)
20398  mData[i] = z;
20399  mDataBounds = QCPRange(z, z);
20400 }
20401 
20414 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
20415 {
20416  if (keyIndex)
20417  *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
20418  if (valueIndex)
20419  *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
20420 }
20421 
20432 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
20433 {
20434  if (key)
20435  *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
20436  if (value)
20437  *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
20438 }
20439 
20440 
20444 
20517 /* start documentation of inline functions */
20518 
20527 /* end documentation of inline functions */
20528 
20529 /* start documentation of signals */
20530 
20552 /* end documentation of signals */
20553 
20561  QCPAbstractPlottable(keyAxis, valueAxis),
20562  mDataScaleType(QCPAxis::stLinear),
20563  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
20564  mInterpolate(true),
20565  mTightBoundary(false),
20566  mMapImageInvalidated(true)
20567 {
20568 }
20569 
20571 {
20572  delete mMapData;
20573 }
20574 
20583 {
20584  if (mMapData == data)
20585  {
20586  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20587  return;
20588  }
20589  if (copy)
20590  {
20591  *mMapData = *data;
20592  } else
20593  {
20594  delete mMapData;
20595  mMapData = data;
20596  }
20597  mMapImageInvalidated = true;
20598 }
20599 
20609 {
20610  if (!QCPRange::validRange(dataRange)) return;
20611  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
20612  {
20614  mDataRange = dataRange.sanitizedForLogScale();
20615  else
20616  mDataRange = dataRange.sanitizedForLinScale();
20617  mMapImageInvalidated = true;
20619  }
20620 }
20621 
20628 {
20629  if (mDataScaleType != scaleType)
20630  {
20631  mDataScaleType = scaleType;
20632  mMapImageInvalidated = true;
20636  }
20637 }
20638 
20651 {
20652  if (mGradient != gradient)
20653  {
20654  mGradient = gradient;
20655  mMapImageInvalidated = true;
20656  emit gradientChanged(mGradient);
20657  }
20658 }
20659 
20667 {
20668  mInterpolate = enabled;
20669 }
20670 
20683 {
20684  mTightBoundary = enabled;
20685 }
20686 
20702 {
20703  if (mColorScale) // unconnect signals from old color scale
20704  {
20705  disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20706  disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20707  disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20708  disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20709  disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20710  disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20711  }
20713  if (mColorScale) // connect signals to new color scale
20714  {
20715  setGradient(mColorScale.data()->gradient());
20716  setDataRange(mColorScale.data()->dataRange());
20717  setDataScaleType(mColorScale.data()->dataScaleType());
20718  connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20719  connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20720  connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20721  connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20722  connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20723  connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20724  }
20725 }
20726 
20747 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
20748 {
20749  if (recalculateDataBounds)
20752 }
20753 
20768 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
20769 {
20770  if (mMapImage.isNull() && !data()->isEmpty())
20771  updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
20772 
20773  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
20774  {
20775  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20776  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20777  mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
20778  }
20779 }
20780 
20786 {
20787  mMapData->clear();
20788 }
20789 
20790 /* inherits documentation from base class */
20791 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20792 {
20793  Q_UNUSED(details)
20794  if (onlySelectable && !mSelectable)
20795  return -1;
20796  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20797 
20798  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20799  {
20800  double posKey, posValue;
20801  pixelsToCoords(pos, posKey, posValue);
20802  if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
20803  return mParentPlot->selectionTolerance()*0.99;
20804  }
20805  return -1;
20806 }
20807 
20818 {
20819  QCPAxis *keyAxis = mKeyAxis.data();
20820  if (!keyAxis) return;
20821 
20822  // resize mMapImage to correct dimensions, according to key/value axes orientation:
20823  if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.size().width() != mMapData->keySize() || mMapImage.size().height() != mMapData->valueSize()))
20824  mMapImage = QImage(QSize(mMapData->keySize(), mMapData->valueSize()), QImage::Format_RGB32);
20825  else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.size().width() != mMapData->valueSize() || mMapImage.size().height() != mMapData->keySize()))
20826  mMapImage = QImage(QSize(mMapData->valueSize(), mMapData->keySize()), QImage::Format_RGB32);
20827 
20828  const int keySize = mMapData->keySize();
20829  const int valueSize = mMapData->valueSize();
20830  const double *rawData = mMapData->mData;
20831 
20832  if (keyAxis->orientation() == Qt::Horizontal)
20833  {
20834  const int lineCount = valueSize;
20835  const int rowCount = keySize;
20836  for (int line=0; line<lineCount; ++line)
20837  {
20838  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)
20839  mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
20840  }
20841  } else // keyAxis->orientation() == Qt::Vertical
20842  {
20843  const int lineCount = keySize;
20844  const int rowCount = valueSize;
20845  for (int line=0; line<lineCount; ++line)
20846  {
20847  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)
20848  mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
20849  }
20850  }
20851 
20852  mMapData->mDataModified = false;
20853  mMapImageInvalidated = false;
20854 }
20855 
20856 /* inherits documentation from base class */
20858 {
20859  if (mMapData->isEmpty()) return;
20860  if (!mKeyAxis || !mValueAxis) return;
20862 
20864  updateMapImage();
20865 
20866  double halfSampleKey = 0;
20867  double halfSampleValue = 0;
20868  if (mMapData->keySize() > 1)
20869  halfSampleKey = 0.5*mMapData->keyRange().size()/(double)(mMapData->keySize()-1);
20870  if (mMapData->valueSize() > 1)
20871  halfSampleValue = 0.5*mMapData->valueRange().size()/(double)(mMapData->valueSize()-1);
20872  QRectF imageRect(coordsToPixels(mMapData->keyRange().lower-halfSampleKey, mMapData->valueRange().lower-halfSampleValue),
20873  coordsToPixels(mMapData->keyRange().upper+halfSampleKey, mMapData->valueRange().upper+halfSampleValue));
20874  imageRect = imageRect.normalized();
20875  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20876  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20877  bool smoothBackup = painter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
20878  painter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
20879  QRegion clipBackup;
20880  if (mTightBoundary)
20881  {
20882  clipBackup = painter->clipRegion();
20883  painter->setClipRect(QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20884  coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(), Qt::IntersectClip);
20885  }
20886  painter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
20887  if (mTightBoundary)
20888  painter->setClipRegion(clipBackup);
20889  painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
20890 }
20891 
20892 /* inherits documentation from base class */
20893 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20894 {
20896  // draw map thumbnail:
20897  if (!mLegendIcon.isNull())
20898  {
20899  QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
20900  QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
20901  iconRect.moveCenter(rect.center());
20902  painter->drawPixmap(iconRect.topLeft(), scaledIcon);
20903  }
20904  /*
20905  // draw frame:
20906  painter->setBrush(Qt::NoBrush);
20907  painter->setPen(Qt::black);
20908  painter->drawRect(rect.adjusted(1, 1, 0, 0));
20909  */
20910 }
20911 
20912 /* inherits documentation from base class */
20913 QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
20914 {
20915  foundRange = true;
20916  QCPRange result = mMapData->keyRange();
20917  result.normalize();
20918  if (inSignDomain == QCPAbstractPlottable::sdPositive)
20919  {
20920  if (result.lower <= 0 && result.upper > 0)
20921  result.lower = result.upper*1e-3;
20922  else if (result.lower <= 0 && result.upper <= 0)
20923  foundRange = false;
20924  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20925  {
20926  if (result.upper >= 0 && result.lower < 0)
20927  result.upper = result.lower*1e-3;
20928  else if (result.upper >= 0 && result.lower >= 0)
20929  foundRange = false;
20930  }
20931  return result;
20932 }
20933 
20934 /* inherits documentation from base class */
20935 QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
20936 {
20937  foundRange = true;
20938  QCPRange result = mMapData->valueRange();
20939  result.normalize();
20940  if (inSignDomain == QCPAbstractPlottable::sdPositive)
20941  {
20942  if (result.lower <= 0 && result.upper > 0)
20943  result.lower = result.upper*1e-3;
20944  else if (result.lower <= 0 && result.upper <= 0)
20945  foundRange = false;
20946  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20947  {
20948  if (result.upper >= 0 && result.lower < 0)
20949  result.upper = result.lower*1e-3;
20950  else if (result.upper >= 0 && result.lower >= 0)
20951  foundRange = false;
20952  }
20953  return result;
20954 }
20955 
20956 
20960 
20980  key(0),
20981  open(0),
20982  high(0),
20983  low(0),
20984  close(0)
20985 {
20986 }
20987 
20991 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
20992  key(key),
20993  open(open),
20994  high(high),
20995  low(low),
20996  close(close)
20997 {
20998 }
20999 
21000 
21004 
21037 /* start of documentation of inline functions */
21038 
21046 /* end of documentation of inline functions */
21047 
21058  QCPAbstractPlottable(keyAxis, valueAxis),
21059  mData(0),
21060  mChartStyle(csOhlc),
21061  mWidth(0.5),
21062  mTwoColored(false),
21063  mBrushPositive(QBrush(QColor(210, 210, 255))),
21064  mBrushNegative(QBrush(QColor(255, 210, 210))),
21065  mPenPositive(QPen(QColor(10, 40, 180))),
21066  mPenNegative(QPen(QColor(180, 40, 10)))
21067 {
21068  mData = new QCPFinancialDataMap;
21069 
21070  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
21071  setSelectedBrush(QBrush(QColor(80, 80, 255)));
21072 }
21073 
21075 {
21076  delete mData;
21077 }
21078 
21092 {
21093  if (mData == data)
21094  {
21095  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
21096  return;
21097  }
21098  if (copy)
21099  {
21100  *mData = *data;
21101  } else
21102  {
21103  delete mData;
21104  mData = data;
21105  }
21106 }
21107 
21115 void QCPFinancial::setData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
21116 {
21117  mData->clear();
21118  int n = key.size();
21119  n = qMin(n, open.size());
21120  n = qMin(n, high.size());
21121  n = qMin(n, low.size());
21122  n = qMin(n, close.size());
21123  for (int i=0; i<n; ++i)
21124  {
21125  mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
21126  }
21127 }
21128 
21133 {
21134  mChartStyle = style;
21135 }
21136 
21143 {
21144  mWidth = width;
21145 }
21146 
21157 {
21159 }
21160 
21171 {
21173 }
21174 
21185 {
21187 }
21188 
21199 {
21200  mPenPositive = pen;
21201 }
21202 
21213 {
21214  mPenNegative = pen;
21215 }
21216 
21226 {
21227  mData->unite(dataMap);
21228 }
21229 
21240 {
21241  mData->insertMulti(data.key, data);
21242 }
21243 
21254 void QCPFinancial::addData(double key, double open, double high, double low, double close)
21255 {
21256  mData->insertMulti(key, QCPFinancialData(key, open, high, low, close));
21257 }
21258 
21268 void QCPFinancial::addData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
21269 {
21270  int n = key.size();
21271  n = qMin(n, open.size());
21272  n = qMin(n, high.size());
21273  n = qMin(n, low.size());
21274  n = qMin(n, close.size());
21275  for (int i=0; i<n; ++i)
21276  {
21277  mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
21278  }
21279 }
21280 
21287 {
21288  QCPFinancialDataMap::iterator it = mData->begin();
21289  while (it != mData->end() && it.key() < key)
21290  it = mData->erase(it);
21291 }
21292 
21299 {
21300  if (mData->isEmpty()) return;
21301  QCPFinancialDataMap::iterator it = mData->upperBound(key);
21302  while (it != mData->end())
21303  it = mData->erase(it);
21304 }
21305 
21313 void QCPFinancial::removeData(double fromKey, double toKey)
21314 {
21315  if (fromKey >= toKey || mData->isEmpty()) return;
21316  QCPFinancialDataMap::iterator it = mData->upperBound(fromKey);
21317  QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey);
21318  while (it != itEnd)
21319  it = mData->erase(it);
21320 }
21321 
21331 {
21332  mData->remove(key);
21333 }
21334 
21341 {
21342  mData->clear();
21343 }
21344 
21345 /* inherits documentation from base class */
21346 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21347 {
21348  Q_UNUSED(details)
21349  if (onlySelectable && !mSelectable)
21350  return -1;
21351  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21352 
21353  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
21354  {
21355  // get visible data range:
21356  QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
21357  getVisibleDataBounds(lower, upper);
21358  if (lower == mData->constEnd() || upper == mData->constEnd())
21359  return -1;
21360  // perform select test according to configured style:
21361  switch (mChartStyle)
21362  {
21363  case QCPFinancial::csOhlc:
21364  return ohlcSelectTest(pos, lower, upper+1); break;
21366  return candlestickSelectTest(pos, lower, upper+1); break;
21367  }
21368  }
21369  return -1;
21370 }
21371 
21385 QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
21386 {
21387  QCPFinancialDataMap map;
21388  int count = qMin(time.size(), value.size());
21389  if (count == 0)
21390  return QCPFinancialDataMap();
21391 
21392  QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
21393  int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
21394  for (int i=0; i<count; ++i)
21395  {
21396  int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
21397  if (currentBinIndex == index) // data point still in current bin, extend high/low:
21398  {
21399  if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
21400  if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
21401  if (i == count-1) // last data point is in current bin, finalize bin:
21402  {
21403  currentBinData.close = value.at(i);
21404  currentBinData.key = timeBinOffset+(index)*timeBinSize;
21405  map.insert(currentBinData.key, currentBinData);
21406  }
21407  } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
21408  {
21409  // finalize current bin:
21410  currentBinData.close = value.at(i-1);
21411  currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
21412  map.insert(currentBinData.key, currentBinData);
21413  // start next bin:
21414  currentBinIndex = index;
21415  currentBinData.open = value.at(i);
21416  currentBinData.high = value.at(i);
21417  currentBinData.low = value.at(i);
21418  }
21419  }
21420 
21421  return map;
21422 }
21423 
21424 /* inherits documentation from base class */
21426 {
21427  // get visible data range:
21428  QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
21429  getVisibleDataBounds(lower, upper);
21430  if (lower == mData->constEnd() || upper == mData->constEnd())
21431  return;
21432 
21433  // draw visible data range according to configured style:
21434  switch (mChartStyle)
21435  {
21436  case QCPFinancial::csOhlc:
21437  drawOhlcPlot(painter, lower, upper+1); break;
21439  drawCandlestickPlot(painter, lower, upper+1); break;
21440  }
21441 }
21442 
21443 /* inherits documentation from base class */
21444 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
21445 {
21446  painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
21447  if (mChartStyle == csOhlc)
21448  {
21449  if (mTwoColored)
21450  {
21451  // draw upper left half icon with positive color:
21452  painter->setBrush(mBrushPositive);
21453  painter->setPen(mPenPositive);
21454  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21455  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21456  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21457  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21458  // draw bottom right hald icon with negative color:
21459  painter->setBrush(mBrushNegative);
21460  painter->setPen(mPenNegative);
21461  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21462  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21463  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21464  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21465  } else
21466  {
21467  painter->setBrush(mBrush);
21468  painter->setPen(mPen);
21469  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21470  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21471  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21472  }
21473  } else if (mChartStyle == csCandlestick)
21474  {
21475  if (mTwoColored)
21476  {
21477  // draw upper left half icon with positive color:
21478  painter->setBrush(mBrushPositive);
21479  painter->setPen(mPenPositive);
21480  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21481  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21482  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21483  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21484  // draw bottom right hald icon with negative color:
21485  painter->setBrush(mBrushNegative);
21486  painter->setPen(mPenNegative);
21487  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21488  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21489  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21490  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21491  } else
21492  {
21493  painter->setBrush(mBrush);
21494  painter->setPen(mPen);
21495  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21496  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21497  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21498  }
21499  }
21500 }
21501 
21502 /* inherits documentation from base class */
21504 {
21505  QCPRange range;
21506  bool haveLower = false;
21507  bool haveUpper = false;
21508 
21509  double current;
21510  QCPFinancialDataMap::const_iterator it = mData->constBegin();
21511  while (it != mData->constEnd())
21512  {
21513  current = it.value().key;
21514  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
21515  {
21516  if (current < range.lower || !haveLower)
21517  {
21518  range.lower = current;
21519  haveLower = true;
21520  }
21521  if (current > range.upper || !haveUpper)
21522  {
21523  range.upper = current;
21524  haveUpper = true;
21525  }
21526  }
21527  ++it;
21528  }
21529  // determine exact range by including width of bars/flags:
21530  if (haveLower && mKeyAxis)
21531  range.lower = range.lower-mWidth*0.5;
21532  if (haveUpper && mKeyAxis)
21533  range.upper = range.upper+mWidth*0.5;
21534  foundRange = haveLower && haveUpper;
21535  return range;
21536 }
21537 
21538 /* inherits documentation from base class */
21540 {
21541  QCPRange range;
21542  bool haveLower = false;
21543  bool haveUpper = false;
21544 
21545  QCPFinancialDataMap::const_iterator it = mData->constBegin();
21546  while (it != mData->constEnd())
21547  {
21548  // high:
21549  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().high < 0) || (inSignDomain == sdPositive && it.value().high > 0))
21550  {
21551  if (it.value().high < range.lower || !haveLower)
21552  {
21553  range.lower = it.value().high;
21554  haveLower = true;
21555  }
21556  if (it.value().high > range.upper || !haveUpper)
21557  {
21558  range.upper = it.value().high;
21559  haveUpper = true;
21560  }
21561  }
21562  // low:
21563  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().low < 0) || (inSignDomain == sdPositive && it.value().low > 0))
21564  {
21565  if (it.value().low < range.lower || !haveLower)
21566  {
21567  range.lower = it.value().low;
21568  haveLower = true;
21569  }
21570  if (it.value().low > range.upper || !haveUpper)
21571  {
21572  range.upper = it.value().low;
21573  haveUpper = true;
21574  }
21575  }
21576  ++it;
21577  }
21578 
21579  foundRange = haveLower && haveUpper;
21580  return range;
21581 }
21582 
21589 void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21590 {
21591  QCPAxis *keyAxis = mKeyAxis.data();
21592  QCPAxis *valueAxis = mValueAxis.data();
21593  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21594 
21595  QPen linePen;
21596 
21597  if (keyAxis->orientation() == Qt::Horizontal)
21598  {
21599  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21600  {
21601  if (mSelected)
21602  linePen = mSelectedPen;
21603  else if (mTwoColored)
21604  linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21605  else
21606  linePen = mPen;
21607  painter->setPen(linePen);
21608  double keyPixel = keyAxis->coordToPixel(it.value().key);
21609  double openPixel = valueAxis->coordToPixel(it.value().open);
21610  double closePixel = valueAxis->coordToPixel(it.value().close);
21611  // draw backbone:
21612  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
21613  // draw open:
21614  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21615  painter->drawLine(QPointF(keyPixel-keyWidthPixels, openPixel), QPointF(keyPixel, openPixel));
21616  // draw close:
21617  painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+keyWidthPixels, closePixel));
21618  }
21619  } else
21620  {
21621  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21622  {
21623  if (mSelected)
21624  linePen = mSelectedPen;
21625  else if (mTwoColored)
21626  linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21627  else
21628  linePen = mPen;
21629  painter->setPen(linePen);
21630  double keyPixel = keyAxis->coordToPixel(it.value().key);
21631  double openPixel = valueAxis->coordToPixel(it.value().open);
21632  double closePixel = valueAxis->coordToPixel(it.value().close);
21633  // draw backbone:
21634  painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
21635  // draw open:
21636  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21637  painter->drawLine(QPointF(openPixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel));
21638  // draw close:
21639  painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+keyWidthPixels));
21640  }
21641  }
21642 }
21643 
21650 void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21651 {
21652  QCPAxis *keyAxis = mKeyAxis.data();
21653  QCPAxis *valueAxis = mValueAxis.data();
21654  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21655 
21656  QPen linePen;
21657  QBrush boxBrush;
21658 
21659  if (keyAxis->orientation() == Qt::Horizontal)
21660  {
21661  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21662  {
21663  if (mSelected)
21664  {
21665  linePen = mSelectedPen;
21666  boxBrush = mSelectedBrush;
21667  } else if (mTwoColored)
21668  {
21669  if (it.value().close >= it.value().open)
21670  {
21671  linePen = mPenPositive;
21672  boxBrush = mBrushPositive;
21673  } else
21674  {
21675  linePen = mPenNegative;
21676  boxBrush = mBrushNegative;
21677  }
21678  } else
21679  {
21680  linePen = mPen;
21681  boxBrush = mBrush;
21682  }
21683  painter->setPen(linePen);
21684  painter->setBrush(boxBrush);
21685  double keyPixel = keyAxis->coordToPixel(it.value().key);
21686  double openPixel = valueAxis->coordToPixel(it.value().open);
21687  double closePixel = valueAxis->coordToPixel(it.value().close);
21688  // draw high:
21689  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))));
21690  // draw low:
21691  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))));
21692  // draw open-close box:
21693  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21694  painter->drawRect(QRectF(QPointF(keyPixel-keyWidthPixels, closePixel), QPointF(keyPixel+keyWidthPixels, openPixel)));
21695  }
21696  } else // keyAxis->orientation() == Qt::Vertical
21697  {
21698  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21699  {
21700  if (mSelected)
21701  {
21702  linePen = mSelectedPen;
21703  boxBrush = mSelectedBrush;
21704  } else if (mTwoColored)
21705  {
21706  if (it.value().close >= it.value().open)
21707  {
21708  linePen = mPenPositive;
21709  boxBrush = mBrushPositive;
21710  } else
21711  {
21712  linePen = mPenNegative;
21713  boxBrush = mBrushNegative;
21714  }
21715  } else
21716  {
21717  linePen = mPen;
21718  boxBrush = mBrush;
21719  }
21720  painter->setPen(linePen);
21721  painter->setBrush(boxBrush);
21722  double keyPixel = keyAxis->coordToPixel(it.value().key);
21723  double openPixel = valueAxis->coordToPixel(it.value().open);
21724  double closePixel = valueAxis->coordToPixel(it.value().close);
21725  // draw high:
21726  painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel));
21727  // draw low:
21728  painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel));
21729  // draw open-close box:
21730  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21731  painter->drawRect(QRectF(QPointF(closePixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel+keyWidthPixels)));
21732  }
21733  }
21734 }
21735 
21741 double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21742 {
21743  QCPAxis *keyAxis = mKeyAxis.data();
21744  QCPAxis *valueAxis = mValueAxis.data();
21745  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21746 
21747  double minDistSqr = std::numeric_limits<double>::max();
21748  QCPFinancialDataMap::const_iterator it;
21749  if (keyAxis->orientation() == Qt::Horizontal)
21750  {
21751  for (it = begin; it != end; ++it)
21752  {
21753  double keyPixel = keyAxis->coordToPixel(it.value().key);
21754  // calculate distance to backbone:
21755  double currentDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos);
21756  if (currentDistSqr < minDistSqr)
21757  minDistSqr = currentDistSqr;
21758  }
21759  } else // keyAxis->orientation() == Qt::Vertical
21760  {
21761  for (it = begin; it != end; ++it)
21762  {
21763  double keyPixel = keyAxis->coordToPixel(it.value().key);
21764  // calculate distance to backbone:
21765  double currentDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos);
21766  if (currentDistSqr < minDistSqr)
21767  minDistSqr = currentDistSqr;
21768  }
21769  }
21770  return qSqrt(minDistSqr);
21771 }
21772 
21779 double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21780 {
21781  QCPAxis *keyAxis = mKeyAxis.data();
21782  QCPAxis *valueAxis = mValueAxis.data();
21783  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21784 
21785  double minDistSqr = std::numeric_limits<double>::max();
21786  QCPFinancialDataMap::const_iterator it;
21787  if (keyAxis->orientation() == Qt::Horizontal)
21788  {
21789  for (it = begin; it != end; ++it)
21790  {
21791  double currentDistSqr;
21792  // determine whether pos is in open-close-box:
21793  QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21794  QCPRange boxValueRange(it.value().close, it.value().open);
21795  double posKey, posValue;
21796  pixelsToCoords(pos, posKey, posValue);
21797  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21798  {
21799  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21800  } else
21801  {
21802  // calculate distance to high/low lines:
21803  double keyPixel = keyAxis->coordToPixel(it.value().key);
21804  double highLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))), pos);
21805  double lowLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))), pos);
21806  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21807  }
21808  if (currentDistSqr < minDistSqr)
21809  minDistSqr = currentDistSqr;
21810  }
21811  } else // keyAxis->orientation() == Qt::Vertical
21812  {
21813  for (it = begin; it != end; ++it)
21814  {
21815  double currentDistSqr;
21816  // determine whether pos is in open-close-box:
21817  QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21818  QCPRange boxValueRange(it.value().close, it.value().open);
21819  double posKey, posValue;
21820  pixelsToCoords(pos, posKey, posValue);
21821  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21822  {
21823  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21824  } else
21825  {
21826  // calculate distance to high/low lines:
21827  double keyPixel = keyAxis->coordToPixel(it.value().key);
21828  double highLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel), pos);
21829  double lowLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel), pos);
21830  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21831  }
21832  if (currentDistSqr < minDistSqr)
21833  minDistSqr = currentDistSqr;
21834  }
21835  }
21836  return qSqrt(minDistSqr);
21837 }
21838 
21855 void QCPFinancial::getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
21856 {
21857  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
21858  if (mData->isEmpty())
21859  {
21860  lower = mData->constEnd();
21861  upper = mData->constEnd();
21862  return;
21863  }
21864 
21865  // get visible data range as QMap iterators
21866  QCPFinancialDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
21867  QCPFinancialDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
21868  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
21869  bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
21870 
21871  lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
21872  upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
21873 }
21874 
21875 
21879 
21894  QCPAbstractItem(parentPlot),
21895  point1(createPosition(QLatin1String("point1"))),
21896  point2(createPosition(QLatin1String("point2")))
21897 {
21898  point1->setCoords(0, 0);
21899  point2->setCoords(1, 1);
21900 
21901  setPen(QPen(Qt::black));
21902  setSelectedPen(QPen(Qt::blue,2));
21903 }
21904 
21906 {
21907 }
21908 
21915 {
21916  mPen = pen;
21917 }
21918 
21925 {
21926  mSelectedPen = pen;
21927 }
21928 
21929 /* inherits documentation from base class */
21930 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21931 {
21932  Q_UNUSED(details)
21933  if (onlySelectable && !mSelectable)
21934  return -1;
21935 
21936  return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
21937 }
21938 
21939 /* inherits documentation from base class */
21941 {
21942  QVector2D start(point1->pixelPoint());
21943  QVector2D end(point2->pixelPoint());
21944  // get visible segment of straight line inside clipRect:
21945  double clipPad = mainPen().widthF();
21946  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21947  // paint visible segment, if existent:
21948  if (!line.isNull())
21949  {
21950  painter->setPen(mainPen());
21951  painter->drawLine(line);
21952  }
21953 }
21954 
21962 double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
21963 {
21964  return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
21965 }
21966 
21974 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
21975 {
21976  double bx, by;
21977  double gamma;
21978  QLineF result;
21979  if (vec.x() == 0 && vec.y() == 0)
21980  return result;
21981  if (qFuzzyIsNull(vec.x())) // line is vertical
21982  {
21983  // check top of rect:
21984  bx = rect.left();
21985  by = rect.top();
21986  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21987  if (gamma >= 0 && gamma <= rect.width())
21988  result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
21989  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
21990  {
21991  // check left of rect:
21992  bx = rect.left();
21993  by = rect.top();
21994  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21995  if (gamma >= 0 && gamma <= rect.height())
21996  result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
21997  } else // line is skewed
21998  {
21999  QList<QVector2D> pointVectors;
22000  // check top of rect:
22001  bx = rect.left();
22002  by = rect.top();
22003  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
22004  if (gamma >= 0 && gamma <= rect.width())
22005  pointVectors.append(QVector2D(bx+gamma, by));
22006  // check bottom of rect:
22007  bx = rect.left();
22008  by = rect.bottom();
22009  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
22010  if (gamma >= 0 && gamma <= rect.width())
22011  pointVectors.append(QVector2D(bx+gamma, by));
22012  // check left of rect:
22013  bx = rect.left();
22014  by = rect.top();
22015  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
22016  if (gamma >= 0 && gamma <= rect.height())
22017  pointVectors.append(QVector2D(bx, by+gamma));
22018  // check right of rect:
22019  bx = rect.right();
22020  by = rect.top();
22021  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
22022  if (gamma >= 0 && gamma <= rect.height())
22023  pointVectors.append(QVector2D(bx, by+gamma));
22024 
22025  // evaluate points:
22026  if (pointVectors.size() == 2)
22027  {
22028  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
22029  } else if (pointVectors.size() > 2)
22030  {
22031  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
22032  double distSqrMax = 0;
22033  QVector2D pv1, pv2;
22034  for (int i=0; i<pointVectors.size()-1; ++i)
22035  {
22036  for (int k=i+1; k<pointVectors.size(); ++k)
22037  {
22038  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
22039  if (distSqr > distSqrMax)
22040  {
22041  pv1 = pointVectors.at(i);
22042  pv2 = pointVectors.at(k);
22043  distSqrMax = distSqr;
22044  }
22045  }
22046  }
22047  result.setPoints(pv1.toPointF(), pv2.toPointF());
22048  }
22049  }
22050  return result;
22051 }
22052 
22059 {
22060  return mSelected ? mSelectedPen : mPen;
22061 }
22062 
22063 
22067 
22084  QCPAbstractItem(parentPlot),
22085  start(createPosition(QLatin1String("start"))),
22086  end(createPosition(QLatin1String("end")))
22087 {
22088  start->setCoords(0, 0);
22089  end->setCoords(1, 1);
22090 
22091  setPen(QPen(Qt::black));
22092  setSelectedPen(QPen(Qt::blue,2));
22093 }
22094 
22096 {
22097 }
22098 
22104 void QCPItemLine::setPen(const QPen &pen)
22105 {
22106  mPen = pen;
22107 }
22108 
22115 {
22116  mSelectedPen = pen;
22117 }
22118 
22128 {
22129  mHead = head;
22130 }
22131 
22141 {
22142  mTail = tail;
22143 }
22144 
22145 /* inherits documentation from base class */
22146 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22147 {
22148  Q_UNUSED(details)
22149  if (onlySelectable && !mSelectable)
22150  return -1;
22151 
22152  return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
22153 }
22154 
22155 /* inherits documentation from base class */
22157 {
22158  QVector2D startVec(start->pixelPoint());
22159  QVector2D endVec(end->pixelPoint());
22160  if (startVec.toPoint() == endVec.toPoint())
22161  return;
22162  // get visible segment of straight line inside clipRect:
22163  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
22164  clipPad = qMax(clipPad, (double)mainPen().widthF());
22165  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
22166  // paint visible segment, if existent:
22167  if (!line.isNull())
22168  {
22169  painter->setPen(mainPen());
22170  painter->drawLine(line);
22171  painter->setBrush(Qt::SolidPattern);
22173  mTail.draw(painter, startVec, startVec-endVec);
22175  mHead.draw(painter, endVec, endVec-startVec);
22176  }
22177 }
22178 
22186 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
22187 {
22188  bool containsStart = rect.contains(start.x(), start.y());
22189  bool containsEnd = rect.contains(end.x(), end.y());
22190  if (containsStart && containsEnd)
22191  return QLineF(start.toPointF(), end.toPointF());
22192 
22193  QVector2D base = start;
22194  QVector2D vec = end-start;
22195  double bx, by;
22196  double gamma, mu;
22197  QLineF result;
22198  QList<QVector2D> pointVectors;
22199 
22200  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
22201  {
22202  // check top of rect:
22203  bx = rect.left();
22204  by = rect.top();
22205  mu = (by-base.y())/vec.y();
22206  if (mu >= 0 && mu <= 1)
22207  {
22208  gamma = base.x()-bx + mu*vec.x();
22209  if (gamma >= 0 && gamma <= rect.width())
22210  pointVectors.append(QVector2D(bx+gamma, by));
22211  }
22212  // check bottom of rect:
22213  bx = rect.left();
22214  by = rect.bottom();
22215  mu = (by-base.y())/vec.y();
22216  if (mu >= 0 && mu <= 1)
22217  {
22218  gamma = base.x()-bx + mu*vec.x();
22219  if (gamma >= 0 && gamma <= rect.width())
22220  pointVectors.append(QVector2D(bx+gamma, by));
22221  }
22222  }
22223  if (!qFuzzyIsNull(vec.x())) // line is not vertical
22224  {
22225  // check left of rect:
22226  bx = rect.left();
22227  by = rect.top();
22228  mu = (bx-base.x())/vec.x();
22229  if (mu >= 0 && mu <= 1)
22230  {
22231  gamma = base.y()-by + mu*vec.y();
22232  if (gamma >= 0 && gamma <= rect.height())
22233  pointVectors.append(QVector2D(bx, by+gamma));
22234  }
22235  // check right of rect:
22236  bx = rect.right();
22237  by = rect.top();
22238  mu = (bx-base.x())/vec.x();
22239  if (mu >= 0 && mu <= 1)
22240  {
22241  gamma = base.y()-by + mu*vec.y();
22242  if (gamma >= 0 && gamma <= rect.height())
22243  pointVectors.append(QVector2D(bx, by+gamma));
22244  }
22245  }
22246 
22247  if (containsStart)
22248  pointVectors.append(start);
22249  if (containsEnd)
22250  pointVectors.append(end);
22251 
22252  // evaluate points:
22253  if (pointVectors.size() == 2)
22254  {
22255  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
22256  } else if (pointVectors.size() > 2)
22257  {
22258  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
22259  double distSqrMax = 0;
22260  QVector2D pv1, pv2;
22261  for (int i=0; i<pointVectors.size()-1; ++i)
22262  {
22263  for (int k=i+1; k<pointVectors.size(); ++k)
22264  {
22265  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
22266  if (distSqr > distSqrMax)
22267  {
22268  pv1 = pointVectors.at(i);
22269  pv2 = pointVectors.at(k);
22270  distSqrMax = distSqr;
22271  }
22272  }
22273  }
22274  result.setPoints(pv1.toPointF(), pv2.toPointF());
22275  }
22276  return result;
22277 }
22278 
22285 {
22286  return mSelected ? mSelectedPen : mPen;
22287 }
22288 
22289 
22293 
22317  QCPAbstractItem(parentPlot),
22318  start(createPosition(QLatin1String("start"))),
22319  startDir(createPosition(QLatin1String("startDir"))),
22320  endDir(createPosition(QLatin1String("endDir"))),
22321  end(createPosition(QLatin1String("end")))
22322 {
22323  start->setCoords(0, 0);
22324  startDir->setCoords(0.5, 0);
22325  endDir->setCoords(0, 0.5);
22326  end->setCoords(1, 1);
22327 
22328  setPen(QPen(Qt::black));
22329  setSelectedPen(QPen(Qt::blue,2));
22330 }
22331 
22333 {
22334 }
22335 
22341 void QCPItemCurve::setPen(const QPen &pen)
22342 {
22343  mPen = pen;
22344 }
22345 
22352 {
22353  mSelectedPen = pen;
22354 }
22355 
22365 {
22366  mHead = head;
22367 }
22368 
22378 {
22379  mTail = tail;
22380 }
22381 
22382 /* inherits documentation from base class */
22383 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22384 {
22385  Q_UNUSED(details)
22386  if (onlySelectable && !mSelectable)
22387  return -1;
22388 
22389  QPointF startVec(start->pixelPoint());
22390  QPointF startDirVec(startDir->pixelPoint());
22391  QPointF endDirVec(endDir->pixelPoint());
22392  QPointF endVec(end->pixelPoint());
22393 
22394  QPainterPath cubicPath(startVec);
22395  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
22396 
22397  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
22398  double minDistSqr = std::numeric_limits<double>::max();
22399  for (int i=1; i<polygon.size(); ++i)
22400  {
22401  double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
22402  if (distSqr < minDistSqr)
22403  minDistSqr = distSqr;
22404  }
22405  return qSqrt(minDistSqr);
22406 }
22407 
22408 /* inherits documentation from base class */
22410 {
22411  QPointF startVec(start->pixelPoint());
22412  QPointF startDirVec(startDir->pixelPoint());
22413  QPointF endDirVec(endDir->pixelPoint());
22414  QPointF endVec(end->pixelPoint());
22415  if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
22416  return;
22417 
22418  QPainterPath cubicPath(startVec);
22419  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
22420 
22421  // paint visible segment, if existent:
22422  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
22423  QRect cubicRect = cubicPath.controlPointRect().toRect();
22424  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
22425  cubicRect.adjust(0, 0, 1, 1);
22426  if (clip.intersects(cubicRect))
22427  {
22428  painter->setPen(mainPen());
22429  painter->drawPath(cubicPath);
22430  painter->setBrush(Qt::SolidPattern);
22432  mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
22434  mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
22435  }
22436 }
22437 
22444 {
22445  return mSelected ? mSelectedPen : mPen;
22446 }
22447 
22448 
22452 
22467  QCPAbstractItem(parentPlot),
22468  topLeft(createPosition(QLatin1String("topLeft"))),
22469  bottomRight(createPosition(QLatin1String("bottomRight"))),
22470  top(createAnchor(QLatin1String("top"), aiTop)),
22471  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22472  right(createAnchor(QLatin1String("right"), aiRight)),
22473  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22474  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22475  left(createAnchor(QLatin1String("left"), aiLeft))
22476 {
22477  topLeft->setCoords(0, 1);
22478  bottomRight->setCoords(1, 0);
22479 
22480  setPen(QPen(Qt::black));
22481  setSelectedPen(QPen(Qt::blue,2));
22482  setBrush(Qt::NoBrush);
22483  setSelectedBrush(Qt::NoBrush);
22484 }
22485 
22487 {
22488 }
22489 
22495 void QCPItemRect::setPen(const QPen &pen)
22496 {
22497  mPen = pen;
22498 }
22499 
22506 {
22507  mSelectedPen = pen;
22508 }
22509 
22516 void QCPItemRect::setBrush(const QBrush &brush)
22517 {
22518  mBrush = brush;
22519 }
22520 
22528 {
22530 }
22531 
22532 /* inherits documentation from base class */
22533 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22534 {
22535  Q_UNUSED(details)
22536  if (onlySelectable && !mSelectable)
22537  return -1;
22538 
22539  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
22540  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
22541  return rectSelectTest(rect, pos, filledRect);
22542 }
22543 
22544 /* inherits documentation from base class */
22546 {
22547  QPointF p1 = topLeft->pixelPoint();
22548  QPointF p2 = bottomRight->pixelPoint();
22549  if (p1.toPoint() == p2.toPoint())
22550  return;
22551  QRectF rect = QRectF(p1, p2).normalized();
22552  double clipPad = mainPen().widthF();
22553  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22554  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
22555  {
22556  painter->setPen(mainPen());
22557  painter->setBrush(mainBrush());
22558  painter->drawRect(rect);
22559  }
22560 }
22561 
22562 /* inherits documentation from base class */
22563 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
22564 {
22565  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22566  switch (anchorId)
22567  {
22568  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22569  case aiTopRight: return rect.topRight();
22570  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22571  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22572  case aiBottomLeft: return rect.bottomLeft();
22573  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22574  }
22575 
22576  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22577  return QPointF();
22578 }
22579 
22586 {
22587  return mSelected ? mSelectedPen : mPen;
22588 }
22589 
22596 {
22597  return mSelected ? mSelectedBrush : mBrush;
22598 }
22599 
22600 
22604 
22625  QCPAbstractItem(parentPlot),
22626  position(createPosition(QLatin1String("position"))),
22627  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
22628  top(createAnchor(QLatin1String("top"), aiTop)),
22629  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22630  right(createAnchor(QLatin1String("right"), aiRight)),
22631  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
22632  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22633  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22634  left(createAnchor(QLatin1String("left"), aiLeft))
22635 {
22636  position->setCoords(0, 0);
22637 
22638  setRotation(0);
22639  setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
22640  setPositionAlignment(Qt::AlignCenter);
22641  setText(QLatin1String("text"));
22642 
22643  setPen(Qt::NoPen);
22644  setSelectedPen(Qt::NoPen);
22645  setBrush(Qt::NoBrush);
22646  setSelectedBrush(Qt::NoBrush);
22647  setColor(Qt::black);
22648  setSelectedColor(Qt::blue);
22649 }
22650 
22652 {
22653 }
22654 
22658 void QCPItemText::setColor(const QColor &color)
22659 {
22660  mColor = color;
22661 }
22662 
22667 {
22669 }
22670 
22677 void QCPItemText::setPen(const QPen &pen)
22678 {
22679  mPen = pen;
22680 }
22681 
22689 {
22690  mSelectedPen = pen;
22691 }
22692 
22699 void QCPItemText::setBrush(const QBrush &brush)
22700 {
22701  mBrush = brush;
22702 }
22703 
22711 {
22713 }
22714 
22720 void QCPItemText::setFont(const QFont &font)
22721 {
22722  mFont = font;
22723 }
22724 
22731 {
22732  mSelectedFont = font;
22733 }
22734 
22741 void QCPItemText::setText(const QString &text)
22742 {
22743  mText = text;
22744 }
22745 
22758 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
22759 {
22760  mPositionAlignment = alignment;
22761 }
22762 
22766 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
22767 {
22768  mTextAlignment = alignment;
22769 }
22770 
22775 void QCPItemText::setRotation(double degrees)
22776 {
22777  mRotation = degrees;
22778 }
22779 
22784 void QCPItemText::setPadding(const QMargins &padding)
22785 {
22786  mPadding = padding;
22787 }
22788 
22789 /* inherits documentation from base class */
22790 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22791 {
22792  Q_UNUSED(details)
22793  if (onlySelectable && !mSelectable)
22794  return -1;
22795 
22796  // The rect may be rotated, so we transform the actual clicked pos to the rotated
22797  // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
22798  QPointF positionPixels(position->pixelPoint());
22799  QTransform inputTransform;
22800  inputTransform.translate(positionPixels.x(), positionPixels.y());
22801  inputTransform.rotate(-mRotation);
22802  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
22803  QPointF rotatedPos = inputTransform.map(pos);
22804  QFontMetrics fontMetrics(mFont);
22805  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22806  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22807  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
22808  textBoxRect.moveTopLeft(textPos.toPoint());
22809 
22810  return rectSelectTest(textBoxRect, rotatedPos, true);
22811 }
22812 
22813 /* inherits documentation from base class */
22815 {
22816  QPointF pos(position->pixelPoint());
22817  QTransform transform = painter->transform();
22818  transform.translate(pos.x(), pos.y());
22819  if (!qFuzzyIsNull(mRotation))
22820  transform.rotate(mRotation);
22821  painter->setFont(mainFont());
22822  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22823  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22824  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22825  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
22826  textBoxRect.moveTopLeft(textPos.toPoint());
22827  double clipPad = mainPen().widthF();
22828  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22829  if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
22830  {
22831  painter->setTransform(transform);
22832  if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
22833  (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
22834  {
22835  painter->setPen(mainPen());
22836  painter->setBrush(mainBrush());
22837  painter->drawRect(textBoxRect);
22838  }
22839  painter->setBrush(Qt::NoBrush);
22840  painter->setPen(QPen(mainColor()));
22841  painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
22842  }
22843 }
22844 
22845 /* inherits documentation from base class */
22846 QPointF QCPItemText::anchorPixelPoint(int anchorId) const
22847 {
22848  // get actual rect points (pretty much copied from draw function):
22849  QPointF pos(position->pixelPoint());
22850  QTransform transform;
22851  transform.translate(pos.x(), pos.y());
22852  if (!qFuzzyIsNull(mRotation))
22853  transform.rotate(mRotation);
22854  QFontMetrics fontMetrics(mainFont());
22855  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22856  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22857  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22858  textBoxRect.moveTopLeft(textPos.toPoint());
22859  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
22860 
22861  switch (anchorId)
22862  {
22863  case aiTopLeft: return rectPoly.at(0);
22864  case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
22865  case aiTopRight: return rectPoly.at(1);
22866  case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
22867  case aiBottomRight: return rectPoly.at(2);
22868  case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
22869  case aiBottomLeft: return rectPoly.at(3);
22870  case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
22871  }
22872 
22873  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22874  return QPointF();
22875 }
22876 
22887 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
22888 {
22889  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
22890  return pos;
22891 
22892  QPointF result = pos; // start at top left
22893  if (positionAlignment.testFlag(Qt::AlignHCenter))
22894  result.rx() -= rect.width()/2.0;
22895  else if (positionAlignment.testFlag(Qt::AlignRight))
22896  result.rx() -= rect.width();
22897  if (positionAlignment.testFlag(Qt::AlignVCenter))
22898  result.ry() -= rect.height()/2.0;
22899  else if (positionAlignment.testFlag(Qt::AlignBottom))
22900  result.ry() -= rect.height();
22901  return result;
22902 }
22903 
22910 {
22911  return mSelected ? mSelectedFont : mFont;
22912 }
22913 
22920 {
22921  return mSelected ? mSelectedColor : mColor;
22922 }
22923 
22930 {
22931  return mSelected ? mSelectedPen : mPen;
22932 }
22933 
22940 {
22941  return mSelected ? mSelectedBrush : mBrush;
22942 }
22943 
22944 
22948 
22963  QCPAbstractItem(parentPlot),
22964  topLeft(createPosition(QLatin1String("topLeft"))),
22965  bottomRight(createPosition(QLatin1String("bottomRight"))),
22966  topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
22967  top(createAnchor(QLatin1String("top"), aiTop)),
22968  topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
22969  right(createAnchor(QLatin1String("right"), aiRight)),
22970  bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
22971  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22972  bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
22973  left(createAnchor(QLatin1String("left"), aiLeft)),
22974  center(createAnchor(QLatin1String("center"), aiCenter))
22975 {
22976  topLeft->setCoords(0, 1);
22977  bottomRight->setCoords(1, 0);
22978 
22979  setPen(QPen(Qt::black));
22980  setSelectedPen(QPen(Qt::blue, 2));
22981  setBrush(Qt::NoBrush);
22982  setSelectedBrush(Qt::NoBrush);
22983 }
22984 
22986 {
22987 }
22988 
22994 void QCPItemEllipse::setPen(const QPen &pen)
22995 {
22996  mPen = pen;
22997 }
22998 
23005 {
23006  mSelectedPen = pen;
23007 }
23008 
23015 void QCPItemEllipse::setBrush(const QBrush &brush)
23016 {
23017  mBrush = brush;
23018 }
23019 
23027 {
23029 }
23030 
23031 /* inherits documentation from base class */
23032 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23033 {
23034  Q_UNUSED(details)
23035  if (onlySelectable && !mSelectable)
23036  return -1;
23037 
23038  double result = -1;
23039  QPointF p1 = topLeft->pixelPoint();
23040  QPointF p2 = bottomRight->pixelPoint();
23041  QPointF center((p1+p2)/2.0);
23042  double a = qAbs(p1.x()-p2.x())/2.0;
23043  double b = qAbs(p1.y()-p2.y())/2.0;
23044  double x = pos.x()-center.x();
23045  double y = pos.y()-center.y();
23046 
23047  // distance to border:
23048  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
23049  result = qAbs(c-1)*qSqrt(x*x+y*y);
23050  // filled ellipse, allow click inside to count as hit:
23051  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
23052  {
23053  if (x*x/(a*a) + y*y/(b*b) <= 1)
23054  result = mParentPlot->selectionTolerance()*0.99;
23055  }
23056  return result;
23057 }
23058 
23059 /* inherits documentation from base class */
23061 {
23062  QPointF p1 = topLeft->pixelPoint();
23063  QPointF p2 = bottomRight->pixelPoint();
23064  if (p1.toPoint() == p2.toPoint())
23065  return;
23066  QRectF ellipseRect = QRectF(p1, p2).normalized();
23067  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
23068  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
23069  {
23070  painter->setPen(mainPen());
23071  painter->setBrush(mainBrush());
23072 #ifdef __EXCEPTIONS
23073  try // drawEllipse sometimes throws exceptions if ellipse is too big
23074  {
23075 #endif
23076  painter->drawEllipse(ellipseRect);
23077 #ifdef __EXCEPTIONS
23078  } catch (...)
23079  {
23080  qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
23081  setVisible(false);
23082  }
23083 #endif
23084  }
23085 }
23086 
23087 /* inherits documentation from base class */
23088 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
23089 {
23090  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
23091  switch (anchorId)
23092  {
23093  case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
23094  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
23095  case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
23096  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
23097  case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
23098  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
23099  case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
23100  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
23101  case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
23102  }
23103 
23104  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23105  return QPointF();
23106 }
23107 
23114 {
23115  return mSelected ? mSelectedPen : mPen;
23116 }
23117 
23124 {
23125  return mSelected ? mSelectedBrush : mBrush;
23126 }
23127 
23128 
23132 
23153  QCPAbstractItem(parentPlot),
23154  topLeft(createPosition(QLatin1String("topLeft"))),
23155  bottomRight(createPosition(QLatin1String("bottomRight"))),
23156  top(createAnchor(QLatin1String("top"), aiTop)),
23157  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
23158  right(createAnchor(QLatin1String("right"), aiRight)),
23159  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
23160  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
23161  left(createAnchor(QLatin1String("left"), aiLeft))
23162 {
23163  topLeft->setCoords(0, 1);
23164  bottomRight->setCoords(1, 0);
23165 
23166  setPen(Qt::NoPen);
23167  setSelectedPen(QPen(Qt::blue));
23168  setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
23169 }
23170 
23172 {
23173 }
23174 
23178 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
23179 {
23180  mPixmap = pixmap;
23181  if (mPixmap.isNull())
23182  qDebug() << Q_FUNC_INFO << "pixmap is null";
23183 }
23184 
23189 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
23190 {
23191  mScaled = scaled;
23195 }
23196 
23202 void QCPItemPixmap::setPen(const QPen &pen)
23203 {
23204  mPen = pen;
23205 }
23206 
23213 {
23214  mSelectedPen = pen;
23215 }
23216 
23217 /* inherits documentation from base class */
23218 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23219 {
23220  Q_UNUSED(details)
23221  if (onlySelectable && !mSelectable)
23222  return -1;
23223 
23224  return rectSelectTest(getFinalRect(), pos, true);
23225 }
23226 
23227 /* inherits documentation from base class */
23229 {
23230  bool flipHorz = false;
23231  bool flipVert = false;
23232  QRect rect = getFinalRect(&flipHorz, &flipVert);
23233  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
23234  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
23235  if (boundingRect.intersects(clipRect()))
23236  {
23237  updateScaledPixmap(rect, flipHorz, flipVert);
23238  painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
23239  QPen pen = mainPen();
23240  if (pen.style() != Qt::NoPen)
23241  {
23242  painter->setPen(pen);
23243  painter->setBrush(Qt::NoBrush);
23244  painter->drawRect(rect);
23245  }
23246  }
23247 }
23248 
23249 /* inherits documentation from base class */
23250 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
23251 {
23252  bool flipHorz;
23253  bool flipVert;
23254  QRect rect = getFinalRect(&flipHorz, &flipVert);
23255  // we actually want denormal rects (negative width/height) here, so restore
23256  // the flipped state:
23257  if (flipHorz)
23258  rect.adjust(rect.width(), 0, -rect.width(), 0);
23259  if (flipVert)
23260  rect.adjust(0, rect.height(), 0, -rect.height());
23261 
23262  switch (anchorId)
23263  {
23264  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
23265  case aiTopRight: return rect.topRight();
23266  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
23267  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
23268  case aiBottomLeft: return rect.bottomLeft();
23269  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
23270  }
23271 
23272  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23273  return QPointF();
23274 }
23275 
23289 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
23290 {
23291  if (mPixmap.isNull())
23292  return;
23293 
23294  if (mScaled)
23295  {
23296  if (finalRect.isNull())
23297  finalRect = getFinalRect(&flipHorz, &flipVert);
23298  if (finalRect.size() != mScaledPixmap.size())
23299  {
23300  mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, mTransformationMode);
23301  if (flipHorz || flipVert)
23302  mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
23303  }
23304  } else if (!mScaledPixmap.isNull())
23305  mScaledPixmap = QPixmap();
23306 }
23307 
23322 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
23323 {
23324  QRect result;
23325  bool flipHorz = false;
23326  bool flipVert = false;
23327  QPoint p1 = topLeft->pixelPoint().toPoint();
23328  QPoint p2 = bottomRight->pixelPoint().toPoint();
23329  if (p1 == p2)
23330  return QRect(p1, QSize(0, 0));
23331  if (mScaled)
23332  {
23333  QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
23334  QPoint topLeft = p1;
23335  if (newSize.width() < 0)
23336  {
23337  flipHorz = true;
23338  newSize.rwidth() *= -1;
23339  topLeft.setX(p2.x());
23340  }
23341  if (newSize.height() < 0)
23342  {
23343  flipVert = true;
23344  newSize.rheight() *= -1;
23345  topLeft.setY(p2.y());
23346  }
23347  QSize scaledSize = mPixmap.size();
23348  scaledSize.scale(newSize, mAspectRatioMode);
23349  result = QRect(topLeft, scaledSize);
23350  } else
23351  {
23352  result = QRect(p1, mPixmap.size());
23353  }
23354  if (flippedHorz)
23355  *flippedHorz = flipHorz;
23356  if (flippedVert)
23357  *flippedVert = flipVert;
23358  return result;
23359 }
23360 
23367 {
23368  return mSelected ? mSelectedPen : mPen;
23369 }
23370 
23371 
23375 
23412  QCPAbstractItem(parentPlot),
23413  position(createPosition(QLatin1String("position"))),
23414  mGraph(0)
23415 {
23416  position->setCoords(0, 0);
23417 
23418  setBrush(Qt::NoBrush);
23419  setSelectedBrush(Qt::NoBrush);
23420  setPen(QPen(Qt::black));
23421  setSelectedPen(QPen(Qt::blue, 2));
23423  setSize(6);
23424  setInterpolating(false);
23425  setGraphKey(0);
23426 }
23427 
23429 {
23430 }
23431 
23437 void QCPItemTracer::setPen(const QPen &pen)
23438 {
23439  mPen = pen;
23440 }
23441 
23448 {
23449  mSelectedPen = pen;
23450 }
23451 
23457 void QCPItemTracer::setBrush(const QBrush &brush)
23458 {
23459  mBrush = brush;
23460 }
23461 
23468 {
23470 }
23471 
23477 {
23478  mSize = size;
23479 }
23480 
23488 {
23489  mStyle = style;
23490 }
23491 
23503 {
23504  if (graph)
23505  {
23506  if (graph->parentPlot() == mParentPlot)
23507  {
23509  position->setAxes(graph->keyAxis(), graph->valueAxis());
23510  mGraph = graph;
23511  updatePosition();
23512  } else
23513  qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
23514  } else
23515  {
23516  mGraph = 0;
23517  }
23518 }
23519 
23530 {
23531  mGraphKey = key;
23532 }
23533 
23546 {
23547  mInterpolating = enabled;
23548 }
23549 
23550 /* inherits documentation from base class */
23551 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23552 {
23553  Q_UNUSED(details)
23554  if (onlySelectable && !mSelectable)
23555  return -1;
23556 
23557  QPointF center(position->pixelPoint());
23558  double w = mSize/2.0;
23559  QRect clip = clipRect();
23560  switch (mStyle)
23561  {
23562  case tsNone: return -1;
23563  case tsPlus:
23564  {
23565  if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23566  return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
23567  distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
23568  break;
23569  }
23570  case tsCrosshair:
23571  {
23572  return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
23573  distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
23574  }
23575  case tsCircle:
23576  {
23577  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23578  {
23579  // distance to border:
23580  double centerDist = QVector2D(center-pos).length();
23581  double circleLine = w;
23582  double result = qAbs(centerDist-circleLine);
23583  // filled ellipse, allow click inside to count as hit:
23584  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
23585  {
23586  if (centerDist <= circleLine)
23587  result = mParentPlot->selectionTolerance()*0.99;
23588  }
23589  return result;
23590  }
23591  break;
23592  }
23593  case tsSquare:
23594  {
23595  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23596  {
23597  QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
23598  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
23599  return rectSelectTest(rect, pos, filledRect);
23600  }
23601  break;
23602  }
23603  }
23604  return -1;
23605 }
23606 
23607 /* inherits documentation from base class */
23609 {
23610  updatePosition();
23611  if (mStyle == tsNone)
23612  return;
23613 
23614  painter->setPen(mainPen());
23615  painter->setBrush(mainBrush());
23616  QPointF center(position->pixelPoint());
23617  double w = mSize/2.0;
23618  QRect clip = clipRect();
23619  switch (mStyle)
23620  {
23621  case tsNone: return;
23622  case tsPlus:
23623  {
23624  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23625  {
23626  painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
23627  painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
23628  }
23629  break;
23630  }
23631  case tsCrosshair:
23632  {
23633  if (center.y() > clip.top() && center.y() < clip.bottom())
23634  painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
23635  if (center.x() > clip.left() && center.x() < clip.right())
23636  painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
23637  break;
23638  }
23639  case tsCircle:
23640  {
23641  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23642  painter->drawEllipse(center, w, w);
23643  break;
23644  }
23645  case tsSquare:
23646  {
23647  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23648  painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
23649  break;
23650  }
23651  }
23652 }
23653 
23667 {
23668  if (mGraph)
23669  {
23671  {
23672  if (mGraph->data()->size() > 1)
23673  {
23674  QCPDataMap::const_iterator first = mGraph->data()->constBegin();
23675  QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
23676  if (mGraphKey < first.key())
23677  position->setCoords(first.key(), first.value().value);
23678  else if (mGraphKey > last.key())
23679  position->setCoords(last.key(), last.value().value);
23680  else
23681  {
23682  QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
23683  if (it != first) // mGraphKey is somewhere between iterators
23684  {
23685  QCPDataMap::const_iterator prevIt = it-1;
23686  if (mInterpolating)
23687  {
23688  // interpolate between iterators around mGraphKey:
23689  double slope = 0;
23690  if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
23691  slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
23692  position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
23693  } else
23694  {
23695  // find iterator with key closest to mGraphKey:
23696  if (mGraphKey < (prevIt.key()+it.key())*0.5)
23697  it = prevIt;
23698  position->setCoords(it.key(), it.value().value);
23699  }
23700  } else // mGraphKey is exactly on first iterator
23701  position->setCoords(it.key(), it.value().value);
23702  }
23703  } else if (mGraph->data()->size() == 1)
23704  {
23705  QCPDataMap::const_iterator it = mGraph->data()->constBegin();
23706  position->setCoords(it.key(), it.value().value);
23707  } else
23708  qDebug() << Q_FUNC_INFO << "graph has no data";
23709  } else
23710  qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
23711  }
23712 }
23713 
23720 {
23721  return mSelected ? mSelectedPen : mPen;
23722 }
23723 
23730 {
23731  return mSelected ? mSelectedBrush : mBrush;
23732 }
23733 
23734 
23738 
23765  QCPAbstractItem(parentPlot),
23766  left(createPosition(QLatin1String("left"))),
23767  right(createPosition(QLatin1String("right"))),
23768  center(createAnchor(QLatin1String("center"), aiCenter))
23769 {
23770  left->setCoords(0, 0);
23771  right->setCoords(1, 1);
23772 
23773  setPen(QPen(Qt::black));
23774  setSelectedPen(QPen(Qt::blue, 2));
23775  setLength(8);
23777 }
23778 
23780 {
23781 }
23782 
23792 void QCPItemBracket::setPen(const QPen &pen)
23793 {
23794  mPen = pen;
23795 }
23796 
23803 {
23804  mSelectedPen = pen;
23805 }
23806 
23816 {
23817  mLength = length;
23818 }
23819 
23826 {
23827  mStyle = style;
23828 }
23829 
23830 /* inherits documentation from base class */
23831 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23832 {
23833  Q_UNUSED(details)
23834  if (onlySelectable && !mSelectable)
23835  return -1;
23836 
23837  QVector2D leftVec(left->pixelPoint());
23838  QVector2D rightVec(right->pixelPoint());
23839  if (leftVec.toPoint() == rightVec.toPoint())
23840  return -1;
23841 
23842  QVector2D widthVec = (rightVec-leftVec)*0.5f;
23843  QVector2D lengthVec(-widthVec.y(), widthVec.x());
23844  lengthVec = lengthVec.normalized()*mLength;
23845  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23846 
23847  return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
23848 }
23849 
23850 /* inherits documentation from base class */
23852 {
23853  QVector2D leftVec(left->pixelPoint());
23854  QVector2D rightVec(right->pixelPoint());
23855  if (leftVec.toPoint() == rightVec.toPoint())
23856  return;
23857 
23858  QVector2D widthVec = (rightVec-leftVec)*0.5f;
23859  QVector2D lengthVec(-widthVec.y(), widthVec.x());
23860  lengthVec = lengthVec.normalized()*mLength;
23861  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23862 
23863  QPolygon boundingPoly;
23864  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
23865  << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
23866  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
23867  if (clip.intersects(boundingPoly.boundingRect()))
23868  {
23869  painter->setPen(mainPen());
23870  switch (mStyle)
23871  {
23872  case bsSquare:
23873  {
23874  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
23875  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23876  painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23877  break;
23878  }
23879  case bsRound:
23880  {
23881  painter->setBrush(Qt::NoBrush);
23882  QPainterPath path;
23883  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23884  path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
23885  path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23886  painter->drawPath(path);
23887  break;
23888  }
23889  case bsCurly:
23890  {
23891  painter->setBrush(Qt::NoBrush);
23892  QPainterPath path;
23893  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23894  path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
23895  path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23896  painter->drawPath(path);
23897  break;
23898  }
23899  case bsCalligraphic:
23900  {
23901  painter->setPen(Qt::NoPen);
23902  painter->setBrush(QBrush(mainPen().color()));
23903  QPainterPath path;
23904  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23905 
23906  path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
23907  path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23908 
23909  path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
23910  path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23911 
23912  painter->drawPath(path);
23913  break;
23914  }
23915  }
23916  }
23917 }
23918 
23919 /* inherits documentation from base class */
23920 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
23921 {
23922  QVector2D leftVec(left->pixelPoint());
23923  QVector2D rightVec(right->pixelPoint());
23924  if (leftVec.toPoint() == rightVec.toPoint())
23925  return leftVec.toPointF();
23926 
23927  QVector2D widthVec = (rightVec-leftVec)*0.5f;
23928  QVector2D lengthVec(-widthVec.y(), widthVec.x());
23929  lengthVec = lengthVec.normalized()*mLength;
23930  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23931 
23932  switch (anchorId)
23933  {
23934  case aiCenter:
23935  return centerVec.toPointF();
23936  }
23937  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23938  return QPointF();
23939 }
23940 
23947 {
23948  return mSelected ? mSelectedPen : mPen;
23949 }
23950 
void setOutliers(const QVector< double > &values)
QList< QList< QCPLayoutElement * > > mElements
Definition: qcustomplot.h:814
virtual int elementCount() const
void drawShape(QCPPainter *painter, QPointF pos) const
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:2105
double size() const
void setType(PositionType type)
QCPLayoutElement(QCustomPlot *parentPlot=0)
bool getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
QPen basePen() const
Definition: qcustomplot.h:1128
QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale)
virtual QCPLayoutElement * elementAt(int index) const
void itemClick(QCPAbstractItem *item, QMouseEvent *event)
void drawSubGridLines(QCPPainter *painter) const
QPen mSelectedPen
Definition: qcustomplot.h:3458
void addElement(QCPLayoutElement *element, Qt::Alignment alignment)
bool rangeZoom() const
QCPItemPosition * position(const QString &name) const
void insertRow(int newIndex)
A margin group allows synchronization of margin sides if working with multiple layout elements...
Definition: qcustomplot.h:605
QList< QCPGraph * > mGraphs
Definition: qcustomplot.h:1855
QCPGraph * addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0)
virtual ~QCPAxisPainterPrivate()
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setSubTickLengthIn(int inside)
QMargins padding() const
Definition: qcustomplot.h:3506
void setMargins(const QMargins &margins)
0x04 Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width...
Definition: qcustomplot.h:328
0xFFFF All elements
Definition: qcustomplot.h:134
QList< QCPLayoutElement * > elements(QCP::MarginSide side) const
Definition: qcustomplot.h:613
void setWhiskerBarPen(const QPen &pen)
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
int clearPlottables()
void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true)
void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
Qt::Orientations mRangeDrag
Definition: qcustomplot.h:2100
A plus shaped crosshair which spans the complete axis rect.
Definition: qcustomplot.h:3705
static const double maxRange
Definition: qcustomplot.h:515
void setPeriodic(bool enabled)
void setLevelCount(int n)
QColor mainTextColor() const
QRgb color(double position, const QCPRange &range, bool logarithmic=false)
Whether to use immediate repaint or queued update depends on whether the plotting hint QCP::phForceRe...
Definition: qcustomplot.h:1717
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setInsetPlacement(int index, InsetPlacement placement)
Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
bool isInvalidData(double value)
Definition: qcustomplot.h:177
The negative sign domain, i.e. numbers smaller than zero.
Definition: qcustomplot.h:1460
QPen mainPen() const
void clear()
virtual void updateMapImage()
virtual void update(UpdatePhase phase)
int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
QCPColorGradient gradient() const
Definition: qcustomplot.h:2458
QCPAxis * rangeZoomAxis(Qt::Orientation orientation)
void setDateTimeFormat(const QString &format)
void setText(const QString &text)
void selectableChanged(bool selectable)
void setLowerEnding(const QCPLineEnding &ending)
0x08 bottom margin
Definition: qcustomplot.h:108
virtual ~QCPAxisRect()
Open-High-Low-Close bar representation.
Definition: qcustomplot.h:3213
bool clipToAxisRect() const
Definition: qcustomplot.h:1626
virtual QRect clipRect() const
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
QCPItemPosition *const end
Definition: qcustomplot.h:3402
void updateAxesOffset(QCPAxis::AxisType type)
QPen mErrorPen
Definition: qcustomplot.h:2633
QCPColorMapData * mMapData
Definition: qcustomplot.h:3146
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
virtual void draw(QCPPainter *painter)
int plottableCount() const
QCPLayout * mParentLayout
Definition: qcustomplot.h:694
void setAutoTickLabels(bool on)
double cell(int keyIndex, int valueIndex)
QCPAxis * mParentAxis
Definition: qcustomplot.h:971
Q_SLOT void setSelected(bool selected)
void setMinimumMargins(const QMargins &margins)
A filled square.
Definition: qcustomplot.h:898
QFont mLabelFont
Definition: qcustomplot.h:1249
QCPAxis(QCPAxisRect *parent, AxisType type)
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
PainterModes modes() const
Definition: qcustomplot.h:339
QCPRange dataRange() const
Definition: qcustomplot.h:2456
void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true)
bool mAutoTickStep
Definition: qcustomplot.h:1268
void setKeySize(int keySize)
QMap< double, QCPData > QCPDataMap
Definition: qcustomplot.h:2535
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual ~QCPColorScale()
void remove(QCPBars *bars)
void removeDataAfter(double key)
QPen mSubGridPen
Definition: qcustomplot.h:969
QCPGrid * mGrid
Definition: qcustomplot.h:1279
int padding() const
Definition: qcustomplot.h:1135
QCP::AntialiasedElements antialiasedElements() const
Definition: qcustomplot.h:1729
void setMaximumSize(const QSize &size)
Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the ...
Definition: qcustomplot.h:1915
static QCPFinancialDataMap timeSeriesToOhlc(const QVector< double > &time, const QVector< double > &value, double timeBinSize, double timeBinOffset=0)
Holds the data of one single data point for QCPCurve.
Definition: qcustomplot.h:2688
void mouseMove(QMouseEvent *event)
QCPRange keyRange() const
Definition: qcustomplot.h:3058
No line is drawn between data points (e.g. only scatters)
Definition: qcustomplot.h:2723
QCustomPlot(QWidget *parent=0)
virtual int calculateAutoSubTickCount(double tickStep) const
void setParentLayerable(QCPLayerable *parentLayerable)
QCPItemBracket(QCustomPlot *parentPlot)
QCPLineEnding tail() const
Definition: qcustomplot.h:3388
void rescaleKeyAxis(bool onlyEnlarge=false) const
bool mNumberBeautifulPowers
Definition: qcustomplot.h:1262
QCPItemAnchor *const center
Definition: qcustomplot.h:3601
A legend item representing a plottable with an icon and the plottable name.
Definition: qcustomplot.h:2195
QHash< QCP::MarginSide, QCPMarginGroup * > mMarginGroups
Definition: qcustomplot.h:699
Bar spacing is given by a fraction of the axis rect size.
Definition: qcustomplot.h:2805
bool antialiasing() const
Definition: qcustomplot.h:338
void setType(QCPAxis::AxisType type)
void setIconTextPadding(int padding)
each data point is represented by a line parallel to the value axis, which reaches from the data poin...
Definition: qcustomplot.h:2565
QCPItemPosition *const start
Definition: qcustomplot.h:3399
void selectionChanged(bool selected)
int numberPrecision() const
Definition: qcustomplot.h:1119
virtual QList< QCPLayoutElement * > elements(bool recursive) const
void expandTo(int newRowCount, int newColumnCount)
double mErrorBarSize
Definition: qcustomplot.h:2637
SelectableParts selectedParts() const
QByteArray mLabelParameterHash
Definition: qcustomplot.h:1379
0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fix...
Definition: qcustomplot.h:326
bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
QBrush mSelectedBrush
Definition: qcustomplot.h:3747
void setMedianPen(const QPen &pen)
QList< QCPAbstractPlottable * > selectedPlottables() const
QBrush mBackgroundBrush
Definition: qcustomplot.h:2094
void selectionChanged(bool selected)
double maximum() const
Definition: qcustomplot.h:2992
Q_SLOT bool setLayer(QCPLayer *layer)
QList< QCPGraph * > graphs() const
QCPAbstractPlottable * plottable()
Definition: qcustomplot.h:2202
bool contains(double value) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void visibleTickBounds(int &lowIndex, int &highIndex) const
The axis backbone and tick marks.
Definition: qcustomplot.h:1086
double realLength() const
bool scaled() const
Definition: qcustomplot.h:3637
virtual void mouseMoveEvent(QMouseEvent *event)
virtual ~QCPGraph()
QCPBarDataMap * data() const
Definition: qcustomplot.h:2907
QCPLineEnding lowerEnding
Definition: qcustomplot.h:1343
EndingStyle style() const
Definition: qcustomplot.h:909
Bar spacing is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:2806
virtual void draw(QCPPainter *painter)
QCPItemAnchor * anchor(const QString &name) const
void drawGridLines(QCPPainter *painter) const
void setSpacingType(SpacingType spacingType)
virtual void draw(QCPPainter *painter)
void setSelectedTickLabelFont(const QFont &font)
void setOffset(int offset)
QColor mSelectedTickLabelColor
Definition: qcustomplot.h:1257
void setBasePen(const QPen &pen)
bool hasPlottable(QCPAbstractPlottable *plottable) const
virtual ~QCPItemText()
void setVisible(bool visible)
double key
Definition: qcustomplot.h:2522
The tracer is not visible.
Definition: qcustomplot.h:3703
virtual QPointF anchorPixelPoint(int anchorId) const
virtual void drawImpulsePlot(QCPPainter *painter, QVector< QPointF > *lineData) const
QString text() const
Definition: qcustomplot.h:3502
void setSelectedBorderPen(const QPen &pen)
int bottom() const
Definition: qcustomplot.h:2078
void setPixmap(const QPixmap &pixmap)
QColor getTickLabelColor() const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3445
SelectableParts mSelectedParts
Definition: qcustomplot.h:1243
custom painter operations are performed per scatter (As QPainterPath, see setCustomPath) ...
Definition: qcustomplot.h:268
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
The abstract base class for all entries in a QCPLegend.
Definition: qcustomplot.h:2131
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
QFont mSelectedTickLabelFont
Definition: qcustomplot.h:1256
QColor mColor
Definition: qcustomplot.h:3540
void setHead(const QCPLineEnding &head)
bool remove(QCPLayoutElement *element)
{ssPeace.png} a circle, with one vertical and two downward diagonal lines
Definition: qcustomplot.h:266
QBrush mBrush
Definition: qcustomplot.h:3459
bool twoColored() const
Definition: qcustomplot.h:3225
Tick labels (numbers) of this axis (as a whole, not individually)
Definition: qcustomplot.h:1087
bool mAntialiasedZeroLine
Definition: qcustomplot.h:968
void setStyle(BracketStyle style)
virtual void draw(QCPPainter *painter)
void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
QColor getTextColor() const
void addChild(QCPLayerable *layerable, bool prepend)
void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
void setPenPositive(const QPen &pen)
QCPAxisRect * mAxisRect
Definition: qcustomplot.h:1239
static AxisType marginSideToAxisType(QCP::MarginSide side)
0x0001 Axis base line and tick marks
Definition: qcustomplot.h:123
QCPLineEnding mHead
Definition: qcustomplot.h:3360
QCPItemAnchor * createAnchor(const QString &name, int anchorId)
Q_SLOT void setDataRange(const QCPRange &dataRange)
void setName(const QString &name)
QCPAxis::AxisType mType
Definition: qcustomplot.h:2488
QPainterPath customPath() const
Definition: qcustomplot.h:285
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
void setRowStretchFactors(const QList< double > &factors)
virtual void draw(QCPPainter *painter)
{ssSquare.png} a square
Definition: qcustomplot.h:257
void setRangeReversed(bool reversed)
QPointF upperFillBasePoint(double upperKey) const
void setSelectedBasePen(const QPen &pen)
0x0008 Legend box
Definition: qcustomplot.h:126
Responsible for drawing the grid of a QCPAxis.
Definition: qcustomplot.h:935
Qt::Alignment positionAlignment() const
Definition: qcustomplot.h:3503
QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes) ...
Definition: qcustomplot.h:160
QCPItemAnchor * parentAnchor() const
Definition: qcustomplot.h:1571
0x00 no margin
Definition: qcustomplot.h:110
QVector< QString > tickLabels
Definition: qcustomplot.h:1364
QImage mMapImage
Definition: qcustomplot.h:3152
void addData(const QCPFinancialDataMap &dataMap)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setWhiskerWidth(double width)
void dataRangeChanged(QCPRange newRange)
QCPLayer * mLayer
Definition: qcustomplot.h:454
void setTextAlignment(Qt::Alignment alignment)
bool visible() const
Definition: qcustomplot.h:428
const QCPRange range() const
Definition: qcustomplot.h:1101
virtual ~QCPItemLine()
QCPColorGradient mGradient
Definition: qcustomplot.h:2491
int itemCount() const
0x04 top margin
Definition: qcustomplot.h:107
QList< QCPGraph * > graphs() const
void setSelectedBrush(const QBrush &brush)
void setSelectedSubTickPen(const QPen &pen)
void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
virtual ~QCPAxis()
virtual void mouseReleaseEvent(QMouseEvent *event)
QCPColorMapData * data() const
Definition: qcustomplot.h:3112
double size() const
Definition: qcustomplot.h:280
bool noAntialiasingOnDrag() const
Definition: qcustomplot.h:1734
QRect axisSelectionBox() const
Definition: qcustomplot.h:1336
bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1)
double mLength
Definition: qcustomplot.h:929
virtual void wheelEvent(QWheelEvent *event)
void setCustomPath(const QPainterPath &customPath)
virtual ~QCPColorMap()
Linear scaling.
Definition: qcustomplot.h:1077
QColor color() const
Definition: qcustomplot.h:3494
void removeDataBefore(double key)
QList< QCPItemPosition * > mPositions
Definition: qcustomplot.h:1655
void setShape(ScatterShape shape)
QCP::PlottingHints plottingHints() const
Definition: qcustomplot.h:1735
QCustomPlot * mParentPlot
Definition: qcustomplot.h:1523
QColor mSelectedTextColor
Definition: qcustomplot.h:2390
void makeNonCosmetic()
QCPLayer * layer() const
Definition: qcustomplot.h:431
void setBackgroundScaled(bool scaled)
Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white.
Definition: qcustomplot.h:1930
double value
Definition: qcustomplot.h:2522
void setTickLabels(bool show)
An anchor of an item to which positions can be attached to.
Definition: qcustomplot.h:1508
void setSelectedPen(const QPen &pen)
Qt::Orientations mRangeZoom
Definition: qcustomplot.h:2100
void setBrush(const QBrush &brush)
virtual int calculateAutoMargin(QCP::MarginSide side)
int mSelectionTolerance
Definition: qcustomplot.h:1860
QBrush mainBrush() const
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
QCPAxis * keyAxis() const
Definition: qcustomplot.h:1577
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual QPointF anchorPixelPoint(int anchorId) const
Phase in which the margins are calculated and set.
Definition: qcustomplot.h:652
QPen mainPen() const
Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts)
int labelPadding() const
void setSelectedIconBorderPen(const QPen &pen)
Static positioning in pixels, starting from the top left corner of the viewport/widget.
Definition: qcustomplot.h:1554
void setAntialiasedScatters(bool enabled)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
double width() const
Definition: qcustomplot.h:2994
void setRangeZoom(Qt::Orientations orientations)
void setLowerQuartile(double value)
QCPItemRect(QCustomPlot *parentPlot)
QCPItemAnchor * mParentAnchorX
Definition: qcustomplot.h:1601
0x0004 Sub grid lines
Definition: qcustomplot.h:125
QCache< QString, CachedLabel > mLabelCache
Definition: qcustomplot.h:1380
void mouseRelease(QMouseEvent *event)
void setSelectedTextColor(const QColor &color)
A brace with angled edges.
Definition: qcustomplot.h:3773
void setSelectedPen(const QPen &pen)
QCPItemPosition *const right
Definition: qcustomplot.h:3798
void setWhiskerPen(const QPen &pen)
void setAdaptiveSampling(bool enabled)
void setAutoTickStep(bool on)
int findIndexAboveY(const QVector< QPointF > *data, double y) const
QVector< double > subTickPositions
Definition: qcustomplot.h:1362
void setSubTickCount(int count)
void rangeChanged(const QCPRange &newRange)
void setAutoTicks(bool on)
A plus shaped crosshair with limited size.
Definition: qcustomplot.h:3704
void rescaleDataRange(bool onlyVisibleMaps)
void mouseDoubleClick(QMouseEvent *event)
virtual QList< QCPLayoutElement * > elements(bool recursive) const
QCPPlotTitle(QCustomPlot *parentPlot)
void setupFullAxesBox(bool connectRanges=false)
virtual ~QCustomPlot()
void getPreparedData(QVector< QCPData > *lineData, QVector< QCPData > *scatterData) const
QCPItemLine(QCustomPlot *parentPlot)
virtual void mouseReleaseEvent(QMouseEvent *event)
void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
QCPFinancialDataMap * mData
Definition: qcustomplot.h:3262
Q_SLOT void setSelectableParts(const SelectableParts &selectableParts)
QCPLineEnding mTail
Definition: qcustomplot.h:3407
QCPAxis * addAxis(QCPAxis::AxisType type, QCPAxis *axis=0)
double tickLabelRotation() const
0x0002 Grid lines
Definition: qcustomplot.h:124
QColor mainColor() const
QCPLineEnding head() const
Definition: qcustomplot.h:3387
0x01 left margin
Definition: qcustomplot.h:105
static bool validRange(double lower, double upper)
bool mAutoTickLabels
Definition: qcustomplot.h:1253
Base class for all drawable objects.
Definition: qcustomplot.h:413
QBrush mBrushNegative
Definition: qcustomplot.h:3266
virtual void draw(QCPPainter *painter)
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:1863
void setAutoSubTicks(bool on)
VESPUCCI_EXPORT arma::uword max(arma::uword a, arma::uword b)
Vespucci::Math::max.
Definition: accessory.cpp:237
void setInterpolating(bool enabled)
virtual void drawWhiskers(QCPPainter *painter) const
void setValueAxis(QCPAxis *axis)
void setBackground(const QPixmap &pm)
double getPixelSpacing(const QCPBars *bars, double keyCoord)
QFont font() const
Definition: qcustomplot.h:2361
void setPadding(int padding)
QPen mainPen() const
QCPAxis * xAxis
Definition: qcustomplot.h:1824
A plottable representing a bar chart in a plot.
Definition: qcustomplot.h:2873
void rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
const QCP::Interactions interactions() const
Definition: qcustomplot.h:1732
QPen subTickPen() const
Definition: qcustomplot.h:1130
QFont font() const
Definition: qcustomplot.h:3500
bool removeGraph(QCPGraph *graph)
QVector< double > mTickVector
Definition: qcustomplot.h:1282
void setRange(const QCPRange &keyRange, const QCPRange &valueRange)
0x080 All other objects are selectable (e.g. your own derived layerables, the plot title...
Definition: qcustomplot.h:167
QList< QCPAbstractPlottable * > plottables() const
void setCoords(double key, double value)
void addChild(QCP::MarginSide side, QCPLayoutElement *element)
T diff(const T &X, arma::uword deriv_order=1)
Vespucci::Math::diff.
void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
SpacingType mSpacingType
Definition: qcustomplot.h:2833
0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap) ...
Definition: qcustomplot.h:130
friend class QCPItemAnchor
Definition: qcustomplot.h:1681
Q_SLOT void setSelectable(bool selectable)
void setSelectedPen(const QPen &pen)
void setWidth(double width)
void loadPreset(GradientPreset preset)
void moveAbove(QCPBars *bars)
virtual QCP::Interaction selectionCategory() const
virtual void mousePressEvent(QMouseEvent *event)
void setSize(int keySize, int valueSize)
void moveBelow(QCPBars *bars)
QString mDateTimeFormat
Definition: qcustomplot.h:1258
void setScaleLogBase(double base)
void setScatterStyle(const QCPScatterStyle &style)
virtual void mouseMoveEvent(QMouseEvent *event)
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
void setRangeUpper(double upper)
void setModes(PainterModes modes)
QCPItemTracer(QCustomPlot *parentPlot)
QPen mSubTickPen
Definition: qcustomplot.h:1271
void recalculateDataBounds()
int valueSize() const
Definition: qcustomplot.h:3057
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void getStepCenterPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QPoint mDragStart
Definition: qcustomplot.h:2106
void titleClick(QMouseEvent *event, QCPPlotTitle *title)
virtual QRect clipRect() const
virtual QCP::Interaction selectionCategory() const
void layerChanged(QCPLayer *newLayer)
virtual bool addToLegend()
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const =0
int mLowestVisibleTick
Definition: qcustomplot.h:1281
bool addElement(int row, int column, QCPLayoutElement *element)
QPen pen() const
Definition: qcustomplot.h:3385
void setStyle(EndingStyle style)
QPointer< QCPBars > mBarBelow
Definition: qcustomplot.h:2940
QPixmap mScaledPixmap
Definition: qcustomplot.h:3666
void setValueSize(int valueSize)
virtual ~QCPLayoutElement()
QBrush mBrush
Definition: qcustomplot.h:3542
QList< QCPLayerable * > children() const
Definition: qcustomplot.h:388
void setTickLabelRotation(double degrees)
double spacing() const
Definition: qcustomplot.h:2813
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:2759
void setTypeX(PositionType type)
virtual ~QCPItemTracer()
TracerStyle mStyle
Definition: qcustomplot.h:3749
0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixma...
Definition: qcustomplot.h:327
QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual void deselectEvent(bool *selectionStateChanged)
A layout that arranges child elements in a grid.
Definition: qcustomplot.h:762
void setPen(const QPen &pen)
virtual void mousePressEvent(QMouseEvent *event)
virtual ~QCPBars()
void setFont(const QFont &font)
int levelCount() const
Definition: qcustomplot.h:1988
void setColor(const QColor &color)
void selectionChangedByUser()
QMargins margins() const
Definition: qcustomplot.h:664
0x08 Axis is horizontal and on the bottom side of the axis rect
Definition: qcustomplot.h:1050
QPen mBasePen
Definition: qcustomplot.h:1244
bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0)
QPointer< QCPBars > mBarAbove
Definition: qcustomplot.h:2940
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPScatterStyle mOutlierStyle
Definition: qcustomplot.h:3028
bool rangeDrag() const
void setUpperEnding(const QCPLineEnding &ending)
void setPixmap(const QPixmap &pixmap)
Tick coordinate is regarded as a date/time (seconds since 1970-01-01T00:00:00 UTC) and will be displa...
Definition: qcustomplot.h:1061
void setWidth(double width)
void getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector< QPointF > &beforeTraverse, QVector< QPointF > &afterTraverse) const
virtual QSize minimumSizeHint() const
double mSpacing
Definition: qcustomplot.h:2834
virtual QCP::Interaction selectionCategory() const
QCPItemText(QCustomPlot *parentPlot)
double valueErrorPlus
Definition: qcustomplot.h:2524
QList< QCPLayer * > mLayers
Definition: qcustomplot.h:1857
QBrush mainBrush() const
void titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
void setRangeDrag(Qt::Orientations orientations)
QCP::AntialiasedElements mNotAntialiasedElements
Definition: qcustomplot.h:1858
QCPItemAnchor *const bottom
Definition: qcustomplot.h:3532
0x0040 Main lines of items
Definition: qcustomplot.h:129
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
void removeChild(QCPLayerable *layerable)
QCPAxisRect * axisRect() const
virtual QCPLayoutElement * takeAt(int index)
virtual QCP::Interaction selectionCategory() const
void removeData(double fromKey, double toKey)
no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines) ...
Definition: qcustomplot.h:251
void removeDataBefore(double key)
bool begin(QPaintDevice *device)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
double value() const
Definition: qcustomplot.h:1575
virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false)
void getLinePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QCPLineEnding upperEnding() const
The abstract base class for all data representing objects in a plot.
Definition: qcustomplot.h:1393
QCP::MarginSides mAutoMargins
Definition: qcustomplot.h:698
void setColorScale(QCPColorScale *colorScale)
void fill(double z)
EndingStyle mStyle
Definition: qcustomplot.h:928
QCPColorGradient inverted() const
Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion ...
Definition: qcustomplot.h:1933
QRectF insetRect(int index) const
QCPRange sanitizedForLogScale() const
void setSelectedBrush(const QBrush &brush)
void setBackground(const QPixmap &pm)
virtual QByteArray generateLabelParameterHash() const
QPointF coords() const
Definition: qcustomplot.h:1576
void setAutoTickCount(int approximateCount)
void releaseElement(QCPLayoutElement *el)
void removeChildX(QCPItemPosition *pos)
QCPItemAnchor *const left
Definition: qcustomplot.h:3600
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:1470
void setSubTickLengthOut(int outside)
bool removeAt(int index)
double mGraphKey
Definition: qcustomplot.h:3751
virtual void clearData()
void setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
QList< QCPBars * > bars() const
Definition: qcustomplot.h:2820
void removeData(double fromt, double tot)
void setTwoColored(bool twoColored)
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpHint)
QPointF lowerFillBasePoint(double lowerKey) const
virtual ~QCPCurve()
bool moveToLayer(QCPLayer *layer, bool prepend)
QPointer< QCPAxis > mColorAxis
Definition: qcustomplot.h:2496
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable) ...
Definition: qcustomplot.h:163
void setSubGridPen(const QPen &pen)
virtual QSize minimumSizeHint() const
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void removeDataAfter(double t)
QList< QCPAbstractLegendItem * > selectedItems() const
void setAntialiasedSubGrid(bool enabled)
A filled circle.
Definition: qcustomplot.h:897
virtual ~QCPItemStraightLine()
Tick labels will be displayed outside the axis rect.
Definition: qcustomplot.h:1070
virtual void draw(QCPPainter *painter)
QCPItemAnchor * parentAnchorY() const
Definition: qcustomplot.h:1573
Continuous lightness from black over icey colors to white (suited for non-biased data representation)...
Definition: qcustomplot.h:1925
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
line is drawn as steps where the step height is the value of the left data point
Definition: qcustomplot.h:2562
double valueErrorMinus
Definition: qcustomplot.h:2524
void selectionChanged(const QCPAxis::SelectableParts &parts)
virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const
QCPItemPosition *const endDir
Definition: qcustomplot.h:3401
QPen getBasePen() const
double minimum() const
Definition: qcustomplot.h:2988
void setErrorBarSize(double size)
0x02 Axis is vertical and on the right side of the axis rect
Definition: qcustomplot.h:1048
virtual void clearData()
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
An approximation of the visible light spectrum (creates banding illusion but allows more precise magn...
Definition: qcustomplot.h:1932
void setBackgroundScaled(bool scaled)
void setTickLabelFont(const QFont &font)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3592
QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange)
double width() const
Definition: qcustomplot.h:910
bool mInterpolate
Definition: qcustomplot.h:3148
int subTickLengthIn() const
QCPColorGradient mGradient
Definition: qcustomplot.h:3147
void setTextColor(const QColor &color)
virtual ~QCPItemEllipse()
Bar width is in absolute pixels.
Definition: qcustomplot.h:2891
int tickLengthOut() const
void getScatterPlotData(QVector< QCPData > *scatterData) const
Qt::AspectRatioMode aspectRatioMode() const
Definition: qcustomplot.h:3638
int mHighestVisibleTick
Definition: qcustomplot.h:1281
QList< QCPAxis * > axes() const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:396
bool removeLayer(QCPLayer *layer)
void setSelectedLabelFont(const QFont &font)
virtual int size() const
void getPixelWidth(double key, double &lower, double &upper) const
QCPColorScale * colorScale() const
Definition: qcustomplot.h:3118
A plottable representing a graph in a plot.
Definition: qcustomplot.h:2540
void applyFillAntialiasingHint(QCPPainter *painter) const
QCPItemPosition *const start
Definition: qcustomplot.h:3354
int iconTextPadding() const
Definition: qcustomplot.h:2260
The abstract base class for all items in a plot.
Definition: qcustomplot.h:1612
QCPAbstractLegendItem(QCPLegend *parent)
QString name() const
Definition: qcustomplot.h:386
QPen mainPen() const
QCPLayoutInset * mInsetLayout
Definition: qcustomplot.h:2099
void setData(double key, double value, double z)
double length() const
Definition: qcustomplot.h:3785
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const =0
Q_SLOT void setSelectable(bool selectable)
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
Q_SLOT void setGradient(const QCPColorGradient &gradient)
0x004 axis (tick) labels will be cached as pixmaps, increasing replot performance.
Definition: qcustomplot.h:149
QRect tickLabelsSelectionBox() const
Definition: qcustomplot.h:1337
void setPadding(const QMargins &padding)
Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power...
Definition: qcustomplot.h:1078
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPCurveDataMap * mData
Definition: qcustomplot.h:2758
Q_SLOT void setSelectable(bool selectable)
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
QCPLegend * mParentLegend
Definition: qcustomplot.h:2172
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:1470
QBrush brush() const
Definition: qcustomplot.h:3717
QVector< int > getSectionSizes(QVector< int > maxSizes, QVector< int > minSizes, QVector< double > stretchFactors, int totalSize) const
void getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
virtual QSize maximumSizeHint() const
void setRangeLower(double lower)
void setBrush(const QBrush &brush)
QCPItemCurve(QCustomPlot *parentPlot)
QCPLineEnding mHead
Definition: qcustomplot.h:3407
QFont mSelectedLabelFont
Definition: qcustomplot.h:1249
QCPColorScale(QCustomPlot *parentPlot)
QPointer< QCPAxis > mRangeZoomHorzAxis
Definition: qcustomplot.h:2101
QCPItemAnchor *const right
Definition: qcustomplot.h:3449
bool mNoAntialiasingOnDrag
Definition: qcustomplot.h:1861
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:1685
virtual void wheelEvent(QWheelEvent *event)
void setMedian(double value)
void setAntialiasing(bool enabled)
VESPUCCI_EXPORT void position(arma::uword index, arma::uword n_rows, arma::uword n_cols, arma::uword &i, arma::uword &j)
Vespucci::Math::position Find row and column numbers for index.
Definition: accessory.cpp:469
void setPen(const QPen &pen)
virtual QSize minimumSizeHint() const
0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see QCPAbstractItem) ...
Definition: qcustomplot.h:166
virtual void draw(QCPPainter *painter)
Candlestick representation.
Definition: qcustomplot.h:3214
void setMinimum(double value)
void setWidth(double width)
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
bool selectable() const
Definition: qcustomplot.h:1628
void setBrushNegative(const QBrush &brush)
Manages a single axis inside a QCustomPlot.
Definition: qcustomplot.h:985
QCPLineEnding tail() const
Definition: qcustomplot.h:3343
QCPAxis::AxisType type() const
Definition: qcustomplot.h:2455
double upperQuartile() const
Definition: qcustomplot.h:2991
QCPBarsGroup * barsGroup() const
Definition: qcustomplot.h:2903
Q_SLOT void setSelectable(bool selectable)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
virtual ~QCPLegend()
double mScaleLogBase
Definition: qcustomplot.h:1276
double lowerQuartile() const
Definition: qcustomplot.h:2989
void drawLine(const QLineF &line)
QList< QCPAbstractItem * > selectedItems() const
QBrush brush() const
Definition: qcustomplot.h:283
Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allo...
Definition: qcustomplot.h:1929
void draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
double width() const
Definition: qcustomplot.h:2901
bool mIsAntialiasing
Definition: qcustomplot.h:362
void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
int commonMargin(QCP::MarginSide side) const
void setTickPen(const QPen &pen)
void setMaximum(double value)
void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
QCPColorScale * mParentColorScale
Definition: qcustomplot.h:2418
void setPen(const QPen &pen)
void setColorStops(const QMap< double, QColor > &colorStops)
virtual int elementCount() const
QCPLayer * layer(const QString &name) const
QPen pen() const
Definition: qcustomplot.h:3340
A filled arrow head with a straight/flat back (a triangle)
Definition: qcustomplot.h:894
void getImpulsePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
PositionType type() const
Definition: qcustomplot.h:1568
int findIndexAboveX(const QVector< QPointF > *data, double x) const
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:239
{ssCircle.png} a circle
Definition: qcustomplot.h:255
int index() const
Definition: qcustomplot.h:387
virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const
double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const
void setOutlierStyle(const QCPScatterStyle &style)
Manages a legend inside a QCustomPlot.
Definition: qcustomplot.h:2219
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QList< QCPLayerable * > mChildren
Definition: qcustomplot.h:399
QSet< QCPItemPosition * > mChildrenY
Definition: qcustomplot.h:1526
void insert(int i, QCPBars *bars)
QCPLineEnding lowerEnding() const
void setCell(int keyIndex, int valueIndex, double z)
QCPRange mDataRange
Definition: qcustomplot.h:3144
bool addItem(QCPAbstractItem *item)
PositionType mPositionTypeY
Definition: qcustomplot.h:1597
QCPLayerable * parentLayerable() const
Definition: qcustomplot.h:430
QVector< QPointF > getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
virtual QSize maximumSizeHint() const
virtual void drawLinePlot(QCPPainter *painter, QVector< QPointF > *lineData) const
bool mAutoSubTicks
Definition: qcustomplot.h:1268
QPointer< QCPColorScaleAxisRectPrivate > mAxisRect
Definition: qcustomplot.h:2495
void setInsetRect(int index, const QRectF &rect)
line is drawn as steps where the step is in between two data points
Definition: qcustomplot.h:2564
void setTextColor(const QColor &color)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual bool take(QCPLayoutElement *element)=0
int columnCount() const
void getStepLeftPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
bool addPlottable(QCPAbstractPlottable *plottable)
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:2098
double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
void setBrush(const QBrush &brush)
0x0200 Borders of fills (e.g. under or between graphs)
Definition: qcustomplot.h:132
0x004 The user can select multiple objects by holding the modifier set by QCustomPlot::setMultiSelect...
Definition: qcustomplot.h:162
QCPAbstractPlottable * plottable()
void removeData(double fromKey, double toKey)
QString label() const
QCPItemAnchor *const bottomRight
Definition: qcustomplot.h:3531
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
virtual ~QCPItemPixmap()
void setSpacing(double spacing)
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:1598
Bar width is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:2893
void addData(const QCPCurveDataMap &dataMap)
void setRowSpacing(int pixels)
QCPRange valueRange() const
Definition: qcustomplot.h:3059
QCPItemPosition * createPosition(const QString &name)
QMargins mMargins
Definition: qcustomplot.h:697
void setMinimumSize(const QSize &size)
QCPRange mDragStartVertRange
Definition: qcustomplot.h:2104
QVector< double > mSubTickVector
Definition: qcustomplot.h:1284
void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
QPen tickPen() const
Definition: qcustomplot.h:1129
QList< QCPBars * > mBars
Definition: qcustomplot.h:2835
int itemCount() const
QPen getSubTickPen() const
bool isEmpty() const
Definition: qcustomplot.h:3078
QPen pen() const
Definition: qcustomplot.h:3496
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPGraph * graph() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setKeyAxis(QCPAxis *axis)
virtual ~QCPItemCurve()
QLineF getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
QPolygonF getBarPolygon(double key, double value) const
double width() const
Definition: qcustomplot.h:3224
int height() const
Definition: qcustomplot.h:2080
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
A layer that may contain objects, to control the rendering order.
Definition: qcustomplot.h:370
bool mTickLabels
Definition: qcustomplot.h:1253
Qt::Orientation orientation() const
Definition: qcustomplot.h:1211
Continuous lightness from black over firey colors to white (suited for non-biased data representation...
Definition: qcustomplot.h:1924
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void setPen(const QPen &pen)
Definition: qcustomplot.cpp:91
QCPItemPosition *const position
Definition: qcustomplot.h:3742
void rescaleDataRange(bool recalculateDataBounds=false)
virtual ~QCPItemRect()
void setInverted(bool inverted)
LabelSide tickLabelSide() const
void setAutoMargins(QCP::MarginSides sides)
virtual void clearData()
virtual void axisRemoved(QCPAxis *axis)
0x00 Default mode for painting on screen devices
Definition: qcustomplot.h:325
void setTickVector(const QVector< double > &vec)
LineStyle mLineStyle
Definition: qcustomplot.h:2760
QFont getLabelFont() const
Error bars for both key and value dimensions of the data point are shown.
Definition: qcustomplot.h:2574
SpacingType spacingType() const
Definition: qcustomplot.h:2812
The abstract base class for all objects that form the layout system.
Definition: qcustomplot.h:634
QCPBarsGroup(QCustomPlot *parentPlot)
PlottingHint
Definition: qcustomplot.h:144
QColor getLabelColor() const
void setGraph(QCPGraph *graph)
void mousePress(QMouseEvent *event)
QCPRange mValueRange
Definition: qcustomplot.h:3085
Continuous lightness from black to white (suited for non-biased data representation) ...
Definition: qcustomplot.h:1923
virtual void update(UpdatePhase phase)
void setFont(const QFont &font)
{ssPlus.png} a plus
Definition: qcustomplot.h:254
virtual void draw(QCPPainter *painter)
int findIndexBelowX(const QVector< QPointF > *data, double x) const
QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
void plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
QCPRange expanded(const QCPRange &otherRange) const
QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name)
Q_SLOT void setGradient(const QCPColorGradient &gradient)
void applyScattersAntialiasingHint(QCPPainter *painter) const
QCPAxis * keyAxis() const
Definition: qcustomplot.h:1422
QBrush brush() const
Definition: qcustomplot.h:3498
void setOuterRect(const QRect &rect)
void setPen(const QPen &pen)
bool mayTraverse(int prevRegion, int currentRegion) const
QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
void setLineStyle(LineStyle ls)
int offset() const
void setDataBothError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError, const QVector< double > &valueError)
int mAutoTickCount
Definition: qcustomplot.h:1267
QCPColorGradient gradient() const
Definition: qcustomplot.h:3117
int keySize() const
Definition: qcustomplot.h:3056
void setPen(const QPen &pen)
virtual QPointF anchorPixelPoint(int anchorId) const
void setUpperQuartile(double value)
void updateLayerIndices() const
Blue over pink to white.
Definition: qcustomplot.h:1927
QCPLineEnding upperEnding
Definition: qcustomplot.h:1343
void selectableChanged(bool selectable)
QString mLabel
Definition: qcustomplot.h:1248
void setTextColor(const QColor &color)
Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts)
void getMaximumRowColSizes(QVector< int > *maxColWidths, QVector< int > *maxRowHeights) const
void insertColumn(int newIndex)
void setColumnSpacing(int pixels)
QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const
Qt::AspectRatioMode mAspectRatioMode
Definition: qcustomplot.h:3668
QCPItemStraightLine(QCustomPlot *parentPlot)
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
bool selected() const
Definition: qcustomplot.h:1425
QColor mTextColor
Definition: qcustomplot.h:2388
QBrush mSelectedBrush
Definition: qcustomplot.h:3542
QPen selectedIconBorderPen() const
Definition: qcustomplot.h:2265
virtual void draw(QCPPainter *painter)=0
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPItemAnchor * parentAnchorX() const
Definition: qcustomplot.h:1572
double mRotation
Definition: qcustomplot.h:3547
int findIndexBelowY(const QVector< QPointF > *data, double y) const
QCPLayer * mCurrentLayer
Definition: qcustomplot.h:1867
virtual QList< QCPLayoutElement * > elements(bool recursive) const
virtual ~QCPAbstractItem()
QPainterPath mCustomPath
Definition: qcustomplot.h:309
WidthType mWidthType
Definition: qcustomplot.h:2937
virtual bool removeFromLegend() const
QCPBarsGroup * mBarsGroup
Definition: qcustomplot.h:2938
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
QMap< double, QCPBarData > QCPBarDataMap
Definition: qcustomplot.h:2868
QCPItemAnchor *const left
Definition: qcustomplot.h:3534
void removeData(double fromKey, double toKey)
double center() const
virtual ~QCPItemAnchor()
virtual void draw(QCPPainter *painter)
virtual void drawOutliers(QCPPainter *painter) const
double getStackedBaseValue(double key, bool positive) const
QFont mainFont() const
virtual void updateLayout()
void setTightBoundary(bool enabled)
bool mReplotting
Definition: qcustomplot.h:1875
bool operator==(const QCPColorGradient &other) const
QList< QCPAxis * > addAxes(QCPAxis::AxisTypes types)
QPen mSelectedTickPen
Definition: qcustomplot.h:1270
bool autoSubTicks() const
Definition: qcustomplot.h:1107
{ssCross.png} a cross
Definition: qcustomplot.h:253
void setWidthType(WidthType widthType)
PainterModes mModes
Definition: qcustomplot.h:361
virtual QPointF pixelPoint() const
QPen mainPen() const
A brace with round edges.
Definition: qcustomplot.h:3774
A layout that places child elements aligned to the border or arbitrarily positioned.
Definition: qcustomplot.h:828
void setData(QCPColorMapData *data, bool copy=false)
virtual void deselectEvent(bool *selectionStateChanged)
QPen getIconBorderPen() const
void setViewport(const QRect &rect)
0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts) ...
Definition: qcustomplot.h:165
void setVisible(bool on)
virtual void drawMedian(QCPPainter *painter) const
void setStyle(TracerStyle style)
void setNoAntialiasingOnDrag(bool enabled)
double scaleLogBase() const
Definition: qcustomplot.h:1100
Handles the different ending decorations for line-like items.
Definition: qcustomplot.h:877
A color scale for use with color coding data such as QCPColorMap.
Definition: qcustomplot.h:2436
QCPDataMap * data() const
Definition: qcustomplot.h:2582
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void removeFillBasePoints(QVector< QPointF > *lineData) const
virtual void update(UpdatePhase phase)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual QCP::Interaction selectionCategory() const
QPen pen() const
Definition: qcustomplot.h:3577
QPen mTickPen
Definition: qcustomplot.h:1270
QCP::PlottingHints mPlottingHints
Definition: qcustomplot.h:1868
BracketStyle mStyle
Definition: qcustomplot.h:3806
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:2517
QCPAxis * valueAxis() const
Definition: qcustomplot.h:1578
QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
Error bars for the key dimension of the data point are shown.
Definition: qcustomplot.h:2572
virtual QSize minimumSizeHint() const
int mCachedMargin
Definition: qcustomplot.h:1286
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual QCPItemPosition * toQCPItemPosition()
Definition: qcustomplot.h:1529
QPointer< QCPAxisRect > mClipAxisRect
Definition: qcustomplot.h:1654
void applyDefaultAntialiasingHint(QCPPainter *painter) const
QVector< QString > mTickVectorLabels
Definition: qcustomplot.h:1283
int getMarginValue(const QMargins &margins, QCP::MarginSide side)
Definition: qcustomplot.h:218
The positive sign domain, i.e. numbers greater than zero.
Definition: qcustomplot.h:1462
void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
void setPixelPoint(const QPointF &pixelPoint)
QList< QCPAbstractPlottable * > mPlottables
Definition: qcustomplot.h:1854
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:385
friend class QCPAxisRect
Definition: qcustomplot.h:1901
void applyTo(QCPPainter *painter, const QPen &defaultPen) const
QPen pen() const
Definition: qcustomplot.h:3783
QPen pen() const
Definition: qcustomplot.h:1418
QPainter subclass used internally.
Definition: qcustomplot.h:317
void append(QCPBars *bars)
Qt::TransformationMode transformationMode() const
Definition: qcustomplot.h:3639
void setTickLength(int inside, int outside=0)
QCPCurveDataMap * data() const
Definition: qcustomplot.h:2730
void setAntialiasedErrorBars(bool enabled)
ScaleType mScaleType
Definition: qcustomplot.h:1275
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
bool selectable() const
Definition: qcustomplot.h:2152
Q_SLOT void setSelected(bool selected)
QPixmap mLegendIcon
Definition: qcustomplot.h:3153
virtual QPointF anchorPixelPoint(int anchorId) const
QSet< QCPItemPosition * > mChildrenX
Definition: qcustomplot.h:1526
Qt::TimeSpec mDateTimeSpec
Definition: qcustomplot.h:1259
WidthType widthType() const
Definition: qcustomplot.h:2902
QCPItemEllipse(QCustomPlot *parentPlot)
{ssCrossCircle.png} a circle with a cross inside
Definition: qcustomplot.h:264
void setPlottingHints(const QCP::PlottingHints &hints)
bool mAutoTicks
Definition: qcustomplot.h:1268
void coordsToPixels(double key, double value, double &x, double &y) const
void setErrorBarSkipSymbol(bool enabled)
void drawBackground(QCPPainter *painter)
double data(double key, double value)
virtual void simplify()
int left() const
Definition: qcustomplot.h:2075
bool mCachedMarginValid
Definition: qcustomplot.h:1285
bool selected() const
Definition: qcustomplot.h:2366
double boundingDistance() const
QCPItemAnchor *const bottom
Definition: qcustomplot.h:3598
QPixmap mPixmap
Definition: qcustomplot.h:3665
QCPGrid(QCPAxis *parentAxis)
virtual bool take(QCPLayoutElement *element)
Q_SLOT void setSelected(bool selected)
void setColorInterpolation(ColorInterpolation interpolation)
QColor mSelectedColor
Definition: qcustomplot.h:3540
QPen mSelectedBasePen
Definition: qcustomplot.h:1244
QCPLineEnding head() const
Definition: qcustomplot.h:3342
double median() const
Definition: qcustomplot.h:2990
{ssDot.png} a single pixel (use ssDisc or ssCircle if you want a round shape with a certain radius) ...
Definition: qcustomplot.h:252
void normalize()
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lineData) const
0x0100 Error bars
Definition: qcustomplot.h:131
QPen mainPen() const
double pointDistance(const QPointF &pixelPoint) const
int mPadding
Definition: qcustomplot.h:1241
void setChartStyle(ChartStyle style)
void setBarsGroup(QCPBarsGroup *barsGroup)
virtual void mouseMoveEvent(QMouseEvent *event)
QCPAxis * yAxis2
Definition: qcustomplot.h:1824
void setDateTimeSpec(const Qt::TimeSpec &timeSpec)
QVector< QRgb > mColorBuffer
Definition: qcustomplot.h:2017
void registerBars(QCPBars *bars)
QCPAxisRect * axisRect() const
Definition: qcustomplot.h:1098
QCPAbstractItem * mParentItem
Definition: qcustomplot.h:1524
{ssPlusSquare.png} a square with a plus inside
Definition: qcustomplot.h:263
A filled diamond (45° rotated square)
Definition: qcustomplot.h:899
void setIconBorderPen(const QPen &pen)
void removeChildY(QCPItemPosition *pos)
Colors suitable to represent different elevations on geographical maps.
Definition: qcustomplot.h:1928
int selectionTolerance() const
Definition: qcustomplot.h:1733
void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
void setSelectedFont(const QFont &font)
virtual QCP::Interaction selectionCategory() const
QCPAxis * xAxis2
Definition: qcustomplot.h:1824
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QPen mSelectedSubTickPen
Definition: qcustomplot.h:1271
QBrush mSelectedBrush
Definition: qcustomplot.h:3608
void setNumberPrecision(int precision)
QRect labelSelectionBox() const
Definition: qcustomplot.h:1338
static const double minRange
Definition: qcustomplot.h:514
void removeDataBefore(double t)
QList< QCPAbstractItem * > items() const
void setSelectedPen(const QPen &pen)
QString mText
Definition: qcustomplot.h:2386
double keyErrorPlus
Definition: qcustomplot.h:2523
QCPLayer * currentLayer() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
QVector< double > mOutliers
Definition: qcustomplot.h:3023
Tick coordinate is regarded as normal number and will be displayed as such. (see setNumberFormat) ...
Definition: qcustomplot.h:1060
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
QPointer< QCPAxis > mRangeZoomVertAxis
Definition: qcustomplot.h:2101
void setKeyRange(const QCPRange &keyRange)
int layerCount() const
{ssTriangle.png} an equilateral triangle, standing on baseline
Definition: qcustomplot.h:260
{ssDisc.png} a circle which is filled with the pen&#39;s color (not the brush as with ssCircle) ...
Definition: qcustomplot.h:256
QCPGraph * graph() const
Definition: qcustomplot.h:3721
QCPAxis::AxisType type
Definition: qcustomplot.h:1341
bool isNone() const
Definition: qcustomplot.h:296
void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
void rescaleAxes(bool onlyEnlarge=false) const
0x0020 Main lines of plottables (excluding error bars, see element aeErrorBars)
Definition: qcustomplot.h:128
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
bool autoTickStep() const
Definition: qcustomplot.h:1106
friend class QCPLayer
Definition: qcustomplot.h:1900
LabelType tickLabelType() const
Definition: qcustomplot.h:1111
QCPRange mKeyRange
Definition: qcustomplot.h:3085
void setData(QCPBarDataMap *data, bool copy=false)
Interaction
Definition: qcustomplot.h:160
static void connectBars(QCPBars *lower, QCPBars *upper)
QCPBars * barBelow() const
Definition: qcustomplot.h:2905
bool rangeReversed() const
Definition: qcustomplot.h:1102
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setBrush(const QBrush &brush)
int top() const
Definition: qcustomplot.h:2077
QPen mSelectedPen
Definition: qcustomplot.h:3359
virtual void deselectEvent(bool *selectionStateChanged)
void setTickVectorLabels(const QVector< QString > &vec)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
A bar perpendicular to the line.
Definition: qcustomplot.h:900
Q_SLOT void setSelectedParts(const SelectableParts &selectedParts)
void dataRangeChanged(QCPRange newRange)
void setRangeDrag(bool enabled)
QBrush mBrushPositive
Definition: qcustomplot.h:3266
QPen pen() const
Definition: qcustomplot.h:3715
data points are connected by a straight line
Definition: qcustomplot.h:2561
Final phase in which the layout system places the rects of the elements.
Definition: qcustomplot.h:653
0x01 Axis is vertical and on the left side of the axis rect
Definition: qcustomplot.h:1047
void setLabelColor(const QColor &color)
void setAntialiasedFill(bool enabled)
QCP::AntialiasedElements mAntialiasedElements
Definition: qcustomplot.h:1858
bool inverted() const
Definition: qcustomplot.h:912
No error bars are shown.
Definition: qcustomplot.h:2571
void selectableChanged(bool selectable)
int getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
QLineF getRectClippedStraightLine(const QVector2D &point1, const QVector2D &vec, const QRect &rect) const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3652
QMap< double, QCPFinancialData > QCPFinancialDataMap
Definition: qcustomplot.h:3190
void setHead(const QCPLineEnding &head)
void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
QCPItemAnchor *const bottom
Definition: qcustomplot.h:3450
bool mAutoAddPlottableToLegend
Definition: qcustomplot.h:1853
void setAntialiased(bool enabled)
void setSelectedBrush(const QBrush &brush)
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:429
virtual QPointF anchorPixelPoint(int anchorId) const
VESPUCCI_EXPORT arma::uword min(arma::uword a, arma::uword b)
Vespucci::Math::min.
Definition: accessory.cpp:249
void scaleRange(double factor, double center)
QPen pen() const
Definition: qcustomplot.h:953
void getCurveData(QVector< QPointF > *lineData) const
void setData(QCPCurveDataMap *data, bool copy=false)
bool mErrorBarSkipSymbol
Definition: qcustomplot.h:2638
double baseLog(double value) const
QCPItemPixmap(QCustomPlot *parentPlot)
QCPLayout * layout() const
Definition: qcustomplot.h:661
QCPAxis * rangeDragAxis(Qt::Orientation orientation)
bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0, const QString &pdfCreator=QString(), const QString &pdfTitle=QString())
QCPAxisRect * axisRect(int index=0) const
LabelType mTickLabelType
Definition: qcustomplot.h:1255
0x0000 No elements
Definition: qcustomplot.h:135
QCPColorGradient(GradientPreset preset=gpCold)
void rescaleValueAxis(bool onlyEnlarge=false) const
void setRowStretchFactor(int row, double factor)
cmplx FADDEEVA() w(cmplx z, double relerr)
Definition: Faddeeva.cpp:680
bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1)
double key
Definition: qcustomplot.h:2857
QCPAbstractPlottable * plottableAt(const QPointF &pos, bool onlySelectable=false) const
void setBarWidth(int width)
QList< double > mColumnStretchFactors
Definition: qcustomplot.h:815
bool mMapImageInvalidated
Definition: qcustomplot.h:3154
BracketStyle style() const
Definition: qcustomplot.h:3786
QList< QCPGraph * > selectedGraphs() const
QCPItemAnchor *const top
Definition: qcustomplot.h:3528
bool removeItem(QCPAbstractItem *item)
void drawBackground(QCPPainter *painter)
A layout element displaying a plot title text.
Definition: qcustomplot.h:2343
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual bool take(QCPLayoutElement *element)
Bar width is given by a fraction of the axis rect size.
Definition: qcustomplot.h:2892
0x02 right margin
Definition: qcustomplot.h:106
void setInterpolate(bool enabled)
SelectableParts selectedParts() const
Definition: qcustomplot.h:1137
void setPen(const QPen &pen)
Q_SLOT void deselectAll()
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
QPointer< QCPColorScale > mColorScale
Definition: qcustomplot.h:3150
bool selected() const
Definition: qcustomplot.h:1629
QCPLineEnding mTail
Definition: qcustomplot.h:3360
void setTickLabelSide(LabelSide side)
QCPLayoutInset * insetLayout() const
Definition: qcustomplot.h:2067
QCPRange mDataRange
Definition: qcustomplot.h:2489
QPointer< QCPAxis > mRangeDragVertAxis
Definition: qcustomplot.h:2101
The QCustomPlot surface is immediately refreshed, by calling QWidget::repaint() after the replot...
Definition: qcustomplot.h:1715
virtual ~QCPItemBracket()
void removeChild(QCP::MarginSide side, QCPLayoutElement *element)
void setBrush(const QBrush &brush)
virtual QCPLayoutElement * takeAt(int index)
QFont mSelectedFont
Definition: qcustomplot.h:2389
void setSelectionTolerance(int pixels)
void setSelectedPen(const QPen &pen)
void setDataKeyError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError)
QPointer< QCPAxis > mRangeDragHorzAxis
Definition: qcustomplot.h:2101
void setPen(const QPen &pen)
static AxisType opposite(AxisType type)
QString name() const
Definition: qcustomplot.h:1414
int mNumberPrecision
Definition: qcustomplot.h:1260
virtual void draw(QCPPainter *painter)
double pixelToCoord(double value) const
QMap< double, QColor > mColorStops
Definition: qcustomplot.h:2012
{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
Definition: qcustomplot.h:259
QStack< bool > mAntialiasingStack
Definition: qcustomplot.h:365
void addData(const QCPBarDataMap &dataMap)
void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
QPen mainPen() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QSize iconSize() const
Definition: qcustomplot.h:2259
void clearItems()
Phase used for any type of preparation that needs to be done before margin calculation and layout...
Definition: qcustomplot.h:651
virtual void draw(QCPPainter *painter)
void setTickStep(double step)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove)
void selectableChanged(bool selectable)
void setTickLabelType(LabelType type)
double mRangeZoomFactorHorz
Definition: qcustomplot.h:2102
Q_SLOT void setDataRange(const QCPRange &dataRange)
virtual int calculateAutoMargin(QCP::MarginSide side)
void setSelectedTextColor(const QColor &color)
double mScaleLogBaseLogInv
Definition: qcustomplot.h:1276
void setTickLengthOut(int outside)
double rangeZoomFactor(Qt::Orientation orientation)
QCPItemPosition *const point1
Definition: qcustomplot.h:3309
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
double tickStep() const
Definition: qcustomplot.h:1120
QPen getBorderPen() const
void removeDataAfter(double key)
QList< QCPColorMap * > colorMaps() const
bool hasItem(QCPAbstractLegendItem *item) const
virtual void legendRemoved(QCPLegend *legend)
bool mSubGridVisible
Definition: qcustomplot.h:967
virtual void deselectEvent(bool *selectionStateChanged)
void unregisterBars(QCPBars *bars)
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const =0
virtual void drawScatterPlot(QCPPainter *painter, QVector< QCPData > *scatterData) const
void setSelectedTickPen(const QPen &pen)
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:3145
void setSelectedFont(const QFont &font)
virtual void simplify()
void moveRange(double diff)
void setLabelFont(const QFont &font)
A bar perpendicular to the line, pointing out to only one side (to which side can be changed with set...
Definition: qcustomplot.h:901
virtual void mousePressEvent(QMouseEvent *event)
0x002 Legend items individually (see selectedItems)
Definition: qcustomplot.h:2246
virtual void mouseDoubleClickEvent(QMouseEvent *event)
QPixmap pixmap() const
Definition: qcustomplot.h:284
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:2490
QList< QCPLegend * > selectedLegends() const
double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
void restore()
QCPAbstractItem * item() const
virtual void updateLayout()
ErrorType mErrorType
Definition: qcustomplot.h:2636
void addChildY(QCPItemPosition *pos)
{ssPlusCircle.png} a circle with a plus inside
Definition: qcustomplot.h:265
double keyErrorMinus
Definition: qcustomplot.h:2523
QPen pen() const
Definition: qcustomplot.h:3640
bool mBackgroundScaled
Definition: qcustomplot.h:1865
QRect rect() const
Definition: qcustomplot.h:662
void setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void setRotation(double degrees)
void setSubTickPen(const QPen &pen)
{ssCrossSquare.png} a square with a cross inside
Definition: qcustomplot.h:262
QHash< QCPAxis::AxisType, QList< QCPAxis * > > mAxes
Definition: qcustomplot.h:2108
QString name() const
Definition: qcustomplot.h:1515
QCPItemAnchor *const right
Definition: qcustomplot.h:3530
void setLength(double length)
Data points are connected with a straight line.
Definition: qcustomplot.h:2724
void removeDataBefore(double key)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const =0
QBrush mSelectedBrush
Definition: qcustomplot.h:3459
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
Definition: qcustomplot.h:1561
void addFillBasePoints(QVector< QPointF > *lineData) const
void setNumberFormat(const QString &formatCode)
SelectableParts mSelectableParts
Definition: qcustomplot.h:1243
bool visible() const
Definition: qcustomplot.h:389
void rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
QCPItemPosition *const startDir
Definition: qcustomplot.h:3400
virtual void draw(QCPPainter *painter)
ErrorType errorType() const
Definition: qcustomplot.h:2585
The axis label.
Definition: qcustomplot.h:1088
QCPRange sanitizedForLinScale() const
void setLineStyle(LineStyle style)
QFont mainFont() const
virtual QRect clipRect() const
bool mAntialiased
Definition: qcustomplot.h:455
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
QRect mTextBoundingRect
Definition: qcustomplot.h:2391
Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle ...
Definition: qcustomplot.h:1931
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
QCPLayoutElement * element(int row, int column) const
QPen pen() const
Definition: qcustomplot.h:282
QCPAxisPainterPrivate(QCustomPlot *parentPlot)
Qt::Alignment insetAlignment(int index) const
void getPlotData(QVector< QPointF > *lineData, QVector< QCPData > *scatterData) const
virtual QPointF pixelPoint() const
double basePow(double value) const
SelectablePart getPartAt(const QPointF &pos) const
QPen mainPen() const
virtual void draw(QCPPainter *painter)
void setTail(const QCPLineEnding &tail)
Holds the two-dimensional data of a QCPColorMap plottable.
Definition: qcustomplot.h:3047
bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
virtual void update(UpdatePhase phase)
QBrush mainBrush() const
void setSelectedFont(const QFont &font)
QList< QCPAbstractItem * > mItems
Definition: qcustomplot.h:1856
int autoTickCount() const
Definition: qcustomplot.h:1104
virtual ~QCPLayoutGrid()
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom, QCPAxisRect::setRangeZoomAxes)
Definition: qcustomplot.h:161
QCPAbstractLegendItem * item(int index) const
QCPItemAnchor *const top
Definition: qcustomplot.h:3594
virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
QString numberFormat() const
QCPDataMap * mData
Definition: qcustomplot.h:2632
void setTickLengthIn(int inside)
double mRangeZoomFactorVert
Definition: qcustomplot.h:2102
void setSelectedColor(const QColor &color)
double size() const
Definition: qcustomplot.h:3719
void setSelectedPen(const QPen &pen)
void setErrorType(ErrorType errorType)
void setRangeZoomFactor(double horizontalFactor, double verticalFactor)
void setSelectedBrush(const QBrush &brush)
Q_SLOT void setScaleType(QCPAxis::ScaleType type)
void setClipToAxisRect(bool clip)
TracerStyle style() const
Definition: qcustomplot.h:3720
void plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
void setColumnStretchFactors(const QList< double > &factors)
a custom pixmap specified by setPixmap, centered on the data point coordinates
Definition: qcustomplot.h:267
void mouseWheel(QWheelEvent *event)
bool addItem(QCPAbstractLegendItem *item)
0x0010 Legend items
Definition: qcustomplot.h:127
Continuous lightness from black over weak blueish colors to white (suited for non-biased data represe...
Definition: qcustomplot.h:1926
void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
Definition: qcustomplot.h:198
void beforeReplot()
void setPen(const QPen &pen)
QFont getTickLabelFont() const
QCPAxisPainterPrivate * mAxisPainter
Definition: qcustomplot.h:1280
virtual void updateLayout()
void setMode(PainterMode mode, bool enabled=true)
The abstract base class for layouts.
Definition: qcustomplot.h:724
void setSubGridVisible(bool visible)
int rowCount() const
void setDataValueError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &valueError)
QPixmap mPaintBuffer
Definition: qcustomplot.h:1872
Holds the data of one single data point for QCPFinancial.
Definition: qcustomplot.h:3174
void setTickLabelPadding(int padding)
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > *pointData) const
QBrush mBackgroundBrush
Definition: qcustomplot.h:1862
virtual void resizeEvent(QResizeEvent *event)
double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
void setSelectedLabelColor(const QColor &color)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3653
None of the selectable parts.
Definition: qcustomplot.h:1085
void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
QCPLayoutElement * layoutElementAt(const QPointF &pos) const
void setInteractions(const QCP::Interactions &interactions)
void setBrush(const QBrush &brush)
void selectionChanged(bool selected)
0x04 Axis is horizontal and on the top side of the axis rect
Definition: qcustomplot.h:1049
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
bool realVisibility() const
QCPItemAnchor *const left
Definition: qcustomplot.h:3452
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:2206
QCPAbstractPlottable * plottable(int index)
ColorInterpolation mColorInterpolation
Definition: qcustomplot.h:2013
QCPPlottableLegendItem * itemWithPlottable(const QCPAbstractPlottable *plottable) const
QSize size() const
Definition: qcustomplot.h:2081
QCPItemAnchor *const bottomLeft
Definition: qcustomplot.h:3451
QCPAxis * axis(QCPAxis::AxisType type, int index=0) const
{ssTriangleInverted.png} an equilateral triangle, standing on corner
Definition: qcustomplot.h:261
virtual void draw(QCPPainter *painter)
void setLength(double length)
void setKey(double key)
A plottable representing a two-dimensional color map in a plot.
Definition: qcustomplot.h:3096
Layer is inserted above other layer.
Definition: qcustomplot.h:1706
void rescale(bool onlyVisiblePlottables=false)
bool mAdaptiveSampling
Definition: qcustomplot.h:2640
QCPBarDataMap * mData
Definition: qcustomplot.h:2935
QHash< QCP::MarginSide, QList< QCPLayoutElement * > > mChildren
Definition: qcustomplot.h:620
void setPlottingHint(QCP::PlottingHint hint, bool enabled=true)
QCPAxis * valueAxis() const
Definition: qcustomplot.h:1423
QColor mTickLabelColor
Definition: qcustomplot.h:1257
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:1598
Both sign domains, including zero, i.e. all (rational) numbers.
Definition: qcustomplot.h:1461
InsetPlacement insetPlacement(int index) const
QString dateTimeFormat() const
Definition: qcustomplot.h:1116
QRect viewport() const
Definition: qcustomplot.h:1724
void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation)
void setColumnStretchFactor(int column, double factor)
void setSelectedPen(const QPen &pen)
bool mVisible
Definition: qcustomplot.h:400
bool setCurrentLayer(const QString &name)
QCPItemPosition *const position
Definition: qcustomplot.h:3526
void setAxisRect(QCPAxisRect *axisRect)
int tickLabelPadding() const
void addChildX(QCPItemPosition *pos)
QCPAxis::LabelSide tickLabelSide
Definition: qcustomplot.h:1350
void getStepRightPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QPointer< QCPLayerable > mParentLayerable
Definition: qcustomplot.h:453
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
int subTickLengthOut() const
QList< QCPAbstractPlottable * > plottables() const
QCPItemAnchor *const topLeft
Definition: qcustomplot.h:3527
QPen mZeroLinePen
Definition: qcustomplot.h:969
QPen mainPen() const
Queues the refresh such that it is performed at a slightly delayed point in time after the replot...
Definition: qcustomplot.h:1716
QPoint mMousePressPos
Definition: qcustomplot.h:1873
Color channels red, green and blue are linearly interpolated.
Definition: qcustomplot.h:1914
bool ticks() const
Definition: qcustomplot.h:1108
virtual void draw(QCPPainter *painter)
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lineData) const
QCPItemAnchor *const right
Definition: qcustomplot.h:3596
Tick labels will be displayed inside the axis rect and clipped to the inner axis rect.
Definition: qcustomplot.h:1069
ChartStyle mChartStyle
Definition: qcustomplot.h:3263
void setSelectedFont(const QFont &font)
QList< double > mRowStretchFactors
Definition: qcustomplot.h:816
void setInsetAlignment(int index, Qt::Alignment alignment)
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
QRect mViewport
Definition: qcustomplot.h:1851
void setPen(const QPen &pen)
virtual void clearData()
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:2096
bool selectable() const
Definition: qcustomplot.h:2365
friend class QCPLegend
Definition: qcustomplot.h:1898
QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
bool removeAxis(QCPAxis *axis)
double key() const
Definition: qcustomplot.h:1574
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Qt::TimeSpec dateTimeSpec() const
Definition: qcustomplot.h:1117
void setRangeZoom(bool enabled)
QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId=-1)
void setAutoAddPlottableToLegend(bool on)
QPointer< QCPAxisRect > mAxisRect
Definition: qcustomplot.h:1599
void setClipAxisRect(QCPAxisRect *rect)
Qt::TransformationMode mTransformationMode
Definition: qcustomplot.h:3669
double baseValue() const
Definition: qcustomplot.h:2904
virtual int calculateMargin()
void setText(const QString &text)
QPen pen() const
Definition: qcustomplot.h:3299
QFont mTickLabelFont
Definition: qcustomplot.h:1256
void setPen(const QPen &pen)
QVector< double > tickPositions
Definition: qcustomplot.h:1363
QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true)
void selectionChanged(bool selected)
QPen pen() const
Definition: qcustomplot.h:3431
void setSelectedTextColor(const QColor &color)
virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
void removeDataAfter(double key)
void expand(const QCPRange &otherRange)
QCPAbstractItem * itemAt(const QPointF &pos, bool onlySelectable=false) const
QCPRange dataBounds() const
Definition: qcustomplot.h:3060
QCPLayerable * layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const
int subTickCount() const
Definition: qcustomplot.h:1125
double mTickStep
Definition: qcustomplot.h:1266
A non-filled arrow head with open back.
Definition: qcustomplot.h:896
void setSelectedPen(const QPen &pen)
void setBorderPen(const QPen &pen)
SelectableParts selectableParts() const
Definition: qcustomplot.h:1138
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
double key() const
Definition: qcustomplot.h:2987
void setZeroLinePen(const QPen &pen)
QCPItemPosition *const point2
Definition: qcustomplot.h:3310
bool hasAnchor(const QString &name) const
QString text() const
Definition: qcustomplot.h:2360
Bar spacing is in absolute pixels.
Definition: qcustomplot.h:2804
QMargins mPadding
Definition: qcustomplot.h:3548
QCPRange mDataBounds
Definition: qcustomplot.h:3089
double length() const
Definition: qcustomplot.h:911
void gradientChanged(QCPColorGradient newGradient)
void selectableChanged(const QCPAxis::SelectableParts &parts)
virtual void draw(QCPPainter *painter)
void setIconSize(const QSize &size)
virtual void mouseReleaseEvent(QMouseEvent *event)
void setBaseValue(double baseValue)
QCPItemAnchor * mParentAnchorY
Definition: qcustomplot.h:1601
virtual QSize maximumSizeHint() const
0x0400 Zero-lines, see QCPGrid::setZeroLinePen
Definition: qcustomplot.h:133
virtual QRect clipRect() const
void setData(QCPDataMap *data, bool copy=false)
bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove)
QString mText
Definition: qcustomplot.h:3544
void setSelectedPen(const QPen &pen)
double coordToPixel(double value) const
void applyErrorBarsAntialiasingHint(QCPPainter *painter) const
void setScatterStyle(const QCPScatterStyle &style)
QList< QCPAxis * > axes(QCPAxis::AxisTypes types) const
QBrush getBrush() const
virtual ~QCPItemPosition()
Manages the position of an item.
Definition: qcustomplot.h:1545
void adoptElement(QCPLayoutElement *el)
void setValueRange(const QCPRange &valueRange)
bool hasItem(QCPAbstractItem *item) const
QPen iconBorderPen() const
Definition: qcustomplot.h:2261
QCPItemPosition *const end
Definition: qcustomplot.h:3355
A curly brace with varying stroke width giving a calligraphic impression.
Definition: qcustomplot.h:3776
QList< QCPAbstractItem * > items() const
void setSize(double size)
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:2095
void setTicks(bool show)
QFont mSelectedFont
Definition: qcustomplot.h:3543
void setGraphKey(double key)
Defines a color gradient for use with e.g. QCPColorMap.
Definition: qcustomplot.h:1905
QCPMarginGroup(QCustomPlot *parentPlot)
void setErrorPen(const QPen &pen)
virtual QSize sizeHint() const
void setTypeY(PositionType type)
QCPGrid * grid() const
Definition: qcustomplot.h:1148
double upper
Definition: qcustomplot.h:484
int tickLengthIn() const
QCPMarginGroup * marginGroup(QCP::MarginSide side) const
Definition: qcustomplot.h:669
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:1864
QCPAbstractItem(QCustomPlot *parentPlot)
void setSubTickLength(int inside, int outside=0)
void setBrushPositive(const QBrush &brush)
bool selectable() const
Definition: qcustomplot.h:1424
QMap< double, QColor > colorStops() const
Definition: qcustomplot.h:1989
QMargins mMinimumMargins
Definition: qcustomplot.h:697
A bar that is skewed (skew controllable via setLength)
Definition: qcustomplot.h:902
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
Represents the range an axis is encompassing.
Definition: qcustomplot.h:481
QColor mLabelColor
Definition: qcustomplot.h:1250
QCPRange dataRange() const
Definition: qcustomplot.h:3113
bool tickLabels() const
Definition: qcustomplot.h:1109
QCPLayoutGrid * mPlotLayout
Definition: qcustomplot.h:1852
void setAntialiasedZeroLine(bool enabled)
virtual void draw(QCPPainter *painter)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
virtual QPointF anchorPixelPoint(int anchorId) const
void setPositionAlignment(Qt::Alignment alignment)
void setData(QCPFinancialDataMap *data, bool copy=false)
QList< QCPItemAnchor * > mAnchors
Definition: qcustomplot.h:1656
QCPRange mDragStartHorzRange
Definition: qcustomplot.h:2104
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:2635
virtual void clearData()
Groups multiple QCPBars together so they appear side by side.
Definition: qcustomplot.h:2790
virtual void deselectEvent(bool *selectionStateChanged)
0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)
Definition: qcustomplot.h:164
void initializeParentPlot(QCustomPlot *parentPlot)
double lower
Definition: qcustomplot.h:484
void setSelectedBrush(const QBrush &brush)
int graphCount() const
void setSelectedTickLabelColor(const QColor &color)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QFont font() const
Definition: qcustomplot.h:2148
virtual void generateAutoTicks()
bool removePlottable(QCPAbstractPlottable *plottable)
void setTail(const QCPLineEnding &tail)
bool isEmpty() const
void setTickLabelColor(const QColor &color)
void setSize(double size)
void setBrush(const QBrush &brush)
ScaleType scaleType() const
Definition: qcustomplot.h:1099
ScatterShape mShape
Definition: qcustomplot.h:305
Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and pha...
Definition: qcustomplot.h:1934
void pixelsToCoords(double x, double y, double &key, double &value) const
void setSelectedPen(const QPen &pen)
QPen mainPen() const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3591
bool removeItem(int index)
SelectableParts selectableParts() const
Definition: qcustomplot.h:2262
0xFF all margins
Definition: qcustomplot.h:109
QPixmap pixmap() const
Definition: qcustomplot.h:3636
virtual void draw(QCPPainter *painter)
void ticksRequest()
virtual void deselectEvent(bool *selectionStateChanged)
bool mTightBoundary
Definition: qcustomplot.h:3149
virtual void draw(QCPPainter *painter)
virtual QCPLayoutElement * elementAt(int index) const
friend class QCPPlottableLegendItem
Definition: qcustomplot.h:1504
QCPFinancialDataMap * data() const
Definition: qcustomplot.h:3222
bool mRangeReversed
Definition: qcustomplot.h:1274
virtual void setupTickVectors()
PositionType mPositionTypeX
Definition: qcustomplot.h:1597
void addData(const QCPDataMap &dataMap)
Qt::Alignment mPositionAlignment
Definition: qcustomplot.h:3545
QList< QCPAxisRect * > axisRects() const
Holds multiple axes and arranges them in a rectangular shape.
Definition: qcustomplot.h:2022
QCPAxis * yAxis
Definition: qcustomplot.h:1824
void setPen(const QPen &pen)
QBrush brush() const
Definition: qcustomplot.h:3579
int width() const
Definition: qcustomplot.h:2079
QBrush mainBrush() const
virtual ~QCPLayoutInset()
virtual void paintEvent(QPaintEvent *event)
{ssDiamond.png} a diamond
Definition: qcustomplot.h:258
QCPAxisRect * clipAxisRect() const
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:2105
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QCPColorMapData & operator=(const QCPColorMapData &other)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
void toPainter(QCPPainter *painter, int width=0, int height=0)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QPixmap toPixmap(int width=0, int height=0, double scale=1.0)
QCPRange mRange
Definition: qcustomplot.h:1273
double mBaseValue
Definition: qcustomplot.h:2939
QPen getTickPen() const
virtual QSize minimumSizeHint() const
QBrush brush() const
Definition: qcustomplot.h:3433
Qt::KeyboardModifier mMultiSelectModifier
Definition: qcustomplot.h:1869
QCPLegend * legend
Definition: qcustomplot.h:1825
void setPenNegative(const QPen &pen)
void setLabel(const QString &str)
QPointer< QCPLayoutElement > mMouseEventElement
Definition: qcustomplot.h:1874
virtual void draw(QCPPainter *painter)
QCP::AntialiasedElements notAntialiasedElements() const
Definition: qcustomplot.h:1730
QCPItemAnchor *const top
Definition: qcustomplot.h:3447
void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
AxisType mAxisType
Definition: qcustomplot.h:1238
QMap< double, QCPCurveData > QCPCurveDataMap
Definition: qcustomplot.h:2705
Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18))
Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
int mSubTickCount
Definition: qcustomplot.h:1267
bool mAntialiasedSubGrid
Definition: qcustomplot.h:968
virtual void clearData()
void sizeConstraintsChanged() const
Q_SLOT void setRange(const QCPRange &range)
LineStyle mLineStyle
Definition: qcustomplot.h:2634
QList< QCPAxis * > selectedAxes() const
A filled arrow head with an indented back.
Definition: qcustomplot.h:895
line is drawn as steps where the step height is the value of the right data point ...
Definition: qcustomplot.h:2563
virtual void deselectEvent(bool *selectionStateChanged)
QCP::Interactions mInteractions
Definition: qcustomplot.h:1859
QCPGraph * mGraph
Definition: qcustomplot.h:3750
QCPItemAnchor *const topRight
Definition: qcustomplot.h:3448
MarginSide
Definition: qcustomplot.h:105
void gradientChanged(QCPColorGradient newGradient)
Qt::Alignment mTextAlignment
Definition: qcustomplot.h:3546
void setLabel(const QString &str)
QPen mPen
Definition: qcustomplot.h:969
QLatin1Char mNumberFormatChar
Definition: qcustomplot.h:1261
bool mBackgroundScaled
Definition: qcustomplot.h:2097
int axisCount(QCPAxis::AxisType type) const
QBrush brush() const
Definition: qcustomplot.h:1420
AntialiasedElement
Definition: qcustomplot.h:123
QCPItemPosition *const left
Definition: qcustomplot.h:3797
bool mColorBufferInvalidated
Definition: qcustomplot.h:2018
int axisRectCount() const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:1378
QPointer< QCPGraph > mChannelFillGraph
Definition: qcustomplot.h:2639
virtual void draw(QCPPainter *painter)
AxisType axisType() const
Definition: qcustomplot.h:1097
void setChannelFillGraph(QCPGraph *targetGraph)
void setWidth(double width)
double keyPixelOffset(const QCPBars *bars, double keyCoord)
void setFont(const QFont &font)
double mWidth
Definition: qcustomplot.h:2936
No ending decoration.
Definition: qcustomplot.h:893
bool hasElement(int row, int column)
virtual QList< QCPLayoutElement * > elements(bool recursive) const
QPen mSelectedPen
Definition: qcustomplot.h:3541
QCustomPlot * mParentPlot
Definition: qcustomplot.h:452
ScatterShape shape() const
Definition: qcustomplot.h:281
void setSelectedBrush(const QBrush &brush)
void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
void setPen(const QPen &pen)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3446
double value
Definition: qcustomplot.h:2857
Error bars for the value dimension of the data point are shown.
Definition: qcustomplot.h:2573
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:1866
virtual void wheelEvent(QWheelEvent *event)
QBrush mainBrush() const
Q_SLOT void setSelected(bool selected)
QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0)
virtual void draw(QCPPainter *painter)
bool selected() const
Definition: qcustomplot.h:2153
virtual ~QCPFinancial()
int right() const
Definition: qcustomplot.h:2076
double pointDistance(const QPointF &pixelPoint) const
virtual void draw(QCPPainter *painter)
void afterReplot()
QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
Holds the data of one single data point (one bar) for QCPBars.
Definition: qcustomplot.h:2852
QColor mSelectedLabelColor
Definition: qcustomplot.h:1250
void setFont(const QFont &font)
void setColorStopAt(double position, const QColor &color)
bool mTicks
Definition: qcustomplot.h:1265
void setLabelPadding(int padding)