Biomedical Image Analysis Library
The Biomedical Image Analysis Library is a poweful tool for developers, physicians, researchers, engineers, and so on.
qcustomplot.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 ** **
3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
4 ** Copyright (C) 2011-2015 Emanuel Eichhammer **
5 ** **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
8 ** the Free Software Foundation, either version 3 of the License, or **
9 ** (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, **
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program. If not, see http://www.gnu.org/licenses/. **
18 ** **
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Date: 25.04.15 **
23 ** Version: 1.3.1 **
24 ****************************************************************************/
25 
26 #include "qcustomplot.h"
27 
28 
29 
33 
51  QPainter(),
52  mModes(pmDefault),
53  mIsAntialiasing(false)
54 {
55  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
56  // a call to begin() will follow
57 }
58 
65 QCPPainter::QCPPainter(QPaintDevice *device) :
66  QPainter(device),
68  mIsAntialiasing(false)
69 {
70 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
71  if (isActive())
72  setRenderHint(QPainter::NonCosmeticDefaultPen);
73 #endif
74 }
75 
77 {
78 }
79 
86 void QCPPainter::setPen(const QPen &pen)
87 {
88  QPainter::setPen(pen);
89  if (mModes.testFlag(pmNonCosmetic))
91 }
92 
100 void QCPPainter::setPen(const QColor &color)
101 {
102  QPainter::setPen(color);
103  if (mModes.testFlag(pmNonCosmetic))
104  makeNonCosmetic();
105 }
106 
114 void QCPPainter::setPen(Qt::PenStyle penStyle)
115 {
116  QPainter::setPen(penStyle);
117  if (mModes.testFlag(pmNonCosmetic))
118  makeNonCosmetic();
119 }
120 
129 void QCPPainter::drawLine(const QLineF &line)
130 {
131  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
132  QPainter::drawLine(line);
133  else
134  QPainter::drawLine(line.toLine());
135 }
136 
143 void QCPPainter::setAntialiasing(bool enabled)
144 {
145  setRenderHint(QPainter::Antialiasing, enabled);
146  if (mIsAntialiasing != enabled)
147  {
148  mIsAntialiasing = enabled;
149  if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
150  {
151  if (mIsAntialiasing)
152  translate(0.5, 0.5);
153  else
154  translate(-0.5, -0.5);
155  }
156  }
157 }
158 
163 void QCPPainter::setModes(QCPPainter::PainterModes modes)
164 {
165  mModes = modes;
166 }
167 
179 bool QCPPainter::begin(QPaintDevice *device)
180 {
181  bool result = QPainter::begin(device);
182 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
183  if (result)
184  setRenderHint(QPainter::NonCosmeticDefaultPen);
185 #endif
186  return result;
187 }
188 
195 {
196  if (!enabled && mModes.testFlag(mode))
197  mModes &= ~mode;
198  else if (enabled && !mModes.testFlag(mode))
199  mModes |= mode;
200 }
201 
211 {
213  QPainter::save();
214 }
215 
225 {
226  if (!mAntialiasingStack.isEmpty())
228  else
229  qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
230  QPainter::restore();
231 }
232 
238 {
239  if (qFuzzyIsNull(pen().widthF()))
240  {
241  QPen p = pen();
242  p.setWidth(1);
243  QPainter::setPen(p);
244  }
245 }
246 
247 
251 
302 /* start documentation of inline functions */
303 
323 /* end documentation of inline functions */
324 
332  mSize(6),
333  mShape(ssNone),
334  mPen(Qt::NoPen),
335  mBrush(Qt::NoBrush),
336  mPenDefined(false)
337 {
338 }
339 
348  mSize(size),
349  mShape(shape),
350  mPen(Qt::NoPen),
351  mBrush(Qt::NoBrush),
352  mPenDefined(false)
353 {
354 }
355 
361  mSize(size),
362  mShape(shape),
363  mPen(QPen(color)),
364  mBrush(Qt::NoBrush),
365  mPenDefined(true)
366 {
367 }
368 
373 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
374  mSize(size),
375  mShape(shape),
376  mPen(QPen(color)),
377  mBrush(QBrush(fill)),
378  mPenDefined(true)
379 {
380 }
381 
397 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
398  mSize(size),
399  mShape(shape),
400  mPen(pen),
401  mBrush(brush),
402  mPenDefined(pen.style() != Qt::NoPen)
403 {
404 }
405 
411  mSize(5),
412  mShape(ssPixmap),
413  mPen(Qt::NoPen),
414  mBrush(Qt::NoBrush),
415  mPixmap(pixmap),
416  mPenDefined(false)
417 {
418 }
419 
429 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
430  mSize(size),
431  mShape(ssCustom),
432  mPen(pen),
433  mBrush(brush),
434  mCustomPath(customPath),
435  mPenDefined(pen.style() != Qt::NoPen)
436 {
437 }
438 
445 {
446  mSize = size;
447 }
448 
458 {
459  mShape = shape;
460 }
461 
470 void QCPScatterStyle::setPen(const QPen &pen)
471 {
472  mPenDefined = true;
473  mPen = pen;
474 }
475 
482 void QCPScatterStyle::setBrush(const QBrush &brush)
483 {
484  mBrush = brush;
485 }
486 
494 void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
495 {
497  mPixmap = pixmap;
498 }
499 
506 {
509 }
510 
520 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
521 {
522  painter->setPen(mPenDefined ? mPen : defaultPen);
523  painter->setBrush(mBrush);
524 }
525 
534 void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
535 {
536  drawShape(painter, pos.x(), pos.y());
537 }
538 
542 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
543 {
544  double w = mSize/2.0;
545  switch (mShape)
546  {
547  case ssNone: break;
548  case ssDot:
549  {
550  painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
551  break;
552  }
553  case ssCross:
554  {
555  painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
556  painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
557  break;
558  }
559  case ssPlus:
560  {
561  painter->drawLine(QLineF(x-w, y, x+w, y));
562  painter->drawLine(QLineF( x, y+w, x, y-w));
563  break;
564  }
565  case ssCircle:
566  {
567  painter->drawEllipse(QPointF(x , y), w, w);
568  break;
569  }
570  case ssDisc:
571  {
572  QBrush b = painter->brush();
573  painter->setBrush(painter->pen().color());
574  painter->drawEllipse(QPointF(x , y), w, w);
575  painter->setBrush(b);
576  break;
577  }
578  case ssSquare:
579  {
580  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
581  break;
582  }
583  case ssDiamond:
584  {
585  painter->drawLine(QLineF(x-w, y, x, y-w));
586  painter->drawLine(QLineF( x, y-w, x+w, y));
587  painter->drawLine(QLineF(x+w, y, x, y+w));
588  painter->drawLine(QLineF( x, y+w, x-w, y));
589  break;
590  }
591  case ssStar:
592  {
593  painter->drawLine(QLineF(x-w, y, x+w, y));
594  painter->drawLine(QLineF( x, y+w, x, y-w));
595  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
596  painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
597  break;
598  }
599  case ssTriangle:
600  {
601  painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
602  painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
603  painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w));
604  break;
605  }
606  case ssTriangleInverted:
607  {
608  painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
609  painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
610  painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w));
611  break;
612  }
613  case ssCrossSquare:
614  {
615  painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
616  painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
617  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
618  break;
619  }
620  case ssPlusSquare:
621  {
622  painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
623  painter->drawLine(QLineF( x, y+w, x, y-w));
624  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
625  break;
626  }
627  case ssCrossCircle:
628  {
629  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
630  painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
631  painter->drawEllipse(QPointF(x, y), w, w);
632  break;
633  }
634  case ssPlusCircle:
635  {
636  painter->drawLine(QLineF(x-w, y, x+w, y));
637  painter->drawLine(QLineF( x, y+w, x, y-w));
638  painter->drawEllipse(QPointF(x, y), w, w);
639  break;
640  }
641  case ssPeace:
642  {
643  painter->drawLine(QLineF(x, y-w, x, y+w));
644  painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
645  painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
646  painter->drawEllipse(QPointF(x, y), w, w);
647  break;
648  }
649  case ssPixmap:
650  {
651  painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
652  break;
653  }
654  case ssCustom:
655  {
656  QTransform oldTransform = painter->transform();
657  painter->translate(x, y);
658  painter->scale(mSize/6.0, mSize/6.0);
659  painter->drawPath(mCustomPath);
660  painter->setTransform(oldTransform);
661  break;
662  }
663  }
664 }
665 
666 
670 
713 /* start documentation of inline functions */
714 
729 /* end documentation of inline functions */
730 
739 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
740  QObject(parentPlot),
741  mParentPlot(parentPlot),
742  mName(layerName),
743  mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
744  mVisible(true)
745 {
746  // Note: no need to make sure layerName is unique, because layer
747  // management is done with QCustomPlot functions.
748 }
749 
751 {
752  // If child layerables are still on this layer, detach them, so they don't try to reach back to this
753  // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
754  // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
755  // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
756 
757  while (!mChildren.isEmpty())
758  mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
759 
760  if (mParentPlot->currentLayer() == this)
761  qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand.";
762 }
763 
773 {
774  mVisible = visible;
775 }
776 
787 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
788 {
789  if (!mChildren.contains(layerable))
790  {
791  if (prepend)
792  mChildren.prepend(layerable);
793  else
794  mChildren.append(layerable);
795  } else
796  qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
797 }
798 
809 {
810  if (!mChildren.removeOne(layerable))
811  qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
812 }
813 
814 
818 
831 /* start documentation of inline functions */
832 
846 /* end documentation of inline functions */
847 /* start documentation of pure virtual functions */
848 
889 /* end documentation of pure virtual functions */
890 /* start documentation of signals */
891 
900 /* end documentation of signals */
901 
920 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
921  QObject(plot),
922  mVisible(true),
923  mParentPlot(plot),
924  mParentLayerable(parentLayerable),
925  mLayer(0),
926  mAntialiased(true)
927 {
928  if (mParentPlot)
929  {
930  if (targetLayer.isEmpty())
932  else if (!setLayer(targetLayer))
933  qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
934  }
935 }
936 
938 {
939  if (mLayer)
940  {
941  mLayer->removeChild(this);
942  mLayer = 0;
943  }
944 }
945 
952 {
953  mVisible = on;
954 }
955 
963 {
964  return moveToLayer(layer, false);
965 }
966 
972 bool QCPLayerable::setLayer(const QString &layerName)
973 {
974  if (!mParentPlot)
975  {
976  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
977  return false;
978  }
979  if (QCPLayer *layer = mParentPlot->layer(layerName))
980  {
981  return setLayer(layer);
982  } else
983  {
984  qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
985  return false;
986  }
987 }
988 
996 {
997  mAntialiased = enabled;
998 }
999 
1014 {
1015  return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1016 }
1017 
1052 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1053 {
1054  Q_UNUSED(pos)
1055  Q_UNUSED(onlySelectable)
1056  Q_UNUSED(details)
1057  return -1.0;
1058 }
1059 
1078 {
1079  if (mParentPlot)
1080  {
1081  qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1082  return;
1083  }
1084 
1085  if (!parentPlot)
1086  qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1087 
1090 }
1091 
1104 {
1106 }
1107 
1117 {
1118  if (layer && !mParentPlot)
1119  {
1120  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1121  return false;
1122  }
1123  if (layer && layer->parentPlot() != mParentPlot)
1124  {
1125  qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1126  return false;
1127  }
1128 
1129  QCPLayer *oldLayer = mLayer;
1130  if (mLayer)
1131  mLayer->removeChild(this);
1132  mLayer = layer;
1133  if (mLayer)
1134  mLayer->addChild(this, prepend);
1135  if (mLayer != oldLayer)
1136  emit layerChanged(mLayer);
1137  return true;
1138 }
1139 
1147 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1148 {
1149  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1150  painter->setAntialiasing(false);
1151  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1152  painter->setAntialiasing(true);
1153  else
1154  painter->setAntialiasing(localAntialiased);
1155 }
1156 
1174 {
1175  Q_UNUSED(parentPlot)
1176 }
1177 
1190 {
1191  return QCP::iSelectOther;
1192 }
1193 
1204 {
1205  if (mParentPlot)
1206  return mParentPlot->viewport();
1207  else
1208  return QRect();
1209 }
1210 
1239 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1240 {
1241  Q_UNUSED(event)
1242  Q_UNUSED(additive)
1243  Q_UNUSED(details)
1244  Q_UNUSED(selectionStateChanged)
1245 }
1246 
1259 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1260 {
1261  Q_UNUSED(selectionStateChanged)
1262 }
1263 
1264 
1268 
1283 const double QCPRange::minRange = 1e-280;
1284 
1293 const double QCPRange::maxRange = 1e250;
1294 
1299  lower(0),
1300  upper(0)
1301 {
1302 }
1303 
1307 QCPRange::QCPRange(double lower, double upper) :
1308  lower(lower),
1309  upper(upper)
1310 {
1311  normalize();
1312 }
1313 
1317 double QCPRange::size() const
1318 {
1319  return upper-lower;
1320 }
1321 
1325 double QCPRange::center() const
1326 {
1327  return (upper+lower)*0.5;
1328 }
1329 
1335 {
1336  if (lower > upper)
1337  qSwap(lower, upper);
1338 }
1339 
1348 void QCPRange::expand(const QCPRange &otherRange)
1349 {
1350  if (lower > otherRange.lower)
1351  lower = otherRange.lower;
1352  if (upper < otherRange.upper)
1353  upper = otherRange.upper;
1354 }
1355 
1356 
1363 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1364 {
1365  QCPRange result = *this;
1366  result.expand(otherRange);
1367  return result;
1368 }
1369 
1383 {
1384  double rangeFac = 1e-3;
1385  QCPRange sanitizedRange(lower, upper);
1386  sanitizedRange.normalize();
1387  // can't have range spanning negative and positive values in log plot, so change range to fix it
1388  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
1389  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
1390  {
1391  // case lower is 0
1392  if (rangeFac < sanitizedRange.upper*rangeFac)
1393  sanitizedRange.lower = rangeFac;
1394  else
1395  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1396  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1397  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
1398  {
1399  // case upper is 0
1400  if (-rangeFac > sanitizedRange.lower*rangeFac)
1401  sanitizedRange.upper = -rangeFac;
1402  else
1403  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1404  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
1405  {
1406  // find out whether negative or positive interval is wider to decide which sign domain will be chosen
1407  if (-sanitizedRange.lower > sanitizedRange.upper)
1408  {
1409  // negative is wider, do same as in case upper is 0
1410  if (-rangeFac > sanitizedRange.lower*rangeFac)
1411  sanitizedRange.upper = -rangeFac;
1412  else
1413  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1414  } else
1415  {
1416  // positive is wider, do same as in case lower is 0
1417  if (rangeFac < sanitizedRange.upper*rangeFac)
1418  sanitizedRange.lower = rangeFac;
1419  else
1420  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1421  }
1422  }
1423  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
1424  return sanitizedRange;
1425 }
1426 
1432 {
1433  QCPRange sanitizedRange(lower, upper);
1434  sanitizedRange.normalize();
1435  return sanitizedRange;
1436 }
1437 
1441 bool QCPRange::contains(double value) const
1442 {
1443  return value >= lower && value <= upper;
1444 }
1445 
1454 bool QCPRange::validRange(double lower, double upper)
1455 {
1456  /*
1457  return (lower > -maxRange &&
1458  upper < maxRange &&
1459  qAbs(lower-upper) > minRange &&
1460  (lower < -minRange || lower > minRange) &&
1461  (upper < -minRange || upper > minRange));
1462  */
1463  return (lower > -maxRange &&
1464  upper < maxRange &&
1465  qAbs(lower-upper) > minRange &&
1466  qAbs(lower-upper) < maxRange);
1467 }
1468 
1478 bool QCPRange::validRange(const QCPRange &range)
1479 {
1480  /*
1481  return (range.lower > -maxRange &&
1482  range.upper < maxRange &&
1483  qAbs(range.lower-range.upper) > minRange &&
1484  qAbs(range.lower-range.upper) < maxRange &&
1485  (range.lower < -minRange || range.lower > minRange) &&
1486  (range.upper < -minRange || range.upper > minRange));
1487  */
1488  return (range.lower > -maxRange &&
1489  range.upper < maxRange &&
1490  qAbs(range.lower-range.upper) > minRange &&
1491  qAbs(range.lower-range.upper) < maxRange);
1492 }
1493 
1494 
1498 
1530 /* start documentation of inline functions */
1531 
1538 /* end documentation of inline functions */
1539 
1544  QObject(parentPlot),
1545  mParentPlot(parentPlot)
1546 {
1547  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
1548  mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
1549  mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
1550  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
1551 }
1552 
1554 {
1555  clear();
1556 }
1557 
1563 {
1564  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1565  while (it.hasNext())
1566  {
1567  it.next();
1568  if (!it.value().isEmpty())
1569  return false;
1570  }
1571  return true;
1572 }
1573 
1579 {
1580  // make all children remove themselves from this margin group:
1581  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1582  while (it.hasNext())
1583  {
1584  it.next();
1585  const QList<QCPLayoutElement*> elements = it.value();
1586  for (int i=elements.size()-1; i>=0; --i)
1587  elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
1588  }
1589 }
1590 
1602 {
1603  // query all automatic margins of the layout elements in this margin group side and find maximum:
1604  int result = 0;
1605  const QList<QCPLayoutElement*> elements = mChildren.value(side);
1606  for (int i=0; i<elements.size(); ++i)
1607  {
1608  if (!elements.at(i)->autoMargins().testFlag(side))
1609  continue;
1610  int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1611  if (m > result)
1612  result = m;
1613  }
1614  return result;
1615 }
1616 
1624 {
1625  if (!mChildren[side].contains(element))
1626  mChildren[side].append(element);
1627  else
1628  qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
1629 }
1630 
1638 {
1639  if (!mChildren[side].removeOne(element))
1640  qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
1641 }
1642 
1643 
1647 
1674 /* start documentation of inline functions */
1675 
1723 /* end documentation of inline functions */
1724 
1729  QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
1730  mParentLayout(0),
1731  mMinimumSize(),
1732  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1733  mRect(0, 0, 0, 0),
1734  mOuterRect(0, 0, 0, 0),
1735  mMargins(0, 0, 0, 0),
1736  mMinimumMargins(0, 0, 0, 0),
1737  mAutoMargins(QCP::msAll)
1738 {
1739 }
1740 
1742 {
1743  setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
1744  // unregister at layout:
1745  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
1746  mParentLayout->take(this);
1747 }
1748 
1761 {
1762  if (mOuterRect != rect)
1763  {
1764  mOuterRect = rect;
1765  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1766  }
1767 }
1768 
1781 {
1782  if (mMargins != margins)
1783  {
1784  mMargins = margins;
1785  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1786  }
1787 }
1788 
1799 {
1800  if (mMinimumMargins != margins)
1801  {
1803  }
1804 }
1805 
1816 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
1817 {
1818  mAutoMargins = sides;
1819 }
1820 
1831 {
1832  if (mMinimumSize != size)
1833  {
1834  mMinimumSize = size;
1835  if (mParentLayout)
1837  }
1838 }
1839 
1844 void QCPLayoutElement::setMinimumSize(int width, int height)
1845 {
1846  setMinimumSize(QSize(width, height));
1847 }
1848 
1854 {
1855  if (mMaximumSize != size)
1856  {
1857  mMaximumSize = size;
1858  if (mParentLayout)
1860  }
1861 }
1862 
1867 void QCPLayoutElement::setMaximumSize(int width, int height)
1868 {
1869  setMaximumSize(QSize(width, height));
1870 }
1871 
1883 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
1884 {
1885  QVector<QCP::MarginSide> sideVector;
1886  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1887  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1888  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1889  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1890 
1891  for (int i=0; i<sideVector.size(); ++i)
1892  {
1893  QCP::MarginSide side = sideVector.at(i);
1894  if (marginGroup(side) != group)
1895  {
1896  QCPMarginGroup *oldGroup = marginGroup(side);
1897  if (oldGroup) // unregister at old group
1898  oldGroup->removeChild(side, this);
1899 
1900  if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
1901  {
1902  mMarginGroups.remove(side);
1903  } else // setting to a new group
1904  {
1905  mMarginGroups[side] = group;
1906  group->addChild(side, this);
1907  }
1908  }
1909  }
1910 }
1911 
1925 {
1926  if (phase == upMargins)
1927  {
1928  if (mAutoMargins != QCP::msNone)
1929  {
1930  // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
1931  QMargins newMargins = mMargins;
1932  QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
1933  foreach (QCP::MarginSide side, allMarginSides)
1934  {
1935  if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
1936  {
1937  if (mMarginGroups.contains(side))
1938  QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
1939  else
1940  QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
1941  // apply minimum margin restrictions:
1942  if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
1943  QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
1944  }
1945  }
1946  setMargins(newMargins);
1947  }
1948  }
1949 }
1950 
1959 {
1960  return mMinimumSize;
1961 }
1962 
1971 {
1972  return mMaximumSize;
1973 }
1974 
1982 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
1983 {
1984  Q_UNUSED(recursive)
1985  return QList<QCPLayoutElement*>();
1986 }
1987 
1999 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
2000 {
2001  Q_UNUSED(details)
2002 
2003  if (onlySelectable)
2004  return -1;
2005 
2006  if (QRectF(mOuterRect).contains(pos))
2007  {
2008  if (mParentPlot)
2009  return mParentPlot->selectionTolerance()*0.99;
2010  else
2011  {
2012  qDebug() << Q_FUNC_INFO << "parent plot not defined";
2013  return -1;
2014  }
2015  } else
2016  return -1;
2017 }
2018 
2025 {
2026  foreach (QCPLayoutElement* el, elements(false))
2027  {
2028  if (!el->parentPlot())
2029  el->initializeParentPlot(parentPlot);
2030  }
2031 }
2032 
2043 {
2045 }
2046 
2050 
2074 /* start documentation of pure virtual functions */
2075 
2118 /* end documentation of pure virtual functions */
2119 
2125 {
2126 }
2127 
2137 {
2138  QCPLayoutElement::update(phase);
2139 
2140  // set child element rects according to layout:
2141  if (phase == upLayout)
2142  updateLayout();
2143 
2144  // propagate update call to child elements:
2145  const int elCount = elementCount();
2146  for (int i=0; i<elCount; ++i)
2147  {
2148  if (QCPLayoutElement *el = elementAt(i))
2149  el->update(phase);
2150  }
2151 }
2152 
2153 /* inherits documentation from base class */
2154 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2155 {
2156  const int c = elementCount();
2157  QList<QCPLayoutElement*> result;
2158 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2159  result.reserve(c);
2160 #endif
2161  for (int i=0; i<c; ++i)
2162  result.append(elementAt(i));
2163  if (recursive)
2164  {
2165  for (int i=0; i<c; ++i)
2166  {
2167  if (result.at(i))
2168  result << result.at(i)->elements(recursive);
2169  }
2170  }
2171  return result;
2172 }
2173 
2182 {
2183 }
2184 
2196 bool QCPLayout::removeAt(int index)
2197 {
2198  if (QCPLayoutElement *el = takeAt(index))
2199  {
2200  delete el;
2201  return true;
2202  } else
2203  return false;
2204 }
2205 
2218 {
2219  if (take(element))
2220  {
2221  delete element;
2222  return true;
2223  } else
2224  return false;
2225 }
2226 
2234 {
2235  for (int i=elementCount()-1; i>=0; --i)
2236  {
2237  if (elementAt(i))
2238  removeAt(i);
2239  }
2240  simplify();
2241 }
2242 
2252 {
2253  if (QWidget *w = qobject_cast<QWidget*>(parent()))
2254  w->updateGeometry();
2255  else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
2256  l->sizeConstraintsChanged();
2257 }
2258 
2272 {
2273 }
2274 
2275 
2289 {
2290  if (el)
2291  {
2292  el->mParentLayout = this;
2293  el->setParentLayerable(this);
2294  el->setParent(this);
2295  if (!el->parentPlot())
2297  } else
2298  qDebug() << Q_FUNC_INFO << "Null element passed";
2299 }
2300 
2312 {
2313  if (el)
2314  {
2315  el->mParentLayout = 0;
2316  el->setParentLayerable(0);
2317  el->setParent(mParentPlot);
2318  // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
2319  } else
2320  qDebug() << Q_FUNC_INFO << "Null element passed";
2321 }
2322 
2352 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
2353 {
2354  if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
2355  {
2356  qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
2357  return QVector<int>();
2358  }
2359  if (stretchFactors.isEmpty())
2360  return QVector<int>();
2361  int sectionCount = stretchFactors.size();
2362  QVector<double> sectionSizes(sectionCount);
2363  // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
2364  int minSizeSum = 0;
2365  for (int i=0; i<sectionCount; ++i)
2366  minSizeSum += minSizes.at(i);
2367  if (totalSize < minSizeSum)
2368  {
2369  // new stretch factors are minimum sizes and minimum sizes are set to zero:
2370  for (int i=0; i<sectionCount; ++i)
2371  {
2372  stretchFactors[i] = minSizes.at(i);
2373  minSizes[i] = 0;
2374  }
2375  }
2376 
2377  QList<int> minimumLockedSections;
2378  QList<int> unfinishedSections;
2379  for (int i=0; i<sectionCount; ++i)
2380  unfinishedSections.append(i);
2381  double freeSize = totalSize;
2382 
2383  int outerIterations = 0;
2384  while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2385  {
2386  ++outerIterations;
2387  int innerIterations = 0;
2388  while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2389  {
2390  ++innerIterations;
2391  // find section that hits its maximum next:
2392  int nextId = -1;
2393  double nextMax = 1e12;
2394  for (int i=0; i<unfinishedSections.size(); ++i)
2395  {
2396  int secId = unfinishedSections.at(i);
2397  double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
2398  if (hitsMaxAt < nextMax)
2399  {
2400  nextMax = hitsMaxAt;
2401  nextId = secId;
2402  }
2403  }
2404  // 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
2405  // actually hits its maximum, without exceeding the total size when we add up all sections)
2406  double stretchFactorSum = 0;
2407  for (int i=0; i<unfinishedSections.size(); ++i)
2408  stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2409  double nextMaxLimit = freeSize/stretchFactorSum;
2410  if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
2411  {
2412  for (int i=0; i<unfinishedSections.size(); ++i)
2413  {
2414  sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2415  freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
2416  }
2417  unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
2418  } else // next maximum isn't hit, just distribute rest of free space on remaining sections
2419  {
2420  for (int i=0; i<unfinishedSections.size(); ++i)
2421  sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2422  unfinishedSections.clear();
2423  }
2424  }
2425  if (innerIterations == sectionCount*2)
2426  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2427 
2428  // now check whether the resulting section sizes violate minimum restrictions:
2429  bool foundMinimumViolation = false;
2430  for (int i=0; i<sectionSizes.size(); ++i)
2431  {
2432  if (minimumLockedSections.contains(i))
2433  continue;
2434  if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
2435  {
2436  sectionSizes[i] = minSizes.at(i); // set it to minimum
2437  foundMinimumViolation = true; // make sure we repeat the whole optimization process
2438  minimumLockedSections.append(i);
2439  }
2440  }
2441  if (foundMinimumViolation)
2442  {
2443  freeSize = totalSize;
2444  for (int i=0; i<sectionCount; ++i)
2445  {
2446  if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
2447  unfinishedSections.append(i);
2448  else
2449  freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
2450  }
2451  // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
2452  for (int i=0; i<unfinishedSections.size(); ++i)
2453  sectionSizes[unfinishedSections.at(i)] = 0;
2454  }
2455  }
2456  if (outerIterations == sectionCount*2)
2457  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2458 
2459  QVector<int> result(sectionCount);
2460  for (int i=0; i<sectionCount; ++i)
2461  result[i] = qRound(sectionSizes.at(i));
2462  return result;
2463 }
2464 
2465 
2469 
2490  mColumnSpacing(5),
2491  mRowSpacing(5)
2492 {
2493 }
2494 
2496 {
2497  // clear all child layout elements. This is important because only the specific layouts know how
2498  // to handle removing elements (clear calls virtual removeAt method to do that).
2499  clear();
2500 }
2501 
2510 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
2511 {
2512  if (row >= 0 && row < mElements.size())
2513  {
2514  if (column >= 0 && column < mElements.first().size())
2515  {
2516  if (QCPLayoutElement *result = mElements.at(row).at(column))
2517  return result;
2518  else
2519  qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
2520  } else
2521  qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
2522  } else
2523  qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
2524  return 0;
2525 }
2526 
2533 {
2534  return mElements.size();
2535 }
2536 
2543 {
2544  if (mElements.size() > 0)
2545  return mElements.first().size();
2546  else
2547  return 0;
2548 }
2549 
2561 {
2562  if (element)
2563  {
2564  if (!hasElement(row, column))
2565  {
2566  if (element->layout()) // remove from old layout first
2567  element->layout()->take(element);
2568  expandTo(row+1, column+1);
2569  mElements[row][column] = element;
2570  adoptElement(element);
2571  return true;
2572  } else
2573  qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2574  } else
2575  qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2576  return false;
2577 }
2578 
2585 bool QCPLayoutGrid::hasElement(int row, int column)
2586 {
2587  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2588  return mElements.at(row).at(column);
2589  else
2590  return false;
2591 }
2592 
2604 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
2605 {
2606  if (column >= 0 && column < columnCount())
2607  {
2608  if (factor > 0)
2609  mColumnStretchFactors[column] = factor;
2610  else
2611  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2612  } else
2613  qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2614 }
2615 
2627 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
2628 {
2629  if (factors.size() == mColumnStretchFactors.size())
2630  {
2631  mColumnStretchFactors = factors;
2632  for (int i=0; i<mColumnStretchFactors.size(); ++i)
2633  {
2634  if (mColumnStretchFactors.at(i) <= 0)
2635  {
2636  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
2637  mColumnStretchFactors[i] = 1;
2638  }
2639  }
2640  } else
2641  qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
2642 }
2643 
2655 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
2656 {
2657  if (row >= 0 && row < rowCount())
2658  {
2659  if (factor > 0)
2660  mRowStretchFactors[row] = factor;
2661  else
2662  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2663  } else
2664  qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2665 }
2666 
2678 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
2679 {
2680  if (factors.size() == mRowStretchFactors.size())
2681  {
2682  mRowStretchFactors = factors;
2683  for (int i=0; i<mRowStretchFactors.size(); ++i)
2684  {
2685  if (mRowStretchFactors.at(i) <= 0)
2686  {
2687  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
2688  mRowStretchFactors[i] = 1;
2689  }
2690  }
2691  } else
2692  qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
2693 }
2694 
2701 {
2702  mColumnSpacing = pixels;
2703 }
2704 
2711 {
2712  mRowSpacing = pixels;
2713 }
2714 
2729 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
2730 {
2731  // add rows as necessary:
2732  while (rowCount() < newRowCount)
2733  {
2734  mElements.append(QList<QCPLayoutElement*>());
2735  mRowStretchFactors.append(1);
2736  }
2737  // go through rows and expand columns as necessary:
2738  int newColCount = qMax(columnCount(), newColumnCount);
2739  for (int i=0; i<rowCount(); ++i)
2740  {
2741  while (mElements.at(i).size() < newColCount)
2742  mElements[i].append(0);
2743  }
2744  while (mColumnStretchFactors.size() < newColCount)
2745  mColumnStretchFactors.append(1);
2746 }
2747 
2754 void QCPLayoutGrid::insertRow(int newIndex)
2755 {
2756  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2757  {
2758  expandTo(1, 1);
2759  return;
2760  }
2761 
2762  if (newIndex < 0)
2763  newIndex = 0;
2764  if (newIndex > rowCount())
2765  newIndex = rowCount();
2766 
2767  mRowStretchFactors.insert(newIndex, 1);
2768  QList<QCPLayoutElement*> newRow;
2769  for (int col=0; col<columnCount(); ++col)
2770  newRow.append((QCPLayoutElement*)0);
2771  mElements.insert(newIndex, newRow);
2772 }
2773 
2781 {
2782  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2783  {
2784  expandTo(1, 1);
2785  return;
2786  }
2787 
2788  if (newIndex < 0)
2789  newIndex = 0;
2790  if (newIndex > columnCount())
2791  newIndex = columnCount();
2792 
2793  mColumnStretchFactors.insert(newIndex, 1);
2794  for (int row=0; row<rowCount(); ++row)
2795  mElements[row].insert(newIndex, (QCPLayoutElement*)0);
2796 }
2797 
2798 /* inherits documentation from base class */
2800 {
2801  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2802  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2803  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2804 
2805  int totalRowSpacing = (rowCount()-1) * mRowSpacing;
2806  int totalColSpacing = (columnCount()-1) * mColumnSpacing;
2807  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
2808  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
2809 
2810  // go through cells and set rects accordingly:
2811  int yOffset = mRect.top();
2812  for (int row=0; row<rowCount(); ++row)
2813  {
2814  if (row > 0)
2815  yOffset += rowHeights.at(row-1)+mRowSpacing;
2816  int xOffset = mRect.left();
2817  for (int col=0; col<columnCount(); ++col)
2818  {
2819  if (col > 0)
2820  xOffset += colWidths.at(col-1)+mColumnSpacing;
2821  if (mElements.at(row).at(col))
2822  mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2823  }
2824  }
2825 }
2826 
2827 /* inherits documentation from base class */
2829 {
2830  return rowCount()*columnCount();
2831 }
2832 
2833 /* inherits documentation from base class */
2835 {
2836  if (index >= 0 && index < elementCount())
2837  return mElements.at(index / columnCount()).at(index % columnCount());
2838  else
2839  return 0;
2840 }
2841 
2842 /* inherits documentation from base class */
2844 {
2845  if (QCPLayoutElement *el = elementAt(index))
2846  {
2847  releaseElement(el);
2848  mElements[index / columnCount()][index % columnCount()] = 0;
2849  return el;
2850  } else
2851  {
2852  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2853  return 0;
2854  }
2855 }
2856 
2857 /* inherits documentation from base class */
2859 {
2860  if (element)
2861  {
2862  for (int i=0; i<elementCount(); ++i)
2863  {
2864  if (elementAt(i) == element)
2865  {
2866  takeAt(i);
2867  return true;
2868  }
2869  }
2870  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2871  } else
2872  qDebug() << Q_FUNC_INFO << "Can't take null element";
2873  return false;
2874 }
2875 
2876 /* inherits documentation from base class */
2877 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
2878 {
2879  QList<QCPLayoutElement*> result;
2880  int colC = columnCount();
2881  int rowC = rowCount();
2882 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2883  result.reserve(colC*rowC);
2884 #endif
2885  for (int row=0; row<rowC; ++row)
2886  {
2887  for (int col=0; col<colC; ++col)
2888  {
2889  result.append(mElements.at(row).at(col));
2890  }
2891  }
2892  if (recursive)
2893  {
2894  int c = result.size();
2895  for (int i=0; i<c; ++i)
2896  {
2897  if (result.at(i))
2898  result << result.at(i)->elements(recursive);
2899  }
2900  }
2901  return result;
2902 }
2903 
2908 {
2909  // remove rows with only empty cells:
2910  for (int row=rowCount()-1; row>=0; --row)
2911  {
2912  bool hasElements = false;
2913  for (int col=0; col<columnCount(); ++col)
2914  {
2915  if (mElements.at(row).at(col))
2916  {
2917  hasElements = true;
2918  break;
2919  }
2920  }
2921  if (!hasElements)
2922  {
2923  mRowStretchFactors.removeAt(row);
2924  mElements.removeAt(row);
2925  if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
2926  mColumnStretchFactors.clear();
2927  }
2928  }
2929 
2930  // remove columns with only empty cells:
2931  for (int col=columnCount()-1; col>=0; --col)
2932  {
2933  bool hasElements = false;
2934  for (int row=0; row<rowCount(); ++row)
2935  {
2936  if (mElements.at(row).at(col))
2937  {
2938  hasElements = true;
2939  break;
2940  }
2941  }
2942  if (!hasElements)
2943  {
2944  mColumnStretchFactors.removeAt(col);
2945  for (int row=0; row<rowCount(); ++row)
2946  mElements[row].removeAt(col);
2947  }
2948  }
2949 }
2950 
2951 /* inherits documentation from base class */
2953 {
2954  QVector<int> minColWidths, minRowHeights;
2955  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2956  QSize result(0, 0);
2957  for (int i=0; i<minColWidths.size(); ++i)
2958  result.rwidth() += minColWidths.at(i);
2959  for (int i=0; i<minRowHeights.size(); ++i)
2960  result.rheight() += minRowHeights.at(i);
2961  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2962  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2963  return result;
2964 }
2965 
2966 /* inherits documentation from base class */
2968 {
2969  QVector<int> maxColWidths, maxRowHeights;
2970  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2971 
2972  QSize result(0, 0);
2973  for (int i=0; i<maxColWidths.size(); ++i)
2974  result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
2975  for (int i=0; i<maxRowHeights.size(); ++i)
2976  result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
2977  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2978  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2979  return result;
2980 }
2981 
2994 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
2995 {
2996  *minColWidths = QVector<int>(columnCount(), 0);
2997  *minRowHeights = QVector<int>(rowCount(), 0);
2998  for (int row=0; row<rowCount(); ++row)
2999  {
3000  for (int col=0; col<columnCount(); ++col)
3001  {
3002  if (mElements.at(row).at(col))
3003  {
3004  QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
3005  QSize min = mElements.at(row).at(col)->minimumSize();
3006  QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
3007  if (minColWidths->at(col) < final.width())
3008  (*minColWidths)[col] = final.width();
3009  if (minRowHeights->at(row) < final.height())
3010  (*minRowHeights)[row] = final.height();
3011  }
3012  }
3013  }
3014 }
3015 
3028 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
3029 {
3030  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3031  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3032  for (int row=0; row<rowCount(); ++row)
3033  {
3034  for (int col=0; col<columnCount(); ++col)
3035  {
3036  if (mElements.at(row).at(col))
3037  {
3038  QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3039  QSize max = mElements.at(row).at(col)->maximumSize();
3040  QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
3041  if (maxColWidths->at(col) > final.width())
3042  (*maxColWidths)[col] = final.width();
3043  if (maxRowHeights->at(row) > final.height())
3044  (*maxRowHeights)[row] = final.height();
3045  }
3046  }
3047  }
3048 }
3049 
3050 
3054 
3072 /* start documentation of inline functions */
3073 
3080 /* end documentation of inline functions */
3081 
3086 {
3087 }
3088 
3090 {
3091  // clear all child layout elements. This is important because only the specific layouts know how
3092  // to handle removing elements (clear calls virtual removeAt method to do that).
3093  clear();
3094 }
3095 
3100 {
3101  if (elementAt(index))
3102  return mInsetPlacement.at(index);
3103  else
3104  {
3105  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3106  return ipFree;
3107  }
3108 }
3109 
3114 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
3115 {
3116  if (elementAt(index))
3117  return mInsetAlignment.at(index);
3118  else
3119  {
3120  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3121  return 0;
3122  }
3123 }
3124 
3129 QRectF QCPLayoutInset::insetRect(int index) const
3130 {
3131  if (elementAt(index))
3132  return mInsetRect.at(index);
3133  else
3134  {
3135  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3136  return QRectF();
3137  }
3138 }
3139 
3146 {
3147  if (elementAt(index))
3148  mInsetPlacement[index] = placement;
3149  else
3150  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3151 }
3152 
3161 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
3162 {
3163  if (elementAt(index))
3164  mInsetAlignment[index] = alignment;
3165  else
3166  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3167 }
3168 
3180 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
3181 {
3182  if (elementAt(index))
3183  mInsetRect[index] = rect;
3184  else
3185  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3186 }
3187 
3188 /* inherits documentation from base class */
3190 {
3191  for (int i=0; i<mElements.size(); ++i)
3192  {
3193  QRect insetRect;
3194  QSize finalMinSize, finalMaxSize;
3195  QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3196  QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3197  finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
3198  finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
3199  finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
3200  finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
3201  if (mInsetPlacement.at(i) == ipFree)
3202  {
3203  insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
3204  rect().y()+rect().height()*mInsetRect.at(i).y(),
3205  rect().width()*mInsetRect.at(i).width(),
3206  rect().height()*mInsetRect.at(i).height());
3207  if (insetRect.size().width() < finalMinSize.width())
3208  insetRect.setWidth(finalMinSize.width());
3209  if (insetRect.size().height() < finalMinSize.height())
3210  insetRect.setHeight(finalMinSize.height());
3211  if (insetRect.size().width() > finalMaxSize.width())
3212  insetRect.setWidth(finalMaxSize.width());
3213  if (insetRect.size().height() > finalMaxSize.height())
3214  insetRect.setHeight(finalMaxSize.height());
3215  } else if (mInsetPlacement.at(i) == ipBorderAligned)
3216  {
3217  insetRect.setSize(finalMinSize);
3218  Qt::Alignment al = mInsetAlignment.at(i);
3219  if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
3220  else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
3221  else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
3222  if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
3223  else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
3224  else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
3225  }
3226  mElements.at(i)->setOuterRect(insetRect);
3227  }
3228 }
3229 
3230 /* inherits documentation from base class */
3232 {
3233  return mElements.size();
3234 }
3235 
3236 /* inherits documentation from base class */
3238 {
3239  if (index >= 0 && index < mElements.size())
3240  return mElements.at(index);
3241  else
3242  return 0;
3243 }
3244 
3245 /* inherits documentation from base class */
3247 {
3248  if (QCPLayoutElement *el = elementAt(index))
3249  {
3250  releaseElement(el);
3251  mElements.removeAt(index);
3252  mInsetPlacement.removeAt(index);
3253  mInsetAlignment.removeAt(index);
3254  mInsetRect.removeAt(index);
3255  return el;
3256  } else
3257  {
3258  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3259  return 0;
3260  }
3261 }
3262 
3263 /* inherits documentation from base class */
3265 {
3266  if (element)
3267  {
3268  for (int i=0; i<elementCount(); ++i)
3269  {
3270  if (elementAt(i) == element)
3271  {
3272  takeAt(i);
3273  return true;
3274  }
3275  }
3276  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3277  } else
3278  qDebug() << Q_FUNC_INFO << "Can't take null element";
3279  return false;
3280 }
3281 
3291 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3292 {
3293  Q_UNUSED(details)
3294  if (onlySelectable)
3295  return -1;
3296 
3297  for (int i=0; i<mElements.size(); ++i)
3298  {
3299  // inset layout shall only return positive selectTest, if actually an inset object is at pos
3300  // else it would block the entire underlying QCPAxisRect with its surface.
3301  if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3302  return mParentPlot->selectionTolerance()*0.99;
3303  }
3304  return -1;
3305 }
3306 
3318 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3319 {
3320  if (element)
3321  {
3322  if (element->layout()) // remove from old layout first
3323  element->layout()->take(element);
3324  mElements.append(element);
3325  mInsetPlacement.append(ipBorderAligned);
3326  mInsetAlignment.append(alignment);
3327  mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3328  adoptElement(element);
3329  } else
3330  qDebug() << Q_FUNC_INFO << "Can't add null element";
3331 }
3332 
3345 {
3346  if (element)
3347  {
3348  if (element->layout()) // remove from old layout first
3349  element->layout()->take(element);
3350  mElements.append(element);
3351  mInsetPlacement.append(ipFree);
3352  mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
3353  mInsetRect.append(rect);
3354  adoptElement(element);
3355  } else
3356  qDebug() << Q_FUNC_INFO << "Can't add null element";
3357 }
3358 
3359 
3363 
3388  mStyle(esNone),
3389  mWidth(8),
3390  mLength(10),
3391  mInverted(false)
3392 {
3393 }
3394 
3399  mStyle(style),
3400  mWidth(width),
3401  mLength(length),
3402  mInverted(inverted)
3403 {
3404 }
3405 
3410 {
3411  mStyle = style;
3412 }
3413 
3421 {
3422  mWidth = width;
3423 }
3424 
3432 {
3433  mLength = length;
3434 }
3435 
3445 {
3446  mInverted = inverted;
3447 }
3448 
3459 {
3460  switch (mStyle)
3461  {
3462  case esNone:
3463  return 0;
3464 
3465  case esFlatArrow:
3466  case esSpikeArrow:
3467  case esLineArrow:
3468  case esSkewedBar:
3469  return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
3470 
3471  case esDisc:
3472  case esSquare:
3473  case esDiamond:
3474  case esBar:
3475  case esHalfBar:
3476  return mWidth*1.42; // items that only have a width -> width*sqrt(2)
3477 
3478  }
3479  return 0;
3480 }
3481 
3494 {
3495  switch (mStyle)
3496  {
3497  case esNone:
3498  case esLineArrow:
3499  case esSkewedBar:
3500  case esBar:
3501  case esHalfBar:
3502  return 0;
3503 
3504  case esFlatArrow:
3505  return mLength;
3506 
3507  case esDisc:
3508  case esSquare:
3509  case esDiamond:
3510  return mWidth*0.5;
3511 
3512  case esSpikeArrow:
3513  return mLength*0.8;
3514  }
3515  return 0;
3516 }
3517 
3523 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
3524 {
3525  if (mStyle == esNone)
3526  return;
3527 
3528  QVector2D lengthVec(dir.normalized());
3529  if (lengthVec.isNull())
3530  lengthVec = QVector2D(1, 0);
3531  QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3532  lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3533  widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3534 
3535  QPen penBackup = painter->pen();
3536  QBrush brushBackup = painter->brush();
3537  QPen miterPen = penBackup;
3538  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3539  QBrush brush(painter->pen().color(), Qt::SolidPattern);
3540  switch (mStyle)
3541  {
3542  case esNone: break;
3543  case esFlatArrow:
3544  {
3545  QPointF points[3] = {pos.toPointF(),
3546  (pos-lengthVec+widthVec).toPointF(),
3547  (pos-lengthVec-widthVec).toPointF()
3548  };
3549  painter->setPen(miterPen);
3550  painter->setBrush(brush);
3551  painter->drawConvexPolygon(points, 3);
3552  painter->setBrush(brushBackup);
3553  painter->setPen(penBackup);
3554  break;
3555  }
3556  case esSpikeArrow:
3557  {
3558  QPointF points[4] = {pos.toPointF(),
3559  (pos-lengthVec+widthVec).toPointF(),
3560  (pos-lengthVec*0.8f).toPointF(),
3561  (pos-lengthVec-widthVec).toPointF()
3562  };
3563  painter->setPen(miterPen);
3564  painter->setBrush(brush);
3565  painter->drawConvexPolygon(points, 4);
3566  painter->setBrush(brushBackup);
3567  painter->setPen(penBackup);
3568  break;
3569  }
3570  case esLineArrow:
3571  {
3572  QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
3573  pos.toPointF(),
3574  (pos-lengthVec-widthVec).toPointF()
3575  };
3576  painter->setPen(miterPen);
3577  painter->drawPolyline(points, 3);
3578  painter->setPen(penBackup);
3579  break;
3580  }
3581  case esDisc:
3582  {
3583  painter->setBrush(brush);
3584  painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
3585  painter->setBrush(brushBackup);
3586  break;
3587  }
3588  case esSquare:
3589  {
3590  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3591  QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
3592  (pos-widthVecPerp-widthVec).toPointF(),
3593  (pos+widthVecPerp-widthVec).toPointF(),
3594  (pos+widthVecPerp+widthVec).toPointF()
3595  };
3596  painter->setPen(miterPen);
3597  painter->setBrush(brush);
3598  painter->drawConvexPolygon(points, 4);
3599  painter->setBrush(brushBackup);
3600  painter->setPen(penBackup);
3601  break;
3602  }
3603  case esDiamond:
3604  {
3605  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3606  QPointF points[4] = {(pos-widthVecPerp).toPointF(),
3607  (pos-widthVec).toPointF(),
3608  (pos+widthVecPerp).toPointF(),
3609  (pos+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 esBar:
3619  {
3620  painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
3621  break;
3622  }
3623  case esHalfBar:
3624  {
3625  painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
3626  break;
3627  }
3628  case esSkewedBar:
3629  {
3630  if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3631  {
3632  // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3633  painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3634  (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3635  } else
3636  {
3637  // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3638  painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3639  (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
3640  }
3641  break;
3642  }
3643  }
3644 }
3645 
3651 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
3652 {
3653  draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3654 }
3655 
3656 
3660 
3680  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3681  mParentAxis(parentAxis)
3682 {
3683  // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
3684  setParent(parentAxis);
3685  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
3686  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
3687  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
3688  setSubGridVisible(false);
3689  setAntialiased(false);
3690  setAntialiasedSubGrid(false);
3691  setAntialiasedZeroLine(false);
3692 }
3693 
3700 {
3702 }
3703 
3708 {
3709  mAntialiasedSubGrid = enabled;
3710 }
3711 
3716 {
3717  mAntialiasedZeroLine = enabled;
3718 }
3719 
3723 void QCPGrid::setPen(const QPen &pen)
3724 {
3725  mPen = pen;
3726 }
3727 
3731 void QCPGrid::setSubGridPen(const QPen &pen)
3732 {
3733  mSubGridPen = pen;
3734 }
3735 
3742 void QCPGrid::setZeroLinePen(const QPen &pen)
3743 {
3744  mZeroLinePen = pen;
3745 }
3746 
3761 {
3763 }
3764 
3771 {
3772  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3773 
3774  if (mSubGridVisible)
3775  drawSubGridLines(painter);
3776  drawGridLines(painter);
3777 }
3778 
3786 {
3787  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3788 
3789  int lowTick = mParentAxis->mLowestVisibleTick;
3790  int highTick = mParentAxis->mHighestVisibleTick;
3791  double t; // helper variable, result of coordinate-to-pixel transforms
3792  if (mParentAxis->orientation() == Qt::Horizontal)
3793  {
3794  // draw zeroline:
3795  int zeroLineIndex = -1;
3796  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3797  {
3799  painter->setPen(mZeroLinePen);
3800  double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
3801  for (int i=lowTick; i <= highTick; ++i)
3802  {
3803  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3804  {
3805  zeroLineIndex = i;
3806  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3807  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3808  break;
3809  }
3810  }
3811  }
3812  // draw grid lines:
3814  painter->setPen(mPen);
3815  for (int i=lowTick; i <= highTick; ++i)
3816  {
3817  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3818  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3819  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3820  }
3821  } else
3822  {
3823  // draw zeroline:
3824  int zeroLineIndex = -1;
3825  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3826  {
3828  painter->setPen(mZeroLinePen);
3829  double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
3830  for (int i=lowTick; i <= highTick; ++i)
3831  {
3832  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3833  {
3834  zeroLineIndex = i;
3835  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3836  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3837  break;
3838  }
3839  }
3840  }
3841  // draw grid lines:
3843  painter->setPen(mPen);
3844  for (int i=lowTick; i <= highTick; ++i)
3845  {
3846  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3847  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3848  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3849  }
3850  }
3851 }
3852 
3860 {
3861  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3862 
3864  double t; // helper variable, result of coordinate-to-pixel transforms
3865  painter->setPen(mSubGridPen);
3866  if (mParentAxis->orientation() == Qt::Horizontal)
3867  {
3868  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3869  {
3871  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3872  }
3873  } else
3874  {
3875  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3876  {
3878  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3879  }
3880  }
3881 }
3882 
3883 
3887 
3906 /* start of documentation of inline functions */
3907 
3929 /* end of documentation of inline functions */
3930 /* start of documentation of signals */
3931 
3986 /* end of documentation of signals */
3987 
3996  QCPLayerable(parent->parentPlot(), QString(), parent),
3997  // axis base:
3998  mAxisType(type),
3999  mAxisRect(parent),
4000  mPadding(5),
4001  mOrientation(orientation(type)),
4002  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
4003  mSelectedParts(spNone),
4004  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4005  mSelectedBasePen(QPen(Qt::blue, 2)),
4006  // axis label:
4007  mLabel(),
4008  mLabelFont(mParentPlot->font()),
4009  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4010  mLabelColor(Qt::black),
4011  mSelectedLabelColor(Qt::blue),
4012  // tick labels:
4013  mTickLabels(true),
4014  mAutoTickLabels(true),
4015  mTickLabelType(ltNumber),
4016  mTickLabelFont(mParentPlot->font()),
4017  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4018  mTickLabelColor(Qt::black),
4019  mSelectedTickLabelColor(Qt::blue),
4020  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
4021  mDateTimeSpec(Qt::LocalTime),
4022  mNumberPrecision(6),
4023  mNumberFormatChar('g'),
4024  mNumberBeautifulPowers(true),
4025  // ticks and subticks:
4026  mTicks(true),
4027  mTickStep(1),
4028  mSubTickCount(4),
4029  mAutoTickCount(6),
4030  mAutoTicks(true),
4031  mAutoTickStep(true),
4032  mAutoSubTicks(true),
4033  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4034  mSelectedTickPen(QPen(Qt::blue, 2)),
4035  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4036  mSelectedSubTickPen(QPen(Qt::blue, 2)),
4037  // scale and range:
4038  mRange(0, 5),
4039  mRangeReversed(false),
4040  mScaleType(stLinear),
4041  mScaleLogBase(10),
4042  mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4043  // internal members:
4044  mGrid(new QCPGrid(this)),
4045  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4046  mLowestVisibleTick(0),
4047  mHighestVisibleTick(-1),
4048  mCachedMarginValid(false),
4049  mCachedMargin(0)
4050 {
4051  mGrid->setVisible(false);
4052  setAntialiased(false);
4053  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
4054 
4055  if (type == atTop)
4056  {
4058  setLabelPadding(6);
4059  } else if (type == atRight)
4060  {
4062  setLabelPadding(12);
4063  } else if (type == atBottom)
4064  {
4066  setLabelPadding(3);
4067  } else if (type == atLeft)
4068  {
4070  setLabelPadding(10);
4071  }
4072 }
4073 
4075 {
4076  delete mAxisPainter;
4077  delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
4078 }
4079 
4080 /* No documentation as it is a property getter */
4082 {
4084 }
4085 
4086 /* No documentation as it is a property getter */
4088 {
4090 }
4091 
4092 /* No documentation as it is a property getter */
4094 {
4095  return mAxisPainter->tickLabelSide;
4096 }
4097 
4098 /* No documentation as it is a property getter */
4099 QString QCPAxis::numberFormat() const
4100 {
4101  QString result;
4102  result.append(mNumberFormatChar);
4104  {
4105  result.append(QLatin1Char('b'));
4107  result.append(QLatin1Char('c'));
4108  }
4109  return result;
4110 }
4111 
4112 /* No documentation as it is a property getter */
4114 {
4115  return mAxisPainter->tickLengthIn;
4116 }
4117 
4118 /* No documentation as it is a property getter */
4120 {
4121  return mAxisPainter->tickLengthOut;
4122 }
4123 
4124 /* No documentation as it is a property getter */
4126 {
4127  return mAxisPainter->subTickLengthIn;
4128 }
4129 
4130 /* No documentation as it is a property getter */
4132 {
4134 }
4135 
4136 /* No documentation as it is a property getter */
4138 {
4139  return mAxisPainter->labelPadding;
4140 }
4141 
4142 /* No documentation as it is a property getter */
4143 int QCPAxis::offset() const
4144 {
4145  return mAxisPainter->offset;
4146 }
4147 
4148 /* No documentation as it is a property getter */
4150 {
4151  return mAxisPainter->lowerEnding;
4152 }
4153 
4154 /* No documentation as it is a property getter */
4156 {
4157  return mAxisPainter->upperEnding;
4158 }
4159 
4174 {
4175  if (mScaleType != type)
4176  {
4177  mScaleType = type;
4178  if (mScaleType == stLogarithmic)
4180  mCachedMarginValid = false;
4182  }
4183 }
4184 
4192 void QCPAxis::setScaleLogBase(double base)
4193 {
4194  if (base > 1)
4195  {
4196  mScaleLogBase = base;
4197  mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4198  mCachedMarginValid = false;
4199  } else
4200  qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
4201 }
4202 
4212 {
4213  if (range.lower == mRange.lower && range.upper == mRange.upper)
4214  return;
4215 
4216  if (!QCPRange::validRange(range)) return;
4217  QCPRange oldRange = mRange;
4218  if (mScaleType == stLogarithmic)
4219  {
4220  mRange = range.sanitizedForLogScale();
4221  } else
4222  {
4223  mRange = range.sanitizedForLinScale();
4224  }
4225  mCachedMarginValid = false;
4226  emit rangeChanged(mRange);
4227  emit rangeChanged(mRange, oldRange);
4228 }
4229 
4240 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4241 {
4242  if (mSelectableParts != selectable)
4243  {
4244  mSelectableParts = selectable;
4246  }
4247 }
4248 
4264 void QCPAxis::setSelectedParts(const SelectableParts &selected)
4265 {
4266  if (mSelectedParts != selected)
4267  {
4268  mSelectedParts = selected;
4270  }
4271 }
4272 
4282 void QCPAxis::setRange(double lower, double upper)
4283 {
4284  if (lower == mRange.lower && upper == mRange.upper)
4285  return;
4286 
4287  if (!QCPRange::validRange(lower, upper)) return;
4288  QCPRange oldRange = mRange;
4289  mRange.lower = lower;
4290  mRange.upper = upper;
4291  if (mScaleType == stLogarithmic)
4292  {
4294  } else
4295  {
4297  }
4298  mCachedMarginValid = false;
4299  emit rangeChanged(mRange);
4300  emit rangeChanged(mRange, oldRange);
4301 }
4302 
4314 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
4315 {
4316  if (alignment == Qt::AlignLeft)
4317  setRange(position, position+size);
4318  else if (alignment == Qt::AlignRight)
4319  setRange(position-size, position);
4320  else // alignment == Qt::AlignCenter
4321  setRange(position-size/2.0, position+size/2.0);
4322 }
4323 
4328 void QCPAxis::setRangeLower(double lower)
4329 {
4330  if (mRange.lower == lower)
4331  return;
4332 
4333  QCPRange oldRange = mRange;
4334  mRange.lower = lower;
4335  if (mScaleType == stLogarithmic)
4336  {
4338  } else
4339  {
4341  }
4342  mCachedMarginValid = false;
4343  emit rangeChanged(mRange);
4344  emit rangeChanged(mRange, oldRange);
4345 }
4346 
4351 void QCPAxis::setRangeUpper(double upper)
4352 {
4353  if (mRange.upper == upper)
4354  return;
4355 
4356  QCPRange oldRange = mRange;
4357  mRange.upper = upper;
4358  if (mScaleType == stLogarithmic)
4359  {
4361  } else
4362  {
4364  }
4365  mCachedMarginValid = false;
4366  emit rangeChanged(mRange);
4367  emit rangeChanged(mRange, oldRange);
4368 }
4369 
4379 void QCPAxis::setRangeReversed(bool reversed)
4380 {
4381  if (mRangeReversed != reversed)
4382  {
4383  mRangeReversed = reversed;
4384  mCachedMarginValid = false;
4385  }
4386 }
4387 
4404 {
4405  if (mAutoTicks != on)
4406  {
4407  mAutoTicks = on;
4408  mCachedMarginValid = false;
4409  }
4410 }
4411 
4423 void QCPAxis::setAutoTickCount(int approximateCount)
4424 {
4425  if (mAutoTickCount != approximateCount)
4426  {
4427  if (approximateCount > 0)
4428  {
4429  mAutoTickCount = approximateCount;
4430  mCachedMarginValid = false;
4431  } else
4432  qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
4433  }
4434 }
4435 
4452 {
4453  if (mAutoTickLabels != on)
4454  {
4455  mAutoTickLabels = on;
4456  mCachedMarginValid = false;
4457  }
4458 }
4459 
4473 {
4474  if (mAutoTickStep != on)
4475  {
4476  mAutoTickStep = on;
4477  mCachedMarginValid = false;
4478  }
4479 }
4480 
4491 {
4492  if (mAutoSubTicks != on)
4493  {
4494  mAutoSubTicks = on;
4495  mCachedMarginValid = false;
4496  }
4497 }
4498 
4505 void QCPAxis::setTicks(bool show)
4506 {
4507  if (mTicks != show)
4508  {
4509  mTicks = show;
4510  mCachedMarginValid = false;
4511  }
4512 }
4513 
4517 void QCPAxis::setTickLabels(bool show)
4518 {
4519  if (mTickLabels != show)
4520  {
4521  mTickLabels = show;
4522  mCachedMarginValid = false;
4523  }
4524 }
4525 
4531 {
4532  if (mAxisPainter->tickLabelPadding != padding)
4533  {
4535  mCachedMarginValid = false;
4536  }
4537 }
4538 
4561 {
4562  if (mTickLabelType != type)
4563  {
4564  mTickLabelType = type;
4565  mCachedMarginValid = false;
4566  }
4567 }
4568 
4574 void QCPAxis::setTickLabelFont(const QFont &font)
4575 {
4576  if (font != mTickLabelFont)
4577  {
4578  mTickLabelFont = font;
4579  mCachedMarginValid = false;
4580  }
4581 }
4582 
4588 void QCPAxis::setTickLabelColor(const QColor &color)
4589 {
4590  if (color != mTickLabelColor)
4591  {
4592  mTickLabelColor = color;
4593  mCachedMarginValid = false;
4594  }
4595 }
4596 
4606 void QCPAxis::setTickLabelRotation(double degrees)
4607 {
4608  if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4609  {
4610  mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4611  mCachedMarginValid = false;
4612  }
4613 }
4614 
4623 {
4624  mAxisPainter->tickLabelSide = side;
4625  mCachedMarginValid = false;
4626 }
4627 
4636 void QCPAxis::setDateTimeFormat(const QString &format)
4637 {
4638  if (mDateTimeFormat != format)
4639  {
4640  mDateTimeFormat = format;
4641  mCachedMarginValid = false;
4642  }
4643 }
4644 
4655 void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
4656 {
4657  mDateTimeSpec = timeSpec;
4658 }
4659 
4696 void QCPAxis::setNumberFormat(const QString &formatCode)
4697 {
4698  if (formatCode.isEmpty())
4699  {
4700  qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4701  return;
4702  }
4703  mCachedMarginValid = false;
4704 
4705  // interpret first char as number format char:
4706  QString allowedFormatChars(QLatin1String("eEfgG"));
4707  if (allowedFormatChars.contains(formatCode.at(0)))
4708  {
4709  mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
4710  } else
4711  {
4712  qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4713  return;
4714  }
4715  if (formatCode.length() < 2)
4716  {
4717  mNumberBeautifulPowers = false;
4719  return;
4720  }
4721 
4722  // interpret second char as indicator for beautiful decimal powers:
4723  if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
4724  {
4725  mNumberBeautifulPowers = true;
4726  } else
4727  {
4728  qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4729  return;
4730  }
4731  if (formatCode.length() < 3)
4732  {
4734  return;
4735  }
4736 
4737  // interpret third char as indicator for dot or cross multiplication symbol:
4738  if (formatCode.at(2) == QLatin1Char('c'))
4739  {
4741  } else if (formatCode.at(2) == QLatin1Char('d'))
4742  {
4744  } else
4745  {
4746  qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4747  return;
4748  }
4749 }
4750 
4762 void QCPAxis::setNumberPrecision(int precision)
4763 {
4764  if (mNumberPrecision != precision)
4765  {
4766  mNumberPrecision = precision;
4767  mCachedMarginValid = false;
4768  }
4769 }
4770 
4776 void QCPAxis::setTickStep(double step)
4777 {
4778  if (mTickStep != step)
4779  {
4780  mTickStep = step;
4781  mCachedMarginValid = false;
4782  }
4783 }
4784 
4798 void QCPAxis::setTickVector(const QVector<double> &vec)
4799 {
4800  // don't check whether mTickVector != vec here, because it takes longer than we would save
4801  mTickVector = vec;
4802  mCachedMarginValid = false;
4803 }
4804 
4816 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4817 {
4818  // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
4819  mTickVectorLabels = vec;
4820  mCachedMarginValid = false;
4821 }
4822 
4831 void QCPAxis::setTickLength(int inside, int outside)
4832 {
4833  setTickLengthIn(inside);
4834  setTickLengthOut(outside);
4835 }
4836 
4844 {
4845  if (mAxisPainter->tickLengthIn != inside)
4846  {
4847  mAxisPainter->tickLengthIn = inside;
4848  }
4849 }
4850 
4858 void QCPAxis::setTickLengthOut(int outside)
4859 {
4860  if (mAxisPainter->tickLengthOut != outside)
4861  {
4862  mAxisPainter->tickLengthOut = outside;
4863  mCachedMarginValid = false; // only outside tick length can change margin
4864  }
4865 }
4866 
4879 {
4880  mSubTickCount = count;
4881 }
4882 
4891 void QCPAxis::setSubTickLength(int inside, int outside)
4892 {
4893  setSubTickLengthIn(inside);
4894  setSubTickLengthOut(outside);
4895 }
4896 
4904 {
4905  if (mAxisPainter->subTickLengthIn != inside)
4906  {
4907  mAxisPainter->subTickLengthIn = inside;
4908  }
4909 }
4910 
4919 {
4920  if (mAxisPainter->subTickLengthOut != outside)
4921  {
4922  mAxisPainter->subTickLengthOut = outside;
4923  mCachedMarginValid = false; // only outside tick length can change margin
4924  }
4925 }
4926 
4932 void QCPAxis::setBasePen(const QPen &pen)
4933 {
4934  mBasePen = pen;
4935 }
4936 
4942 void QCPAxis::setTickPen(const QPen &pen)
4943 {
4944  mTickPen = pen;
4945 }
4946 
4952 void QCPAxis::setSubTickPen(const QPen &pen)
4953 {
4954  mSubTickPen = pen;
4955 }
4956 
4962 void QCPAxis::setLabelFont(const QFont &font)
4963 {
4964  if (mLabelFont != font)
4965  {
4966  mLabelFont = font;
4967  mCachedMarginValid = false;
4968  }
4969 }
4970 
4976 void QCPAxis::setLabelColor(const QColor &color)
4977 {
4978  mLabelColor = color;
4979 }
4980 
4985 void QCPAxis::setLabel(const QString &str)
4986 {
4987  if (mLabel != str)
4988  {
4989  mLabel = str;
4990  mCachedMarginValid = false;
4991  }
4992 }
4993 
5000 {
5001  if (mAxisPainter->labelPadding != padding)
5002  {
5004  mCachedMarginValid = false;
5005  }
5006 }
5007 
5019 {
5020  if (mPadding != padding)
5021  {
5022  mPadding = padding;
5023  mCachedMarginValid = false;
5024  }
5025 }
5026 
5036 {
5038 }
5039 
5045 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
5046 {
5047  if (font != mSelectedTickLabelFont)
5048  {
5049  mSelectedTickLabelFont = font;
5050  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5051  }
5052 }
5053 
5059 void QCPAxis::setSelectedLabelFont(const QFont &font)
5060 {
5061  mSelectedLabelFont = font;
5062  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5063 }
5064 
5070 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
5071 {
5072  if (color != mSelectedTickLabelColor)
5073  {
5074  mSelectedTickLabelColor = color;
5075  }
5076 }
5077 
5083 void QCPAxis::setSelectedLabelColor(const QColor &color)
5084 {
5085  mSelectedLabelColor = color;
5086 }
5087 
5093 void QCPAxis::setSelectedBasePen(const QPen &pen)
5094 {
5095  mSelectedBasePen = pen;
5096 }
5097 
5103 void QCPAxis::setSelectedTickPen(const QPen &pen)
5104 {
5105  mSelectedTickPen = pen;
5106 }
5107 
5113 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
5114 {
5115  mSelectedSubTickPen = pen;
5116 }
5117 
5129 {
5130  mAxisPainter->lowerEnding = ending;
5131 }
5132 
5144 {
5145  mAxisPainter->upperEnding = ending;
5146 }
5147 
5155 void QCPAxis::moveRange(double diff)
5156 {
5157  QCPRange oldRange = mRange;
5158  if (mScaleType == stLinear)
5159  {
5160  mRange.lower += diff;
5161  mRange.upper += diff;
5162  } else // mScaleType == stLogarithmic
5163  {
5164  mRange.lower *= diff;
5165  mRange.upper *= diff;
5166  }
5167  mCachedMarginValid = false;
5168  emit rangeChanged(mRange);
5169  emit rangeChanged(mRange, oldRange);
5170 }
5171 
5178 void QCPAxis::scaleRange(double factor, double center)
5179 {
5180  QCPRange oldRange = mRange;
5181  if (mScaleType == stLinear)
5182  {
5183  QCPRange newRange;
5184  newRange.lower = (mRange.lower-center)*factor + center;
5185  newRange.upper = (mRange.upper-center)*factor + center;
5186  if (QCPRange::validRange(newRange))
5187  mRange = newRange.sanitizedForLinScale();
5188  } else // mScaleType == stLogarithmic
5189  {
5190  if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
5191  {
5192  QCPRange newRange;
5193  newRange.lower = qPow(mRange.lower/center, factor)*center;
5194  newRange.upper = qPow(mRange.upper/center, factor)*center;
5195  if (QCPRange::validRange(newRange))
5196  mRange = newRange.sanitizedForLogScale();
5197  } else
5198  qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
5199  }
5200  mCachedMarginValid = false;
5201  emit rangeChanged(mRange);
5202  emit rangeChanged(mRange, oldRange);
5203 }
5204 
5218 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
5219 {
5220  int otherPixelSize, ownPixelSize;
5221 
5222  if (otherAxis->orientation() == Qt::Horizontal)
5223  otherPixelSize = otherAxis->axisRect()->width();
5224  else
5225  otherPixelSize = otherAxis->axisRect()->height();
5226 
5227  if (orientation() == Qt::Horizontal)
5228  ownPixelSize = axisRect()->width();
5229  else
5230  ownPixelSize = axisRect()->height();
5231 
5232  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
5233  setRange(range().center(), newRangeSize, Qt::AlignCenter);
5234 }
5235 
5242 void QCPAxis::rescale(bool onlyVisiblePlottables)
5243 {
5244  QList<QCPAbstractPlottable*> p = plottables();
5245  QCPRange newRange;
5246  bool haveRange = false;
5247  for (int i=0; i<p.size(); ++i)
5248  {
5249  if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5250  continue;
5251  QCPRange plottableRange;
5252  bool currentFoundRange;
5254  if (mScaleType == stLogarithmic)
5256  if (p.at(i)->keyAxis() == this)
5257  plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5258  else
5259  plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5260  if (currentFoundRange)
5261  {
5262  if (!haveRange)
5263  newRange = plottableRange;
5264  else
5265  newRange.expand(plottableRange);
5266  haveRange = true;
5267  }
5268  }
5269  if (haveRange)
5270  {
5271  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
5272  {
5273  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
5274  if (mScaleType == stLinear)
5275  {
5276  newRange.lower = center-mRange.size()/2.0;
5277  newRange.upper = center+mRange.size()/2.0;
5278  } else // mScaleType == stLogarithmic
5279  {
5280  newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
5281  newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
5282  }
5283  }
5284  setRange(newRange);
5285  }
5286 }
5287 
5291 double QCPAxis::pixelToCoord(double value) const
5292 {
5293  if (orientation() == Qt::Horizontal)
5294  {
5295  if (mScaleType == stLinear)
5296  {
5297  if (!mRangeReversed)
5298  return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
5299  else
5300  return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
5301  } else // mScaleType == stLogarithmic
5302  {
5303  if (!mRangeReversed)
5304  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
5305  else
5306  return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
5307  }
5308  } else // orientation() == Qt::Vertical
5309  {
5310  if (mScaleType == stLinear)
5311  {
5312  if (!mRangeReversed)
5313  return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
5314  else
5315  return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
5316  } else // mScaleType == stLogarithmic
5317  {
5318  if (!mRangeReversed)
5319  return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
5320  else
5321  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
5322  }
5323  }
5324 }
5325 
5329 double QCPAxis::coordToPixel(double value) const
5330 {
5331  if (orientation() == Qt::Horizontal)
5332  {
5333  if (mScaleType == stLinear)
5334  {
5335  if (!mRangeReversed)
5336  return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5337  else
5338  return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5339  } else // mScaleType == stLogarithmic
5340  {
5341  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5342  return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
5343  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5344  return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
5345  else
5346  {
5347  if (!mRangeReversed)
5349  else
5351  }
5352  }
5353  } else // orientation() == Qt::Vertical
5354  {
5355  if (mScaleType == stLinear)
5356  {
5357  if (!mRangeReversed)
5358  return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
5359  else
5360  return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5361  } else // mScaleType == stLogarithmic
5362  {
5363  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5364  return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5365  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5366  return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
5367  else
5368  {
5369  if (!mRangeReversed)
5371  else
5373  }
5374  }
5375  }
5376 }
5377 
5388 {
5389  if (!mVisible)
5390  return spNone;
5391 
5392  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5393  return spAxis;
5394  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5395  return spTickLabels;
5396  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5397  return spAxisLabel;
5398  else
5399  return spNone;
5400 }
5401 
5402 /* inherits documentation from base class */
5403 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5404 {
5405  if (!mParentPlot) return -1;
5406  SelectablePart part = getPartAt(pos);
5407  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5408  return -1;
5409 
5410  if (details)
5411  details->setValue(part);
5412  return mParentPlot->selectionTolerance()*0.99;
5413 }
5414 
5422 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
5423 {
5424  QList<QCPAbstractPlottable*> result;
5425  if (!mParentPlot) return result;
5426 
5427  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
5428  {
5429  if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
5430  result.append(mParentPlot->mPlottables.at(i));
5431  }
5432  return result;
5433 }
5434 
5440 QList<QCPGraph*> QCPAxis::graphs() const
5441 {
5442  QList<QCPGraph*> result;
5443  if (!mParentPlot) return result;
5444 
5445  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
5446  {
5447  if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
5448  result.append(mParentPlot->mGraphs.at(i));
5449  }
5450  return result;
5451 }
5452 
5459 QList<QCPAbstractItem*> QCPAxis::items() const
5460 {
5461  QList<QCPAbstractItem*> result;
5462  if (!mParentPlot) return result;
5463 
5464  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
5465  {
5466  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
5467  for (int posId=0; posId<positions.size(); ++posId)
5468  {
5469  if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
5470  {
5471  result.append(mParentPlot->mItems.at(itemId));
5472  break;
5473  }
5474  }
5475  }
5476  return result;
5477 }
5478 
5484 {
5485  switch (side)
5486  {
5487  case QCP::msLeft: return atLeft;
5488  case QCP::msRight: return atRight;
5489  case QCP::msTop: return atTop;
5490  case QCP::msBottom: return atBottom;
5491  default: break;
5492  }
5493  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5494  return atLeft;
5495 }
5496 
5501 {
5502  switch (type)
5503  {
5504  case atLeft: return atRight; break;
5505  case atRight: return atLeft; break;
5506  case atBottom: return atTop; break;
5507  case atTop: return atBottom; break;
5508  default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
5509  }
5510 }
5511 
5520 {
5521  if (!mParentPlot) return;
5522  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
5523 
5524  // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
5525  if (mAutoTicks)
5526  {
5528  } else
5529  {
5530  emit ticksRequest();
5531  }
5532 
5534  if (mTickVector.isEmpty())
5535  {
5536  mSubTickVector.clear();
5537  return;
5538  }
5539 
5540  // generate subticks between ticks:
5541  mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
5542  if (mSubTickCount > 0)
5543  {
5544  double subTickStep = 0;
5545  double subTickPosition = 0;
5546  int subTickIndex = 0;
5547  bool done = false;
5550  for (int i=lowTick+1; i<=highTick; ++i)
5551  {
5552  subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
5553  for (int k=1; k<=mSubTickCount; ++k)
5554  {
5555  subTickPosition = mTickVector.at(i-1) + k*subTickStep;
5556  if (subTickPosition < mRange.lower)
5557  continue;
5558  if (subTickPosition > mRange.upper)
5559  {
5560  done = true;
5561  break;
5562  }
5563  mSubTickVector[subTickIndex] = subTickPosition;
5564  subTickIndex++;
5565  }
5566  if (done) break;
5567  }
5568  mSubTickVector.resize(subTickIndex);
5569  }
5570 
5571  // generate tick labels according to tick positions:
5572  if (mAutoTickLabels)
5573  {
5574  int vecsize = mTickVector.size();
5575  mTickVectorLabels.resize(vecsize);
5576  if (mTickLabelType == ltNumber)
5577  {
5578  for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5579  mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision);
5580  } else if (mTickLabelType == ltDateTime)
5581  {
5582  for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5583  {
5584 #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")
5585  mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5586 #else
5587  mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5588 #endif
5589  }
5590  }
5591  } else // mAutoTickLabels == false
5592  {
5593  if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
5594  {
5595  emit ticksRequest();
5596  }
5597  // make sure provided tick label vector has correct (minimal) length:
5598  if (mTickVectorLabels.size() < mTickVector.size())
5599  mTickVectorLabels.resize(mTickVector.size());
5600  }
5601 }
5602 
5613 {
5614  if (mScaleType == stLinear)
5615  {
5616  if (mAutoTickStep)
5617  {
5618  // Generate tick positions according to linear scaling:
5619  mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
5620  double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5621  double tickStepMantissa = mTickStep/magnitudeFactor;
5622  if (tickStepMantissa < 5)
5623  {
5624  // round digit after decimal point to 0.5
5625  mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
5626  } else
5627  {
5628  // round to first digit in multiples of 2
5629  mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
5630  }
5631  }
5632  if (mAutoSubTicks)
5634  // Generate tick positions according to mTickStep:
5635  qint64 firstStep = floor(mRange.lower/mTickStep); // do not use qFloor here, or we'll lose 64 bit precision
5636  qint64 lastStep = ceil(mRange.upper/mTickStep); // do not use qCeil here, or we'll lose 64 bit precision
5637  int tickcount = lastStep-firstStep+1;
5638  if (tickcount < 0) tickcount = 0;
5639  mTickVector.resize(tickcount);
5640  for (int i=0; i<tickcount; ++i)
5641  mTickVector[i] = (firstStep+i)*mTickStep;
5642  } else // mScaleType == stLogarithmic
5643  {
5644  // Generate tick positions according to logbase scaling:
5645  if (mRange.lower > 0 && mRange.upper > 0) // positive range
5646  {
5647  double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
5648  double currentMag = lowerMag;
5649  mTickVector.clear();
5650  mTickVector.append(currentMag);
5651  while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5652  {
5653  currentMag *= mScaleLogBase;
5654  mTickVector.append(currentMag);
5655  }
5656  } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5657  {
5658  double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
5659  double currentMag = lowerMag;
5660  mTickVector.clear();
5661  mTickVector.append(currentMag);
5662  while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5663  {
5664  currentMag /= mScaleLogBase;
5665  mTickVector.append(currentMag);
5666  }
5667  } else // invalid range for logarithmic scale, because lower and upper have different sign
5668  {
5669  mTickVector.clear();
5670  qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
5671  }
5672  }
5673 }
5674 
5689 {
5690  int result = mSubTickCount; // default to current setting, if no proper value can be found
5691 
5692  // get mantissa of tickstep:
5693  double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5694  double tickStepMantissa = tickStep/magnitudeFactor;
5695 
5696  // separate integer and fractional part of mantissa:
5697  double epsilon = 0.01;
5698  double intPartf;
5699  int intPart;
5700  double fracPart = modf(tickStepMantissa, &intPartf);
5701  intPart = intPartf;
5702 
5703  // handle cases with (almost) integer mantissa:
5704  if (fracPart < epsilon || 1.0-fracPart < epsilon)
5705  {
5706  if (1.0-fracPart < epsilon)
5707  ++intPart;
5708  switch (intPart)
5709  {
5710  case 1: result = 4; break; // 1.0 -> 0.2 substep
5711  case 2: result = 3; break; // 2.0 -> 0.5 substep
5712  case 3: result = 2; break; // 3.0 -> 1.0 substep
5713  case 4: result = 3; break; // 4.0 -> 1.0 substep
5714  case 5: result = 4; break; // 5.0 -> 1.0 substep
5715  case 6: result = 2; break; // 6.0 -> 2.0 substep
5716  case 7: result = 6; break; // 7.0 -> 1.0 substep
5717  case 8: result = 3; break; // 8.0 -> 2.0 substep
5718  case 9: result = 2; break; // 9.0 -> 3.0 substep
5719  }
5720  } else
5721  {
5722  // handle cases with significantly fractional mantissa:
5723  if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5724  {
5725  switch (intPart)
5726  {
5727  case 1: result = 2; break; // 1.5 -> 0.5 substep
5728  case 2: result = 4; break; // 2.5 -> 0.5 substep
5729  case 3: result = 4; break; // 3.5 -> 0.7 substep
5730  case 4: result = 2; break; // 4.5 -> 1.5 substep
5731  case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
5732  case 6: result = 4; break; // 6.5 -> 1.3 substep
5733  case 7: result = 2; break; // 7.5 -> 2.5 substep
5734  case 8: result = 4; break; // 8.5 -> 1.7 substep
5735  case 9: result = 4; break; // 9.5 -> 1.9 substep
5736  }
5737  }
5738  // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5739  }
5740 
5741  return result;
5742 }
5743 
5744 /* inherits documentation from base class */
5745 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
5746 {
5747  Q_UNUSED(event)
5748  SelectablePart part = details.value<SelectablePart>();
5749  if (mSelectableParts.testFlag(part))
5750  {
5751  SelectableParts selBefore = mSelectedParts;
5752  setSelectedParts(additive ? mSelectedParts^part : part);
5753  if (selectionStateChanged)
5754  *selectionStateChanged = mSelectedParts != selBefore;
5755  }
5756 }
5757 
5758 /* inherits documentation from base class */
5759 void QCPAxis::deselectEvent(bool *selectionStateChanged)
5760 {
5761  SelectableParts selBefore = mSelectedParts;
5763  if (selectionStateChanged)
5764  *selectionStateChanged = mSelectedParts != selBefore;
5765 }
5766 
5781 {
5783 }
5784 
5791 {
5792  const int lowTick = mLowestVisibleTick;
5793  const int highTick = mHighestVisibleTick;
5794  QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5795  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5796  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5797  tickPositions.reserve(highTick-lowTick+1);
5798  tickLabels.reserve(highTick-lowTick+1);
5799  subTickPositions.reserve(mSubTickVector.size());
5800 
5801  if (mTicks)
5802  {
5803  for (int i=lowTick; i<=highTick; ++i)
5804  {
5805  tickPositions.append(coordToPixel(mTickVector.at(i)));
5806  if (mTickLabels)
5807  tickLabels.append(mTickVectorLabels.at(i));
5808  }
5809 
5810  if (mSubTickCount > 0)
5811  {
5812  const int subTickCount = mSubTickVector.size();
5813  for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
5814  subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5815  }
5816  }
5817  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
5818  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
5833  mAxisPainter->tickPositions = tickPositions;
5835  mAxisPainter->subTickPositions = subTickPositions;
5836  mAxisPainter->draw(painter);
5837 }
5838 
5855 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5856 {
5857  bool lowFound = false;
5858  bool highFound = false;
5859  lowIndex = 0;
5860  highIndex = -1;
5861 
5862  for (int i=0; i < mTickVector.size(); ++i)
5863  {
5864  if (mTickVector.at(i) >= mRange.lower)
5865  {
5866  lowFound = true;
5867  lowIndex = i;
5868  break;
5869  }
5870  }
5871  for (int i=mTickVector.size()-1; i >= 0; --i)
5872  {
5873  if (mTickVector.at(i) <= mRange.upper)
5874  {
5875  highFound = true;
5876  highIndex = i;
5877  break;
5878  }
5879  }
5880 
5881  if (!lowFound && highFound)
5882  lowIndex = highIndex+1;
5883  else if (lowFound && !highFound)
5884  highIndex = lowIndex-1;
5885 }
5886 
5895 double QCPAxis::baseLog(double value) const
5896 {
5897  return qLn(value)*mScaleLogBaseLogInv;
5898 }
5899 
5907 double QCPAxis::basePow(double value) const
5908 {
5909  return qPow(mScaleLogBase, value);
5910 }
5911 
5918 {
5919  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5920 }
5921 
5928 {
5929  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5930 }
5931 
5938 {
5940 }
5941 
5948 {
5950 }
5951 
5958 {
5960 }
5961 
5968 {
5970 }
5971 
5978 {
5980 }
5981 
5997 {
5998  if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
5999  return 0;
6000 
6001  if (mCachedMarginValid)
6002  return mCachedMargin;
6003 
6004  // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6005  int margin = 0;
6006 
6007  int lowTick, highTick;
6008  visibleTickBounds(lowTick, highTick);
6009  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
6010  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6011  tickPositions.reserve(highTick-lowTick+1);
6012  tickLabels.reserve(highTick-lowTick+1);
6013  if (mTicks)
6014  {
6015  for (int i=lowTick; i<=highTick; ++i)
6016  {
6017  tickPositions.append(coordToPixel(mTickVector.at(i)));
6018  if (mTickLabels)
6019  tickLabels.append(mTickVectorLabels.at(i));
6020  }
6021  }
6022  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6023  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6030  mAxisPainter->tickPositions = tickPositions;
6032  margin += mAxisPainter->size();
6033  margin += mPadding;
6034 
6035  mCachedMargin = margin;
6036  mCachedMarginValid = true;
6037  return margin;
6038 }
6039 
6040 /* inherits documentation from base class */
6042 {
6043  return QCP::iSelectAxes;
6044 }
6045 
6046 
6050 
6068  type(QCPAxis::atLeft),
6069  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6070  lowerEnding(QCPLineEnding::esNone),
6071  upperEnding(QCPLineEnding::esNone),
6072  labelPadding(0),
6073  tickLabelPadding(0),
6074  tickLabelRotation(0),
6076  substituteExponent(true),
6077  numberMultiplyCross(false),
6078  tickLengthIn(5),
6079  tickLengthOut(0),
6080  subTickLengthIn(2),
6081  subTickLengthOut(0),
6082  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6083  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6084  offset(0),
6085  abbreviateDecimalPowers(false),
6086  reversedEndings(false),
6087  mParentPlot(parentPlot),
6088  mLabelCache(16) // cache at most 16 (tick) labels
6089 {
6090 }
6091 
6093 {
6094 }
6095 
6104 {
6105  QByteArray newHash = generateLabelParameterHash();
6106  if (newHash != mLabelParameterHash)
6107  {
6108  mLabelCache.clear();
6109  mLabelParameterHash = newHash;
6110  }
6111 
6112  QPoint origin;
6113  switch (type)
6114  {
6115  case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
6116  case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
6117  case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break;
6118  case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
6119  }
6120 
6121  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
6122  switch (type)
6123  {
6124  case QCPAxis::atTop: yCor = -1; break;
6125  case QCPAxis::atRight: xCor = 1; break;
6126  default: break;
6127  }
6128  int margin = 0;
6129  // draw baseline:
6130  QLineF baseLine;
6131  painter->setPen(basePen);
6132  if (QCPAxis::orientation(type) == Qt::Horizontal)
6133  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
6134  else
6135  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
6136  if (reversedEndings)
6137  baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
6138  painter->drawLine(baseLine);
6139 
6140  // draw ticks:
6141  if (!tickPositions.isEmpty())
6142  {
6143  painter->setPen(tickPen);
6144  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
6145  if (QCPAxis::orientation(type) == Qt::Horizontal)
6146  {
6147  for (int i=0; i<tickPositions.size(); ++i)
6148  painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
6149  } else
6150  {
6151  for (int i=0; i<tickPositions.size(); ++i)
6152  painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
6153  }
6154  }
6155 
6156  // draw subticks:
6157  if (!subTickPositions.isEmpty())
6158  {
6159  painter->setPen(subTickPen);
6160  // direction of ticks ("inward" is right for left axis and left for right axis)
6161  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6162  if (QCPAxis::orientation(type) == Qt::Horizontal)
6163  {
6164  for (int i=0; i<subTickPositions.size(); ++i)
6165  painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
6166  } else
6167  {
6168  for (int i=0; i<subTickPositions.size(); ++i)
6169  painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
6170  }
6171  }
6172  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6173 
6174  // draw axis base endings:
6175  bool antialiasingBackup = painter->antialiasing();
6176  painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
6177  painter->setBrush(QBrush(basePen.color()));
6178  QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6180  lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6182  upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
6183  painter->setAntialiasing(antialiasingBackup);
6184 
6185  // tick labels:
6186  QRect oldClipRect;
6187  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
6188  {
6189  oldClipRect = painter->clipRegion().boundingRect();
6190  painter->setClipRect(axisRect);
6191  }
6192  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
6193  if (!tickLabels.isEmpty())
6194  {
6196  margin += tickLabelPadding;
6197  painter->setFont(tickLabelFont);
6198  painter->setPen(QPen(tickLabelColor));
6199  const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6200  int distanceToAxis = margin;
6202  distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6203  for (int i=0; i<maxLabelIndex; ++i)
6204  placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
6206  margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
6207  }
6209  painter->setClipRect(oldClipRect);
6210 
6211  // axis label:
6212  QRect labelBounds;
6213  if (!label.isEmpty())
6214  {
6215  margin += labelPadding;
6216  painter->setFont(labelFont);
6217  painter->setPen(QPen(labelColor));
6218  labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
6219  if (type == QCPAxis::atLeft)
6220  {
6221  QTransform oldTransform = painter->transform();
6222  painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
6223  painter->rotate(-90);
6224  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6225  painter->setTransform(oldTransform);
6226  }
6227  else if (type == QCPAxis::atRight)
6228  {
6229  QTransform oldTransform = painter->transform();
6230  painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
6231  painter->rotate(90);
6232  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6233  painter->setTransform(oldTransform);
6234  }
6235  else if (type == QCPAxis::atTop)
6236  painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6237  else if (type == QCPAxis::atBottom)
6238  painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6239  }
6240 
6241  // set selection boxes:
6242  int selectionTolerance = 0;
6243  if (mParentPlot)
6244  selectionTolerance = mParentPlot->selectionTolerance();
6245  else
6246  qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6247  int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6248  int selAxisInSize = selectionTolerance;
6249  int selTickLabelSize;
6250  int selTickLabelOffset;
6252  {
6253  selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6254  selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
6255  } else
6256  {
6257  selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6258  selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6259  }
6260  int selLabelSize = labelBounds.height();
6261  int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
6262  if (type == QCPAxis::atLeft)
6263  {
6264  mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
6265  mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
6266  mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
6267  } else if (type == QCPAxis::atRight)
6268  {
6269  mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
6270  mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
6271  mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
6272  } else if (type == QCPAxis::atTop)
6273  {
6274  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
6275  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
6276  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
6277  } else if (type == QCPAxis::atBottom)
6278  {
6279  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
6280  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
6281  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
6282  }
6283  mAxisSelectionBox = mAxisSelectionBox.normalized();
6285  mLabelSelectionBox = mLabelSelectionBox.normalized();
6286  // draw hitboxes for debug purposes:
6287  //painter->setBrush(Qt::NoBrush);
6288  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
6289 }
6290 
6297 {
6298  int result = 0;
6299 
6300  // get length of tick marks pointing outwards:
6301  if (!tickPositions.isEmpty())
6302  result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6303 
6304  // calculate size of tick labels:
6306  {
6307  QSize tickLabelsSize(0, 0);
6308  if (!tickLabels.isEmpty())
6309  {
6310  for (int i=0; i<tickLabels.size(); ++i)
6311  getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6312  result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6313  result += tickLabelPadding;
6314  }
6315  }
6316 
6317  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6318  if (!label.isEmpty())
6319  {
6320  QFontMetrics fontMetrics(labelFont);
6321  QRect bounds;
6322  bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6323  result += bounds.height() + labelPadding;
6324  }
6325 
6326  return result;
6327 }
6328 
6336 {
6337  mLabelCache.clear();
6338 }
6339 
6348 {
6349  QByteArray result;
6350  result.append(QByteArray::number(tickLabelRotation));
6351  result.append(QByteArray::number((int)tickLabelSide));
6352  result.append(QByteArray::number((int)substituteExponent));
6353  result.append(QByteArray::number((int)numberMultiplyCross));
6354  result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
6355  result.append(tickLabelFont.toString().toLatin1());
6356  return result;
6357 }
6358 
6378 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
6379 {
6380  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
6381  if (text.isEmpty()) return;
6382  QSize finalSize;
6383  QPointF labelAnchor;
6384  switch (type)
6385  {
6386  case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
6387  case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
6388  case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
6389  case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
6390  }
6391  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
6392  {
6393  CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache
6394  if (!cachedLabel) // no cached label existed, create it
6395  {
6396  cachedLabel = new CachedLabel;
6397  TickLabelData labelData = getTickLabelData(painter->font(), text);
6398  cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
6399  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6400  cachedLabel->pixmap.fill(Qt::transparent);
6401  QCPPainter cachePainter(&cachedLabel->pixmap);
6402  cachePainter.setPen(painter->pen());
6403  drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6404  }
6405  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6406  bool labelClippedByBorder = false;
6408  {
6409  if (QCPAxis::orientation(type) == Qt::Horizontal)
6410  labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
6411  else
6412  labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
6413  }
6414  if (!labelClippedByBorder)
6415  {
6416  painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
6417  finalSize = cachedLabel->pixmap.size();
6418  }
6419  mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created
6420  } else // label caching disabled, draw text directly on surface:
6421  {
6422  TickLabelData labelData = getTickLabelData(painter->font(), text);
6423  QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6424  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6425  bool labelClippedByBorder = false;
6427  {
6428  if (QCPAxis::orientation(type) == Qt::Horizontal)
6429  labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
6430  else
6431  labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
6432  }
6433  if (!labelClippedByBorder)
6434  {
6435  drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6436  finalSize = labelData.rotatedTotalBounds.size();
6437  }
6438  }
6439 
6440  // expand passed tickLabelsSize if current tick label is larger:
6441  if (finalSize.width() > tickLabelsSize->width())
6442  tickLabelsSize->setWidth(finalSize.width());
6443  if (finalSize.height() > tickLabelsSize->height())
6444  tickLabelsSize->setHeight(finalSize.height());
6445 }
6446 
6456 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
6457 {
6458  // backup painter settings that we're about to change:
6459  QTransform oldTransform = painter->transform();
6460  QFont oldFont = painter->font();
6461 
6462  // transform painter to position/rotation:
6463  painter->translate(x, y);
6464  if (!qFuzzyIsNull(tickLabelRotation))
6465  painter->rotate(tickLabelRotation);
6466 
6467  // draw text:
6468  if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6469  {
6470  painter->setFont(labelData.baseFont);
6471  painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6472  painter->setFont(labelData.expFont);
6473  painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
6474  } else
6475  {
6476  painter->setFont(labelData.baseFont);
6477  painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6478  }
6479 
6480  // reset painter settings to what it was before:
6481  painter->setTransform(oldTransform);
6482  painter->setFont(oldFont);
6483 }
6484 
6494 {
6495  TickLabelData result;
6496 
6497  // determine whether beautiful decimal powers should be used
6498  bool useBeautifulPowers = false;
6499  int ePos = -1;
6500  if (substituteExponent)
6501  {
6502  ePos = text.indexOf(QLatin1Char('e'));
6503  if (ePos > -1)
6504  useBeautifulPowers = true;
6505  }
6506 
6507  // calculate text bounding rects and do string preparation for beautiful decimal powers:
6508  result.baseFont = font;
6509  if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
6510  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
6511  if (useBeautifulPowers)
6512  {
6513  // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6514  result.basePart = text.left(ePos);
6515  // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6516  if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
6517  result.basePart = QLatin1String("10");
6518  else
6519  result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
6520  result.expPart = text.mid(ePos+1);
6521  // clip "+" and leading zeros off expPart:
6522  while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
6523  result.expPart.remove(1, 1);
6524  if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
6525  result.expPart.remove(0, 1);
6526  // prepare smaller font for exponent:
6527  result.expFont = font;
6528  if (result.expFont.pointSize() > 0)
6529  result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6530  else
6531  result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
6532  // calculate bounding rects of base part, exponent part and total one:
6533  result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6534  result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6535  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
6536  } else // useBeautifulPowers == false
6537  {
6538  result.basePart = text;
6539  result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6540  }
6541  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
6542 
6543  // calculate possibly different bounding rect after rotation:
6544  result.rotatedTotalBounds = result.totalBounds;
6545  if (!qFuzzyIsNull(tickLabelRotation))
6546  {
6547  QTransform transform;
6548  transform.rotate(tickLabelRotation);
6549  result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6550  }
6551 
6552  return result;
6553 }
6554 
6566 {
6567  /*
6568  calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6569  explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6570  horizontally under the corresponding tick is always on the label side that is closer to the
6571  axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6572  is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6573  will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6574  time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6575  labels.
6576  */
6577  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6578  bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6579  double radians = tickLabelRotation/180.0*M_PI;
6580  int x=0, y=0;
6581  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
6582  {
6583  if (doRotation)
6584  {
6585  if (tickLabelRotation > 0)
6586  {
6587  x = -qCos(radians)*labelData.totalBounds.width();
6588  y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6589  } else
6590  {
6591  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6592  y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6593  }
6594  } else
6595  {
6596  x = -labelData.totalBounds.width();
6597  y = -labelData.totalBounds.height()/2.0;
6598  }
6599  } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
6600  {
6601  if (doRotation)
6602  {
6603  if (tickLabelRotation > 0)
6604  {
6605  x = +qSin(radians)*labelData.totalBounds.height();
6606  y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6607  } else
6608  {
6609  x = 0;
6610  y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6611  }
6612  } else
6613  {
6614  x = 0;
6615  y = -labelData.totalBounds.height()/2.0;
6616  }
6617  } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
6618  {
6619  if (doRotation)
6620  {
6621  if (tickLabelRotation > 0)
6622  {
6623  x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6624  y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6625  } else
6626  {
6627  x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6628  y = -qCos(-radians)*labelData.totalBounds.height();
6629  }
6630  } else
6631  {
6632  x = -labelData.totalBounds.width()/2.0;
6633  y = -labelData.totalBounds.height();
6634  }
6635  } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
6636  {
6637  if (doRotation)
6638  {
6639  if (tickLabelRotation > 0)
6640  {
6641  x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6642  y = 0;
6643  } else
6644  {
6645  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6646  y = +qSin(-radians)*labelData.totalBounds.width();
6647  }
6648  } else
6649  {
6650  x = -labelData.totalBounds.width()/2.0;
6651  y = 0;
6652  }
6653  }
6654 
6655  return QPointF(x, y);
6656 }
6657 
6665 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
6666 {
6667  // note: this function must return the same tick label sizes as the placeTickLabel function.
6668  QSize finalSize;
6669  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6670  {
6671  const CachedLabel *cachedLabel = mLabelCache.object(text);
6672  finalSize = cachedLabel->pixmap.size();
6673  } else // label caching disabled or no label with this text cached:
6674  {
6675  TickLabelData labelData = getTickLabelData(font, text);
6676  finalSize = labelData.rotatedTotalBounds.size();
6677  }
6678 
6679  // expand passed tickLabelsSize if current tick label is larger:
6680  if (finalSize.width() > tickLabelsSize->width())
6681  tickLabelsSize->setWidth(finalSize.width());
6682  if (finalSize.height() > tickLabelsSize->height())
6683  tickLabelsSize->setHeight(finalSize.height());
6684 }
6685 
6686 
6690 
6759 /* start of documentation of pure virtual functions */
6760 
6809 /* end of documentation of pure virtual functions */
6810 /* start of documentation of signals */
6811 
6825 /* end of documentation of signals */
6826 
6839  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
6840  mName(),
6841  mAntialiasedFill(true),
6842  mAntialiasedScatters(true),
6843  mAntialiasedErrorBars(false),
6844  mPen(Qt::black),
6845  mSelectedPen(Qt::black),
6846  mBrush(Qt::NoBrush),
6847  mSelectedBrush(Qt::NoBrush),
6848  mKeyAxis(keyAxis),
6849  mValueAxis(valueAxis),
6850  mSelectable(true),
6851  mSelected(false)
6852 {
6853  if (keyAxis->parentPlot() != valueAxis->parentPlot())
6854  qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
6855  if (keyAxis->orientation() == valueAxis->orientation())
6856  qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
6857 }
6858 
6864 {
6865  mName = name;
6866 }
6867 
6875 {
6876  mAntialiasedFill = enabled;
6877 }
6878 
6886 {
6887  mAntialiasedScatters = enabled;
6888 }
6889 
6897 {
6898  mAntialiasedErrorBars = enabled;
6899 }
6900 
6901 
6911 {
6912  mPen = pen;
6913 }
6914 
6922 {
6923  mSelectedPen = pen;
6924 }
6925 
6936 {
6937  mBrush = brush;
6938 }
6939 
6947 {
6949 }
6950 
6963 {
6964  mKeyAxis = axis;
6965 }
6966 
6979 {
6980  mValueAxis = axis;
6981 }
6982 
6993 {
6994  if (mSelectable != selectable)
6995  {
6998  }
6999 }
7000 
7016 {
7017  if (mSelected != selected)
7018  {
7019  mSelected = selected;
7021  }
7022 }
7023 
7037 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
7038 {
7039  rescaleKeyAxis(onlyEnlarge);
7040  rescaleValueAxis(onlyEnlarge);
7041 }
7042 
7048 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
7049 {
7050  QCPAxis *keyAxis = mKeyAxis.data();
7051  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
7052 
7053  SignDomain signDomain = sdBoth;
7054  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
7055  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7056 
7057  bool foundRange;
7058  QCPRange newRange = getKeyRange(foundRange, signDomain);
7059  if (foundRange)
7060  {
7061  if (onlyEnlarge)
7062  newRange.expand(keyAxis->range());
7063  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
7064  {
7065  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
7066  if (keyAxis->scaleType() == QCPAxis::stLinear)
7067  {
7068  newRange.lower = center-keyAxis->range().size()/2.0;
7069  newRange.upper = center+keyAxis->range().size()/2.0;
7070  } else // scaleType() == stLogarithmic
7071  {
7072  newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7073  newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7074  }
7075  }
7076  keyAxis->setRange(newRange);
7077  }
7078 }
7079 
7088 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
7089 {
7090  QCPAxis *valueAxis = mValueAxis.data();
7091  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
7092 
7093  SignDomain signDomain = sdBoth;
7094  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
7095  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7096 
7097  bool foundRange;
7098  QCPRange newRange = getValueRange(foundRange, signDomain);
7099  if (foundRange)
7100  {
7101  if (onlyEnlarge)
7102  newRange.expand(valueAxis->range());
7103  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
7104  {
7105  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
7106  if (valueAxis->scaleType() == QCPAxis::stLinear)
7107  {
7108  newRange.lower = center-valueAxis->range().size()/2.0;
7109  newRange.upper = center+valueAxis->range().size()/2.0;
7110  } else // scaleType() == stLogarithmic
7111  {
7112  newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7113  newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7114  }
7115  }
7116  valueAxis->setRange(newRange);
7117  }
7118 }
7119 
7133 {
7134  if (!mParentPlot || !mParentPlot->legend)
7135  return false;
7136 
7138  {
7140  return true;
7141  } else
7142  return false;
7143 }
7144 
7156 {
7157  if (!mParentPlot->legend)
7158  return false;
7159 
7161  return mParentPlot->legend->removeItem(lip);
7162  else
7163  return false;
7164 }
7165 
7166 /* inherits documentation from base class */
7168 {
7169  if (mKeyAxis && mValueAxis)
7170  return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
7171  else
7172  return QRect();
7173 }
7174 
7175 /* inherits documentation from base class */
7177 {
7178  return QCP::iSelectPlottables;
7179 }
7180 
7191 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
7192 {
7193  QCPAxis *keyAxis = mKeyAxis.data();
7194  QCPAxis *valueAxis = mValueAxis.data();
7195  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7196 
7197  if (keyAxis->orientation() == Qt::Horizontal)
7198  {
7199  x = keyAxis->coordToPixel(key);
7200  y = valueAxis->coordToPixel(value);
7201  } else
7202  {
7203  y = keyAxis->coordToPixel(key);
7204  x = valueAxis->coordToPixel(value);
7205  }
7206 }
7207 
7213 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
7214 {
7215  QCPAxis *keyAxis = mKeyAxis.data();
7216  QCPAxis *valueAxis = mValueAxis.data();
7217  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
7218 
7219  if (keyAxis->orientation() == Qt::Horizontal)
7220  return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7221  else
7222  return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7223 }
7224 
7235 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
7236 {
7237  QCPAxis *keyAxis = mKeyAxis.data();
7238  QCPAxis *valueAxis = mValueAxis.data();
7239  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7240 
7241  if (keyAxis->orientation() == Qt::Horizontal)
7242  {
7243  key = keyAxis->pixelToCoord(x);
7244  value = valueAxis->pixelToCoord(y);
7245  } else
7246  {
7247  key = keyAxis->pixelToCoord(y);
7248  value = valueAxis->pixelToCoord(x);
7249  }
7250 }
7251 
7257 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
7258 {
7259  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7260 }
7261 
7268 {
7269  return mSelected ? mSelectedPen : mPen;
7270 }
7271 
7278 {
7279  return mSelected ? mSelectedBrush : mBrush;
7280 }
7281 
7296 {
7298 }
7299 
7312 {
7314 }
7315 
7328 {
7330 }
7331 
7344 {
7346 }
7347 
7358 double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
7359 {
7360  QVector2D a(start);
7361  QVector2D b(end);
7362  QVector2D p(point);
7363  QVector2D v(b-a);
7364 
7365  double vLengthSqr = v.lengthSquared();
7366  if (!qFuzzyIsNull(vLengthSqr))
7367  {
7368  double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
7369  if (mu < 0)
7370  return (a-p).lengthSquared();
7371  else if (mu > 1)
7372  return (b-p).lengthSquared();
7373  else
7374  return ((a + mu*v)-p).lengthSquared();
7375  } else
7376  return (a-p).lengthSquared();
7377 }
7378 
7379 /* inherits documentation from base class */
7380 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
7381 {
7382  Q_UNUSED(event)
7383  Q_UNUSED(details)
7384  if (mSelectable)
7385  {
7386  bool selBefore = mSelected;
7387  setSelected(additive ? !mSelected : true);
7388  if (selectionStateChanged)
7389  *selectionStateChanged = mSelected != selBefore;
7390  }
7391 }
7392 
7393 /* inherits documentation from base class */
7394 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
7395 {
7396  if (mSelectable)
7397  {
7398  bool selBefore = mSelected;
7399  setSelected(false);
7400  if (selectionStateChanged)
7401  *selectionStateChanged = mSelected != selBefore;
7402  }
7403 }
7404 
7405 
7409 
7431 /* start documentation of inline functions */
7432 
7443 /* end documentation of inline functions */
7444 
7450 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
7451  mName(name),
7452  mParentPlot(parentPlot),
7453  mParentItem(parentItem),
7454  mAnchorId(anchorId)
7455 {
7456 }
7457 
7459 {
7460  // unregister as parent at children:
7461  foreach (QCPItemPosition *child, mChildrenX.toList())
7462  {
7463  if (child->parentAnchorX() == this)
7464  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7465  }
7466  foreach (QCPItemPosition *child, mChildrenY.toList())
7467  {
7468  if (child->parentAnchorY() == this)
7469  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7470  }
7471 }
7472 
7480 {
7481  if (mParentItem)
7482  {
7483  if (mAnchorId > -1)
7484  {
7486  } else
7487  {
7488  qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7489  return QPointF();
7490  }
7491  } else
7492  {
7493  qDebug() << Q_FUNC_INFO << "no parent item set";
7494  return QPointF();
7495  }
7496 }
7497 
7507 {
7508  if (!mChildrenX.contains(pos))
7509  mChildrenX.insert(pos);
7510  else
7511  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7512 }
7513 
7521 {
7522  if (!mChildrenX.remove(pos))
7523  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7524 }
7525 
7535 {
7536  if (!mChildrenY.contains(pos))
7537  mChildrenY.insert(pos);
7538  else
7539  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7540 }
7541 
7549 {
7550  if (!mChildrenY.remove(pos))
7551  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7552 }
7553 
7554 
7558 
7593 /* start documentation of inline functions */
7594 
7616 /* end documentation of inline functions */
7617 
7623 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
7624  QCPItemAnchor(parentPlot, parentItem, name),
7625  mPositionTypeX(ptAbsolute),
7626  mPositionTypeY(ptAbsolute),
7627  mKey(0),
7628  mValue(0),
7629  mParentAnchorX(0),
7630  mParentAnchorY(0)
7631 {
7632 }
7633 
7635 {
7636  // unregister as parent at children:
7637  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
7638  // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
7639  foreach (QCPItemPosition *child, mChildrenX.toList())
7640  {
7641  if (child->parentAnchorX() == this)
7642  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7643  }
7644  foreach (QCPItemPosition *child, mChildrenY.toList())
7645  {
7646  if (child->parentAnchorY() == this)
7647  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7648  }
7649  // unregister as child in parent:
7650  if (mParentAnchorX)
7652  if (mParentAnchorY)
7654 }
7655 
7656 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
7658 {
7659  return mAxisRect.data();
7660 }
7661 
7688 {
7689  setTypeX(type);
7690  setTypeY(type);
7691 }
7692 
7701 {
7702  if (mPositionTypeX != type)
7703  {
7704  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7705  // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7706  bool retainPixelPosition = true;
7707  if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7708  retainPixelPosition = false;
7709  if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7710  retainPixelPosition = false;
7711 
7712  QPointF pixel;
7713  if (retainPixelPosition)
7714  pixel = pixelPoint();
7715 
7716  mPositionTypeX = type;
7717 
7718  if (retainPixelPosition)
7719  setPixelPoint(pixel);
7720  }
7721 }
7722 
7731 {
7732  if (mPositionTypeY != type)
7733  {
7734  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7735  // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7736  bool retainPixelPosition = true;
7737  if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7738  retainPixelPosition = false;
7739  if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7740  retainPixelPosition = false;
7741 
7742  QPointF pixel;
7743  if (retainPixelPosition)
7744  pixel = pixelPoint();
7745 
7746  mPositionTypeY = type;
7747 
7748  if (retainPixelPosition)
7749  setPixelPoint(pixel);
7750  }
7751 }
7752 
7772 {
7773  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
7774  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
7775  return successX && successY;
7776 }
7777 
7786 {
7787  // make sure self is not assigned as parent:
7788  if (parentAnchor == this)
7789  {
7790  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7791  return false;
7792  }
7793  // make sure no recursive parent-child-relationships are created:
7794  QCPItemAnchor *currentParent = parentAnchor;
7795  while (currentParent)
7796  {
7797  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7798  {
7799  // is a QCPItemPosition, might have further parent, so keep iterating
7800  if (currentParentPos == this)
7801  {
7802  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7803  return false;
7804  }
7805  currentParent = currentParentPos->parentAnchorX();
7806  } else
7807  {
7808  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7809  // same, to prevent a position being child of an anchor which itself depends on the position,
7810  // because they're both on the same item:
7811  if (currentParent->mParentItem == mParentItem)
7812  {
7813  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7814  return false;
7815  }
7816  break;
7817  }
7818  }
7819 
7820  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7823 
7824  // save pixel position:
7825  QPointF pixelP;
7826  if (keepPixelPosition)
7827  pixelP = pixelPoint();
7828  // unregister at current parent anchor:
7829  if (mParentAnchorX)
7831  // register at new parent anchor:
7832  if (parentAnchor)
7833  parentAnchor->addChildX(this);
7835  // restore pixel position under new parent:
7836  if (keepPixelPosition)
7837  setPixelPoint(pixelP);
7838  else
7839  setCoords(0, coords().y());
7840  return true;
7841 }
7842 
7851 {
7852  // make sure self is not assigned as parent:
7853  if (parentAnchor == this)
7854  {
7855  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7856  return false;
7857  }
7858  // make sure no recursive parent-child-relationships are created:
7859  QCPItemAnchor *currentParent = parentAnchor;
7860  while (currentParent)
7861  {
7862  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7863  {
7864  // is a QCPItemPosition, might have further parent, so keep iterating
7865  if (currentParentPos == this)
7866  {
7867  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7868  return false;
7869  }
7870  currentParent = currentParentPos->parentAnchorY();
7871  } else
7872  {
7873  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7874  // same, to prevent a position being child of an anchor which itself depends on the position,
7875  // because they're both on the same item:
7876  if (currentParent->mParentItem == mParentItem)
7877  {
7878  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7879  return false;
7880  }
7881  break;
7882  }
7883  }
7884 
7885  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7888 
7889  // save pixel position:
7890  QPointF pixelP;
7891  if (keepPixelPosition)
7892  pixelP = pixelPoint();
7893  // unregister at current parent anchor:
7894  if (mParentAnchorY)
7896  // register at new parent anchor:
7897  if (parentAnchor)
7898  parentAnchor->addChildY(this);
7900  // restore pixel position under new parent:
7901  if (keepPixelPosition)
7902  setPixelPoint(pixelP);
7903  else
7904  setCoords(coords().x(), 0);
7905  return true;
7906 }
7907 
7926 {
7927  mKey = key;
7928  mValue = value;
7929 }
7930 
7936 void QCPItemPosition::setCoords(const QPointF &pos)
7937 {
7938  setCoords(pos.x(), pos.y());
7939 }
7940 
7948 {
7949  QPointF result;
7950 
7951  // determine X:
7952  switch (mPositionTypeX)
7953  {
7954  case ptAbsolute:
7955  {
7956  result.rx() = mKey;
7957  if (mParentAnchorX)
7958  result.rx() += mParentAnchorX->pixelPoint().x();
7959  break;
7960  }
7961  case ptViewportRatio:
7962  {
7963  result.rx() = mKey*mParentPlot->viewport().width();
7964  if (mParentAnchorX)
7965  result.rx() += mParentAnchorX->pixelPoint().x();
7966  else
7967  result.rx() += mParentPlot->viewport().left();
7968  break;
7969  }
7970  case ptAxisRectRatio:
7971  {
7972  if (mAxisRect)
7973  {
7974  result.rx() = mKey*mAxisRect.data()->width();
7975  if (mParentAnchorX)
7976  result.rx() += mParentAnchorX->pixelPoint().x();
7977  else
7978  result.rx() += mAxisRect.data()->left();
7979  } else
7980  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
7981  break;
7982  }
7983  case ptPlotCoords:
7984  {
7985  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
7986  result.rx() = mKeyAxis.data()->coordToPixel(mKey);
7987  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
7988  result.rx() = mValueAxis.data()->coordToPixel(mValue);
7989  else
7990  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
7991  break;
7992  }
7993  }
7994 
7995  // determine Y:
7996  switch (mPositionTypeY)
7997  {
7998  case ptAbsolute:
7999  {
8000  result.ry() = mValue;
8001  if (mParentAnchorY)
8002  result.ry() += mParentAnchorY->pixelPoint().y();
8003  break;
8004  }
8005  case ptViewportRatio:
8006  {
8007  result.ry() = mValue*mParentPlot->viewport().height();
8008  if (mParentAnchorY)
8009  result.ry() += mParentAnchorY->pixelPoint().y();
8010  else
8011  result.ry() += mParentPlot->viewport().top();
8012  break;
8013  }
8014  case ptAxisRectRatio:
8015  {
8016  if (mAxisRect)
8017  {
8018  result.ry() = mValue*mAxisRect.data()->height();
8019  if (mParentAnchorY)
8020  result.ry() += mParentAnchorY->pixelPoint().y();
8021  else
8022  result.ry() += mAxisRect.data()->top();
8023  } else
8024  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8025  break;
8026  }
8027  case ptPlotCoords:
8028  {
8029  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8030  result.ry() = mKeyAxis.data()->coordToPixel(mKey);
8031  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8032  result.ry() = mValueAxis.data()->coordToPixel(mValue);
8033  else
8034  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8035  break;
8036  }
8037  }
8038 
8039  return result;
8040 }
8041 
8048 {
8049  mKeyAxis = keyAxis;
8051 }
8052 
8059 {
8060  mAxisRect = axisRect;
8061 }
8062 
8074 {
8075  double x = pixelPoint.x();
8076  double y = pixelPoint.y();
8077 
8078  switch (mPositionTypeX)
8079  {
8080  case ptAbsolute:
8081  {
8082  if (mParentAnchorX)
8083  x -= mParentAnchorX->pixelPoint().x();
8084  break;
8085  }
8086  case ptViewportRatio:
8087  {
8088  if (mParentAnchorX)
8089  x -= mParentAnchorX->pixelPoint().x();
8090  else
8091  x -= mParentPlot->viewport().left();
8092  x /= (double)mParentPlot->viewport().width();
8093  break;
8094  }
8095  case ptAxisRectRatio:
8096  {
8097  if (mAxisRect)
8098  {
8099  if (mParentAnchorX)
8100  x -= mParentAnchorX->pixelPoint().x();
8101  else
8102  x -= mAxisRect.data()->left();
8103  x /= (double)mAxisRect.data()->width();
8104  } else
8105  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
8106  break;
8107  }
8108  case ptPlotCoords:
8109  {
8110  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8111  x = mKeyAxis.data()->pixelToCoord(x);
8112  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
8113  y = mValueAxis.data()->pixelToCoord(x);
8114  else
8115  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
8116  break;
8117  }
8118  }
8119 
8120  switch (mPositionTypeY)
8121  {
8122  case ptAbsolute:
8123  {
8124  if (mParentAnchorY)
8125  y -= mParentAnchorY->pixelPoint().y();
8126  break;
8127  }
8128  case ptViewportRatio:
8129  {
8130  if (mParentAnchorY)
8131  y -= mParentAnchorY->pixelPoint().y();
8132  else
8133  y -= mParentPlot->viewport().top();
8134  y /= (double)mParentPlot->viewport().height();
8135  break;
8136  }
8137  case ptAxisRectRatio:
8138  {
8139  if (mAxisRect)
8140  {
8141  if (mParentAnchorY)
8142  y -= mParentAnchorY->pixelPoint().y();
8143  else
8144  y -= mAxisRect.data()->top();
8145  y /= (double)mAxisRect.data()->height();
8146  } else
8147  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8148  break;
8149  }
8150  case ptPlotCoords:
8151  {
8152  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8153  x = mKeyAxis.data()->pixelToCoord(y);
8154  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8155  y = mValueAxis.data()->pixelToCoord(y);
8156  else
8157  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8158  break;
8159  }
8160  }
8161 
8162  setCoords(x, y);
8163 }
8164 
8165 
8169 
8295 /* start of documentation of inline functions */
8296 
8312 /* end of documentation of inline functions */
8313 /* start documentation of pure virtual functions */
8314 
8325 /* end documentation of pure virtual functions */
8326 /* start documentation of signals */
8327 
8333 /* end documentation of signals */
8334 
8339  QCPLayerable(parentPlot),
8340  mClipToAxisRect(false),
8341  mSelectable(true),
8342  mSelected(false)
8343 {
8344  QList<QCPAxisRect*> rects = parentPlot->axisRects();
8345  if (rects.size() > 0)
8346  {
8347  setClipToAxisRect(true);
8348  setClipAxisRect(rects.first());
8349  }
8350 }
8351 
8353 {
8354  // don't delete mPositions because every position is also an anchor and thus in mAnchors
8355  qDeleteAll(mAnchors);
8356 }
8357 
8358 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
8360 {
8361  return mClipAxisRect.data();
8362 }
8363 
8371 {
8372  mClipToAxisRect = clip;
8373  if (mClipToAxisRect)
8375 }
8376 
8384 {
8385  mClipAxisRect = rect;
8386  if (mClipToAxisRect)
8388 }
8389 
8400 {
8401  if (mSelectable != selectable)
8402  {
8405  }
8406 }
8407 
8423 {
8424  if (mSelected != selected)
8425  {
8426  mSelected = selected;
8428  }
8429 }
8430 
8441 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
8442 {
8443  for (int i=0; i<mPositions.size(); ++i)
8444  {
8445  if (mPositions.at(i)->name() == name)
8446  return mPositions.at(i);
8447  }
8448  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8449  return 0;
8450 }
8451 
8462 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8463 {
8464  for (int i=0; i<mAnchors.size(); ++i)
8465  {
8466  if (mAnchors.at(i)->name() == name)
8467  return mAnchors.at(i);
8468  }
8469  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8470  return 0;
8471 }
8472 
8481 bool QCPAbstractItem::hasAnchor(const QString &name) const
8482 {
8483  for (int i=0; i<mAnchors.size(); ++i)
8484  {
8485  if (mAnchors.at(i)->name() == name)
8486  return true;
8487  }
8488  return false;
8489 }
8490 
8501 {
8503  return mClipAxisRect.data()->rect();
8504  else
8505  return mParentPlot->viewport();
8506 }
8507 
8522 {
8524 }
8525 
8538 double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8539 {
8540  QVector2D a(start);
8541  QVector2D b(end);
8542  QVector2D p(point);
8543  QVector2D v(b-a);
8544 
8545  double vLengthSqr = v.lengthSquared();
8546  if (!qFuzzyIsNull(vLengthSqr))
8547  {
8548  double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8549  if (mu < 0)
8550  return (a-p).lengthSquared();
8551  else if (mu > 1)
8552  return (b-p).lengthSquared();
8553  else
8554  return ((a + mu*v)-p).lengthSquared();
8555  } else
8556  return (a-p).lengthSquared();
8557 }
8558 
8574 double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
8575 {
8576  double result = -1;
8577 
8578  // distance to border:
8579  QList<QLineF> lines;
8580  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
8581  << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
8582  double minDistSqr = std::numeric_limits<double>::max();
8583  for (int i=0; i<lines.size(); ++i)
8584  {
8585  double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8586  if (distSqr < minDistSqr)
8587  minDistSqr = distSqr;
8588  }
8589  result = qSqrt(minDistSqr);
8590 
8591  // filled rect, allow click inside to count as hit:
8592  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
8593  {
8594  if (rect.contains(pos))
8595  result = mParentPlot->selectionTolerance()*0.99;
8596  }
8597  return result;
8598 }
8599 
8610 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
8611 {
8612  qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
8613  return QPointF();
8614 }
8615 
8631 {
8632  if (hasAnchor(name))
8633  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8634  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8635  mPositions.append(newPosition);
8636  mAnchors.append(newPosition); // every position is also an anchor
8637  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8638  newPosition->setType(QCPItemPosition::ptPlotCoords);
8639  if (mParentPlot->axisRect())
8640  newPosition->setAxisRect(mParentPlot->axisRect());
8641  newPosition->setCoords(0, 0);
8642  return newPosition;
8643 }
8644 
8664 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
8665 {
8666  if (hasAnchor(name))
8667  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8668  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
8669  mAnchors.append(newAnchor);
8670  return newAnchor;
8671 }
8672 
8673 /* inherits documentation from base class */
8674 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8675 {
8676  Q_UNUSED(event)
8677  Q_UNUSED(details)
8678  if (mSelectable)
8679  {
8680  bool selBefore = mSelected;
8681  setSelected(additive ? !mSelected : true);
8682  if (selectionStateChanged)
8683  *selectionStateChanged = mSelected != selBefore;
8684  }
8685 }
8686 
8687 /* inherits documentation from base class */
8688 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
8689 {
8690  if (mSelectable)
8691  {
8692  bool selBefore = mSelected;
8693  setSelected(false);
8694  if (selectionStateChanged)
8695  *selectionStateChanged = mSelected != selBefore;
8696  }
8697 }
8698 
8699 /* inherits documentation from base class */
8701 {
8702  return QCP::iSelectItems;
8703 }
8704 
8705 
8710 
8723 /* start of documentation of inline functions */
8724 
8746 /* end of documentation of inline functions */
8747 /* start of documentation of signals */
8748 
8936 /* end of documentation of signals */
8937 /* start of documentation of public members */
8938 
9010 /* end of documentation of public members */
9011 
9015 QCustomPlot::QCustomPlot(QWidget *parent) :
9016  QWidget(parent),
9017  xAxis(0),
9018  yAxis(0),
9019  xAxis2(0),
9020  yAxis2(0),
9021  legend(0),
9022  mPlotLayout(0),
9023  mAutoAddPlottableToLegend(true),
9024  mAntialiasedElements(QCP::aeNone),
9025  mNotAntialiasedElements(QCP::aeNone),
9026  mInteractions(0),
9027  mSelectionTolerance(8),
9028  mNoAntialiasingOnDrag(false),
9029  mBackgroundBrush(Qt::white, Qt::SolidPattern),
9030  mBackgroundScaled(true),
9031  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9032  mCurrentLayer(0),
9033  mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
9034  mMultiSelectModifier(Qt::ControlModifier),
9035  mPaintBuffer(size()),
9036  mMouseEventElement(0),
9037  mReplotting(false)
9038 {
9039  setAttribute(Qt::WA_NoMousePropagation);
9040  setAttribute(Qt::WA_OpaquePaintEvent);
9041  setMouseTracking(true);
9042  QLocale currentLocale = locale();
9043  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9044  setLocale(currentLocale);
9045 
9046  // create initial layers:
9047  mLayers.append(new QCPLayer(this, QLatin1String("background")));
9048  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
9049  mLayers.append(new QCPLayer(this, QLatin1String("main")));
9050  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
9051  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
9053  setCurrentLayer(QLatin1String("main"));
9054 
9055  // create initial layout, axis rect and legend:
9056  mPlotLayout = new QCPLayoutGrid;
9058  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9059  mPlotLayout->setLayer(QLatin1String("main"));
9060  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9061  mPlotLayout->addElement(0, 0, defaultAxisRect);
9062  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9063  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9064  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9065  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9066  legend = new QCPLegend;
9067  legend->setVisible(false);
9068  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
9069  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9070 
9071  defaultAxisRect->setLayer(QLatin1String("background"));
9072  xAxis->setLayer(QLatin1String("axes"));
9073  yAxis->setLayer(QLatin1String("axes"));
9074  xAxis2->setLayer(QLatin1String("axes"));
9075  yAxis2->setLayer(QLatin1String("axes"));
9076  xAxis->grid()->setLayer(QLatin1String("grid"));
9077  yAxis->grid()->setLayer(QLatin1String("grid"));
9078  xAxis2->grid()->setLayer(QLatin1String("grid"));
9079  yAxis2->grid()->setLayer(QLatin1String("grid"));
9080  legend->setLayer(QLatin1String("legend"));
9081 
9082  setViewport(rect()); // needs to be called after mPlotLayout has been created
9083 
9084  replot();
9085 }
9086 
9088 {
9089  clearPlottables();
9090  clearItems();
9091 
9092  if (mPlotLayout)
9093  {
9094  delete mPlotLayout;
9095  mPlotLayout = 0;
9096  }
9097 
9098  mCurrentLayer = 0;
9099  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
9100  mLayers.clear();
9101 }
9102 
9120 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
9121 {
9123 
9124  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9126  mNotAntialiasedElements |= ~mAntialiasedElements;
9127 }
9128 
9136 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
9137 {
9138  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9139  mAntialiasedElements &= ~antialiasedElement;
9140  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9141  mAntialiasedElements |= antialiasedElement;
9142 
9143  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9145  mNotAntialiasedElements |= ~mAntialiasedElements;
9146 }
9147 
9167 {
9169 
9170  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9172  mAntialiasedElements |= ~mNotAntialiasedElements;
9173 }
9174 
9182 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
9183 {
9184  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9185  mNotAntialiasedElements &= ~notAntialiasedElement;
9186  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9187  mNotAntialiasedElements |= notAntialiasedElement;
9188 
9189  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9191  mAntialiasedElements |= ~mNotAntialiasedElements;
9192 }
9193 
9201 {
9203 }
9204 
9259 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
9260 {
9262 }
9263 
9271 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
9272 {
9273  if (!enabled && mInteractions.testFlag(interaction))
9274  mInteractions &= ~interaction;
9275  else if (enabled && !mInteractions.testFlag(interaction))
9276  mInteractions |= interaction;
9277 }
9278 
9293 {
9294  mSelectionTolerance = pixels;
9295 }
9296 
9307 {
9308  mNoAntialiasingOnDrag = enabled;
9309 }
9310 
9316 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
9317 {
9318  mPlottingHints = hints;
9319 }
9320 
9327 {
9328  QCP::PlottingHints newHints = mPlottingHints;
9329  if (!enabled)
9330  newHints &= ~hint;
9331  else
9332  newHints |= hint;
9333 
9334  if (newHints != mPlottingHints)
9335  setPlottingHints(newHints);
9336 }
9337 
9348 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
9349 {
9350  mMultiSelectModifier = modifier;
9351 }
9352 
9360 void QCustomPlot::setViewport(const QRect &rect)
9361 {
9362  mViewport = rect;
9363  if (mPlotLayout)
9365 }
9366 
9382 void QCustomPlot::setBackground(const QPixmap &pm)
9383 {
9384  mBackgroundPixmap = pm;
9385  mScaledBackgroundPixmap = QPixmap();
9386 }
9387 
9401 void QCustomPlot::setBackground(const QBrush &brush)
9402 {
9403  mBackgroundBrush = brush;
9404 }
9405 
9413 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
9414 {
9415  mBackgroundPixmap = pm;
9416  mScaledBackgroundPixmap = QPixmap();
9417  mBackgroundScaled = scaled;
9418  mBackgroundScaledMode = mode;
9419 }
9420 
9432 {
9433  mBackgroundScaled = scaled;
9434 }
9435 
9442 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
9443 {
9444  mBackgroundScaledMode = mode;
9445 }
9446 
9456 {
9457  if (index >= 0 && index < mPlottables.size())
9458  {
9459  return mPlottables.at(index);
9460  } else
9461  {
9462  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9463  return 0;
9464  }
9465 }
9466 
9475 {
9476  if (!mPlottables.isEmpty())
9477  {
9478  return mPlottables.last();
9479  } else
9480  return 0;
9481 }
9482 
9494 {
9495  if (mPlottables.contains(plottable))
9496  {
9497  qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
9498  return false;
9499  }
9500  if (plottable->parentPlot() != this)
9501  {
9502  qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
9503  return false;
9504  }
9505 
9506  mPlottables.append(plottable);
9507  // possibly add plottable to legend:
9509  plottable->addToLegend();
9510  // special handling for QCPGraphs to maintain the simple graph interface:
9511  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9512  mGraphs.append(graph);
9513  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
9514  plottable->setLayer(currentLayer());
9515  return true;
9516 }
9517 
9526 {
9527  if (!mPlottables.contains(plottable))
9528  {
9529  qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
9530  return false;
9531  }
9532 
9533  // remove plottable from legend:
9534  plottable->removeFromLegend();
9535  // special handling for QCPGraphs to maintain the simple graph interface:
9536  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9537  mGraphs.removeOne(graph);
9538  // remove plottable:
9539  delete plottable;
9540  mPlottables.removeOne(plottable);
9541  return true;
9542 }
9543 
9549 {
9550  if (index >= 0 && index < mPlottables.size())
9551  return removePlottable(mPlottables[index]);
9552  else
9553  {
9554  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9555  return false;
9556  }
9557 }
9558 
9567 {
9568  int c = mPlottables.size();
9569  for (int i=c-1; i >= 0; --i)
9571  return c;
9572 }
9573 
9580 {
9581  return mPlottables.size();
9582 }
9583 
9591 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9592 {
9593  QList<QCPAbstractPlottable*> result;
9595  {
9596  if (plottable->selected())
9597  result.append(plottable);
9598  }
9599  return result;
9600 }
9601 
9614 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
9615 {
9616  QCPAbstractPlottable *resultPlottable = 0;
9617  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9618 
9620  {
9621  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
9622  continue;
9623  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
9624  {
9625  double currentDistance = plottable->selectTest(pos, false);
9626  if (currentDistance >= 0 && currentDistance < resultDistance)
9627  {
9628  resultPlottable = plottable;
9629  resultDistance = currentDistance;
9630  }
9631  }
9632  }
9633 
9634  return resultPlottable;
9635 }
9636 
9643 {
9644  return mPlottables.contains(plottable);
9645 }
9646 
9655 QCPGraph *QCustomPlot::graph(int index) const
9656 {
9657  if (index >= 0 && index < mGraphs.size())
9658  {
9659  return mGraphs.at(index);
9660  } else
9661  {
9662  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9663  return 0;
9664  }
9665 }
9666 
9675 {
9676  if (!mGraphs.isEmpty())
9677  {
9678  return mGraphs.last();
9679  } else
9680  return 0;
9681 }
9682 
9696 {
9697  if (!keyAxis) keyAxis = xAxis;
9698  if (!valueAxis) valueAxis = yAxis;
9699  if (!keyAxis || !valueAxis)
9700  {
9701  qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
9702  return 0;
9703  }
9704  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
9705  {
9706  qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
9707  return 0;
9708  }
9709 
9710  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9711  if (addPlottable(newGraph))
9712  {
9713  newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
9714  return newGraph;
9715  } else
9716  {
9717  delete newGraph;
9718  return 0;
9719  }
9720 }
9721 
9732 {
9733  return removePlottable(graph);
9734 }
9735 
9741 {
9742  if (index >= 0 && index < mGraphs.size())
9743  return removeGraph(mGraphs[index]);
9744  else
9745  return false;
9746 }
9747 
9756 {
9757  int c = mGraphs.size();
9758  for (int i=c-1; i >= 0; --i)
9759  removeGraph(mGraphs[i]);
9760  return c;
9761 }
9762 
9769 {
9770  return mGraphs.size();
9771 }
9772 
9781 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9782 {
9783  QList<QCPGraph*> result;
9784  foreach (QCPGraph *graph, mGraphs)
9785  {
9786  if (graph->selected())
9787  result.append(graph);
9788  }
9789  return result;
9790 }
9791 
9801 {
9802  if (index >= 0 && index < mItems.size())
9803  {
9804  return mItems.at(index);
9805  } else
9806  {
9807  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9808  return 0;
9809  }
9810 }
9811 
9820 {
9821  if (!mItems.isEmpty())
9822  {
9823  return mItems.last();
9824  } else
9825  return 0;
9826 }
9827 
9837 {
9838  if (!mItems.contains(item) && item->parentPlot() == this)
9839  {
9840  mItems.append(item);
9841  return true;
9842  } else
9843  {
9844  qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
9845  return false;
9846  }
9847 }
9848 
9857 {
9858  if (mItems.contains(item))
9859  {
9860  delete item;
9861  mItems.removeOne(item);
9862  return true;
9863  } else
9864  {
9865  qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
9866  return false;
9867  }
9868 }
9869 
9875 {
9876  if (index >= 0 && index < mItems.size())
9877  return removeItem(mItems[index]);
9878  else
9879  {
9880  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9881  return false;
9882  }
9883 }
9884 
9893 {
9894  int c = mItems.size();
9895  for (int i=c-1; i >= 0; --i)
9896  removeItem(mItems[i]);
9897  return c;
9898 }
9899 
9906 {
9907  return mItems.size();
9908 }
9909 
9915 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9916 {
9917  QList<QCPAbstractItem*> result;
9918  foreach (QCPAbstractItem *item, mItems)
9919  {
9920  if (item->selected())
9921  result.append(item);
9922  }
9923  return result;
9924 }
9925 
9939 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
9940 {
9941  QCPAbstractItem *resultItem = 0;
9942  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9943 
9944  foreach (QCPAbstractItem *item, mItems)
9945  {
9946  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
9947  continue;
9948  if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
9949  {
9950  double currentDistance = item->selectTest(pos, false);
9951  if (currentDistance >= 0 && currentDistance < resultDistance)
9952  {
9953  resultItem = item;
9954  resultDistance = currentDistance;
9955  }
9956  }
9957  }
9958 
9959  return resultItem;
9960 }
9961 
9968 {
9969  return mItems.contains(item);
9970 }
9971 
9980 QCPLayer *QCustomPlot::layer(const QString &name) const
9981 {
9982  foreach (QCPLayer *layer, mLayers)
9983  {
9984  if (layer->name() == name)
9985  return layer;
9986  }
9987  return 0;
9988 }
9989 
9996 QCPLayer *QCustomPlot::layer(int index) const
9997 {
9998  if (index >= 0 && index < mLayers.size())
9999  {
10000  return mLayers.at(index);
10001  } else
10002  {
10003  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10004  return 0;
10005  }
10006 }
10007 
10012 {
10013  return mCurrentLayer;
10014 }
10015 
10026 bool QCustomPlot::setCurrentLayer(const QString &name)
10027 {
10028  if (QCPLayer *newCurrentLayer = layer(name))
10029  {
10030  return setCurrentLayer(newCurrentLayer);
10031  } else
10032  {
10033  qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10034  return false;
10035  }
10036 }
10037 
10047 {
10048  if (!mLayers.contains(layer))
10049  {
10050  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10051  return false;
10052  }
10053 
10054  mCurrentLayer = layer;
10055  return true;
10056 }
10057 
10064 {
10065  return mLayers.size();
10066 }
10067 
10081 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10082 {
10083  if (!otherLayer)
10084  otherLayer = mLayers.last();
10085  if (!mLayers.contains(otherLayer))
10086  {
10087  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10088  return false;
10089  }
10090  if (layer(name))
10091  {
10092  qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10093  return false;
10094  }
10095 
10096  QCPLayer *newLayer = new QCPLayer(this, name);
10097  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
10099  return true;
10100 }
10101 
10117 {
10118  if (!mLayers.contains(layer))
10119  {
10120  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10121  return false;
10122  }
10123  if (mLayers.size() < 2)
10124  {
10125  qDebug() << Q_FUNC_INFO << "can't remove last layer";
10126  return false;
10127  }
10128 
10129  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
10130  int removedIndex = layer->index();
10131  bool isFirstLayer = removedIndex==0;
10132  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
10133  QList<QCPLayerable*> children = layer->children();
10134  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
10135  {
10136  for (int i=children.size()-1; i>=0; --i)
10137  children.at(i)->moveToLayer(targetLayer, true);
10138  } else // append normally
10139  {
10140  for (int i=0; i<children.size(); ++i)
10141  children.at(i)->moveToLayer(targetLayer, false);
10142  }
10143  // if removed layer is current layer, change current layer to layer below/above:
10144  if (layer == mCurrentLayer)
10145  setCurrentLayer(targetLayer);
10146  // remove layer:
10147  delete layer;
10148  mLayers.removeOne(layer);
10150  return true;
10151 }
10152 
10163 {
10164  if (!mLayers.contains(layer))
10165  {
10166  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10167  return false;
10168  }
10169  if (!mLayers.contains(otherLayer))
10170  {
10171  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10172  return false;
10173  }
10174 
10175  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
10177  return true;
10178 }
10179 
10190 {
10191  return axisRects().size();
10192 }
10193 
10204 {
10205  const QList<QCPAxisRect*> rectList = axisRects();
10206  if (index >= 0 && index < rectList.size())
10207  {
10208  return rectList.at(index);
10209  } else
10210  {
10211  qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10212  return 0;
10213  }
10214 }
10215 
10221 QList<QCPAxisRect*> QCustomPlot::axisRects() const
10222 {
10223  QList<QCPAxisRect*> result;
10224  QStack<QCPLayoutElement*> elementStack;
10225  if (mPlotLayout)
10226  elementStack.push(mPlotLayout);
10227 
10228  while (!elementStack.isEmpty())
10229  {
10230  foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
10231  {
10232  if (element)
10233  {
10234  elementStack.push(element);
10235  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10236  result.append(ar);
10237  }
10238  }
10239  }
10240 
10241  return result;
10242 }
10243 
10254 {
10255  QCPLayoutElement *currentElement = mPlotLayout;
10256  bool searchSubElements = true;
10257  while (searchSubElements && currentElement)
10258  {
10259  searchSubElements = false;
10260  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10261  {
10262  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10263  {
10264  currentElement = subElement;
10265  searchSubElements = true;
10266  break;
10267  }
10268  }
10269  }
10270  return currentElement;
10271 }
10272 
10280 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10281 {
10282  QList<QCPAxis*> result, allAxes;
10283  foreach (QCPAxisRect *rect, axisRects())
10284  allAxes << rect->axes();
10285 
10286  foreach (QCPAxis *axis, allAxes)
10287  {
10288  if (axis->selectedParts() != QCPAxis::spNone)
10289  result.append(axis);
10290  }
10291 
10292  return result;
10293 }
10294 
10302 QList<QCPLegend*> QCustomPlot::selectedLegends() const
10303 {
10304  QList<QCPLegend*> result;
10305 
10306  QStack<QCPLayoutElement*> elementStack;
10307  if (mPlotLayout)
10308  elementStack.push(mPlotLayout);
10309 
10310  while (!elementStack.isEmpty())
10311  {
10312  foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
10313  {
10314  if (subElement)
10315  {
10316  elementStack.push(subElement);
10317  if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10318  {
10319  if (leg->selectedParts() != QCPLegend::spNone)
10320  result.append(leg);
10321  }
10322  }
10323  }
10324  }
10325 
10326  return result;
10327 }
10328 
10339 {
10340  foreach (QCPLayer *layer, mLayers)
10341  {
10342  foreach (QCPLayerable *layerable, layer->children())
10343  layerable->deselectEvent(0);
10344  }
10345 }
10346 
10361 {
10362  if (mReplotting) // incase signals loop back to replot slot
10363  return;
10364  mReplotting = true;
10365  emit beforeReplot();
10366 
10367  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10368  QCPPainter painter;
10369  painter.begin(&mPaintBuffer);
10370  if (painter.isActive())
10371  {
10372  painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10373  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10374  painter.fillRect(mViewport, mBackgroundBrush);
10375  draw(&painter);
10376  painter.end();
10377  if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10378  repaint();
10379  else
10380  update();
10381  } else // might happen if QCustomPlot has width or height zero
10382  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
10383 
10384  emit afterReplot();
10385  mReplotting = false;
10386 }
10387 
10396 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10397 {
10398  QList<QCPAxis*> allAxes;
10399  foreach (QCPAxisRect *rect, axisRects())
10400  allAxes << rect->axes();
10401 
10402  foreach (QCPAxis *axis, allAxes)
10403  axis->rescale(onlyVisiblePlottables);
10404 }
10405 
10443 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10444 {
10445  bool success = false;
10446 #ifdef QT_NO_PRINTER
10447  Q_UNUSED(fileName)
10448  Q_UNUSED(noCosmeticPen)
10449  Q_UNUSED(width)
10450  Q_UNUSED(height)
10451  Q_UNUSED(pdfCreator)
10452  Q_UNUSED(pdfTitle)
10453  qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
10454 #else
10455  int newWidth, newHeight;
10456  if (width == 0 || height == 0)
10457  {
10458  newWidth = this->width();
10459  newHeight = this->height();
10460  } else
10461  {
10462  newWidth = width;
10463  newHeight = height;
10464  }
10465 
10466  QPrinter printer(QPrinter::ScreenResolution);
10467  printer.setOutputFileName(fileName);
10468  printer.setOutputFormat(QPrinter::PdfFormat);
10469  printer.setColorMode(QPrinter::Color);
10470  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10471  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10472  QRect oldViewport = viewport();
10473  setViewport(QRect(0, 0, newWidth, newHeight));
10474 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
10475  printer.setFullPage(true);
10476  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10477 #else
10478  QPageLayout pageLayout;
10479  pageLayout.setMode(QPageLayout::FullPageMode);
10480  pageLayout.setOrientation(QPageLayout::Portrait);
10481  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
10482  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
10483  printer.setPageLayout(pageLayout);
10484 #endif
10485  QCPPainter printpainter;
10486  if (printpainter.begin(&printer))
10487  {
10488  printpainter.setMode(QCPPainter::pmVectorized);
10489  printpainter.setMode(QCPPainter::pmNoCaching);
10490  printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10491  printpainter.setWindow(mViewport);
10492  if (mBackgroundBrush.style() != Qt::NoBrush &&
10493  mBackgroundBrush.color() != Qt::white &&
10494  mBackgroundBrush.color() != Qt::transparent &&
10495  mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
10496  printpainter.fillRect(viewport(), mBackgroundBrush);
10497  draw(&printpainter);
10498  printpainter.end();
10499  success = true;
10500  }
10501  setViewport(oldViewport);
10502 #endif // QT_NO_PRINTER
10503  return success;
10504 }
10505 
10543 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10544 {
10545  return saveRastered(fileName, width, height, scale, "PNG", quality);
10546 }
10547 
10582 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
10583 {
10584  return saveRastered(fileName, width, height, scale, "JPG", quality);
10585 }
10586 
10618 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
10619 {
10620  return saveRastered(fileName, width, height, scale, "BMP");
10621 }
10622 
10632 {
10633  return mPlotLayout->minimumSizeHint();
10634 }
10635 
10642 {
10643  return mPlotLayout->minimumSizeHint();
10644 }
10645 
10651 void QCustomPlot::paintEvent(QPaintEvent *event)
10652 {
10653  Q_UNUSED(event);
10654  QPainter painter(this);
10655  painter.drawPixmap(0, 0, mPaintBuffer);
10656 }
10657 
10664 void QCustomPlot::resizeEvent(QResizeEvent *event)
10665 {
10666  // resize and repaint the buffer:
10667  mPaintBuffer = QPixmap(event->size());
10668  setViewport(rect());
10669  replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10670 }
10671 
10681 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
10682 {
10683  emit mouseDoubleClick(event);
10684 
10685  QVariant details;
10686  QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10687 
10688  // emit specialized object double click signals:
10689  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10690  emit plottableDoubleClick(ap, event);
10691  else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10692  emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10693  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10694  emit itemDoubleClick(ai, event);
10695  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10696  emit legendDoubleClick(lg, 0, event);
10697  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10698  emit legendDoubleClick(li->parentLegend(), li, event);
10699  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10700  emit titleDoubleClick(event, pt);
10701 
10702  // call double click event of affected layout element:
10703  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10704  el->mouseDoubleClickEvent(event);
10705 
10706  // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
10707  if (mMouseEventElement)
10708  {
10709  mMouseEventElement->mouseReleaseEvent(event);
10710  mMouseEventElement = 0;
10711  }
10712 
10713  //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
10714 }
10715 
10723 void QCustomPlot::mousePressEvent(QMouseEvent *event)
10724 {
10725  emit mousePress(event);
10726  mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
10727 
10728  // call event of affected layout element:
10729  mMouseEventElement = layoutElementAt(event->pos());
10730  if (mMouseEventElement)
10731  mMouseEventElement->mousePressEvent(event);
10732 
10733  QWidget::mousePressEvent(event);
10734 }
10735 
10745 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
10746 {
10747  emit mouseMove(event);
10748 
10749  // call event of affected layout element:
10750  if (mMouseEventElement)
10751  mMouseEventElement->mouseMoveEvent(event);
10752 
10753  QWidget::mouseMoveEvent(event);
10754 }
10755 
10770 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
10771 {
10772  emit mouseRelease(event);
10773  bool doReplot = false;
10774 
10775  if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
10776  {
10777  if (event->button() == Qt::LeftButton)
10778  {
10779  // handle selection mechanism:
10780  QVariant details;
10781  QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10782  bool selectionStateChanged = false;
10783  bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
10784  // deselect all other layerables if not additive selection:
10785  if (!additive)
10786  {
10787  foreach (QCPLayer *layer, mLayers)
10788  {
10789  foreach (QCPLayerable *layerable, layer->children())
10790  {
10791  if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
10792  {
10793  bool selChanged = false;
10794  layerable->deselectEvent(&selChanged);
10795  selectionStateChanged |= selChanged;
10796  }
10797  }
10798  }
10799  }
10800  if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10801  {
10802  // a layerable was actually clicked, call its selectEvent:
10803  bool selChanged = false;
10804  clickedLayerable->selectEvent(event, additive, details, &selChanged);
10805  selectionStateChanged |= selChanged;
10806  }
10807  if (selectionStateChanged)
10808  {
10809  doReplot = true;
10810  emit selectionChangedByUser();
10811  }
10812  }
10813 
10814  // emit specialized object click signals:
10815  QVariant details;
10816  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
10817  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10818  emit plottableClick(ap, event);
10819  else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10820  emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10821  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10822  emit itemClick(ai, event);
10823  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10824  emit legendClick(lg, 0, event);
10825  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10826  emit legendClick(li->parentLegend(), li, event);
10827  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10828  emit titleClick(event, pt);
10829  }
10830 
10831  // call event of affected layout element:
10832  if (mMouseEventElement)
10833  {
10834  mMouseEventElement->mouseReleaseEvent(event);
10835  mMouseEventElement = 0;
10836  }
10837 
10838  if (doReplot || noAntialiasingOnDrag())
10839  replot();
10840 
10841  QWidget::mouseReleaseEvent(event);
10842 }
10843 
10850 void QCustomPlot::wheelEvent(QWheelEvent *event)
10851 {
10852  emit mouseWheel(event);
10853 
10854  // call event of affected layout element:
10855  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10856  el->wheelEvent(event);
10857 
10858  QWidget::wheelEvent(event);
10859 }
10860 
10869 {
10870  // run through layout phases:
10874 
10875  // draw viewport background pixmap:
10876  drawBackground(painter);
10877 
10878  // draw all layered objects (grid, axes, plottables, items, legend,...):
10879  foreach (QCPLayer *layer, mLayers)
10880  {
10881  foreach (QCPLayerable *child, layer->children())
10882  {
10883  if (child->realVisibility())
10884  {
10885  painter->save();
10886  painter->setClipRect(child->clipRect().translated(0, -1));
10887  child->applyDefaultAntialiasingHint(painter);
10888  child->draw(painter);
10889  painter->restore();
10890  }
10891  }
10892  }
10893 
10894  /* Debug code to draw all layout element rects
10895  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
10896  {
10897  painter->setBrush(Qt::NoBrush);
10898  painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
10899  painter->drawRect(el->rect());
10900  painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
10901  painter->drawRect(el->outerRect());
10902  }
10903  */
10904 }
10905 
10924 {
10925  // Note: background color is handled in individual replot/save functions
10926 
10927  // draw background pixmap (on top of fill, if brush specified):
10928  if (!mBackgroundPixmap.isNull())
10929  {
10930  if (mBackgroundScaled)
10931  {
10932  // check whether mScaledBackground needs to be updated:
10933  QSize scaledSize(mBackgroundPixmap.size());
10934  scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
10935  if (mScaledBackgroundPixmap.size() != scaledSize)
10936  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
10937  painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
10938  } else
10939  {
10940  painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
10941  }
10942  }
10943 }
10944 
10945 
10952 {
10953  if (xAxis == axis)
10954  xAxis = 0;
10955  if (xAxis2 == axis)
10956  xAxis2 = 0;
10957  if (yAxis == axis)
10958  yAxis = 0;
10959  if (yAxis2 == axis)
10960  yAxis2 = 0;
10961 
10962  // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
10963 }
10964 
10971 {
10972  if (this->legend == legend)
10973  this->legend = 0;
10974 }
10975 
10983 {
10984  for (int i=0; i<mLayers.size(); ++i)
10985  mLayers.at(i)->mIndex = i;
10986 }
10987 
11000 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
11001 {
11002  for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
11003  {
11004  const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
11005  double minimumDistance = selectionTolerance()*1.1;
11006  QCPLayerable *minimumDistanceLayerable = 0;
11007  for (int i=layerables.size()-1; i>=0; --i)
11008  {
11009  if (!layerables.at(i)->realVisibility())
11010  continue;
11011  QVariant details;
11012  double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11013  if (dist >= 0 && dist < minimumDistance)
11014  {
11015  minimumDistance = dist;
11016  minimumDistanceLayerable = layerables.at(i);
11017  if (selectionDetails) *selectionDetails = details;
11018  }
11019  }
11020  if (minimumDistance < selectionTolerance())
11021  return minimumDistanceLayerable;
11022  }
11023  return 0;
11024 }
11025 
11037 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
11038 {
11039  QPixmap buffer = toPixmap(width, height, scale);
11040  if (!buffer.isNull())
11041  return buffer.save(fileName, format, quality);
11042  else
11043  return false;
11044 }
11045 
11054 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
11055 {
11056  // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11057  int newWidth, newHeight;
11058  if (width == 0 || height == 0)
11059  {
11060  newWidth = this->width();
11061  newHeight = this->height();
11062  } else
11063  {
11064  newWidth = width;
11065  newHeight = height;
11066  }
11067  int scaledWidth = qRound(scale*newWidth);
11068  int scaledHeight = qRound(scale*newHeight);
11069 
11070  QPixmap result(scaledWidth, scaledHeight);
11071  result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
11072  QCPPainter painter;
11073  painter.begin(&result);
11074  if (painter.isActive())
11075  {
11076  QRect oldViewport = viewport();
11077  setViewport(QRect(0, 0, newWidth, newHeight));
11079  if (!qFuzzyCompare(scale, 1.0))
11080  {
11081  if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
11083  painter.scale(scale, scale);
11084  }
11085  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill
11086  painter.fillRect(mViewport, mBackgroundBrush);
11087  draw(&painter);
11088  setViewport(oldViewport);
11089  painter.end();
11090  } else // might happen if pixmap has width or height zero
11091  {
11092  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11093  return QPixmap();
11094  }
11095  return result;
11096 }
11097 
11110 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
11111 {
11112  // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11113  int newWidth, newHeight;
11114  if (width == 0 || height == 0)
11115  {
11116  newWidth = this->width();
11117  newHeight = this->height();
11118  } else
11119  {
11120  newWidth = width;
11121  newHeight = height;
11122  }
11123 
11124  if (painter->isActive())
11125  {
11126  QRect oldViewport = viewport();
11127  setViewport(QRect(0, 0, newWidth, newHeight));
11128  painter->setMode(QCPPainter::pmNoCaching);
11129  if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here
11130  painter->fillRect(mViewport, mBackgroundBrush);
11131  draw(painter);
11132  setViewport(oldViewport);
11133  } else
11134  qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11135 }
11136 
11137 
11141 
11173  mLevelCount(350),
11174  mColorInterpolation(ciRGB),
11175  mPeriodic(false),
11176  mColorBufferInvalidated(true)
11177 {
11178  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11179  loadPreset(preset);
11180 }
11181 
11182 /* undocumented operator */
11184 {
11185  return ((other.mLevelCount == this->mLevelCount) &&
11186  (other.mColorInterpolation == this->mColorInterpolation) &&
11187  (other.mPeriodic == this->mPeriodic) &&
11188  (other.mColorStops == this->mColorStops));
11189 }
11190 
11198 {
11199  if (n < 2)
11200  {
11201  qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11202  n = 2;
11203  }
11204  if (n != mLevelCount)
11205  {
11206  mLevelCount = n;
11207  mColorBufferInvalidated = true;
11208  }
11209 }
11210 
11221 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11222 {
11224  mColorBufferInvalidated = true;
11225 }
11226 
11233 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
11234 {
11235  mColorStops.insert(position, color);
11236  mColorBufferInvalidated = true;
11237 }
11238 
11247 {
11248  if (interpolation != mColorInterpolation)
11249  {
11250  mColorInterpolation = interpolation;
11251  mColorBufferInvalidated = true;
11252  }
11253 }
11254 
11271 {
11272  mPeriodic = enabled;
11273 }
11274 
11287 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
11288 {
11289  // If you change something here, make sure to also adapt ::color()
11290  if (!data)
11291  {
11292  qDebug() << Q_FUNC_INFO << "null pointer given as data";
11293  return;
11294  }
11295  if (!scanLine)
11296  {
11297  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11298  return;
11299  }
11302 
11303  if (!logarithmic)
11304  {
11305  const double posToIndexFactor = (mLevelCount-1)/range.size();
11306  if (mPeriodic)
11307  {
11308  for (int i=0; i<n; ++i)
11309  {
11310  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
11311  if (index < 0)
11312  index += mLevelCount;
11313  scanLine[i] = mColorBuffer.at(index);
11314  }
11315  } else
11316  {
11317  for (int i=0; i<n; ++i)
11318  {
11319  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
11320  if (index < 0)
11321  index = 0;
11322  else if (index >= mLevelCount)
11323  index = mLevelCount-1;
11324  scanLine[i] = mColorBuffer.at(index);
11325  }
11326  }
11327  } else // logarithmic == true
11328  {
11329  if (mPeriodic)
11330  {
11331  for (int i=0; i<n; ++i)
11332  {
11333  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
11334  if (index < 0)
11335  index += mLevelCount;
11336  scanLine[i] = mColorBuffer.at(index);
11337  }
11338  } else
11339  {
11340  for (int i=0; i<n; ++i)
11341  {
11342  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11343  if (index < 0)
11344  index = 0;
11345  else if (index >= mLevelCount)
11346  index = mLevelCount-1;
11347  scanLine[i] = mColorBuffer.at(index);
11348  }
11349  }
11350  }
11351 }
11352 
11362 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11363 {
11364  // If you change something here, make sure to also adapt ::colorize()
11367  int index = 0;
11368  if (!logarithmic)
11369  index = (position-range.lower)*(mLevelCount-1)/range.size();
11370  else
11371  index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11372  if (mPeriodic)
11373  {
11374  index = index % mLevelCount;
11375  if (index < 0)
11376  index += mLevelCount;
11377  } else
11378  {
11379  if (index < 0)
11380  index = 0;
11381  else if (index >= mLevelCount)
11382  index = mLevelCount-1;
11383  }
11384  return mColorBuffer.at(index);
11385 }
11386 
11395 {
11396  clearColorStops();
11397  switch (preset)
11398  {
11399  case gpGrayscale:
11401  setColorStopAt(0, Qt::black);
11402  setColorStopAt(1, Qt::white);
11403  break;
11404  case gpHot:
11406  setColorStopAt(0, QColor(50, 0, 0));
11407  setColorStopAt(0.2, QColor(180, 10, 0));
11408  setColorStopAt(0.4, QColor(245, 50, 0));
11409  setColorStopAt(0.6, QColor(255, 150, 10));
11410  setColorStopAt(0.8, QColor(255, 255, 50));
11411  setColorStopAt(1, QColor(255, 255, 255));
11412  break;
11413  case gpCold:
11415  setColorStopAt(0, QColor(0, 0, 50));
11416  setColorStopAt(0.2, QColor(0, 10, 180));
11417  setColorStopAt(0.4, QColor(0, 50, 245));
11418  setColorStopAt(0.6, QColor(10, 150, 255));
11419  setColorStopAt(0.8, QColor(50, 255, 255));
11420  setColorStopAt(1, QColor(255, 255, 255));
11421  break;
11422  case gpNight:
11424  setColorStopAt(0, QColor(10, 20, 30));
11425  setColorStopAt(1, QColor(250, 255, 250));
11426  break;
11427  case gpCandy:
11429  setColorStopAt(0, QColor(0, 0, 255));
11430  setColorStopAt(1, QColor(255, 250, 250));
11431  break;
11432  case gpGeography:
11434  setColorStopAt(0, QColor(70, 170, 210));
11435  setColorStopAt(0.20, QColor(90, 160, 180));
11436  setColorStopAt(0.25, QColor(45, 130, 175));
11437  setColorStopAt(0.30, QColor(100, 140, 125));
11438  setColorStopAt(0.5, QColor(100, 140, 100));
11439  setColorStopAt(0.6, QColor(130, 145, 120));
11440  setColorStopAt(0.7, QColor(140, 130, 120));
11441  setColorStopAt(0.9, QColor(180, 190, 190));
11442  setColorStopAt(1, QColor(210, 210, 230));
11443  break;
11444  case gpIon:
11446  setColorStopAt(0, QColor(50, 10, 10));
11447  setColorStopAt(0.45, QColor(0, 0, 255));
11448  setColorStopAt(0.8, QColor(0, 255, 255));
11449  setColorStopAt(1, QColor(0, 255, 0));
11450  break;
11451  case gpThermal:
11453  setColorStopAt(0, QColor(0, 0, 50));
11454  setColorStopAt(0.15, QColor(20, 0, 120));
11455  setColorStopAt(0.33, QColor(200, 30, 140));
11456  setColorStopAt(0.6, QColor(255, 100, 0));
11457  setColorStopAt(0.85, QColor(255, 255, 40));
11458  setColorStopAt(1, QColor(255, 255, 255));
11459  break;
11460  case gpPolar:
11462  setColorStopAt(0, QColor(50, 255, 255));
11463  setColorStopAt(0.18, QColor(10, 70, 255));
11464  setColorStopAt(0.28, QColor(10, 10, 190));
11465  setColorStopAt(0.5, QColor(0, 0, 0));
11466  setColorStopAt(0.72, QColor(190, 10, 10));
11467  setColorStopAt(0.82, QColor(255, 70, 10));
11468  setColorStopAt(1, QColor(255, 255, 50));
11469  break;
11470  case gpSpectrum:
11472  setColorStopAt(0, QColor(50, 0, 50));
11473  setColorStopAt(0.15, QColor(0, 0, 255));
11474  setColorStopAt(0.35, QColor(0, 255, 255));
11475  setColorStopAt(0.6, QColor(255, 255, 0));
11476  setColorStopAt(0.75, QColor(255, 30, 0));
11477  setColorStopAt(1, QColor(50, 0, 0));
11478  break;
11479  case gpJet:
11481  setColorStopAt(0, QColor(0, 0, 100));
11482  setColorStopAt(0.15, QColor(0, 50, 255));
11483  setColorStopAt(0.35, QColor(0, 255, 255));
11484  setColorStopAt(0.65, QColor(255, 255, 0));
11485  setColorStopAt(0.85, QColor(255, 30, 0));
11486  setColorStopAt(1, QColor(100, 0, 0));
11487  break;
11488  case gpHues:
11490  setColorStopAt(0, QColor(255, 0, 0));
11491  setColorStopAt(1.0/3.0, QColor(0, 0, 255));
11492  setColorStopAt(2.0/3.0, QColor(0, 255, 0));
11493  setColorStopAt(1, QColor(255, 0, 0));
11494  break;
11495  }
11496 }
11497 
11504 {
11505  mColorStops.clear();
11506  mColorBufferInvalidated = true;
11507 }
11508 
11516 {
11517  QCPColorGradient result(*this);
11518  result.clearColorStops();
11519  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
11520  result.setColorStopAt(1.0-it.key(), it.value());
11521  return result;
11522 }
11523 
11530 {
11531  if (mColorBuffer.size() != mLevelCount)
11532  mColorBuffer.resize(mLevelCount);
11533  if (mColorStops.size() > 1)
11534  {
11535  double indexToPosFactor = 1.0/(double)(mLevelCount-1);
11536  for (int i=0; i<mLevelCount; ++i)
11537  {
11538  double position = i*indexToPosFactor;
11539  QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
11540  if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
11541  {
11542  mColorBuffer[i] = (it-1).value().rgb();
11543  } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
11544  {
11545  mColorBuffer[i] = it.value().rgb();
11546  } else // position is in between stops (or on an intermediate stop), interpolate color
11547  {
11548  QMap<double, QColor>::const_iterator high = it;
11549  QMap<double, QColor>::const_iterator low = it-1;
11550  double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
11551  switch (mColorInterpolation)
11552  {
11553  case ciRGB:
11554  {
11555  mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
11556  (1-t)*low.value().green() + t*high.value().green(),
11557  (1-t)*low.value().blue() + t*high.value().blue());
11558  break;
11559  }
11560  case ciHSV:
11561  {
11562  QColor lowHsv = low.value().toHsv();
11563  QColor highHsv = high.value().toHsv();
11564  double hue = 0;
11565  double hueDiff = highHsv.hueF()-lowHsv.hueF();
11566  if (hueDiff > 0.5)
11567  hue = lowHsv.hueF() - t*(1.0-hueDiff);
11568  else if (hueDiff < -0.5)
11569  hue = lowHsv.hueF() + t*(1.0+hueDiff);
11570  else
11571  hue = lowHsv.hueF() + t*hueDiff;
11572  if (hue < 0) hue += 1.0;
11573  else if (hue >= 1.0) hue -= 1.0;
11574  mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
11575  break;
11576  }
11577  }
11578  }
11579  }
11580  } else if (mColorStops.size() == 1)
11581  {
11582  mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11583  } else // mColorStops is empty, fill color buffer with black
11584  {
11585  mColorBuffer.fill(qRgb(0, 0, 0));
11586  }
11587  mColorBufferInvalidated = false;
11588 }
11589 
11590 
11594 
11632 /* start documentation of inline functions */
11633 
11714 /* end documentation of inline functions */
11715 
11720 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
11721  QCPLayoutElement(parentPlot),
11722  mBackgroundBrush(Qt::NoBrush),
11723  mBackgroundScaled(true),
11724  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
11725  mInsetLayout(new QCPLayoutInset),
11726  mRangeDrag(Qt::Horizontal|Qt::Vertical),
11727  mRangeZoom(Qt::Horizontal|Qt::Vertical),
11728  mRangeZoomFactorHorz(0.85),
11729  mRangeZoomFactorVert(0.85),
11730  mDragging(false)
11731 {
11734  mInsetLayout->setParent(this);
11735 
11736  setMinimumSize(50, 50);
11737  setMinimumMargins(QMargins(15, 15, 15, 15));
11738  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
11739  mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
11740  mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
11741  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
11742 
11743  if (setupDefaultAxes)
11744  {
11745  QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
11746  QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
11747  QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
11748  QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
11749  setRangeDragAxes(xAxis, yAxis);
11750  setRangeZoomAxes(xAxis, yAxis);
11751  xAxis2->setVisible(false);
11752  yAxis2->setVisible(false);
11753  xAxis->grid()->setVisible(true);
11754  yAxis->grid()->setVisible(true);
11755  xAxis2->grid()->setVisible(false);
11756  yAxis2->grid()->setVisible(false);
11757  xAxis2->grid()->setZeroLinePen(Qt::NoPen);
11758  yAxis2->grid()->setZeroLinePen(Qt::NoPen);
11759  xAxis2->grid()->setVisible(false);
11760  yAxis2->grid()->setVisible(false);
11761  }
11762 }
11763 
11765 {
11766  delete mInsetLayout;
11767  mInsetLayout = 0;
11768 
11769  QList<QCPAxis*> axesList = axes();
11770  for (int i=0; i<axesList.size(); ++i)
11771  removeAxis(axesList.at(i));
11772 }
11773 
11780 {
11781  return mAxes.value(type).size();
11782 }
11783 
11790 {
11791  QList<QCPAxis*> ax(mAxes.value(type));
11792  if (index >= 0 && index < ax.size())
11793  {
11794  return ax.at(index);
11795  } else
11796  {
11797  qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
11798  return 0;
11799  }
11800 }
11801 
11810 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
11811 {
11812  QList<QCPAxis*> result;
11813  if (types.testFlag(QCPAxis::atLeft))
11814  result << mAxes.value(QCPAxis::atLeft);
11815  if (types.testFlag(QCPAxis::atRight))
11816  result << mAxes.value(QCPAxis::atRight);
11817  if (types.testFlag(QCPAxis::atTop))
11818  result << mAxes.value(QCPAxis::atTop);
11819  if (types.testFlag(QCPAxis::atBottom))
11820  result << mAxes.value(QCPAxis::atBottom);
11821  return result;
11822 }
11823 
11828 QList<QCPAxis*> QCPAxisRect::axes() const
11829 {
11830  QList<QCPAxis*> result;
11831  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11832  while (it.hasNext())
11833  {
11834  it.next();
11835  result << it.value();
11836  }
11837  return result;
11838 }
11839 
11860 {
11861  QCPAxis *newAxis = axis;
11862  if (!newAxis)
11863  {
11864  newAxis = new QCPAxis(this, type);
11865  } else // user provided existing axis instance, do some sanity checks
11866  {
11867  if (newAxis->axisType() != type)
11868  {
11869  qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
11870  return 0;
11871  }
11872  if (newAxis->axisRect() != this)
11873  {
11874  qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
11875  return 0;
11876  }
11877  if (axes().contains(newAxis))
11878  {
11879  qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
11880  return 0;
11881  }
11882  }
11883  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
11884  {
11885  bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
11886  newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
11887  newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
11888  }
11889  mAxes[type].append(newAxis);
11890  return newAxis;
11891 }
11892 
11901 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
11902 {
11903  QList<QCPAxis*> result;
11904  if (types.testFlag(QCPAxis::atLeft))
11905  result << addAxis(QCPAxis::atLeft);
11906  if (types.testFlag(QCPAxis::atRight))
11907  result << addAxis(QCPAxis::atRight);
11908  if (types.testFlag(QCPAxis::atTop))
11909  result << addAxis(QCPAxis::atTop);
11910  if (types.testFlag(QCPAxis::atBottom))
11911  result << addAxis(QCPAxis::atBottom);
11912  return result;
11913 }
11914 
11923 {
11924  // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
11925  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11926  while (it.hasNext())
11927  {
11928  it.next();
11929  if (it.value().contains(axis))
11930  {
11931  mAxes[it.key()].removeOne(axis);
11932  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)
11933  parentPlot()->axisRemoved(axis);
11934  delete axis;
11935  return true;
11936  }
11937  }
11938  qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
11939  return false;
11940 }
11941 
11968 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
11969 {
11970  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
11971  if (axisCount(QCPAxis::atBottom) == 0)
11972  xAxis = addAxis(QCPAxis::atBottom);
11973  else
11974  xAxis = axis(QCPAxis::atBottom);
11975 
11976  if (axisCount(QCPAxis::atLeft) == 0)
11977  yAxis = addAxis(QCPAxis::atLeft);
11978  else
11979  yAxis = axis(QCPAxis::atLeft);
11980 
11981  if (axisCount(QCPAxis::atTop) == 0)
11982  xAxis2 = addAxis(QCPAxis::atTop);
11983  else
11984  xAxis2 = axis(QCPAxis::atTop);
11985 
11986  if (axisCount(QCPAxis::atRight) == 0)
11987  yAxis2 = addAxis(QCPAxis::atRight);
11988  else
11989  yAxis2 = axis(QCPAxis::atRight);
11990 
11991  xAxis->setVisible(true);
11992  yAxis->setVisible(true);
11993  xAxis2->setVisible(true);
11994  yAxis2->setVisible(true);
11995  xAxis2->setTickLabels(false);
11996  yAxis2->setTickLabels(false);
11997 
11998  xAxis2->setRange(xAxis->range());
11999  xAxis2->setRangeReversed(xAxis->rangeReversed());
12000  xAxis2->setScaleType(xAxis->scaleType());
12001  xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12002  xAxis2->setTicks(xAxis->ticks());
12003  xAxis2->setAutoTickCount(xAxis->autoTickCount());
12004  xAxis2->setSubTickCount(xAxis->subTickCount());
12005  xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12006  xAxis2->setTickStep(xAxis->tickStep());
12007  xAxis2->setAutoTickStep(xAxis->autoTickStep());
12008  xAxis2->setNumberFormat(xAxis->numberFormat());
12009  xAxis2->setNumberPrecision(xAxis->numberPrecision());
12010  xAxis2->setTickLabelType(xAxis->tickLabelType());
12011  xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12012  xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12013 
12014  yAxis2->setRange(yAxis->range());
12015  yAxis2->setRangeReversed(yAxis->rangeReversed());
12016  yAxis2->setScaleType(yAxis->scaleType());
12017  yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12018  yAxis2->setTicks(yAxis->ticks());
12019  yAxis2->setAutoTickCount(yAxis->autoTickCount());
12020  yAxis2->setSubTickCount(yAxis->subTickCount());
12021  yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12022  yAxis2->setTickStep(yAxis->tickStep());
12023  yAxis2->setAutoTickStep(yAxis->autoTickStep());
12024  yAxis2->setNumberFormat(yAxis->numberFormat());
12025  yAxis2->setNumberPrecision(yAxis->numberPrecision());
12026  yAxis2->setTickLabelType(yAxis->tickLabelType());
12027  yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12028  yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12029 
12030  if (connectRanges)
12031  {
12032  connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
12033  connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
12034  }
12035 }
12036 
12045 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
12046 {
12047  // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
12048  QList<QCPAbstractPlottable*> result;
12049  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
12050  {
12051  if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12052  result.append(mParentPlot->mPlottables.at(i));
12053  }
12054  return result;
12055 }
12056 
12065 QList<QCPGraph*> QCPAxisRect::graphs() const
12066 {
12067  // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
12068  QList<QCPGraph*> result;
12069  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
12070  {
12071  if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12072  result.append(mParentPlot->mGraphs.at(i));
12073  }
12074  return result;
12075 }
12076 
12087 QList<QCPAbstractItem *> QCPAxisRect::items() const
12088 {
12089  // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
12090  // and miss those items that have this axis rect as clipAxisRect.
12091  QList<QCPAbstractItem*> result;
12092  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
12093  {
12094  if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
12095  {
12096  result.append(mParentPlot->mItems.at(itemId));
12097  continue;
12098  }
12099  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
12100  for (int posId=0; posId<positions.size(); ++posId)
12101  {
12102  if (positions.at(posId)->axisRect() == this ||
12103  positions.at(posId)->keyAxis()->axisRect() == this ||
12104  positions.at(posId)->valueAxis()->axisRect() == this)
12105  {
12106  result.append(mParentPlot->mItems.at(itemId));
12107  break;
12108  }
12109  }
12110  }
12111  return result;
12112 }
12113 
12123 {
12124  QCPLayoutElement::update(phase);
12125 
12126  switch (phase)
12127  {
12128  case upPreparation:
12129  {
12130  QList<QCPAxis*> allAxes = axes();
12131  for (int i=0; i<allAxes.size(); ++i)
12132  allAxes.at(i)->setupTickVectors();
12133  break;
12134  }
12135  case upLayout:
12136  {
12138  break;
12139  }
12140  default: break;
12141  }
12142 
12143  // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
12144  mInsetLayout->update(phase);
12145 }
12146 
12147 /* inherits documentation from base class */
12148 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
12149 {
12150  QList<QCPLayoutElement*> result;
12151  if (mInsetLayout)
12152  {
12153  result << mInsetLayout;
12154  if (recursive)
12155  result << mInsetLayout->elements(recursive);
12156  }
12157  return result;
12158 }
12159 
12160 /* inherits documentation from base class */
12162 {
12163  painter->setAntialiasing(false);
12164 }
12165 
12166 /* inherits documentation from base class */
12168 {
12169  drawBackground(painter);
12170 }
12171 
12187 void QCPAxisRect::setBackground(const QPixmap &pm)
12188 {
12189  mBackgroundPixmap = pm;
12190  mScaledBackgroundPixmap = QPixmap();
12191 }
12192 
12206 void QCPAxisRect::setBackground(const QBrush &brush)
12207 {
12208  mBackgroundBrush = brush;
12209 }
12210 
12218 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
12219 {
12220  mBackgroundPixmap = pm;
12221  mScaledBackgroundPixmap = QPixmap();
12222  mBackgroundScaled = scaled;
12223  mBackgroundScaledMode = mode;
12224 }
12225 
12237 {
12238  mBackgroundScaled = scaled;
12239 }
12240 
12246 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12247 {
12248  mBackgroundScaledMode = mode;
12249 }
12250 
12256 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12257 {
12258  return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12259 }
12260 
12266 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12267 {
12268  return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12269 }
12270 
12276 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12277 {
12278  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12279 }
12280 
12297 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12298 {
12299  mRangeDrag = orientations;
12300 }
12301 
12317 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12318 {
12319  mRangeZoom = orientations;
12320 }
12321 
12328 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
12329 {
12330  mRangeDragHorzAxis = horizontal;
12331  mRangeDragVertAxis = vertical;
12332 }
12333 
12341 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
12342 {
12343  mRangeZoomHorzAxis = horizontal;
12344  mRangeZoomVertAxis = vertical;
12345 }
12346 
12357 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12358 {
12359  mRangeZoomFactorHorz = horizontalFactor;
12360  mRangeZoomFactorVert = verticalFactor;
12361 }
12362 
12368 {
12369  mRangeZoomFactorHorz = factor;
12370  mRangeZoomFactorVert = factor;
12371 }
12372 
12392 {
12393  // draw background fill:
12394  if (mBackgroundBrush != Qt::NoBrush)
12395  painter->fillRect(mRect, mBackgroundBrush);
12396 
12397  // draw background pixmap (on top of fill, if brush specified):
12398  if (!mBackgroundPixmap.isNull())
12399  {
12400  if (mBackgroundScaled)
12401  {
12402  // check whether mScaledBackground needs to be updated:
12403  QSize scaledSize(mBackgroundPixmap.size());
12404  scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12405  if (mScaledBackgroundPixmap.size() != scaledSize)
12406  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12407  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
12408  } else
12409  {
12410  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
12411  }
12412  }
12413 }
12414 
12426 {
12427  const QList<QCPAxis*> axesList = mAxes.value(type);
12428  if (axesList.isEmpty())
12429  return;
12430 
12431  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
12432  for (int i=1; i<axesList.size(); ++i)
12433  {
12434  int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
12435  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)
12436  {
12437  if (!isFirstVisible)
12438  offset += axesList.at(i)->tickLengthIn();
12439  isFirstVisible = false;
12440  }
12441  axesList.at(i)->setOffset(offset);
12442  }
12443 }
12444 
12445 /* inherits documentation from base class */
12447 {
12448  if (!mAutoMargins.testFlag(side))
12449  qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12450 
12452 
12453  // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
12454  const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
12455  if (axesList.size() > 0)
12456  return axesList.last()->offset() + axesList.last()->calculateMargin();
12457  else
12458  return 0;
12459 }
12460 
12472 void QCPAxisRect::mousePressEvent(QMouseEvent *event)
12473 {
12474  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)
12475  if (event->buttons() & Qt::LeftButton)
12476  {
12477  mDragging = true;
12478  // initialize antialiasing backup in case we start dragging:
12480  {
12483  }
12484  // Mouse range dragging interaction:
12485  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12486  {
12487  if (mRangeDragHorzAxis)
12488  mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12489  if (mRangeDragVertAxis)
12490  mDragStartVertRange = mRangeDragVertAxis.data()->range();
12491  }
12492  }
12493 }
12494 
12502 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
12503 {
12504  // Mouse range dragging interaction:
12505  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12506  {
12507  if (mRangeDrag.testFlag(Qt::Horizontal))
12508  {
12509  if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
12510  {
12511  if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
12512  {
12513  double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
12514  rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
12515  } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
12516  {
12517  double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
12518  rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
12519  }
12520  }
12521  }
12522  if (mRangeDrag.testFlag(Qt::Vertical))
12523  {
12524  if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
12525  {
12526  if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
12527  {
12528  double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
12529  rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
12530  } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
12531  {
12532  double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
12533  rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
12534  }
12535  }
12536  }
12537  if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
12538  {
12541  mParentPlot->replot();
12542  }
12543  }
12544 }
12545 
12546 /* inherits documentation from base class */
12547 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
12548 {
12549  Q_UNUSED(event)
12550  mDragging = false;
12552  {
12555  }
12556 }
12557 
12572 void QCPAxisRect::wheelEvent(QWheelEvent *event)
12573 {
12574  // Mouse range zooming interaction:
12575  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
12576  {
12577  if (mRangeZoom != 0)
12578  {
12579  double factor;
12580  double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
12581  if (mRangeZoom.testFlag(Qt::Horizontal))
12582  {
12583  factor = qPow(mRangeZoomFactorHorz, wheelSteps);
12584  if (mRangeZoomHorzAxis.data())
12585  mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
12586  }
12587  if (mRangeZoom.testFlag(Qt::Vertical))
12588  {
12589  factor = qPow(mRangeZoomFactorVert, wheelSteps);
12590  if (mRangeZoomVertAxis.data())
12591  mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
12592  }
12593  mParentPlot->replot();
12594  }
12595  }
12596 }
12597 
12598 
12602 
12627 /* start of documentation of signals */
12628 
12635 /* end of documentation of signals */
12636 
12642  QCPLayoutElement(parent->parentPlot()),
12643  mParentLegend(parent),
12644  mFont(parent->font()),
12645  mTextColor(parent->textColor()),
12646  mSelectedFont(parent->selectedFont()),
12647  mSelectedTextColor(parent->selectedTextColor()),
12648  mSelectable(true),
12649  mSelected(false)
12650 {
12651  setLayer(QLatin1String("legend"));
12652  setMargins(QMargins(8, 2, 8, 2));
12653 }
12654 
12661 {
12662  mFont = font;
12663 }
12664 
12670 void QCPAbstractLegendItem::setTextColor(const QColor &color)
12671 {
12672  mTextColor = color;
12673 }
12674 
12682 {
12683  mSelectedFont = font;
12684 }
12685 
12693 {
12694  mSelectedTextColor = color;
12695 }
12696 
12703 {
12704  if (mSelectable != selectable)
12705  {
12708  }
12709 }
12710 
12720 {
12721  if (mSelected != selected)
12722  {
12723  mSelected = selected;
12725  }
12726 }
12727 
12728 /* inherits documentation from base class */
12729 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
12730 {
12731  Q_UNUSED(details)
12732  if (!mParentPlot) return -1;
12733  if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
12734  return -1;
12735 
12736  if (mRect.contains(pos.toPoint()))
12737  return mParentPlot->selectionTolerance()*0.99;
12738  else
12739  return -1;
12740 }
12741 
12742 /* inherits documentation from base class */
12744 {
12746 }
12747 
12748 /* inherits documentation from base class */
12750 {
12751  return mOuterRect;
12752 }
12753 
12754 /* inherits documentation from base class */
12755 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12756 {
12757  Q_UNUSED(event)
12758  Q_UNUSED(details)
12760  {
12761  bool selBefore = mSelected;
12762  setSelected(additive ? !mSelected : true);
12763  if (selectionStateChanged)
12764  *selectionStateChanged = mSelected != selBefore;
12765  }
12766 }
12767 
12768 /* inherits documentation from base class */
12769 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
12770 {
12772  {
12773  bool selBefore = mSelected;
12774  setSelected(false);
12775  if (selectionStateChanged)
12776  *selectionStateChanged = mSelected != selBefore;
12777  }
12778 }
12779 
12783 
12818  QCPAbstractLegendItem(parent),
12819  mPlottable(plottable)
12820 {
12821 }
12822 
12829 {
12831 }
12832 
12839 {
12841 }
12842 
12849 {
12850  return mSelected ? mSelectedFont : mFont;
12851 }
12852 
12860 {
12861  if (!mPlottable) return;
12862  painter->setFont(getFont());
12863  painter->setPen(QPen(getTextColor()));
12864  QSizeF iconSize = mParentLegend->iconSize();
12865  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12866  QRectF iconRect(mRect.topLeft(), iconSize);
12867  int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
12868  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
12869  // draw icon:
12870  painter->save();
12871  painter->setClipRect(iconRect, Qt::IntersectClip);
12872  mPlottable->drawLegendIcon(painter, iconRect);
12873  painter->restore();
12874  // draw icon border:
12875  if (getIconBorderPen().style() != Qt::NoPen)
12876  {
12877  painter->setPen(getIconBorderPen());
12878  painter->setBrush(Qt::NoBrush);
12879  painter->drawRect(iconRect);
12880  }
12881 }
12882 
12889 {
12890  if (!mPlottable) return QSize();
12891  QSize result(0, 0);
12892  QRect textRect;
12893  QFontMetrics fontMetrics(getFont());
12894  QSize iconSize = mParentLegend->iconSize();
12895  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12896  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
12897  result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
12898  return result;
12899 }
12900 
12901 
12905 
12930 /* start of documentation of signals */
12931 
12939 /* end of documentation of signals */
12940 
12948 {
12949  setRowSpacing(0);
12950  setColumnSpacing(10);
12951  setMargins(QMargins(2, 3, 2, 2));
12952  setAntialiased(false);
12953  setIconSize(32, 18);
12954 
12955  setIconTextPadding(7);
12956 
12957  setSelectableParts(spLegendBox | spItems);
12958  setSelectedParts(spNone);
12959 
12960  setBorderPen(QPen(Qt::black));
12961  setSelectedBorderPen(QPen(Qt::blue, 2));
12962  setIconBorderPen(Qt::NoPen);
12963  setSelectedIconBorderPen(QPen(Qt::blue, 2));
12964  setBrush(Qt::white);
12965  setSelectedBrush(Qt::white);
12966  setTextColor(Qt::black);
12967  setSelectedTextColor(Qt::blue);
12968 }
12969 
12971 {
12972  clearItems();
12973  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)
12974  mParentPlot->legendRemoved(this);
12975 }
12976 
12977 /* no doc for getter, see setSelectedParts */
12978 QCPLegend::SelectableParts QCPLegend::selectedParts() const
12979 {
12980  // check whether any legend elements selected, if yes, add spItems to return value
12981  bool hasSelectedItems = false;
12982  for (int i=0; i<itemCount(); ++i)
12983  {
12984  if (item(i) && item(i)->selected())
12985  {
12986  hasSelectedItems = true;
12987  break;
12988  }
12989  }
12990  if (hasSelectedItems)
12991  return mSelectedParts | spItems;
12992  else
12993  return mSelectedParts & ~spItems;
12994 }
12995 
12999 void QCPLegend::setBorderPen(const QPen &pen)
13000 {
13001  mBorderPen = pen;
13002 }
13003 
13007 void QCPLegend::setBrush(const QBrush &brush)
13008 {
13009  mBrush = brush;
13010 }
13011 
13021 void QCPLegend::setFont(const QFont &font)
13022 {
13023  mFont = font;
13024  for (int i=0; i<itemCount(); ++i)
13025  {
13026  if (item(i))
13027  item(i)->setFont(mFont);
13028  }
13029 }
13030 
13040 void QCPLegend::setTextColor(const QColor &color)
13041 {
13042  mTextColor = color;
13043  for (int i=0; i<itemCount(); ++i)
13044  {
13045  if (item(i))
13046  item(i)->setTextColor(color);
13047  }
13048 }
13049 
13054 void QCPLegend::setIconSize(const QSize &size)
13055 {
13056  mIconSize = size;
13057 }
13058 
13061 void QCPLegend::setIconSize(int width, int height)
13062 {
13063  mIconSize.setWidth(width);
13064  mIconSize.setHeight(height);
13065 }
13066 
13073 {
13074  mIconTextPadding = padding;
13075 }
13076 
13083 void QCPLegend::setIconBorderPen(const QPen &pen)
13084 {
13085  mIconBorderPen = pen;
13086 }
13087 
13098 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13099 {
13100  if (mSelectableParts != selectable)
13101  {
13102  mSelectableParts = selectable;
13103  emit selectableChanged(mSelectableParts);
13104  }
13105 }
13106 
13128 void QCPLegend::setSelectedParts(const SelectableParts &selected)
13129 {
13130  SelectableParts newSelected = selected;
13131  mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
13132 
13133  if (mSelectedParts != newSelected)
13134  {
13135  if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
13136  {
13137  qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
13138  newSelected &= ~spItems;
13139  }
13140  if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
13141  {
13142  for (int i=0; i<itemCount(); ++i)
13143  {
13144  if (item(i))
13145  item(i)->setSelected(false);
13146  }
13147  }
13148  mSelectedParts = newSelected;
13149  emit selectionChanged(mSelectedParts);
13150  }
13151 }
13152 
13159 void QCPLegend::setSelectedBorderPen(const QPen &pen)
13160 {
13161  mSelectedBorderPen = pen;
13162 }
13163 
13170 {
13171  mSelectedIconBorderPen = pen;
13172 }
13173 
13180 void QCPLegend::setSelectedBrush(const QBrush &brush)
13181 {
13182  mSelectedBrush = brush;
13183 }
13184 
13193 {
13194  mSelectedFont = font;
13195  for (int i=0; i<itemCount(); ++i)
13196  {
13197  if (item(i))
13198  item(i)->setSelectedFont(font);
13199  }
13200 }
13201 
13209 void QCPLegend::setSelectedTextColor(const QColor &color)
13210 {
13211  mSelectedTextColor = color;
13212  for (int i=0; i<itemCount(); ++i)
13213  {
13214  if (item(i))
13215  item(i)->setSelectedTextColor(color);
13216  }
13217 }
13218 
13225 {
13226  return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13227 }
13228 
13236 {
13237  for (int i=0; i<itemCount(); ++i)
13238  {
13239  if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
13240  {
13241  if (pli->plottable() == plottable)
13242  return pli;
13243  }
13244  }
13245  return 0;
13246 }
13247 
13253 {
13254  return elementCount();
13255 }
13256 
13261 {
13262  for (int i=0; i<itemCount(); ++i)
13263  {
13264  if (item == this->item(i))
13265  return true;
13266  }
13267  return false;
13268 }
13269 
13277 {
13278  return itemWithPlottable(plottable);
13279 }
13280 
13289 {
13290  if (!hasItem(item))
13291  {
13292  return addElement(rowCount(), 0, item);
13293  } else
13294  return false;
13295 }
13296 
13304 bool QCPLegend::removeItem(int index)
13305 {
13306  if (QCPAbstractLegendItem *ali = item(index))
13307  {
13308  bool success = remove(ali);
13309  simplify();
13310  return success;
13311  } else
13312  return false;
13313 }
13314 
13324 {
13325  bool success = remove(item);
13326  simplify();
13327  return success;
13328 }
13329 
13334 {
13335  for (int i=itemCount()-1; i>=0; --i)
13336  removeItem(i);
13337 }
13338 
13345 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
13346 {
13347  QList<QCPAbstractLegendItem*> result;
13348  for (int i=0; i<itemCount(); ++i)
13349  {
13350  if (QCPAbstractLegendItem *ali = item(i))
13351  {
13352  if (ali->selected())
13353  result.append(ali);
13354  }
13355  }
13356  return result;
13357 }
13358 
13373 {
13375 }
13376 
13383 {
13384  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
13385 }
13386 
13392 QBrush QCPLegend::getBrush() const
13393 {
13394  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13395 }
13396 
13403 {
13404  // draw background rect:
13405  painter->setBrush(getBrush());
13406  painter->setPen(getBorderPen());
13407  painter->drawRect(mOuterRect);
13408 }
13409 
13410 /* inherits documentation from base class */
13411 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13412 {
13413  if (!mParentPlot) return -1;
13414  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
13415  return -1;
13416 
13417  if (mOuterRect.contains(pos.toPoint()))
13418  {
13419  if (details) details->setValue(spLegendBox);
13420  return mParentPlot->selectionTolerance()*0.99;
13421  }
13422  return -1;
13423 }
13424 
13425 /* inherits documentation from base class */
13426 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13427 {
13428  Q_UNUSED(event)
13429  mSelectedParts = selectedParts(); // in case item selection has changed
13430  if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
13431  {
13432  SelectableParts selBefore = mSelectedParts;
13433  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)
13434  if (selectionStateChanged)
13435  *selectionStateChanged = mSelectedParts != selBefore;
13436  }
13437 }
13438 
13439 /* inherits documentation from base class */
13440 void QCPLegend::deselectEvent(bool *selectionStateChanged)
13441 {
13442  mSelectedParts = selectedParts(); // in case item selection has changed
13443  if (mSelectableParts.testFlag(spLegendBox))
13444  {
13445  SelectableParts selBefore = mSelectedParts;
13446  setSelectedParts(selectedParts() & ~spLegendBox);
13447  if (selectionStateChanged)
13448  *selectionStateChanged = mSelectedParts != selBefore;
13449  }
13450 }
13451 
13452 /* inherits documentation from base class */
13454 {
13455  return QCP::iSelectLegend;
13456 }
13457 
13458 /* inherits documentation from base class */
13460 {
13461  return QCP::iSelectLegend;
13462 }
13463 
13464 /* inherits documentation from base class */
13466 {
13467  Q_UNUSED(parentPlot)
13468 }
13469 
13470 
13474 
13490 /* start documentation of signals */
13491 
13500 /* end documentation of signals */
13501 
13508  QCPLayoutElement(parentPlot),
13509  mFont(QFont(QLatin1String("sans serif"), 13*1.5, QFont::Bold)),
13510  mTextColor(Qt::black),
13511  mSelectedFont(QFont(QLatin1String("sans serif"), 13*1.6, QFont::Bold)),
13512  mSelectedTextColor(Qt::blue),
13513  mSelectable(false),
13514  mSelected(false)
13515 {
13516  if (parentPlot)
13517  {
13518  setLayer(parentPlot->currentLayer());
13519  mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
13520  mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
13521  }
13522  setMargins(QMargins(5, 5, 5, 0));
13523 }
13524 
13530  QCPLayoutElement(parentPlot),
13531  mText(text),
13532  mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
13533  mTextColor(Qt::black),
13534  mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
13535  mSelectedTextColor(Qt::blue),
13536  mSelectable(false),
13537  mSelected(false)
13538 {
13539  setLayer(QLatin1String("axes"));
13540  setMargins(QMargins(5, 5, 5, 0));
13541 }
13542 
13548 void QCPPlotTitle::setText(const QString &text)
13549 {
13550  mText = text;
13551 }
13552 
13558 void QCPPlotTitle::setFont(const QFont &font)
13559 {
13560  mFont = font;
13561 }
13562 
13568 void QCPPlotTitle::setTextColor(const QColor &color)
13569 {
13570  mTextColor = color;
13571 }
13572 
13579 {
13580  mSelectedFont = font;
13581 }
13582 
13588 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
13589 {
13590  mSelectedTextColor = color;
13591 }
13592 
13600 {
13601  if (mSelectable != selectable)
13602  {
13605  }
13606 }
13607 
13616 {
13617  if (mSelected != selected)
13618  {
13619  mSelected = selected;
13621  }
13622 }
13623 
13624 /* inherits documentation from base class */
13626 {
13628 }
13629 
13630 /* inherits documentation from base class */
13632 {
13633  painter->setFont(mainFont());
13634  painter->setPen(QPen(mainTextColor()));
13635  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
13636 }
13637 
13638 /* inherits documentation from base class */
13640 {
13641  QFontMetrics metrics(mFont);
13642  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13643  result.rwidth() += mMargins.left() + mMargins.right();
13644  result.rheight() += mMargins.top() + mMargins.bottom();
13645  return result;
13646 }
13647 
13648 /* inherits documentation from base class */
13650 {
13651  QFontMetrics metrics(mFont);
13652  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13653  result.rheight() += mMargins.top() + mMargins.bottom();
13654  result.setWidth(QWIDGETSIZE_MAX);
13655  return result;
13656 }
13657 
13658 /* inherits documentation from base class */
13659 void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13660 {
13661  Q_UNUSED(event)
13662  Q_UNUSED(details)
13663  if (mSelectable)
13664  {
13665  bool selBefore = mSelected;
13666  setSelected(additive ? !mSelected : true);
13667  if (selectionStateChanged)
13668  *selectionStateChanged = mSelected != selBefore;
13669  }
13670 }
13671 
13672 /* inherits documentation from base class */
13673 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
13674 {
13675  if (mSelectable)
13676  {
13677  bool selBefore = mSelected;
13678  setSelected(false);
13679  if (selectionStateChanged)
13680  *selectionStateChanged = mSelected != selBefore;
13681  }
13682 }
13683 
13684 /* inherits documentation from base class */
13685 double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13686 {
13687  Q_UNUSED(details)
13688  if (onlySelectable && !mSelectable)
13689  return -1;
13690 
13691  if (mTextBoundingRect.contains(pos.toPoint()))
13692  return mParentPlot->selectionTolerance()*0.99;
13693  else
13694  return -1;
13695 }
13696 
13703 {
13704  return mSelected ? mSelectedFont : mFont;
13705 }
13706 
13713 {
13715 }
13716 
13717 
13721 
13760 /* start documentation of inline functions */
13761 
13775 /* end documentation of signals */
13776 /* start documentation of signals */
13777 
13799 /* end documentation of signals */
13800 
13805  QCPLayoutElement(parentPlot),
13806  mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
13807  mDataScaleType(QCPAxis::stLinear),
13808  mBarWidth(20),
13809  mAxisRect(new QCPColorScaleAxisRectPrivate(this))
13810 {
13811  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)
13813  setDataRange(QCPRange(0, 6));
13814 }
13815 
13817 {
13818  delete mAxisRect;
13819 }
13820 
13821 /* undocumented getter */
13822 QString QCPColorScale::label() const
13823 {
13824  if (!mColorAxis)
13825  {
13826  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13827  return QString();
13828  }
13829 
13830  return mColorAxis.data()->label();
13831 }
13832 
13833 /* undocumented getter */
13835 {
13836  if (!mAxisRect)
13837  {
13838  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13839  return false;
13840  }
13841 
13842  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
13843  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
13844  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13845 }
13846 
13847 /* undocumented getter */
13849 {
13850  if (!mAxisRect)
13851  {
13852  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13853  return false;
13854  }
13855 
13856  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
13857  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
13858  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13859 }
13860 
13869 {
13870  if (!mAxisRect)
13871  {
13872  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13873  return;
13874  }
13875  if (mType != type)
13876  {
13877  mType = type;
13878  QCPRange rangeTransfer(0, 6);
13879  double logBaseTransfer = 10;
13880  QString labelTransfer;
13881  // revert some settings on old axis:
13882  if (mColorAxis)
13883  {
13884  rangeTransfer = mColorAxis.data()->range();
13885  labelTransfer = mColorAxis.data()->label();
13886  logBaseTransfer = mColorAxis.data()->scaleLogBase();
13887  mColorAxis.data()->setLabel(QString());
13888  disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13889  disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13890  }
13891  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
13892  foreach (QCPAxis::AxisType atype, allAxisTypes)
13893  {
13894  mAxisRect.data()->axis(atype)->setTicks(atype == mType);
13895  mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
13896  }
13897  // set new mColorAxis pointer:
13898  mColorAxis = mAxisRect.data()->axis(mType);
13899  // transfer settings to new axis:
13900  mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
13901  mColorAxis.data()->setLabel(labelTransfer);
13902  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
13903  connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13904  connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13905  mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
13906  QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
13907  }
13908 }
13909 
13920 {
13921  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
13922  {
13924  if (mColorAxis)
13925  mColorAxis.data()->setRange(mDataRange);
13927  }
13928 }
13929 
13941 {
13942  if (mDataScaleType != scaleType)
13943  {
13944  mDataScaleType = scaleType;
13945  if (mColorAxis)
13946  mColorAxis.data()->setScaleType(mDataScaleType);
13950  }
13951 }
13952 
13961 {
13962  if (mGradient != gradient)
13963  {
13964  mGradient = gradient;
13965  if (mAxisRect)
13966  mAxisRect.data()->mGradientImageInvalidated = true;
13967  emit gradientChanged(mGradient);
13968  }
13969 }
13970 
13975 void QCPColorScale::setLabel(const QString &str)
13976 {
13977  if (!mColorAxis)
13978  {
13979  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13980  return;
13981  }
13982 
13983  mColorAxis.data()->setLabel(str);
13984 }
13985 
13991 {
13992  mBarWidth = width;
13993 }
13994 
14002 {
14003  if (!mAxisRect)
14004  {
14005  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14006  return;
14007  }
14008 
14009  if (enabled)
14010  mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14011  else
14012  mAxisRect.data()->setRangeDrag(0);
14013 }
14014 
14022 {
14023  if (!mAxisRect)
14024  {
14025  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14026  return;
14027  }
14028 
14029  if (enabled)
14030  mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14031  else
14032  mAxisRect.data()->setRangeZoom(0);
14033 }
14034 
14038 QList<QCPColorMap*> QCPColorScale::colorMaps() const
14039 {
14040  QList<QCPColorMap*> result;
14041  for (int i=0; i<mParentPlot->plottableCount(); ++i)
14042  {
14043  if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
14044  if (cm->colorScale() == this)
14045  result.append(cm);
14046  }
14047  return result;
14048 }
14049 
14056 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
14057 {
14058  QList<QCPColorMap*> maps = colorMaps();
14059  QCPRange newRange;
14060  bool haveRange = false;
14061  int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
14063  sign = (mDataRange.upper < 0 ? -1 : 1);
14064  for (int i=0; i<maps.size(); ++i)
14065  {
14066  if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
14067  continue;
14068  QCPRange mapRange;
14069  if (maps.at(i)->colorScale() == this)
14070  {
14071  bool currentFoundRange = true;
14072  mapRange = maps.at(i)->data()->dataBounds();
14073  if (sign == 1)
14074  {
14075  if (mapRange.lower <= 0 && mapRange.upper > 0)
14076  mapRange.lower = mapRange.upper*1e-3;
14077  else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14078  currentFoundRange = false;
14079  } else if (sign == -1)
14080  {
14081  if (mapRange.upper >= 0 && mapRange.lower < 0)
14082  mapRange.upper = mapRange.lower*1e-3;
14083  else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14084  currentFoundRange = false;
14085  }
14086  if (currentFoundRange)
14087  {
14088  if (!haveRange)
14089  newRange = mapRange;
14090  else
14091  newRange.expand(mapRange);
14092  haveRange = true;
14093  }
14094  }
14095  }
14096  if (haveRange)
14097  {
14098  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
14099  {
14100  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
14102  {
14103  newRange.lower = center-mDataRange.size()/2.0;
14104  newRange.upper = center+mDataRange.size()/2.0;
14105  } else // mScaleType == stLogarithmic
14106  {
14107  newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
14108  newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
14109  }
14110  }
14111  setDataRange(newRange);
14112  }
14113 }
14114 
14115 /* inherits documentation from base class */
14117 {
14118  QCPLayoutElement::update(phase);
14119  if (!mAxisRect)
14120  {
14121  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14122  return;
14123  }
14124 
14125  mAxisRect.data()->update(phase);
14126 
14127  switch (phase)
14128  {
14129  case upMargins:
14130  {
14132  {
14133  setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14134  setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14135  } else
14136  {
14137  setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
14138  setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
14139  }
14140  break;
14141  }
14142  case upLayout:
14143  {
14144  mAxisRect.data()->setOuterRect(rect());
14145  break;
14146  }
14147  default: break;
14148  }
14149 }
14150 
14151 /* inherits documentation from base class */
14153 {
14154  painter->setAntialiasing(false);
14155 }
14156 
14157 /* inherits documentation from base class */
14158 void QCPColorScale::mousePressEvent(QMouseEvent *event)
14159 {
14160  if (!mAxisRect)
14161  {
14162  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14163  return;
14164  }
14165  mAxisRect.data()->mousePressEvent(event);
14166 }
14167 
14168 /* inherits documentation from base class */
14169 void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
14170 {
14171  if (!mAxisRect)
14172  {
14173  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14174  return;
14175  }
14176  mAxisRect.data()->mouseMoveEvent(event);
14177 }
14178 
14179 /* inherits documentation from base class */
14180 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
14181 {
14182  if (!mAxisRect)
14183  {
14184  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14185  return;
14186  }
14187  mAxisRect.data()->mouseReleaseEvent(event);
14188 }
14189 
14190 /* inherits documentation from base class */
14191 void QCPColorScale::wheelEvent(QWheelEvent *event)
14192 {
14193  if (!mAxisRect)
14194  {
14195  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14196  return;
14197  }
14198  mAxisRect.data()->wheelEvent(event);
14199 }
14200 
14204 
14220  QCPAxisRect(parentColorScale->parentPlot(), true),
14221  mParentColorScale(parentColorScale),
14222  mGradientImageInvalidated(true)
14223 {
14224  setParentLayerable(parentColorScale);
14225  setMinimumMargins(QMargins(0, 0, 0, 0));
14226  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14227  foreach (QCPAxis::AxisType type, allAxisTypes)
14228  {
14229  axis(type)->setVisible(true);
14230  axis(type)->grid()->setVisible(false);
14231  axis(type)->setPadding(0);
14232  connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14233  connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14234  }
14235 
14236  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14237  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14238  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14239  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14240  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14241  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14242  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14243  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14244 
14245  // make layer transfers of color scale transfer to axis rect and axes
14246  // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
14247  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
14248  foreach (QCPAxis::AxisType type, allAxisTypes)
14249  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
14250 }
14251 
14257 {
14260 
14261  bool mirrorHorz = false;
14262  bool mirrorVert = false;
14264  {
14265  mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
14266  mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
14267  }
14268 
14269  painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
14270  QCPAxisRect::draw(painter);
14271 }
14272 
14279 {
14280  if (rect().isEmpty())
14281  return;
14282 
14284  int w, h;
14285  QVector<double> data(n);
14286  for (int i=0; i<n; ++i)
14287  data[i] = i;
14289  {
14290  w = n;
14291  h = rect().height();
14292  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14293  QVector<QRgb*> pixels;
14294  for (int y=0; y<h; ++y)
14295  pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
14296  mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
14297  for (int y=1; y<h; ++y)
14298  memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
14299  } else
14300  {
14301  w = rect().width();
14302  h = n;
14303  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14304  for (int y=0; y<h; ++y)
14305  {
14306  QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
14307  const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
14308  for (int x=0; x<w; ++x)
14309  pixels[x] = lineColor;
14310  }
14311  }
14312  mGradientImageInvalidated = false;
14313 }
14314 
14320 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
14321 {
14322  // axis bases of four axes shall always (de-)selected synchronously:
14323  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14324  foreach (QCPAxis::AxisType type, allAxisTypes)
14325  {
14326  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14327  if (senderAxis->axisType() == type)
14328  continue;
14329 
14330  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14331  {
14332  if (selectedParts.testFlag(QCPAxis::spAxis))
14333  axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
14334  else
14335  axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
14336  }
14337  }
14338 }
14339 
14345 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
14346 {
14347  // synchronize axis base selectability:
14348  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14349  foreach (QCPAxis::AxisType type, allAxisTypes)
14350  {
14351  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14352  if (senderAxis->axisType() == type)
14353  continue;
14354 
14355  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14356  {
14357  if (selectableParts.testFlag(QCPAxis::spAxis))
14358  axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
14359  else
14360  axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
14361  }
14362  }
14363 }
14364 
14365 
14369 
14390  key(0),
14391  value(0),
14392  keyErrorPlus(0),
14393  keyErrorMinus(0),
14394  valueErrorPlus(0),
14395  valueErrorMinus(0)
14396 {
14397 }
14398 
14402 QCPData::QCPData(double key, double value) :
14403  key(key),
14404  value(value),
14405  keyErrorPlus(0),
14406  keyErrorMinus(0),
14407  valueErrorPlus(0),
14408  valueErrorMinus(0)
14409 {
14410 }
14411 
14412 
14416 
14455 /* start of documentation of inline functions */
14456 
14464 /* end of documentation of inline functions */
14465 
14477 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
14478  QCPAbstractPlottable(keyAxis, valueAxis)
14479 {
14480  mData = new QCPDataMap;
14481 
14482  setPen(QPen(Qt::blue, 0));
14483  setErrorPen(QPen(Qt::black));
14484  setBrush(Qt::NoBrush);
14485  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14486  setSelectedBrush(Qt::NoBrush);
14487 
14490  setErrorBarSize(6);
14491  setErrorBarSkipSymbol(true);
14493  setAdaptiveSampling(true);
14494 }
14495 
14497 {
14498  delete mData;
14499 }
14500 
14512 {
14513  if (mData == data)
14514  {
14515  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
14516  return;
14517  }
14518  if (copy)
14519  {
14520  *mData = *data;
14521  } else
14522  {
14523  delete mData;
14524  mData = data;
14525  }
14526 }
14527 
14534 void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
14535 {
14536  mData->clear();
14537  int n = key.size();
14538  n = qMin(n, value.size());
14539  QCPData newData;
14540  for (int i=0; i<n; ++i)
14541  {
14542  newData.key = key[i];
14543  newData.value = value[i];
14544  mData->insertMulti(newData.key, newData);
14545  }
14546 }
14547 
14557 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
14558 {
14559  mData->clear();
14560  int n = key.size();
14561  n = qMin(n, value.size());
14562  n = qMin(n, valueError.size());
14563  QCPData newData;
14564  for (int i=0; i<n; ++i)
14565  {
14566  newData.key = key[i];
14567  newData.value = value[i];
14568  newData.valueErrorMinus = valueError[i];
14569  newData.valueErrorPlus = valueError[i];
14570  mData->insertMulti(key[i], newData);
14571  }
14572 }
14573 
14583 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14584 {
14585  mData->clear();
14586  int n = key.size();
14587  n = qMin(n, value.size());
14588  n = qMin(n, valueErrorMinus.size());
14589  n = qMin(n, valueErrorPlus.size());
14590  QCPData newData;
14591  for (int i=0; i<n; ++i)
14592  {
14593  newData.key = key[i];
14594  newData.value = value[i];
14595  newData.valueErrorMinus = valueErrorMinus[i];
14596  newData.valueErrorPlus = valueErrorPlus[i];
14597  mData->insertMulti(key[i], newData);
14598  }
14599 }
14600 
14610 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
14611 {
14612  mData->clear();
14613  int n = key.size();
14614  n = qMin(n, value.size());
14615  n = qMin(n, keyError.size());
14616  QCPData newData;
14617  for (int i=0; i<n; ++i)
14618  {
14619  newData.key = key[i];
14620  newData.value = value[i];
14621  newData.keyErrorMinus = keyError[i];
14622  newData.keyErrorPlus = keyError[i];
14623  mData->insertMulti(key[i], newData);
14624  }
14625 }
14626 
14636 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
14637 {
14638  mData->clear();
14639  int n = key.size();
14640  n = qMin(n, value.size());
14641  n = qMin(n, keyErrorMinus.size());
14642  n = qMin(n, keyErrorPlus.size());
14643  QCPData newData;
14644  for (int i=0; i<n; ++i)
14645  {
14646  newData.key = key[i];
14647  newData.value = value[i];
14648  newData.keyErrorMinus = keyErrorMinus[i];
14649  newData.keyErrorPlus = keyErrorPlus[i];
14650  mData->insertMulti(key[i], newData);
14651  }
14652 }
14653 
14663 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
14664 {
14665  mData->clear();
14666  int n = key.size();
14667  n = qMin(n, value.size());
14668  n = qMin(n, valueError.size());
14669  n = qMin(n, keyError.size());
14670  QCPData newData;
14671  for (int i=0; i<n; ++i)
14672  {
14673  newData.key = key[i];
14674  newData.value = value[i];
14675  newData.keyErrorMinus = keyError[i];
14676  newData.keyErrorPlus = keyError[i];
14677  newData.valueErrorMinus = valueError[i];
14678  newData.valueErrorPlus = valueError[i];
14679  mData->insertMulti(key[i], newData);
14680  }
14681 }
14682 
14692 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)
14693 {
14694  mData->clear();
14695  int n = key.size();
14696  n = qMin(n, value.size());
14697  n = qMin(n, valueErrorMinus.size());
14698  n = qMin(n, valueErrorPlus.size());
14699  n = qMin(n, keyErrorMinus.size());
14700  n = qMin(n, keyErrorPlus.size());
14701  QCPData newData;
14702  for (int i=0; i<n; ++i)
14703  {
14704  newData.key = key[i];
14705  newData.value = value[i];
14706  newData.keyErrorMinus = keyErrorMinus[i];
14707  newData.keyErrorPlus = keyErrorPlus[i];
14708  newData.valueErrorMinus = valueErrorMinus[i];
14709  newData.valueErrorPlus = valueErrorPlus[i];
14710  mData->insertMulti(key[i], newData);
14711  }
14712 }
14713 
14714 
14722 {
14723  mLineStyle = ls;
14724 }
14725 
14733 {
14734  mScatterStyle = style;
14735 }
14736 
14746 {
14748 }
14749 
14754 void QCPGraph::setErrorPen(const QPen &pen)
14755 {
14756  mErrorPen = pen;
14757 }
14758 
14763 {
14764  mErrorBarSize = size;
14765 }
14766 
14779 {
14780  mErrorBarSkipSymbol = enabled;
14781 }
14782 
14793 {
14794  // prevent setting channel target to this graph itself:
14795  if (targetGraph == this)
14796  {
14797  qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
14798  mChannelFillGraph = 0;
14799  return;
14800  }
14801  // prevent setting channel target to a graph not in the plot:
14802  if (targetGraph && targetGraph->mParentPlot != mParentPlot)
14803  {
14804  qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
14805  mChannelFillGraph = 0;
14806  return;
14807  }
14808 
14809  mChannelFillGraph = targetGraph;
14810 }
14811 
14844 {
14845  mAdaptiveSampling = enabled;
14846 }
14847 
14856 void QCPGraph::addData(const QCPDataMap &dataMap)
14857 {
14858  mData->unite(dataMap);
14859 }
14860 
14870 {
14871  mData->insertMulti(data.key, data);
14872 }
14873 
14882 void QCPGraph::addData(double key, double value)
14883 {
14884  QCPData newData;
14885  newData.key = key;
14886  newData.value = value;
14887  mData->insertMulti(newData.key, newData);
14888 }
14889 
14898 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
14899 {
14900  int n = qMin(keys.size(), values.size());
14901  QCPData newData;
14902  for (int i=0; i<n; ++i)
14903  {
14904  newData.key = keys[i];
14905  newData.value = values[i];
14906  mData->insertMulti(newData.key, newData);
14907  }
14908 }
14909 
14915 {
14916  QCPDataMap::iterator it = mData->begin();
14917  while (it != mData->end() && it.key() < key)
14918  it = mData->erase(it);
14919 }
14920 
14926 {
14927  if (mData->isEmpty()) return;
14928  QCPDataMap::iterator it = mData->upperBound(key);
14929  while (it != mData->end())
14930  it = mData->erase(it);
14931 }
14932 
14940 void QCPGraph::removeData(double fromKey, double toKey)
14941 {
14942  if (fromKey >= toKey || mData->isEmpty()) return;
14943  QCPDataMap::iterator it = mData->upperBound(fromKey);
14944  QCPDataMap::iterator itEnd = mData->upperBound(toKey);
14945  while (it != itEnd)
14946  it = mData->erase(it);
14947 }
14948 
14957 void QCPGraph::removeData(double key)
14958 {
14959  mData->remove(key);
14960 }
14961 
14967 {
14968  mData->clear();
14969 }
14970 
14971 /* inherits documentation from base class */
14972 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
14973 {
14974  Q_UNUSED(details)
14975  if ((onlySelectable && !mSelectable) || mData->isEmpty())
14976  return -1;
14977  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
14978 
14979  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
14980  return pointDistance(pos);
14981  else
14982  return -1;
14983 }
14984 
14992 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
14993 {
14994  rescaleKeyAxis(onlyEnlarge, includeErrorBars);
14995  rescaleValueAxis(onlyEnlarge, includeErrorBars);
14996 }
14997 
15005 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
15006 {
15007  // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
15008  // that getKeyRange is passed the includeErrorBars value.
15009  if (mData->isEmpty()) return;
15010 
15011  QCPAxis *keyAxis = mKeyAxis.data();
15012  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15013 
15014  SignDomain signDomain = sdBoth;
15015  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
15016  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15017 
15018  bool foundRange;
15019  QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15020 
15021  if (foundRange)
15022  {
15023  if (onlyEnlarge)
15024  {
15025  if (keyAxis->range().lower < newRange.lower)
15026  newRange.lower = keyAxis->range().lower;
15027  if (keyAxis->range().upper > newRange.upper)
15028  newRange.upper = keyAxis->range().upper;
15029  }
15030  keyAxis->setRange(newRange);
15031  }
15032 }
15033 
15041 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
15042 {
15043  // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
15044  // is that getValueRange is passed the includeErrorBars value.
15045  if (mData->isEmpty()) return;
15046 
15047  QCPAxis *valueAxis = mValueAxis.data();
15048  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
15049 
15050  SignDomain signDomain = sdBoth;
15051  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
15052  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15053 
15054  bool foundRange;
15055  QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15056 
15057  if (foundRange)
15058  {
15059  if (onlyEnlarge)
15060  {
15061  if (valueAxis->range().lower < newRange.lower)
15062  newRange.lower = valueAxis->range().lower;
15063  if (valueAxis->range().upper > newRange.upper)
15064  newRange.upper = valueAxis->range().upper;
15065  }
15066  valueAxis->setRange(newRange);
15067  }
15068 }
15069 
15070 /* inherits documentation from base class */
15072 {
15073  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15074  if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15075  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15076 
15077  // allocate line and (if necessary) point vectors:
15078  QVector<QPointF> *lineData = new QVector<QPointF>;
15079  QVector<QCPData> *scatterData = 0;
15080  if (!mScatterStyle.isNone())
15081  scatterData = new QVector<QCPData>;
15082 
15083  // fill vectors with data appropriate to plot style:
15084  getPlotData(lineData, scatterData);
15085 
15086  // check data validity if flag set:
15087 #ifdef QCUSTOMPLOT_CHECK_DATA
15088  QCPDataMap::const_iterator it;
15089  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
15090  {
15091  if (QCP::isInvalidData(it.value().key, it.value().value) ||
15092  QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15093  QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
15094  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
15095  }
15096 #endif
15097 
15098  // draw fill of graph:
15099  drawFill(painter, lineData);
15100 
15101  // draw line:
15102  if (mLineStyle == lsImpulse)
15103  drawImpulsePlot(painter, lineData);
15104  else if (mLineStyle != lsNone)
15105  drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15106 
15107  // draw scatters:
15108  if (scatterData)
15109  drawScatterPlot(painter, scatterData);
15110 
15111  // free allocated line and point vectors:
15112  delete lineData;
15113  if (scatterData)
15114  delete scatterData;
15115 }
15116 
15117 /* inherits documentation from base class */
15118 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
15119 {
15120  // draw fill:
15121  if (mBrush.style() != Qt::NoBrush)
15122  {
15123  applyFillAntialiasingHint(painter);
15124  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
15125  }
15126  // draw line vertically centered:
15127  if (mLineStyle != lsNone)
15128  {
15130  painter->setPen(mPen);
15131  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
15132  }
15133  // draw scatter symbol:
15134  if (!mScatterStyle.isNone())
15135  {
15137  // scale scatter pixmap if it's too large to fit in legend icon rect:
15138  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
15139  {
15140  QCPScatterStyle scaledStyle(mScatterStyle);
15141  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15142  scaledStyle.applyTo(painter, mPen);
15143  scaledStyle.drawShape(painter, QRectF(rect).center());
15144  } else
15145  {
15146  mScatterStyle.applyTo(painter, mPen);
15147  mScatterStyle.drawShape(painter, QRectF(rect).center());
15148  }
15149  }
15150 }
15151 
15170 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
15171 {
15172  switch(mLineStyle)
15173  {
15174  case lsNone: getScatterPlotData(scatterData); break;
15175  case lsLine: getLinePlotData(lineData, scatterData); break;
15176  case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
15177  case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
15178  case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
15179  case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
15180  }
15181 }
15182 
15194 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15195 {
15196  getPreparedData(0, scatterData);
15197 }
15198 
15210 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15211 {
15212  QCPAxis *keyAxis = mKeyAxis.data();
15213  QCPAxis *valueAxis = mValueAxis.data();
15214  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15215  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15216 
15217  QVector<QCPData> lineData;
15218  getPreparedData(&lineData, scatterData);
15219  linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15220  linePixelData->resize(lineData.size());
15221 
15222  // transform lineData points to pixels:
15223  if (keyAxis->orientation() == Qt::Vertical)
15224  {
15225  for (int i=0; i<lineData.size(); ++i)
15226  {
15227  (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15228  (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15229  }
15230  } else // key axis is horizontal
15231  {
15232  for (int i=0; i<lineData.size(); ++i)
15233  {
15234  (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15235  (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15236  }
15237  }
15238 }
15239 
15251 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15252 {
15253  QCPAxis *keyAxis = mKeyAxis.data();
15254  QCPAxis *valueAxis = mValueAxis.data();
15255  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15256  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15257 
15258  QVector<QCPData> lineData;
15259  getPreparedData(&lineData, scatterData);
15260  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15261  linePixelData->resize(lineData.size()*2);
15262 
15263  // calculate steps from lineData and transform to pixel coordinates:
15264  if (keyAxis->orientation() == Qt::Vertical)
15265  {
15266  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15267  double key;
15268  for (int i=0; i<lineData.size(); ++i)
15269  {
15270  key = keyAxis->coordToPixel(lineData.at(i).key);
15271  (*linePixelData)[i*2+0].setX(lastValue);
15272  (*linePixelData)[i*2+0].setY(key);
15273  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15274  (*linePixelData)[i*2+1].setX(lastValue);
15275  (*linePixelData)[i*2+1].setY(key);
15276  }
15277  } else // key axis is horizontal
15278  {
15279  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15280  double key;
15281  for (int i=0; i<lineData.size(); ++i)
15282  {
15283  key = keyAxis->coordToPixel(lineData.at(i).key);
15284  (*linePixelData)[i*2+0].setX(key);
15285  (*linePixelData)[i*2+0].setY(lastValue);
15286  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15287  (*linePixelData)[i*2+1].setX(key);
15288  (*linePixelData)[i*2+1].setY(lastValue);
15289  }
15290  }
15291 }
15292 
15304 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15305 {
15306  QCPAxis *keyAxis = mKeyAxis.data();
15307  QCPAxis *valueAxis = mValueAxis.data();
15308  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15309  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15310 
15311  QVector<QCPData> lineData;
15312  getPreparedData(&lineData, scatterData);
15313  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15314  linePixelData->resize(lineData.size()*2);
15315 
15316  // calculate steps from lineData and transform to pixel coordinates:
15317  if (keyAxis->orientation() == Qt::Vertical)
15318  {
15319  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15320  double value;
15321  for (int i=0; i<lineData.size(); ++i)
15322  {
15323  value = valueAxis->coordToPixel(lineData.at(i).value);
15324  (*linePixelData)[i*2+0].setX(value);
15325  (*linePixelData)[i*2+0].setY(lastKey);
15326  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15327  (*linePixelData)[i*2+1].setX(value);
15328  (*linePixelData)[i*2+1].setY(lastKey);
15329  }
15330  } else // key axis is horizontal
15331  {
15332  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15333  double value;
15334  for (int i=0; i<lineData.size(); ++i)
15335  {
15336  value = valueAxis->coordToPixel(lineData.at(i).value);
15337  (*linePixelData)[i*2+0].setX(lastKey);
15338  (*linePixelData)[i*2+0].setY(value);
15339  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15340  (*linePixelData)[i*2+1].setX(lastKey);
15341  (*linePixelData)[i*2+1].setY(value);
15342  }
15343  }
15344 }
15345 
15357 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15358 {
15359  QCPAxis *keyAxis = mKeyAxis.data();
15360  QCPAxis *valueAxis = mValueAxis.data();
15361  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15362  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15363 
15364  QVector<QCPData> lineData;
15365  getPreparedData(&lineData, scatterData);
15366  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15367  linePixelData->resize(lineData.size()*2);
15368  // calculate steps from lineData and transform to pixel coordinates:
15369  if (keyAxis->orientation() == Qt::Vertical)
15370  {
15371  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15372  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15373  double key;
15374  (*linePixelData)[0].setX(lastValue);
15375  (*linePixelData)[0].setY(lastKey);
15376  for (int i=1; i<lineData.size(); ++i)
15377  {
15378  key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15379  (*linePixelData)[i*2-1].setX(lastValue);
15380  (*linePixelData)[i*2-1].setY(key);
15381  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15382  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15383  (*linePixelData)[i*2+0].setX(lastValue);
15384  (*linePixelData)[i*2+0].setY(key);
15385  }
15386  (*linePixelData)[lineData.size()*2-1].setX(lastValue);
15387  (*linePixelData)[lineData.size()*2-1].setY(lastKey);
15388  } else // key axis is horizontal
15389  {
15390  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15391  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15392  double key;
15393  (*linePixelData)[0].setX(lastKey);
15394  (*linePixelData)[0].setY(lastValue);
15395  for (int i=1; i<lineData.size(); ++i)
15396  {
15397  key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15398  (*linePixelData)[i*2-1].setX(key);
15399  (*linePixelData)[i*2-1].setY(lastValue);
15400  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15401  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15402  (*linePixelData)[i*2+0].setX(key);
15403  (*linePixelData)[i*2+0].setY(lastValue);
15404  }
15405  (*linePixelData)[lineData.size()*2-1].setX(lastKey);
15406  (*linePixelData)[lineData.size()*2-1].setY(lastValue);
15407  }
15408 
15409 }
15410 
15422 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15423 {
15424  QCPAxis *keyAxis = mKeyAxis.data();
15425  QCPAxis *valueAxis = mValueAxis.data();
15426  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15427  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15428 
15429  QVector<QCPData> lineData;
15430  getPreparedData(&lineData, scatterData);
15431  linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
15432 
15433  // transform lineData points to pixels:
15434  if (keyAxis->orientation() == Qt::Vertical)
15435  {
15436  double zeroPointX = valueAxis->coordToPixel(0);
15437  double key;
15438  for (int i=0; i<lineData.size(); ++i)
15439  {
15440  key = keyAxis->coordToPixel(lineData.at(i).key);
15441  (*linePixelData)[i*2+0].setX(zeroPointX);
15442  (*linePixelData)[i*2+0].setY(key);
15443  (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
15444  (*linePixelData)[i*2+1].setY(key);
15445  }
15446  } else // key axis is horizontal
15447  {
15448  double zeroPointY = valueAxis->coordToPixel(0);
15449  double key;
15450  for (int i=0; i<lineData.size(); ++i)
15451  {
15452  key = keyAxis->coordToPixel(lineData.at(i).key);
15453  (*linePixelData)[i*2+0].setX(key);
15454  (*linePixelData)[i*2+0].setY(zeroPointY);
15455  (*linePixelData)[i*2+1].setX(key);
15456  (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
15457  }
15458  }
15459 }
15460 
15474 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
15475 {
15476  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
15477  if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
15478 
15479  applyFillAntialiasingHint(painter);
15480  if (!mChannelFillGraph)
15481  {
15482  // draw base fill under graph, fill goes all the way to the zero-value-line:
15483  addFillBasePoints(lineData);
15484  painter->setPen(Qt::NoPen);
15485  painter->setBrush(mainBrush());
15486  painter->drawPolygon(QPolygonF(*lineData));
15487  removeFillBasePoints(lineData);
15488  } else
15489  {
15490  // draw channel fill between this graph and mChannelFillGraph:
15491  painter->setPen(Qt::NoPen);
15492  painter->setBrush(mainBrush());
15493  painter->drawPolygon(getChannelFillPolygon(lineData));
15494  }
15495 }
15496 
15506 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
15507 {
15508  QCPAxis *keyAxis = mKeyAxis.data();
15509  QCPAxis *valueAxis = mValueAxis.data();
15510  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15511 
15512  // draw error bars:
15513  if (mErrorType != etNone)
15514  {
15516  painter->setPen(mErrorPen);
15517  if (keyAxis->orientation() == Qt::Vertical)
15518  {
15519  for (int i=0; i<scatterData->size(); ++i)
15520  drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
15521  } else
15522  {
15523  for (int i=0; i<scatterData->size(); ++i)
15524  drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
15525  }
15526  }
15527 
15528  // draw scatter point symbols:
15530  mScatterStyle.applyTo(painter, mPen);
15531  if (keyAxis->orientation() == Qt::Vertical)
15532  {
15533  for (int i=0; i<scatterData->size(); ++i)
15534  if (!qIsNaN(scatterData->at(i).value))
15535  mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
15536  } else
15537  {
15538  for (int i=0; i<scatterData->size(); ++i)
15539  if (!qIsNaN(scatterData->at(i).value))
15540  mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
15541  }
15542 }
15543 
15553 void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15554 {
15555  // draw line of graph:
15556  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15557  {
15559  painter->setPen(mainPen());
15560  painter->setBrush(Qt::NoBrush);
15561 
15562  /* Draws polyline in batches, currently not used:
15563  int p = 0;
15564  while (p < lineData->size())
15565  {
15566  int batch = qMin(25, lineData->size()-p);
15567  if (p != 0)
15568  {
15569  ++batch;
15570  --p; // to draw the connection lines between two batches
15571  }
15572  painter->drawPolyline(lineData->constData()+p, batch);
15573  p += batch;
15574  }
15575  */
15576 
15577  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
15578  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
15579  painter->pen().style() == Qt::SolidLine &&
15580  !painter->modes().testFlag(QCPPainter::pmVectorized) &&
15581  !painter->modes().testFlag(QCPPainter::pmNoCaching))
15582  {
15583  int i = 0;
15584  bool lastIsNan = false;
15585  const int lineDataSize = lineData->size();
15586  while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
15587  ++i;
15588  ++i; // because drawing works in 1 point retrospect
15589  while (i < lineDataSize)
15590  {
15591  if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
15592  {
15593  if (!lastIsNan)
15594  painter->drawLine(lineData->at(i-1), lineData->at(i));
15595  else
15596  lastIsNan = false;
15597  } else
15598  lastIsNan = true;
15599  ++i;
15600  }
15601  } else
15602  {
15603  int segmentStart = 0;
15604  int i = 0;
15605  const int lineDataSize = lineData->size();
15606  while (i < lineDataSize)
15607  {
15608  if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
15609  {
15610  painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
15611  segmentStart = i+1;
15612  }
15613  ++i;
15614  }
15615  // draw last segment:
15616  painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
15617  }
15618  }
15619 }
15620 
15628 void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15629 {
15630  // draw impulses:
15631  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15632  {
15634  QPen pen = mainPen();
15635  pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
15636  painter->setPen(pen);
15637  painter->setBrush(Qt::NoBrush);
15638  painter->drawLines(*lineData);
15639  }
15640 }
15641 
15654 void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
15655 {
15656  QCPAxis *keyAxis = mKeyAxis.data();
15657  QCPAxis *valueAxis = mValueAxis.data();
15658  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15659  // get visible data range:
15660  QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
15661  getVisibleDataBounds(lower, upper);
15662  if (lower == mData->constEnd() || upper == mData->constEnd())
15663  return;
15664 
15665  // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
15666  int maxCount = std::numeric_limits<int>::max();
15667  if (mAdaptiveSampling)
15668  {
15669  int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
15670  maxCount = 2*keyPixelSpan+2;
15671  }
15672  int dataCount = countDataInBounds(lower, upper, maxCount);
15673 
15674  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
15675  {
15676  if (lineData)
15677  {
15678  QCPDataMap::const_iterator it = lower;
15679  QCPDataMap::const_iterator upperEnd = upper+1;
15680  double minValue = it.value().value;
15681  double maxValue = it.value().value;
15682  QCPDataMap::const_iterator currentIntervalFirstPoint = it;
15683  int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15684  int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15685  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15686  double lastIntervalEndKey = currentIntervalStartKey;
15687  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15688  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15689  int intervalDataCount = 1;
15690  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15691  while (it != upperEnd)
15692  {
15693  if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
15694  {
15695  if (it.value().value < minValue)
15696  minValue = it.value().value;
15697  else if (it.value().value > maxValue)
15698  maxValue = it.value().value;
15699  ++intervalDataCount;
15700  } else // new pixel interval started
15701  {
15702  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15703  {
15704  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
15705  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15706  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15707  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15708  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
15709  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
15710  } else
15711  lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15712  lastIntervalEndKey = (it-1).value().key;
15713  minValue = it.value().value;
15714  maxValue = it.value().value;
15715  currentIntervalFirstPoint = it;
15716  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15717  if (keyEpsilonVariable)
15718  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15719  intervalDataCount = 1;
15720  }
15721  ++it;
15722  }
15723  // handle last interval:
15724  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15725  {
15726  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
15727  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15728  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15729  lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15730  } else
15731  lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15732  }
15733 
15734  if (scatterData)
15735  {
15736  double valueMaxRange = valueAxis->range().upper;
15737  double valueMinRange = valueAxis->range().lower;
15738  QCPDataMap::const_iterator it = lower;
15739  QCPDataMap::const_iterator upperEnd = upper+1;
15740  double minValue = it.value().value;
15741  double maxValue = it.value().value;
15742  QCPDataMap::const_iterator minValueIt = it;
15743  QCPDataMap::const_iterator maxValueIt = it;
15744  QCPDataMap::const_iterator currentIntervalStart = it;
15745  int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15746  int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15747  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15748  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15749  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15750  int intervalDataCount = 1;
15751  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15752  while (it != upperEnd)
15753  {
15754  if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
15755  {
15756  if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15757  {
15758  minValue = it.value().value;
15759  minValueIt = it;
15760  } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15761  {
15762  maxValue = it.value().value;
15763  maxValueIt = it;
15764  }
15765  ++intervalDataCount;
15766  } else // new pixel started
15767  {
15768  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15769  {
15770  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15771  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15772  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15773  QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15774  int c = 0;
15775  while (intervalIt != it)
15776  {
15777  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15778  scatterData->append(intervalIt.value());
15779  ++c;
15780  ++intervalIt;
15781  }
15782  } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15783  scatterData->append(currentIntervalStart.value());
15784  minValue = it.value().value;
15785  maxValue = it.value().value;
15786  currentIntervalStart = it;
15787  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15788  if (keyEpsilonVariable)
15789  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15790  intervalDataCount = 1;
15791  }
15792  ++it;
15793  }
15794  // handle last interval:
15795  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15796  {
15797  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15798  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15799  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15800  QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15801  int c = 0;
15802  while (intervalIt != it)
15803  {
15804  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15805  scatterData->append(intervalIt.value());
15806  ++c;
15807  ++intervalIt;
15808  }
15809  } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15810  scatterData->append(currentIntervalStart.value());
15811  }
15812  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
15813  {
15814  QVector<QCPData> *dataVector = 0;
15815  if (lineData)
15816  dataVector = lineData;
15817  else if (scatterData)
15818  dataVector = scatterData;
15819  if (dataVector)
15820  {
15821  QCPDataMap::const_iterator it = lower;
15822  QCPDataMap::const_iterator upperEnd = upper+1;
15823  dataVector->reserve(dataCount+2); // +2 for possible fill end points
15824  while (it != upperEnd)
15825  {
15826  dataVector->append(it.value());
15827  ++it;
15828  }
15829  }
15830  if (lineData && scatterData)
15831  *scatterData = *dataVector;
15832  }
15833 }
15834 
15842 void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
15843 {
15844  if (qIsNaN(data.value))
15845  return;
15846  QCPAxis *keyAxis = mKeyAxis.data();
15847  QCPAxis *valueAxis = mValueAxis.data();
15848  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15849 
15850  double a, b; // positions of error bar bounds in pixels
15851  double barWidthHalf = mErrorBarSize*0.5;
15852  double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
15853 
15854  if (keyAxis->orientation() == Qt::Vertical)
15855  {
15856  // draw key error vertically and value error horizontally
15857  if (mErrorType == etKey || mErrorType == etBoth)
15858  {
15859  a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15860  b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15861  if (keyAxis->rangeReversed())
15862  qSwap(a,b);
15863  // draw spine:
15864  if (mErrorBarSkipSymbol)
15865  {
15866  if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15867  painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15868  if (y-b > skipSymbolMargin)
15869  painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15870  } else
15871  painter->drawLine(QLineF(x, a, x, b));
15872  // draw handles:
15873  painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15874  painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15875  }
15876  if (mErrorType == etValue || mErrorType == etBoth)
15877  {
15878  a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15879  b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15880  if (valueAxis->rangeReversed())
15881  qSwap(a,b);
15882  // draw spine:
15883  if (mErrorBarSkipSymbol)
15884  {
15885  if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15886  painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15887  if (b-x > skipSymbolMargin)
15888  painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15889  } else
15890  painter->drawLine(QLineF(a, y, b, y));
15891  // draw handles:
15892  painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15893  painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15894  }
15895  } else // mKeyAxis->orientation() is Qt::Horizontal
15896  {
15897  // draw value error vertically and key error horizontally
15898  if (mErrorType == etKey || mErrorType == etBoth)
15899  {
15900  a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15901  b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15902  if (keyAxis->rangeReversed())
15903  qSwap(a,b);
15904  // draw spine:
15905  if (mErrorBarSkipSymbol)
15906  {
15907  if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15908  painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15909  if (b-x > skipSymbolMargin)
15910  painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15911  } else
15912  painter->drawLine(QLineF(a, y, b, y));
15913  // draw handles:
15914  painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15915  painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15916  }
15917  if (mErrorType == etValue || mErrorType == etBoth)
15918  {
15919  a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15920  b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15921  if (valueAxis->rangeReversed())
15922  qSwap(a,b);
15923  // draw spine:
15924  if (mErrorBarSkipSymbol)
15925  {
15926  if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15927  painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15928  if (y-b > skipSymbolMargin)
15929  painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15930  } else
15931  painter->drawLine(QLineF(x, a, x, b));
15932  // draw handles:
15933  painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15934  painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15935  }
15936  }
15937 }
15938 
15953 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
15954 {
15955  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15956  if (mData->isEmpty())
15957  {
15958  lower = mData->constEnd();
15959  upper = mData->constEnd();
15960  return;
15961  }
15962 
15963  // get visible data range as QMap iterators
15964  QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
15965  QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
15966  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
15967  bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
15968 
15969  lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
15970  upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
15971 }
15972 
15983 int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
15984 {
15985  if (upper == mData->constEnd() && lower == mData->constEnd())
15986  return 0;
15987  QCPDataMap::const_iterator it = lower;
15988  int count = 1;
15989  while (it != upper && count < maxCount)
15990  {
15991  ++it;
15992  ++count;
15993  }
15994  return count;
15995 }
15996 
16012 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
16013 {
16014  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16015 
16016  // append points that close the polygon fill at the key axis:
16017  if (mKeyAxis.data()->orientation() == Qt::Vertical)
16018  {
16019  *lineData << upperFillBasePoint(lineData->last().y());
16020  *lineData << lowerFillBasePoint(lineData->first().y());
16021  } else
16022  {
16023  *lineData << upperFillBasePoint(lineData->last().x());
16024  *lineData << lowerFillBasePoint(lineData->first().x());
16025  }
16026 }
16027 
16034 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
16035 {
16036  lineData->remove(lineData->size()-2, 2);
16037 }
16038 
16053 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
16054 {
16055  QCPAxis *keyAxis = mKeyAxis.data();
16056  QCPAxis *valueAxis = mValueAxis.data();
16057  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16058 
16059  QPointF point;
16060  if (valueAxis->scaleType() == QCPAxis::stLinear)
16061  {
16062  if (keyAxis->axisType() == QCPAxis::atLeft)
16063  {
16064  point.setX(valueAxis->coordToPixel(0));
16065  point.setY(lowerKey);
16066  } else if (keyAxis->axisType() == QCPAxis::atRight)
16067  {
16068  point.setX(valueAxis->coordToPixel(0));
16069  point.setY(lowerKey);
16070  } else if (keyAxis->axisType() == QCPAxis::atTop)
16071  {
16072  point.setX(lowerKey);
16073  point.setY(valueAxis->coordToPixel(0));
16074  } else if (keyAxis->axisType() == QCPAxis::atBottom)
16075  {
16076  point.setX(lowerKey);
16077  point.setY(valueAxis->coordToPixel(0));
16078  }
16079  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16080  {
16081  // In logarithmic scaling we can't just draw to value zero so we just fill all the way
16082  // to the axis which is in the direction towards zero
16083  if (keyAxis->orientation() == Qt::Vertical)
16084  {
16085  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16086  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16087  point.setX(keyAxis->axisRect()->right());
16088  else
16089  point.setX(keyAxis->axisRect()->left());
16090  point.setY(lowerKey);
16091  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16092  {
16093  point.setX(lowerKey);
16094  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16095  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16096  point.setY(keyAxis->axisRect()->top());
16097  else
16098  point.setY(keyAxis->axisRect()->bottom());
16099  }
16100  }
16101  return point;
16102 }
16103 
16118 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
16119 {
16120  QCPAxis *keyAxis = mKeyAxis.data();
16121  QCPAxis *valueAxis = mValueAxis.data();
16122  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16123 
16124  QPointF point;
16125  if (valueAxis->scaleType() == QCPAxis::stLinear)
16126  {
16127  if (keyAxis->axisType() == QCPAxis::atLeft)
16128  {
16129  point.setX(valueAxis->coordToPixel(0));
16130  point.setY(upperKey);
16131  } else if (keyAxis->axisType() == QCPAxis::atRight)
16132  {
16133  point.setX(valueAxis->coordToPixel(0));
16134  point.setY(upperKey);
16135  } else if (keyAxis->axisType() == QCPAxis::atTop)
16136  {
16137  point.setX(upperKey);
16138  point.setY(valueAxis->coordToPixel(0));
16139  } else if (keyAxis->axisType() == QCPAxis::atBottom)
16140  {
16141  point.setX(upperKey);
16142  point.setY(valueAxis->coordToPixel(0));
16143  }
16144  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16145  {
16146  // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
16147  // to the axis which is in the direction towards 0
16148  if (keyAxis->orientation() == Qt::Vertical)
16149  {
16150  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16151  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16152  point.setX(keyAxis->axisRect()->right());
16153  else
16154  point.setX(keyAxis->axisRect()->left());
16155  point.setY(upperKey);
16156  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16157  {
16158  point.setX(upperKey);
16159  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16160  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16161  point.setY(keyAxis->axisRect()->top());
16162  else
16163  point.setY(keyAxis->axisRect()->bottom());
16164  }
16165  }
16166  return point;
16167 }
16168 
16178 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
16179 {
16180  if (!mChannelFillGraph)
16181  return QPolygonF();
16182 
16183  QCPAxis *keyAxis = mKeyAxis.data();
16184  QCPAxis *valueAxis = mValueAxis.data();
16185  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
16186  if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
16187 
16188  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
16189  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)
16190 
16191  if (lineData->isEmpty()) return QPolygonF();
16192  QVector<QPointF> otherData;
16193  mChannelFillGraph.data()->getPlotData(&otherData, 0);
16194  if (otherData.isEmpty()) return QPolygonF();
16195  QVector<QPointF> thisData;
16196  thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
16197  for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
16198  thisData << lineData->at(i);
16199 
16200  // pointers to be able to swap them, depending which data range needs cropping:
16201  QVector<QPointF> *staticData = &thisData;
16202  QVector<QPointF> *croppedData = &otherData;
16203 
16204  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
16205  if (keyAxis->orientation() == Qt::Horizontal)
16206  {
16207  // x is key
16208  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16209  if (staticData->first().x() > staticData->last().x())
16210  {
16211  int size = staticData->size();
16212  for (int i=0; i<size/2; ++i)
16213  qSwap((*staticData)[i], (*staticData)[size-1-i]);
16214  }
16215  if (croppedData->first().x() > croppedData->last().x())
16216  {
16217  int size = croppedData->size();
16218  for (int i=0; i<size/2; ++i)
16219  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16220  }
16221  // crop lower bound:
16222  if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
16223  qSwap(staticData, croppedData);
16224  int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16225  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16226  croppedData->remove(0, lowBound);
16227  // set lowest point of cropped data to fit exactly key position of first static data
16228  // point via linear interpolation:
16229  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16230  double slope;
16231  if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
16232  slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
16233  else
16234  slope = 0;
16235  (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
16236  (*croppedData)[0].setX(staticData->first().x());
16237 
16238  // crop upper bound:
16239  if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
16240  qSwap(staticData, croppedData);
16241  int highBound = findIndexAboveX(croppedData, staticData->last().x());
16242  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16243  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16244  // set highest point of cropped data to fit exactly key position of last static data
16245  // point via linear interpolation:
16246  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16247  int li = croppedData->size()-1; // last index
16248  if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
16249  slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
16250  else
16251  slope = 0;
16252  (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
16253  (*croppedData)[li].setX(staticData->last().x());
16254  } else // mKeyAxis->orientation() == Qt::Vertical
16255  {
16256  // y is key
16257  // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
16258  // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
16259  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16260  if (staticData->first().y() < staticData->last().y())
16261  {
16262  int size = staticData->size();
16263  for (int i=0; i<size/2; ++i)
16264  qSwap((*staticData)[i], (*staticData)[size-1-i]);
16265  }
16266  if (croppedData->first().y() < croppedData->last().y())
16267  {
16268  int size = croppedData->size();
16269  for (int i=0; i<size/2; ++i)
16270  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16271  }
16272  // crop lower bound:
16273  if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
16274  qSwap(staticData, croppedData);
16275  int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16276  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16277  croppedData->remove(0, lowBound);
16278  // set lowest point of cropped data to fit exactly key position of first static data
16279  // point via linear interpolation:
16280  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16281  double slope;
16282  if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
16283  slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
16284  else
16285  slope = 0;
16286  (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
16287  (*croppedData)[0].setY(staticData->first().y());
16288 
16289  // crop upper bound:
16290  if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
16291  qSwap(staticData, croppedData);
16292  int highBound = findIndexBelowY(croppedData, staticData->last().y());
16293  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16294  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16295  // set highest point of cropped data to fit exactly key position of last static data
16296  // point via linear interpolation:
16297  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16298  int li = croppedData->size()-1; // last index
16299  if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
16300  slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
16301  else
16302  slope = 0;
16303  (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
16304  (*croppedData)[li].setY(staticData->last().y());
16305  }
16306 
16307  // return joined:
16308  for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
16309  thisData << otherData.at(i);
16310  return QPolygonF(thisData);
16311 }
16312 
16320 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
16321 {
16322  for (int i=data->size()-1; i>=0; --i)
16323  {
16324  if (data->at(i).x() < x)
16325  {
16326  if (i<data->size()-1)
16327  return i+1;
16328  else
16329  return data->size()-1;
16330  }
16331  }
16332  return -1;
16333 }
16334 
16342 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
16343 {
16344  for (int i=0; i<data->size(); ++i)
16345  {
16346  if (data->at(i).x() > x)
16347  {
16348  if (i>0)
16349  return i-1;
16350  else
16351  return 0;
16352  }
16353  }
16354  return -1;
16355 }
16356 
16364 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
16365 {
16366  for (int i=0; i<data->size(); ++i)
16367  {
16368  if (data->at(i).y() < y)
16369  {
16370  if (i>0)
16371  return i-1;
16372  else
16373  return 0;
16374  }
16375  }
16376  return -1;
16377 }
16378 
16388 double QCPGraph::pointDistance(const QPointF &pixelPoint) const
16389 {
16390  if (mData->isEmpty())
16391  return -1.0;
16392  if (mLineStyle == lsNone && mScatterStyle.isNone())
16393  return -1.0;
16394 
16395  // calculate minimum distances to graph representation:
16396  if (mLineStyle == lsNone)
16397  {
16398  // no line displayed, only calculate distance to scatter points:
16399  QVector<QCPData> scatterData;
16400  getScatterPlotData(&scatterData);
16401  if (scatterData.size() > 0)
16402  {
16403  double minDistSqr = std::numeric_limits<double>::max();
16404  for (int i=0; i<scatterData.size(); ++i)
16405  {
16406  double currentDistSqr = QVector2D(coordsToPixels(scatterData.at(i).key, scatterData.at(i).value)-pixelPoint).lengthSquared();
16407  if (currentDistSqr < minDistSqr)
16408  minDistSqr = currentDistSqr;
16409  }
16410  return qSqrt(minDistSqr);
16411  } else // no data available in view to calculate distance to
16412  return -1.0;
16413  } else
16414  {
16415  // line displayed, calculate distance to line segments:
16416  QVector<QPointF> lineData;
16417  getPlotData(&lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
16418  if (lineData.size() > 1) // at least one line segment, compare distance to line segments
16419  {
16420  double minDistSqr = std::numeric_limits<double>::max();
16421  if (mLineStyle == lsImpulse)
16422  {
16423  // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
16424  for (int i=0; i<lineData.size()-1; i+=2) // iterate pairs
16425  {
16426  double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16427  if (currentDistSqr < minDistSqr)
16428  minDistSqr = currentDistSqr;
16429  }
16430  } else
16431  {
16432  // all other line plots (line and step) connect points directly:
16433  for (int i=0; i<lineData.size()-1; ++i)
16434  {
16435  double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16436  if (currentDistSqr < minDistSqr)
16437  minDistSqr = currentDistSqr;
16438  }
16439  }
16440  return qSqrt(minDistSqr);
16441  } else if (lineData.size() > 0) // only single data point, calculate distance to that point
16442  {
16443  return QVector2D(lineData.at(0)-pixelPoint).length();
16444  } else // no data available in view to calculate distance to
16445  return -1.0;
16446  }
16447 }
16448 
16457 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
16458 {
16459  for (int i=data->size()-1; i>=0; --i)
16460  {
16461  if (data->at(i).y() > y)
16462  {
16463  if (i<data->size()-1)
16464  return i+1;
16465  else
16466  return data->size()-1;
16467  }
16468  }
16469  return -1;
16470 }
16471 
16472 /* inherits documentation from base class */
16473 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
16474 {
16475  // just call the specialized version which takes an additional argument whether error bars
16476  // should also be taken into consideration for range calculation. We set this to true here.
16477  return getKeyRange(foundRange, inSignDomain, true);
16478 }
16479 
16480 /* inherits documentation from base class */
16481 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
16482 {
16483  // just call the specialized version which takes an additional argument whether error bars
16484  // should also be taken into consideration for range calculation. We set this to true here.
16485  return getValueRange(foundRange, inSignDomain, true);
16486 }
16487 
16494 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16495 {
16496  QCPRange range;
16497  bool haveLower = false;
16498  bool haveUpper = false;
16499 
16500  double current, currentErrorMinus, currentErrorPlus;
16501 
16502  if (inSignDomain == sdBoth) // range may be anywhere
16503  {
16504  QCPDataMap::const_iterator it = mData->constBegin();
16505  while (it != mData->constEnd())
16506  {
16507  if (!qIsNaN(it.value().value))
16508  {
16509  current = it.value().key;
16510  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16511  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16512  if (current-currentErrorMinus < range.lower || !haveLower)
16513  {
16514  range.lower = current-currentErrorMinus;
16515  haveLower = true;
16516  }
16517  if (current+currentErrorPlus > range.upper || !haveUpper)
16518  {
16519  range.upper = current+currentErrorPlus;
16520  haveUpper = true;
16521  }
16522  }
16523  ++it;
16524  }
16525  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16526  {
16527  QCPDataMap::const_iterator it = mData->constBegin();
16528  while (it != mData->constEnd())
16529  {
16530  if (!qIsNaN(it.value().value))
16531  {
16532  current = it.value().key;
16533  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16534  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16535  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16536  {
16537  range.lower = current-currentErrorMinus;
16538  haveLower = true;
16539  }
16540  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16541  {
16542  range.upper = current+currentErrorPlus;
16543  haveUpper = true;
16544  }
16545  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16546  {
16547  if ((current < range.lower || !haveLower) && current < 0)
16548  {
16549  range.lower = current;
16550  haveLower = true;
16551  }
16552  if ((current > range.upper || !haveUpper) && current < 0)
16553  {
16554  range.upper = current;
16555  haveUpper = true;
16556  }
16557  }
16558  }
16559  ++it;
16560  }
16561  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16562  {
16563  QCPDataMap::const_iterator it = mData->constBegin();
16564  while (it != mData->constEnd())
16565  {
16566  if (!qIsNaN(it.value().value))
16567  {
16568  current = it.value().key;
16569  currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16570  currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16571  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16572  {
16573  range.lower = current-currentErrorMinus;
16574  haveLower = true;
16575  }
16576  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16577  {
16578  range.upper = current+currentErrorPlus;
16579  haveUpper = true;
16580  }
16581  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16582  {
16583  if ((current < range.lower || !haveLower) && current > 0)
16584  {
16585  range.lower = current;
16586  haveLower = true;
16587  }
16588  if ((current > range.upper || !haveUpper) && current > 0)
16589  {
16590  range.upper = current;
16591  haveUpper = true;
16592  }
16593  }
16594  }
16595  ++it;
16596  }
16597  }
16598 
16599  foundRange = haveLower && haveUpper;
16600  return range;
16601 }
16602 
16609 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16610 {
16611  QCPRange range;
16612  bool haveLower = false;
16613  bool haveUpper = false;
16614 
16615  double current, currentErrorMinus, currentErrorPlus;
16616 
16617  if (inSignDomain == sdBoth) // range may be anywhere
16618  {
16619  QCPDataMap::const_iterator it = mData->constBegin();
16620  while (it != mData->constEnd())
16621  {
16622  current = it.value().value;
16623  if (!qIsNaN(current))
16624  {
16625  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16626  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16627  if (current-currentErrorMinus < range.lower || !haveLower)
16628  {
16629  range.lower = current-currentErrorMinus;
16630  haveLower = true;
16631  }
16632  if (current+currentErrorPlus > range.upper || !haveUpper)
16633  {
16634  range.upper = current+currentErrorPlus;
16635  haveUpper = true;
16636  }
16637  }
16638  ++it;
16639  }
16640  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16641  {
16642  QCPDataMap::const_iterator it = mData->constBegin();
16643  while (it != mData->constEnd())
16644  {
16645  current = it.value().value;
16646  if (!qIsNaN(current))
16647  {
16648  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16649  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16650  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16651  {
16652  range.lower = current-currentErrorMinus;
16653  haveLower = true;
16654  }
16655  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16656  {
16657  range.upper = current+currentErrorPlus;
16658  haveUpper = true;
16659  }
16660  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16661  {
16662  if ((current < range.lower || !haveLower) && current < 0)
16663  {
16664  range.lower = current;
16665  haveLower = true;
16666  }
16667  if ((current > range.upper || !haveUpper) && current < 0)
16668  {
16669  range.upper = current;
16670  haveUpper = true;
16671  }
16672  }
16673  }
16674  ++it;
16675  }
16676  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16677  {
16678  QCPDataMap::const_iterator it = mData->constBegin();
16679  while (it != mData->constEnd())
16680  {
16681  current = it.value().value;
16682  if (!qIsNaN(current))
16683  {
16684  currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16685  currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16686  if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16687  {
16688  range.lower = current-currentErrorMinus;
16689  haveLower = true;
16690  }
16691  if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16692  {
16693  range.upper = current+currentErrorPlus;
16694  haveUpper = true;
16695  }
16696  if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16697  {
16698  if ((current < range.lower || !haveLower) && current > 0)
16699  {
16700  range.lower = current;
16701  haveLower = true;
16702  }
16703  if ((current > range.upper || !haveUpper) && current > 0)
16704  {
16705  range.upper = current;
16706  haveUpper = true;
16707  }
16708  }
16709  }
16710  ++it;
16711  }
16712  }
16713 
16714  foundRange = haveLower && haveUpper;
16715  return range;
16716 }
16717 
16718 
16722 
16740  t(0),
16741  key(0),
16742  value(0)
16743 {
16744 }
16745 
16749 QCPCurveData::QCPCurveData(double t, double key, double value) :
16750  t(t),
16751  key(key),
16752  value(value)
16753 {
16754 }
16755 
16756 
16760 
16800 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
16801  QCPAbstractPlottable(keyAxis, valueAxis)
16802 {
16803  mData = new QCPCurveDataMap;
16804  mPen.setColor(Qt::blue);
16805  mPen.setStyle(Qt::SolidLine);
16806  mBrush.setColor(Qt::blue);
16807  mBrush.setStyle(Qt::NoBrush);
16808  mSelectedPen = mPen;
16809  mSelectedPen.setWidthF(2.5);
16810  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
16812 
16815 }
16816 
16818 {
16819  delete mData;
16820 }
16821 
16830 {
16831  if (mData == data)
16832  {
16833  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
16834  return;
16835  }
16836  if (copy)
16837  {
16838  *mData = *data;
16839  } else
16840  {
16841  delete mData;
16842  mData = data;
16843  }
16844 }
16845 
16852 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
16853 {
16854  mData->clear();
16855  int n = t.size();
16856  n = qMin(n, key.size());
16857  n = qMin(n, value.size());
16858  QCPCurveData newData;
16859  for (int i=0; i<n; ++i)
16860  {
16861  newData.t = t[i];
16862  newData.key = key[i];
16863  newData.value = value[i];
16864  mData->insertMulti(newData.t, newData);
16865  }
16866 }
16867 
16873 void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
16874 {
16875  mData->clear();
16876  int n = key.size();
16877  n = qMin(n, value.size());
16878  QCPCurveData newData;
16879  for (int i=0; i<n; ++i)
16880  {
16881  newData.t = i; // no t vector given, so we assign t the index of the key/value pair
16882  newData.key = key[i];
16883  newData.value = value[i];
16884  mData->insertMulti(newData.t, newData);
16885  }
16886 }
16887 
16896 {
16897  mScatterStyle = style;
16898 }
16899 
16908 {
16909  mLineStyle = style;
16910 }
16911 
16917 {
16918  mData->unite(dataMap);
16919 }
16920 
16926 {
16927  mData->insertMulti(data.t, data);
16928 }
16929 
16934 void QCPCurve::addData(double t, double key, double value)
16935 {
16936  QCPCurveData newData;
16937  newData.t = t;
16938  newData.key = key;
16939  newData.value = value;
16940  mData->insertMulti(newData.t, newData);
16941 }
16942 
16951 void QCPCurve::addData(double key, double value)
16952 {
16953  QCPCurveData newData;
16954  if (!mData->isEmpty())
16955  newData.t = (mData->constEnd()-1).key()+1;
16956  else
16957  newData.t = 0;
16958  newData.key = key;
16959  newData.value = value;
16960  mData->insertMulti(newData.t, newData);
16961 }
16962 
16967 void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
16968 {
16969  int n = ts.size();
16970  n = qMin(n, keys.size());
16971  n = qMin(n, values.size());
16972  QCPCurveData newData;
16973  for (int i=0; i<n; ++i)
16974  {
16975  newData.t = ts[i];
16976  newData.key = keys[i];
16977  newData.value = values[i];
16978  mData->insertMulti(newData.t, newData);
16979  }
16980 }
16981 
16987 {
16988  QCPCurveDataMap::iterator it = mData->begin();
16989  while (it != mData->end() && it.key() < t)
16990  it = mData->erase(it);
16991 }
16992 
16998 {
16999  if (mData->isEmpty()) return;
17000  QCPCurveDataMap::iterator it = mData->upperBound(t);
17001  while (it != mData->end())
17002  it = mData->erase(it);
17003 }
17004 
17012 void QCPCurve::removeData(double fromt, double tot)
17013 {
17014  if (fromt >= tot || mData->isEmpty()) return;
17015  QCPCurveDataMap::iterator it = mData->upperBound(fromt);
17016  QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
17017  while (it != itEnd)
17018  it = mData->erase(it);
17019 }
17020 
17030 void QCPCurve::removeData(double t)
17031 {
17032  mData->remove(t);
17033 }
17034 
17040 {
17041  mData->clear();
17042 }
17043 
17044 /* inherits documentation from base class */
17045 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17046 {
17047  Q_UNUSED(details)
17048  if ((onlySelectable && !mSelectable) || mData->isEmpty())
17049  return -1;
17050  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17051 
17052  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17053  return pointDistance(pos);
17054  else
17055  return -1;
17056 }
17057 
17058 /* inherits documentation from base class */
17060 {
17061  if (mData->isEmpty()) return;
17062 
17063  // allocate line vector:
17064  QVector<QPointF> *lineData = new QVector<QPointF>;
17065 
17066  // fill with curve data:
17067  getCurveData(lineData);
17068 
17069  // check data validity if flag set:
17070 #ifdef QCUSTOMPLOT_CHECK_DATA
17071  QCPCurveDataMap::const_iterator it;
17072  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17073  {
17074  if (QCP::isInvalidData(it.value().t) ||
17075  QCP::isInvalidData(it.value().key, it.value().value))
17076  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
17077  }
17078 #endif
17079 
17080  // draw curve fill:
17081  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17082  {
17083  applyFillAntialiasingHint(painter);
17084  painter->setPen(Qt::NoPen);
17085  painter->setBrush(mainBrush());
17086  painter->drawPolygon(QPolygonF(*lineData));
17087  }
17088 
17089  // draw curve line:
17090  if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17091  {
17093  painter->setPen(mainPen());
17094  painter->setBrush(Qt::NoBrush);
17095  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
17096  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17097  painter->pen().style() == Qt::SolidLine &&
17098  !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17099  !painter->modes().testFlag(QCPPainter::pmNoCaching))
17100  {
17101  int i = 0;
17102  bool lastIsNan = false;
17103  const int lineDataSize = lineData->size();
17104  while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
17105  ++i;
17106  ++i; // because drawing works in 1 point retrospect
17107  while (i < lineDataSize)
17108  {
17109  if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17110  {
17111  if (!lastIsNan)
17112  painter->drawLine(lineData->at(i-1), lineData->at(i));
17113  else
17114  lastIsNan = false;
17115  } else
17116  lastIsNan = true;
17117  ++i;
17118  }
17119  } else
17120  {
17121  int segmentStart = 0;
17122  int i = 0;
17123  const int lineDataSize = lineData->size();
17124  while (i < lineDataSize)
17125  {
17126  if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17127  {
17128  painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
17129  segmentStart = i+1;
17130  }
17131  ++i;
17132  }
17133  // draw last segment:
17134  painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
17135  }
17136  }
17137 
17138  // draw scatters:
17139  if (!mScatterStyle.isNone())
17140  drawScatterPlot(painter, lineData);
17141 
17142  // free allocated line data:
17143  delete lineData;
17144 }
17145 
17146 /* inherits documentation from base class */
17147 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17148 {
17149  // draw fill:
17150  if (mBrush.style() != Qt::NoBrush)
17151  {
17152  applyFillAntialiasingHint(painter);
17153  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
17154  }
17155  // draw line vertically centered:
17156  if (mLineStyle != lsNone)
17157  {
17159  painter->setPen(mPen);
17160  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
17161  }
17162  // draw scatter symbol:
17163  if (!mScatterStyle.isNone())
17164  {
17166  // scale scatter pixmap if it's too large to fit in legend icon rect:
17167  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
17168  {
17169  QCPScatterStyle scaledStyle(mScatterStyle);
17170  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17171  scaledStyle.applyTo(painter, mPen);
17172  scaledStyle.drawShape(painter, QRectF(rect).center());
17173  } else
17174  {
17175  mScatterStyle.applyTo(painter, mPen);
17176  mScatterStyle.drawShape(painter, QRectF(rect).center());
17177  }
17178  }
17179 }
17180 
17186 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
17187 {
17188  // draw scatter point symbols:
17190  mScatterStyle.applyTo(painter, mPen);
17191  for (int i=0; i<pointData->size(); ++i)
17192  if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
17193  mScatterStyle.drawShape(painter, pointData->at(i));
17194 }
17195 
17209 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
17210 {
17211  QCPAxis *keyAxis = mKeyAxis.data();
17212  QCPAxis *valueAxis = mValueAxis.data();
17213  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17214 
17215  // add margins to rect to compensate for stroke width
17216  double strokeMargin = qMax(qreal(1.0), qreal(mainPen().widthF()*0.75)); // stroke radius + 50% safety
17217  if (!mScatterStyle.isNone())
17218  strokeMargin = qMax(strokeMargin, mScatterStyle.size());
17219  double rectLeft = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17220  double rectRight = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17221  double rectBottom = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)+strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17222  double rectTop = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)-strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17223  int currentRegion;
17224  QCPCurveDataMap::const_iterator it = mData->constBegin();
17225  QCPCurveDataMap::const_iterator prevIt = mData->constEnd()-1;
17226  int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom);
17227  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)
17228  while (it != mData->constEnd())
17229  {
17230  currentRegion = getRegion(it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17231  if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
17232  {
17233  if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
17234  {
17235  QPointF crossA, crossB;
17236  if (prevRegion == 5) // we're coming from R, so add this point optimized
17237  {
17238  lineData->append(getOptimizedPoint(currentRegion, it.value().key, it.value().value, prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom));
17239  // 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
17240  *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17241  } else if (mayTraverse(prevRegion, currentRegion) &&
17242  getTraverse(prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom, crossA, crossB))
17243  {
17244  // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
17245  QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
17246  getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop, rectRight, rectBottom, beforeTraverseCornerPoints, afterTraverseCornerPoints);
17247  if (it != mData->constBegin())
17248  {
17249  *lineData << beforeTraverseCornerPoints;
17250  lineData->append(crossA);
17251  lineData->append(crossB);
17252  *lineData << afterTraverseCornerPoints;
17253  } else
17254  {
17255  lineData->append(crossB);
17256  *lineData << afterTraverseCornerPoints;
17257  trailingPoints << beforeTraverseCornerPoints << crossA ;
17258  }
17259  } 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)
17260  {
17261  *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17262  }
17263  } else // segment does end in R, so we add previous point optimized and this point at original position
17264  {
17265  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
17266  trailingPoints << getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17267  else
17268  lineData->append(getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom));
17269  lineData->append(coordsToPixels(it.value().key, it.value().value));
17270  }
17271  } else // region didn't change
17272  {
17273  if (currentRegion == 5) // still in R, keep adding original points
17274  {
17275  lineData->append(coordsToPixels(it.value().key, it.value().value));
17276  } else // still outside R, no need to add anything
17277  {
17278  // see how this is not doing anything? That's the main optimization...
17279  }
17280  }
17281  prevIt = it;
17282  prevRegion = currentRegion;
17283  ++it;
17284  }
17285  *lineData << trailingPoints;
17286 }
17287 
17306 int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17307 {
17308  if (x < rectLeft) // region 123
17309  {
17310  if (y > rectTop)
17311  return 1;
17312  else if (y < rectBottom)
17313  return 3;
17314  else
17315  return 2;
17316  } else if (x > rectRight) // region 789
17317  {
17318  if (y > rectTop)
17319  return 7;
17320  else if (y < rectBottom)
17321  return 9;
17322  else
17323  return 8;
17324  } else // region 456
17325  {
17326  if (y > rectTop)
17327  return 4;
17328  else if (y < rectBottom)
17329  return 6;
17330  else
17331  return 5;
17332  }
17333 }
17334 
17350 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17351 {
17352  double intersectKey = rectLeft; // initial value is just fail-safe
17353  double intersectValue = rectTop; // initial value is just fail-safe
17354  switch (otherRegion)
17355  {
17356  case 1: // top and left edge
17357  {
17358  intersectValue = rectTop;
17359  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17360  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17361  {
17362  intersectKey = rectLeft;
17363  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17364  }
17365  break;
17366  }
17367  case 2: // left edge
17368  {
17369  intersectKey = rectLeft;
17370  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17371  break;
17372  }
17373  case 3: // bottom and left edge
17374  {
17375  intersectValue = rectBottom;
17376  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17377  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17378  {
17379  intersectKey = rectLeft;
17380  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17381  }
17382  break;
17383  }
17384  case 4: // top edge
17385  {
17386  intersectValue = rectTop;
17387  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17388  break;
17389  }
17390  case 5:
17391  {
17392  break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
17393  }
17394  case 6: // bottom edge
17395  {
17396  intersectValue = rectBottom;
17397  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17398  break;
17399  }
17400  case 7: // top and right edge
17401  {
17402  intersectValue = rectTop;
17403  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17404  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17405  {
17406  intersectKey = rectRight;
17407  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17408  }
17409  break;
17410  }
17411  case 8: // right edge
17412  {
17413  intersectKey = rectRight;
17414  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17415  break;
17416  }
17417  case 9: // bottom and right edge
17418  {
17419  intersectValue = rectBottom;
17420  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17421  if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17422  {
17423  intersectKey = rectRight;
17424  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17425  }
17426  break;
17427  }
17428  }
17429  return coordsToPixels(intersectKey, intersectValue);
17430 }
17431 
17450 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
17451 {
17452  QVector<QPointF> result;
17453  switch (prevRegion)
17454  {
17455  case 1:
17456  {
17457  switch (currentRegion)
17458  {
17459  case 2: { result << coordsToPixels(rectLeft, rectTop); break; }
17460  case 4: { result << coordsToPixels(rectLeft, rectTop); break; }
17461  case 3: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); break; }
17462  case 7: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); break; }
17463  case 6: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17464  case 8: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17465  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
17466  if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17467  { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17468  else
17469  { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17470  break;
17471  }
17472  }
17473  break;
17474  }
17475  case 2:
17476  {
17477  switch (currentRegion)
17478  {
17479  case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17480  case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17481  case 4: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17482  case 6: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17483  case 7: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17484  case 9: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17485  }
17486  break;
17487  }
17488  case 3:
17489  {
17490  switch (currentRegion)
17491  {
17492  case 2: { result << coordsToPixels(rectLeft, rectBottom); break; }
17493  case 6: { result << coordsToPixels(rectLeft, rectBottom); break; }
17494  case 1: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); break; }
17495  case 9: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); break; }
17496  case 4: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17497  case 8: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17498  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
17499  if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17500  { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17501  else
17502  { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17503  break;
17504  }
17505  }
17506  break;
17507  }
17508  case 4:
17509  {
17510  switch (currentRegion)
17511  {
17512  case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17513  case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17514  case 2: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17515  case 8: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17516  case 3: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17517  case 9: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17518  }
17519  break;
17520  }
17521  case 5:
17522  {
17523  switch (currentRegion)
17524  {
17525  case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17526  case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17527  case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17528  case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17529  }
17530  break;
17531  }
17532  case 6:
17533  {
17534  switch (currentRegion)
17535  {
17536  case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17537  case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17538  case 2: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17539  case 8: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17540  case 1: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17541  case 7: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17542  }
17543  break;
17544  }
17545  case 7:
17546  {
17547  switch (currentRegion)
17548  {
17549  case 4: { result << coordsToPixels(rectRight, rectTop); break; }
17550  case 8: { result << coordsToPixels(rectRight, rectTop); break; }
17551  case 1: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); break; }
17552  case 9: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); break; }
17553  case 2: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17554  case 6: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17555  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
17556  if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17557  { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17558  else
17559  { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17560  break;
17561  }
17562  }
17563  break;
17564  }
17565  case 8:
17566  {
17567  switch (currentRegion)
17568  {
17569  case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17570  case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17571  case 4: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17572  case 6: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17573  case 1: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17574  case 3: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17575  }
17576  break;
17577  }
17578  case 9:
17579  {
17580  switch (currentRegion)
17581  {
17582  case 6: { result << coordsToPixels(rectRight, rectBottom); break; }
17583  case 8: { result << coordsToPixels(rectRight, rectBottom); break; }
17584  case 3: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); break; }
17585  case 7: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); break; }
17586  case 2: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17587  case 4: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17588  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
17589  if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17590  { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17591  else
17592  { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17593  break;
17594  }
17595  }
17596  break;
17597  }
17598  }
17599  return result;
17600 }
17601 
17614 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
17615 {
17616  switch (prevRegion)
17617  {
17618  case 1:
17619  {
17620  switch (currentRegion)
17621  {
17622  case 4:
17623  case 7:
17624  case 2:
17625  case 3: return false;
17626  default: return true;
17627  }
17628  }
17629  case 2:
17630  {
17631  switch (currentRegion)
17632  {
17633  case 1:
17634  case 3: return false;
17635  default: return true;
17636  }
17637  }
17638  case 3:
17639  {
17640  switch (currentRegion)
17641  {
17642  case 1:
17643  case 2:
17644  case 6:
17645  case 9: return false;
17646  default: return true;
17647  }
17648  }
17649  case 4:
17650  {
17651  switch (currentRegion)
17652  {
17653  case 1:
17654  case 7: return false;
17655  default: return true;
17656  }
17657  }
17658  case 5: return false; // should never occur
17659  case 6:
17660  {
17661  switch (currentRegion)
17662  {
17663  case 3:
17664  case 9: return false;
17665  default: return true;
17666  }
17667  }
17668  case 7:
17669  {
17670  switch (currentRegion)
17671  {
17672  case 1:
17673  case 4:
17674  case 8:
17675  case 9: return false;
17676  default: return true;
17677  }
17678  }
17679  case 8:
17680  {
17681  switch (currentRegion)
17682  {
17683  case 7:
17684  case 9: return false;
17685  default: return true;
17686  }
17687  }
17688  case 9:
17689  {
17690  switch (currentRegion)
17691  {
17692  case 3:
17693  case 6:
17694  case 8:
17695  case 7: return false;
17696  default: return true;
17697  }
17698  }
17699  default: return true;
17700  }
17701 }
17702 
17703 
17717 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
17718 {
17719  QList<QPointF> intersections; // x of QPointF corresponds to key and y to value
17720  if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
17721  {
17722  // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17723  intersections.append(QPointF(key, rectBottom)); // direction will be taken care of at end of method
17724  intersections.append(QPointF(key, rectTop));
17725  } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
17726  {
17727  // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17728  intersections.append(QPointF(rectLeft, value)); // direction will be taken care of at end of method
17729  intersections.append(QPointF(rectRight, value));
17730  } else // line is skewed
17731  {
17732  double gamma;
17733  double keyPerValue = (key-prevKey)/(value-prevValue);
17734  // check top of rect:
17735  gamma = prevKey + (rectTop-prevValue)*keyPerValue;
17736  if (gamma >= rectLeft && gamma <= rectRight)
17737  intersections.append(QPointF(gamma, rectTop));
17738  // check bottom of rect:
17739  gamma = prevKey + (rectBottom-prevValue)*keyPerValue;
17740  if (gamma >= rectLeft && gamma <= rectRight)
17741  intersections.append(QPointF(gamma, rectBottom));
17742  double valuePerKey = 1.0/keyPerValue;
17743  // check left of rect:
17744  gamma = prevValue + (rectLeft-prevKey)*valuePerKey;
17745  if (gamma >= rectBottom && gamma <= rectTop)
17746  intersections.append(QPointF(rectLeft, gamma));
17747  // check right of rect:
17748  gamma = prevValue + (rectRight-prevKey)*valuePerKey;
17749  if (gamma >= rectBottom && gamma <= rectTop)
17750  intersections.append(QPointF(rectRight, gamma));
17751  }
17752 
17753  // handle cases where found points isn't exactly 2:
17754  if (intersections.size() > 2)
17755  {
17756  // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
17757  double distSqrMax = 0;
17758  QPointF pv1, pv2;
17759  for (int i=0; i<intersections.size()-1; ++i)
17760  {
17761  for (int k=i+1; k<intersections.size(); ++k)
17762  {
17763  QPointF distPoint = intersections.at(i)-intersections.at(k);
17764  double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
17765  if (distSqr > distSqrMax)
17766  {
17767  pv1 = intersections.at(i);
17768  pv2 = intersections.at(k);
17769  distSqrMax = distSqr;
17770  }
17771  }
17772  }
17773  intersections = QList<QPointF>() << pv1 << pv2;
17774  } else if (intersections.size() != 2)
17775  {
17776  // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
17777  return false;
17778  }
17779 
17780  // possibly re-sort points so optimized point segment has same direction as original segment:
17781  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
17782  intersections.move(0, 1);
17783  crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
17784  crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
17785  return true;
17786 }
17787 
17813 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
17814 {
17815  switch (prevRegion)
17816  {
17817  case 1:
17818  {
17819  switch (currentRegion)
17820  {
17821  case 6: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17822  case 9: { beforeTraverse << coordsToPixels(rectLeft, rectTop); afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17823  case 8: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17824  }
17825  break;
17826  }
17827  case 2:
17828  {
17829  switch (currentRegion)
17830  {
17831  case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17832  case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17833  }
17834  break;
17835  }
17836  case 3:
17837  {
17838  switch (currentRegion)
17839  {
17840  case 4: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17841  case 7: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17842  case 8: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17843  }
17844  break;
17845  }
17846  case 4:
17847  {
17848  switch (currentRegion)
17849  {
17850  case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17851  case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17852  }
17853  break;
17854  }
17855  case 5: { break; } // shouldn't happen because this method only handles full traverses
17856  case 6:
17857  {
17858  switch (currentRegion)
17859  {
17860  case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17861  case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17862  }
17863  break;
17864  }
17865  case 7:
17866  {
17867  switch (currentRegion)
17868  {
17869  case 2: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17870  case 3: { beforeTraverse << coordsToPixels(rectRight, rectTop); afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17871  case 6: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17872  }
17873  break;
17874  }
17875  case 8:
17876  {
17877  switch (currentRegion)
17878  {
17879  case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17880  case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17881  }
17882  break;
17883  }
17884  case 9:
17885  {
17886  switch (currentRegion)
17887  {
17888  case 2: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17889  case 1: { beforeTraverse << coordsToPixels(rectRight, rectBottom); afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17890  case 4: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17891  }
17892  break;
17893  }
17894  }
17895 }
17896 
17903 double QCPCurve::pointDistance(const QPointF &pixelPoint) const
17904 {
17905  if (mData->isEmpty())
17906  {
17907  qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
17908  return 500;
17909  }
17910  if (mData->size() == 1)
17911  {
17912  QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
17913  return QVector2D(dataPoint-pixelPoint).length();
17914  }
17915 
17916  // calculate minimum distance to line segments:
17917  QVector<QPointF> *lineData = new QVector<QPointF>;
17918  getCurveData(lineData);
17919  double minDistSqr = std::numeric_limits<double>::max();
17920  for (int i=0; i<lineData->size()-1; ++i)
17921  {
17922  double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
17923  if (currentDistSqr < minDistSqr)
17924  minDistSqr = currentDistSqr;
17925  }
17926  delete lineData;
17927  return qSqrt(minDistSqr);
17928 }
17929 
17930 /* inherits documentation from base class */
17931 QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17932 {
17933  QCPRange range;
17934  bool haveLower = false;
17935  bool haveUpper = false;
17936 
17937  double current;
17938 
17939  QCPCurveDataMap::const_iterator it = mData->constBegin();
17940  while (it != mData->constEnd())
17941  {
17942  current = it.value().key;
17943  if (!qIsNaN(current) && !qIsNaN(it.value().value))
17944  {
17945  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17946  {
17947  if (current < range.lower || !haveLower)
17948  {
17949  range.lower = current;
17950  haveLower = true;
17951  }
17952  if (current > range.upper || !haveUpper)
17953  {
17954  range.upper = current;
17955  haveUpper = true;
17956  }
17957  }
17958  }
17959  ++it;
17960  }
17961 
17962  foundRange = haveLower && haveUpper;
17963  return range;
17964 }
17965 
17966 /* inherits documentation from base class */
17967 QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17968 {
17969  QCPRange range;
17970  bool haveLower = false;
17971  bool haveUpper = false;
17972 
17973  double current;
17974 
17975  QCPCurveDataMap::const_iterator it = mData->constBegin();
17976  while (it != mData->constEnd())
17977  {
17978  current = it.value().value;
17979  if (!qIsNaN(current) && !qIsNaN(it.value().key))
17980  {
17981  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17982  {
17983  if (current < range.lower || !haveLower)
17984  {
17985  range.lower = current;
17986  haveLower = true;
17987  }
17988  if (current > range.upper || !haveUpper)
17989  {
17990  range.upper = current;
17991  haveUpper = true;
17992  }
17993  }
17994  }
17995  ++it;
17996  }
17997 
17998  foundRange = haveLower && haveUpper;
17999  return range;
18000 }
18001 
18002 
18006 
18041 /* start of documentation of inline functions */
18042 
18069 /* end of documentation of inline functions */
18070 
18075  QObject(parentPlot),
18076  mParentPlot(parentPlot),
18077  mSpacingType(stAbsolute),
18078  mSpacing(4)
18079 {
18080 }
18081 
18083 {
18084  clear();
18085 }
18086 
18095 {
18097 }
18098 
18106 {
18107  mSpacing = spacing;
18108 }
18109 
18116 QCPBars *QCPBarsGroup::bars(int index) const
18117 {
18118  if (index >= 0 && index < mBars.size())
18119  {
18120  return mBars.at(index);
18121  } else
18122  {
18123  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
18124  return 0;
18125  }
18126 }
18127 
18134 {
18135  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
18136  bars->setBarsGroup(0); // removes itself via removeBars
18137 }
18138 
18146 {
18147  if (!bars)
18148  {
18149  qDebug() << Q_FUNC_INFO << "bars is 0";
18150  return;
18151  }
18152 
18153  if (!mBars.contains(bars))
18154  bars->setBarsGroup(this);
18155  else
18156  qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
18157 }
18158 
18169 {
18170  if (!bars)
18171  {
18172  qDebug() << Q_FUNC_INFO << "bars is 0";
18173  return;
18174  }
18175 
18176  // first append to bars list normally:
18177  if (!mBars.contains(bars))
18178  bars->setBarsGroup(this);
18179  // then move to according position:
18180  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
18181 }
18182 
18189 {
18190  if (!bars)
18191  {
18192  qDebug() << Q_FUNC_INFO << "bars is 0";
18193  return;
18194  }
18195 
18196  if (mBars.contains(bars))
18197  bars->setBarsGroup(0);
18198  else
18199  qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
18200 }
18201 
18210 {
18211  if (!mBars.contains(bars))
18212  mBars.append(bars);
18213 }
18214 
18223 {
18224  mBars.removeOne(bars);
18225 }
18226 
18233 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
18234 {
18235  // find list of all base bars in case some mBars are stacked:
18236  QList<const QCPBars*> baseBars;
18237  foreach (const QCPBars *b, mBars)
18238  {
18239  while (b->barBelow())
18240  b = b->barBelow();
18241  if (!baseBars.contains(b))
18242  baseBars.append(b);
18243  }
18244  // find base bar this "bars" is stacked on:
18245  const QCPBars *thisBase = bars;
18246  while (thisBase->barBelow())
18247  thisBase = thisBase->barBelow();
18248 
18249  // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
18250  double result = 0;
18251  int index = baseBars.indexOf(thisBase);
18252  if (index >= 0)
18253  {
18254  int startIndex;
18255  double lowerPixelWidth, upperPixelWidth;
18256  if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
18257  {
18258  return result;
18259  } else if (index < (baseBars.size()-1)/2.0) // bar is to the left of center
18260  {
18261  if (baseBars.size() % 2 == 0) // even number of bars
18262  {
18263  startIndex = baseBars.size()/2-1;
18264  result -= getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18265  } else // uneven number of bars
18266  {
18267  startIndex = (baseBars.size()-1)/2-1;
18268  baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18269  result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18270  result -= getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18271  }
18272  for (int i=startIndex; i>index; --i) // add widths and spacings of bars in between center and our bars
18273  {
18274  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18275  result -= qAbs(upperPixelWidth-lowerPixelWidth);
18276  result -= getPixelSpacing(baseBars.at(i), keyCoord);
18277  }
18278  // finally half of our bars width:
18279  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18280  result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18281  } else // bar is to the right of center
18282  {
18283  if (baseBars.size() % 2 == 0) // even number of bars
18284  {
18285  startIndex = baseBars.size()/2;
18286  result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18287  } else // uneven number of bars
18288  {
18289  startIndex = (baseBars.size()-1)/2+1;
18290  baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18291  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18292  result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18293  }
18294  for (int i=startIndex; i<index; ++i) // add widths and spacings of bars in between center and our bars
18295  {
18296  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18297  result += qAbs(upperPixelWidth-lowerPixelWidth);
18298  result += getPixelSpacing(baseBars.at(i), keyCoord);
18299  }
18300  // finally half of our bars width:
18301  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18302  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18303  }
18304  }
18305  return result;
18306 }
18307 
18318 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
18319 {
18320  switch (mSpacingType)
18321  {
18322  case stAbsolute:
18323  {
18324  return mSpacing;
18325  }
18326  case stAxisRectRatio:
18327  {
18328  if (bars->keyAxis()->orientation() == Qt::Horizontal)
18329  return bars->keyAxis()->axisRect()->width()*mSpacing;
18330  else
18331  return bars->keyAxis()->axisRect()->height()*mSpacing;
18332  }
18333  case stPlotCoords:
18334  {
18335  double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
18336  return bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel;
18337  }
18338  }
18339  return 0;
18340 }
18341 
18342 
18346 
18363  key(0),
18364  value(0)
18365 {
18366 }
18367 
18372  key(key),
18373  value(value)
18374 {
18375 }
18376 
18377 
18381 
18417 /* start of documentation of inline functions */
18418 
18433 /* end of documentation of inline functions */
18434 
18444 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
18445  QCPAbstractPlottable(keyAxis, valueAxis),
18446  mData(new QCPBarDataMap),
18447  mWidth(0.75),
18448  mWidthType(wtPlotCoords),
18449  mBarsGroup(0),
18450  mBaseValue(0)
18451 {
18452  // modify inherited properties from abstract plottable:
18453  mPen.setColor(Qt::blue);
18454  mPen.setStyle(Qt::SolidLine);
18455  mBrush.setColor(QColor(40, 50, 255, 30));
18456  mBrush.setStyle(Qt::SolidPattern);
18457  mSelectedPen = mPen;
18458  mSelectedPen.setWidthF(2.5);
18459  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
18461 }
18462 
18464 {
18465  setBarsGroup(0);
18466  if (mBarBelow || mBarAbove)
18467  connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
18468  delete mData;
18469 }
18470 
18478 {
18479  mWidth = width;
18480 }
18481 
18491 {
18493 }
18494 
18502 {
18503  // deregister at old group:
18504  if (mBarsGroup)
18505  mBarsGroup->unregisterBars(this);
18507  // register at new group:
18508  if (mBarsGroup)
18509  mBarsGroup->registerBars(this);
18510 }
18511 
18525 {
18527 }
18528 
18537 {
18538  if (mData == data)
18539  {
18540  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
18541  return;
18542  }
18543  if (copy)
18544  {
18545  *mData = *data;
18546  } else
18547  {
18548  delete mData;
18549  mData = data;
18550  }
18551 }
18552 
18559 void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
18560 {
18561  mData->clear();
18562  int n = key.size();
18563  n = qMin(n, value.size());
18564  QCPBarData newData;
18565  for (int i=0; i<n; ++i)
18566  {
18567  newData.key = key[i];
18568  newData.value = value[i];
18569  mData->insertMulti(newData.key, newData);
18570  }
18571 }
18572 
18588 {
18589  if (bars == this) return;
18590  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18591  {
18592  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18593  return;
18594  }
18595  // remove from stacking:
18596  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18597  // if new bar given, insert this bar below it:
18598  if (bars)
18599  {
18600  if (bars->mBarBelow)
18601  connectBars(bars->mBarBelow.data(), this);
18602  connectBars(this, bars);
18603  }
18604 }
18605 
18621 {
18622  if (bars == this) return;
18623  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18624  {
18625  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18626  return;
18627  }
18628  // remove from stacking:
18629  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18630  // if new bar given, insert this bar above it:
18631  if (bars)
18632  {
18633  if (bars->mBarAbove)
18634  connectBars(this, bars->mBarAbove.data());
18635  connectBars(bars, this);
18636  }
18637 }
18638 
18643 void QCPBars::addData(const QCPBarDataMap &dataMap)
18644 {
18645  mData->unite(dataMap);
18646 }
18647 
18653 {
18654  mData->insertMulti(data.key, data);
18655 }
18656 
18661 void QCPBars::addData(double key, double value)
18662 {
18663  QCPBarData newData;
18664  newData.key = key;
18665  newData.value = value;
18666  mData->insertMulti(newData.key, newData);
18667 }
18668 
18673 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
18674 {
18675  int n = keys.size();
18676  n = qMin(n, values.size());
18677  QCPBarData newData;
18678  for (int i=0; i<n; ++i)
18679  {
18680  newData.key = keys[i];
18681  newData.value = values[i];
18682  mData->insertMulti(newData.key, newData);
18683  }
18684 }
18685 
18691 {
18692  QCPBarDataMap::iterator it = mData->begin();
18693  while (it != mData->end() && it.key() < key)
18694  it = mData->erase(it);
18695 }
18696 
18702 {
18703  if (mData->isEmpty()) return;
18704  QCPBarDataMap::iterator it = mData->upperBound(key);
18705  while (it != mData->end())
18706  it = mData->erase(it);
18707 }
18708 
18716 void QCPBars::removeData(double fromKey, double toKey)
18717 {
18718  if (fromKey >= toKey || mData->isEmpty()) return;
18719  QCPBarDataMap::iterator it = mData->upperBound(fromKey);
18720  QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
18721  while (it != itEnd)
18722  it = mData->erase(it);
18723 }
18724 
18733 void QCPBars::removeData(double key)
18734 {
18735  mData->remove(key);
18736 }
18737 
18743 {
18744  mData->clear();
18745 }
18746 
18747 /* inherits documentation from base class */
18748 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18749 {
18750  Q_UNUSED(details)
18751  if (onlySelectable && !mSelectable)
18752  return -1;
18753  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
18754 
18755  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
18756  {
18757  QCPBarDataMap::ConstIterator it;
18758  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
18759  {
18760  if (getBarPolygon(it.value().key, it.value().value).boundingRect().contains(pos))
18761  return mParentPlot->selectionTolerance()*0.99;
18762  }
18763  }
18764  return -1;
18765 }
18766 
18767 /* inherits documentation from base class */
18769 {
18770  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
18771  if (mData->isEmpty()) return;
18772 
18773  QCPBarDataMap::const_iterator it, lower, upperEnd;
18774  getVisibleDataBounds(lower, upperEnd);
18775  for (it = lower; it != upperEnd; ++it)
18776  {
18777  // check data validity if flag set:
18778 #ifdef QCUSTOMPLOT_CHECK_DATA
18779  if (QCP::isInvalidData(it.value().key, it.value().value))
18780  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
18781 #endif
18782  QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
18783  // draw bar fill:
18784  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
18785  {
18786  applyFillAntialiasingHint(painter);
18787  painter->setPen(Qt::NoPen);
18788  painter->setBrush(mainBrush());
18789  painter->drawPolygon(barPolygon);
18790  }
18791  // draw bar line:
18792  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
18793  {
18795  painter->setPen(mainPen());
18796  painter->setBrush(Qt::NoBrush);
18797  painter->drawPolyline(barPolygon);
18798  }
18799  }
18800 }
18801 
18802 /* inherits documentation from base class */
18803 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
18804 {
18805  // draw filled rect:
18807  painter->setBrush(mBrush);
18808  painter->setPen(mPen);
18809  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
18810  r.moveCenter(rect.center());
18811  painter->drawRect(r);
18812 }
18813 
18828 void QCPBars::getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
18829 {
18830  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
18831  if (mData->isEmpty())
18832  {
18833  lower = mData->constEnd();
18834  upperEnd = mData->constEnd();
18835  return;
18836  }
18837 
18838  // get visible data range as QMap iterators
18839  lower = mData->lowerBound(mKeyAxis.data()->range().lower);
18840  upperEnd = mData->upperBound(mKeyAxis.data()->range().upper);
18841  double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
18842  double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
18843  bool isVisible = false;
18844  // walk left from lbound to find lower bar that actually is completely outside visible pixel range:
18845  QCPBarDataMap::const_iterator it = lower;
18846  while (it != mData->constBegin())
18847  {
18848  --it;
18849  QRectF barBounds = getBarPolygon(it.value().key, it.value().value).boundingRect();
18850  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18851  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.left() <= lowerPixelBound));
18852  else // keyaxis is vertical
18853  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= lowerPixelBound));
18854  if (isVisible)
18855  lower = it;
18856  else
18857  break;
18858  }
18859  // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
18860  it = upperEnd;
18861  while (it != mData->constEnd())
18862  {
18863  QRectF barBounds = getBarPolygon(upperEnd.value().key, upperEnd.value().value).boundingRect();
18864  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18865  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.right() >= upperPixelBound));
18866  else // keyaxis is vertical
18867  isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.top() <= upperPixelBound));
18868  if (isVisible)
18869  upperEnd = it+1;
18870  else
18871  break;
18872  ++it;
18873  }
18874 }
18875 
18882 QPolygonF QCPBars::getBarPolygon(double key, double value) const
18883 {
18884  QCPAxis *keyAxis = mKeyAxis.data();
18885  QCPAxis *valueAxis = mValueAxis.data();
18886  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
18887 
18888  QPolygonF result;
18889  double lowerPixelWidth, upperPixelWidth;
18890  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
18891  double base = getStackedBaseValue(key, value >= 0);
18892  double basePixel = valueAxis->coordToPixel(base);
18893  double valuePixel = valueAxis->coordToPixel(base+value);
18894  double keyPixel = keyAxis->coordToPixel(key);
18895  if (mBarsGroup)
18896  keyPixel += mBarsGroup->keyPixelOffset(this, key);
18897  if (keyAxis->orientation() == Qt::Horizontal)
18898  {
18899  result << QPointF(keyPixel+lowerPixelWidth, basePixel);
18900  result << QPointF(keyPixel+lowerPixelWidth, valuePixel);
18901  result << QPointF(keyPixel+upperPixelWidth, valuePixel);
18902  result << QPointF(keyPixel+upperPixelWidth, basePixel);
18903  } else
18904  {
18905  result << QPointF(basePixel, keyPixel+lowerPixelWidth);
18906  result << QPointF(valuePixel, keyPixel+lowerPixelWidth);
18907  result << QPointF(valuePixel, keyPixel+upperPixelWidth);
18908  result << QPointF(basePixel, keyPixel+upperPixelWidth);
18909  }
18910  return result;
18911 }
18912 
18922 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
18923 {
18924  switch (mWidthType)
18925  {
18926  case wtAbsolute:
18927  {
18928  upper = mWidth*0.5;
18929  lower = -upper;
18930  if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18931  qSwap(lower, upper);
18932  break;
18933  }
18934  case wtAxisRectRatio:
18935  {
18936  if (mKeyAxis && mKeyAxis.data()->axisRect())
18937  {
18938  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18939  upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5;
18940  else
18941  upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5;
18942  lower = -upper;
18943  if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18944  qSwap(lower, upper);
18945  } else
18946  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
18947  break;
18948  }
18949  case wtPlotCoords:
18950  {
18951  if (mKeyAxis)
18952  {
18953  double keyPixel = mKeyAxis.data()->coordToPixel(key);
18954  upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
18955  lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
18956  // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
18957  // coordinate transform which includes range direction
18958  } else
18959  qDebug() << Q_FUNC_INFO << "No key axis defined";
18960  break;
18961  }
18962  }
18963 }
18964 
18974 double QCPBars::getStackedBaseValue(double key, bool positive) const
18975 {
18976  if (mBarBelow)
18977  {
18978  double max = 0; // don't use mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
18979  // find bars of mBarBelow that are approximately at key and find largest one:
18980  double epsilon = qAbs(key)*1e-6; // should be safe even when changed to use float at some point
18981  if (key == 0)
18982  epsilon = 1e-6;
18983  QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-epsilon);
18984  QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+epsilon);
18985  while (it != itEnd)
18986  {
18987  if ((positive && it.value().value > max) ||
18988  (!positive && it.value().value < max))
18989  max = it.value().value;
18990  ++it;
18991  }
18992  // recurse down the bar-stack to find the total height:
18993  return max + mBarBelow.data()->getStackedBaseValue(key, positive);
18994  } else
18995  return mBaseValue;
18996 }
18997 
19007 {
19008  if (!lower && !upper) return;
19009 
19010  if (!lower) // disconnect upper at bottom
19011  {
19012  // disconnect old bar below upper:
19013  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19014  upper->mBarBelow.data()->mBarAbove = 0;
19015  upper->mBarBelow = 0;
19016  } else if (!upper) // disconnect lower at top
19017  {
19018  // disconnect old bar above lower:
19019  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19020  lower->mBarAbove.data()->mBarBelow = 0;
19021  lower->mBarAbove = 0;
19022  } else // connect lower and upper
19023  {
19024  // disconnect old bar above lower:
19025  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19026  lower->mBarAbove.data()->mBarBelow = 0;
19027  // disconnect old bar below upper:
19028  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19029  upper->mBarBelow.data()->mBarAbove = 0;
19030  lower->mBarAbove = upper;
19031  upper->mBarBelow = lower;
19032  }
19033 }
19034 
19035 /* inherits documentation from base class */
19036 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19037 {
19038  QCPRange range;
19039  bool haveLower = false;
19040  bool haveUpper = false;
19041 
19042  double current;
19043  QCPBarDataMap::const_iterator it = mData->constBegin();
19044  while (it != mData->constEnd())
19045  {
19046  current = it.value().key;
19047  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19048  {
19049  if (current < range.lower || !haveLower)
19050  {
19051  range.lower = current;
19052  haveLower = true;
19053  }
19054  if (current > range.upper || !haveUpper)
19055  {
19056  range.upper = current;
19057  haveUpper = true;
19058  }
19059  }
19060  ++it;
19061  }
19062  // determine exact range of bars by including bar width and barsgroup offset:
19063  if (haveLower && mKeyAxis)
19064  {
19065  double lowerPixelWidth, upperPixelWidth, keyPixel;
19066  getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
19067  keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
19068  if (mBarsGroup)
19069  keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
19070  range.lower = mKeyAxis.data()->pixelToCoord(keyPixel);
19071  }
19072  if (haveUpper && mKeyAxis)
19073  {
19074  double lowerPixelWidth, upperPixelWidth, keyPixel;
19075  getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
19076  keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
19077  if (mBarsGroup)
19078  keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
19079  range.upper = mKeyAxis.data()->pixelToCoord(keyPixel);
19080  }
19081  foundRange = haveLower && haveUpper;
19082  return range;
19083 }
19084 
19085 /* inherits documentation from base class */
19086 QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19087 {
19088  QCPRange range;
19089  range.lower = mBaseValue;
19090  range.upper = mBaseValue;
19091  bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
19092  bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
19093  double current;
19094 
19095  QCPBarDataMap::const_iterator it = mData->constBegin();
19096  while (it != mData->constEnd())
19097  {
19098  current = it.value().value + getStackedBaseValue(it.value().key, it.value().value >= 0);
19099  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19100  {
19101  if (current < range.lower || !haveLower)
19102  {
19103  range.lower = current;
19104  haveLower = true;
19105  }
19106  if (current > range.upper || !haveUpper)
19107  {
19108  range.upper = current;
19109  haveUpper = true;
19110  }
19111  }
19112  ++it;
19113  }
19114 
19115  foundRange = true; // return true because bar charts always have the 0-line visible
19116  return range;
19117 }
19118 
19119 
19123 
19177  QCPAbstractPlottable(keyAxis, valueAxis),
19178  mKey(0),
19179  mMinimum(0),
19180  mLowerQuartile(0),
19181  mMedian(0),
19182  mUpperQuartile(0),
19183  mMaximum(0)
19184 {
19186  setWhiskerWidth(0.2);
19187  setWidth(0.5);
19188 
19189  setPen(QPen(Qt::black));
19190  setSelectedPen(QPen(Qt::blue, 2.5));
19191  setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
19192  setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
19193  setWhiskerBarPen(QPen(Qt::black));
19194  setBrush(Qt::NoBrush);
19195  setSelectedBrush(Qt::NoBrush);
19196 }
19197 
19202 {
19203  mKey = key;
19204 }
19205 
19213 {
19214  mMinimum = value;
19215 }
19216 
19225 {
19226  mLowerQuartile = value;
19227 }
19228 
19237 {
19238  mMedian = value;
19239 }
19240 
19249 {
19250  mUpperQuartile = value;
19251 }
19252 
19260 {
19261  mMaximum = value;
19262 }
19263 
19271 void QCPStatisticalBox::setOutliers(const QVector<double> &values)
19272 {
19273  mOutliers = values;
19274 }
19275 
19281 void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
19282 {
19283  setKey(key);
19284  setMinimum(minimum);
19285  setLowerQuartile(lowerQuartile);
19286  setMedian(median);
19287  setUpperQuartile(upperQuartile);
19288  setMaximum(maximum);
19289 }
19290 
19297 {
19298  mWidth = width;
19299 }
19300 
19307 {
19308  mWhiskerWidth = width;
19309 }
19310 
19320 {
19321  mWhiskerPen = pen;
19322 }
19323 
19331 {
19332  mWhiskerBarPen = pen;
19333 }
19334 
19339 {
19340  mMedianPen = pen;
19341 }
19342 
19349 {
19350  mOutlierStyle = style;
19351 }
19352 
19353 /* inherits documentation from base class */
19355 {
19356  setOutliers(QVector<double>());
19357  setKey(0);
19358  setMinimum(0);
19359  setLowerQuartile(0);
19360  setMedian(0);
19361  setUpperQuartile(0);
19362  setMaximum(0);
19363 }
19364 
19365 /* inherits documentation from base class */
19366 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19367 {
19368  Q_UNUSED(details)
19369  if (onlySelectable && !mSelectable)
19370  return -1;
19371  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19372 
19373  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19374  {
19375  double posKey, posValue;
19376  pixelsToCoords(pos, posKey, posValue);
19377  // quartile box:
19378  QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19379  QCPRange valueRange(mLowerQuartile, mUpperQuartile);
19380  if (keyRange.contains(posKey) && valueRange.contains(posValue))
19381  return mParentPlot->selectionTolerance()*0.99;
19382 
19383  // min/max whiskers:
19384  if (QCPRange(mMinimum, mMaximum).contains(posValue))
19385  return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
19386  }
19387  return -1;
19388 }
19389 
19390 /* inherits documentation from base class */
19392 {
19393  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
19394 
19395  // check data validity if flag set:
19396 #ifdef QCUSTOMPLOT_CHECK_DATA
19400  qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
19401  for (int i=0; i<mOutliers.size(); ++i)
19402  if (QCP::isInvalidData(mOutliers.at(i)))
19403  qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
19404 #endif
19405 
19406  QRectF quartileBox;
19407  drawQuartileBox(painter, &quartileBox);
19408 
19409  painter->save();
19410  painter->setClipRect(quartileBox, Qt::IntersectClip);
19411  drawMedian(painter);
19412  painter->restore();
19413 
19414  drawWhiskers(painter);
19415  drawOutliers(painter);
19416 }
19417 
19418 /* inherits documentation from base class */
19419 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19420 {
19421  // draw filled rect:
19423  painter->setPen(mPen);
19424  painter->setBrush(mBrush);
19425  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
19426  r.moveCenter(rect.center());
19427  painter->drawRect(r);
19428 }
19429 
19436 void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
19437 {
19438  QRectF box;
19439  box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
19440  box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
19442  painter->setPen(mainPen());
19443  painter->setBrush(mainBrush());
19444  painter->drawRect(box);
19445  if (quartileBox)
19446  *quartileBox = box;
19447 }
19448 
19454 {
19455  QLineF medianLine;
19456  medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
19457  medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
19459  painter->setPen(mMedianPen);
19460  painter->drawLine(medianLine);
19461 }
19462 
19468 {
19469  QLineF backboneMin, backboneMax, barMin, barMax;
19470  backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
19471  backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
19475  painter->setPen(mWhiskerPen);
19476  painter->drawLine(backboneMin);
19477  painter->drawLine(backboneMax);
19478  painter->setPen(mWhiskerBarPen);
19479  painter->drawLine(barMin);
19480  painter->drawLine(barMax);
19481 }
19482 
19488 {
19490  mOutlierStyle.applyTo(painter, mPen);
19491  for (int i=0; i<mOutliers.size(); ++i)
19493 }
19494 
19495 /* inherits documentation from base class */
19496 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19497 {
19498  foundRange = true;
19499  if (inSignDomain == sdBoth)
19500  {
19501  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19502  } else if (inSignDomain == sdNegative)
19503  {
19504  if (mKey+mWidth*0.5 < 0)
19505  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19506  else if (mKey < 0)
19507  return QCPRange(mKey-mWidth*0.5, mKey);
19508  else
19509  {
19510  foundRange = false;
19511  return QCPRange();
19512  }
19513  } else if (inSignDomain == sdPositive)
19514  {
19515  if (mKey-mWidth*0.5 > 0)
19516  return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19517  else if (mKey > 0)
19518  return QCPRange(mKey, mKey+mWidth*0.5);
19519  else
19520  {
19521  foundRange = false;
19522  return QCPRange();
19523  }
19524  }
19525  foundRange = false;
19526  return QCPRange();
19527 }
19528 
19529 /* inherits documentation from base class */
19530 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19531 {
19532  QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
19533  values.reserve(mOutliers.size() + 5);
19534  values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
19535  values << mOutliers;
19536  // go through values and find the ones in legal range:
19537  bool haveUpper = false;
19538  bool haveLower = false;
19539  double upper = 0;
19540  double lower = 0;
19541  for (int i=0; i<values.size(); ++i)
19542  {
19543  if ((inSignDomain == sdNegative && values.at(i) < 0) ||
19544  (inSignDomain == sdPositive && values.at(i) > 0) ||
19545  (inSignDomain == sdBoth))
19546  {
19547  if (values.at(i) > upper || !haveUpper)
19548  {
19549  upper = values.at(i);
19550  haveUpper = true;
19551  }
19552  if (values.at(i) < lower || !haveLower)
19553  {
19554  lower = values.at(i);
19555  haveLower = true;
19556  }
19557  }
19558  }
19559  // return the bounds if we found some sensible values:
19560  if (haveLower && haveUpper)
19561  {
19562  foundRange = true;
19563  return QCPRange(lower, upper);
19564  } else // might happen if all values are in other sign domain
19565  {
19566  foundRange = false;
19567  return QCPRange();
19568  }
19569 }
19570 
19571 
19575 
19604 /* start of documentation of inline functions */
19605 
19612 /* end of documentation of inline functions */
19613 
19621 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
19622  mKeySize(0),
19623  mValueSize(0),
19624  mKeyRange(keyRange),
19625  mValueRange(valueRange),
19626  mIsEmpty(true),
19627  mData(0),
19628  mDataModified(true)
19629 {
19630  setSize(keySize, valueSize);
19631  fill(0);
19632 }
19633 
19635 {
19636  if (mData)
19637  delete[] mData;
19638 }
19639 
19644  mKeySize(0),
19645  mValueSize(0),
19646  mIsEmpty(true),
19647  mData(0),
19648  mDataModified(true)
19649 {
19650  *this = other;
19651 }
19652 
19657 {
19658  if (&other != this)
19659  {
19660  const int keySize = other.keySize();
19661  const int valueSize = other.valueSize();
19662  setSize(keySize, valueSize);
19663  setRange(other.keyRange(), other.valueRange());
19664  if (!mIsEmpty)
19665  memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
19666  mDataBounds = other.mDataBounds;
19667  mDataModified = true;
19668  }
19669  return *this;
19670 }
19671 
19672 /* undocumented getter */
19673 double QCPColorMapData::data(double key, double value)
19674 {
19675  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19676  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19677  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19678  return mData[valueCell*mKeySize + keyCell];
19679  else
19680  return 0;
19681 }
19682 
19683 /* undocumented getter */
19684 double QCPColorMapData::cell(int keyIndex, int valueIndex)
19685 {
19686  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19687  return mData[valueIndex*mKeySize + keyIndex];
19688  else
19689  return 0;
19690 }
19691 
19705 {
19706  if (keySize != mKeySize || valueSize != mValueSize)
19707  {
19708  mKeySize = keySize;
19710  if (mData)
19711  delete[] mData;
19712  mIsEmpty = mKeySize == 0 || mValueSize == 0;
19713  if (!mIsEmpty)
19714  {
19715 #ifdef __EXCEPTIONS
19716  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
19717 #endif
19718  mData = new double[mKeySize*mValueSize];
19719 #ifdef __EXCEPTIONS
19720  } catch (...) { mData = 0; }
19721 #endif
19722  if (mData)
19723  fill(0);
19724  else
19725  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
19726  } else
19727  mData = 0;
19728  mDataModified = true;
19729  }
19730 }
19731 
19743 {
19744  setSize(keySize, mValueSize);
19745 }
19746 
19758 {
19759  setSize(mKeySize, valueSize);
19760 }
19761 
19773 {
19774  setKeyRange(keyRange);
19775  setValueRange(valueRange);
19776 }
19777 
19789 {
19790  mKeyRange = keyRange;
19791 }
19792 
19804 {
19806 }
19807 
19820 void QCPColorMapData::setData(double key, double value, double z)
19821 {
19822  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19823  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19824  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19825  {
19826  mData[valueCell*mKeySize + keyCell] = z;
19827  if (z < mDataBounds.lower)
19828  mDataBounds.lower = z;
19829  if (z > mDataBounds.upper)
19830  mDataBounds.upper = z;
19831  mDataModified = true;
19832  }
19833 }
19834 
19846 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
19847 {
19848  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19849  {
19850  mData[valueIndex*mKeySize + keyIndex] = z;
19851  if (z < mDataBounds.lower)
19852  mDataBounds.lower = z;
19853  if (z > mDataBounds.upper)
19854  mDataBounds.upper = z;
19855  mDataModified = true;
19856  }
19857 }
19858 
19873 {
19874  if (mKeySize > 0 && mValueSize > 0)
19875  {
19876  double minHeight = mData[0];
19877  double maxHeight = mData[0];
19878  const int dataCount = mValueSize*mKeySize;
19879  for (int i=0; i<dataCount; ++i)
19880  {
19881  if (mData[i] > maxHeight)
19882  maxHeight = mData[i];
19883  if (mData[i] < minHeight)
19884  minHeight = mData[i];
19885  }
19886  mDataBounds.lower = minHeight;
19887  mDataBounds.upper = maxHeight;
19888  }
19889 }
19890 
19897 {
19898  setSize(0, 0);
19899 }
19900 
19905 {
19906  const int dataCount = mValueSize*mKeySize;
19907  for (int i=0; i<dataCount; ++i)
19908  mData[i] = z;
19909  mDataBounds = QCPRange(z, z);
19910  mDataModified = true;
19911 }
19912 
19930 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
19931 {
19932  if (keyIndex)
19933  *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19934  if (valueIndex)
19935  *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19936 }
19937 
19953 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
19954 {
19955  if (key)
19956  *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
19957  if (value)
19958  *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
19959 }
19960 
19961 
19965 
20024 /* start documentation of inline functions */
20025 
20034 /* end documentation of inline functions */
20035 
20036 /* start documentation of signals */
20037 
20059 /* end documentation of signals */
20060 
20068  QCPAbstractPlottable(keyAxis, valueAxis),
20069  mDataScaleType(QCPAxis::stLinear),
20070  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
20071  mInterpolate(true),
20072  mTightBoundary(false),
20073  mMapImageInvalidated(true)
20074 {
20075 }
20076 
20078 {
20079  delete mMapData;
20080 }
20081 
20090 {
20091  if (mMapData == data)
20092  {
20093  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20094  return;
20095  }
20096  if (copy)
20097  {
20098  *mMapData = *data;
20099  } else
20100  {
20101  delete mMapData;
20102  mMapData = data;
20103  }
20104  mMapImageInvalidated = true;
20105 }
20106 
20116 {
20117  if (!QCPRange::validRange(dataRange)) return;
20118  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
20119  {
20121  mDataRange = dataRange.sanitizedForLogScale();
20122  else
20123  mDataRange = dataRange.sanitizedForLinScale();
20124  mMapImageInvalidated = true;
20126  }
20127 }
20128 
20135 {
20136  if (mDataScaleType != scaleType)
20137  {
20138  mDataScaleType = scaleType;
20139  mMapImageInvalidated = true;
20143  }
20144 }
20145 
20158 {
20159  if (mGradient != gradient)
20160  {
20161  mGradient = gradient;
20162  mMapImageInvalidated = true;
20163  emit gradientChanged(mGradient);
20164  }
20165 }
20166 
20174 {
20175  mInterpolate = enabled;
20176  mMapImageInvalidated = true; // because oversampling factors might need to change
20177 }
20178 
20191 {
20192  mTightBoundary = enabled;
20193 }
20194 
20210 {
20211  if (mColorScale) // unconnect signals from old color scale
20212  {
20213  disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20214  disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20215  disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20216  disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20217  disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20218  disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20219  }
20221  if (mColorScale) // connect signals to new color scale
20222  {
20223  setGradient(mColorScale.data()->gradient());
20224  setDataRange(mColorScale.data()->dataRange());
20225  setDataScaleType(mColorScale.data()->dataScaleType());
20226  connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20227  connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20228  connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20229  connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20230  connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20231  connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20232  }
20233 }
20234 
20255 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
20256 {
20257  if (recalculateDataBounds)
20260 }
20261 
20276 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
20277 {
20278  if (mMapImage.isNull() && !data()->isEmpty())
20279  updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
20280 
20281  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
20282  {
20283  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20284  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20285  mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
20286  }
20287 }
20288 
20294 {
20295  mMapData->clear();
20296 }
20297 
20298 /* inherits documentation from base class */
20299 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20300 {
20301  Q_UNUSED(details)
20302  if (onlySelectable && !mSelectable)
20303  return -1;
20304  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20305 
20306  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20307  {
20308  double posKey, posValue;
20309  pixelsToCoords(pos, posKey, posValue);
20310  if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
20311  return mParentPlot->selectionTolerance()*0.99;
20312  }
20313  return -1;
20314 }
20315 
20331 {
20332  QCPAxis *keyAxis = mKeyAxis.data();
20333  if (!keyAxis) return;
20334  if (mMapData->isEmpty()) return;
20335 
20336  const int keySize = mMapData->keySize();
20337  const int valueSize = mMapData->valueSize();
20338  int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
20339  int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
20340 
20341  // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
20342  if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
20343  mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), QImage::Format_RGB32);
20344  else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
20345  mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), QImage::Format_RGB32);
20346 
20347  QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage
20348  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20349  {
20350  // resize undersampled map image to actual key/value cell sizes:
20351  if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
20352  mUndersampledMapImage = QImage(QSize(keySize, valueSize), QImage::Format_RGB32);
20353  else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
20354  mUndersampledMapImage = QImage(QSize(valueSize, keySize), QImage::Format_RGB32);
20355  localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image
20356  } else if (!mUndersampledMapImage.isNull())
20357  mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it
20358 
20359  const double *rawData = mMapData->mData;
20360  if (keyAxis->orientation() == Qt::Horizontal)
20361  {
20362  const int lineCount = valueSize;
20363  const int rowCount = keySize;
20364  for (int line=0; line<lineCount; ++line)
20365  {
20366  QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
20367  mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
20368  }
20369  } else // keyAxis->orientation() == Qt::Vertical
20370  {
20371  const int lineCount = keySize;
20372  const int rowCount = valueSize;
20373  for (int line=0; line<lineCount; ++line)
20374  {
20375  QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
20376  mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
20377  }
20378  }
20379 
20380  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20381  {
20382  if (keyAxis->orientation() == Qt::Horizontal)
20383  mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20384  else
20385  mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20386  }
20387  mMapData->mDataModified = false;
20388  mMapImageInvalidated = false;
20389 }
20390 
20391 /* inherits documentation from base class */
20393 {
20394  if (mMapData->isEmpty()) return;
20395  if (!mKeyAxis || !mValueAxis) return;
20397 
20399  updateMapImage();
20400 
20401  // use buffer if painting vectorized (PDF):
20402  bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
20403  QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized
20404  QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in
20405  QPixmap mapBuffer;
20406  double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps
20407  if (useBuffer)
20408  {
20409  mapBufferTarget = painter->clipRegion().boundingRect();
20410  mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
20411  mapBuffer.fill(Qt::transparent);
20412  localPainter = new QCPPainter(&mapBuffer);
20413  localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
20414  localPainter->translate(-mapBufferTarget.topLeft());
20415  }
20416 
20417  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20419  // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):
20420  double halfCellWidth = 0; // in pixels
20421  double halfCellHeight = 0; // in pixels
20422  if (keyAxis()->orientation() == Qt::Horizontal)
20423  {
20424  if (mMapData->keySize() > 1)
20425  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
20426  if (mMapData->valueSize() > 1)
20427  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
20428  } else // keyAxis orientation is Qt::Vertical
20429  {
20430  if (mMapData->keySize() > 1)
20431  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
20432  if (mMapData->valueSize() > 1)
20433  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
20434  }
20435  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
20436  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20437  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20438  bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
20439  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
20440  QRegion clipBackup;
20441  if (mTightBoundary)
20442  {
20443  clipBackup = localPainter->clipRegion();
20444  QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20446  localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
20447  }
20448  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
20449  if (mTightBoundary)
20450  localPainter->setClipRegion(clipBackup);
20451  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
20452 
20453  if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter
20454  {
20455  delete localPainter;
20456  painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
20457  }
20458 }
20459 
20460 /* inherits documentation from base class */
20461 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20462 {
20464  // draw map thumbnail:
20465  if (!mLegendIcon.isNull())
20466  {
20467  QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
20468  QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
20469  iconRect.moveCenter(rect.center());
20470  painter->drawPixmap(iconRect.topLeft(), scaledIcon);
20471  }
20472  /*
20473  // draw frame:
20474  painter->setBrush(Qt::NoBrush);
20475  painter->setPen(Qt::black);
20476  painter->drawRect(rect.adjusted(1, 1, 0, 0));
20477  */
20478 }
20479 
20480 /* inherits documentation from base class */
20481 QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
20482 {
20483  foundRange = true;
20484  QCPRange result = mMapData->keyRange();
20485  result.normalize();
20486  if (inSignDomain == QCPAbstractPlottable::sdPositive)
20487  {
20488  if (result.lower <= 0 && result.upper > 0)
20489  result.lower = result.upper*1e-3;
20490  else if (result.lower <= 0 && result.upper <= 0)
20491  foundRange = false;
20492  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20493  {
20494  if (result.upper >= 0 && result.lower < 0)
20495  result.upper = result.lower*1e-3;
20496  else if (result.upper >= 0 && result.lower >= 0)
20497  foundRange = false;
20498  }
20499  return result;
20500 }
20501 
20502 /* inherits documentation from base class */
20503 QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
20504 {
20505  foundRange = true;
20506  QCPRange result = mMapData->valueRange();
20507  result.normalize();
20508  if (inSignDomain == QCPAbstractPlottable::sdPositive)
20509  {
20510  if (result.lower <= 0 && result.upper > 0)
20511  result.lower = result.upper*1e-3;
20512  else if (result.lower <= 0 && result.upper <= 0)
20513  foundRange = false;
20514  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20515  {
20516  if (result.upper >= 0 && result.lower < 0)
20517  result.upper = result.lower*1e-3;
20518  else if (result.upper >= 0 && result.lower >= 0)
20519  foundRange = false;
20520  }
20521  return result;
20522 }
20523 
20524 
20528 
20548  key(0),
20549  open(0),
20550  high(0),
20551  low(0),
20552  close(0)
20553 {
20554 }
20555 
20559 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
20560  key(key),
20561  open(open),
20562  high(high),
20563  low(low),
20564  close(close)
20565 {
20566 }
20567 
20568 
20572 
20605 /* start of documentation of inline functions */
20606 
20614 /* end of documentation of inline functions */
20615 
20626  QCPAbstractPlottable(keyAxis, valueAxis),
20627  mData(0),
20628  mChartStyle(csOhlc),
20629  mWidth(0.5),
20630  mTwoColored(false),
20631  mBrushPositive(QBrush(QColor(210, 210, 255))),
20632  mBrushNegative(QBrush(QColor(255, 210, 210))),
20633  mPenPositive(QPen(QColor(10, 40, 180))),
20634  mPenNegative(QPen(QColor(180, 40, 10)))
20635 {
20636  mData = new QCPFinancialDataMap;
20637 
20638  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
20639  setSelectedBrush(QBrush(QColor(80, 80, 255)));
20640 }
20641 
20643 {
20644  delete mData;
20645 }
20646 
20660 {
20661  if (mData == data)
20662  {
20663  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20664  return;
20665  }
20666  if (copy)
20667  {
20668  *mData = *data;
20669  } else
20670  {
20671  delete mData;
20672  mData = data;
20673  }
20674 }
20675 
20683 void QCPFinancial::setData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20684 {
20685  mData->clear();
20686  int n = key.size();
20687  n = qMin(n, open.size());
20688  n = qMin(n, high.size());
20689  n = qMin(n, low.size());
20690  n = qMin(n, close.size());
20691  for (int i=0; i<n; ++i)
20692  {
20693  mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20694  }
20695 }
20696 
20701 {
20702  mChartStyle = style;
20703 }
20704 
20711 {
20712  mWidth = width;
20713 }
20714 
20725 {
20727 }
20728 
20739 {
20741 }
20742 
20753 {
20755 }
20756 
20767 {
20768  mPenPositive = pen;
20769 }
20770 
20781 {
20782  mPenNegative = pen;
20783 }
20784 
20794 {
20795  mData->unite(dataMap);
20796 }
20797 
20808 {
20809  mData->insertMulti(data.key, data);
20810 }
20811 
20822 void QCPFinancial::addData(double key, double open, double high, double low, double close)
20823 {
20824  mData->insertMulti(key, QCPFinancialData(key, open, high, low, close));
20825 }
20826 
20836 void QCPFinancial::addData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20837 {
20838  int n = key.size();
20839  n = qMin(n, open.size());
20840  n = qMin(n, high.size());
20841  n = qMin(n, low.size());
20842  n = qMin(n, close.size());
20843  for (int i=0; i<n; ++i)
20844  {
20845  mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20846  }
20847 }
20848 
20855 {
20856  QCPFinancialDataMap::iterator it = mData->begin();
20857  while (it != mData->end() && it.key() < key)
20858  it = mData->erase(it);
20859 }
20860 
20867 {
20868  if (mData->isEmpty()) return;
20869  QCPFinancialDataMap::iterator it = mData->upperBound(key);
20870  while (it != mData->end())
20871  it = mData->erase(it);
20872 }
20873 
20881 void QCPFinancial::removeData(double fromKey, double toKey)
20882 {
20883  if (fromKey >= toKey || mData->isEmpty()) return;
20884  QCPFinancialDataMap::iterator it = mData->upperBound(fromKey);
20885  QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey);
20886  while (it != itEnd)
20887  it = mData->erase(it);
20888 }
20889 
20899 {
20900  mData->remove(key);
20901 }
20902 
20909 {
20910  mData->clear();
20911 }
20912 
20913 /* inherits documentation from base class */
20914 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20915 {
20916  Q_UNUSED(details)
20917  if (onlySelectable && !mSelectable)
20918  return -1;
20919  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20920 
20921  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20922  {
20923  // get visible data range:
20924  QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20925  getVisibleDataBounds(lower, upper);
20926  if (lower == mData->constEnd() || upper == mData->constEnd())
20927  return -1;
20928  // perform select test according to configured style:
20929  switch (mChartStyle)
20930  {
20931  case QCPFinancial::csOhlc:
20932  return ohlcSelectTest(pos, lower, upper+1); break;
20934  return candlestickSelectTest(pos, lower, upper+1); break;
20935  }
20936  }
20937  return -1;
20938 }
20939 
20953 QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
20954 {
20955  QCPFinancialDataMap map;
20956  int count = qMin(time.size(), value.size());
20957  if (count == 0)
20958  return QCPFinancialDataMap();
20959 
20960  QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
20961  int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
20962  for (int i=0; i<count; ++i)
20963  {
20964  int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
20965  if (currentBinIndex == index) // data point still in current bin, extend high/low:
20966  {
20967  if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
20968  if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
20969  if (i == count-1) // last data point is in current bin, finalize bin:
20970  {
20971  currentBinData.close = value.at(i);
20972  currentBinData.key = timeBinOffset+(index)*timeBinSize;
20973  map.insert(currentBinData.key, currentBinData);
20974  }
20975  } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
20976  {
20977  // finalize current bin:
20978  currentBinData.close = value.at(i-1);
20979  currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
20980  map.insert(currentBinData.key, currentBinData);
20981  // start next bin:
20982  currentBinIndex = index;
20983  currentBinData.open = value.at(i);
20984  currentBinData.high = value.at(i);
20985  currentBinData.low = value.at(i);
20986  }
20987  }
20988 
20989  return map;
20990 }
20991 
20992 /* inherits documentation from base class */
20994 {
20995  // get visible data range:
20996  QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20997  getVisibleDataBounds(lower, upper);
20998  if (lower == mData->constEnd() || upper == mData->constEnd())
20999  return;
21000 
21001  // draw visible data range according to configured style:
21002  switch (mChartStyle)
21003  {
21004  case QCPFinancial::csOhlc:
21005  drawOhlcPlot(painter, lower, upper+1); break;
21007  drawCandlestickPlot(painter, lower, upper+1); break;
21008  }
21009 }
21010 
21011 /* inherits documentation from base class */
21012 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
21013 {
21014  painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
21015  if (mChartStyle == csOhlc)
21016  {
21017  if (mTwoColored)
21018  {
21019  // draw upper left half icon with positive color:
21020  painter->setBrush(mBrushPositive);
21021  painter->setPen(mPenPositive);
21022  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21023  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21024  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21025  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21026  // draw bottom right hald icon with negative color:
21027  painter->setBrush(mBrushNegative);
21028  painter->setPen(mPenNegative);
21029  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21030  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21031  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21032  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21033  } else
21034  {
21035  painter->setBrush(mBrush);
21036  painter->setPen(mPen);
21037  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21038  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21039  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21040  }
21041  } else if (mChartStyle == csCandlestick)
21042  {
21043  if (mTwoColored)
21044  {
21045  // draw upper left half icon with positive color:
21046  painter->setBrush(mBrushPositive);
21047  painter->setPen(mPenPositive);
21048  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21049  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21050  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21051  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21052  // draw bottom right hald icon with negative color:
21053  painter->setBrush(mBrushNegative);
21054  painter->setPen(mPenNegative);
21055  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21056  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21057  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21058  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21059  } else
21060  {
21061  painter->setBrush(mBrush);
21062  painter->setPen(mPen);
21063  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21064  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21065  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21066  }
21067  }
21068 }
21069 
21070 /* inherits documentation from base class */
21072 {
21073  QCPRange range;
21074  bool haveLower = false;
21075  bool haveUpper = false;
21076 
21077  double current;
21078  QCPFinancialDataMap::const_iterator it = mData->constBegin();
21079  while (it != mData->constEnd())
21080  {
21081  current = it.value().key;
21082  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
21083  {
21084  if (current < range.lower || !haveLower)
21085  {
21086  range.lower = current;
21087  haveLower = true;
21088  }
21089  if (current > range.upper || !haveUpper)
21090  {
21091  range.upper = current;
21092  haveUpper = true;
21093  }
21094  }
21095  ++it;
21096  }
21097  // determine exact range by including width of bars/flags:
21098  if (haveLower && mKeyAxis)
21099  range.lower = range.lower-mWidth*0.5;
21100  if (haveUpper && mKeyAxis)
21101  range.upper = range.upper+mWidth*0.5;
21102  foundRange = haveLower && haveUpper;
21103  return range;
21104 }
21105 
21106 /* inherits documentation from base class */
21108 {
21109  QCPRange range;
21110  bool haveLower = false;
21111  bool haveUpper = false;
21112 
21113  QCPFinancialDataMap::const_iterator it = mData->constBegin();
21114  while (it != mData->constEnd())
21115  {
21116  // high:
21117  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().high < 0) || (inSignDomain == sdPositive && it.value().high > 0))
21118  {
21119  if (it.value().high < range.lower || !haveLower)
21120  {
21121  range.lower = it.value().high;
21122  haveLower = true;
21123  }
21124  if (it.value().high > range.upper || !haveUpper)
21125  {
21126  range.upper = it.value().high;
21127  haveUpper = true;
21128  }
21129  }
21130  // low:
21131  if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().low < 0) || (inSignDomain == sdPositive && it.value().low > 0))
21132  {
21133  if (it.value().low < range.lower || !haveLower)
21134  {
21135  range.lower = it.value().low;
21136  haveLower = true;
21137  }
21138  if (it.value().low > range.upper || !haveUpper)
21139  {
21140  range.upper = it.value().low;
21141  haveUpper = true;
21142  }
21143  }
21144  ++it;
21145  }
21146 
21147  foundRange = haveLower && haveUpper;
21148  return range;
21149 }
21150 
21157 void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21158 {
21159  QCPAxis *keyAxis = mKeyAxis.data();
21160  QCPAxis *valueAxis = mValueAxis.data();
21161  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21162 
21163  QPen linePen;
21164 
21165  if (keyAxis->orientation() == Qt::Horizontal)
21166  {
21167  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21168  {
21169  if (mSelected)
21170  linePen = mSelectedPen;
21171  else if (mTwoColored)
21172  linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21173  else
21174  linePen = mPen;
21175  painter->setPen(linePen);
21176  double keyPixel = keyAxis->coordToPixel(it.value().key);
21177  double openPixel = valueAxis->coordToPixel(it.value().open);
21178  double closePixel = valueAxis->coordToPixel(it.value().close);
21179  // draw backbone:
21180  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
21181  // draw open:
21182  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21183  painter->drawLine(QPointF(keyPixel-keyWidthPixels, openPixel), QPointF(keyPixel, openPixel));
21184  // draw close:
21185  painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+keyWidthPixels, closePixel));
21186  }
21187  } else
21188  {
21189  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21190  {
21191  if (mSelected)
21192  linePen = mSelectedPen;
21193  else if (mTwoColored)
21194  linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21195  else
21196  linePen = mPen;
21197  painter->setPen(linePen);
21198  double keyPixel = keyAxis->coordToPixel(it.value().key);
21199  double openPixel = valueAxis->coordToPixel(it.value().open);
21200  double closePixel = valueAxis->coordToPixel(it.value().close);
21201  // draw backbone:
21202  painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
21203  // draw open:
21204  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21205  painter->drawLine(QPointF(openPixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel));
21206  // draw close:
21207  painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+keyWidthPixels));
21208  }
21209  }
21210 }
21211 
21218 void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21219 {
21220  QCPAxis *keyAxis = mKeyAxis.data();
21221  QCPAxis *valueAxis = mValueAxis.data();
21222  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21223 
21224  QPen linePen;
21225  QBrush boxBrush;
21226 
21227  if (keyAxis->orientation() == Qt::Horizontal)
21228  {
21229  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21230  {
21231  if (mSelected)
21232  {
21233  linePen = mSelectedPen;
21234  boxBrush = mSelectedBrush;
21235  } else if (mTwoColored)
21236  {
21237  if (it.value().close >= it.value().open)
21238  {
21239  linePen = mPenPositive;
21240  boxBrush = mBrushPositive;
21241  } else
21242  {
21243  linePen = mPenNegative;
21244  boxBrush = mBrushNegative;
21245  }
21246  } else
21247  {
21248  linePen = mPen;
21249  boxBrush = mBrush;
21250  }
21251  painter->setPen(linePen);
21252  painter->setBrush(boxBrush);
21253  double keyPixel = keyAxis->coordToPixel(it.value().key);
21254  double openPixel = valueAxis->coordToPixel(it.value().open);
21255  double closePixel = valueAxis->coordToPixel(it.value().close);
21256  // draw high:
21257  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))));
21258  // draw low:
21259  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))));
21260  // draw open-close box:
21261  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21262  painter->drawRect(QRectF(QPointF(keyPixel-keyWidthPixels, closePixel), QPointF(keyPixel+keyWidthPixels, openPixel)));
21263  }
21264  } else // keyAxis->orientation() == Qt::Vertical
21265  {
21266  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21267  {
21268  if (mSelected)
21269  {
21270  linePen = mSelectedPen;
21271  boxBrush = mSelectedBrush;
21272  } else if (mTwoColored)
21273  {
21274  if (it.value().close >= it.value().open)
21275  {
21276  linePen = mPenPositive;
21277  boxBrush = mBrushPositive;
21278  } else
21279  {
21280  linePen = mPenNegative;
21281  boxBrush = mBrushNegative;
21282  }
21283  } else
21284  {
21285  linePen = mPen;
21286  boxBrush = mBrush;
21287  }
21288  painter->setPen(linePen);
21289  painter->setBrush(boxBrush);
21290  double keyPixel = keyAxis->coordToPixel(it.value().key);
21291  double openPixel = valueAxis->coordToPixel(it.value().open);
21292  double closePixel = valueAxis->coordToPixel(it.value().close);
21293  // draw high:
21294  painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel));
21295  // draw low:
21296  painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel));
21297  // draw open-close box:
21298  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21299  painter->drawRect(QRectF(QPointF(closePixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel+keyWidthPixels)));
21300  }
21301  }
21302 }
21303 
21309 double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21310 {
21311  QCPAxis *keyAxis = mKeyAxis.data();
21312  QCPAxis *valueAxis = mValueAxis.data();
21313  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21314 
21315  double minDistSqr = std::numeric_limits<double>::max();
21316  QCPFinancialDataMap::const_iterator it;
21317  if (keyAxis->orientation() == Qt::Horizontal)
21318  {
21319  for (it = begin; it != end; ++it)
21320  {
21321  double keyPixel = keyAxis->coordToPixel(it.value().key);
21322  // calculate distance to backbone:
21323  double currentDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos);
21324  if (currentDistSqr < minDistSqr)
21325  minDistSqr = currentDistSqr;
21326  }
21327  } else // keyAxis->orientation() == Qt::Vertical
21328  {
21329  for (it = begin; it != end; ++it)
21330  {
21331  double keyPixel = keyAxis->coordToPixel(it.value().key);
21332  // calculate distance to backbone:
21333  double currentDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos);
21334  if (currentDistSqr < minDistSqr)
21335  minDistSqr = currentDistSqr;
21336  }
21337  }
21338  return qSqrt(minDistSqr);
21339 }
21340 
21347 double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21348 {
21349  QCPAxis *keyAxis = mKeyAxis.data();
21350  QCPAxis *valueAxis = mValueAxis.data();
21351  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21352 
21353  double minDistSqr = std::numeric_limits<double>::max();
21354  QCPFinancialDataMap::const_iterator it;
21355  if (keyAxis->orientation() == Qt::Horizontal)
21356  {
21357  for (it = begin; it != end; ++it)
21358  {
21359  double currentDistSqr;
21360  // determine whether pos is in open-close-box:
21361  QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21362  QCPRange boxValueRange(it.value().close, it.value().open);
21363  double posKey, posValue;
21364  pixelsToCoords(pos, posKey, posValue);
21365  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21366  {
21367  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21368  } else
21369  {
21370  // calculate distance to high/low lines:
21371  double keyPixel = keyAxis->coordToPixel(it.value().key);
21372  double highLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))), pos);
21373  double lowLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))), pos);
21374  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21375  }
21376  if (currentDistSqr < minDistSqr)
21377  minDistSqr = currentDistSqr;
21378  }
21379  } else // keyAxis->orientation() == Qt::Vertical
21380  {
21381  for (it = begin; it != end; ++it)
21382  {
21383  double currentDistSqr;
21384  // determine whether pos is in open-close-box:
21385  QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21386  QCPRange boxValueRange(it.value().close, it.value().open);
21387  double posKey, posValue;
21388  pixelsToCoords(pos, posKey, posValue);
21389  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21390  {
21391  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21392  } else
21393  {
21394  // calculate distance to high/low lines:
21395  double keyPixel = keyAxis->coordToPixel(it.value().key);
21396  double highLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel), pos);
21397  double lowLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel), pos);
21398  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21399  }
21400  if (currentDistSqr < minDistSqr)
21401  minDistSqr = currentDistSqr;
21402  }
21403  }
21404  return qSqrt(minDistSqr);
21405 }
21406 
21423 void QCPFinancial::getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
21424 {
21425  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
21426  if (mData->isEmpty())
21427  {
21428  lower = mData->constEnd();
21429  upper = mData->constEnd();
21430  return;
21431  }
21432 
21433  // get visible data range as QMap iterators
21434  QCPFinancialDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
21435  QCPFinancialDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
21436  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
21437  bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
21438 
21439  lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
21440  upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
21441 }
21442 
21443 
21447 
21462  QCPAbstractItem(parentPlot),
21463  point1(createPosition(QLatin1String("point1"))),
21464  point2(createPosition(QLatin1String("point2")))
21465 {
21466  point1->setCoords(0, 0);
21467  point2->setCoords(1, 1);
21468 
21469  setPen(QPen(Qt::black));
21470  setSelectedPen(QPen(Qt::blue,2));
21471 }
21472 
21474 {
21475 }
21476 
21483 {
21484  mPen = pen;
21485 }
21486 
21493 {
21494  mSelectedPen = pen;
21495 }
21496 
21497 /* inherits documentation from base class */
21498 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21499 {
21500  Q_UNUSED(details)
21501  if (onlySelectable && !mSelectable)
21502  return -1;
21503 
21504  return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
21505 }
21506 
21507 /* inherits documentation from base class */
21509 {
21510  QVector2D start(point1->pixelPoint());
21511  QVector2D end(point2->pixelPoint());
21512  // get visible segment of straight line inside clipRect:
21513  double clipPad = mainPen().widthF();
21514  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21515  // paint visible segment, if existent:
21516  if (!line.isNull())
21517  {
21518  painter->setPen(mainPen());
21519  painter->drawLine(line);
21520  }
21521 }
21522 
21530 double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
21531 {
21532  return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
21533 }
21534 
21542 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
21543 {
21544  double bx, by;
21545  double gamma;
21546  QLineF result;
21547  if (vec.x() == 0 && vec.y() == 0)
21548  return result;
21549  if (qFuzzyIsNull(vec.x())) // line is vertical
21550  {
21551  // check top of rect:
21552  bx = rect.left();
21553  by = rect.top();
21554  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21555  if (gamma >= 0 && gamma <= rect.width())
21556  result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
21557  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
21558  {
21559  // check left of rect:
21560  bx = rect.left();
21561  by = rect.top();
21562  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21563  if (gamma >= 0 && gamma <= rect.height())
21564  result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
21565  } else // line is skewed
21566  {
21567  QList<QVector2D> pointVectors;
21568  // check top of rect:
21569  bx = rect.left();
21570  by = rect.top();
21571  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21572  if (gamma >= 0 && gamma <= rect.width())
21573  pointVectors.append(QVector2D(bx+gamma, by));
21574  // check bottom of rect:
21575  bx = rect.left();
21576  by = rect.bottom();
21577  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21578  if (gamma >= 0 && gamma <= rect.width())
21579  pointVectors.append(QVector2D(bx+gamma, by));
21580  // check left of rect:
21581  bx = rect.left();
21582  by = rect.top();
21583  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21584  if (gamma >= 0 && gamma <= rect.height())
21585  pointVectors.append(QVector2D(bx, by+gamma));
21586  // check right of rect:
21587  bx = rect.right();
21588  by = rect.top();
21589  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21590  if (gamma >= 0 && gamma <= rect.height())
21591  pointVectors.append(QVector2D(bx, by+gamma));
21592 
21593  // evaluate points:
21594  if (pointVectors.size() == 2)
21595  {
21596  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21597  } else if (pointVectors.size() > 2)
21598  {
21599  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21600  double distSqrMax = 0;
21601  QVector2D pv1, pv2;
21602  for (int i=0; i<pointVectors.size()-1; ++i)
21603  {
21604  for (int k=i+1; k<pointVectors.size(); ++k)
21605  {
21606  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21607  if (distSqr > distSqrMax)
21608  {
21609  pv1 = pointVectors.at(i);
21610  pv2 = pointVectors.at(k);
21611  distSqrMax = distSqr;
21612  }
21613  }
21614  }
21615  result.setPoints(pv1.toPointF(), pv2.toPointF());
21616  }
21617  }
21618  return result;
21619 }
21620 
21627 {
21628  return mSelected ? mSelectedPen : mPen;
21629 }
21630 
21631 
21635 
21652  QCPAbstractItem(parentPlot),
21653  start(createPosition(QLatin1String("start"))),
21654  end(createPosition(QLatin1String("end")))
21655 {
21656  start->setCoords(0, 0);
21657  end->setCoords(1, 1);
21658 
21659  setPen(QPen(Qt::black));
21660  setSelectedPen(QPen(Qt::blue,2));
21661 }
21662 
21664 {
21665 }
21666 
21672 void QCPItemLine::setPen(const QPen &pen)
21673 {
21674  mPen = pen;
21675 }
21676 
21683 {
21684  mSelectedPen = pen;
21685 }
21686 
21696 {
21697  mHead = head;
21698 }
21699 
21709 {
21710  mTail = tail;
21711 }
21712 
21713 /* inherits documentation from base class */
21714 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21715 {
21716  Q_UNUSED(details)
21717  if (onlySelectable && !mSelectable)
21718  return -1;
21719 
21720  return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
21721 }
21722 
21723 /* inherits documentation from base class */
21725 {
21726  QVector2D startVec(start->pixelPoint());
21727  QVector2D endVec(end->pixelPoint());
21728  if (startVec.toPoint() == endVec.toPoint())
21729  return;
21730  // get visible segment of straight line inside clipRect:
21731  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
21732  clipPad = qMax(clipPad, (double)mainPen().widthF());
21733  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21734  // paint visible segment, if existent:
21735  if (!line.isNull())
21736  {
21737  painter->setPen(mainPen());
21738  painter->drawLine(line);
21739  painter->setBrush(Qt::SolidPattern);
21741  mTail.draw(painter, startVec, startVec-endVec);
21743  mHead.draw(painter, endVec, endVec-startVec);
21744  }
21745 }
21746 
21754 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
21755 {
21756  bool containsStart = rect.contains(start.x(), start.y());
21757  bool containsEnd = rect.contains(end.x(), end.y());
21758  if (containsStart && containsEnd)
21759  return QLineF(start.toPointF(), end.toPointF());
21760 
21761  QVector2D base = start;
21762  QVector2D vec = end-start;
21763  double bx, by;
21764  double gamma, mu;
21765  QLineF result;
21766  QList<QVector2D> pointVectors;
21767 
21768  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
21769  {
21770  // check top of rect:
21771  bx = rect.left();
21772  by = rect.top();
21773  mu = (by-base.y())/vec.y();
21774  if (mu >= 0 && mu <= 1)
21775  {
21776  gamma = base.x()-bx + mu*vec.x();
21777  if (gamma >= 0 && gamma <= rect.width())
21778  pointVectors.append(QVector2D(bx+gamma, by));
21779  }
21780  // check bottom of rect:
21781  bx = rect.left();
21782  by = rect.bottom();
21783  mu = (by-base.y())/vec.y();
21784  if (mu >= 0 && mu <= 1)
21785  {
21786  gamma = base.x()-bx + mu*vec.x();
21787  if (gamma >= 0 && gamma <= rect.width())
21788  pointVectors.append(QVector2D(bx+gamma, by));
21789  }
21790  }
21791  if (!qFuzzyIsNull(vec.x())) // line is not vertical
21792  {
21793  // check left of rect:
21794  bx = rect.left();
21795  by = rect.top();
21796  mu = (bx-base.x())/vec.x();
21797  if (mu >= 0 && mu <= 1)
21798  {
21799  gamma = base.y()-by + mu*vec.y();
21800  if (gamma >= 0 && gamma <= rect.height())
21801  pointVectors.append(QVector2D(bx, by+gamma));
21802  }
21803  // check right of rect:
21804  bx = rect.right();
21805  by = rect.top();
21806  mu = (bx-base.x())/vec.x();
21807  if (mu >= 0 && mu <= 1)
21808  {
21809  gamma = base.y()-by + mu*vec.y();
21810  if (gamma >= 0 && gamma <= rect.height())
21811  pointVectors.append(QVector2D(bx, by+gamma));
21812  }
21813  }
21814 
21815  if (containsStart)
21816  pointVectors.append(start);
21817  if (containsEnd)
21818  pointVectors.append(end);
21819 
21820  // evaluate points:
21821  if (pointVectors.size() == 2)
21822  {
21823  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21824  } else if (pointVectors.size() > 2)
21825  {
21826  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21827  double distSqrMax = 0;
21828  QVector2D pv1, pv2;
21829  for (int i=0; i<pointVectors.size()-1; ++i)
21830  {
21831  for (int k=i+1; k<pointVectors.size(); ++k)
21832  {
21833  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21834  if (distSqr > distSqrMax)
21835  {
21836  pv1 = pointVectors.at(i);
21837  pv2 = pointVectors.at(k);
21838  distSqrMax = distSqr;
21839  }
21840  }
21841  }
21842  result.setPoints(pv1.toPointF(), pv2.toPointF());
21843  }
21844  return result;
21845 }
21846 
21853 {
21854  return mSelected ? mSelectedPen : mPen;
21855 }
21856 
21857 
21861 
21885  QCPAbstractItem(parentPlot),
21886  start(createPosition(QLatin1String("start"))),
21887  startDir(createPosition(QLatin1String("startDir"))),
21888  endDir(createPosition(QLatin1String("endDir"))),
21889  end(createPosition(QLatin1String("end")))
21890 {
21891  start->setCoords(0, 0);
21892  startDir->setCoords(0.5, 0);
21893  endDir->setCoords(0, 0.5);
21894  end->setCoords(1, 1);
21895 
21896  setPen(QPen(Qt::black));
21897  setSelectedPen(QPen(Qt::blue,2));
21898 }
21899 
21901 {
21902 }
21903 
21909 void QCPItemCurve::setPen(const QPen &pen)
21910 {
21911  mPen = pen;
21912 }
21913 
21920 {
21921  mSelectedPen = pen;
21922 }
21923 
21933 {
21934  mHead = head;
21935 }
21936 
21946 {
21947  mTail = tail;
21948 }
21949 
21950 /* inherits documentation from base class */
21951 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21952 {
21953  Q_UNUSED(details)
21954  if (onlySelectable && !mSelectable)
21955  return -1;
21956 
21957  QPointF startVec(start->pixelPoint());
21958  QPointF startDirVec(startDir->pixelPoint());
21959  QPointF endDirVec(endDir->pixelPoint());
21960  QPointF endVec(end->pixelPoint());
21961 
21962  QPainterPath cubicPath(startVec);
21963  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21964 
21965  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
21966  double minDistSqr = std::numeric_limits<double>::max();
21967  for (int i=1; i<polygon.size(); ++i)
21968  {
21969  double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
21970  if (distSqr < minDistSqr)
21971  minDistSqr = distSqr;
21972  }
21973  return qSqrt(minDistSqr);
21974 }
21975 
21976 /* inherits documentation from base class */
21978 {
21979  QPointF startVec(start->pixelPoint());
21980  QPointF startDirVec(startDir->pixelPoint());
21981  QPointF endDirVec(endDir->pixelPoint());
21982  QPointF endVec(end->pixelPoint());
21983  if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
21984  return;
21985 
21986  QPainterPath cubicPath(startVec);
21987  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21988 
21989  // paint visible segment, if existent:
21990  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
21991  QRect cubicRect = cubicPath.controlPointRect().toRect();
21992  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
21993  cubicRect.adjust(0, 0, 1, 1);
21994  if (clip.intersects(cubicRect))
21995  {
21996  painter->setPen(mainPen());
21997  painter->drawPath(cubicPath);
21998  painter->setBrush(Qt::SolidPattern);
22000  mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
22002  mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
22003  }
22004 }
22005 
22012 {
22013  return mSelected ? mSelectedPen : mPen;
22014 }
22015 
22016 
22020 
22035  QCPAbstractItem(parentPlot),
22036  topLeft(createPosition(QLatin1String("topLeft"))),
22037  bottomRight(createPosition(QLatin1String("bottomRight"))),
22038  top(createAnchor(QLatin1String("top"), aiTop)),
22039  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22040  right(createAnchor(QLatin1String("right"), aiRight)),
22041  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22042  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22043  left(createAnchor(QLatin1String("left"), aiLeft))
22044 {
22045  topLeft->setCoords(0, 1);
22046  bottomRight->setCoords(1, 0);
22047 
22048  setPen(QPen(Qt::black));
22049  setSelectedPen(QPen(Qt::blue,2));
22050  setBrush(Qt::NoBrush);
22051  setSelectedBrush(Qt::NoBrush);
22052 }
22053 
22055 {
22056 }
22057 
22063 void QCPItemRect::setPen(const QPen &pen)
22064 {
22065  mPen = pen;
22066 }
22067 
22074 {
22075  mSelectedPen = pen;
22076 }
22077 
22084 void QCPItemRect::setBrush(const QBrush &brush)
22085 {
22086  mBrush = brush;
22087 }
22088 
22096 {
22098 }
22099 
22100 /* inherits documentation from base class */
22101 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22102 {
22103  Q_UNUSED(details)
22104  if (onlySelectable && !mSelectable)
22105  return -1;
22106 
22107  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
22108  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
22109  return rectSelectTest(rect, pos, filledRect);
22110 }
22111 
22112 /* inherits documentation from base class */
22114 {
22115  QPointF p1 = topLeft->pixelPoint();
22116  QPointF p2 = bottomRight->pixelPoint();
22117  if (p1.toPoint() == p2.toPoint())
22118  return;
22119  QRectF rect = QRectF(p1, p2).normalized();
22120  double clipPad = mainPen().widthF();
22121  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22122  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
22123  {
22124  painter->setPen(mainPen());
22125  painter->setBrush(mainBrush());
22126  painter->drawRect(rect);
22127  }
22128 }
22129 
22130 /* inherits documentation from base class */
22131 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
22132 {
22133  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22134  switch (anchorId)
22135  {
22136  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22137  case aiTopRight: return rect.topRight();
22138  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22139  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22140  case aiBottomLeft: return rect.bottomLeft();
22141  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22142  }
22143 
22144  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22145  return QPointF();
22146 }
22147 
22154 {
22155  return mSelected ? mSelectedPen : mPen;
22156 }
22157 
22164 {
22165  return mSelected ? mSelectedBrush : mBrush;
22166 }
22167 
22168 
22172 
22193  QCPAbstractItem(parentPlot),
22194  position(createPosition(QLatin1String("position"))),
22195  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
22196  top(createAnchor(QLatin1String("top"), aiTop)),
22197  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22198  right(createAnchor(QLatin1String("right"), aiRight)),
22199  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
22200  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22201  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22202  left(createAnchor(QLatin1String("left"), aiLeft))
22203 {
22204  position->setCoords(0, 0);
22205 
22206  setRotation(0);
22207  setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
22208  setPositionAlignment(Qt::AlignCenter);
22209  setText(QLatin1String("text"));
22210 
22211  setPen(Qt::NoPen);
22212  setSelectedPen(Qt::NoPen);
22213  setBrush(Qt::NoBrush);
22214  setSelectedBrush(Qt::NoBrush);
22215  setColor(Qt::black);
22216  setSelectedColor(Qt::blue);
22217 }
22218 
22220 {
22221 }
22222 
22226 void QCPItemText::setColor(const QColor &color)
22227 {
22228  mColor = color;
22229 }
22230 
22235 {
22237 }
22238 
22245 void QCPItemText::setPen(const QPen &pen)
22246 {
22247  mPen = pen;
22248 }
22249 
22257 {
22258  mSelectedPen = pen;
22259 }
22260 
22267 void QCPItemText::setBrush(const QBrush &brush)
22268 {
22269  mBrush = brush;
22270 }
22271 
22279 {
22281 }
22282 
22288 void QCPItemText::setFont(const QFont &font)
22289 {
22290  mFont = font;
22291 }
22292 
22299 {
22300  mSelectedFont = font;
22301 }
22302 
22309 void QCPItemText::setText(const QString &text)
22310 {
22311  mText = text;
22312 }
22313 
22326 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
22327 {
22328  mPositionAlignment = alignment;
22329 }
22330 
22334 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
22335 {
22336  mTextAlignment = alignment;
22337 }
22338 
22343 void QCPItemText::setRotation(double degrees)
22344 {
22345  mRotation = degrees;
22346 }
22347 
22352 void QCPItemText::setPadding(const QMargins &padding)
22353 {
22354  mPadding = padding;
22355 }
22356 
22357 /* inherits documentation from base class */
22358 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22359 {
22360  Q_UNUSED(details)
22361  if (onlySelectable && !mSelectable)
22362  return -1;
22363 
22364  // The rect may be rotated, so we transform the actual clicked pos to the rotated
22365  // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
22366  QPointF positionPixels(position->pixelPoint());
22367  QTransform inputTransform;
22368  inputTransform.translate(positionPixels.x(), positionPixels.y());
22369  inputTransform.rotate(-mRotation);
22370  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
22371  QPointF rotatedPos = inputTransform.map(pos);
22372  QFontMetrics fontMetrics(mFont);
22373  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22374  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22375  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
22376  textBoxRect.moveTopLeft(textPos.toPoint());
22377 
22378  return rectSelectTest(textBoxRect, rotatedPos, true);
22379 }
22380 
22381 /* inherits documentation from base class */
22383 {
22384  QPointF pos(position->pixelPoint());
22385  QTransform transform = painter->transform();
22386  transform.translate(pos.x(), pos.y());
22387  if (!qFuzzyIsNull(mRotation))
22388  transform.rotate(mRotation);
22389  painter->setFont(mainFont());
22390  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22391  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22392  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22393  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
22394  textBoxRect.moveTopLeft(textPos.toPoint());
22395  double clipPad = mainPen().widthF();
22396  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22397  if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
22398  {
22399  painter->setTransform(transform);
22400  if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
22401  (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
22402  {
22403  painter->setPen(mainPen());
22404  painter->setBrush(mainBrush());
22405  painter->drawRect(textBoxRect);
22406  }
22407  painter->setBrush(Qt::NoBrush);
22408  painter->setPen(QPen(mainColor()));
22409  painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
22410  }
22411 }
22412 
22413 /* inherits documentation from base class */
22414 QPointF QCPItemText::anchorPixelPoint(int anchorId) const
22415 {
22416  // get actual rect points (pretty much copied from draw function):
22417  QPointF pos(position->pixelPoint());
22418  QTransform transform;
22419  transform.translate(pos.x(), pos.y());
22420  if (!qFuzzyIsNull(mRotation))
22421  transform.rotate(mRotation);
22422  QFontMetrics fontMetrics(mainFont());
22423  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22424  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22425  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22426  textBoxRect.moveTopLeft(textPos.toPoint());
22427  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
22428 
22429  switch (anchorId)
22430  {
22431  case aiTopLeft: return rectPoly.at(0);
22432  case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
22433  case aiTopRight: return rectPoly.at(1);
22434  case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
22435  case aiBottomRight: return rectPoly.at(2);
22436  case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
22437  case aiBottomLeft: return rectPoly.at(3);
22438  case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
22439  }
22440 
22441  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22442  return QPointF();
22443 }
22444 
22455 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
22456 {
22457  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
22458  return pos;
22459 
22460  QPointF result = pos; // start at top left
22461  if (positionAlignment.testFlag(Qt::AlignHCenter))
22462  result.rx() -= rect.width()/2.0;
22463  else if (positionAlignment.testFlag(Qt::AlignRight))
22464  result.rx() -= rect.width();
22465  if (positionAlignment.testFlag(Qt::AlignVCenter))
22466  result.ry() -= rect.height()/2.0;
22467  else if (positionAlignment.testFlag(Qt::AlignBottom))
22468  result.ry() -= rect.height();
22469  return result;
22470 }
22471 
22478 {
22479  return mSelected ? mSelectedFont : mFont;
22480 }
22481 
22488 {
22489  return mSelected ? mSelectedColor : mColor;
22490 }
22491 
22498 {
22499  return mSelected ? mSelectedPen : mPen;
22500 }
22501 
22508 {
22509  return mSelected ? mSelectedBrush : mBrush;
22510 }
22511 
22512 
22516 
22531  QCPAbstractItem(parentPlot),
22532  topLeft(createPosition(QLatin1String("topLeft"))),
22533  bottomRight(createPosition(QLatin1String("bottomRight"))),
22534  topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
22535  top(createAnchor(QLatin1String("top"), aiTop)),
22536  topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
22537  right(createAnchor(QLatin1String("right"), aiRight)),
22538  bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
22539  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22540  bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
22541  left(createAnchor(QLatin1String("left"), aiLeft)),
22542  center(createAnchor(QLatin1String("center"), aiCenter))
22543 {
22544  topLeft->setCoords(0, 1);
22545  bottomRight->setCoords(1, 0);
22546 
22547  setPen(QPen(Qt::black));
22548  setSelectedPen(QPen(Qt::blue, 2));
22549  setBrush(Qt::NoBrush);
22550  setSelectedBrush(Qt::NoBrush);
22551 }
22552 
22554 {
22555 }
22556 
22562 void QCPItemEllipse::setPen(const QPen &pen)
22563 {
22564  mPen = pen;
22565 }
22566 
22573 {
22574  mSelectedPen = pen;
22575 }
22576 
22583 void QCPItemEllipse::setBrush(const QBrush &brush)
22584 {
22585  mBrush = brush;
22586 }
22587 
22595 {
22597 }
22598 
22599 /* inherits documentation from base class */
22600 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22601 {
22602  Q_UNUSED(details)
22603  if (onlySelectable && !mSelectable)
22604  return -1;
22605 
22606  double result = -1;
22607  QPointF p1 = topLeft->pixelPoint();
22608  QPointF p2 = bottomRight->pixelPoint();
22609  QPointF center((p1+p2)/2.0);
22610  double a = qAbs(p1.x()-p2.x())/2.0;
22611  double b = qAbs(p1.y()-p2.y())/2.0;
22612  double x = pos.x()-center.x();
22613  double y = pos.y()-center.y();
22614 
22615  // distance to border:
22616  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
22617  result = qAbs(c-1)*qSqrt(x*x+y*y);
22618  // filled ellipse, allow click inside to count as hit:
22619  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
22620  {
22621  if (x*x/(a*a) + y*y/(b*b) <= 1)
22622  result = mParentPlot->selectionTolerance()*0.99;
22623  }
22624  return result;
22625 }
22626 
22627 /* inherits documentation from base class */
22629 {
22630  QPointF p1 = topLeft->pixelPoint();
22631  QPointF p2 = bottomRight->pixelPoint();
22632  if (p1.toPoint() == p2.toPoint())
22633  return;
22634  QRectF ellipseRect = QRectF(p1, p2).normalized();
22635  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
22636  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
22637  {
22638  painter->setPen(mainPen());
22639  painter->setBrush(mainBrush());
22640 #ifdef __EXCEPTIONS
22641  try // drawEllipse sometimes throws exceptions if ellipse is too big
22642  {
22643 #endif
22644  painter->drawEllipse(ellipseRect);
22645 #ifdef __EXCEPTIONS
22646  } catch (...)
22647  {
22648  qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
22649  setVisible(false);
22650  }
22651 #endif
22652  }
22653 }
22654 
22655 /* inherits documentation from base class */
22656 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
22657 {
22658  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22659  switch (anchorId)
22660  {
22661  case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
22662  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22663  case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
22664  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22665  case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
22666  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22667  case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
22668  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22669  case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
22670  }
22671 
22672  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22673  return QPointF();
22674 }
22675 
22682 {
22683  return mSelected ? mSelectedPen : mPen;
22684 }
22685 
22692 {
22693  return mSelected ? mSelectedBrush : mBrush;
22694 }
22695 
22696 
22700 
22721  QCPAbstractItem(parentPlot),
22722  topLeft(createPosition(QLatin1String("topLeft"))),
22723  bottomRight(createPosition(QLatin1String("bottomRight"))),
22724  top(createAnchor(QLatin1String("top"), aiTop)),
22725  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22726  right(createAnchor(QLatin1String("right"), aiRight)),
22727  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22728  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22729  left(createAnchor(QLatin1String("left"), aiLeft))
22730 {
22731  topLeft->setCoords(0, 1);
22732  bottomRight->setCoords(1, 0);
22733 
22734  setPen(Qt::NoPen);
22735  setSelectedPen(QPen(Qt::blue));
22736  setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
22737 }
22738 
22740 {
22741 }
22742 
22746 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
22747 {
22748  mPixmap = pixmap;
22749  if (mPixmap.isNull())
22750  qDebug() << Q_FUNC_INFO << "pixmap is null";
22751 }
22752 
22757 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
22758 {
22759  mScaled = scaled;
22763 }
22764 
22770 void QCPItemPixmap::setPen(const QPen &pen)
22771 {
22772  mPen = pen;
22773 }
22774 
22781 {
22782  mSelectedPen = pen;
22783 }
22784 
22785 /* inherits documentation from base class */
22786 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22787 {
22788  Q_UNUSED(details)
22789  if (onlySelectable && !mSelectable)
22790  return -1;
22791 
22792  return rectSelectTest(getFinalRect(), pos, true);
22793 }
22794 
22795 /* inherits documentation from base class */
22797 {
22798  bool flipHorz = false;
22799  bool flipVert = false;
22800  QRect rect = getFinalRect(&flipHorz, &flipVert);
22801  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
22802  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22803  if (boundingRect.intersects(clipRect()))
22804  {
22805  updateScaledPixmap(rect, flipHorz, flipVert);
22806  painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
22807  QPen pen = mainPen();
22808  if (pen.style() != Qt::NoPen)
22809  {
22810  painter->setPen(pen);
22811  painter->setBrush(Qt::NoBrush);
22812  painter->drawRect(rect);
22813  }
22814  }
22815 }
22816 
22817 /* inherits documentation from base class */
22818 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
22819 {
22820  bool flipHorz;
22821  bool flipVert;
22822  QRect rect = getFinalRect(&flipHorz, &flipVert);
22823  // we actually want denormal rects (negative width/height) here, so restore
22824  // the flipped state:
22825  if (flipHorz)
22826  rect.adjust(rect.width(), 0, -rect.width(), 0);
22827  if (flipVert)
22828  rect.adjust(0, rect.height(), 0, -rect.height());
22829 
22830  switch (anchorId)
22831  {
22832  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22833  case aiTopRight: return rect.topRight();
22834  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22835  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22836  case aiBottomLeft: return rect.bottomLeft();
22837  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
22838  }
22839 
22840  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22841  return QPointF();
22842 }
22843 
22857 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
22858 {
22859  if (mPixmap.isNull())
22860  return;
22861 
22862  if (mScaled)
22863  {
22864  if (finalRect.isNull())
22865  finalRect = getFinalRect(&flipHorz, &flipVert);
22866  if (finalRect.size() != mScaledPixmap.size())
22867  {
22868  mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, mTransformationMode);
22869  if (flipHorz || flipVert)
22870  mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
22871  }
22872  } else if (!mScaledPixmap.isNull())
22873  mScaledPixmap = QPixmap();
22874 }
22875 
22890 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
22891 {
22892  QRect result;
22893  bool flipHorz = false;
22894  bool flipVert = false;
22895  QPoint p1 = topLeft->pixelPoint().toPoint();
22896  QPoint p2 = bottomRight->pixelPoint().toPoint();
22897  if (p1 == p2)
22898  return QRect(p1, QSize(0, 0));
22899  if (mScaled)
22900  {
22901  QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
22902  QPoint topLeft = p1;
22903  if (newSize.width() < 0)
22904  {
22905  flipHorz = true;
22906  newSize.rwidth() *= -1;
22907  topLeft.setX(p2.x());
22908  }
22909  if (newSize.height() < 0)
22910  {
22911  flipVert = true;
22912  newSize.rheight() *= -1;
22913  topLeft.setY(p2.y());
22914  }
22915  QSize scaledSize = mPixmap.size();
22916  scaledSize.scale(newSize, mAspectRatioMode);
22917  result = QRect(topLeft, scaledSize);
22918  } else
22919  {
22920  result = QRect(p1, mPixmap.size());
22921  }
22922  if (flippedHorz)
22923  *flippedHorz = flipHorz;
22924  if (flippedVert)
22925  *flippedVert = flipVert;
22926  return result;
22927 }
22928 
22935 {
22936  return mSelected ? mSelectedPen : mPen;
22937 }
22938 
22939 
22943 
22980  QCPAbstractItem(parentPlot),
22981  position(createPosition(QLatin1String("position"))),
22982  mGraph(0)
22983 {
22984  position->setCoords(0, 0);
22985 
22986  setBrush(Qt::NoBrush);
22987  setSelectedBrush(Qt::NoBrush);
22988  setPen(QPen(Qt::black));
22989  setSelectedPen(QPen(Qt::blue, 2));
22991  setSize(6);
22992  setInterpolating(false);
22993  setGraphKey(0);
22994 }
22995 
22997 {
22998 }
22999 
23005 void QCPItemTracer::setPen(const QPen &pen)
23006 {
23007  mPen = pen;
23008 }
23009 
23016 {
23017  mSelectedPen = pen;
23018 }
23019 
23025 void QCPItemTracer::setBrush(const QBrush &brush)
23026 {
23027  mBrush = brush;
23028 }
23029 
23036 {
23038 }
23039 
23045 {
23046  mSize = size;
23047 }
23048 
23056 {
23057  mStyle = style;
23058 }
23059 
23071 {
23072  if (graph)
23073  {
23074  if (graph->parentPlot() == mParentPlot)
23075  {
23077  position->setAxes(graph->keyAxis(), graph->valueAxis());
23078  mGraph = graph;
23079  updatePosition();
23080  } else
23081  qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
23082  } else
23083  {
23084  mGraph = 0;
23085  }
23086 }
23087 
23098 {
23099  mGraphKey = key;
23100 }
23101 
23114 {
23115  mInterpolating = enabled;
23116 }
23117 
23118 /* inherits documentation from base class */
23119 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23120 {
23121  Q_UNUSED(details)
23122  if (onlySelectable && !mSelectable)
23123  return -1;
23124 
23125  QPointF center(position->pixelPoint());
23126  double w = mSize/2.0;
23127  QRect clip = clipRect();
23128  switch (mStyle)
23129  {
23130  case tsNone: return -1;
23131  case tsPlus:
23132  {
23133  if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23134  return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
23135  distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
23136  break;
23137  }
23138  case tsCrosshair:
23139  {
23140  return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
23141  distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
23142  }
23143  case tsCircle:
23144  {
23145  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23146  {
23147  // distance to border:
23148  double centerDist = QVector2D(center-pos).length();
23149  double circleLine = w;
23150  double result = qAbs(centerDist-circleLine);
23151  // filled ellipse, allow click inside to count as hit:
23152  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
23153  {
23154  if (centerDist <= circleLine)
23155  result = mParentPlot->selectionTolerance()*0.99;
23156  }
23157  return result;
23158  }
23159  break;
23160  }
23161  case tsSquare:
23162  {
23163  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23164  {
23165  QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
23166  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
23167  return rectSelectTest(rect, pos, filledRect);
23168  }
23169  break;
23170  }
23171  }
23172  return -1;
23173 }
23174 
23175 /* inherits documentation from base class */
23177 {
23178  updatePosition();
23179  if (mStyle == tsNone)
23180  return;
23181 
23182  painter->setPen(mainPen());
23183  painter->setBrush(mainBrush());
23184  QPointF center(position->pixelPoint());
23185  double w = mSize/2.0;
23186  QRect clip = clipRect();
23187  switch (mStyle)
23188  {
23189  case tsNone: return;
23190  case tsPlus:
23191  {
23192  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23193  {
23194  painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
23195  painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
23196  }
23197  break;
23198  }
23199  case tsCrosshair:
23200  {
23201  if (center.y() > clip.top() && center.y() < clip.bottom())
23202  painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
23203  if (center.x() > clip.left() && center.x() < clip.right())
23204  painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
23205  break;
23206  }
23207  case tsCircle:
23208  {
23209  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23210  painter->drawEllipse(center, w, w);
23211  break;
23212  }
23213  case tsSquare:
23214  {
23215  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23216  painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
23217  break;
23218  }
23219  }
23220 }
23221 
23235 {
23236  if (mGraph)
23237  {
23239  {
23240  if (mGraph->data()->size() > 1)
23241  {
23242  QCPDataMap::const_iterator first = mGraph->data()->constBegin();
23243  QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
23244  if (mGraphKey < first.key())
23245  position->setCoords(first.key(), first.value().value);
23246  else if (mGraphKey > last.key())
23247  position->setCoords(last.key(), last.value().value);
23248  else
23249  {
23250  QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
23251  if (it != first) // mGraphKey is somewhere between iterators
23252  {
23253  QCPDataMap::const_iterator prevIt = it-1;
23254  if (mInterpolating)
23255  {
23256  // interpolate between iterators around mGraphKey:
23257  double slope = 0;
23258  if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
23259  slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
23260  position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
23261  } else
23262  {
23263  // find iterator with key closest to mGraphKey:
23264  if (mGraphKey < (prevIt.key()+it.key())*0.5)
23265  it = prevIt;
23266  position->setCoords(it.key(), it.value().value);
23267  }
23268  } else // mGraphKey is exactly on first iterator
23269  position->setCoords(it.key(), it.value().value);
23270  }
23271  } else if (mGraph->data()->size() == 1)
23272  {
23273  QCPDataMap::const_iterator it = mGraph->data()->constBegin();
23274  position->setCoords(it.key(), it.value().value);
23275  } else
23276  qDebug() << Q_FUNC_INFO << "graph has no data";
23277  } else
23278  qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
23279  }
23280 }
23281 
23288 {
23289  return mSelected ? mSelectedPen : mPen;
23290 }
23291 
23298 {
23299  return mSelected ? mSelectedBrush : mBrush;
23300 }
23301 
23302 
23306 
23333  QCPAbstractItem(parentPlot),
23334  left(createPosition(QLatin1String("left"))),
23335  right(createPosition(QLatin1String("right"))),
23336  center(createAnchor(QLatin1String("center"), aiCenter))
23337 {
23338  left->setCoords(0, 0);
23339  right->setCoords(1, 1);
23340 
23341  setPen(QPen(Qt::black));
23342  setSelectedPen(QPen(Qt::blue, 2));
23343  setLength(8);
23345 }
23346 
23348 {
23349 }
23350 
23360 void QCPItemBracket::setPen(const QPen &pen)
23361 {
23362  mPen = pen;
23363 }
23364 
23371 {
23372  mSelectedPen = pen;
23373 }
23374 
23384 {
23385  mLength = length;
23386 }
23387 
23394 {
23395  mStyle = style;
23396 }
23397 
23398 /* inherits documentation from base class */
23399 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23400 {
23401  Q_UNUSED(details)
23402  if (onlySelectable && !mSelectable)
23403  return -1;
23404 
23405  QVector2D leftVec(left->pixelPoint());
23406  QVector2D rightVec(right->pixelPoint());
23407  if (leftVec.toPoint() == rightVec.toPoint())
23408  return -1;
23409 
23410  QVector2D widthVec = (rightVec-leftVec)*0.5f;
23411  QVector2D lengthVec(-widthVec.y(), widthVec.x());
23412  lengthVec = lengthVec.normalized()*mLength;
23413  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23414 
23415  switch (mStyle)
23416  {
23419  {
23420  double a = distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23421  double b = distSqrToLine((centerVec-widthVec+lengthVec).toPointF(), (centerVec-widthVec).toPointF(), pos);
23422  double c = distSqrToLine((centerVec+widthVec+lengthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23423  return qSqrt(qMin(qMin(a, b), c));
23424  }
23427  {
23428  double a = distSqrToLine((centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23429  double b = distSqrToLine((centerVec-widthVec+lengthVec*0.7f).toPointF(), (centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23430  double c = distSqrToLine((centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23431  double d = distSqrToLine((centerVec+widthVec+lengthVec*0.7f).toPointF(), (centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23432  return qSqrt(qMin(qMin(a, b), qMin(c, d)));
23433  }
23434  }
23435  return -1;
23436 }
23437 
23438 /* inherits documentation from base class */
23440 {
23441  QVector2D leftVec(left->pixelPoint());
23442  QVector2D rightVec(right->pixelPoint());
23443  if (leftVec.toPoint() == rightVec.toPoint())
23444  return;
23445 
23446  QVector2D widthVec = (rightVec-leftVec)*0.5f;
23447  QVector2D lengthVec(-widthVec.y(), widthVec.x());
23448  lengthVec = lengthVec.normalized()*mLength;
23449  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23450 
23451  QPolygon boundingPoly;
23452  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
23453  << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
23454  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
23455  if (clip.intersects(boundingPoly.boundingRect()))
23456  {
23457  painter->setPen(mainPen());
23458  switch (mStyle)
23459  {
23460  case bsSquare:
23461  {
23462  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
23463  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23464  painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23465  break;
23466  }
23467  case bsRound:
23468  {
23469  painter->setBrush(Qt::NoBrush);
23470  QPainterPath path;
23471  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23472  path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
23473  path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23474  painter->drawPath(path);
23475  break;
23476  }
23477  case bsCurly:
23478  {
23479  painter->setBrush(Qt::NoBrush);
23480  QPainterPath path;
23481  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23482  path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
23483  path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23484  painter->drawPath(path);
23485  break;
23486  }
23487  case bsCalligraphic:
23488  {
23489  painter->setPen(Qt::NoPen);
23490  painter->setBrush(QBrush(mainPen().color()));
23491  QPainterPath path;
23492  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23493 
23494  path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
23495  path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23496 
23497  path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
23498  path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23499 
23500  painter->drawPath(path);
23501  break;
23502  }
23503  }
23504  }
23505 }
23506 
23507 /* inherits documentation from base class */
23508 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
23509 {
23510  QVector2D leftVec(left->pixelPoint());
23511  QVector2D rightVec(right->pixelPoint());
23512  if (leftVec.toPoint() == rightVec.toPoint())
23513  return leftVec.toPointF();
23514 
23515  QVector2D widthVec = (rightVec-leftVec)*0.5f;
23516  QVector2D lengthVec(-widthVec.y(), widthVec.x());
23517  lengthVec = lengthVec.normalized()*mLength;
23518  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23519 
23520  switch (anchorId)
23521  {
23522  case aiCenter:
23523  return centerVec.toPointF();
23524  }
23525  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23526  return QPointF();
23527 }
23528 
23535 {
23536  return mSelected ? mSelectedPen : mPen;
23537 }
23538 
void setOutliers(const QVector< double > &values)
QList< QList< QCPLayoutElement * > > mElements
Definition: qcustomplot.h:809
virtual int elementCount() const
void drawShape(QCPPainter *painter, QPointF pos) const
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:2055
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:1123
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:3408
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:600
QList< QCPGraph * > mGraphs
Definition: qcustomplot.h:1850
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:3456
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:323
0xFFFF All elements
Definition: qcustomplot.h:129
QList< QCPLayoutElement * > elements(QCP::MarginSide side) const
Definition: qcustomplot.h:608
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:2050
A plus shaped crosshair which spans the complete axis rect.
Definition: qcustomplot.h:3655
static const double maxRange
Definition: qcustomplot.h:510
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:1712
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:172
The negative sign domain, i.e. numbers smaller than zero.
Definition: qcustomplot.h:1455
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:2408
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:103
virtual ~QCPAxisRect()
Open-High-Low-Close bar representation.
Definition: qcustomplot.h:3163
bool clipToAxisRect() const
Definition: qcustomplot.h:1621
virtual QRect clipRect() const
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
QCPItemPosition *const end
Definition: qcustomplot.h:3352
void updateAxesOffset(QCPAxis::AxisType type)
QPen mErrorPen
Definition: qcustomplot.h:2583
QCPColorMapData * mMapData
Definition: qcustomplot.h:3096
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
virtual void draw(QCPPainter *painter)
int plottableCount() const
QCPLayout * mParentLayout
Definition: qcustomplot.h:689
void setAutoTickLabels(bool on)
double cell(int keyIndex, int valueIndex)
QCPAxis * mParentAxis
Definition: qcustomplot.h:966
Q_SLOT void setSelected(bool selected)
AdjacencyIterator end(const Adjacency &adj, const Vector< D > &vct, size_t pixel_index)
Returns an iterator to position after the end of elements.
void setMinimumMargins(const QMargins &margins)
A filled square.
Definition: qcustomplot.h:893
QFont mLabelFont
Definition: qcustomplot.h:1244
QCPAxis(QCPAxisRect *parent, AxisType type)
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
PainterModes modes() const
Definition: qcustomplot.h:334
QCPRange dataRange() const
Definition: qcustomplot.h:2406
void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true)
bool mAutoTickStep
Definition: qcustomplot.h:1263
void setKeySize(int keySize)
QMap< double, QCPData > QCPDataMap
Definition: qcustomplot.h:2485
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual ~QCPColorScale()
void remove(QCPBars *bars)
void removeDataAfter(double key)
QPen mSubGridPen
Definition: qcustomplot.h:964
QCPGrid * mGrid
Definition: qcustomplot.h:1274
int padding() const
Definition: qcustomplot.h:1130
QCP::AntialiasedElements antialiasedElements() const
Definition: qcustomplot.h:1724
void setMaximumSize(const QSize &size)
Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the ...
Definition: qcustomplot.h:1910
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:2638
void mouseMove(QMouseEvent *event)
QCPRange keyRange() const
Definition: qcustomplot.h:3008
No line is drawn between data points (e.g. only scatters)
Definition: qcustomplot.h:2673
QCustomPlot(QWidget *parent=0)
virtual int calculateAutoSubTickCount(double tickStep) const
void setParentLayerable(QCPLayerable *parentLayerable)
QCPItemBracket(QCustomPlot *parentPlot)
QCPLineEnding tail() const
Definition: qcustomplot.h:3338
void rescaleKeyAxis(bool onlyEnlarge=false) const
bool mNumberBeautifulPowers
Definition: qcustomplot.h:1257
QCPItemAnchor *const center
Definition: qcustomplot.h:3551
A legend item representing a plottable with an icon and the plottable name.
Definition: qcustomplot.h:2145
QHash< QCP::MarginSide, QCPMarginGroup * > mMarginGroups
Definition: qcustomplot.h:694
Bar spacing is given by a fraction of the axis rect size.
Definition: qcustomplot.h:2755
bool antialiasing() const
Definition: qcustomplot.h:333
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:2515
QCPItemPosition *const start
Definition: qcustomplot.h:3349
void selectionChanged(bool selected)
int numberPrecision() const
Definition: qcustomplot.h:1114
virtual QList< QCPLayoutElement * > elements(bool recursive) const
void expandTo(int newRowCount, int newColumnCount)
double mErrorBarSize
Definition: qcustomplot.h:2587
SelectableParts selectedParts() const
QByteArray mLabelParameterHash
Definition: qcustomplot.h:1374
0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fix...
Definition: qcustomplot.h:321
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:3697
void setMedianPen(const QPen &pen)
QList< QCPAbstractPlottable * > selectedPlottables() const
QBrush mBackgroundBrush
Definition: qcustomplot.h:2044
void selectionChanged(bool selected)
double maximum() const
Definition: qcustomplot.h:2942
Q_SLOT bool setLayer(QCPLayer *layer)
QList< QCPGraph * > graphs() const
QCPAbstractPlottable * plottable()
Definition: qcustomplot.h:2152
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:1081
double realLength() const
bool scaled() const
Definition: qcustomplot.h:3587
virtual void mouseMoveEvent(QMouseEvent *event)
virtual ~QCPGraph()
QCPBarDataMap * data() const
Definition: qcustomplot.h:2857
QCPLineEnding lowerEnding
Definition: qcustomplot.h:1338
EndingStyle style() const
Definition: qcustomplot.h:904
Bar spacing is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:2756
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:1252
void setBasePen(const QPen &pen)
bool hasPlottable(QCPAbstractPlottable *plottable) const
virtual ~QCPItemText()
void setVisible(bool visible)
double key
Definition: qcustomplot.h:2472
The tracer is not visible.
Definition: qcustomplot.h:3653
virtual QPointF anchorPixelPoint(int anchorId) const
virtual void drawImpulsePlot(QCPPainter *painter, QVector< QPointF > *lineData) const
QString text() const
Definition: qcustomplot.h:3452
void setSelectedBorderPen(const QPen &pen)
int bottom() const
Definition: qcustomplot.h:2028
void setPixmap(const QPixmap &pixmap)
QColor getTickLabelColor() const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:3395
SelectableParts mSelectedParts
Definition: qcustomplot.h:1238
custom painter operations are performed per scatter (As QPainterPath, see setCustomPath) ...
Definition: qcustomplot.h:263
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
The abstract base class for all entries in a QCPLegend.
Definition: qcustomplot.h:2081
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
QFont mSelectedTickLabelFont
Definition: qcustomplot.h:1251
QColor mColor
Definition: qcustomplot.h:3490
void setHead(const QCPLineEnding &head)
bool remove(QCPLayoutElement *element)
{ssPeace.png} a circle, with one vertical and two downward diagonal lines
Definition: qcustomplot.h:261
QBrush mBrush
Definition: qcustomplot.h:3409
bool twoColored() const
Definition: qcustomplot.h:3175
Tick labels (numbers) of this axis (as a whole, not individually)
Definition: qcustomplot.h:1082
bool mAntialiasedZeroLine
Definition: qcustomplot.h:963
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:1234
static AxisType marginSideToAxisType(QCP::MarginSide side)
0x0001 Axis base line and tick marks
Definition: qcustomplot.h:118
QCPLineEnding mHead
Definition: qcustomplot.h:3310
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:2438
QPainterPath customPath() const
Definition: qcustomplot.h:280
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:252
void setRangeReversed(bool reversed)
QPointF upperFillBasePoint(double upperKey) const
void setSelectedBasePen(const QPen &pen)
0x0008 Legend box
Definition: qcustomplot.h:121
Responsible for drawing the grid of a QCPAxis.
Definition: qcustomplot.h:930
Qt::Alignment positionAlignment() const
Definition: qcustomplot.h:3453
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:155
QCPItemAnchor * parentAnchor() const
Definition: qcustomplot.h:1566
0x00 no margin
Definition: qcustomplot.h:105
QVector< QString > tickLabels
Definition: qcustomplot.h:1359
QImage mMapImage
Definition: qcustomplot.h:3102
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:449
void setTextAlignment(Qt::Alignment alignment)
bool visible() const
Definition: qcustomplot.h:423
const QCPRange range() const
Definition: qcustomplot.h:1096
virtual ~QCPItemLine()
QCPColorGradient mGradient
Definition: qcustomplot.h:2441
int itemCount() const
0x04 top margin
Definition: qcustomplot.h:102
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:3062
double size() const
Definition: qcustomplot.h:275
bool noAntialiasingOnDrag() const
Definition: qcustomplot.h:1729
QRect axisSelectionBox() const
Definition: qcustomplot.h:1331
bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1)
double mLength
Definition: qcustomplot.h:924
virtual void wheelEvent(QWheelEvent *event)
void setCustomPath(const QPainterPath &customPath)
virtual ~QCPColorMap()
Linear scaling.
Definition: qcustomplot.h:1072
QColor color() const
Definition: qcustomplot.h:3444
void removeDataBefore(double key)
QList< QCPItemPosition * > mPositions
Definition: qcustomplot.h:1650
void setShape(ScatterShape shape)
QCP::PlottingHints plottingHints() const
Definition: qcustomplot.h:1730
QCustomPlot * mParentPlot
Definition: qcustomplot.h:1518
QColor mSelectedTextColor
Definition: qcustomplot.h:2340
void makeNonCosmetic()
QCPLayer * layer() const
Definition: qcustomplot.h:426
void setBackgroundScaled(bool scaled)
Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white.
Definition: qcustomplot.h:1925
double value
Definition: qcustomplot.h:2472
void setTickLabels(bool show)
An anchor of an item to which positions can be attached to.
Definition: qcustomplot.h:1503
void setSelectedPen(const QPen &pen)
Qt::Orientations mRangeZoom
Definition: qcustomplot.h:2050
void setBrush(const QBrush &brush)
virtual int calculateAutoMargin(QCP::MarginSide side)
int mSelectionTolerance
Definition: qcustomplot.h:1855
QBrush mainBrush() const
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
QCPAxis * keyAxis() const
Definition: qcustomplot.h:1572
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:647
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:1549
void setAntialiasedScatters(bool enabled)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
double width() const
Definition: qcustomplot.h:2944
void setRangeZoom(Qt::Orientations orientations)
void setLowerQuartile(double value)
QCPItemRect(QCustomPlot *parentPlot)
QCPItemAnchor * mParentAnchorX
Definition: qcustomplot.h:1596
0x0004 Sub grid lines
Definition: qcustomplot.h:120
QCache< QString, CachedLabel > mLabelCache
Definition: qcustomplot.h:1375
void mouseRelease(QMouseEvent *event)
void setSelectedTextColor(const QColor &color)
A brace with angled edges.
Definition: qcustomplot.h:3723
void setSelectedPen(const QPen &pen)
QCPItemPosition *const right
Definition: qcustomplot.h:3748
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:1357
void setSubTickCount(int count)
void rangeChanged(const QCPRange &newRange)
void setAutoTicks(bool on)
A plus shaped crosshair with limited size.
Definition: qcustomplot.h:3654
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:3212
Q_SLOT void setSelectableParts(const SelectableParts &selectableParts)
QCPLineEnding mTail
Definition: qcustomplot.h:3357
QCPAxis * addAxis(QCPAxis::AxisType type, QCPAxis *axis=0)
double tickLabelRotation() const
0x0002 Grid lines
Definition: qcustomplot.h:119
QColor mainColor() const
QCPLineEnding head() const
Definition: qcustomplot.h:3337
0x01 left margin
Definition: qcustomplot.h:100
static bool validRange(double lower, double upper)
bool mAutoTickLabels
Definition: qcustomplot.h:1248
Base class for all drawable objects.
Definition: qcustomplot.h:408
QBrush mBrushNegative
Definition: qcustomplot.h:3216
virtual void draw(QCPPainter *painter)
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:1858
void setAutoSubTicks(bool on)
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:2311
void setPadding(int padding)
QPen mainPen() const
QCPAxis * xAxis
Definition: qcustomplot.h:1819
A plottable representing a bar chart in a plot.
Definition: qcustomplot.h:2823
void rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
const QCP::Interactions interactions() const
Definition: qcustomplot.h:1727
QPen subTickPen() const
Definition: qcustomplot.h:1125
QFont font() const
Definition: qcustomplot.h:3450
bool removeGraph(QCPGraph *graph)
QVector< double > mTickVector
Definition: qcustomplot.h:1277
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:162
QList< QCPAbstractPlottable * > plottables() const
void setCoords(double key, double value)
void addChild(QCP::MarginSide side, QCPLayoutElement *element)
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:2783
0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap) ...
Definition: qcustomplot.h:125
friend class QCPItemAnchor
Definition: qcustomplot.h:1676
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:1253
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:1266
void recalculateDataBounds()
int valueSize() const
Definition: qcustomplot.h:3007
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void getStepCenterPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QPoint mDragStart
Definition: qcustomplot.h:2056
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:1276
bool addElement(int row, int column, QCPLayoutElement *element)
QPen pen() const
Definition: qcustomplot.h:3335
void setStyle(EndingStyle style)
QPointer< QCPBars > mBarBelow
Definition: qcustomplot.h:2890
QPixmap mScaledPixmap
Definition: qcustomplot.h:3616
void setValueSize(int valueSize)
virtual ~QCPLayoutElement()
QBrush mBrush
Definition: qcustomplot.h:3492
QList< QCPLayerable * > children() const
Definition: qcustomplot.h:383
void setTickLabelRotation(double degrees)
double spacing() const
Definition: qcustomplot.h:2763
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:2709
void setTypeX(PositionType type)
virtual ~QCPItemTracer()
TracerStyle mStyle
Definition: qcustomplot.h:3699
0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixma...
Definition: qcustomplot.h:322
QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual void deselectEvent(bool *selectionStateChanged)
A layout that arranges child elements in a grid.
Definition: qcustomplot.h:757
void setPen(const QPen &pen)
virtual void mousePressEvent(QMouseEvent *event)
virtual ~QCPBars()
void setFont(const QFont &font)
int levelCount() const
Definition: qcustomplot.h:1938
void setColor(const QColor &color)
void selectionChangedByUser()
QMargins margins() const
Definition: qcustomplot.h:659
0x08 Axis is horizontal and on the bottom side of the axis rect
Definition: qcustomplot.h:1045
QPen mBasePen
Definition: qcustomplot.h:1239
bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0)
QPointer< QCPBars > mBarAbove
Definition: qcustomplot.h:2890
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPScatterStyle mOutlierStyle
Definition: qcustomplot.h:2978
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:1056
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:2784
virtual QCP::Interaction selectionCategory() const
QCPItemText(QCustomPlot *parentPlot)
double valueErrorPlus
Definition: qcustomplot.h:2474
QList< QCPLayer * > mLayers
Definition: qcustomplot.h:1852
QBrush mainBrush() const
void titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
void setRangeDrag(Qt::Orientations orientations)
QCP::AntialiasedElements mNotAntialiasedElements
Definition: qcustomplot.h:1853
QCPItemAnchor *const bottom
Definition: qcustomplot.h:3482
0x0040 Main lines of items
Definition: qcustomplot.h:124
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
void removeChild(QCPLayerable *layerable)
QCPAxisRect * axisRect() const
virtual QCPLayoutElement * takeAt(int index)
QImage mUndersampledMapImage
Definition: qcustomplot.h:3102
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:246
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:1570
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:1388
QCP::MarginSides mAutoMargins
Definition: qcustomplot.h:693
void setColorScale(QCPColorScale *colorScale)
void fill(double z)
EndingStyle mStyle
Definition: qcustomplot.h:923
QCPColorGradient inverted() const
Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion ...
Definition: qcustomplot.h:1928
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:1571
void setAutoTickCount(int approximateCount)
void releaseElement(QCPLayoutElement *el)
void removeChildX(QCPItemPosition *pos)
QCPItemAnchor *const left
Definition: qcustomplot.h:3550
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:1465
void setSubTickLengthOut(int outside)
bool removeAt(int index)
double mGraphKey
Definition: qcustomplot.h:3701
virtual void clearData()
void setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
static size_t size
Definition: enough.c:173
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
QList< QCPBars * > bars() const
Definition: qcustomplot.h:2770
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:2446
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable) ...
Definition: qcustomplot.h:158
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:892
virtual ~QCPItemStraightLine()
Tick labels will be displayed outside the axis rect.
Definition: qcustomplot.h:1065
virtual void draw(QCPPainter *painter)
QCPItemAnchor * parentAnchorY() const
Definition: qcustomplot.h:1568
Continuous lightness from black over icey colors to white (suited for non-biased data representation)...
Definition: qcustomplot.h:1920
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:2512
double valueErrorMinus
Definition: qcustomplot.h:2474
void selectionChanged(const QCPAxis::SelectableParts &parts)
virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const
QCPItemPosition *const endDir
Definition: qcustomplot.h:3351
QPen getBasePen() const
double minimum() const
Definition: qcustomplot.h:2938
void setErrorBarSize(double size)
0x02 Axis is vertical and on the right side of the axis rect
Definition: qcustomplot.h:1043
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:1927
void setBackgroundScaled(bool scaled)
void setTickLabelFont(const QFont &font)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:3542
QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange)
double width() const
Definition: qcustomplot.h:905
bool mInterpolate
Definition: qcustomplot.h:3098
int subTickLengthIn() const
QCPColorGradient mGradient
Definition: qcustomplot.h:3097
void setTextColor(const QColor &color)
virtual ~QCPItemEllipse()
Bar width is in absolute pixels.
Definition: qcustomplot.h:2841
int tickLengthOut() const
void getScatterPlotData(QVector< QCPData > *scatterData) const
Qt::AspectRatioMode aspectRatioMode() const
Definition: qcustomplot.h:3588
int mHighestVisibleTick
Definition: qcustomplot.h:1276
QList< QCPAxis * > axes() const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:391
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:3068
A plottable representing a graph in a plot.
Definition: qcustomplot.h:2490
void applyFillAntialiasingHint(QCPPainter *painter) const
QCPItemPosition *const start
Definition: qcustomplot.h:3304
int iconTextPadding() const
Definition: qcustomplot.h:2210
The abstract base class for all items in a plot.
Definition: qcustomplot.h:1607
QCPAbstractLegendItem(QCPLegend *parent)
QString name() const
Definition: qcustomplot.h:381
QPen mainPen() const
QCPLayoutInset * mInsetLayout
Definition: qcustomplot.h:2049
void setData(double key, double value, double z)
double length() const
Definition: qcustomplot.h:3735
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:144
QRect tickLabelsSelectionBox() const
Definition: qcustomplot.h:1332
void setPadding(const QMargins &padding)
Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power...
Definition: qcustomplot.h:1073
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPCurveDataMap * mData
Definition: qcustomplot.h:2708
Q_SLOT void setSelectable(bool selectable)
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
QCPLegend * mParentLegend
Definition: qcustomplot.h:2122
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:1465
QBrush brush() const
Definition: qcustomplot.h:3667
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:3357
QFont mSelectedLabelFont
Definition: qcustomplot.h:1244
QCPColorScale(QCustomPlot *parentPlot)
QPointer< QCPAxis > mRangeZoomHorzAxis
Definition: qcustomplot.h:2051
QCPItemAnchor *const right
Definition: qcustomplot.h:3399
bool mNoAntialiasingOnDrag
Definition: qcustomplot.h:1856
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:1680
virtual void wheelEvent(QWheelEvent *event)
void setMedian(double value)
void setAntialiasing(bool enabled)
void setPen(const QPen &pen)
virtual QSize minimumSizeHint() const
0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see QCPAbstractItem) ...
Definition: qcustomplot.h:161
virtual void draw(QCPPainter *painter)
Candlestick representation.
Definition: qcustomplot.h:3164
void setMinimum(double value)
void setWidth(double width)
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
bool selectable() const
Definition: qcustomplot.h:1623
void setBrushNegative(const QBrush &brush)
Manages a single axis inside a QCustomPlot.
Definition: qcustomplot.h:980
QCPLineEnding tail() const
Definition: qcustomplot.h:3293
QCPAxis::AxisType type() const
Definition: qcustomplot.h:2405
double upperQuartile() const
Definition: qcustomplot.h:2941
QCPBarsGroup * barsGroup() const
Definition: qcustomplot.h:2853
Q_SLOT void setSelectable(bool selectable)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
virtual ~QCPLegend()
double mScaleLogBase
Definition: qcustomplot.h:1271
double lowerQuartile() const
Definition: qcustomplot.h:2939
void drawLine(const QLineF &line)
QList< QCPAbstractItem * > selectedItems() const
QBrush brush() const
Definition: qcustomplot.h:278
Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allo...
Definition: qcustomplot.h:1924
void draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
double width() const
Definition: qcustomplot.h:2851
bool mIsAntialiasing
Definition: qcustomplot.h:357
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:2368
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:3290
A filled arrow head with a straight/flat back (a triangle)
Definition: qcustomplot.h:889
void getImpulsePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
PositionType type() const
Definition: qcustomplot.h:1563
int findIndexAboveX(const QVector< QPointF > *data, double x) const
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:234
{ssCircle.png} a circle
Definition: qcustomplot.h:250
int index() const
Definition: qcustomplot.h:382
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:2169
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QList< QCPLayerable * > mChildren
Definition: qcustomplot.h:394
QSet< QCPItemPosition * > mChildrenY
Definition: qcustomplot.h:1521
void insert(int i, QCPBars *bars)
QCPLineEnding lowerEnding() const
void setCell(int keyIndex, int valueIndex, double z)
QCPRange mDataRange
Definition: qcustomplot.h:3094
bool addItem(QCPAbstractItem *item)
PositionType mPositionTypeY
Definition: qcustomplot.h:1592
QCPLayerable * parentLayerable() const
Definition: qcustomplot.h:425
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:1263
QPointer< QCPColorScaleAxisRectPrivate > mAxisRect
Definition: qcustomplot.h:2445
void setInsetRect(int index, const QRectF &rect)
line is drawn as steps where the step is in between two data points
Definition: qcustomplot.h:2514
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:2048
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:127
0x004 The user can select multiple objects by holding the modifier set by QCustomPlot::setMultiSelect...
Definition: qcustomplot.h:157
QCPAbstractPlottable * plottable()
void removeData(double fromKey, double toKey)
QString label() const
QCPItemAnchor *const bottomRight
Definition: qcustomplot.h:3481
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
virtual ~QCPItemPixmap()
void setSpacing(double spacing)
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:1593
Bar width is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:2843
void addData(const QCPCurveDataMap &dataMap)
void setRowSpacing(int pixels)
QCPRange valueRange() const
Definition: qcustomplot.h:3009
QCPItemPosition * createPosition(const QString &name)
QMargins mMargins
Definition: qcustomplot.h:692
void setMinimumSize(const QSize &size)
QCPRange mDragStartVertRange
Definition: qcustomplot.h:2054
QVector< double > mSubTickVector
Definition: qcustomplot.h:1279
void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
QPen tickPen() const
Definition: qcustomplot.h:1124
QList< QCPBars * > mBars
Definition: qcustomplot.h:2785
int itemCount() const
QPen getSubTickPen() const
bool isEmpty() const
Definition: qcustomplot.h:3028
QPen pen() const
Definition: qcustomplot.h:3446
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:3174
int height() const
Definition: qcustomplot.h:2030
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
AdjacencyIterator begin(const Adjacency &adj, const Vector< D > &vct, size_t pixel_index)
returns an iterator to first valid adjacent element.
A layer that may contain objects, to control the rendering order.
Definition: qcustomplot.h:365
bool mTickLabels
Definition: qcustomplot.h:1248
Qt::Orientation orientation() const
Definition: qcustomplot.h:1206
Continuous lightness from black over firey colors to white (suited for non-biased data representation...
Definition: qcustomplot.h:1919
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void setPen(const QPen &pen)
Definition: qcustomplot.cpp:86
QCPItemPosition *const position
Definition: qcustomplot.h:3692
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:320
void setTickVector(const QVector< double > &vec)
LineStyle mLineStyle
Definition: qcustomplot.h:2710
QFont getLabelFont() const
Error bars for both key and value dimensions of the data point are shown.
Definition: qcustomplot.h:2524
SpacingType spacingType() const
Definition: qcustomplot.h:2762
The abstract base class for all objects that form the layout system.
Definition: qcustomplot.h:629
QCPBarsGroup(QCustomPlot *parentPlot)
PlottingHint
Definition: qcustomplot.h:139
QColor getLabelColor() const
void setGraph(QCPGraph *graph)
void mousePress(QMouseEvent *event)
QCPRange mValueRange
Definition: qcustomplot.h:3035
Continuous lightness from black to white (suited for non-biased data representation) ...
Definition: qcustomplot.h:1918
virtual void update(UpdatePhase phase)
void setFont(const QFont &font)
{ssPlus.png} a plus
Definition: qcustomplot.h:249
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:1417
QBrush brush() const
Definition: qcustomplot.h:3448
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:1262
QCPColorGradient gradient() const
Definition: qcustomplot.h:3067
int keySize() const
Definition: qcustomplot.h:3006
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:1922
QCPLineEnding upperEnding
Definition: qcustomplot.h:1338
void selectableChanged(bool selectable)
QString mLabel
Definition: qcustomplot.h:1243
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:3618
QCPItemStraightLine(QCustomPlot *parentPlot)
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
bool selected() const
Definition: qcustomplot.h:1420
QColor mTextColor
Definition: qcustomplot.h:2338
QBrush mSelectedBrush
Definition: qcustomplot.h:3492
QPen selectedIconBorderPen() const
Definition: qcustomplot.h:2215
virtual void draw(QCPPainter *painter)=0
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPItemAnchor * parentAnchorX() const
Definition: qcustomplot.h:1567
double mRotation
Definition: qcustomplot.h:3497
int findIndexBelowY(const QVector< QPointF > *data, double y) const
QCPLayer * mCurrentLayer
Definition: qcustomplot.h:1862
virtual QList< QCPLayoutElement * > elements(bool recursive) const
virtual ~QCPAbstractItem()
QPainterPath mCustomPath
Definition: qcustomplot.h:304
WidthType mWidthType
Definition: qcustomplot.h:2887
virtual bool removeFromLegend() const
QCPBarsGroup * mBarsGroup
Definition: qcustomplot.h:2888
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
QMap< double, QCPBarData > QCPBarDataMap
Definition: qcustomplot.h:2818
QCPItemAnchor *const left
Definition: qcustomplot.h:3484
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:1870
bool operator==(const QCPColorGradient &other) const
QList< QCPAxis * > addAxes(QCPAxis::AxisTypes types)
QPen mSelectedTickPen
Definition: qcustomplot.h:1265
bool autoSubTicks() const
Definition: qcustomplot.h:1102
{ssCross.png} a cross
Definition: qcustomplot.h:248
void setWidthType(WidthType widthType)
PainterModes mModes
Definition: qcustomplot.h:356
virtual QPointF pixelPoint() const
QPen mainPen() const
A brace with round edges.
Definition: qcustomplot.h:3724
A layout that places child elements aligned to the border or arbitrarily positioned.
Definition: qcustomplot.h:823
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:160
void setVisible(bool on)
virtual void drawMedian(QCPPainter *painter) const
void setStyle(TracerStyle style)
void setNoAntialiasingOnDrag(bool enabled)
double scaleLogBase() const
Definition: qcustomplot.h:1095
Handles the different ending decorations for line-like items.
Definition: qcustomplot.h:872
A color scale for use with color coding data such as QCPColorMap.
Definition: qcustomplot.h:2386
QCPDataMap * data() const
Definition: qcustomplot.h:2532
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:3527
QPen mTickPen
Definition: qcustomplot.h:1265
QCP::PlottingHints mPlottingHints
Definition: qcustomplot.h:1863
BracketStyle mStyle
Definition: qcustomplot.h:3756
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:2467
QCPAxis * valueAxis() const
Definition: qcustomplot.h:1573
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:2522
virtual QSize minimumSizeHint() const
int mCachedMargin
Definition: qcustomplot.h:1281
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual QCPItemPosition * toQCPItemPosition()
Definition: qcustomplot.h:1524
QPointer< QCPAxisRect > mClipAxisRect
Definition: qcustomplot.h:1649
void applyDefaultAntialiasingHint(QCPPainter *painter) const
QVector< QString > mTickVectorLabels
Definition: qcustomplot.h:1278
int getMarginValue(const QMargins &margins, QCP::MarginSide side)
Definition: qcustomplot.h:213
The positive sign domain, i.e. numbers greater than zero.
Definition: qcustomplot.h:1457
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:1849
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:380
friend class QCPAxisRect
Definition: qcustomplot.h:1896
void applyTo(QCPPainter *painter, const QPen &defaultPen) const
QPen pen() const
Definition: qcustomplot.h:3733
QPen pen() const
Definition: qcustomplot.h:1413
QPainter subclass used internally.
Definition: qcustomplot.h:312
void append(QCPBars *bars)
Qt::TransformationMode transformationMode() const
Definition: qcustomplot.h:3589
void setTickLength(int inside, int outside=0)
QCPCurveDataMap * data() const
Definition: qcustomplot.h:2680
void setAntialiasedErrorBars(bool enabled)
ScaleType mScaleType
Definition: qcustomplot.h:1270
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
bool selectable() const
Definition: qcustomplot.h:2102
Q_SLOT void setSelected(bool selected)
QPixmap mLegendIcon
Definition: qcustomplot.h:3103
virtual QPointF anchorPixelPoint(int anchorId) const
QSet< QCPItemPosition * > mChildrenX
Definition: qcustomplot.h:1521
Qt::TimeSpec mDateTimeSpec
Definition: qcustomplot.h:1254
WidthType widthType() const
Definition: qcustomplot.h:2852
QCPItemEllipse(QCustomPlot *parentPlot)
{ssCrossCircle.png} a circle with a cross inside
Definition: qcustomplot.h:259
void setPlottingHints(const QCP::PlottingHints &hints)
bool mAutoTicks
Definition: qcustomplot.h:1263
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:2025
bool mCachedMarginValid
Definition: qcustomplot.h:1280
bool selected() const
Definition: qcustomplot.h:2316
double boundingDistance() const
QCPItemAnchor *const bottom
Definition: qcustomplot.h:3548
QPixmap mPixmap
Definition: qcustomplot.h:3615
QCPGrid(QCPAxis *parentAxis)
virtual bool take(QCPLayoutElement *element)
Q_SLOT void setSelected(bool selected)
void setColorInterpolation(ColorInterpolation interpolation)
QColor mSelectedColor
Definition: qcustomplot.h:3490
QPen mSelectedBasePen
Definition: qcustomplot.h:1239
QCPLineEnding head() const
Definition: qcustomplot.h:3292
double median() const
Definition: qcustomplot.h:2940
{ssDot.png} a single pixel (use ssDisc or ssCircle if you want a round shape with a certain radius) ...
Definition: qcustomplot.h:247
void normalize()
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lineData) const
0x0100 Error bars
Definition: qcustomplot.h:126
QPen mainPen() const
double pointDistance(const QPointF &pixelPoint) const
int mPadding
Definition: qcustomplot.h:1236
void setChartStyle(ChartStyle style)
void setBarsGroup(QCPBarsGroup *barsGroup)
virtual void mouseMoveEvent(QMouseEvent *event)
QCPAxis * yAxis2
Definition: qcustomplot.h:1819
void setDateTimeSpec(const Qt::TimeSpec &timeSpec)
QVector< QRgb > mColorBuffer
Definition: qcustomplot.h:1967
void registerBars(QCPBars *bars)
QCPAxisRect * axisRect() const
Definition: qcustomplot.h:1093
QCPAbstractItem * mParentItem
Definition: qcustomplot.h:1519
{ssPlusSquare.png} a square with a plus inside
Definition: qcustomplot.h:258
A filled diamond (45° rotated square)
Definition: qcustomplot.h:894
void setIconBorderPen(const QPen &pen)
void removeChildY(QCPItemPosition *pos)
Colors suitable to represent different elevations on geographical maps.
Definition: qcustomplot.h:1923
int selectionTolerance() const
Definition: qcustomplot.h:1728
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:1819
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QPen mSelectedSubTickPen
Definition: qcustomplot.h:1266
QBrush mSelectedBrush
Definition: qcustomplot.h:3558
void setNumberPrecision(int precision)
QRect labelSelectionBox() const
Definition: qcustomplot.h:1333
static const double minRange
Definition: qcustomplot.h:509
void removeDataBefore(double t)
QList< QCPAbstractItem * > items() const
void setSelectedPen(const QPen &pen)
QString mText
Definition: qcustomplot.h:2336
double keyErrorPlus
Definition: qcustomplot.h:2473
QCPLayer * currentLayer() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
QVector< double > mOutliers
Definition: qcustomplot.h:2973
Tick coordinate is regarded as normal number and will be displayed as such. (see setNumberFormat) ...
Definition: qcustomplot.h:1055
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
QPointer< QCPAxis > mRangeZoomVertAxis
Definition: qcustomplot.h:2051
void setKeyRange(const QCPRange &keyRange)
int layerCount() const
{ssTriangle.png} an equilateral triangle, standing on baseline
Definition: qcustomplot.h:255
{ssDisc.png} a circle which is filled with the pen&#39;s color (not the brush as with ssCircle) ...
Definition: qcustomplot.h:251
QCPGraph * graph() const
Definition: qcustomplot.h:3671
QCPAxis::AxisType type
Definition: qcustomplot.h:1336
bool isNone() const
Definition: qcustomplot.h:291
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:123
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
bool autoTickStep() const
Definition: qcustomplot.h:1101
friend class QCPLayer
Definition: qcustomplot.h:1895
LabelType tickLabelType() const
Definition: qcustomplot.h:1106
QCPRange mKeyRange
Definition: qcustomplot.h:3035
void setData(QCPBarDataMap *data, bool copy=false)
Interaction
Definition: qcustomplot.h:155
static void connectBars(QCPBars *lower, QCPBars *upper)
QCPBars * barBelow() const
Definition: qcustomplot.h:2855
bool rangeReversed() const
Definition: qcustomplot.h:1097
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setBrush(const QBrush &brush)
int top() const
Definition: qcustomplot.h:2027
QPen mSelectedPen
Definition: qcustomplot.h:3309
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:895
Q_SLOT void setSelectedParts(const SelectableParts &selectedParts)
void dataRangeChanged(QCPRange newRange)
void setRangeDrag(bool enabled)
QBrush mBrushPositive
Definition: qcustomplot.h:3216
QPen pen() const
Definition: qcustomplot.h:3665
data points are connected by a straight line
Definition: qcustomplot.h:2511
Final phase in which the layout system places the rects of the elements.
Definition: qcustomplot.h:648
0x01 Axis is vertical and on the left side of the axis rect
Definition: qcustomplot.h:1042
void setLabelColor(const QColor &color)
void setAntialiasedFill(bool enabled)
QCP::AntialiasedElements mAntialiasedElements
Definition: qcustomplot.h:1853
bool inverted() const
Definition: qcustomplot.h:907
No error bars are shown.
Definition: qcustomplot.h:2521
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:3602
QMap< double, QCPFinancialData > QCPFinancialDataMap
Definition: qcustomplot.h:3140
void setHead(const QCPLineEnding &head)
void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
QCPItemAnchor *const bottom
Definition: qcustomplot.h:3400
bool mAutoAddPlottableToLegend
Definition: qcustomplot.h:1848
void setAntialiased(bool enabled)
void setSelectedBrush(const QBrush &brush)
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:424
virtual QPointF anchorPixelPoint(int anchorId) const
void scaleRange(double factor, double center)
QPen pen() const
Definition: qcustomplot.h:948
void getCurveData(QVector< QPointF > *lineData) const
void setData(QCPCurveDataMap *data, bool copy=false)
bool mErrorBarSkipSymbol
Definition: qcustomplot.h:2588
double baseLog(double value) const
QCPItemPixmap(QCustomPlot *parentPlot)
QCPLayout * layout() const
Definition: qcustomplot.h:656
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:1250
0x0000 No elements
Definition: qcustomplot.h:130
QCPColorGradient(GradientPreset preset=gpCold)
void rescaleValueAxis(bool onlyEnlarge=false) const
void setRowStretchFactor(int row, double factor)
bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1)
double key
Definition: qcustomplot.h:2807
QCPAbstractPlottable * plottableAt(const QPointF &pos, bool onlySelectable=false) const
void setBarWidth(int width)
QList< double > mColumnStretchFactors
Definition: qcustomplot.h:810
bool mMapImageInvalidated
Definition: qcustomplot.h:3104
BracketStyle style() const
Definition: qcustomplot.h:3736
QList< QCPGraph * > selectedGraphs() const
QCPItemAnchor *const top
Definition: qcustomplot.h:3478
bool removeItem(QCPAbstractItem *item)
void drawBackground(QCPPainter *painter)
A layout element displaying a plot title text.
Definition: qcustomplot.h:2293
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Definition: zran.c:67
virtual bool take(QCPLayoutElement *element)
Bar width is given by a fraction of the axis rect size.
Definition: qcustomplot.h:2842
0x02 right margin
Definition: qcustomplot.h:101
void setInterpolate(bool enabled)
SelectableParts selectedParts() const
Definition: qcustomplot.h:1132
void setPen(const QPen &pen)
Q_SLOT void deselectAll()
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
QPointer< QCPColorScale > mColorScale
Definition: qcustomplot.h:3100
bool selected() const
Definition: qcustomplot.h:1624
QCPLineEnding mTail
Definition: qcustomplot.h:3310
void setTickLabelSide(LabelSide side)
QCPLayoutInset * insetLayout() const
Definition: qcustomplot.h:2017
QCPRange mDataRange
Definition: qcustomplot.h:2439
QPointer< QCPAxis > mRangeDragVertAxis
Definition: qcustomplot.h:2051
The QCustomPlot surface is immediately refreshed, by calling QWidget::repaint() after the replot...
Definition: qcustomplot.h:1710
virtual ~QCPItemBracket()
void removeChild(QCP::MarginSide side, QCPLayoutElement *element)
void setBrush(const QBrush &brush)
virtual QCPLayoutElement * takeAt(int index)
QFont mSelectedFont
Definition: qcustomplot.h:2339
static int max
Definition: enough.c:170
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:2051
void setPen(const QPen &pen)
static AxisType opposite(AxisType type)
QString name() const
Definition: qcustomplot.h:1409
int mNumberPrecision
Definition: qcustomplot.h:1255
virtual void draw(QCPPainter *painter)
double pixelToCoord(double value) const
QMap< double, QColor > mColorStops
Definition: qcustomplot.h:1962
{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
Definition: qcustomplot.h:254
QStack< bool > mAntialiasingStack
Definition: qcustomplot.h:360
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:2209
void clearItems()
Phase used for any type of preparation that needs to be done before margin calculation and layout...
Definition: qcustomplot.h:646
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:2052
Q_SLOT void setDataRange(const QCPRange &dataRange)
virtual int calculateAutoMargin(QCP::MarginSide side)
void setSelectedTextColor(const QColor &color)
double mScaleLogBaseLogInv
Definition: qcustomplot.h:1271
void setTickLengthOut(int outside)
double rangeZoomFactor(Qt::Orientation orientation)
QCPItemPosition *const point1
Definition: qcustomplot.h:3259
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
double tickStep() const
Definition: qcustomplot.h:1115
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:962
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:3095
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:896
virtual void mousePressEvent(QMouseEvent *event)
0x002 Legend items individually (see selectedItems)
Definition: qcustomplot.h:2196
virtual void mouseDoubleClickEvent(QMouseEvent *event)
QPixmap pixmap() const
Definition: qcustomplot.h:279
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:2440
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:2586
void addChildY(QCPItemPosition *pos)
{ssPlusCircle.png} a circle with a plus inside
Definition: qcustomplot.h:260
double keyErrorMinus
Definition: qcustomplot.h:2473
QPen pen() const
Definition: qcustomplot.h:3590
bool mBackgroundScaled
Definition: qcustomplot.h:1860
QRect rect() const
Definition: qcustomplot.h:657
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:257
QHash< QCPAxis::AxisType, QList< QCPAxis * > > mAxes
Definition: qcustomplot.h:2058
QString name() const
Definition: qcustomplot.h:1510
QCPItemAnchor *const right
Definition: qcustomplot.h:3480
void setLength(double length)
Data points are connected with a straight line.
Definition: qcustomplot.h:2674
void removeDataBefore(double key)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const =0
QBrush mSelectedBrush
Definition: qcustomplot.h:3409
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
Definition: qcustomplot.h:1556
void addFillBasePoints(QVector< QPointF > *lineData) const
void setNumberFormat(const QString &formatCode)
SelectableParts mSelectableParts
Definition: qcustomplot.h:1238
bool visible() const
Definition: qcustomplot.h:384
void rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
QCPItemPosition *const startDir
Definition: qcustomplot.h:3350
virtual void draw(QCPPainter *painter)
ErrorType errorType() const
Definition: qcustomplot.h:2535
The axis label.
Definition: qcustomplot.h:1083
QCPRange sanitizedForLinScale() const
void setLineStyle(LineStyle style)
QFont mainFont() const
virtual QRect clipRect() const
bool mAntialiased
Definition: qcustomplot.h:450
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
QRect mTextBoundingRect
Definition: qcustomplot.h:2341
Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle ...
Definition: qcustomplot.h:1926
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
QCPLayoutElement * element(int row, int column) const
QPen pen() const
Definition: qcustomplot.h:277
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:2997
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:1851
int autoTickCount() const
Definition: qcustomplot.h:1099
virtual ~QCPLayoutGrid()
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom, QCPAxisRect::setRangeZoomAxes)
Definition: qcustomplot.h:156
QCPAbstractLegendItem * item(int index) const
QCPItemAnchor *const top
Definition: qcustomplot.h:3544
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:2582
void setTickLengthIn(int inside)
double mRangeZoomFactorVert
Definition: qcustomplot.h:2052
void setSelectedColor(const QColor &color)
double size() const
Definition: qcustomplot.h:3669
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:3670
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:262
void mouseWheel(QWheelEvent *event)
bool addItem(QCPAbstractLegendItem *item)
0x0010 Legend items
Definition: qcustomplot.h:122
Continuous lightness from black over weak blueish colors to white (suited for non-biased data represe...
Definition: qcustomplot.h:1921
void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
Definition: qcustomplot.h:193
void beforeReplot()
void setPen(const QPen &pen)
QFont getTickLabelFont() const
QCPAxisPainterPrivate * mAxisPainter
Definition: qcustomplot.h:1275
virtual void updateLayout()
void setMode(PainterMode mode, bool enabled=true)
The abstract base class for layouts.
Definition: qcustomplot.h:719
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:1867
Holds the data of one single data point for QCPFinancial.
Definition: qcustomplot.h:3124
void setTickLabelPadding(int padding)
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > *pointData) const
QBrush mBackgroundBrush
Definition: qcustomplot.h:1857
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:3603
None of the selectable parts.
Definition: qcustomplot.h:1080
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:1044
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
bool realVisibility() const
QCPItemAnchor *const left
Definition: qcustomplot.h:3402
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:2156
QCPAbstractPlottable * plottable(int index)
ColorInterpolation mColorInterpolation
Definition: qcustomplot.h:1963
QCPPlottableLegendItem * itemWithPlottable(const QCPAbstractPlottable *plottable) const
QSize size() const
Definition: qcustomplot.h:2031
QCPItemAnchor *const bottomLeft
Definition: qcustomplot.h:3401
QCPAxis * axis(QCPAxis::AxisType type, int index=0) const
{ssTriangleInverted.png} an equilateral triangle, standing on corner
Definition: qcustomplot.h:256
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:3046
Layer is inserted above other layer.
Definition: qcustomplot.h:1701
void rescale(bool onlyVisiblePlottables=false)
bool mAdaptiveSampling
Definition: qcustomplot.h:2590
QCPBarDataMap * mData
Definition: qcustomplot.h:2885
QHash< QCP::MarginSide, QList< QCPLayoutElement * > > mChildren
Definition: qcustomplot.h:615
void setPlottingHint(QCP::PlottingHint hint, bool enabled=true)
QCPAxis * valueAxis() const
Definition: qcustomplot.h:1418
QColor mTickLabelColor
Definition: qcustomplot.h:1252
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:1593
Both sign domains, including zero, i.e. all (rational) numbers.
Definition: qcustomplot.h:1456
InsetPlacement insetPlacement(int index) const
QString dateTimeFormat() const
Definition: qcustomplot.h:1111
QRect viewport() const
Definition: qcustomplot.h:1719
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:395
bool setCurrentLayer(const QString &name)
QCPItemPosition *const position
Definition: qcustomplot.h:3476
void setAxisRect(QCPAxisRect *axisRect)
int tickLabelPadding() const
void addChildX(QCPItemPosition *pos)
QCPAxis::LabelSide tickLabelSide
Definition: qcustomplot.h:1345
void getStepRightPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QPointer< QCPLayerable > mParentLayerable
Definition: qcustomplot.h:448
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
int subTickLengthOut() const
QList< QCPAbstractPlottable * > plottables() const
QCPItemAnchor *const topLeft
Definition: qcustomplot.h:3477
QPen mZeroLinePen
Definition: qcustomplot.h:964
QPen mainPen() const
Queues the refresh such that it is performed at a slightly delayed point in time after the replot...
Definition: qcustomplot.h:1711
QPoint mMousePressPos
Definition: qcustomplot.h:1868
Color channels red, green and blue are linearly interpolated.
Definition: qcustomplot.h:1909
bool ticks() const
Definition: qcustomplot.h:1103
virtual void draw(QCPPainter *painter)
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lineData) const
QCPItemAnchor *const right
Definition: qcustomplot.h:3546
Tick labels will be displayed inside the axis rect and clipped to the inner axis rect.
Definition: qcustomplot.h:1064
ChartStyle mChartStyle
Definition: qcustomplot.h:3213
void setSelectedFont(const QFont &font)
QList< double > mRowStretchFactors
Definition: qcustomplot.h:811
void setInsetAlignment(int index, Qt::Alignment alignment)
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
QRect mViewport
Definition: qcustomplot.h:1846
void setPen(const QPen &pen)
virtual void clearData()
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:2046
bool selectable() const
Definition: qcustomplot.h:2315
friend class QCPLegend
Definition: qcustomplot.h:1893
QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
bool removeAxis(QCPAxis *axis)
double key() const
Definition: qcustomplot.h:1569
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Qt::TimeSpec dateTimeSpec() const
Definition: qcustomplot.h:1112
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:1594
void setClipAxisRect(QCPAxisRect *rect)
Qt::TransformationMode mTransformationMode
Definition: qcustomplot.h:3619
double baseValue() const
Definition: qcustomplot.h:2854
virtual int calculateMargin()
void setText(const QString &text)
QPen pen() const
Definition: qcustomplot.h:3249
QFont mTickLabelFont
Definition: qcustomplot.h:1251
void setPen(const QPen &pen)
QVector< double > tickPositions
Definition: qcustomplot.h:1358
QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true)
void selectionChanged(bool selected)
QPen pen() const
Definition: qcustomplot.h:3381
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:3010
QCPLayerable * layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const
int subTickCount() const
Definition: qcustomplot.h:1120
double mTickStep
Definition: qcustomplot.h:1261
A non-filled arrow head with open back.
Definition: qcustomplot.h:891
void setSelectedPen(const QPen &pen)
void setBorderPen(const QPen &pen)
SelectableParts selectableParts() const
Definition: qcustomplot.h:1133
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
double key() const
Definition: qcustomplot.h:2937
void setZeroLinePen(const QPen &pen)
QCPItemPosition *const point2
Definition: qcustomplot.h:3260
bool hasAnchor(const QString &name) const
QString text() const
Definition: qcustomplot.h:2310
Bar spacing is in absolute pixels.
Definition: qcustomplot.h:2754
QMargins mPadding
Definition: qcustomplot.h:3498
QCPRange mDataBounds
Definition: qcustomplot.h:3039
double length() const
Definition: qcustomplot.h:906
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:1596
virtual QSize maximumSizeHint() const
0x0400 Zero-lines, see QCPGrid::setZeroLinePen
Definition: qcustomplot.h:128
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:3494
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:1540
void adoptElement(QCPLayoutElement *el)
void setValueRange(const QCPRange &valueRange)
bool hasItem(QCPAbstractItem *item) const
QPen iconBorderPen() const
Definition: qcustomplot.h:2211
QCPItemPosition *const end
Definition: qcustomplot.h:3305
A curly brace with varying stroke width giving a calligraphic impression.
Definition: qcustomplot.h:3726
QList< QCPAbstractItem * > items() const
void setSize(double size)
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:2045
void setTicks(bool show)
QFont mSelectedFont
Definition: qcustomplot.h:3493
void setGraphKey(double key)
Defines a color gradient for use with e.g. QCPColorMap.
Definition: qcustomplot.h:1900
QCPMarginGroup(QCustomPlot *parentPlot)
void setErrorPen(const QPen &pen)
virtual QSize sizeHint() const
void setTypeY(PositionType type)
QCPGrid * grid() const
Definition: qcustomplot.h:1143
double upper
Definition: qcustomplot.h:479
int tickLengthIn() const
QCPMarginGroup * marginGroup(QCP::MarginSide side) const
Definition: qcustomplot.h:664
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:1859
QCPAbstractItem(QCustomPlot *parentPlot)
void setSubTickLength(int inside, int outside=0)
void setBrushPositive(const QBrush &brush)
bool selectable() const
Definition: qcustomplot.h:1419
QMap< double, QColor > colorStops() const
Definition: qcustomplot.h:1939
QMargins mMinimumMargins
Definition: qcustomplot.h:692
A bar that is skewed (skew controllable via setLength)
Definition: qcustomplot.h:897
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
Represents the range an axis is encompassing.
Definition: qcustomplot.h:476
QColor mLabelColor
Definition: qcustomplot.h:1245
QCPRange dataRange() const
Definition: qcustomplot.h:3063
bool tickLabels() const
Definition: qcustomplot.h:1104
QCPLayoutGrid * mPlotLayout
Definition: qcustomplot.h:1847
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:1651
QCPRange mDragStartHorzRange
Definition: qcustomplot.h:2054
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:2585
virtual void clearData()
Groups multiple QCPBars together so they appear side by side.
Definition: qcustomplot.h:2740
virtual void deselectEvent(bool *selectionStateChanged)
0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)
Definition: qcustomplot.h:159
void initializeParentPlot(QCustomPlot *parentPlot)
double lower
Definition: qcustomplot.h:479
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:2098
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:1094
ScatterShape mShape
Definition: qcustomplot.h:300
Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and pha...
Definition: qcustomplot.h:1929
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:3541
bool removeItem(int index)
SelectableParts selectableParts() const
Definition: qcustomplot.h:2212
0xFF all margins
Definition: qcustomplot.h:104
QPixmap pixmap() const
Definition: qcustomplot.h:3586
virtual void draw(QCPPainter *painter)
void ticksRequest()
virtual void deselectEvent(bool *selectionStateChanged)
bool mTightBoundary
Definition: qcustomplot.h:3099
virtual void draw(QCPPainter *painter)
virtual QCPLayoutElement * elementAt(int index) const
friend class QCPPlottableLegendItem
Definition: qcustomplot.h:1499
QCPFinancialDataMap * data() const
Definition: qcustomplot.h:3172
bool mRangeReversed
Definition: qcustomplot.h:1269
virtual void setupTickVectors()
PositionType mPositionTypeX
Definition: qcustomplot.h:1592
void addData(const QCPDataMap &dataMap)
Qt::Alignment mPositionAlignment
Definition: qcustomplot.h:3495
QList< QCPAxisRect * > axisRects() const
Holds multiple axes and arranges them in a rectangular shape.
Definition: qcustomplot.h:1972
QCPAxis * yAxis
Definition: qcustomplot.h:1819
void setPen(const QPen &pen)
QBrush brush() const
Definition: qcustomplot.h:3529
int width() const
Definition: qcustomplot.h:2029
QBrush mainBrush() const
virtual ~QCPLayoutInset()
virtual void paintEvent(QPaintEvent *event)
{ssDiamond.png} a diamond
Definition: qcustomplot.h:253
QCPAxisRect * clipAxisRect() const
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:2055
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:1268
double mBaseValue
Definition: qcustomplot.h:2889
QPen getTickPen() const
virtual QSize minimumSizeHint() const
QBrush brush() const
Definition: qcustomplot.h:3383
Qt::KeyboardModifier mMultiSelectModifier
Definition: qcustomplot.h:1864
QCPLegend * legend
Definition: qcustomplot.h:1820
void setPenNegative(const QPen &pen)
void setLabel(const QString &str)
QPointer< QCPLayoutElement > mMouseEventElement
Definition: qcustomplot.h:1869
virtual void draw(QCPPainter *painter)
QCP::AntialiasedElements notAntialiasedElements() const
Definition: qcustomplot.h:1725
QCPItemAnchor *const top
Definition: qcustomplot.h:3397
void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
AxisType mAxisType
Definition: qcustomplot.h:1233
QMap< double, QCPCurveData > QCPCurveDataMap
Definition: qcustomplot.h:2655
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:1262
bool mAntialiasedSubGrid
Definition: qcustomplot.h:963
virtual void clearData()
void sizeConstraintsChanged() const
Q_SLOT void setRange(const QCPRange &range)
LineStyle mLineStyle
Definition: qcustomplot.h:2584
QList< QCPAxis * > selectedAxes() const
A filled arrow head with an indented back.
Definition: qcustomplot.h:890
line is drawn as steps where the step height is the value of the right data point ...
Definition: qcustomplot.h:2513
virtual void deselectEvent(bool *selectionStateChanged)
QCP::Interactions mInteractions
Definition: qcustomplot.h:1854
QCPGraph * mGraph
Definition: qcustomplot.h:3700
QCPItemAnchor *const topRight
Definition: qcustomplot.h:3398
MarginSide
Definition: qcustomplot.h:100
void gradientChanged(QCPColorGradient newGradient)
Qt::Alignment mTextAlignment
Definition: qcustomplot.h:3496
void setLabel(const QString &str)
QPen mPen
Definition: qcustomplot.h:964
QLatin1Char mNumberFormatChar
Definition: qcustomplot.h:1256
bool mBackgroundScaled
Definition: qcustomplot.h:2047
int axisCount(QCPAxis::AxisType type) const
QBrush brush() const
Definition: qcustomplot.h:1415
AntialiasedElement
Definition: qcustomplot.h:118
QCPItemPosition *const left
Definition: qcustomplot.h:3747
bool mColorBufferInvalidated
Definition: qcustomplot.h:1968
int axisRectCount() const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:1373
QPointer< QCPGraph > mChannelFillGraph
Definition: qcustomplot.h:2589
virtual void draw(QCPPainter *painter)
AxisType axisType() const
Definition: qcustomplot.h:1092
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:2886
No ending decoration.
Definition: qcustomplot.h:888
bool hasElement(int row, int column)
virtual QList< QCPLayoutElement * > elements(bool recursive) const
QPen mSelectedPen
Definition: qcustomplot.h:3491
QCustomPlot * mParentPlot
Definition: qcustomplot.h:447
ScatterShape shape() const
Definition: qcustomplot.h:276
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:3396
double value
Definition: qcustomplot.h:2807
Error bars for the value dimension of the data point are shown.
Definition: qcustomplot.h:2523
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:1861
virtual void wheelEvent(QWheelEvent *event)
QBrush mainBrush() const
static big_t count(int syms, int len, int left)
Definition: enough.c:203
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:2103
virtual ~QCPFinancial()
int right() const
Definition: qcustomplot.h:2026
double pointDistance(const QPointF &pixelPoint) const
virtual void draw(QCPPainter *painter)
void afterReplot()
QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
static struct tab * done
Definition: enough.c:176
Holds the data of one single data point (one bar) for QCPBars.
Definition: qcustomplot.h:2802
QColor mSelectedLabelColor
Definition: qcustomplot.h:1245
void setFont(const QFont &font)
void setColorStopAt(double position, const QColor &color)
bool mTicks
Definition: qcustomplot.h:1260
void setLabelPadding(int padding)