ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
qcustomplot.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "qcustomplot.h"
9 
10 // Qt5/Qt6 Compatibility
11 #include <QtCompat.h>
12 
13 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
14 #include <QtGui/QPrintEngine>
15 #include <QtGui/QPrinter>
16 #include <QtGui/QToolTip>
17 #else
18 #include <QtPrintSupport/QPrintEngine>
19 #include <QtPrintSupport/QPrinter>
20 #include <QtWidgets/QToolTip>
21 #endif
22 
26 
46  : QPainter(), mModes(pmDefault), mIsAntialiasing(false) {
47  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter
48  // isn't active yet and a call to begin() will follow
49 }
50 
59 QCPPainter::QCPPainter(QPaintDevice *device)
60  : QPainter(device), mModes(pmDefault), mIsAntialiasing(false) {
61 #if QT_VERSION < \
62  QT_VERSION_CHECK(5, 0, \
63  0) // before Qt5, default pens used to be cosmetic if
64  // NonCosmeticDefaultPen flag isn't set. So we set
65  // it to get consistency across Qt versions.
66  if (isActive()) setRenderHint(QPainter::NonCosmeticDefaultPen);
67 #endif
68 }
69 
71 
78 void QCPPainter::setPen(const QPen &pen) {
79  QPainter::setPen(pen);
80  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
81 }
82 
90 void QCPPainter::setPen(const QColor &color) {
91  QPainter::setPen(color);
92  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
93 }
94 
102 void QCPPainter::setPen(Qt::PenStyle penStyle) {
103  QPainter::setPen(penStyle);
104  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
105 }
106 
116 void QCPPainter::drawLine(const QLineF &line) {
117  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
118  QPainter::drawLine(line);
119  else
120  QPainter::drawLine(line.toLine());
121 }
122 
130 void QCPPainter::setAntialiasing(bool enabled) {
131  setRenderHint(QPainter::Antialiasing, enabled);
132  if (mIsAntialiasing != enabled) {
133  mIsAntialiasing = enabled;
134  if (!mModes.testFlag(
135  pmVectorized)) // antialiasing half-pixel shift only needed
136  // for rasterized outputs
137  {
138  if (mIsAntialiasing)
139  translate(0.5, 0.5);
140  else
141  translate(-0.5, -0.5);
142  }
143  }
144 }
145 
150 void QCPPainter::setModes(QCPPainter::PainterModes modes) { mModes = modes; }
151 
163 bool QCPPainter::begin(QPaintDevice *device) {
164  bool result = QPainter::begin(device);
165 #if QT_VERSION < \
166  QT_VERSION_CHECK(5, 0, \
167  0) // before Qt5, default pens used to be cosmetic if
168  // NonCosmeticDefaultPen flag isn't set. So we set
169  // it to get consistency across Qt versions.
170  if (result) setRenderHint(QPainter::NonCosmeticDefaultPen);
171 #endif
172  return result;
173 }
174 
180 void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled) {
181  if (!enabled && mModes.testFlag(mode))
182  mModes &= ~mode;
183  else if (enabled && !mModes.testFlag(mode))
184  mModes |= mode;
185 }
186 
196 void QCPPainter::save() {
198  QPainter::save();
199 }
200 
210 void QCPPainter::restore() {
211  if (!mAntialiasingStack.isEmpty())
213  else
214  qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
215  QPainter::restore();
216 }
217 
223  if (qFuzzyIsNull(pen().widthF())) {
224  QPen p = pen();
225  p.setWidth(1);
226  QPainter::setPen(p);
227  }
228 }
229 
233 
293 /* start documentation of inline functions */
294 
315 /* end documentation of inline functions */
316 
325  : mSize(6),
326  mShape(ssNone),
327  mPen(Qt::NoPen),
328  mBrush(Qt::NoBrush),
329  mPenDefined(false) {}
330 
338 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size)
339  : mSize(size),
340  mShape(shape),
341  mPen(Qt::NoPen),
342  mBrush(Qt::NoBrush),
343  mPenDefined(false) {}
344 
350 QCPScatterStyle::QCPScatterStyle(ScatterShape shape,
351  const QColor &color,
352  double size)
353  : mSize(size),
354  mShape(shape),
355  mPen(QPen(color)),
356  mBrush(Qt::NoBrush),
357  mPenDefined(true) {}
358 
364 QCPScatterStyle::QCPScatterStyle(ScatterShape shape,
365  const QColor &color,
366  const QColor &fill,
367  double size)
368  : mSize(size),
369  mShape(shape),
370  mPen(QPen(color)),
371  mBrush(QBrush(fill)),
372  mPenDefined(true) {}
373 
390 QCPScatterStyle::QCPScatterStyle(ScatterShape shape,
391  const QPen &pen,
392  const QBrush &brush,
393  double size)
394  : mSize(size),
395  mShape(shape),
396  mPen(pen),
397  mBrush(brush),
398  mPenDefined(pen.style() != Qt::NoPen) {}
399 
404 QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap)
405  : mSize(5),
406  mShape(ssPixmap),
407  mPen(Qt::NoPen),
408  mBrush(Qt::NoBrush),
409  mPixmap(pixmap),
410  mPenDefined(false) {}
411 
422 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath,
423  const QPen &pen,
424  const QBrush &brush,
425  double size)
426  : mSize(size),
427  mShape(ssCustom),
428  mPen(pen),
429  mBrush(brush),
430  mCustomPath(customPath),
431  mPenDefined(pen.style() != Qt::NoPen) {}
432 
438 void QCPScatterStyle::setSize(double size) { mSize = size; }
439 
449  mShape = shape;
450 }
451 
461 void QCPScatterStyle::setPen(const QPen &pen) {
462  mPenDefined = true;
463  mPen = pen;
464 }
465 
473 void QCPScatterStyle::setBrush(const QBrush &brush) { mBrush = brush; }
474 
482 void QCPScatterStyle::setPixmap(const QPixmap &pixmap) {
484  mPixmap = pixmap;
485 }
486 
492 void QCPScatterStyle::setCustomPath(const QPainterPath &customPath) {
495 }
496 
509  const QPen &defaultPen) const {
510  painter->setPen(mPenDefined ? mPen : defaultPen);
511  painter->setBrush(mBrush);
512 }
513 
523 void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const {
524  drawShape(painter, pos.x(), pos.y());
525 }
526 
530 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const {
531  double w = mSize / 2.0;
532  switch (mShape) {
533  case ssNone:
534  break;
535  case ssDot: {
536  painter->drawLine(QPointF(x, y), QPointF(x + 0.0001, y));
537  break;
538  }
539  case ssCross: {
540  painter->drawLine(QLineF(x - w, y - w, x + w, y + w));
541  painter->drawLine(QLineF(x - w, y + w, x + w, y - w));
542  break;
543  }
544  case ssPlus: {
545  painter->drawLine(QLineF(x - w, y, x + w, y));
546  painter->drawLine(QLineF(x, y + w, x, y - w));
547  break;
548  }
549  case ssCircle: {
550  painter->drawEllipse(QPointF(x, y), w, w);
551  break;
552  }
553  case ssDisc: {
554  QBrush b = painter->brush();
555  painter->setBrush(painter->pen().color());
556  painter->drawEllipse(QPointF(x, y), w, w);
557  painter->setBrush(b);
558  break;
559  }
560  case ssSquare: {
561  painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
562  break;
563  }
564  case ssDiamond: {
565  painter->drawLine(QLineF(x - w, y, x, y - w));
566  painter->drawLine(QLineF(x, y - w, x + w, y));
567  painter->drawLine(QLineF(x + w, y, x, y + w));
568  painter->drawLine(QLineF(x, y + w, x - w, y));
569  break;
570  }
571  case ssStar: {
572  painter->drawLine(QLineF(x - w, y, x + w, y));
573  painter->drawLine(QLineF(x, y + w, x, y - w));
574  painter->drawLine(QLineF(x - w * 0.707, y - w * 0.707,
575  x + w * 0.707, y + w * 0.707));
576  painter->drawLine(QLineF(x - w * 0.707, y + w * 0.707,
577  x + w * 0.707, y - w * 0.707));
578  break;
579  }
580  case ssTriangle: {
581  painter->drawLine(
582  QLineF(x - w, y + 0.755 * w, x + w, y + 0.755 * w));
583  painter->drawLine(QLineF(x + w, y + 0.755 * w, x, y - 0.977 * w));
584  painter->drawLine(QLineF(x, y - 0.977 * w, x - w, y + 0.755 * w));
585  break;
586  }
587  case ssTriangleInverted: {
588  painter->drawLine(
589  QLineF(x - w, y - 0.755 * w, x + w, y - 0.755 * w));
590  painter->drawLine(QLineF(x + w, y - 0.755 * w, x, y + 0.977 * w));
591  painter->drawLine(QLineF(x, y + 0.977 * w, x - w, y - 0.755 * w));
592  break;
593  }
594  case ssCrossSquare: {
595  painter->drawLine(QLineF(x - w, y - w, x + w * 0.95, y + w * 0.95));
596  painter->drawLine(QLineF(x - w, y + w * 0.95, x + w * 0.95, y - w));
597  painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
598  break;
599  }
600  case ssPlusSquare: {
601  painter->drawLine(QLineF(x - w, y, x + w * 0.95, y));
602  painter->drawLine(QLineF(x, y + w, x, y - w));
603  painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
604  break;
605  }
606  case ssCrossCircle: {
607  painter->drawLine(QLineF(x - w * 0.707, y - w * 0.707,
608  x + w * 0.670, y + w * 0.670));
609  painter->drawLine(QLineF(x - w * 0.707, y + w * 0.670,
610  x + w * 0.670, y - w * 0.707));
611  painter->drawEllipse(QPointF(x, y), w, w);
612  break;
613  }
614  case ssPlusCircle: {
615  painter->drawLine(QLineF(x - w, y, x + w, y));
616  painter->drawLine(QLineF(x, y + w, x, y - w));
617  painter->drawEllipse(QPointF(x, y), w, w);
618  break;
619  }
620  case ssPeace: {
621  painter->drawLine(QLineF(x, y - w, x, y + w));
622  painter->drawLine(QLineF(x, y, x - w * 0.707, y + w * 0.707));
623  painter->drawLine(QLineF(x, y, x + w * 0.707, y + w * 0.707));
624  painter->drawEllipse(QPointF(x, y), w, w);
625  break;
626  }
627  case ssPixmap: {
628  painter->drawPixmap(x - mPixmap.width() * 0.5,
629  y - mPixmap.height() * 0.5, mPixmap);
630  break;
631  }
632  case ssCustom: {
633  QTransform oldTransform = painter->transform();
634  painter->translate(x, y);
635  painter->scale(mSize / 6.0, mSize / 6.0);
636  painter->drawPath(mCustomPath);
637  painter->setTransform(oldTransform);
638  break;
639  }
640  }
641 }
642 
646 
695 /* start documentation of inline functions */
696 
712 /* end documentation of inline functions */
713 
723 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
724  : QObject(parentPlot),
725  mParentPlot(parentPlot),
726  mName(layerName),
727  mIndex(-1), // will be set to a proper value by the QCustomPlot layer
728  // creation function
729  mVisible(true) {
730  // Note: no need to make sure layerName is unique, because layer
731  // management is done with QCustomPlot functions.
732 }
733 
735  // If child layerables are still on this layer, detach them, so they don't
736  // try to reach back to this then invalid layer once they get deleted/moved
737  // themselves. This only happens when layers are deleted directly, like in
738  // the QCustomPlot destructor. (The regular layer removal procedure for the
739  // user is to call QCustomPlot::removeLayer, which moves all layerables off
740  // this layer before deleting it.)
741 
742  while (!mChildren.isEmpty())
743  mChildren.last()->setLayer(
744  0); // removes itself from mChildren via removeChild()
745 
746  if (mParentPlot->currentLayer() == this)
747  qDebug() << Q_FUNC_INFO
748  << "The parent plot's mCurrentLayer will be a dangling "
749  "pointer. Should have been set to a valid layer or 0 "
750  "beforehand.";
751 }
752 
761 void QCPLayer::setVisible(bool visible) { mVisible = visible; }
762 
775 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend) {
776  if (!mChildren.contains(layerable)) {
777  if (prepend)
778  mChildren.prepend(layerable);
779  else
780  mChildren.append(layerable);
781  } else
782  qDebug() << Q_FUNC_INFO << "layerable is already child of this layer"
783  << reinterpret_cast<quintptr>(layerable);
784 }
785 
795 void QCPLayer::removeChild(QCPLayerable *layerable) {
796  if (!mChildren.removeOne(layerable))
797  qDebug() << Q_FUNC_INFO << "layerable is not child of this layer"
798  << reinterpret_cast<quintptr>(layerable);
799 }
800 
804 
817 /* start documentation of inline functions */
818 
834 /* end documentation of inline functions */
835 /* start documentation of pure virtual functions */
836 
882 /* end documentation of pure virtual functions */
883 /* start documentation of signals */
884 
893 /* end documentation of signals */
894 
915  QString targetLayer,
916  QCPLayerable *parentLayerable)
917  : QObject(plot),
918  mVisible(true),
919  mParentPlot(plot),
920  mParentLayerable(parentLayerable),
921  mLayer(0),
922  mAntialiased(true) {
923  if (mParentPlot) {
924  if (targetLayer.isEmpty())
925  setLayer(mParentPlot->currentLayer());
926  else if (!setLayer(targetLayer))
927  qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to"
928  << targetLayer << "failed.";
929  }
930 }
931 
933  if (mLayer) {
934  mLayer->removeChild(this);
935  mLayer = 0;
936  }
937 }
938 
944 void QCPLayerable::setVisible(bool on) { mVisible = on; }
945 
952 bool QCPLayerable::setLayer(QCPLayer *layer) {
953  return moveToLayer(layer, false);
954 }
955 
961 bool QCPLayerable::setLayer(const QString &layerName) {
962  if (!mParentPlot) {
963  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
964  return false;
965  }
966  if (QCPLayer *layer = mParentPlot->layer(layerName)) {
967  return setLayer(layer);
968  } else {
969  qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
970  return false;
971  }
972 }
973 
981 void QCPLayerable::setAntialiased(bool enabled) { mAntialiased = enabled; }
982 
997 bool QCPLayerable::realVisibility() const {
998  return mVisible && (!mLayer || mLayer->visible()) &&
999  (!mParentLayerable || mParentLayerable.data()->realVisibility());
1000 }
1001 
1043 double QCPLayerable::selectTest(const QPointF &pos,
1044  bool onlySelectable,
1045  QVariant *details) const {
1046  Q_UNUSED(pos)
1047  Q_UNUSED(onlySelectable)
1048  Q_UNUSED(details)
1049  return -1.0;
1050 }
1051 
1071  if (mParentPlot) {
1072  qDebug() << Q_FUNC_INFO
1073  << "called with mParentPlot already initialized";
1074  return;
1075  }
1076 
1077  if (!parentPlot) qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1078 
1081 }
1082 
1095 void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable) {
1097 }
1098 
1107 bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend) {
1108  if (layer && !mParentPlot) {
1109  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1110  return false;
1111  }
1112  if (layer && layer->parentPlot() != mParentPlot) {
1113  qDebug() << Q_FUNC_INFO << "layer" << layer->name()
1114  << "is not in same QCustomPlot as this layerable";
1115  return false;
1116  }
1117 
1118  QCPLayer *oldLayer = mLayer;
1119  if (mLayer) mLayer->removeChild(this);
1120  mLayer = layer;
1121  if (mLayer) mLayer->addChild(this, prepend);
1122  if (mLayer != oldLayer) emit layerChanged(mLayer);
1123  return true;
1124 }
1125 
1135  QCPPainter *painter,
1136  bool localAntialiased,
1137  QCP::AntialiasedElement overrideElement) const {
1138  if (mParentPlot &&
1139  mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1140  painter->setAntialiasing(false);
1141  else if (mParentPlot &&
1142  mParentPlot->antialiasedElements().testFlag(overrideElement))
1143  painter->setAntialiasing(true);
1144  else
1145  painter->setAntialiasing(localAntialiased);
1146 }
1147 
1166  Q_UNUSED(parentPlot)
1167 }
1168 
1181  return QCP::iSelectOther;
1182 }
1183 
1193 QRect QCPLayerable::clipRect() const {
1194  if (mParentPlot)
1195  return mParentPlot->viewport();
1196  else
1197  return QRect();
1198 }
1199 
1233 void QCPLayerable::selectEvent(QMouseEvent *event,
1234  bool additive,
1235  const QVariant &details,
1236  bool *selectionStateChanged) {
1237  Q_UNUSED(event)
1238  Q_UNUSED(additive)
1239  Q_UNUSED(details)
1240  Q_UNUSED(selectionStateChanged)
1241 }
1242 
1255 void QCPLayerable::deselectEvent(bool *selectionStateChanged) {
1256  Q_UNUSED(selectionStateChanged)
1257 }
1258 
1262 
1277 const double QCPRange::minRange = 1e-280;
1278 
1286 const double QCPRange::maxRange = 1e250;
1287 
1291 QCPRange::QCPRange() : lower(0), upper(0) {}
1292 
1296 QCPRange::QCPRange(double lower, double upper) : lower(lower), upper(upper) {
1297  normalize();
1298 }
1299 
1303 double QCPRange::size() const { return upper - lower; }
1304 
1308 double QCPRange::center() const { return (upper + lower) * 0.5; }
1309 
1314 void QCPRange::normalize() {
1315  if (lower > upper) qSwap(lower, upper);
1316 }
1317 
1328 void QCPRange::expand(const QCPRange &otherRange) {
1329  if (lower > otherRange.lower) lower = otherRange.lower;
1330  if (upper < otherRange.upper) upper = otherRange.upper;
1331 }
1332 
1339 QCPRange QCPRange::expanded(const QCPRange &otherRange) const {
1340  QCPRange result = *this;
1341  result.expand(otherRange);
1342  return result;
1343 }
1344 
1360  double rangeFac = 1e-3;
1361  QCPRange sanitizedRange(lower, upper);
1362  sanitizedRange.normalize();
1363  // can't have range spanning negative and positive values in log plot, so
1364  // change range to fix it
1365  // if (qFuzzyCompare(sanitizedRange.lower+1, 1) &&
1366  // !qFuzzyCompare(sanitizedRange.upper+1, 1))
1367  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0) {
1368  // case lower is 0
1369  if (rangeFac < sanitizedRange.upper * rangeFac)
1370  sanitizedRange.lower = rangeFac;
1371  else
1372  sanitizedRange.lower = sanitizedRange.upper * rangeFac;
1373  } // else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1374  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0) {
1375  // case upper is 0
1376  if (-rangeFac > sanitizedRange.lower * rangeFac)
1377  sanitizedRange.upper = -rangeFac;
1378  else
1379  sanitizedRange.upper = sanitizedRange.lower * rangeFac;
1380  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0) {
1381  // find out whether negative or positive interval is wider to decide
1382  // which sign domain will be chosen
1383  if (-sanitizedRange.lower > sanitizedRange.upper) {
1384  // negative is wider, do same as in case upper is 0
1385  if (-rangeFac > sanitizedRange.lower * rangeFac)
1386  sanitizedRange.upper = -rangeFac;
1387  else
1388  sanitizedRange.upper = sanitizedRange.lower * rangeFac;
1389  } else {
1390  // positive is wider, do same as in case lower is 0
1391  if (rangeFac < sanitizedRange.upper * rangeFac)
1392  sanitizedRange.lower = rangeFac;
1393  else
1394  sanitizedRange.lower = sanitizedRange.upper * rangeFac;
1395  }
1396  }
1397  // due to normalization, case lower>0 && upper<0 should never occur, because
1398  // that implies upper<lower
1399  return sanitizedRange;
1400 }
1401 
1407  QCPRange sanitizedRange(lower, upper);
1408  sanitizedRange.normalize();
1409  return sanitizedRange;
1410 }
1411 
1415 bool QCPRange::contains(double value) const {
1416  return value >= lower && value <= upper;
1417 }
1418 
1427 bool QCPRange::validRange(double lower, double upper) {
1428  /*
1429  return (lower > -maxRange &&
1430  upper < maxRange &&
1431  qAbs(lower-upper) > minRange &&
1432  (lower < -minRange || lower > minRange) &&
1433  (upper < -minRange || upper > minRange));
1434  */
1435  return (lower > -maxRange && upper < maxRange &&
1436  qAbs(lower - upper) > minRange && qAbs(lower - upper) < maxRange);
1437 }
1438 
1448 bool QCPRange::validRange(const QCPRange &range) {
1449  /*
1450  return (range.lower > -maxRange &&
1451  range.upper < maxRange &&
1452  qAbs(range.lower-range.upper) > minRange &&
1453  qAbs(range.lower-range.upper) < maxRange &&
1454  (range.lower < -minRange || range.lower > minRange) &&
1455  (range.upper < -minRange || range.upper > minRange));
1456  */
1457  return (range.lower > -maxRange && range.upper < maxRange &&
1458  qAbs(range.lower - range.upper) > minRange &&
1459  qAbs(range.lower - range.upper) < maxRange);
1460 }
1461 
1465 
1501 /* start documentation of inline functions */
1502 
1510 /* end documentation of inline functions */
1511 
1516  : QObject(parentPlot), mParentPlot(parentPlot) {
1517  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement *>());
1518  mChildren.insert(QCP::msRight, QList<QCPLayoutElement *>());
1519  mChildren.insert(QCP::msTop, QList<QCPLayoutElement *>());
1520  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement *>());
1521 }
1522 
1524 
1529 bool QCPMarginGroup::isEmpty() const {
1530  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *>> it(mChildren);
1531  while (it.hasNext()) {
1532  it.next();
1533  if (!it.value().isEmpty()) return false;
1534  }
1535  return true;
1536 }
1537 
1543 void QCPMarginGroup::clear() {
1544  // make all children remove themselves from this margin group:
1545  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *>> it(mChildren);
1546  while (it.hasNext()) {
1547  it.next();
1548  const QList<QCPLayoutElement *> elements = it.value();
1549  for (int i = elements.size() - 1; i >= 0; --i)
1550  elements.at(i)->setMarginGroup(
1551  it.key(),
1552  0); // removes itself from mChildren via removeChild
1553  }
1554 }
1555 
1568  // query all automatic margins of the layout elements in this margin group
1569  // side and find maximum:
1570  int result = 0;
1571  const QList<QCPLayoutElement *> elements = mChildren.value(side);
1572  for (int i = 0; i < elements.size(); ++i) {
1573  if (!elements.at(i)->autoMargins().testFlag(side)) continue;
1574  int m = qMax(
1575  elements.at(i)->calculateAutoMargin(side),
1576  QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1577  if (m > result) result = m;
1578  }
1579  return result;
1580 }
1581 
1590  if (!mChildren[side].contains(element))
1591  mChildren[side].append(element);
1592  else
1593  qDebug() << Q_FUNC_INFO
1594  << "element is already child of this margin group side"
1595  << reinterpret_cast<quintptr>(element);
1596 }
1597 
1606  QCPLayoutElement *element) {
1607  if (!mChildren[side].removeOne(element))
1608  qDebug() << Q_FUNC_INFO
1609  << "element is not child of this margin group side"
1610  << reinterpret_cast<quintptr>(element);
1611 }
1612 
1616 
1649 /* start documentation of inline functions */
1650 
1702 /* end documentation of inline functions */
1703 
1708  : QCPLayerable(parentPlot), // parenthood is changed as soon as layout
1709  // element gets inserted into a layout (except
1710  // for top level layout)
1711  mParentLayout(0),
1712  mMinimumSize(),
1713  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1714  mRect(0, 0, 0, 0),
1715  mOuterRect(0, 0, 0, 0),
1716  mMargins(0, 0, 0, 0),
1717  mMinimumMargins(0, 0, 0, 0),
1718  mAutoMargins(QCP::msAll) {}
1719 
1722  0); // unregister at margin groups, if there are any
1723  // unregister at layout:
1724  if (qobject_cast<QCPLayout *>(
1725  mParentLayout)) // the qobject_cast is just a safeguard in case
1726  // the layout forgets to call clear() in its
1727  // dtor and this dtor is called by QObject dtor
1728  mParentLayout->take(this);
1729 }
1730 
1744 void QCPLayoutElement::setOuterRect(const QRect &rect) {
1745  if (mOuterRect != rect) {
1746  mOuterRect = rect;
1747  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(),
1748  -mMargins.right(), -mMargins.bottom());
1749  }
1750 }
1751 
1764 void QCPLayoutElement::setMargins(const QMargins &margins) {
1765  if (mMargins != margins) {
1766  mMargins = margins;
1767  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(),
1768  -mMargins.right(), -mMargins.bottom());
1769  }
1770 }
1771 
1781 void QCPLayoutElement::setMinimumMargins(const QMargins &margins) {
1782  if (mMinimumMargins != margins) {
1784  }
1785 }
1786 
1799 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides) {
1800  mAutoMargins = sides;
1801 }
1802 
1814 void QCPLayoutElement::setMinimumSize(const QSize &size) {
1815  if (mMinimumSize != size) {
1816  mMinimumSize = size;
1818  }
1819 }
1820 
1826  setMinimumSize(QSize(width, height));
1827 }
1828 
1834 void QCPLayoutElement::setMaximumSize(const QSize &size) {
1835  if (mMaximumSize != size) {
1836  mMaximumSize = size;
1838  }
1839 }
1840 
1846  setMaximumSize(QSize(width, height));
1847 }
1848 
1860 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides,
1861  QCPMarginGroup *group) {
1862  QVector<QCP::MarginSide> sideVector;
1863  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1864  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1865  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1866  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1867 
1868  for (int i = 0; i < sideVector.size(); ++i) {
1869  QCP::MarginSide side = sideVector.at(i);
1870  if (marginGroup(side) != group) {
1871  QCPMarginGroup *oldGroup = marginGroup(side);
1872  if (oldGroup) // unregister at old group
1873  oldGroup->removeChild(side, this);
1874 
1875  if (!group) // if setting to 0, remove hash entry. Else set hash
1876  // entry to new group and register there
1877  {
1878  mMarginGroups.remove(side);
1879  } else // setting to a new group
1880  {
1881  mMarginGroups[side] = group;
1882  group->addChild(side, this);
1883  }
1884  }
1885  }
1886 }
1887 
1902 void QCPLayoutElement::update(UpdatePhase phase) {
1903  if (phase == upMargins) {
1904  if (mAutoMargins != QCP::msNone) {
1905  // set the margins of this layout element according to automatic
1906  // margin calculation, either directly or via a margin group:
1907  QMargins newMargins = mMargins;
1908  QList<QCP::MarginSide> allMarginSides =
1909  QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight
1910  << QCP::msTop << QCP::msBottom;
1911  foreach (QCP::MarginSide side, allMarginSides) {
1912  if (mAutoMargins.testFlag(side)) // this side's margin shall be
1913  // calculated automatically
1914  {
1915  if (mMarginGroups.contains(side))
1917  newMargins, side,
1918  mMarginGroups[side]->commonMargin(
1919  side)); // this side is part of a
1920  // margin group, so get the
1921  // margin value from that group
1922  else
1924  newMargins, side,
1926  side)); // this side is not part of a
1927  // group, so calculate the
1928  // value directly
1929  // apply minimum margin restrictions:
1930  if (QCP::getMarginValue(newMargins, side) <
1933  newMargins, side,
1935  }
1936  }
1937  setMargins(newMargins);
1938  }
1939  }
1940 }
1941 
1951 
1962 
1970 QList<QCPLayoutElement *> QCPLayoutElement::elements(bool recursive) const {
1971  Q_UNUSED(recursive)
1972  return QList<QCPLayoutElement *>();
1973 }
1974 
1987 double QCPLayoutElement::selectTest(const QPointF &pos,
1988  bool onlySelectable,
1989  QVariant *details) const {
1990  Q_UNUSED(details)
1991 
1992  if (onlySelectable) return -1;
1993 
1994  if (QRectF(mOuterRect).contains(pos)) {
1995  if (mParentPlot)
1996  return mParentPlot->selectionTolerance() * 0.99;
1997  else {
1998  qDebug() << Q_FUNC_INFO << "parent plot not defined";
1999  return -1;
2000  }
2001  } else
2002  return -1;
2003 }
2004 
2011  foreach (QCPLayoutElement *el, elements(false)) {
2012  if (!el->parentPlot()) el->initializeParentPlot(parentPlot);
2013  }
2014 }
2015 
2027  return qMax(QCP::getMarginValue(mMargins, side),
2029 }
2030 
2034 
2061 /* start documentation of pure virtual functions */
2062 
2108 /* end documentation of pure virtual functions */
2109 
2115 
2125 void QCPLayout::update(UpdatePhase phase) {
2126  QCPLayoutElement::update(phase);
2127 
2128  // set child element rects according to layout:
2129  if (phase == upLayout) updateLayout();
2130 
2131  // propagate update call to child elements:
2132  const int elCount = elementCount();
2133  for (int i = 0; i < elCount; ++i) {
2134  if (QCPLayoutElement *el = elementAt(i)) el->update(phase);
2135  }
2136 }
2137 
2138 /* inherits documentation from base class */
2139 QList<QCPLayoutElement *> QCPLayout::elements(bool recursive) const {
2140  const int c = elementCount();
2141  QList<QCPLayoutElement *> result;
2142 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2143  result.reserve(c);
2144 #endif
2145  for (int i = 0; i < c; ++i) result.append(elementAt(i));
2146  if (recursive) {
2147  for (int i = 0; i < c; ++i) {
2148  if (result.at(i)) result << result.at(i)->elements(recursive);
2149  }
2150  }
2151  return result;
2152 }
2153 
2161 void QCPLayout::simplify() {}
2162 
2174 bool QCPLayout::removeAt(int index) {
2175  if (QCPLayoutElement *el = takeAt(index)) {
2176  delete el;
2177  return true;
2178  } else
2179  return false;
2180 }
2181 
2193 bool QCPLayout::remove(QCPLayoutElement *element) {
2194  if (take(element)) {
2195  delete element;
2196  return true;
2197  } else
2198  return false;
2199 }
2200 
2207 void QCPLayout::clear() {
2208  for (int i = elementCount() - 1; i >= 0; --i) {
2209  if (elementAt(i)) removeAt(i);
2210  }
2211  simplify();
2212 }
2213 
2224 void QCPLayout::sizeConstraintsChanged() const {
2225  if (QWidget *w = qobject_cast<QWidget *>(parent()))
2226  w->updateGeometry();
2227  else if (QCPLayout *l = qobject_cast<QCPLayout *>(parent()))
2228  l->sizeConstraintsChanged();
2229 }
2230 
2244 void QCPLayout::updateLayout() {}
2245 
2261  if (el) {
2262  el->mParentLayout = this;
2263  el->setParentLayerable(this);
2264  el->setParent(this);
2265  if (!el->parentPlot()) el->initializeParentPlot(mParentPlot);
2266  } else
2267  qDebug() << Q_FUNC_INFO << "Null element passed";
2268 }
2269 
2282  if (el) {
2283  el->mParentLayout = 0;
2284  el->setParentLayerable(0);
2285  el->setParent(mParentPlot);
2286  // Note: Don't initializeParentPlot(0) here, because layout element will
2287  // stay in same parent plot
2288  } else
2289  qDebug() << Q_FUNC_INFO << "Null element passed";
2290 }
2291 
2327 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes,
2328  QVector<int> minSizes,
2329  QVector<double> stretchFactors,
2330  int totalSize) const {
2331  if (maxSizes.size() != minSizes.size() ||
2332  minSizes.size() != stretchFactors.size()) {
2333  qDebug() << Q_FUNC_INFO
2334  << "Passed vector sizes aren't equal:" << maxSizes << minSizes
2335  << stretchFactors;
2336  return QVector<int>();
2337  }
2338  if (stretchFactors.isEmpty()) return QVector<int>();
2339  int sectionCount = stretchFactors.size();
2340  QVector<double> sectionSizes(sectionCount);
2341  // if provided total size is forced smaller than total minimum size, ignore
2342  // minimum sizes (squeeze sections):
2343  int minSizeSum = 0;
2344  for (int i = 0; i < sectionCount; ++i) minSizeSum += minSizes.at(i);
2345  if (totalSize < minSizeSum) {
2346  // new stretch factors are minimum sizes and minimum sizes are set to
2347  // zero:
2348  for (int i = 0; i < sectionCount; ++i) {
2349  stretchFactors[i] = minSizes.at(i);
2350  minSizes[i] = 0;
2351  }
2352  }
2353 
2354  QList<int> minimumLockedSections;
2355  QList<int> unfinishedSections;
2356  for (int i = 0; i < sectionCount; ++i) unfinishedSections.append(i);
2357  double freeSize = totalSize;
2358 
2359  int outerIterations = 0;
2360  while (!unfinishedSections.isEmpty() &&
2361  outerIterations <
2362  sectionCount *
2363  2) // the iteration check ist just a failsafe in
2364  // case something really strange happens
2365  {
2366  ++outerIterations;
2367  int innerIterations = 0;
2368  while (!unfinishedSections.isEmpty() &&
2369  innerIterations <
2370  sectionCount *
2371  2) // the iteration check ist just a failsafe in
2372  // case something really strange happens
2373  {
2374  ++innerIterations;
2375  // find section that hits its maximum next:
2376  int nextId = -1;
2377  double nextMax = 1e12;
2378  for (int i = 0; i < unfinishedSections.size(); ++i) {
2379  int secId = unfinishedSections.at(i);
2380  double hitsMaxAt =
2381  (maxSizes.at(secId) - sectionSizes.at(secId)) /
2382  stretchFactors.at(secId);
2383  if (hitsMaxAt < nextMax) {
2384  nextMax = hitsMaxAt;
2385  nextId = secId;
2386  }
2387  }
2388  // check if that maximum is actually within the bounds of the total
2389  // size (i.e. can we stretch all remaining sections so far that the
2390  // found section actually hits its maximum, without exceeding the
2391  // total size when we add up all sections)
2392  double stretchFactorSum = 0;
2393  for (int i = 0; i < unfinishedSections.size(); ++i)
2394  stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2395  double nextMaxLimit = freeSize / stretchFactorSum;
2396  if (nextMax <
2397  nextMaxLimit) // next maximum is actually hit, move forward to
2398  // that point and fix the size of that section
2399  {
2400  for (int i = 0; i < unfinishedSections.size(); ++i) {
2401  sectionSizes[unfinishedSections.at(i)] +=
2402  nextMax * stretchFactors.at(unfinishedSections.at(
2403  i)); // increment all sections
2404  freeSize -= nextMax *
2405  stretchFactors.at(unfinishedSections.at(i));
2406  }
2407  unfinishedSections.removeOne(
2408  nextId); // exclude the section that is now at maximum
2409  // from further changes
2410  } else // next maximum isn't hit, just distribute rest of free
2411  // space on remaining sections
2412  {
2413  for (int i = 0; i < unfinishedSections.size(); ++i)
2414  sectionSizes[unfinishedSections.at(i)] +=
2415  nextMaxLimit *
2416  stretchFactors.at(unfinishedSections.at(
2417  i)); // increment all sections
2418  unfinishedSections.clear();
2419  }
2420  }
2421  if (innerIterations == sectionCount * 2)
2422  qDebug() << Q_FUNC_INFO
2423  << "Exceeded maximum expected inner iteration count, "
2424  "layouting aborted. Input was:"
2425  << maxSizes << minSizes << stretchFactors << totalSize;
2426 
2427  // now check whether the resulting section sizes violate minimum
2428  // restrictions:
2429  bool foundMinimumViolation = false;
2430  for (int i = 0; i < sectionSizes.size(); ++i) {
2431  if (minimumLockedSections.contains(i)) continue;
2432  if (sectionSizes.at(i) <
2433  minSizes.at(i)) // section violates minimum
2434  {
2435  sectionSizes[i] = minSizes.at(i); // set it to minimum
2436  foundMinimumViolation = true; // make sure we repeat the whole
2437  // optimization process
2438  minimumLockedSections.append(i);
2439  }
2440  }
2441  if (foundMinimumViolation) {
2442  freeSize = totalSize;
2443  for (int i = 0; i < sectionCount; ++i) {
2444  if (!minimumLockedSections.contains(
2445  i)) // only put sections that haven't hit their
2446  // minimum back into the pool
2447  unfinishedSections.append(i);
2448  else
2449  freeSize -= sectionSizes.at(
2450  i); // remove size of minimum locked sections from
2451  // available space in next round
2452  }
2453  // reset all section sizes to zero that are in unfinished sections
2454  // (all others have been set to their minimum):
2455  for (int i = 0; i < unfinishedSections.size(); ++i)
2456  sectionSizes[unfinishedSections.at(i)] = 0;
2457  }
2458  }
2459  if (outerIterations == sectionCount * 2)
2460  qDebug() << Q_FUNC_INFO
2461  << "Exceeded maximum expected outer iteration count, "
2462  "layouting aborted. Input was:"
2463  << maxSizes << minSizes << stretchFactors << totalSize;
2464 
2465  QVector<int> result(sectionCount);
2466  for (int i = 0; i < sectionCount; ++i)
2467  result[i] = qRound(sectionSizes.at(i));
2468  return result;
2469 }
2470 
2474 
2497 QCPLayoutGrid::QCPLayoutGrid() : mColumnSpacing(5), mRowSpacing(5) {}
2498 
2500  // clear all child layout elements. This is important because only the
2501  // specific layouts know how to handle removing elements (clear calls
2502  // virtual removeAt method to do that).
2503  clear();
2504 }
2505 
2515 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const {
2516  if (row >= 0 && row < mElements.size()) {
2517  if (column >= 0 && column < mElements.first().size()) {
2518  if (QCPLayoutElement *result = mElements.at(row).at(column))
2519  return result;
2520  else
2521  qDebug() << Q_FUNC_INFO
2522  << "Requested cell is empty. Row:" << row
2523  << "Column:" << column;
2524  } else
2525  qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row
2526  << "Column:" << column;
2527  } else
2528  qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row
2529  << "Column:" << column;
2530  return 0;
2531 }
2532 
2538 int QCPLayoutGrid::rowCount() const { return mElements.size(); }
2539 
2545 int QCPLayoutGrid::columnCount() const {
2546  if (mElements.size() > 0)
2547  return mElements.first().size();
2548  else
2549  return 0;
2550 }
2551 
2562 bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element) {
2563  if (element) {
2564  if (!hasElement(row, column)) {
2565  if (element->layout()) // remove from old layout first
2566  element->layout()->take(element);
2567  expandTo(row + 1, column + 1);
2568  mElements[row][column] = element;
2570  return true;
2571  } else
2572  qDebug() << Q_FUNC_INFO
2573  << "There is already an element in the specified "
2574  "row/column:"
2575  << row << column;
2576  } else
2577  qDebug() << Q_FUNC_INFO
2578  << "Can't add null element to row/column:" << row << column;
2579  return false;
2580 }
2581 
2588 bool QCPLayoutGrid::hasElement(int row, int column) {
2589  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2590  return mElements.at(row).at(column);
2591  else
2592  return false;
2593 }
2594 
2607 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor) {
2608  if (column >= 0 && column < columnCount()) {
2609  if (factor > 0)
2610  mColumnStretchFactors[column] = factor;
2611  else
2612  qDebug() << Q_FUNC_INFO
2613  << "Invalid stretch factor, must be positive:" << factor;
2614  } else
2615  qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2616 }
2617 
2631 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors) {
2632  if (factors.size() == mColumnStretchFactors.size()) {
2633  mColumnStretchFactors = factors;
2634  for (int i = 0; i < mColumnStretchFactors.size(); ++i) {
2635  if (mColumnStretchFactors.at(i) <= 0) {
2636  qDebug() << Q_FUNC_INFO
2637  << "Invalid stretch factor, must be positive:"
2638  << mColumnStretchFactors.at(i);
2639  mColumnStretchFactors[i] = 1;
2640  }
2641  }
2642  } else
2643  qDebug() << Q_FUNC_INFO
2644  << "Column count not equal to passed stretch factor count:"
2645  << factors;
2646 }
2647 
2660 void QCPLayoutGrid::setRowStretchFactor(int row, double factor) {
2661  if (row >= 0 && row < rowCount()) {
2662  if (factor > 0)
2663  mRowStretchFactors[row] = factor;
2664  else
2665  qDebug() << Q_FUNC_INFO
2666  << "Invalid stretch factor, must be positive:" << factor;
2667  } else
2668  qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2669 }
2670 
2684 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors) {
2685  if (factors.size() == mRowStretchFactors.size()) {
2686  mRowStretchFactors = factors;
2687  for (int i = 0; i < mRowStretchFactors.size(); ++i) {
2688  if (mRowStretchFactors.at(i) <= 0) {
2689  qDebug() << Q_FUNC_INFO
2690  << "Invalid stretch factor, must be positive:"
2691  << mRowStretchFactors.at(i);
2692  mRowStretchFactors[i] = 1;
2693  }
2694  }
2695  } else
2696  qDebug() << Q_FUNC_INFO
2697  << "Row count not equal to passed stretch factor count:"
2698  << factors;
2699 }
2700 
2706 void QCPLayoutGrid::setColumnSpacing(int pixels) { mColumnSpacing = pixels; }
2707 
2713 void QCPLayoutGrid::setRowSpacing(int pixels) { mRowSpacing = pixels; }
2714 
2730 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount) {
2731  // add rows as necessary:
2732  while (rowCount() < newRowCount) {
2733  mElements.append(QList<QCPLayoutElement *>());
2734  mRowStretchFactors.append(1);
2735  }
2736  // go through rows and expand columns as necessary:
2737  int newColCount = qMax(columnCount(), newColumnCount);
2738  for (int i = 0; i < rowCount(); ++i) {
2739  while (mElements.at(i).size() < newColCount) mElements[i].append(0);
2740  }
2741  while (mColumnStretchFactors.size() < newColCount)
2742  mColumnStretchFactors.append(1);
2743 }
2744 
2752 void QCPLayoutGrid::insertRow(int newIndex) {
2753  if (mElements.isEmpty() ||
2754  mElements.first()
2755  .isEmpty()) // if grid is completely empty, add first cell
2756  {
2757  expandTo(1, 1);
2758  return;
2759  }
2760 
2761  if (newIndex < 0) newIndex = 0;
2762  if (newIndex > rowCount()) newIndex = rowCount();
2763 
2764  mRowStretchFactors.insert(newIndex, 1);
2765  QList<QCPLayoutElement *> newRow;
2766  for (int col = 0; col < columnCount(); ++col)
2767  newRow.append((QCPLayoutElement *)0);
2768  mElements.insert(newIndex, newRow);
2769 }
2770 
2778 void QCPLayoutGrid::insertColumn(int newIndex) {
2779  if (mElements.isEmpty() ||
2780  mElements.first()
2781  .isEmpty()) // if grid is completely empty, add first cell
2782  {
2783  expandTo(1, 1);
2784  return;
2785  }
2786 
2787  if (newIndex < 0) newIndex = 0;
2788  if (newIndex > columnCount()) newIndex = columnCount();
2789 
2790  mColumnStretchFactors.insert(newIndex, 1);
2791  for (int row = 0; row < rowCount(); ++row)
2792  mElements[row].insert(newIndex, (QCPLayoutElement *)0);
2793 }
2794 
2795 /* inherits documentation from base class */
2797  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2798  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2799  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2800 
2801  int totalRowSpacing = (rowCount() - 1) * mRowSpacing;
2802  int totalColSpacing = (columnCount() - 1) * mColumnSpacing;
2803  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths,
2804  mColumnStretchFactors.toVector(),
2805  mRect.width() - totalColSpacing);
2806  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights,
2807  mRowStretchFactors.toVector(),
2808  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  if (row > 0) yOffset += rowHeights.at(row - 1) + mRowSpacing;
2814  int xOffset = mRect.left();
2815  for (int col = 0; col < columnCount(); ++col) {
2816  if (col > 0) xOffset += colWidths.at(col - 1) + mColumnSpacing;
2817  if (mElements.at(row).at(col))
2818  mElements.at(row).at(col)->setOuterRect(
2819  QRect(xOffset, yOffset, colWidths.at(col),
2820  rowHeights.at(row)));
2821  }
2822  }
2823 }
2824 
2825 /* inherits documentation from base class */
2826 int QCPLayoutGrid::elementCount() const { return rowCount() * columnCount(); }
2827 
2828 /* inherits documentation from base class */
2829 QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const {
2830  if (index >= 0 && index < elementCount())
2831  return mElements.at(index / columnCount()).at(index % columnCount());
2832  else
2833  return 0;
2834 }
2835 
2836 /* inherits documentation from base class */
2838  if (QCPLayoutElement *el = elementAt(index)) {
2839  releaseElement(el);
2840  mElements[index / columnCount()][index % columnCount()] = 0;
2841  return el;
2842  } else {
2843  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2844  return 0;
2845  }
2846 }
2847 
2848 /* inherits documentation from base class */
2849 bool QCPLayoutGrid::take(QCPLayoutElement *element) {
2850  if (element) {
2851  for (int i = 0; i < elementCount(); ++i) {
2852  if (elementAt(i) == element) {
2853  takeAt(i);
2854  return true;
2855  }
2856  }
2857  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2858  } else
2859  qDebug() << Q_FUNC_INFO << "Can't take null element";
2860  return false;
2861 }
2862 
2863 /* inherits documentation from base class */
2864 QList<QCPLayoutElement *> QCPLayoutGrid::elements(bool recursive) const {
2865  QList<QCPLayoutElement *> result;
2866  int colC = columnCount();
2867  int rowC = rowCount();
2868 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2869  result.reserve(colC * rowC);
2870 #endif
2871  for (int row = 0; row < rowC; ++row) {
2872  for (int col = 0; col < colC; ++col) {
2873  result.append(mElements.at(row).at(col));
2874  }
2875  }
2876  if (recursive) {
2877  int c = result.size();
2878  for (int i = 0; i < c; ++i) {
2879  if (result.at(i)) result << result.at(i)->elements(recursive);
2880  }
2881  }
2882  return result;
2883 }
2884 
2889 void QCPLayoutGrid::simplify() {
2890  // remove rows with only empty cells:
2891  for (int row = rowCount() - 1; row >= 0; --row) {
2892  bool hasElements = false;
2893  for (int col = 0; col < columnCount(); ++col) {
2894  if (mElements.at(row).at(col)) {
2895  hasElements = true;
2896  break;
2897  }
2898  }
2899  if (!hasElements) {
2900  mRowStretchFactors.removeAt(row);
2901  mElements.removeAt(row);
2902  if (mElements.isEmpty()) // removed last element, also remove
2903  // stretch factor (wouldn't happen below
2904  // because also columnCount changed to 0
2905  // now)
2906  mColumnStretchFactors.clear();
2907  }
2908  }
2909 
2910  // remove columns with only empty cells:
2911  for (int col = columnCount() - 1; col >= 0; --col) {
2912  bool hasElements = false;
2913  for (int row = 0; row < rowCount(); ++row) {
2914  if (mElements.at(row).at(col)) {
2915  hasElements = true;
2916  break;
2917  }
2918  }
2919  if (!hasElements) {
2920  mColumnStretchFactors.removeAt(col);
2921  for (int row = 0; row < rowCount(); ++row)
2922  mElements[row].removeAt(col);
2923  }
2924  }
2925 }
2926 
2927 /* inherits documentation from base class */
2929  QVector<int> minColWidths, minRowHeights;
2930  getMinimumRowColSizes(&minColWidths, &minRowHeights);
2931  QSize result(0, 0);
2932  for (int i = 0; i < minColWidths.size(); ++i)
2933  result.rwidth() += minColWidths.at(i);
2934  for (int i = 0; i < minRowHeights.size(); ++i)
2935  result.rheight() += minRowHeights.at(i);
2936  result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing +
2937  mMargins.left() + mMargins.right();
2938  result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing + mMargins.top() +
2939  mMargins.bottom();
2940  return result;
2941 }
2942 
2943 /* inherits documentation from base class */
2945  QVector<int> maxColWidths, maxRowHeights;
2946  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2947 
2948  QSize result(0, 0);
2949  for (int i = 0; i < maxColWidths.size(); ++i)
2950  result.setWidth(
2951  qMin(result.width() + maxColWidths.at(i), QWIDGETSIZE_MAX));
2952  for (int i = 0; i < maxRowHeights.size(); ++i)
2953  result.setHeight(
2954  qMin(result.height() + maxRowHeights.at(i), QWIDGETSIZE_MAX));
2955  result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing +
2956  mMargins.left() + mMargins.right();
2957  result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing + mMargins.top() +
2958  mMargins.bottom();
2959  return result;
2960 }
2961 
2975 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths,
2976  QVector<int> *minRowHeights) const {
2977  *minColWidths = QVector<int>(columnCount(), 0);
2978  *minRowHeights = QVector<int>(rowCount(), 0);
2979  for (int row = 0; row < rowCount(); ++row) {
2980  for (int col = 0; col < columnCount(); ++col) {
2981  if (mElements.at(row).at(col)) {
2982  QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
2983  QSize min = mElements.at(row).at(col)->minimumSize();
2984  QSize final(min.width() > 0 ? min.width() : minHint.width(),
2985  min.height() > 0 ? min.height() : minHint.height());
2986  if (minColWidths->at(col) < final.width())
2987  (*minColWidths)[col] = final.width();
2988  if (minRowHeights->at(row) < final.height())
2989  (*minRowHeights)[row] = final.height();
2990  }
2991  }
2992  }
2993 }
2994 
3008 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths,
3009  QVector<int> *maxRowHeights) const {
3010  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3011  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3012  for (int row = 0; row < rowCount(); ++row) {
3013  for (int col = 0; col < columnCount(); ++col) {
3014  if (mElements.at(row).at(col)) {
3015  QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3016  QSize max = mElements.at(row).at(col)->maximumSize();
3017  QSize final(max.width() < QWIDGETSIZE_MAX ? max.width()
3018  : maxHint.width(),
3019  max.height() < QWIDGETSIZE_MAX ? max.height()
3020  : maxHint.height());
3021  if (maxColWidths->at(col) > final.width())
3022  (*maxColWidths)[col] = final.width();
3023  if (maxRowHeights->at(row) > final.height())
3024  (*maxRowHeights)[row] = final.height();
3025  }
3026  }
3027  }
3028 }
3029 
3033 
3055 /* start documentation of inline functions */
3056 
3063 /* end documentation of inline functions */
3064 
3069 
3071  // clear all child layout elements. This is important because only the
3072  // specific layouts know how to handle removing elements (clear calls
3073  // virtual removeAt method to do that).
3074  clear();
3075 }
3076 
3081  if (elementAt(index))
3082  return mInsetPlacement.at(index);
3083  else {
3084  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3085  return ipFree;
3086  }
3087 }
3088 
3095  if (elementAt(index))
3096  return mInsetAlignment.at(index);
3097  else {
3098  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3099  return Qt::Alignment();
3100  }
3101 }
3102 
3107 QRectF QCPLayoutInset::insetRect(int index) const {
3108  if (elementAt(index))
3109  return mInsetRect.at(index);
3110  else {
3111  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3112  return QRectF();
3113  }
3114 }
3115 
3123  int index, QCPLayoutInset::InsetPlacement placement) {
3124  if (elementAt(index))
3125  mInsetPlacement[index] = placement;
3126  else
3127  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3128 }
3129 
3139 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment) {
3140  if (elementAt(index))
3141  mInsetAlignment[index] = alignment;
3142  else
3143  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3144 }
3145 
3160 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect) {
3161  if (elementAt(index))
3162  mInsetRect[index] = rect;
3163  else
3164  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3165 }
3166 
3167 /* inherits documentation from base class */
3169  for (int i = 0; i < mElements.size(); ++i) {
3170  QRect insetRect;
3171  QSize finalMinSize, finalMaxSize;
3172  QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3173  QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3174  finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0
3175  ? mElements.at(i)->minimumSize().width()
3176  : minSizeHint.width());
3177  finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0
3178  ? mElements.at(i)->minimumSize().height()
3179  : minSizeHint.height());
3180  finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() <
3181  QWIDGETSIZE_MAX
3182  ? mElements.at(i)->maximumSize().width()
3183  : maxSizeHint.width());
3184  finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() <
3185  QWIDGETSIZE_MAX
3186  ? mElements.at(i)->maximumSize().height()
3187  : maxSizeHint.height());
3188  if (mInsetPlacement.at(i) == ipFree) {
3189  insetRect =
3190  QRect(rect().x() + rect().width() * mInsetRect.at(i).x(),
3191  rect().y() + rect().height() * mInsetRect.at(i).y(),
3192  rect().width() * mInsetRect.at(i).width(),
3193  rect().height() * mInsetRect.at(i).height());
3194  if (insetRect.size().width() < finalMinSize.width())
3195  insetRect.setWidth(finalMinSize.width());
3196  if (insetRect.size().height() < finalMinSize.height())
3197  insetRect.setHeight(finalMinSize.height());
3198  if (insetRect.size().width() > finalMaxSize.width())
3199  insetRect.setWidth(finalMaxSize.width());
3200  if (insetRect.size().height() > finalMaxSize.height())
3201  insetRect.setHeight(finalMaxSize.height());
3202  } else if (mInsetPlacement.at(i) == ipBorderAligned) {
3203  insetRect.setSize(finalMinSize);
3204  Qt::Alignment al = mInsetAlignment.at(i);
3205  if (al.testFlag(Qt::AlignLeft))
3206  insetRect.moveLeft(rect().x());
3207  else if (al.testFlag(Qt::AlignRight))
3208  insetRect.moveRight(rect().x() + rect().width());
3209  else
3210  insetRect.moveLeft(rect().x() + rect().width() * 0.5 -
3211  finalMinSize.width() *
3212  0.5); // default to Qt::AlignHCenter
3213  if (al.testFlag(Qt::AlignTop))
3214  insetRect.moveTop(rect().y());
3215  else if (al.testFlag(Qt::AlignBottom))
3216  insetRect.moveBottom(rect().y() + rect().height());
3217  else
3218  insetRect.moveTop(rect().y() + rect().height() * 0.5 -
3219  finalMinSize.height() *
3220  0.5); // default to Qt::AlignVCenter
3221  }
3222  mElements.at(i)->setOuterRect(insetRect);
3223  }
3224 }
3225 
3226 /* inherits documentation from base class */
3227 int QCPLayoutInset::elementCount() const { return mElements.size(); }
3228 
3229 /* inherits documentation from base class */
3230 QCPLayoutElement *QCPLayoutInset::elementAt(int index) const {
3231  if (index >= 0 && index < mElements.size())
3232  return mElements.at(index);
3233  else
3234  return 0;
3235 }
3236 
3237 /* inherits documentation from base class */
3239  if (QCPLayoutElement *el = elementAt(index)) {
3240  releaseElement(el);
3241  mElements.removeAt(index);
3242  mInsetPlacement.removeAt(index);
3243  mInsetAlignment.removeAt(index);
3244  mInsetRect.removeAt(index);
3245  return el;
3246  } else {
3247  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3248  return 0;
3249  }
3250 }
3251 
3252 /* inherits documentation from base class */
3253 bool QCPLayoutInset::take(QCPLayoutElement *element) {
3254  if (element) {
3255  for (int i = 0; i < elementCount(); ++i) {
3256  if (elementAt(i) == element) {
3257  takeAt(i);
3258  return true;
3259  }
3260  }
3261  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3262  } else
3263  qDebug() << Q_FUNC_INFO << "Can't take null element";
3264  return false;
3265 }
3266 
3278 double QCPLayoutInset::selectTest(const QPointF &pos,
3279  bool onlySelectable,
3280  QVariant *details) const {
3281  Q_UNUSED(details)
3282  if (onlySelectable) return -1;
3283 
3284  for (int i = 0; i < mElements.size(); ++i) {
3285  // inset layout shall only return positive selectTest, if actually an
3286  // inset object is at pos else it would block the entire underlying
3287  // QCPAxisRect with its surface.
3288  if (mElements.at(i)->realVisibility() &&
3289  mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3290  return mParentPlot->selectionTolerance() * 0.99;
3291  }
3292  return -1;
3293 }
3294 
3307  Qt::Alignment alignment) {
3308  if (element) {
3309  if (element->layout()) // remove from old layout first
3310  element->layout()->take(element);
3311  mElements.append(element);
3313  mInsetAlignment.append(alignment);
3314  mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3315  adoptElement(element);
3316  } else
3317  qDebug() << Q_FUNC_INFO << "Can't add null element";
3318 }
3319 
3332 void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) {
3333  if (element) {
3334  if (element->layout()) // remove from old layout first
3335  element->layout()->take(element);
3336  mElements.append(element);
3337  mInsetPlacement.append(ipFree);
3338  mInsetAlignment.append(Qt::AlignRight | Qt::AlignTop);
3339  mInsetRect.append(rect);
3340  adoptElement(element);
3341  } else
3342  qDebug() << Q_FUNC_INFO << "Can't add null element";
3343 }
3344 
3348 
3376  : mStyle(esNone), mWidth(8), mLength(10), mInverted(false) {}
3377 
3382  double width,
3383  double length,
3384  bool inverted)
3385  : mStyle(style), mWidth(width), mLength(length), mInverted(inverted) {}
3386 
3391  mStyle = style;
3392 }
3393 
3401 void QCPLineEnding::setWidth(double width) { mWidth = width; }
3402 
3409 void QCPLineEnding::setLength(double length) { mLength = length; }
3410 
3420 void QCPLineEnding::setInverted(bool inverted) { mInverted = inverted; }
3421 
3432 double QCPLineEnding::boundingDistance() const {
3433  switch (mStyle) {
3434  case esNone:
3435  return 0;
3436 
3437  case esFlatArrow:
3438  case esSpikeArrow:
3439  case esLineArrow:
3440  case esSkewedBar:
3441  return qSqrt(mWidth * mWidth +
3442  mLength *
3443  mLength); // items that have width and length
3444 
3445  case esDisc:
3446  case esSquare:
3447  case esDiamond:
3448  case esBar:
3449  case esHalfBar:
3450  return mWidth *
3451  1.42; // items that only have a width -> width*sqrt(2)
3452  }
3453  return 0;
3454 }
3455 
3468 double QCPLineEnding::realLength() const {
3469  switch (mStyle) {
3470  case esNone:
3471  case esLineArrow:
3472  case esSkewedBar:
3473  case esBar:
3474  case esHalfBar:
3475  return 0;
3476 
3477  case esFlatArrow:
3478  return mLength;
3479 
3480  case esDisc:
3481  case esSquare:
3482  case esDiamond:
3483  return mWidth * 0.5;
3484 
3485  case esSpikeArrow:
3486  return mLength * 0.8;
3487  }
3488  return 0;
3489 }
3490 
3497  const QVector2D &pos,
3498  const QVector2D &dir) const {
3499  if (mStyle == esNone) return;
3500 
3501  QVector2D lengthVec(dir.normalized());
3502  if (lengthVec.isNull()) lengthVec = QVector2D(1, 0);
3503  QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3504  lengthVec *= (float)(mLength * (mInverted ? -1 : 1));
3505  widthVec *= (float)(mWidth * 0.5 * (mInverted ? -1 : 1));
3506 
3507  QPen penBackup = painter->pen();
3508  QBrush brushBackup = painter->brush();
3509  QPen miterPen = penBackup;
3510  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3511  QBrush brush(painter->pen().color(), Qt::SolidPattern);
3512  switch (mStyle) {
3513  case esNone:
3514  break;
3515  case esFlatArrow: {
3516  QPointF points[3] = {pos.toPointF(),
3517  (pos - lengthVec + widthVec).toPointF(),
3518  (pos - lengthVec - widthVec).toPointF()};
3519  painter->setPen(miterPen);
3520  painter->setBrush(brush);
3521  painter->drawConvexPolygon(points, 3);
3522  painter->setBrush(brushBackup);
3523  painter->setPen(penBackup);
3524  break;
3525  }
3526  case esSpikeArrow: {
3527  QPointF points[4] = {pos.toPointF(),
3528  (pos - lengthVec + widthVec).toPointF(),
3529  (pos - lengthVec * 0.8f).toPointF(),
3530  (pos - lengthVec - widthVec).toPointF()};
3531  painter->setPen(miterPen);
3532  painter->setBrush(brush);
3533  painter->drawConvexPolygon(points, 4);
3534  painter->setBrush(brushBackup);
3535  painter->setPen(penBackup);
3536  break;
3537  }
3538  case esLineArrow: {
3539  QPointF points[3] = {(pos - lengthVec + widthVec).toPointF(),
3540  pos.toPointF(),
3541  (pos - lengthVec - widthVec).toPointF()};
3542  painter->setPen(miterPen);
3543  painter->drawPolyline(points, 3);
3544  painter->setPen(penBackup);
3545  break;
3546  }
3547  case esDisc: {
3548  painter->setBrush(brush);
3549  painter->drawEllipse(pos.toPointF(), mWidth * 0.5, mWidth * 0.5);
3550  painter->setBrush(brushBackup);
3551  break;
3552  }
3553  case esSquare: {
3554  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3555  QPointF points[4] = {(pos - widthVecPerp + widthVec).toPointF(),
3556  (pos - widthVecPerp - widthVec).toPointF(),
3557  (pos + widthVecPerp - widthVec).toPointF(),
3558  (pos + widthVecPerp + widthVec).toPointF()};
3559  painter->setPen(miterPen);
3560  painter->setBrush(brush);
3561  painter->drawConvexPolygon(points, 4);
3562  painter->setBrush(brushBackup);
3563  painter->setPen(penBackup);
3564  break;
3565  }
3566  case esDiamond: {
3567  QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3568  QPointF points[4] = {(pos - widthVecPerp).toPointF(),
3569  (pos - widthVec).toPointF(),
3570  (pos + widthVecPerp).toPointF(),
3571  (pos + widthVec).toPointF()};
3572  painter->setPen(miterPen);
3573  painter->setBrush(brush);
3574  painter->drawConvexPolygon(points, 4);
3575  painter->setBrush(brushBackup);
3576  painter->setPen(penBackup);
3577  break;
3578  }
3579  case esBar: {
3580  painter->drawLine((pos + widthVec).toPointF(),
3581  (pos - widthVec).toPointF());
3582  break;
3583  }
3584  case esHalfBar: {
3585  painter->drawLine((pos + widthVec).toPointF(), pos.toPointF());
3586  break;
3587  }
3588  case esSkewedBar: {
3589  if (qFuzzyIsNull(painter->pen().widthF()) &&
3590  !painter->modes().testFlag(QCPPainter::pmNonCosmetic)) {
3591  // if drawing with cosmetic pen (perfectly thin stroke, happens
3592  // only in vector exports), draw bar exactly on tip of line
3593  painter->drawLine((pos + widthVec +
3594  lengthVec * 0.2f * (mInverted ? -1 : 1))
3595  .toPointF(),
3596  (pos - widthVec -
3597  lengthVec * 0.2f * (mInverted ? -1 : 1))
3598  .toPointF());
3599  } else {
3600  // if drawing with thick (non-cosmetic) pen, shift bar a little
3601  // in line direction to prevent line from sticking through bar
3602  // slightly
3603  painter->drawLine(
3604  (pos + widthVec +
3605  lengthVec * 0.2f * (mInverted ? -1 : 1) +
3606  dir.normalized() *
3607  qMax(1.0f, (float)painter->pen().widthF()) *
3608  0.5f)
3609  .toPointF(),
3610  (pos - widthVec -
3611  lengthVec * 0.2f * (mInverted ? -1 : 1) +
3612  dir.normalized() *
3613  qMax(1.0f, (float)painter->pen().widthF()) *
3614  0.5f)
3615  .toPointF());
3616  }
3617  break;
3618  }
3619  }
3620 }
3621 
3629  const QVector2D &pos,
3630  double angle) const {
3631  draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3632 }
3633 
3637 
3659 QCPGrid::QCPGrid(QCPAxis *parentAxis)
3660  : QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3661  mParentAxis(parentAxis) {
3662  // warning: this is called in QCPAxis constructor, so parentAxis members
3663  // should not be accessed/called
3664  setParent(parentAxis);
3665  setPen(QPen(QColor(200, 200, 200), 0, Qt::DotLine));
3666  setSubGridPen(QPen(QColor(220, 220, 220), 0, Qt::DotLine));
3667  setZeroLinePen(QPen(QColor(200, 200, 200), 0, Qt::SolidLine));
3668  setSubGridVisible(false);
3669  setAntialiased(false);
3670  setAntialiasedSubGrid(false);
3671  setAntialiasedZeroLine(false);
3672 }
3673 
3679 void QCPGrid::setSubGridVisible(bool visible) { mSubGridVisible = visible; }
3680 
3684 void QCPGrid::setAntialiasedSubGrid(bool enabled) {
3685  mAntialiasedSubGrid = enabled;
3686 }
3687 
3691 void QCPGrid::setAntialiasedZeroLine(bool enabled) {
3692  mAntialiasedZeroLine = enabled;
3693 }
3694 
3698 void QCPGrid::setPen(const QPen &pen) { mPen = pen; }
3699 
3703 void QCPGrid::setSubGridPen(const QPen &pen) { mSubGridPen = pen; }
3704 
3712 void QCPGrid::setZeroLinePen(const QPen &pen) { mZeroLinePen = pen; }
3713 
3730 }
3731 
3738 void QCPGrid::draw(QCPPainter *painter) {
3739  if (!mParentAxis) {
3740  qDebug() << Q_FUNC_INFO << "invalid parent axis";
3741  return;
3742  }
3743 
3744  if (mSubGridVisible) drawSubGridLines(painter);
3745  drawGridLines(painter);
3746 }
3747 
3754 void QCPGrid::drawGridLines(QCPPainter *painter) const {
3755  if (!mParentAxis) {
3756  qDebug() << Q_FUNC_INFO << "invalid parent axis";
3757  return;
3758  }
3759 
3760  int lowTick = mParentAxis->mLowestVisibleTick;
3761  int highTick = mParentAxis->mHighestVisibleTick;
3762  double t; // helper variable, result of coordinate-to-pixel transforms
3763  if (mParentAxis->orientation() == Qt::Horizontal) {
3764  // draw zeroline:
3765  int zeroLineIndex = -1;
3766  if (mZeroLinePen.style() != Qt::NoPen &&
3769  QCP::aeZeroLine);
3770  painter->setPen(mZeroLinePen);
3771  double epsilon = mParentAxis->range().size() *
3772  1E-6; // for comparing double to zero
3773  for (int i = lowTick; i <= highTick; ++i) {
3774  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) {
3775  zeroLineIndex = i;
3777  mParentAxis->mTickVector.at(i)); // x
3778  painter->drawLine(QLineF(t,
3780  t, mParentAxis->mAxisRect->top()));
3781  break;
3782  }
3783  }
3784  }
3785  // draw grid lines:
3787  painter->setPen(mPen);
3788  for (int i = lowTick; i <= highTick; ++i) {
3789  if (i == zeroLineIndex)
3790  continue; // don't draw a gridline on top of the zeroline
3791  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3792  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
3793  mParentAxis->mAxisRect->top()));
3794  }
3795  } else {
3796  // draw zeroline:
3797  int zeroLineIndex = -1;
3798  if (mZeroLinePen.style() != Qt::NoPen &&
3801  QCP::aeZeroLine);
3802  painter->setPen(mZeroLinePen);
3803  double epsilon = mParentAxis->mRange.size() *
3804  1E-6; // for comparing double to zero
3805  for (int i = lowTick; i <= highTick; ++i) {
3806  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) {
3807  zeroLineIndex = i;
3809  mParentAxis->mTickVector.at(i)); // y
3810  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
3812  t));
3813  break;
3814  }
3815  }
3816  }
3817  // draw grid lines:
3819  painter->setPen(mPen);
3820  for (int i = lowTick; i <= highTick; ++i) {
3821  if (i == zeroLineIndex)
3822  continue; // don't draw a gridline on top of the zeroline
3823  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3824  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
3825  mParentAxis->mAxisRect->right(), t));
3826  }
3827  }
3828 }
3829 
3836 void QCPGrid::drawSubGridLines(QCPPainter *painter) const {
3837  if (!mParentAxis) {
3838  qDebug() << Q_FUNC_INFO << "invalid parent axis";
3839  return;
3840  }
3841 
3843  double t; // helper variable, result of coordinate-to-pixel transforms
3844  painter->setPen(mSubGridPen);
3845  if (mParentAxis->orientation() == Qt::Horizontal) {
3846  for (int i = 0; i < mParentAxis->mSubTickVector.size(); ++i) {
3848  mParentAxis->mSubTickVector.at(i)); // x
3849  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
3850  mParentAxis->mAxisRect->top()));
3851  }
3852  } else {
3853  for (int i = 0; i < mParentAxis->mSubTickVector.size(); ++i) {
3855  mParentAxis->mSubTickVector.at(i)); // y
3856  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
3857  mParentAxis->mAxisRect->right(), t));
3858  }
3859  }
3860 }
3861 
3865 
3885 /* start of documentation of inline functions */
3886 
3908 /* end of documentation of inline functions */
3909 /* start of documentation of signals */
3910 
3966 /* end of documentation of signals */
3967 
3976 QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type)
3977  : QCPLayerable(parent->parentPlot(), QString(), parent),
3978  // axis base:
3979  mAxisType(type),
3980  mAxisRect(parent),
3981  mPadding(5),
3982  mOrientation(orientation(type)),
3983  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
3984  mSelectedParts(spNone),
3985  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
3986  mSelectedBasePen(QPen(Qt::blue, 2)),
3987  // axis label:
3988  mLabel(),
3989  mLabelFont(mParentPlot->font()),
3990  mSelectedLabelFont(
3991  QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
3992  mLabelColor(Qt::black),
3993  mSelectedLabelColor(Qt::blue),
3994  // tick labels:
3995  mTickLabels(true),
3996  mAutoTickLabels(true),
3997  mTickLabelType(ltNumber),
3998  mTickLabelFont(mParentPlot->font()),
3999  mSelectedTickLabelFont(QFont(mTickLabelFont.family(),
4000  mTickLabelFont.pointSize(),
4001  QFont::Bold)),
4002  mTickLabelColor(Qt::black),
4003  mSelectedTickLabelColor(Qt::blue),
4004  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
4005  mDateTimeSpec(Qt::LocalTime),
4006  mNumberPrecision(6),
4007  mNumberFormatChar('g'),
4008  mNumberBeautifulPowers(true),
4009  // ticks and subticks:
4010  mTicks(true),
4011  mTickStep(1),
4012  mSubTickCount(4),
4013  mAutoTickCount(6),
4014  mAutoTicks(true),
4015  mAutoTickStep(true),
4016  mAutoSubTicks(true),
4017  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4018  mSelectedTickPen(QPen(Qt::blue, 2)),
4019  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4020  mSelectedSubTickPen(QPen(Qt::blue, 2)),
4021  // scale and range:
4022  mRange(0, 5),
4023  mRangeReversed(false),
4024  mScaleType(stLinear),
4025  mScaleLogBase(10),
4026  mScaleLogBaseLogInv(1.0 / qLn(mScaleLogBase)),
4027  // internal members:
4028  mGrid(new QCPGrid(this)),
4029  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4030  mLowestVisibleTick(0),
4031  mHighestVisibleTick(-1),
4032  mCachedMarginValid(false),
4033  mCachedMargin(0) {
4034  mGrid->setVisible(false);
4035  setAntialiased(false);
4036  setLayer(mParentPlot
4037  ->currentLayer()); // it's actually on that layer already,
4038  // but we want it in front of the grid,
4039  // so we place it on there again
4040 
4041  if (type == atTop) {
4042  setTickLabelPadding(3);
4043  setLabelPadding(6);
4044  } else if (type == atRight) {
4045  setTickLabelPadding(7);
4046  setLabelPadding(12);
4047  } else if (type == atBottom) {
4048  setTickLabelPadding(3);
4049  setLabelPadding(3);
4050  } else if (type == atLeft) {
4051  setTickLabelPadding(5);
4052  setLabelPadding(10);
4053  }
4054 }
4055 
4057  delete mAxisPainter;
4058  delete mGrid; // delete grid here instead of via parent ~QObject for better
4059  // defined deletion order
4060 }
4061 
4062 /* No documentation as it is a property getter */
4063 int QCPAxis::tickLabelPadding() const { return mAxisPainter->tickLabelPadding; }
4064 
4065 /* No documentation as it is a property getter */
4066 double QCPAxis::tickLabelRotation() const {
4067  return mAxisPainter->tickLabelRotation;
4068 }
4069 
4070 /* No documentation as it is a property getter */
4072  return mAxisPainter->tickLabelSide;
4073 }
4074 
4075 /* No documentation as it is a property getter */
4076 QString QCPAxis::numberFormat() const {
4077  QString result;
4078  result.append(mNumberFormatChar);
4079  if (mNumberBeautifulPowers) {
4080  result.append(QLatin1Char('b'));
4081  if (mAxisPainter->numberMultiplyCross) result.append(QLatin1Char('c'));
4082  }
4083  return result;
4084 }
4085 
4086 /* No documentation as it is a property getter */
4087 int QCPAxis::tickLengthIn() const { return mAxisPainter->tickLengthIn; }
4088 
4089 /* No documentation as it is a property getter */
4090 int QCPAxis::tickLengthOut() const { return mAxisPainter->tickLengthOut; }
4091 
4092 /* No documentation as it is a property getter */
4093 int QCPAxis::subTickLengthIn() const { return mAxisPainter->subTickLengthIn; }
4094 
4095 /* No documentation as it is a property getter */
4096 int QCPAxis::subTickLengthOut() const { return mAxisPainter->subTickLengthOut; }
4097 
4098 /* No documentation as it is a property getter */
4099 int QCPAxis::labelPadding() const { return mAxisPainter->labelPadding; }
4100 
4101 /* No documentation as it is a property getter */
4102 int QCPAxis::offset() const { return mAxisPainter->offset; }
4103 
4104 /* No documentation as it is a property getter */
4105 QCPLineEnding QCPAxis::lowerEnding() const { return mAxisPainter->lowerEnding; }
4106 
4107 /* No documentation as it is a property getter */
4108 QCPLineEnding QCPAxis::upperEnding() const { return mAxisPainter->upperEnding; }
4109 
4126  if (mScaleType != type) {
4127  mScaleType = type;
4128  if (mScaleType == stLogarithmic)
4130  mCachedMarginValid = false;
4132  }
4133 }
4134 
4144 void QCPAxis::setScaleLogBase(double base) {
4145  if (base > 1) {
4146  mScaleLogBase = base;
4148  1.0 /
4149  qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4150  mCachedMarginValid = false;
4151  } else
4152  qDebug() << Q_FUNC_INFO
4153  << "Invalid logarithmic scale base (must be greater 1):"
4154  << base;
4155 }
4156 
4166 void QCPAxis::setRange(const QCPRange &range) {
4167  if (range.lower == mRange.lower && range.upper == mRange.upper) return;
4168 
4169  if (!QCPRange::validRange(range)) return;
4170  QCPRange oldRange = mRange;
4171  if (mScaleType == stLogarithmic) {
4173  } else {
4175  }
4176  mCachedMarginValid = false;
4177  emit rangeChanged(mRange);
4178  emit rangeChanged(mRange, oldRange);
4179 }
4180 
4192 void QCPAxis::setSelectableParts(const SelectableParts &selectable) {
4193  if (mSelectableParts != selectable) {
4194  mSelectableParts = selectable;
4196  }
4197 }
4198 
4217 void QCPAxis::setSelectedParts(const SelectableParts &selected) {
4218  if (mSelectedParts != selected) {
4219  mSelectedParts = selected;
4221  }
4222 }
4223 
4233 void QCPAxis::setRange(double lower, double upper) {
4234  if (lower == mRange.lower && upper == mRange.upper) return;
4235 
4236  if (!QCPRange::validRange(lower, upper)) return;
4237  QCPRange oldRange = mRange;
4238  mRange.lower = lower;
4239  mRange.upper = upper;
4240  if (mScaleType == stLogarithmic) {
4242  } else {
4244  }
4245  mCachedMarginValid = false;
4246  emit rangeChanged(mRange);
4247  emit rangeChanged(mRange, oldRange);
4248 }
4249 
4262 void QCPAxis::setRange(double position,
4263  double size,
4264  Qt::AlignmentFlag alignment) {
4265  if (alignment == Qt::AlignLeft)
4267  else if (alignment == Qt::AlignRight)
4269  else // alignment == Qt::AlignCenter
4270  setRange(position - size / 2.0, position + size / 2.0);
4271 }
4272 
4277 void QCPAxis::setRangeLower(double lower) {
4278  if (mRange.lower == lower) return;
4279 
4280  QCPRange oldRange = mRange;
4281  mRange.lower = lower;
4282  if (mScaleType == stLogarithmic) {
4284  } else {
4286  }
4287  mCachedMarginValid = false;
4288  emit rangeChanged(mRange);
4289  emit rangeChanged(mRange, oldRange);
4290 }
4291 
4296 void QCPAxis::setRangeUpper(double upper) {
4297  if (mRange.upper == upper) return;
4298 
4299  QCPRange oldRange = mRange;
4300  mRange.upper = upper;
4301  if (mScaleType == stLogarithmic) {
4303  } else {
4305  }
4306  mCachedMarginValid = false;
4307  emit rangeChanged(mRange);
4308  emit rangeChanged(mRange, oldRange);
4309 }
4310 
4321 void QCPAxis::setRangeReversed(bool reversed) {
4322  if (mRangeReversed != reversed) {
4323  mRangeReversed = reversed;
4324  mCachedMarginValid = false;
4325  }
4326 }
4327 
4345 void QCPAxis::setAutoTicks(bool on) {
4346  if (mAutoTicks != on) {
4347  mAutoTicks = on;
4348  mCachedMarginValid = false;
4349  }
4350 }
4351 
4363 void QCPAxis::setAutoTickCount(int approximateCount) {
4364  if (mAutoTickCount != approximateCount) {
4365  if (approximateCount > 0) {
4366  mAutoTickCount = approximateCount;
4367  mCachedMarginValid = false;
4368  } else
4369  qDebug() << Q_FUNC_INFO
4370  << "approximateCount must be greater than zero:"
4371  << approximateCount;
4372  }
4373 }
4374 
4393  if (mAutoTickLabels != on) {
4394  mAutoTickLabels = on;
4395  mCachedMarginValid = false;
4396  }
4397 }
4398 
4413  if (mAutoTickStep != on) {
4414  mAutoTickStep = on;
4415  mCachedMarginValid = false;
4416  }
4417 }
4418 
4430  if (mAutoSubTicks != on) {
4431  mAutoSubTicks = on;
4432  mCachedMarginValid = false;
4433  }
4434 }
4435 
4442 void QCPAxis::setTicks(bool show) {
4443  if (mTicks != show) {
4444  mTicks = show;
4445  mCachedMarginValid = false;
4446  }
4447 }
4448 
4453 void QCPAxis::setTickLabels(bool show) {
4454  if (mTickLabels != show) {
4455  mTickLabels = show;
4456  mCachedMarginValid = false;
4457  }
4458 }
4459 
4464 void QCPAxis::setTickLabelPadding(int padding) {
4465  if (mAxisPainter->tickLabelPadding != padding) {
4466  mAxisPainter->tickLabelPadding = padding;
4467  mCachedMarginValid = false;
4468  }
4469 }
4470 
4498  if (mTickLabelType != type) {
4499  mTickLabelType = type;
4500  mCachedMarginValid = false;
4501  }
4502 }
4503 
4509 void QCPAxis::setTickLabelFont(const QFont &font) {
4510  if (font != mTickLabelFont) {
4511  mTickLabelFont = font;
4512  mCachedMarginValid = false;
4513  }
4514 }
4515 
4521 void QCPAxis::setTickLabelColor(const QColor &color) {
4522  if (color != mTickLabelColor) {
4524  mCachedMarginValid = false;
4525  }
4526 }
4527 
4537 void QCPAxis::setTickLabelRotation(double degrees) {
4538  if (!qFuzzyIsNull(degrees - mAxisPainter->tickLabelRotation)) {
4539  mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4540  mCachedMarginValid = false;
4541  }
4542 }
4543 
4553 void QCPAxis::setTickLabelSide(LabelSide side) {
4554  mAxisPainter->tickLabelSide = side;
4555  mCachedMarginValid = false;
4556 }
4557 
4567 void QCPAxis::setDateTimeFormat(const QString &format) {
4568  if (mDateTimeFormat != format) {
4570  mCachedMarginValid = false;
4571  }
4572 }
4573 
4585 void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec) {
4586  mDateTimeSpec = timeSpec;
4587 }
4588 
4628 void QCPAxis::setNumberFormat(const QString &formatCode) {
4629  if (formatCode.isEmpty()) {
4630  qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4631  return;
4632  }
4633  mCachedMarginValid = false;
4634 
4635  // interpret first char as number format char:
4636  QString allowedFormatChars(QLatin1String("eEfgG"));
4637  if (allowedFormatChars.contains(formatCode.at(0))) {
4638  mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
4639  } else {
4640  qDebug() << Q_FUNC_INFO
4641  << "Invalid number format code (first char not in 'eEfgG'):"
4642  << formatCode;
4643  return;
4644  }
4645  if (formatCode.length() < 2) {
4646  mNumberBeautifulPowers = false;
4647  mAxisPainter->numberMultiplyCross = false;
4648  return;
4649  }
4650 
4651  // interpret second char as indicator for beautiful decimal powers:
4652  if (formatCode.at(1) == QLatin1Char('b') &&
4653  (mNumberFormatChar == QLatin1Char('e') ||
4654  mNumberFormatChar == QLatin1Char('g'))) {
4655  mNumberBeautifulPowers = true;
4656  } else {
4657  qDebug() << Q_FUNC_INFO
4658  << "Invalid number format code (second char not 'b' or first "
4659  "char neither 'e' nor 'g'):"
4660  << formatCode;
4661  return;
4662  }
4663  if (formatCode.length() < 3) {
4664  mAxisPainter->numberMultiplyCross = false;
4665  return;
4666  }
4667 
4668  // interpret third char as indicator for dot or cross multiplication symbol:
4669  if (formatCode.at(2) == QLatin1Char('c')) {
4670  mAxisPainter->numberMultiplyCross = true;
4671  } else if (formatCode.at(2) == QLatin1Char('d')) {
4672  mAxisPainter->numberMultiplyCross = false;
4673  } else {
4674  qDebug() << Q_FUNC_INFO
4675  << "Invalid number format code (third char neither 'c' nor "
4676  "'d'):"
4677  << formatCode;
4678  return;
4679  }
4680 }
4681 
4694 void QCPAxis::setNumberPrecision(int precision) {
4695  if (mNumberPrecision != precision) {
4696  mNumberPrecision = precision;
4697  mCachedMarginValid = false;
4698  }
4699 }
4700 
4706 void QCPAxis::setTickStep(double step) {
4707  if (mTickStep != step) {
4708  mTickStep = step;
4709  mCachedMarginValid = false;
4710  }
4711 }
4712 
4729 void QCPAxis::setTickVector(const QVector<double> &vec) {
4730  // don't check whether mTickVector != vec here, because it takes longer than
4731  // we would save
4732  mTickVector = vec;
4733  mCachedMarginValid = false;
4734 }
4735 
4748 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec) {
4749  // don't check whether mTickVectorLabels != vec here, because it takes
4750  // longer than we would save
4751  mTickVectorLabels = vec;
4752  mCachedMarginValid = false;
4753 }
4754 
4764 void QCPAxis::setTickLength(int inside, int outside) {
4765  setTickLengthIn(inside);
4766  setTickLengthOut(outside);
4767 }
4768 
4775 void QCPAxis::setTickLengthIn(int inside) {
4776  if (mAxisPainter->tickLengthIn != inside) {
4777  mAxisPainter->tickLengthIn = inside;
4778  }
4779 }
4780 
4789 void QCPAxis::setTickLengthOut(int outside) {
4790  if (mAxisPainter->tickLengthOut != outside) {
4791  mAxisPainter->tickLengthOut = outside;
4793  false; // only outside tick length can change margin
4794  }
4795 }
4796 
4809 
4819 void QCPAxis::setSubTickLength(int inside, int outside) {
4820  setSubTickLengthIn(inside);
4821  setSubTickLengthOut(outside);
4822 }
4823 
4830 void QCPAxis::setSubTickLengthIn(int inside) {
4831  if (mAxisPainter->subTickLengthIn != inside) {
4832  mAxisPainter->subTickLengthIn = inside;
4833  }
4834 }
4835 
4844 void QCPAxis::setSubTickLengthOut(int outside) {
4845  if (mAxisPainter->subTickLengthOut != outside) {
4846  mAxisPainter->subTickLengthOut = outside;
4848  false; // only outside tick length can change margin
4849  }
4850 }
4851 
4857 void QCPAxis::setBasePen(const QPen &pen) { mBasePen = pen; }
4858 
4864 void QCPAxis::setTickPen(const QPen &pen) { mTickPen = pen; }
4865 
4871 void QCPAxis::setSubTickPen(const QPen &pen) { mSubTickPen = pen; }
4872 
4878 void QCPAxis::setLabelFont(const QFont &font) {
4879  if (mLabelFont != font) {
4880  mLabelFont = font;
4881  mCachedMarginValid = false;
4882  }
4883 }
4884 
4890 void QCPAxis::setLabelColor(const QColor &color) { mLabelColor = color; }
4891 
4897 void QCPAxis::setLabel(const QString &str) {
4898  if (mLabel != str) {
4899  mLabel = str;
4900  mCachedMarginValid = false;
4901  }
4902 }
4903 
4909 void QCPAxis::setLabelPadding(int padding) {
4910  if (mAxisPainter->labelPadding != padding) {
4911  mAxisPainter->labelPadding = padding;
4912  mCachedMarginValid = false;
4913  }
4914 }
4915 
4927 void QCPAxis::setPadding(int padding) {
4928  if (mPadding != padding) {
4929  mPadding = padding;
4930  mCachedMarginValid = false;
4931  }
4932 }
4933 
4942 void QCPAxis::setOffset(int offset) { mAxisPainter->offset = offset; }
4943 
4950 void QCPAxis::setSelectedTickLabelFont(const QFont &font) {
4951  if (font != mSelectedTickLabelFont) {
4952  mSelectedTickLabelFont = font;
4953  // don't set mCachedMarginValid to false here because margin calculation
4954  // is always done with non-selected fonts
4955  }
4956 }
4957 
4964 void QCPAxis::setSelectedLabelFont(const QFont &font) {
4965  mSelectedLabelFont = font;
4966  // don't set mCachedMarginValid to false here because margin calculation is
4967  // always done with non-selected fonts
4968 }
4969 
4976 void QCPAxis::setSelectedTickLabelColor(const QColor &color) {
4977  if (color != mSelectedTickLabelColor) {
4979  }
4980 }
4981 
4988 void QCPAxis::setSelectedLabelColor(const QColor &color) {
4990 }
4991 
4998 void QCPAxis::setSelectedBasePen(const QPen &pen) { mSelectedBasePen = pen; }
4999 
5006 void QCPAxis::setSelectedTickPen(const QPen &pen) { mSelectedTickPen = pen; }
5007 
5014 void QCPAxis::setSelectedSubTickPen(const QPen &pen) {
5015  mSelectedSubTickPen = pen;
5016 }
5017 
5028 void QCPAxis::setLowerEnding(const QCPLineEnding &ending) {
5029  mAxisPainter->lowerEnding = ending;
5030 }
5031 
5042 void QCPAxis::setUpperEnding(const QCPLineEnding &ending) {
5043  mAxisPainter->upperEnding = ending;
5044 }
5045 
5054 void QCPAxis::moveRange(double diff) {
5055  QCPRange oldRange = mRange;
5056  if (mScaleType == stLinear) {
5057  mRange.lower += diff;
5058  mRange.upper += diff;
5059  } else // mScaleType == stLogarithmic
5060  {
5061  mRange.lower *= diff;
5062  mRange.upper *= diff;
5063  }
5064  mCachedMarginValid = false;
5065  emit rangeChanged(mRange);
5066  emit rangeChanged(mRange, oldRange);
5067 }
5068 
5076 void QCPAxis::scaleRange(double factor, double center) {
5077  QCPRange oldRange = mRange;
5078  if (mScaleType == stLinear) {
5079  QCPRange newRange;
5080  newRange.lower = (mRange.lower - center) * factor + center;
5081  newRange.upper = (mRange.upper - center) * factor + center;
5082  if (QCPRange::validRange(newRange))
5083  mRange = newRange.sanitizedForLinScale();
5084  } else // mScaleType == stLogarithmic
5085  {
5086  if ((mRange.upper < 0 && center < 0) ||
5087  (mRange.upper > 0 &&
5088  center > 0)) // make sure center has same sign as range
5089  {
5090  QCPRange newRange;
5091  newRange.lower = qPow(mRange.lower / center, factor) * center;
5092  newRange.upper = qPow(mRange.upper / center, factor) * center;
5093  if (QCPRange::validRange(newRange))
5094  mRange = newRange.sanitizedForLogScale();
5095  } else
5096  qDebug() << Q_FUNC_INFO
5097  << "Center of scaling operation doesn't lie in same "
5098  "logarithmic sign domain as range:"
5099  << center;
5100  }
5101  mCachedMarginValid = false;
5102  emit rangeChanged(mRange);
5103  emit rangeChanged(mRange, oldRange);
5104 }
5105 
5120 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio) {
5121  int otherPixelSize, ownPixelSize;
5122 
5123  if (otherAxis->orientation() == Qt::Horizontal)
5124  otherPixelSize = otherAxis->axisRect()->width();
5125  else
5126  otherPixelSize = otherAxis->axisRect()->height();
5127 
5128  if (orientation() == Qt::Horizontal)
5129  ownPixelSize = axisRect()->width();
5130  else
5131  ownPixelSize = axisRect()->height();
5132 
5133  double newRangeSize = ratio * otherAxis->range().size() * ownPixelSize /
5134  (double)otherPixelSize;
5135  setRange(range().center(), newRangeSize, Qt::AlignCenter);
5136 }
5137 
5144 void QCPAxis::rescale(bool onlyVisiblePlottables) {
5145  QList<QCPAbstractPlottable *> p = plottables();
5146  QCPRange newRange;
5147  bool haveRange = false;
5148  for (int i = 0; i < p.size(); ++i) {
5149  if (!p.at(i)->realVisibility() && onlyVisiblePlottables) continue;
5150  QCPRange plottableRange;
5151  bool currentFoundRange;
5154  if (mScaleType == stLogarithmic)
5155  signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative
5157  if (p.at(i)->keyAxis() == this)
5158  plottableRange =
5159  p.at(i)->getKeyRange(currentFoundRange, signDomain);
5160  else
5161  plottableRange =
5162  p.at(i)->getValueRange(currentFoundRange, signDomain);
5163  if (currentFoundRange) {
5164  if (!haveRange)
5165  newRange = plottableRange;
5166  else
5167  newRange.expand(plottableRange);
5168  haveRange = true;
5169  }
5170  }
5171  if (haveRange) {
5172  if (!QCPRange::validRange(
5173  newRange)) // likely due to range being zero (plottable has
5174  // only constant data in this axis dimension),
5175  // shift current range to at least center the
5176  // plottable
5177  {
5178  double center = (newRange.lower + newRange.upper) *
5179  0.5; // upper and lower should be equal anyway, but
5180  // just to make sure, incase validRange
5181  // returned false for other reason
5182  if (mScaleType == stLinear) {
5183  newRange.lower = center - mRange.size() / 2.0;
5184  newRange.upper = center + mRange.size() / 2.0;
5185  } else // mScaleType == stLogarithmic
5186  {
5187  newRange.lower = center / qSqrt(mRange.upper / mRange.lower);
5188  newRange.upper = center * qSqrt(mRange.upper / mRange.lower);
5189  }
5190  }
5191  setRange(newRange);
5192  }
5193 }
5194 
5199 double QCPAxis::pixelToCoord(double value) const {
5200  if (orientation() == Qt::Horizontal) {
5201  if (mScaleType == stLinear) {
5202  if (!mRangeReversed)
5203  return (value - mAxisRect->left()) /
5204  (double)mAxisRect->width() * mRange.size() +
5205  mRange.lower;
5206  else
5207  return -(value - mAxisRect->left()) /
5208  (double)mAxisRect->width() * mRange.size() +
5209  mRange.upper;
5210  } else // mScaleType == stLogarithmic
5211  {
5212  if (!mRangeReversed)
5213  return qPow(mRange.upper / mRange.lower,
5214  (value - mAxisRect->left()) /
5215  (double)mAxisRect->width()) *
5216  mRange.lower;
5217  else
5218  return qPow(mRange.upper / mRange.lower,
5219  (mAxisRect->left() - value) /
5220  (double)mAxisRect->width()) *
5221  mRange.upper;
5222  }
5223  } else // orientation() == Qt::Vertical
5224  {
5225  if (mScaleType == stLinear) {
5226  if (!mRangeReversed)
5227  return (mAxisRect->bottom() - value) /
5228  (double)mAxisRect->height() * mRange.size() +
5229  mRange.lower;
5230  else
5231  return -(mAxisRect->bottom() - value) /
5232  (double)mAxisRect->height() * mRange.size() +
5233  mRange.upper;
5234  } else // mScaleType == stLogarithmic
5235  {
5236  if (!mRangeReversed)
5237  return qPow(mRange.upper / mRange.lower,
5238  (mAxisRect->bottom() - value) /
5239  (double)mAxisRect->height()) *
5240  mRange.lower;
5241  else
5242  return qPow(mRange.upper / mRange.lower,
5243  (value - mAxisRect->bottom()) /
5244  (double)mAxisRect->height()) *
5245  mRange.upper;
5246  }
5247  }
5248 }
5249 
5254 double QCPAxis::coordToPixel(double value) const {
5255  if (orientation() == Qt::Horizontal) {
5256  if (mScaleType == stLinear) {
5257  if (!mRangeReversed)
5258  return (value - mRange.lower) / mRange.size() *
5259  mAxisRect->width() +
5260  mAxisRect->left();
5261  else
5262  return (mRange.upper - value) / mRange.size() *
5263  mAxisRect->width() +
5264  mAxisRect->left();
5265  } else // mScaleType == stLogarithmic
5266  {
5267  if (value >= 0 &&
5268  mRange.upper < 0) // invalid value for logarithmic scale, just
5269  // draw it outside visible range
5270  return !mRangeReversed ? mAxisRect->right() + 200
5271  : mAxisRect->left() - 200;
5272  else if (value <= 0 &&
5273  mRange.upper > 0) // invalid value for logarithmic scale,
5274  // just draw it outside visible range
5275  return !mRangeReversed ? mAxisRect->left() - 200
5276  : mAxisRect->right() + 200;
5277  else {
5278  if (!mRangeReversed)
5279  return baseLog(value / mRange.lower) /
5281  mAxisRect->width() +
5282  mAxisRect->left();
5283  else
5284  return baseLog(mRange.upper / value) /
5286  mAxisRect->width() +
5287  mAxisRect->left();
5288  }
5289  }
5290  } else // orientation() == Qt::Vertical
5291  {
5292  if (mScaleType == stLinear) {
5293  if (!mRangeReversed)
5294  return mAxisRect->bottom() - (value - mRange.lower) /
5295  mRange.size() *
5296  mAxisRect->height();
5297  else
5298  return mAxisRect->bottom() - (mRange.upper - value) /
5299  mRange.size() *
5300  mAxisRect->height();
5301  } else // mScaleType == stLogarithmic
5302  {
5303  if (value >= 0 &&
5304  mRange.upper < 0) // invalid value for logarithmic scale, just
5305  // draw it outside visible range
5306  return !mRangeReversed ? mAxisRect->top() - 200
5307  : mAxisRect->bottom() + 200;
5308  else if (value <= 0 &&
5309  mRange.upper > 0) // invalid value for logarithmic scale,
5310  // just draw it outside visible range
5311  return !mRangeReversed ? mAxisRect->bottom() + 200
5312  : mAxisRect->top() - 200;
5313  else {
5314  if (!mRangeReversed)
5315  return mAxisRect->bottom() -
5316  baseLog(value / mRange.lower) /
5318  mAxisRect->height();
5319  else
5320  return mAxisRect->bottom() -
5321  baseLog(mRange.upper / value) /
5323  mAxisRect->height();
5324  }
5325  }
5326  }
5327 }
5328 
5340 QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const {
5341  if (!mVisible) return spNone;
5342 
5343  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5344  return spAxis;
5345  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5346  return spTickLabels;
5347  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5348  return spAxisLabel;
5349  else
5350  return spNone;
5351 }
5352 
5353 /* inherits documentation from base class */
5354 double QCPAxis::selectTest(const QPointF &pos,
5355  bool onlySelectable,
5356  QVariant *details) const {
5357  if (!mParentPlot) return -1;
5358  SelectablePart part = getPartAt(pos);
5359  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5360  return -1;
5361 
5362  if (details) details->setValue(part);
5363  return mParentPlot->selectionTolerance() * 0.99;
5364 }
5365 
5373 QList<QCPAbstractPlottable *> QCPAxis::plottables() const {
5374  QList<QCPAbstractPlottable *> result;
5375  if (!mParentPlot) return result;
5376 
5377  for (int i = 0; i < mParentPlot->mPlottables.size(); ++i) {
5378  if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||
5379  mParentPlot->mPlottables.at(i)->valueAxis() == this)
5380  result.append(mParentPlot->mPlottables.at(i));
5381  }
5382  return result;
5383 }
5384 
5390 QList<QCPGraph *> QCPAxis::graphs() const {
5391  QList<QCPGraph *> result;
5392  if (!mParentPlot) return result;
5393 
5394  for (int i = 0; i < mParentPlot->mGraphs.size(); ++i) {
5395  if (mParentPlot->mGraphs.at(i)->keyAxis() == this ||
5396  mParentPlot->mGraphs.at(i)->valueAxis() == this)
5397  result.append(mParentPlot->mGraphs.at(i));
5398  }
5399  return result;
5400 }
5401 
5409 QList<QCPAbstractItem *> QCPAxis::items() const {
5410  QList<QCPAbstractItem *> result;
5411  if (!mParentPlot) return result;
5412 
5413  for (int itemId = 0; itemId < mParentPlot->mItems.size(); ++itemId) {
5414  QList<QCPItemPosition *> positions =
5415  mParentPlot->mItems.at(itemId)->positions();
5416  for (int posId = 0; posId < positions.size(); ++posId) {
5417  if (positions.at(posId)->keyAxis() == this ||
5418  positions.at(posId)->valueAxis() == this) {
5419  result.append(mParentPlot->mItems.at(itemId));
5420  break;
5421  }
5422  }
5423  }
5424  return result;
5425 }
5426 
5432  switch (side) {
5433  case QCP::msLeft:
5434  return atLeft;
5435  case QCP::msRight:
5436  return atRight;
5437  case QCP::msTop:
5438  return atTop;
5439  case QCP::msBottom:
5440  return atBottom;
5441  default:
5442  break;
5443  }
5444  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5445  return atLeft;
5446 }
5447 
5453  switch (type) {
5454  case atLeft:
5455  return atRight;
5456  break;
5457  case atRight:
5458  return atLeft;
5459  break;
5460  case atBottom:
5461  return atTop;
5462  break;
5463  case atTop:
5464  return atBottom;
5465  break;
5466  default:
5467  qDebug() << Q_FUNC_INFO << "invalid axis type";
5468  return atLeft;
5469  break;
5470  }
5471 }
5472 
5482  if (!mParentPlot) return;
5483  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0)
5484  return;
5485 
5486  // fill tick vectors, either by auto generating or by notifying user to fill
5487  // the vectors himself
5488  if (mAutoTicks) {
5490  } else {
5491  emit ticksRequest();
5492  }
5493 
5495  if (mTickVector.isEmpty()) {
5496  mSubTickVector.clear();
5497  return;
5498  }
5499 
5500  // generate subticks between ticks:
5501  mSubTickVector.resize((mTickVector.size() - 1) * mSubTickCount);
5502  if (mSubTickCount > 0) {
5503  double subTickStep = 0;
5504  double subTickPosition = 0;
5505  int subTickIndex = 0;
5506  bool done = false;
5507  int lowTick = mLowestVisibleTick > 0 ? mLowestVisibleTick - 1
5509  int highTick = mHighestVisibleTick < mTickVector.size() - 1
5510  ? mHighestVisibleTick + 1
5512  for (int i = lowTick + 1; i <= highTick; ++i) {
5513  subTickStep = (mTickVector.at(i) - mTickVector.at(i - 1)) /
5514  (double)(mSubTickCount + 1);
5515  for (int k = 1; k <= mSubTickCount; ++k) {
5516  subTickPosition = mTickVector.at(i - 1) + k * subTickStep;
5517  if (subTickPosition < mRange.lower) continue;
5518  if (subTickPosition > mRange.upper) {
5519  done = true;
5520  break;
5521  }
5522  mSubTickVector[subTickIndex] = subTickPosition;
5523  subTickIndex++;
5524  }
5525  if (done) break;
5526  }
5527  mSubTickVector.resize(subTickIndex);
5528  }
5529 
5530  // generate tick labels according to tick positions:
5531  if (mAutoTickLabels) {
5532  int vecsize = mTickVector.size();
5533  mTickVectorLabels.resize(vecsize);
5534  if (mTickLabelType == ltNumber) {
5535  for (int i = mLowestVisibleTick; i <= mHighestVisibleTick; ++i)
5536  mTickVectorLabels[i] = mParentPlot->locale().toString(
5537  mTickVector.at(i), mNumberFormatChar.toLatin1(),
5539  } else if (mTickLabelType == ltDateTime) {
5540  for (int i = mLowestVisibleTick; i <= mHighestVisibleTick; ++i) {
5541 #if QT_VERSION < \
5542  QT_VERSION_CHECK(4, 7, \
5543  0) // use fromMSecsSinceEpoch function if available,
5544  // to gain sub-second accuracy on tick labels (e.g.
5545  // for format "hh:mm:ss:zzz")
5546  mTickVectorLabels[i] = mParentPlot->locale().toString(
5547  QDateTime::fromTime_t(mTickVector.at(i))
5548  .toTimeSpec(mDateTimeSpec),
5549  mDateTimeFormat);
5550 #else
5551  mTickVectorLabels[i] = mParentPlot->locale().toString(
5552  QDateTime::fromMSecsSinceEpoch(mTickVector.at(i) * 1000)
5553  .toTimeSpec(mDateTimeSpec),
5554  mDateTimeFormat);
5555 #endif
5556  }
5557  }
5558  } else // mAutoTickLabels == false
5559  {
5560  if (mAutoTicks) // ticks generated automatically, but not ticklabels,
5561  // so emit ticksRequest here for labels
5562  {
5563  emit ticksRequest();
5564  }
5565  // make sure provided tick label vector has correct (minimal) length:
5566  if (mTickVectorLabels.size() < mTickVector.size())
5567  mTickVectorLabels.resize(mTickVector.size());
5568  }
5569 }
5570 
5583  if (mScaleType == stLinear) {
5584  if (mAutoTickStep) {
5585  // Generate tick positions according to linear scaling:
5586  mTickStep = mRange.size() /
5587  (double)(mAutoTickCount +
5588  1e-10); // mAutoTickCount ticks on average,
5589  // the small addition is to prevent
5590  // jitter on exact integers
5591  double magnitudeFactor =
5592  qPow(10.0, qFloor(qLn(mTickStep) /
5593  qLn(10.0))); // get magnitude factor e.g.
5594  // 0.01, 1, 10, 1000 etc.
5595  double tickStepMantissa = mTickStep / magnitudeFactor;
5596  if (tickStepMantissa < 5) {
5597  // round digit after decimal point to 0.5
5598  mTickStep = (int)(tickStepMantissa * 2) / 2.0 * magnitudeFactor;
5599  } else {
5600  // round to first digit in multiples of 2
5601  mTickStep =
5602  (int)(tickStepMantissa / 2.0) * 2.0 * magnitudeFactor;
5603  }
5604  }
5606  // Generate tick positions according to mTickStep:
5607  qint64 firstStep =
5608  floor(mRange.lower / mTickStep); // do not use qFloor here, or
5609  // we'll lose 64 bit precision
5610  qint64 lastStep =
5611  ceil(mRange.upper / mTickStep); // do not use qCeil here, or
5612  // we'll lose 64 bit precision
5613  int tickcount = lastStep - firstStep + 1;
5614  if (tickcount < 0) tickcount = 0;
5615  mTickVector.resize(tickcount);
5616  for (int i = 0; i < tickcount; ++i)
5617  mTickVector[i] = (firstStep + i) * mTickStep;
5618  } else // mScaleType == stLogarithmic
5619  {
5620  // Generate tick positions according to logbase scaling:
5621  if (mRange.lower > 0 && mRange.upper > 0) // positive range
5622  {
5623  double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
5624  double currentMag = lowerMag;
5625  mTickVector.clear();
5626  mTickVector.append(currentMag);
5627  while (currentMag < mRange.upper &&
5628  currentMag > 0) // currentMag might be zero for ranges
5629  // ~1e-300, just cancel in that case
5630  {
5631  currentMag *= mScaleLogBase;
5632  mTickVector.append(currentMag);
5633  }
5634  } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5635  {
5636  double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
5637  double currentMag = lowerMag;
5638  mTickVector.clear();
5639  mTickVector.append(currentMag);
5640  while (currentMag < mRange.upper &&
5641  currentMag < 0) // currentMag might be zero for ranges
5642  // ~1e-300, just cancel in that case
5643  {
5644  currentMag /= mScaleLogBase;
5645  mTickVector.append(currentMag);
5646  }
5647  } else // invalid range for logarithmic scale, because lower and upper
5648  // have different sign
5649  {
5650  mTickVector.clear();
5651  qDebug() << Q_FUNC_INFO
5652  << "Invalid range for logarithmic plot: " << mRange.lower
5653  << "-" << mRange.upper;
5654  }
5655  }
5656 }
5657 
5673 int QCPAxis::calculateAutoSubTickCount(double tickStep) const {
5674  int result = mSubTickCount; // default to current setting, if no proper
5675  // value can be found
5676 
5677  // get mantissa of tickstep:
5678  double magnitudeFactor = qPow(
5679  10.0,
5680  qFloor(qLn(tickStep) / qLn(10.0))); // get magnitude factor e.g.
5681  // 0.01, 1, 10, 1000 etc.
5682  double tickStepMantissa = tickStep / magnitudeFactor;
5683 
5684  // separate integer and fractional part of mantissa:
5685  double epsilon = 0.01;
5686  double intPartf;
5687  int intPart;
5688  double fracPart = modf(tickStepMantissa, &intPartf);
5689  intPart = intPartf;
5690 
5691  // handle cases with (almost) integer mantissa:
5692  if (fracPart < epsilon || 1.0 - fracPart < epsilon) {
5693  if (1.0 - fracPart < epsilon) ++intPart;
5694  switch (intPart) {
5695  case 1:
5696  result = 4;
5697  break; // 1.0 -> 0.2 substep
5698  case 2:
5699  result = 3;
5700  break; // 2.0 -> 0.5 substep
5701  case 3:
5702  result = 2;
5703  break; // 3.0 -> 1.0 substep
5704  case 4:
5705  result = 3;
5706  break; // 4.0 -> 1.0 substep
5707  case 5:
5708  result = 4;
5709  break; // 5.0 -> 1.0 substep
5710  case 6:
5711  result = 2;
5712  break; // 6.0 -> 2.0 substep
5713  case 7:
5714  result = 6;
5715  break; // 7.0 -> 1.0 substep
5716  case 8:
5717  result = 3;
5718  break; // 8.0 -> 2.0 substep
5719  case 9:
5720  result = 2;
5721  break; // 9.0 -> 3.0 substep
5722  }
5723  } else {
5724  // handle cases with significantly fractional mantissa:
5725  if (qAbs(fracPart - 0.5) < epsilon) // *.5 mantissa
5726  {
5727  switch (intPart) {
5728  case 1:
5729  result = 2;
5730  break; // 1.5 -> 0.5 substep
5731  case 2:
5732  result = 4;
5733  break; // 2.5 -> 0.5 substep
5734  case 3:
5735  result = 4;
5736  break; // 3.5 -> 0.7 substep
5737  case 4:
5738  result = 2;
5739  break; // 4.5 -> 1.5 substep
5740  case 5:
5741  result = 4;
5742  break; // 5.5 -> 1.1 substep (won't occur with autoTickStep
5743  // from here on)
5744  case 6:
5745  result = 4;
5746  break; // 6.5 -> 1.3 substep
5747  case 7:
5748  result = 2;
5749  break; // 7.5 -> 2.5 substep
5750  case 8:
5751  result = 4;
5752  break; // 8.5 -> 1.7 substep
5753  case 9:
5754  result = 4;
5755  break; // 9.5 -> 1.9 substep
5756  }
5757  }
5758  // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub
5759  // tick marks, leave default
5760  }
5761 
5762  return result;
5763 }
5764 
5765 /* inherits documentation from base class */
5766 void QCPAxis::selectEvent(QMouseEvent *event,
5767  bool additive,
5768  const QVariant &details,
5769  bool *selectionStateChanged) {
5770  Q_UNUSED(event)
5771  SelectablePart part = details.value<SelectablePart>();
5772  if (mSelectableParts.testFlag(part)) {
5773  SelectableParts selBefore = mSelectedParts;
5774  setSelectedParts(additive ? mSelectedParts ^ part : part);
5775  if (selectionStateChanged)
5776  *selectionStateChanged = mSelectedParts != selBefore;
5777  }
5778 }
5779 
5780 /* inherits documentation from base class */
5781 void QCPAxis::deselectEvent(bool *selectionStateChanged) {
5782  SelectableParts selBefore = mSelectedParts;
5784  if (selectionStateChanged)
5785  *selectionStateChanged = mSelectedParts != selBefore;
5786 }
5787 
5804 }
5805 
5812 void QCPAxis::draw(QCPPainter *painter) {
5813  const int lowTick = mLowestVisibleTick;
5814  const int highTick = mHighestVisibleTick;
5815  QVector<double> subTickPositions; // the final coordToPixel transformed
5816  // vector passed to QCPAxisPainter
5817  QVector<double> tickPositions; // the final coordToPixel transformed vector
5818  // passed to QCPAxisPainter
5819  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5820  tickPositions.reserve(highTick - lowTick + 1);
5821  tickLabels.reserve(highTick - lowTick + 1);
5822  subTickPositions.reserve(mSubTickVector.size());
5823 
5824  if (mTicks) {
5825  for (int i = lowTick; i <= highTick; ++i) {
5826  tickPositions.append(coordToPixel(mTickVector.at(i)));
5827  if (mTickLabels) tickLabels.append(mTickVectorLabels.at(i));
5828  }
5829 
5830  if (mSubTickCount > 0) {
5831  const int subTickCount = mSubTickVector.size();
5832  for (int i = 0; i < subTickCount;
5833  ++i) // no need to check bounds because subticks are always
5834  // only created inside current mRange
5835  subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5836  }
5837  }
5838  // transfer all properties of this axis to QCPAxisPainterPrivate which it
5839  // needs to draw the axis. Note that some axis painter properties are
5840  // already set by direct feed-through with QCPAxis setters
5841  mAxisPainter->type = mAxisType;
5842  mAxisPainter->basePen = getBasePen();
5843  mAxisPainter->labelFont = getLabelFont();
5844  mAxisPainter->labelColor = getLabelColor();
5845  mAxisPainter->label = mLabel;
5846  mAxisPainter->substituteExponent = mAutoTickLabels &&
5849  mAxisPainter->tickPen = getTickPen();
5850  mAxisPainter->subTickPen = getSubTickPen();
5851  mAxisPainter->tickLabelFont = getTickLabelFont();
5852  mAxisPainter->tickLabelColor = getTickLabelColor();
5853  mAxisPainter->axisRect = mAxisRect->rect();
5854  mAxisPainter->viewportRect = mParentPlot->viewport();
5855  mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
5856  mAxisPainter->reversedEndings = mRangeReversed;
5857  mAxisPainter->tickPositions = tickPositions;
5858  mAxisPainter->tickLabels = tickLabels;
5859  mAxisPainter->subTickPositions = subTickPositions;
5860  mAxisPainter->draw(painter);
5861 }
5862 
5881 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const {
5882  bool lowFound = false;
5883  bool highFound = false;
5884  lowIndex = 0;
5885  highIndex = -1;
5886 
5887  for (int i = 0; i < mTickVector.size(); ++i) {
5888  if (mTickVector.at(i) >= mRange.lower) {
5889  lowFound = true;
5890  lowIndex = i;
5891  break;
5892  }
5893  }
5894  for (int i = mTickVector.size() - 1; i >= 0; --i) {
5895  if (mTickVector.at(i) <= mRange.upper) {
5896  highFound = true;
5897  highIndex = i;
5898  break;
5899  }
5900  }
5901 
5902  if (!lowFound && highFound)
5903  lowIndex = highIndex + 1;
5904  else if (lowFound && !highFound)
5905  highIndex = lowIndex - 1;
5906 }
5907 
5917 double QCPAxis::baseLog(double value) const {
5918  return qLn(value) * mScaleLogBaseLogInv;
5919 }
5920 
5928 double QCPAxis::basePow(double value) const {
5929  return qPow(mScaleLogBase, value);
5930 }
5931 
5937 QPen QCPAxis::getBasePen() const {
5938  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5939 }
5940 
5946 QPen QCPAxis::getTickPen() const {
5947  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5948 }
5949 
5955 QPen QCPAxis::getSubTickPen() const {
5957 }
5958 
5964 QFont QCPAxis::getTickLabelFont() const {
5966  : mTickLabelFont;
5967 }
5968 
5974 QFont QCPAxis::getLabelFont() const {
5975  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont
5976  : mLabelFont;
5977 }
5978 
5984 QColor QCPAxis::getTickLabelColor() const {
5986  : mTickLabelColor;
5987 }
5988 
5994 QColor QCPAxis::getLabelColor() const {
5996  : mLabelColor;
5997 }
5998 
6016  if (!mVisible) // if not visible, directly return 0, don't cache 0 because
6017  // we can't react to setVisible in QCPAxis
6018  return 0;
6019 
6020  if (mCachedMarginValid) return mCachedMargin;
6021 
6022  // run through similar steps as QCPAxis::draw, and caluclate margin needed
6023  // to fit axis and its labels
6024  int margin = 0;
6025 
6026  int lowTick, highTick;
6027  visibleTickBounds(lowTick, highTick);
6028  QVector<double> tickPositions; // the final coordToPixel transformed vector
6029  // passed to QCPAxisPainter
6030  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6031  tickPositions.reserve(highTick - lowTick + 1);
6032  tickLabels.reserve(highTick - lowTick + 1);
6033  if (mTicks) {
6034  for (int i = lowTick; i <= highTick; ++i) {
6035  tickPositions.append(coordToPixel(mTickVector.at(i)));
6036  if (mTickLabels) tickLabels.append(mTickVectorLabels.at(i));
6037  }
6038  }
6039  // transfer all properties of this axis to QCPAxisPainterPrivate which it
6040  // needs to calculate the size. Note that some axis painter properties are
6041  // already set by direct feed-through with QCPAxis setters
6042  mAxisPainter->type = mAxisType;
6043  mAxisPainter->labelFont = getLabelFont();
6044  mAxisPainter->label = mLabel;
6045  mAxisPainter->tickLabelFont = mTickLabelFont;
6046  mAxisPainter->axisRect = mAxisRect->rect();
6047  mAxisPainter->viewportRect = mParentPlot->viewport();
6048  mAxisPainter->tickPositions = tickPositions;
6049  mAxisPainter->tickLabels = tickLabels;
6050  margin += mAxisPainter->size();
6051  margin += mPadding;
6052 
6053  mCachedMargin = margin;
6054  mCachedMarginValid = true;
6055  return margin;
6056 }
6057 
6058 /* inherits documentation from base class */
6060 
6064 
6082 QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot)
6083  : type(QCPAxis::atLeft),
6084  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6085  lowerEnding(QCPLineEnding::esNone),
6086  upperEnding(QCPLineEnding::esNone),
6087  labelPadding(0),
6088  tickLabelPadding(0),
6089  tickLabelRotation(0),
6090  tickLabelSide(QCPAxis::lsOutside),
6091  substituteExponent(true),
6092  numberMultiplyCross(false),
6093  tickLengthIn(5),
6094  tickLengthOut(0),
6095  subTickLengthIn(2),
6096  subTickLengthOut(0),
6097  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6098  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6099  offset(0),
6100  abbreviateDecimalPowers(false),
6101  reversedEndings(false),
6102  mParentPlot(parentPlot),
6103  mLabelCache(16) // cache at most 16 (tick) labels
6104 {}
6105 
6106 QCPAxisPainterPrivate::~QCPAxisPainterPrivate() {}
6107 
6115 void QCPAxisPainterPrivate::draw(QCPPainter *painter) {
6116  QByteArray newHash = generateLabelParameterHash();
6117  if (newHash != mLabelParameterHash) {
6118  mLabelCache.clear();
6119  mLabelParameterHash = newHash;
6120  }
6121 
6122  QPoint origin;
6123  switch (type) {
6124  case QCPAxis::atLeft:
6125  origin = axisRect.bottomLeft() + QPoint(-offset, 0);
6126  break;
6127  case QCPAxis::atRight:
6128  origin = axisRect.bottomRight() + QPoint(+offset, 0);
6129  break;
6130  case QCPAxis::atTop:
6131  origin = axisRect.topLeft() + QPoint(0, -offset);
6132  break;
6133  case QCPAxis::atBottom:
6134  origin = axisRect.bottomLeft() + QPoint(0, +offset);
6135  break;
6136  }
6137 
6138  double xCor = 0,
6139  yCor = 0; // paint system correction, for pixel exact matches
6140  // (affects baselines and ticks of top/right axes)
6141  switch (type) {
6142  case QCPAxis::atTop:
6143  yCor = -1;
6144  break;
6145  case QCPAxis::atRight:
6146  xCor = 1;
6147  break;
6148  default:
6149  break;
6150  }
6151  int margin = 0;
6152  // draw baseline:
6153  QLineF baseLine;
6154  painter->setPen(basePen);
6155  if (QCPAxis::orientation(type) == Qt::Horizontal)
6156  baseLine.setPoints(origin + QPointF(xCor, yCor),
6157  origin + QPointF(axisRect.width() + xCor, yCor));
6158  else
6159  baseLine.setPoints(origin + QPointF(xCor, yCor),
6160  origin + QPointF(xCor, -axisRect.height() + yCor));
6161  if (reversedEndings)
6162  baseLine = QLineF(baseLine.p2(),
6163  baseLine.p1()); // won't make a difference for line
6164  // itself, but for line endings later
6165  painter->drawLine(baseLine);
6166 
6167  // draw ticks:
6168  if (!tickPositions.isEmpty()) {
6169  painter->setPen(tickPen);
6170  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight)
6171  ? -1
6172  : 1; // direction of ticks ("inward" is right for
6173  // left axis and left for right axis)
6174  if (QCPAxis::orientation(type) == Qt::Horizontal) {
6175  for (int i = 0; i < tickPositions.size(); ++i)
6176  painter->drawLine(
6177  QLineF(tickPositions.at(i) + xCor,
6178  origin.y() - tickLengthOut * tickDir + yCor,
6179  tickPositions.at(i) + xCor,
6180  origin.y() + tickLengthIn * tickDir + yCor));
6181  } else {
6182  for (int i = 0; i < tickPositions.size(); ++i)
6183  painter->drawLine(
6184  QLineF(origin.x() - tickLengthOut * tickDir + xCor,
6185  tickPositions.at(i) + yCor,
6186  origin.x() + tickLengthIn * tickDir + xCor,
6187  tickPositions.at(i) + yCor));
6188  }
6189  }
6190 
6191  // draw subticks:
6192  if (!subTickPositions.isEmpty()) {
6193  painter->setPen(subTickPen);
6194  // direction of ticks ("inward" is right for left axis and left for
6195  // right axis)
6196  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight)
6197  ? -1
6198  : 1;
6199  if (QCPAxis::orientation(type) == Qt::Horizontal) {
6200  for (int i = 0; i < subTickPositions.size(); ++i)
6201  painter->drawLine(
6202  QLineF(subTickPositions.at(i) + xCor,
6203  origin.y() - subTickLengthOut * tickDir + yCor,
6204  subTickPositions.at(i) + xCor,
6205  origin.y() + subTickLengthIn * tickDir + yCor));
6206  } else {
6207  for (int i = 0; i < subTickPositions.size(); ++i)
6208  painter->drawLine(
6209  QLineF(origin.x() - subTickLengthOut * tickDir + xCor,
6210  subTickPositions.at(i) + yCor,
6211  origin.x() + subTickLengthIn * tickDir + xCor,
6212  subTickPositions.at(i) + yCor));
6213  }
6214  }
6215  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6216 
6217  // draw axis base endings:
6218  bool antialiasingBackup = painter->antialiasing();
6219  painter->setAntialiasing(true); // always want endings to be antialiased,
6220  // even if base and ticks themselves aren't
6221  painter->setBrush(QBrush(basePen.color()));
6222  QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6223  if (lowerEnding.style() != QCPLineEnding::esNone)
6224  lowerEnding.draw(painter,
6225  QVector2D(baseLine.p1()) -
6226  baseLineVector.normalized() *
6227  lowerEnding.realLength() *
6228  (lowerEnding.inverted() ? -1 : 1),
6229  -baseLineVector);
6230  if (upperEnding.style() != QCPLineEnding::esNone)
6231  upperEnding.draw(painter,
6232  QVector2D(baseLine.p2()) +
6233  baseLineVector.normalized() *
6234  upperEnding.realLength() *
6235  (upperEnding.inverted() ? -1 : 1),
6236  baseLineVector);
6237  painter->setAntialiasing(antialiasingBackup);
6238 
6239  // tick labels:
6240  QRect oldClipRect;
6241  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip
6242  // them to the axis rect
6243  {
6244  oldClipRect = painter->clipRegion().boundingRect();
6245  painter->setClipRect(axisRect);
6246  }
6247  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset
6248  // calculation of axis label
6249  if (!tickLabels.isEmpty()) {
6250  if (tickLabelSide == QCPAxis::lsOutside) margin += tickLabelPadding;
6251  painter->setFont(tickLabelFont);
6252  painter->setPen(QPen(tickLabelColor));
6253  const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6254  int distanceToAxis = margin;
6255  if (tickLabelSide == QCPAxis::lsInside)
6256  distanceToAxis =
6257  -(qMax(tickLengthIn, subTickLengthIn) + tickLabelPadding);
6258  for (int i = 0; i < maxLabelIndex; ++i)
6259  placeTickLabel(painter, tickPositions.at(i), distanceToAxis,
6260  tickLabels.at(i), &tickLabelsSize);
6261  if (tickLabelSide == QCPAxis::lsOutside)
6262  margin += (QCPAxis::orientation(type) == Qt::Horizontal)
6263  ? tickLabelsSize.height()
6264  : tickLabelsSize.width();
6265  }
6266  if (tickLabelSide == QCPAxis::lsInside) painter->setClipRect(oldClipRect);
6267 
6268  // axis label:
6269  QRect labelBounds;
6270  if (!label.isEmpty()) {
6271  margin += labelPadding;
6272  painter->setFont(labelFont);
6273  painter->setPen(QPen(labelColor));
6274  labelBounds = painter->fontMetrics().boundingRect(
6275  0, 0, 0, 0, Qt::TextDontClip, label);
6276  if (type == QCPAxis::atLeft) {
6277  QTransform oldTransform = painter->transform();
6278  painter->translate((origin.x() - margin - labelBounds.height()),
6279  origin.y());
6280  painter->rotate(-90);
6281  painter->drawText(0, 0, axisRect.height(), labelBounds.height(),
6282  Qt::TextDontClip | Qt::AlignCenter, label);
6283  painter->setTransform(oldTransform);
6284  } else if (type == QCPAxis::atRight) {
6285  QTransform oldTransform = painter->transform();
6286  painter->translate((origin.x() + margin + labelBounds.height()),
6287  origin.y() - axisRect.height());
6288  painter->rotate(90);
6289  painter->drawText(0, 0, axisRect.height(), labelBounds.height(),
6290  Qt::TextDontClip | Qt::AlignCenter, label);
6291  painter->setTransform(oldTransform);
6292  } else if (type == QCPAxis::atTop)
6293  painter->drawText(origin.x(),
6294  origin.y() - margin - labelBounds.height(),
6295  axisRect.width(), labelBounds.height(),
6296  Qt::TextDontClip | Qt::AlignCenter, label);
6297  else if (type == QCPAxis::atBottom)
6298  painter->drawText(origin.x(), origin.y() + margin, axisRect.width(),
6299  labelBounds.height(),
6300  Qt::TextDontClip | Qt::AlignCenter, label);
6301  }
6302 
6303  // set selection boxes:
6304  int selectionTolerance = 0;
6305  if (mParentPlot)
6306  selectionTolerance = mParentPlot->selectionTolerance();
6307  else
6308  qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6309  int selAxisOutSize =
6310  qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6311  int selAxisInSize = selectionTolerance;
6312  int selTickLabelSize;
6313  int selTickLabelOffset;
6314  if (tickLabelSide == QCPAxis::lsOutside) {
6315  selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal
6316  ? tickLabelsSize.height()
6317  : tickLabelsSize.width());
6318  selTickLabelOffset =
6319  qMax(tickLengthOut, subTickLengthOut) + tickLabelPadding;
6320  } else {
6321  selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal
6322  ? tickLabelsSize.height()
6323  : tickLabelsSize.width());
6324  selTickLabelOffset =
6325  -(qMax(tickLengthIn, subTickLengthIn) + tickLabelPadding);
6326  }
6327  int selLabelSize = labelBounds.height();
6328  int selLabelOffset =
6329  qMax(tickLengthOut, subTickLengthOut) +
6330  (!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside
6331  ? tickLabelPadding + selTickLabelSize
6332  : 0) +
6333  labelPadding;
6334  if (type == QCPAxis::atLeft) {
6335  mAxisSelectionBox.setCoords(origin.x() - selAxisOutSize, axisRect.top(),
6336  origin.x() + selAxisInSize,
6337  axisRect.bottom());
6338  mTickLabelsSelectionBox.setCoords(
6339  origin.x() - selTickLabelOffset - selTickLabelSize,
6340  axisRect.top(), origin.x() - selTickLabelOffset,
6341  axisRect.bottom());
6342  mLabelSelectionBox.setCoords(
6343  origin.x() - selLabelOffset - selLabelSize, axisRect.top(),
6344  origin.x() - selLabelOffset, axisRect.bottom());
6345  } else if (type == QCPAxis::atRight) {
6346  mAxisSelectionBox.setCoords(origin.x() - selAxisInSize, axisRect.top(),
6347  origin.x() + selAxisOutSize,
6348  axisRect.bottom());
6349  mTickLabelsSelectionBox.setCoords(
6350  origin.x() + selTickLabelOffset + selTickLabelSize,
6351  axisRect.top(), origin.x() + selTickLabelOffset,
6352  axisRect.bottom());
6353  mLabelSelectionBox.setCoords(
6354  origin.x() + selLabelOffset + selLabelSize, axisRect.top(),
6355  origin.x() + selLabelOffset, axisRect.bottom());
6356  } else if (type == QCPAxis::atTop) {
6357  mAxisSelectionBox.setCoords(
6358  axisRect.left(), origin.y() - selAxisOutSize, axisRect.right(),
6359  origin.y() + selAxisInSize);
6360  mTickLabelsSelectionBox.setCoords(
6361  axisRect.left(),
6362  origin.y() - selTickLabelOffset - selTickLabelSize,
6363  axisRect.right(), origin.y() - selTickLabelOffset);
6364  mLabelSelectionBox.setCoords(
6365  axisRect.left(), origin.y() - selLabelOffset - selLabelSize,
6366  axisRect.right(), origin.y() - selLabelOffset);
6367  } else if (type == QCPAxis::atBottom) {
6368  mAxisSelectionBox.setCoords(axisRect.left(), origin.y() - selAxisInSize,
6369  axisRect.right(),
6370  origin.y() + selAxisOutSize);
6371  mTickLabelsSelectionBox.setCoords(
6372  axisRect.left(),
6373  origin.y() + selTickLabelOffset + selTickLabelSize,
6374  axisRect.right(), origin.y() + selTickLabelOffset);
6375  mLabelSelectionBox.setCoords(
6376  axisRect.left(), origin.y() + selLabelOffset + selLabelSize,
6377  axisRect.right(), origin.y() + selLabelOffset);
6378  }
6379  mAxisSelectionBox = mAxisSelectionBox.normalized();
6380  mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
6381  mLabelSelectionBox = mLabelSelectionBox.normalized();
6382  // draw hitboxes for debug purposes:
6383  // painter->setBrush(Qt::NoBrush);
6384  // painter->drawRects(QVector<QRect>() << mAxisSelectionBox <<
6385  // mTickLabelsSelectionBox << mLabelSelectionBox);
6386 }
6387 
6393 int QCPAxisPainterPrivate::size() const {
6394  int result = 0;
6395 
6396  // get length of tick marks pointing outwards:
6397  if (!tickPositions.isEmpty())
6398  result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6399 
6400  // calculate size of tick labels:
6401  if (tickLabelSide == QCPAxis::lsOutside) {
6402  QSize tickLabelsSize(0, 0);
6403  if (!tickLabels.isEmpty()) {
6404  for (int i = 0; i < tickLabels.size(); ++i)
6405  getMaxTickLabelSize(tickLabelFont, tickLabels.at(i),
6406  &tickLabelsSize);
6407  result += QCPAxis::orientation(type) == Qt::Horizontal
6408  ? tickLabelsSize.height()
6409  : tickLabelsSize.width();
6410  result += tickLabelPadding;
6411  }
6412  }
6413 
6414  // calculate size of axis label (only height needed, because left/right
6415  // labels are rotated by 90 degrees):
6416  if (!label.isEmpty()) {
6417  QFontMetrics fontMetrics(labelFont);
6418  QRect bounds;
6419  bounds = fontMetrics.boundingRect(
6420  0, 0, 0, 0,
6421  Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6422  result += bounds.height() + labelPadding;
6423  }
6424 
6425  return result;
6426 }
6427 
6435 void QCPAxisPainterPrivate::clearCache() { mLabelCache.clear(); }
6436 
6445 QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const {
6446  QByteArray result;
6447  result.append(QByteArray::number(tickLabelRotation));
6448  result.append(QByteArray::number((int)tickLabelSide));
6449  result.append(QByteArray::number((int)substituteExponent));
6450  result.append(QByteArray::number((int)numberMultiplyCross));
6451  result.append(tickLabelColor.name().toLatin1() +
6452  QByteArray::number(tickLabelColor.alpha(), 16));
6453  result.append(tickLabelFont.toString().toLatin1());
6454  return result;
6455 }
6456 
6478 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter,
6479  double position,
6480  int distanceToAxis,
6481  const QString &text,
6482  QSize *tickLabelsSize) {
6483  // warning: if you change anything here, also adapt getMaxTickLabelSize()
6484  // accordingly!
6485  if (text.isEmpty()) return;
6486  QSize finalSize;
6487  QPointF labelAnchor;
6488  switch (type) {
6489  case QCPAxis::atLeft:
6490  labelAnchor = QPointF(axisRect.left() - distanceToAxis - offset,
6491  position);
6492  break;
6493  case QCPAxis::atRight:
6494  labelAnchor = QPointF(axisRect.right() + distanceToAxis + offset,
6495  position);
6496  break;
6497  case QCPAxis::atTop:
6498  labelAnchor =
6499  QPointF(position, axisRect.top() - distanceToAxis - offset);
6500  break;
6501  case QCPAxis::atBottom:
6502  labelAnchor = QPointF(position,
6503  axisRect.bottom() + distanceToAxis + offset);
6504  break;
6505  }
6506  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) &&
6507  !painter->modes().testFlag(
6508  QCPPainter::pmNoCaching)) // label caching enabled
6509  {
6510  CachedLabel *cachedLabel =
6511  mLabelCache.take(text); // attempt to get label from cache
6512  if (!cachedLabel) // no cached label existed, create it
6513  {
6514  cachedLabel = new CachedLabel;
6515  TickLabelData labelData = getTickLabelData(painter->font(), text);
6516  cachedLabel->offset = getTickLabelDrawOffset(labelData) +
6517  labelData.rotatedTotalBounds.topLeft();
6518  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6519  cachedLabel->pixmap.fill(Qt::transparent);
6520  QCPPainter cachePainter(&cachedLabel->pixmap);
6521  cachePainter.setPen(painter->pen());
6522  drawTickLabel(
6523  &cachePainter, -labelData.rotatedTotalBounds.topLeft().x(),
6524  -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6525  }
6526  // if label would be partly clipped by widget border on sides, don't
6527  // draw it (only for outside tick labels):
6528  bool labelClippedByBorder = false;
6529  if (tickLabelSide == QCPAxis::lsOutside) {
6530  if (QCPAxis::orientation(type) == Qt::Horizontal)
6531  labelClippedByBorder =
6532  labelAnchor.x() + cachedLabel->offset.x() +
6533  cachedLabel->pixmap.width() >
6534  viewportRect.right() ||
6535  labelAnchor.x() + cachedLabel->offset.x() <
6536  viewportRect.left();
6537  else
6538  labelClippedByBorder =
6539  labelAnchor.y() + cachedLabel->offset.y() +
6540  cachedLabel->pixmap.height() >
6541  viewportRect.bottom() ||
6542  labelAnchor.y() + cachedLabel->offset.y() <
6543  viewportRect.top();
6544  }
6545  if (!labelClippedByBorder) {
6546  painter->drawPixmap(labelAnchor + cachedLabel->offset,
6547  cachedLabel->pixmap);
6548  finalSize = cachedLabel->pixmap.size();
6549  }
6550  mLabelCache.insert(text,
6551  cachedLabel); // return label to cache or insert for
6552  // the first time if newly created
6553  } else // label caching disabled, draw text directly on surface:
6554  {
6555  TickLabelData labelData = getTickLabelData(painter->font(), text);
6556  QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6557  // if label would be partly clipped by widget border on sides, don't
6558  // draw it (only for outside tick labels):
6559  bool labelClippedByBorder = false;
6560  if (tickLabelSide == QCPAxis::lsOutside) {
6561  if (QCPAxis::orientation(type) == Qt::Horizontal)
6562  labelClippedByBorder =
6563  finalPosition.x() +
6564  (labelData.rotatedTotalBounds.width() +
6565  labelData.rotatedTotalBounds.left()) >
6566  viewportRect.right() ||
6567  finalPosition.x() +
6568  labelData.rotatedTotalBounds.left() <
6569  viewportRect.left();
6570  else
6571  labelClippedByBorder =
6572  finalPosition.y() +
6573  (labelData.rotatedTotalBounds.height() +
6574  labelData.rotatedTotalBounds.top()) >
6575  viewportRect.bottom() ||
6576  finalPosition.y() + labelData.rotatedTotalBounds.top() <
6577  viewportRect.top();
6578  }
6579  if (!labelClippedByBorder) {
6580  drawTickLabel(painter, finalPosition.x(), finalPosition.y(),
6581  labelData);
6582  finalSize = labelData.rotatedTotalBounds.size();
6583  }
6584  }
6585 
6586  // expand passed tickLabelsSize if current tick label is larger:
6587  if (finalSize.width() > tickLabelsSize->width())
6588  tickLabelsSize->setWidth(finalSize.width());
6589  if (finalSize.height() > tickLabelsSize->height())
6590  tickLabelsSize->setHeight(finalSize.height());
6591 }
6592 
6603 void QCPAxisPainterPrivate::drawTickLabel(
6604  QCPPainter *painter,
6605  double x,
6606  double y,
6607  const TickLabelData &labelData) const {
6608  // backup painter settings that we're about to change:
6609  QTransform oldTransform = painter->transform();
6610  QFont oldFont = painter->font();
6611 
6612  // transform painter to position/rotation:
6613  painter->translate(x, y);
6614  if (!qFuzzyIsNull(tickLabelRotation)) painter->rotate(tickLabelRotation);
6615 
6616  // draw text:
6617  if (!labelData.expPart
6618  .isEmpty()) // indicator that beautiful powers must be used
6619  {
6620  painter->setFont(labelData.baseFont);
6621  painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6622  painter->setFont(labelData.expFont);
6623  painter->drawText(labelData.baseBounds.width() + 1, 0,
6624  labelData.expBounds.width(),
6625  labelData.expBounds.height(), Qt::TextDontClip,
6626  labelData.expPart);
6627  } else {
6628  painter->setFont(labelData.baseFont);
6629  painter->drawText(0, 0, labelData.totalBounds.width(),
6630  labelData.totalBounds.height(),
6631  Qt::TextDontClip | Qt::AlignHCenter,
6632  labelData.basePart);
6633  }
6634 
6635  // reset painter settings to what it was before:
6636  painter->setTransform(oldTransform);
6637  painter->setFont(oldFont);
6638 }
6639 
6649 QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(
6650  const QFont &font, const QString &text) const {
6651  TickLabelData result;
6652 
6653  // determine whether beautiful decimal powers should be used
6654  bool useBeautifulPowers = false;
6655  int ePos = -1;
6656  if (substituteExponent) {
6657  ePos = text.indexOf(QLatin1Char('e'));
6658  if (ePos > -1) useBeautifulPowers = true;
6659  }
6660 
6661  // calculate text bounding rects and do string preparation for beautiful
6662  // decimal powers:
6663  result.baseFont = font;
6664  if (result.baseFont.pointSizeF() >
6665  0) // might return -1 if specified with setPixelSize, in that case we
6666  // can't do correction in next line
6667  result.baseFont.setPointSizeF(
6668  result.baseFont.pointSizeF() +
6669  0.05); // QFontMetrics.boundingRect has a bug for exact point
6670  // sizes that make the results oscillate due to internal
6671  // rounding
6672  if (useBeautifulPowers) {
6673  // split text into parts of number/symbol that will be drawn normally
6674  // and part that will be drawn as exponent:
6675  result.basePart = text.left(ePos);
6676  // in log scaling, we want to turn "1*10^n" into "10^n", else add
6677  // multiplication sign and decimal base:
6678  if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
6679  result.basePart = QLatin1String("10");
6680  else
6681  result.basePart += (numberMultiplyCross ? QString(QChar(215))
6682  : QString(QChar(183))) +
6683  QLatin1String("10");
6684  result.expPart = text.mid(ePos + 1);
6685  // clip "+" and leading zeros off expPart:
6686  while (result.expPart.length() > 2 &&
6687  result.expPart.at(1) ==
6688  QLatin1Char('0')) // length > 2 so we leave one zero
6689  // when numberFormatChar is 'e'
6690  result.expPart.remove(1, 1);
6691  if (!result.expPart.isEmpty() &&
6692  result.expPart.at(0) == QLatin1Char('+'))
6693  result.expPart.remove(0, 1);
6694  // prepare smaller font for exponent:
6695  result.expFont = font;
6696  if (result.expFont.pointSize() > 0)
6697  result.expFont.setPointSize(result.expFont.pointSize() * 0.75);
6698  else
6699  result.expFont.setPixelSize(result.expFont.pixelSize() * 0.75);
6700  // calculate bounding rects of base part, exponent part and total one:
6701  result.baseBounds = QFontMetrics(result.baseFont)
6702  .boundingRect(0, 0, 0, 0, Qt::TextDontClip,
6703  result.basePart);
6704  result.expBounds = QFontMetrics(result.expFont)
6705  .boundingRect(0, 0, 0, 0, Qt::TextDontClip,
6706  result.expPart);
6707  result.totalBounds = result.baseBounds.adjusted(
6708  0, 0, result.expBounds.width() + 2,
6709  0); // +2 consists of the 1 pixel spacing between base and
6710  // exponent (see drawTickLabel) and an extra pixel to
6711  // include AA
6712  } else // useBeautifulPowers == false
6713  {
6714  result.basePart = text;
6715  result.totalBounds =
6716  QFontMetrics(result.baseFont)
6717  .boundingRect(0, 0, 0, 0,
6718  Qt::TextDontClip | Qt::AlignHCenter,
6719  result.basePart);
6720  }
6721  result.totalBounds.moveTopLeft(QPoint(
6722  0,
6723  0)); // want bounding box aligned top left at origin, independent
6724  // of how it was created, to make further processing simpler
6725 
6726  // calculate possibly different bounding rect after rotation:
6727  result.rotatedTotalBounds = result.totalBounds;
6728  if (!qFuzzyIsNull(tickLabelRotation)) {
6729  QTransform transform;
6730  transform.rotate(tickLabelRotation);
6731  result.rotatedTotalBounds =
6732  transform.mapRect(result.rotatedTotalBounds);
6733  }
6734 
6735  return result;
6736 }
6737 
6749 QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(
6750  const TickLabelData &labelData) const {
6751  /*
6752  calculate label offset from base point at tick (non-trivial, for best visual
6753  appearance): short explanation for bottom axis: The anchor, i.e. the point
6754  in the label that is placed horizontally under the corresponding tick is
6755  always on the label side that is closer to the axis (e.g. the left side of
6756  the text when we're rotating clockwise). On that side, the height is halved
6757  and the resulting point is defined the anchor. This way, a 90 degree rotated
6758  text will be centered under the tick (i.e. displaced horizontally by half
6759  its height). At the same time, a 45 degree rotated text will "point toward"
6760  its tick, as is typical for rotated tick labels.
6761  */
6762  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6763  bool flip =
6764  qFuzzyCompare(qAbs(tickLabelRotation),
6765  90.0); // perfect +/-90 degree flip. Indicates
6766  // vertical label centering on vertical axes.
6767  double radians = tickLabelRotation / 180.0 * M_PI;
6768  int x = 0, y = 0;
6769  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) ||
6770  (type == QCPAxis::atRight &&
6771  tickLabelSide ==
6772  QCPAxis::lsInside)) // Anchor at right side of tick label
6773  {
6774  if (doRotation) {
6775  if (tickLabelRotation > 0) {
6776  x = -qCos(radians) * labelData.totalBounds.width();
6777  y = flip ? -labelData.totalBounds.width() / 2.0
6778  : -qSin(radians) * labelData.totalBounds.width() -
6779  qCos(radians) *
6780  labelData.totalBounds.height() /
6781  2.0;
6782  } else {
6783  x = -qCos(-radians) * labelData.totalBounds.width() -
6784  qSin(-radians) * labelData.totalBounds.height();
6785  y = flip ? +labelData.totalBounds.width() / 2.0
6786  : +qSin(-radians) * labelData.totalBounds.width() -
6787  qCos(-radians) *
6788  labelData.totalBounds.height() /
6789  2.0;
6790  }
6791  } else {
6792  x = -labelData.totalBounds.width();
6793  y = -labelData.totalBounds.height() / 2.0;
6794  }
6795  } else if ((type == QCPAxis::atRight &&
6796  tickLabelSide == QCPAxis::lsOutside) ||
6797  (type == QCPAxis::atLeft &&
6798  tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of
6799  // tick label
6800  {
6801  if (doRotation) {
6802  if (tickLabelRotation > 0) {
6803  x = +qSin(radians) * labelData.totalBounds.height();
6804  y = flip ? -labelData.totalBounds.width() / 2.0
6805  : -qCos(radians) * labelData.totalBounds.height() /
6806  2.0;
6807  } else {
6808  x = 0;
6809  y = flip ? +labelData.totalBounds.width() / 2.0
6810  : -qCos(-radians) * labelData.totalBounds.height() /
6811  2.0;
6812  }
6813  } else {
6814  x = 0;
6815  y = -labelData.totalBounds.height() / 2.0;
6816  }
6817  } else if ((type == QCPAxis::atTop &&
6818  tickLabelSide == QCPAxis::lsOutside) ||
6819  (type == QCPAxis::atBottom &&
6820  tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side
6821  // of tick label
6822  {
6823  if (doRotation) {
6824  if (tickLabelRotation > 0) {
6825  x = -qCos(radians) * labelData.totalBounds.width() +
6826  qSin(radians) * labelData.totalBounds.height() / 2.0;
6827  y = -qSin(radians) * labelData.totalBounds.width() -
6828  qCos(radians) * labelData.totalBounds.height();
6829  } else {
6830  x = -qSin(-radians) * labelData.totalBounds.height() / 2.0;
6831  y = -qCos(-radians) * labelData.totalBounds.height();
6832  }
6833  } else {
6834  x = -labelData.totalBounds.width() / 2.0;
6835  y = -labelData.totalBounds.height();
6836  }
6837  } else if ((type == QCPAxis::atBottom &&
6838  tickLabelSide == QCPAxis::lsOutside) ||
6839  (type == QCPAxis::atTop &&
6840  tickLabelSide ==
6841  QCPAxis::lsInside)) // Anchor at top side of tick label
6842  {
6843  if (doRotation) {
6844  if (tickLabelRotation > 0) {
6845  x = +qSin(radians) * labelData.totalBounds.height() / 2.0;
6846  y = 0;
6847  } else {
6848  x = -qCos(-radians) * labelData.totalBounds.width() -
6849  qSin(-radians) * labelData.totalBounds.height() / 2.0;
6850  y = +qSin(-radians) * labelData.totalBounds.width();
6851  }
6852  } else {
6853  x = -labelData.totalBounds.width() / 2.0;
6854  y = 0;
6855  }
6856  }
6857 
6858  return QPointF(x, y);
6859 }
6860 
6869 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font,
6870  const QString &text,
6871  QSize *tickLabelsSize) const {
6872  // note: this function must return the same tick label sizes as the
6873  // placeTickLabel function.
6874  QSize finalSize;
6875  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) &&
6876  mLabelCache.contains(
6877  text)) // label caching enabled and have cached label
6878  {
6879  const CachedLabel *cachedLabel = mLabelCache.object(text);
6880  finalSize = cachedLabel->pixmap.size();
6881  } else // label caching disabled or no label with this text cached:
6882  {
6883  TickLabelData labelData = getTickLabelData(font, text);
6884  finalSize = labelData.rotatedTotalBounds.size();
6885  }
6886 
6887  // expand passed tickLabelsSize if current tick label is larger:
6888  if (finalSize.width() > tickLabelsSize->width())
6889  tickLabelsSize->setWidth(finalSize.width());
6890  if (finalSize.height() > tickLabelsSize->height())
6891  tickLabelsSize->setHeight(finalSize.height());
6892 }
6893 
6897 
6976 /* start of documentation of pure virtual functions */
6977 
7033 /* end of documentation of pure virtual functions */
7034 /* start of documentation of signals */
7035 
7049 /* end of documentation of signals */
7050 
7065  : QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
7066  mName(),
7067  mAntialiasedFill(true),
7068  mAntialiasedScatters(true),
7069  mAntialiasedErrorBars(false),
7070  mPen(Qt::black),
7071  mSelectedPen(Qt::black),
7072  mBrush(Qt::NoBrush),
7073  mSelectedBrush(Qt::NoBrush),
7074  mKeyAxis(keyAxis),
7075  mValueAxis(valueAxis),
7076  mSelectable(true),
7077  mSelected(false) {
7078  if (keyAxis->parentPlot() != valueAxis->parentPlot())
7079  qDebug() << Q_FUNC_INFO
7080  << "Parent plot of keyAxis is not the same as that of "
7081  "valueAxis.";
7082  if (keyAxis->orientation() == valueAxis->orientation())
7083  qDebug() << Q_FUNC_INFO
7084  << "keyAxis and valueAxis must be orthogonal to each other.";
7085 }
7086 
7092 void QCPAbstractPlottable::setName(const QString &name) { mName = name; }
7093 
7101 void QCPAbstractPlottable::setAntialiasedFill(bool enabled) {
7102  mAntialiasedFill = enabled;
7103 }
7104 
7114  mAntialiasedScatters = enabled;
7115 }
7116 
7125  mAntialiasedErrorBars = enabled;
7126 }
7127 
7136 void QCPAbstractPlottable::setPen(const QPen &pen) { mPen = pen; }
7137 
7145  mSelectedPen = pen;
7146 }
7147 
7157 void QCPAbstractPlottable::setBrush(const QBrush &brush) { mBrush = brush; }
7158 
7165 void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush) {
7167 }
7168 
7182 void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis) { mKeyAxis = axis; }
7183 
7198 
7210  if (mSelectable != selectable) {
7213  }
7214 }
7215 
7234  if (mSelected != selected) {
7235  mSelected = selected;
7237  }
7238 }
7239 
7256 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const {
7257  rescaleKeyAxis(onlyEnlarge);
7258  rescaleValueAxis(onlyEnlarge);
7259 }
7260 
7266 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const {
7267  QCPAxis *keyAxis = mKeyAxis.data();
7268  if (!keyAxis) {
7269  qDebug() << Q_FUNC_INFO << "invalid key axis";
7270  return;
7271  }
7272 
7273  SignDomain signDomain = sdBoth;
7275  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7276 
7277  bool foundRange;
7278  QCPRange newRange = getKeyRange(foundRange, signDomain);
7279  if (foundRange) {
7280  if (onlyEnlarge) newRange.expand(keyAxis->range());
7281  if (!QCPRange::validRange(
7282  newRange)) // likely due to range being zero (plottable has
7283  // only constant data in this axis dimension),
7284  // shift current range to at least center the
7285  // plottable
7286  {
7287  double center = (newRange.lower + newRange.upper) *
7288  0.5; // upper and lower should be equal anyway, but
7289  // just to make sure, incase validRange
7290  // returned false for other reason
7291  if (keyAxis->scaleType() == QCPAxis::stLinear) {
7292  newRange.lower = center - keyAxis->range().size() / 2.0;
7293  newRange.upper = center + keyAxis->range().size() / 2.0;
7294  } else // scaleType() == stLogarithmic
7295  {
7296  newRange.lower = center / qSqrt(keyAxis->range().upper /
7297  keyAxis->range().lower);
7298  newRange.upper = center * qSqrt(keyAxis->range().upper /
7299  keyAxis->range().lower);
7300  }
7301  }
7302  keyAxis->setRange(newRange);
7303  }
7304 }
7305 
7314 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const {
7315  QCPAxis *valueAxis = mValueAxis.data();
7316  if (!valueAxis) {
7317  qDebug() << Q_FUNC_INFO << "invalid value axis";
7318  return;
7319  }
7320 
7321  SignDomain signDomain = sdBoth;
7323  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7324 
7325  bool foundRange;
7326  QCPRange newRange = getValueRange(foundRange, signDomain);
7327  if (foundRange) {
7328  if (onlyEnlarge) newRange.expand(valueAxis->range());
7329  if (!QCPRange::validRange(
7330  newRange)) // likely due to range being zero (plottable has
7331  // only constant data in this axis dimension),
7332  // shift current range to at least center the
7333  // plottable
7334  {
7335  double center = (newRange.lower + newRange.upper) *
7336  0.5; // upper and lower should be equal anyway, but
7337  // just to make sure, incase validRange
7338  // returned false for other reason
7340  newRange.lower = center - valueAxis->range().size() / 2.0;
7341  newRange.upper = center + valueAxis->range().size() / 2.0;
7342  } else // scaleType() == stLogarithmic
7343  {
7344  newRange.lower = center / qSqrt(valueAxis->range().upper /
7345  valueAxis->range().lower);
7346  newRange.upper = center * qSqrt(valueAxis->range().upper /
7347  valueAxis->range().lower);
7348  }
7349  }
7350  valueAxis->setRange(newRange);
7351  }
7352 }
7353 
7369  if (!mParentPlot || !mParentPlot->legend) return false;
7370 
7371  if (!mParentPlot->legend->hasItemWithPlottable(this)) {
7374  return true;
7375  } else
7376  return false;
7377 }
7378 
7390  if (!mParentPlot->legend) return false;
7391 
7392  if (QCPPlottableLegendItem *lip =
7394  return mParentPlot->legend->removeItem(lip);
7395  else
7396  return false;
7397 }
7398 
7399 /* inherits documentation from base class */
7400 QRect QCPAbstractPlottable::clipRect() const {
7401  if (mKeyAxis && mValueAxis)
7402  return mKeyAxis.data()->axisRect()->rect() &
7403  mValueAxis.data()->axisRect()->rect();
7404  else
7405  return QRect();
7406 }
7407 
7408 /* inherits documentation from base class */
7410  return QCP::iSelectPlottables;
7411 }
7412 
7424 void QCPAbstractPlottable::coordsToPixels(double key,
7425  double value,
7426  double &x,
7427  double &y) const {
7428  QCPAxis *keyAxis = mKeyAxis.data();
7429  QCPAxis *valueAxis = mValueAxis.data();
7430  if (!keyAxis || !valueAxis) {
7431  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
7432  return;
7433  }
7434 
7435  if (keyAxis->orientation() == Qt::Horizontal) {
7436  x = keyAxis->coordToPixel(key);
7437  y = valueAxis->coordToPixel(value);
7438  } else {
7439  y = keyAxis->coordToPixel(key);
7440  x = valueAxis->coordToPixel(value);
7441  }
7442 }
7443 
7449 const QPointF QCPAbstractPlottable::coordsToPixels(double key,
7450  double value) const {
7451  QCPAxis *keyAxis = mKeyAxis.data();
7452  QCPAxis *valueAxis = mValueAxis.data();
7453  if (!keyAxis || !valueAxis) {
7454  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
7455  return QPointF();
7456  }
7457 
7458  if (keyAxis->orientation() == Qt::Horizontal)
7459  return QPointF(keyAxis->coordToPixel(key),
7460  valueAxis->coordToPixel(value));
7461  else
7462  return QPointF(valueAxis->coordToPixel(value),
7463  keyAxis->coordToPixel(key));
7464 }
7465 
7478  double y,
7479  double &key,
7480  double &value) const {
7481  QCPAxis *keyAxis = mKeyAxis.data();
7482  QCPAxis *valueAxis = mValueAxis.data();
7483  if (!keyAxis || !valueAxis) {
7484  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
7485  return;
7486  }
7487 
7488  if (keyAxis->orientation() == Qt::Horizontal) {
7489  key = keyAxis->pixelToCoord(x);
7490  value = valueAxis->pixelToCoord(y);
7491  } else {
7492  key = keyAxis->pixelToCoord(y);
7493  value = valueAxis->pixelToCoord(x);
7494  }
7495 }
7496 
7502 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos,
7503  double &key,
7504  double &value) const {
7505  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7506 }
7507 
7514  return mSelected ? mSelectedPen : mPen;
7515 }
7516 
7523  return mSelected ? mSelectedBrush : mBrush;
7524 }
7525 
7542  QCPPainter *painter) const {
7544 }
7545 
7559  QCPPainter *painter) const {
7561 }
7562 
7576  QCPPainter *painter) const {
7578 }
7579 
7593  QCPPainter *painter) const {
7595 }
7596 
7607 double QCPAbstractPlottable::distSqrToLine(const QPointF &start,
7608  const QPointF &end,
7609  const QPointF &point) const {
7610  QVector2D a(start);
7611  QVector2D b(end);
7612  QVector2D p(point);
7613  QVector2D v(b - a);
7614 
7615  double vLengthSqr = v.lengthSquared();
7616  if (!qFuzzyIsNull(vLengthSqr)) {
7617  double mu = QVector2D::dotProduct(p - a, v) / vLengthSqr;
7618  if (mu < 0)
7619  return (a - p).lengthSquared();
7620  else if (mu > 1)
7621  return (b - p).lengthSquared();
7622  else
7623  return ((a + mu * v) - p).lengthSquared();
7624  } else
7625  return (a - p).lengthSquared();
7626 }
7627 
7628 /* inherits documentation from base class */
7629 void QCPAbstractPlottable::selectEvent(QMouseEvent *event,
7630  bool additive,
7631  const QVariant &details,
7632  bool *selectionStateChanged) {
7633  Q_UNUSED(event)
7634  Q_UNUSED(details)
7635  if (mSelectable) {
7636  bool selBefore = mSelected;
7637  setSelected(additive ? !mSelected : true);
7638  if (selectionStateChanged)
7639  *selectionStateChanged = mSelected != selBefore;
7640  }
7641 }
7642 
7643 /* inherits documentation from base class */
7644 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) {
7645  if (mSelectable) {
7646  bool selBefore = mSelected;
7647  setSelected(false);
7648  if (selectionStateChanged)
7649  *selectionStateChanged = mSelected != selBefore;
7650  }
7651 }
7652 
7656 
7679 /* start documentation of inline functions */
7680 
7692 /* end documentation of inline functions */
7693 
7701  QCPAbstractItem *parentItem,
7702  const QString name,
7703  int anchorId)
7704  : mName(name),
7705  mParentPlot(parentPlot),
7706  mParentItem(parentItem),
7707  mAnchorId(anchorId) {}
7708 
7710  // unregister as parent at children:
7711  foreach (QCPItemPosition *child, mChildrenX.values()) {
7712  if (child->parentAnchorX() == this)
7713  child->setParentAnchorX(0); // this acts back on this anchor and
7714  // child removes itself from mChildrenX
7715  }
7716  foreach (QCPItemPosition *child, mChildrenY.values()) {
7717  if (child->parentAnchorY() == this)
7718  child->setParentAnchorY(0); // this acts back on this anchor and
7719  // child removes itself from mChildrenY
7720  }
7721 }
7722 
7731 QPointF QCPItemAnchor::pixelPoint() const {
7732  if (mParentItem) {
7733  if (mAnchorId > -1) {
7735  } else {
7736  qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7737  return QPointF();
7738  }
7739  } else {
7740  qDebug() << Q_FUNC_INFO << "no parent item set";
7741  return QPointF();
7742  }
7743 }
7744 
7754  if (!mChildrenX.contains(pos))
7755  mChildrenX.insert(pos);
7756  else
7757  qDebug() << Q_FUNC_INFO << "provided pos is child already"
7758  << reinterpret_cast<quintptr>(pos);
7759 }
7760 
7768  if (!mChildrenX.remove(pos))
7769  qDebug() << Q_FUNC_INFO << "provided pos isn't child"
7770  << reinterpret_cast<quintptr>(pos);
7771 }
7772 
7782  if (!mChildrenY.contains(pos))
7783  mChildrenY.insert(pos);
7784  else
7785  qDebug() << Q_FUNC_INFO << "provided pos is child already"
7786  << reinterpret_cast<quintptr>(pos);
7787 }
7788 
7796  if (!mChildrenY.remove(pos))
7797  qDebug() << Q_FUNC_INFO << "provided pos isn't child"
7798  << reinterpret_cast<quintptr>(pos);
7799 }
7800 
7804 
7845 /* start documentation of inline functions */
7846 
7869 /* end documentation of inline functions */
7870 
7878  QCPAbstractItem *parentItem,
7879  const QString name)
7880  : QCPItemAnchor(parentPlot, parentItem, name),
7881  mPositionTypeX(ptAbsolute),
7882  mPositionTypeY(ptAbsolute),
7883  mKey(0),
7884  mValue(0),
7885  mParentAnchorX(0),
7886  mParentAnchorY(0) {}
7887 
7889  // unregister as parent at children:
7890  // Note: this is done in ~QCPItemAnchor again, but it's important
7891  // QCPItemPosition does it itself, because only then
7892  // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint
7893  // function instead of QCPItemAnchor::pixelPoint
7894  foreach (QCPItemPosition *child, mChildrenX.values()) {
7895  if (child->parentAnchorX() == this)
7896  child->setParentAnchorX(0); // this acts back on this anchor and
7897  // child removes itself from mChildrenX
7898  }
7899  foreach (QCPItemPosition *child, mChildrenY.values()) {
7900  if (child->parentAnchorY() == this)
7901  child->setParentAnchorY(0); // this acts back on this anchor and
7902  // child removes itself from mChildrenY
7903  }
7904  // unregister as child in parent:
7907 }
7908 
7909 /* can't make this a header inline function, because QPointer breaks with
7910  * forward declared types, see QTBUG-29588 */
7911 QCPAxisRect *QCPItemPosition::axisRect() const { return mAxisRect.data(); }
7912 
7943  setTypeX(type);
7944  setTypeY(type);
7945 }
7946 
7956  if (mPositionTypeX != type) {
7957  // if switching from or to coordinate type that isn't valid (e.g.
7958  // because axes or axis rect were deleted), don't try to recover the
7959  // pixelPoint() because it would output a qDebug warning.
7960  bool retainPixelPosition = true;
7961  if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) &&
7962  (!mKeyAxis || !mValueAxis))
7963  retainPixelPosition = false;
7965  (!mAxisRect))
7966  retainPixelPosition = false;
7967 
7968  QPointF pixel;
7969  if (retainPixelPosition) pixel = pixelPoint();
7970 
7971  mPositionTypeX = type;
7972 
7973  if (retainPixelPosition) setPixelPoint(pixel);
7974  }
7975 }
7976 
7986  if (mPositionTypeY != type) {
7987  // if switching from or to coordinate type that isn't valid (e.g.
7988  // because axes or axis rect were deleted), don't try to recover the
7989  // pixelPoint() because it would output a qDebug warning.
7990  bool retainPixelPosition = true;
7991  if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) &&
7992  (!mKeyAxis || !mValueAxis))
7993  retainPixelPosition = false;
7995  (!mAxisRect))
7996  retainPixelPosition = false;
7997 
7998  QPointF pixel;
7999  if (retainPixelPosition) pixel = pixelPoint();
8000 
8001  mPositionTypeY = type;
8002 
8003  if (retainPixelPosition) setPixelPoint(pixel);
8004  }
8005 }
8006 
8031  bool keepPixelPosition) {
8032  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
8033  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
8034  return successX && successY;
8035 }
8036 
8046  bool keepPixelPosition) {
8047  // make sure self is not assigned as parent:
8048  if (parentAnchor == this) {
8049  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor"
8050  << reinterpret_cast<quintptr>(parentAnchor);
8051  return false;
8052  }
8053  // make sure no recursive parent-child-relationships are created:
8054  QCPItemAnchor *currentParent = parentAnchor;
8055  while (currentParent) {
8056  if (QCPItemPosition *currentParentPos =
8057  currentParent->toQCPItemPosition()) {
8058  // is a QCPItemPosition, might have further parent, so keep
8059  // iterating
8060  if (currentParentPos == this) {
8061  qDebug() << Q_FUNC_INFO
8062  << "can't create recursive parent-child-relationship"
8063  << reinterpret_cast<quintptr>(parentAnchor);
8064  return false;
8065  }
8066  currentParent = currentParentPos->parentAnchorX();
8067  } else {
8068  // is a QCPItemAnchor, can't have further parent. Now make sure the
8069  // parent items aren't the same, to prevent a position being child
8070  // of an anchor which itself depends on the position, because
8071  // they're both on the same item:
8072  if (currentParent->mParentItem == mParentItem) {
8073  qDebug() << Q_FUNC_INFO
8074  << "can't set parent to be an anchor which itself "
8075  "depends on this position"
8076  << reinterpret_cast<quintptr>(parentAnchor);
8077  return false;
8078  }
8079  break;
8080  }
8081  }
8082 
8083  // if previously no parent set and PosType is still ptPlotCoords, set to
8084  // ptAbsolute:
8086 
8087  // save pixel position:
8088  QPointF pixelP;
8089  if (keepPixelPosition) pixelP = pixelPoint();
8090  // unregister at current parent anchor:
8092  // register at new parent anchor:
8093  if (parentAnchor) parentAnchor->addChildX(this);
8095  // restore pixel position under new parent:
8096  if (keepPixelPosition)
8097  setPixelPoint(pixelP);
8098  else
8099  setCoords(0, coords().y());
8100  return true;
8101 }
8102 
8112  bool keepPixelPosition) {
8113  // make sure self is not assigned as parent:
8114  if (parentAnchor == this) {
8115  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor"
8116  << reinterpret_cast<quintptr>(parentAnchor);
8117  return false;
8118  }
8119  // make sure no recursive parent-child-relationships are created:
8120  QCPItemAnchor *currentParent = parentAnchor;
8121  while (currentParent) {
8122  if (QCPItemPosition *currentParentPos =
8123  currentParent->toQCPItemPosition()) {
8124  // is a QCPItemPosition, might have further parent, so keep
8125  // iterating
8126  if (currentParentPos == this) {
8127  qDebug() << Q_FUNC_INFO
8128  << "can't create recursive parent-child-relationship"
8129  << reinterpret_cast<quintptr>(parentAnchor);
8130  return false;
8131  }
8132  currentParent = currentParentPos->parentAnchorY();
8133  } else {
8134  // is a QCPItemAnchor, can't have further parent. Now make sure the
8135  // parent items aren't the same, to prevent a position being child
8136  // of an anchor which itself depends on the position, because
8137  // they're both on the same item:
8138  if (currentParent->mParentItem == mParentItem) {
8139  qDebug() << Q_FUNC_INFO
8140  << "can't set parent to be an anchor which itself "
8141  "depends on this position"
8142  << reinterpret_cast<quintptr>(parentAnchor);
8143  return false;
8144  }
8145  break;
8146  }
8147  }
8148 
8149  // if previously no parent set and PosType is still ptPlotCoords, set to
8150  // ptAbsolute:
8152 
8153  // save pixel position:
8154  QPointF pixelP;
8155  if (keepPixelPosition) pixelP = pixelPoint();
8156  // unregister at current parent anchor:
8158  // register at new parent anchor:
8159  if (parentAnchor) parentAnchor->addChildY(this);
8161  // restore pixel position under new parent:
8162  if (keepPixelPosition)
8163  setPixelPoint(pixelP);
8164  else
8165  setCoords(coords().x(), 0);
8166  return true;
8167 }
8168 
8188 void QCPItemPosition::setCoords(double key, double value) {
8189  mKey = key;
8190  mValue = value;
8191 }
8192 
8199 void QCPItemPosition::setCoords(const QPointF &pos) {
8200  setCoords(pos.x(), pos.y());
8201 }
8202 
8211  QPointF result;
8212 
8213  // determine X:
8214  switch (mPositionTypeX) {
8215  case ptAbsolute: {
8216  result.rx() = mKey;
8217  if (mParentAnchorX) result.rx() += mParentAnchorX->pixelPoint().x();
8218  break;
8219  }
8220  case ptViewportRatio: {
8221  result.rx() = mKey * mParentPlot->viewport().width();
8222  if (mParentAnchorX)
8223  result.rx() += mParentAnchorX->pixelPoint().x();
8224  else
8225  result.rx() += mParentPlot->viewport().left();
8226  break;
8227  }
8228  case ptAxisRectRatio: {
8229  if (mAxisRect) {
8230  result.rx() = mKey * mAxisRect.data()->width();
8231  if (mParentAnchorX)
8232  result.rx() += mParentAnchorX->pixelPoint().x();
8233  else
8234  result.rx() += mAxisRect.data()->left();
8235  } else
8236  qDebug() << Q_FUNC_INFO
8237  << "Item position type x is ptAxisRectRatio, but no "
8238  "axis rect was defined";
8239  break;
8240  }
8241  case ptPlotCoords: {
8242  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8243  result.rx() = mKeyAxis.data()->coordToPixel(mKey);
8244  else if (mValueAxis &&
8245  mValueAxis.data()->orientation() == Qt::Horizontal)
8246  result.rx() = mValueAxis.data()->coordToPixel(mValue);
8247  else
8248  qDebug() << Q_FUNC_INFO
8249  << "Item position type x is ptPlotCoords, but no axes "
8250  "were defined";
8251  break;
8252  }
8253  }
8254 
8255  // determine Y:
8256  switch (mPositionTypeY) {
8257  case ptAbsolute: {
8258  result.ry() = mValue;
8259  if (mParentAnchorY) result.ry() += mParentAnchorY->pixelPoint().y();
8260  break;
8261  }
8262  case ptViewportRatio: {
8263  result.ry() = mValue * mParentPlot->viewport().height();
8264  if (mParentAnchorY)
8265  result.ry() += mParentAnchorY->pixelPoint().y();
8266  else
8267  result.ry() += mParentPlot->viewport().top();
8268  break;
8269  }
8270  case ptAxisRectRatio: {
8271  if (mAxisRect) {
8272  result.ry() = mValue * mAxisRect.data()->height();
8273  if (mParentAnchorY)
8274  result.ry() += mParentAnchorY->pixelPoint().y();
8275  else
8276  result.ry() += mAxisRect.data()->top();
8277  } else
8278  qDebug() << Q_FUNC_INFO
8279  << "Item position type y is ptAxisRectRatio, but no "
8280  "axis rect was defined";
8281  break;
8282  }
8283  case ptPlotCoords: {
8284  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8285  result.ry() = mKeyAxis.data()->coordToPixel(mKey);
8286  else if (mValueAxis &&
8287  mValueAxis.data()->orientation() == Qt::Vertical)
8288  result.ry() = mValueAxis.data()->coordToPixel(mValue);
8289  else
8290  qDebug() << Q_FUNC_INFO
8291  << "Item position type y is ptPlotCoords, but no axes "
8292  "were defined";
8293  break;
8294  }
8295  }
8296 
8297  return result;
8298 }
8299 
8305 void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis) {
8306  mKeyAxis = keyAxis;
8308 }
8309 
8315 void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect) {
8316  mAxisRect = axisRect;
8317 }
8318 
8330 void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint) {
8331  double x = pixelPoint.x();
8332  double y = pixelPoint.y();
8333 
8334  switch (mPositionTypeX) {
8335  case ptAbsolute: {
8336  if (mParentAnchorX) x -= mParentAnchorX->pixelPoint().x();
8337  break;
8338  }
8339  case ptViewportRatio: {
8340  if (mParentAnchorX)
8341  x -= mParentAnchorX->pixelPoint().x();
8342  else
8343  x -= mParentPlot->viewport().left();
8344  x /= (double)mParentPlot->viewport().width();
8345  break;
8346  }
8347  case ptAxisRectRatio: {
8348  if (mAxisRect) {
8349  if (mParentAnchorX)
8350  x -= mParentAnchorX->pixelPoint().x();
8351  else
8352  x -= mAxisRect.data()->left();
8353  x /= (double)mAxisRect.data()->width();
8354  } else
8355  qDebug() << Q_FUNC_INFO
8356  << "Item position type x is ptAxisRectRatio, but no "
8357  "axis rect was defined";
8358  break;
8359  }
8360  case ptPlotCoords: {
8361  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8362  x = mKeyAxis.data()->pixelToCoord(x);
8363  else if (mValueAxis &&
8364  mValueAxis.data()->orientation() == Qt::Horizontal)
8365  y = mValueAxis.data()->pixelToCoord(x);
8366  else
8367  qDebug() << Q_FUNC_INFO
8368  << "Item position type x is ptPlotCoords, but no axes "
8369  "were defined";
8370  break;
8371  }
8372  }
8373 
8374  switch (mPositionTypeY) {
8375  case ptAbsolute: {
8376  if (mParentAnchorY) y -= mParentAnchorY->pixelPoint().y();
8377  break;
8378  }
8379  case ptViewportRatio: {
8380  if (mParentAnchorY)
8381  y -= mParentAnchorY->pixelPoint().y();
8382  else
8383  y -= mParentPlot->viewport().top();
8384  y /= (double)mParentPlot->viewport().height();
8385  break;
8386  }
8387  case ptAxisRectRatio: {
8388  if (mAxisRect) {
8389  if (mParentAnchorY)
8390  y -= mParentAnchorY->pixelPoint().y();
8391  else
8392  y -= mAxisRect.data()->top();
8393  y /= (double)mAxisRect.data()->height();
8394  } else
8395  qDebug() << Q_FUNC_INFO
8396  << "Item position type y is ptAxisRectRatio, but no "
8397  "axis rect was defined";
8398  break;
8399  }
8400  case ptPlotCoords: {
8401  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8402  x = mKeyAxis.data()->pixelToCoord(y);
8403  else if (mValueAxis &&
8404  mValueAxis.data()->orientation() == Qt::Vertical)
8405  y = mValueAxis.data()->pixelToCoord(y);
8406  else
8407  qDebug() << Q_FUNC_INFO
8408  << "Item position type y is ptPlotCoords, but no axes "
8409  "were defined";
8410  break;
8411  }
8412  }
8413 
8414  setCoords(x, y);
8415 }
8416 
8420 
8562 /* start of documentation of inline functions */
8563 
8580 /* end of documentation of inline functions */
8581 /* start documentation of pure virtual functions */
8582 
8593 /* end documentation of pure virtual functions */
8594 /* start documentation of signals */
8595 
8601 /* end documentation of signals */
8602 
8607  : QCPLayerable(parentPlot),
8608  mClipToAxisRect(false),
8609  mSelectable(true),
8610  mSelected(false) {
8611  QList<QCPAxisRect *> rects = parentPlot->axisRects();
8612  if (rects.size() > 0) {
8613  setClipToAxisRect(true);
8614  setClipAxisRect(rects.first());
8615  }
8616 }
8617 
8619  // don't delete mPositions because every position is also an anchor and thus
8620  // in mAnchors
8621  qDeleteAll(mAnchors);
8622 }
8623 
8624 /* can't make this a header inline function, because QPointer breaks with
8625  * forward declared types, see QTBUG-29588 */
8627  return mClipAxisRect.data();
8628 }
8629 
8637 void QCPAbstractItem::setClipToAxisRect(bool clip) {
8638  mClipToAxisRect = clip;
8640 }
8641 
8649  mClipAxisRect = rect;
8651 }
8652 
8663 void QCPAbstractItem::setSelectable(bool selectable) {
8664  if (mSelectable != selectable) {
8667  }
8668 }
8669 
8687 void QCPAbstractItem::setSelected(bool selected) {
8688  if (mSelected != selected) {
8689  mSelected = selected;
8691  }
8692 }
8693 
8704 QCPItemPosition *QCPAbstractItem::position(const QString &name) const {
8705  for (int i = 0; i < mPositions.size(); ++i) {
8706  if (mPositions.at(i)->name() == name) return mPositions.at(i);
8707  }
8708  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8709  return 0;
8710 }
8711 
8722 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const {
8723  for (int i = 0; i < mAnchors.size(); ++i) {
8724  if (mAnchors.at(i)->name() == name) return mAnchors.at(i);
8725  }
8726  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8727  return 0;
8728 }
8729 
8739 bool QCPAbstractItem::hasAnchor(const QString &name) const {
8740  for (int i = 0; i < mAnchors.size(); ++i) {
8741  if (mAnchors.at(i)->name() == name) return true;
8742  }
8743  return false;
8744 }
8745 
8757 QRect QCPAbstractItem::clipRect() const {
8759  return mClipAxisRect.data()->rect();
8760  else
8761  return mParentPlot->viewport();
8762 }
8763 
8780 }
8781 
8794 double QCPAbstractItem::distSqrToLine(const QPointF &start,
8795  const QPointF &end,
8796  const QPointF &point) const {
8797  QVector2D a(start);
8798  QVector2D b(end);
8799  QVector2D p(point);
8800  QVector2D v(b - a);
8801 
8802  double vLengthSqr = v.lengthSquared();
8803  if (!qFuzzyIsNull(vLengthSqr)) {
8804  double mu = QVector2D::dotProduct(p - a, v) / vLengthSqr;
8805  if (mu < 0)
8806  return (a - p).lengthSquared();
8807  else if (mu > 1)
8808  return (b - p).lengthSquared();
8809  else
8810  return ((a + mu * v) - p).lengthSquared();
8811  } else
8812  return (a - p).lengthSquared();
8813 }
8814 
8831 double QCPAbstractItem::rectSelectTest(const QRectF &rect,
8832  const QPointF &pos,
8833  bool filledRect) const {
8834  double result = -1;
8835 
8836  // distance to border:
8837  QList<QLineF> lines;
8838  lines << QLineF(rect.topLeft(), rect.topRight())
8839  << QLineF(rect.bottomLeft(), rect.bottomRight())
8840  << QLineF(rect.topLeft(), rect.bottomLeft())
8841  << QLineF(rect.topRight(), rect.bottomRight());
8842  double minDistSqr = std::numeric_limits<double>::max();
8843  for (int i = 0; i < lines.size(); ++i) {
8844  double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8845  if (distSqr < minDistSqr) minDistSqr = distSqr;
8846  }
8847  result = qSqrt(minDistSqr);
8848 
8849  // filled rect, allow click inside to count as hit:
8850  if (filledRect && result > mParentPlot->selectionTolerance() * 0.99) {
8851  if (rect.contains(pos))
8853  }
8854  return result;
8855 }
8856 
8869 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const {
8870  qDebug() << Q_FUNC_INFO
8871  << "called on item which shouldn't have any anchors (this method "
8872  "not reimplemented). anchorId"
8873  << anchorId;
8874  return QPointF();
8875 }
8876 
8894  if (hasAnchor(name))
8895  qDebug() << Q_FUNC_INFO
8896  << "anchor/position with name exists already:" << name;
8897  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8898  mPositions.append(newPosition);
8899  mAnchors.append(newPosition); // every position is also an anchor
8900  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8901  newPosition->setType(QCPItemPosition::ptPlotCoords);
8902  if (mParentPlot->axisRect())
8903  newPosition->setAxisRect(mParentPlot->axisRect());
8904  newPosition->setCoords(0, 0);
8905  return newPosition;
8906 }
8907 
8931  int anchorId) {
8932  if (hasAnchor(name))
8933  qDebug() << Q_FUNC_INFO
8934  << "anchor/position with name exists already:" << name;
8935  QCPItemAnchor *newAnchor =
8936  new QCPItemAnchor(mParentPlot, this, name, anchorId);
8937  mAnchors.append(newAnchor);
8938  return newAnchor;
8939 }
8940 
8941 /* inherits documentation from base class */
8942 void QCPAbstractItem::selectEvent(QMouseEvent *event,
8943  bool additive,
8944  const QVariant &details,
8945  bool *selectionStateChanged) {
8946  Q_UNUSED(event)
8947  Q_UNUSED(details)
8948  if (mSelectable) {
8949  bool selBefore = mSelected;
8950  setSelected(additive ? !mSelected : true);
8951  if (selectionStateChanged)
8952  *selectionStateChanged = mSelected != selBefore;
8953  }
8954 }
8955 
8956 /* inherits documentation from base class */
8957 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged) {
8958  if (mSelectable) {
8959  bool selBefore = mSelected;
8960  setSelected(false);
8961  if (selectionStateChanged)
8962  *selectionStateChanged = mSelected != selBefore;
8963  }
8964 }
8965 
8966 /* inherits documentation from base class */
8968  return QCP::iSelectItems;
8969 }
8970 
8976 
8986 /* start of documentation of inline functions */
8987 
9013 /* end of documentation of inline functions */
9014 /* start of documentation of signals */
9015 
9225 /* end of documentation of signals */
9226 /* start of documentation of public members */
9227 
9305 /* end of documentation of public members */
9306 
9310 QCustomPlot::QCustomPlot(QWidget *parent)
9311  : QWidget(parent),
9312  xAxis(0),
9313  yAxis(0),
9314  xAxis2(0),
9315  yAxis2(0),
9316  legend(0),
9317  mPlotLayout(0),
9318  mAutoAddPlottableToLegend(true),
9319  mAntialiasedElements(QCP::aeNone),
9320  mNotAntialiasedElements(QCP::aeNone),
9321  mInteractions(0),
9322  mSelectionTolerance(8),
9323  mNoAntialiasingOnDrag(false),
9324  mBackgroundBrush(Qt::white, Qt::SolidPattern),
9325  mBackgroundScaled(true),
9326  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9327  mCurrentLayer(0),
9328  mPlottingHints(QCP::phCacheLabels | QCP::phForceRepaint),
9329  mMultiSelectModifier(Qt::ControlModifier),
9330  mPaintBuffer(size()),
9331  mMouseEventElement(0),
9332  mReplotting(false) {
9333  setAttribute(Qt::WA_NoMousePropagation);
9334  setAttribute(Qt::WA_OpaquePaintEvent);
9335  setMouseTracking(true);
9336  QLocale currentLocale = locale();
9337  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9338  setLocale(currentLocale);
9339 
9340  // create initial layers:
9341  mLayers.append(new QCPLayer(this, QLatin1String("background")));
9342  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
9343  mLayers.append(new QCPLayer(this, QLatin1String("main")));
9344  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
9345  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
9346  updateLayerIndices();
9347  setCurrentLayer(QLatin1String("main"));
9348 
9349  // create initial layout, axis rect and legend:
9350  mPlotLayout = new QCPLayoutGrid;
9351  mPlotLayout->initializeParentPlot(this);
9352  mPlotLayout->setParent(this); // important because if parent is QWidget,
9353  // QCPLayout::sizeConstraintsChanged will
9354  // call QWidget::updateGeometry
9355  mPlotLayout->setLayer(QLatin1String("main"));
9356  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9357  mPlotLayout->addElement(0, 0, defaultAxisRect);
9358  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9359  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9360  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9361  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9362  legend = new QCPLegend;
9363  legend->setVisible(false);
9364  defaultAxisRect->insetLayout()->addElement(legend,
9365  Qt::AlignRight | Qt::AlignTop);
9366  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9367 
9368  defaultAxisRect->setLayer(QLatin1String("background"));
9369  xAxis->setLayer(QLatin1String("axes"));
9370  yAxis->setLayer(QLatin1String("axes"));
9371  xAxis2->setLayer(QLatin1String("axes"));
9372  yAxis2->setLayer(QLatin1String("axes"));
9373  xAxis->grid()->setLayer(QLatin1String("grid"));
9374  yAxis->grid()->setLayer(QLatin1String("grid"));
9375  xAxis2->grid()->setLayer(QLatin1String("grid"));
9376  yAxis2->grid()->setLayer(QLatin1String("grid"));
9377  legend->setLayer(QLatin1String("legend"));
9378 
9379  setViewport(
9380  rect()); // needs to be called after mPlotLayout has been created
9381 
9382  replot();
9383 }
9384 
9386  clearPlottables();
9387  clearItems();
9388 
9389  if (mPlotLayout) {
9390  delete mPlotLayout;
9391  mPlotLayout = 0;
9392  }
9393 
9394  mCurrentLayer = 0;
9395  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the
9396  // last layer to be removed
9397  mLayers.clear();
9398 }
9399 
9420  const QCP::AntialiasedElements &antialiasedElements) {
9422 
9423  // make sure elements aren't in mNotAntialiasedElements and
9424  // mAntialiasedElements simultaneously:
9427 }
9428 
9438  QCP::AntialiasedElement antialiasedElement, bool enabled) {
9439  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9440  mAntialiasedElements &= ~antialiasedElement;
9441  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9442  mAntialiasedElements |= antialiasedElement;
9443 
9444  // make sure elements aren't in mNotAntialiasedElements and
9445  // mAntialiasedElements simultaneously:
9448 }
9449 
9470  const QCP::AntialiasedElements &notAntialiasedElements) {
9472 
9473  // make sure elements aren't in mNotAntialiasedElements and
9474  // mAntialiasedElements simultaneously:
9477 }
9478 
9488  QCP::AntialiasedElement notAntialiasedElement, bool enabled) {
9489  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9490  mNotAntialiasedElements &= ~notAntialiasedElement;
9491  else if (enabled &&
9492  !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9493  mNotAntialiasedElements |= notAntialiasedElement;
9494 
9495  // make sure elements aren't in mNotAntialiasedElements and
9496  // mAntialiasedElements simultaneously:
9499 }
9500 
9509 }
9510 
9574 void QCustomPlot::setInteractions(const QCP::Interactions &interactions) {
9576 }
9577 
9585 void QCustomPlot::setInteraction(const QCP::Interaction &interaction,
9586  bool enabled) {
9587  if (!enabled && mInteractions.testFlag(interaction))
9588  mInteractions &= ~interaction;
9589  else if (enabled && !mInteractions.testFlag(interaction))
9590  mInteractions |= interaction;
9591 }
9592 
9607 void QCustomPlot::setSelectionTolerance(int pixels) {
9608  mSelectionTolerance = pixels;
9609 }
9610 
9621 void QCustomPlot::setNoAntialiasingOnDrag(bool enabled) {
9622  mNoAntialiasingOnDrag = enabled;
9623 }
9624 
9631 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints) {
9632  mPlottingHints = hints;
9633 }
9634 
9640 void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled) {
9641  QCP::PlottingHints newHints = mPlottingHints;
9642  if (!enabled)
9643  newHints &= ~hint;
9644  else
9645  newHints |= hint;
9646 
9647  if (newHints != mPlottingHints) setPlottingHints(newHints);
9648 }
9649 
9661 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier) {
9662  mMultiSelectModifier = modifier;
9663 }
9664 
9673 void QCustomPlot::setViewport(const QRect &rect) {
9674  mViewport = rect;
9676 }
9677 
9695 void QCustomPlot::setBackground(const QPixmap &pm) {
9696  mBackgroundPixmap = pm;
9697  mScaledBackgroundPixmap = QPixmap();
9698 }
9699 
9714 void QCustomPlot::setBackground(const QBrush &brush) {
9715  mBackgroundBrush = brush;
9716 }
9717 
9726 void QCustomPlot::setBackground(const QPixmap &pm,
9727  bool scaled,
9728  Qt::AspectRatioMode mode) {
9729  mBackgroundPixmap = pm;
9730  mScaledBackgroundPixmap = QPixmap();
9731  mBackgroundScaled = scaled;
9732  mBackgroundScaledMode = mode;
9733 }
9734 
9746 void QCustomPlot::setBackgroundScaled(bool scaled) {
9747  mBackgroundScaled = scaled;
9748 }
9749 
9757 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode) {
9758  mBackgroundScaledMode = mode;
9759 }
9760 
9770  if (index >= 0 && index < mPlottables.size()) {
9771  return mPlottables.at(index);
9772  } else {
9773  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9774  return 0;
9775  }
9776 }
9777 
9786  if (!mPlottables.isEmpty()) {
9787  return mPlottables.last();
9788  } else
9789  return 0;
9790 }
9791 
9804  if (mPlottables.contains(plottable)) {
9805  qDebug() << Q_FUNC_INFO
9806  << "plottable already added to this QCustomPlot:"
9807  << reinterpret_cast<quintptr>(plottable);
9808  return false;
9809  }
9810  if (plottable->parentPlot() != this) {
9811  qDebug() << Q_FUNC_INFO
9812  << "plottable not created with this QCustomPlot as parent:"
9813  << reinterpret_cast<quintptr>(plottable);
9814  return false;
9815  }
9816 
9817  mPlottables.append(plottable);
9818  // possibly add plottable to legend:
9820  // special handling for QCPGraphs to maintain the simple graph interface:
9821  if (QCPGraph *graph = qobject_cast<QCPGraph *>(plottable))
9822  mGraphs.append(graph);
9823  if (!plottable->layer()) // usually the layer is already set in the
9824  // constructor of the plottable (via QCPLayerable
9825  // constructor)
9827  return true;
9828 }
9829 
9839  if (!mPlottables.contains(plottable)) {
9840  qDebug() << Q_FUNC_INFO << "plottable not in list:"
9841  << reinterpret_cast<quintptr>(plottable);
9842  return false;
9843  }
9844 
9845  // remove plottable from legend:
9847  // special handling for QCPGraphs to maintain the simple graph interface:
9848  if (QCPGraph *graph = qobject_cast<QCPGraph *>(plottable))
9849  mGraphs.removeOne(graph);
9850  // remove plottable:
9851  delete plottable;
9852  mPlottables.removeOne(plottable);
9853  return true;
9854 }
9855 
9860 bool QCustomPlot::removePlottable(int index) {
9861  if (index >= 0 && index < mPlottables.size())
9862  return removePlottable(mPlottables[index]);
9863  else {
9864  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9865  return false;
9866  }
9867 }
9868 
9878  int c = mPlottables.size();
9879  for (int i = c - 1; i >= 0; --i) removePlottable(mPlottables[i]);
9880  return c;
9881 }
9882 
9888 int QCustomPlot::plottableCount() const { return mPlottables.size(); }
9889 
9900 QList<QCPAbstractPlottable *> QCustomPlot::selectedPlottables() const {
9901  QList<QCPAbstractPlottable *> result;
9903  if (plottable->selected()) result.append(plottable);
9904  }
9905  return result;
9906 }
9907 
9922  bool onlySelectable) const {
9923  QCPAbstractPlottable *resultPlottable = 0;
9924  double resultDistance =
9925  mSelectionTolerance; // only regard clicks with distances smaller
9926  // than mSelectionTolerance as selections, so
9927  // initialize with that value
9928 
9930  if (onlySelectable &&
9931  !plottable->selectable()) // we could have also passed
9932  // onlySelectable to the selectTest
9933  // function, but checking here is faster,
9934  // because we have access to
9935  // QCPabstractPlottable::selectable
9936  continue;
9937  if ((plottable->keyAxis()->axisRect()->rect() &
9938  plottable->valueAxis()->axisRect()->rect())
9939  .contains(pos.toPoint())) // only consider clicks inside
9940  // the rect that is spanned by
9941  // the plottable's key/value axes
9942  {
9943  double currentDistance = plottable->selectTest(pos, false);
9944  if (currentDistance >= 0 && currentDistance < resultDistance) {
9945  resultPlottable = plottable;
9946  resultDistance = currentDistance;
9947  }
9948  }
9949  }
9950 
9951  return resultPlottable;
9952 }
9953 
9959 bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const {
9960  return mPlottables.contains(plottable);
9961 }
9962 
9971 QCPGraph *QCustomPlot::graph(int index) const {
9972  if (index >= 0 && index < mGraphs.size()) {
9973  return mGraphs.at(index);
9974  } else {
9975  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9976  return 0;
9977  }
9978 }
9979 
9987 QCPGraph *QCustomPlot::graph() const {
9988  if (!mGraphs.isEmpty()) {
9989  return mGraphs.last();
9990  } else
9991  return 0;
9992 }
9993 
10007 QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) {
10008  if (!keyAxis) keyAxis = xAxis;
10009  if (!valueAxis) valueAxis = yAxis;
10010  if (!keyAxis || !valueAxis) {
10011  qDebug() << Q_FUNC_INFO
10012  << "can't use default QCustomPlot xAxis or yAxis, because at "
10013  "least one is invalid (has been deleted)";
10014  return 0;
10015  }
10016  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this) {
10017  qDebug() << Q_FUNC_INFO
10018  << "passed keyAxis or valueAxis doesn't have this QCustomPlot "
10019  "as parent";
10020  return 0;
10021  }
10022 
10023  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
10024  if (addPlottable(newGraph)) {
10025  newGraph->setName(QLatin1String("Graph ") +
10026  QString::number(mGraphs.size()));
10027  return newGraph;
10028  } else {
10029  delete newGraph;
10030  return 0;
10031  }
10032 }
10033 
10044 bool QCustomPlot::removeGraph(QCPGraph *graph) {
10045  return removePlottable(graph);
10046 }
10047 
10052 bool QCustomPlot::removeGraph(int index) {
10053  if (index >= 0 && index < mGraphs.size())
10054  return removeGraph(mGraphs[index]);
10055  else
10056  return false;
10057 }
10058 
10067  int c = mGraphs.size();
10068  for (int i = c - 1; i >= 0; --i) removeGraph(mGraphs[i]);
10069  return c;
10070 }
10071 
10077 int QCustomPlot::graphCount() const { return mGraphs.size(); }
10078 
10089 QList<QCPGraph *> QCustomPlot::selectedGraphs() const {
10090  QList<QCPGraph *> result;
10091  foreach (QCPGraph *graph, mGraphs) {
10092  if (graph->selected()) result.append(graph);
10093  }
10094  return result;
10095 }
10096 
10105 QCPAbstractItem *QCustomPlot::item(int index) const {
10106  if (index >= 0 && index < mItems.size()) {
10107  return mItems.at(index);
10108  } else {
10109  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10110  return 0;
10111  }
10112 }
10113 
10122  if (!mItems.isEmpty()) {
10123  return mItems.last();
10124  } else
10125  return 0;
10126 }
10127 
10137  if (!mItems.contains(item) && item->parentPlot() == this) {
10138  mItems.append(item);
10139  return true;
10140  } else {
10141  qDebug() << Q_FUNC_INFO
10142  << "item either already in list or not created with this "
10143  "QCustomPlot as parent:"
10144  << reinterpret_cast<quintptr>(item);
10145  return false;
10146  }
10147 }
10148 
10157  if (mItems.contains(item)) {
10158  delete item;
10159  mItems.removeOne(item);
10160  return true;
10161  } else {
10162  qDebug() << Q_FUNC_INFO
10163  << "item not in list:" << reinterpret_cast<quintptr>(item);
10164  return false;
10165  }
10166 }
10167 
10172 bool QCustomPlot::removeItem(int index) {
10173  if (index >= 0 && index < mItems.size())
10174  return removeItem(mItems[index]);
10175  else {
10176  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10177  return false;
10178  }
10179 }
10180 
10189  int c = mItems.size();
10190  for (int i = c - 1; i >= 0; --i) removeItem(mItems[i]);
10191  return c;
10192 }
10193 
10199 int QCustomPlot::itemCount() const { return mItems.size(); }
10200 
10208 QList<QCPAbstractItem *> QCustomPlot::selectedItems() const {
10209  QList<QCPAbstractItem *> result;
10210  foreach (QCPAbstractItem *item, mItems) {
10211  if (item->selected()) result.append(item);
10212  }
10213  return result;
10214 }
10215 
10229 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos,
10230  bool onlySelectable) const {
10231  QCPAbstractItem *resultItem = 0;
10232  double resultDistance =
10233  mSelectionTolerance; // only regard clicks with distances smaller
10234  // than mSelectionTolerance as selections, so
10235  // initialize with that value
10236 
10237  foreach (QCPAbstractItem *item, mItems) {
10238  if (onlySelectable &&
10239  !item->selectable()) // we could have also passed onlySelectable to
10240  // the selectTest function, but checking here
10241  // is faster, because we have access to
10242  // QCPAbstractItem::selectable
10243  continue;
10244  if (!item->clipToAxisRect() ||
10245  item->clipRect().contains(
10246  pos.toPoint())) // only consider clicks inside axis
10247  // cliprect of the item if actually clipped
10248  // to it
10249  {
10250  double currentDistance = item->selectTest(pos, false);
10251  if (currentDistance >= 0 && currentDistance < resultDistance) {
10252  resultItem = item;
10253  resultDistance = currentDistance;
10254  }
10255  }
10256  }
10257 
10258  return resultItem;
10259 }
10260 
10266 bool QCustomPlot::hasItem(QCPAbstractItem *item) const {
10267  return mItems.contains(item);
10268 }
10269 
10278 QCPLayer *QCustomPlot::layer(const QString &name) const {
10279  foreach (QCPLayer *layer, mLayers) {
10280  if (layer->name() == name) return layer;
10281  }
10282  return 0;
10283 }
10284 
10291 QCPLayer *QCustomPlot::layer(int index) const {
10292  if (index >= 0 && index < mLayers.size()) {
10293  return mLayers.at(index);
10294  } else {
10295  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10296  return 0;
10297  }
10298 }
10299 
10304 
10317 bool QCustomPlot::setCurrentLayer(const QString &name) {
10318  if (QCPLayer *newCurrentLayer = layer(name)) {
10319  return setCurrentLayer(newCurrentLayer);
10320  } else {
10321  qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10322  return false;
10323  }
10324 }
10325 
10336  if (!mLayers.contains(layer)) {
10337  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:"
10338  << reinterpret_cast<quintptr>(layer);
10339  return false;
10340  }
10341 
10342  mCurrentLayer = layer;
10343  return true;
10344 }
10345 
10351 int QCustomPlot::layerCount() const { return mLayers.size(); }
10352 
10368 bool QCustomPlot::addLayer(const QString &name,
10369  QCPLayer *otherLayer,
10370  QCustomPlot::LayerInsertMode insertMode) {
10371  if (!otherLayer) otherLayer = mLayers.last();
10372  if (!mLayers.contains(otherLayer)) {
10373  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:"
10374  << reinterpret_cast<quintptr>(otherLayer);
10375  return false;
10376  }
10377  if (layer(name)) {
10378  qDebug() << Q_FUNC_INFO << "A layer exists already with the name"
10379  << name;
10380  return false;
10381  }
10382 
10383  QCPLayer *newLayer = new QCPLayer(this, name);
10384  mLayers.insert(otherLayer->index() + (insertMode == limAbove ? 1 : 0),
10385  newLayer);
10387  return true;
10388 }
10389 
10405 bool QCustomPlot::removeLayer(QCPLayer *layer) {
10406  if (!mLayers.contains(layer)) {
10407  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:"
10408  << reinterpret_cast<quintptr>(layer);
10409  return false;
10410  }
10411  if (mLayers.size() < 2) {
10412  qDebug() << Q_FUNC_INFO << "can't remove last layer";
10413  return false;
10414  }
10415 
10416  // append all children of this layer to layer below (if this is lowest
10417  // layer, prepend to layer above)
10418  int removedIndex = layer->index();
10419  bool isFirstLayer = removedIndex == 0;
10420  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex + 1)
10421  : mLayers.at(removedIndex - 1);
10422  QList<QCPLayerable *> children = layer->children();
10423  if (isFirstLayer) // prepend in reverse order (so order relative to each
10424  // other stays the same)
10425  {
10426  for (int i = children.size() - 1; i >= 0; --i)
10427  children.at(i)->moveToLayer(targetLayer, true);
10428  } else // append normally
10429  {
10430  for (int i = 0; i < children.size(); ++i)
10431  children.at(i)->moveToLayer(targetLayer, false);
10432  }
10433  // if removed layer is current layer, change current layer to layer
10434  // below/above:
10435  if (layer == mCurrentLayer) setCurrentLayer(targetLayer);
10436  // remove layer:
10437  delete layer;
10438  mLayers.removeOne(layer);
10440  return true;
10441 }
10442 
10452 bool QCustomPlot::moveLayer(QCPLayer *layer,
10453  QCPLayer *otherLayer,
10454  QCustomPlot::LayerInsertMode insertMode) {
10455  if (!mLayers.contains(layer)) {
10456  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:"
10457  << reinterpret_cast<quintptr>(layer);
10458  return false;
10459  }
10460  if (!mLayers.contains(otherLayer)) {
10461  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:"
10462  << reinterpret_cast<quintptr>(otherLayer);
10463  return false;
10464  }
10465 
10466  mLayers.move(layer->index(),
10467  otherLayer->index() + (insertMode == limAbove ? 1 : 0));
10469  return true;
10470 }
10471 
10481 int QCustomPlot::axisRectCount() const { return axisRects().size(); }
10482 
10493 QCPAxisRect *QCustomPlot::axisRect(int index) const {
10494  const QList<QCPAxisRect *> rectList = axisRects();
10495  if (index >= 0 && index < rectList.size()) {
10496  return rectList.at(index);
10497  } else {
10498  qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10499  return 0;
10500  }
10501 }
10502 
10508 QList<QCPAxisRect *> QCustomPlot::axisRects() const {
10509  QList<QCPAxisRect *> result;
10510  QStack<QCPLayoutElement *> elementStack;
10511  if (mPlotLayout) elementStack.push(mPlotLayout);
10512 
10513  while (!elementStack.isEmpty()) {
10514  foreach (QCPLayoutElement *element,
10515  elementStack.pop()->elements(false)) {
10516  if (element) {
10517  elementStack.push(element);
10518  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect *>(element))
10519  result.append(ar);
10520  }
10521  }
10522  }
10523 
10524  return result;
10525 }
10526 
10537 QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const {
10538  QCPLayoutElement *currentElement = mPlotLayout;
10539  bool searchSubElements = true;
10540  while (searchSubElements && currentElement) {
10541  searchSubElements = false;
10542  foreach (QCPLayoutElement *subElement,
10543  currentElement->elements(false)) {
10544  if (subElement && subElement->realVisibility() &&
10545  subElement->selectTest(pos, false) >= 0) {
10546  currentElement = subElement;
10547  searchSubElements = true;
10548  break;
10549  }
10550  }
10551  }
10552  return currentElement;
10553 }
10554 
10562 QList<QCPAxis *> QCustomPlot::selectedAxes() const {
10563  QList<QCPAxis *> result, allAxes;
10564  foreach (QCPAxisRect *rect, axisRects()) allAxes << rect->axes();
10565 
10566  foreach (QCPAxis *axis, allAxes) {
10567  if (axis->selectedParts() != QCPAxis::spNone) result.append(axis);
10568  }
10569 
10570  return result;
10571 }
10572 
10581 QList<QCPLegend *> QCustomPlot::selectedLegends() const {
10582  QList<QCPLegend *> result;
10583 
10584  QStack<QCPLayoutElement *> elementStack;
10585  if (mPlotLayout) elementStack.push(mPlotLayout);
10586 
10587  while (!elementStack.isEmpty()) {
10588  foreach (QCPLayoutElement *subElement,
10589  elementStack.pop()->elements(false)) {
10590  if (subElement) {
10591  elementStack.push(subElement);
10592  if (QCPLegend *leg = qobject_cast<QCPLegend *>(subElement)) {
10593  if (leg->selectedParts() != QCPLegend::spNone)
10594  result.append(leg);
10595  }
10596  }
10597  }
10598  }
10599 
10600  return result;
10601 }
10602 
10614 void QCustomPlot::deselectAll() {
10615  foreach (QCPLayer *layer, mLayers) {
10616  foreach (QCPLayerable *layerable, layer->children())
10617  layerable->deselectEvent(0);
10618  }
10619 }
10620 
10636 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority) {
10637  if (mReplotting) // incase signals loop back to replot slot
10638  return;
10639  mReplotting = true;
10640  emit beforeReplot();
10641 
10642  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern
10643  ? mBackgroundBrush.color()
10644  : Qt::transparent);
10645  QCPPainter painter;
10646  painter.begin(&mPaintBuffer);
10647  if (painter.isActive()) {
10648  painter.setRenderHint(
10649  QPainter::Antialiasing); // to make Antialiasing look good if
10650  // using the OpenGL graphicssystem
10651  if (mBackgroundBrush.style() != Qt::SolidPattern &&
10652  mBackgroundBrush.style() != Qt::NoBrush)
10653  painter.fillRect(mViewport, mBackgroundBrush);
10654  draw(&painter);
10655  painter.end();
10656  if ((refreshPriority == rpHint &&
10657  mPlottingHints.testFlag(QCP::phForceRepaint)) ||
10658  refreshPriority == rpImmediate)
10659  repaint();
10660  else
10661  update();
10662  } else // might happen if QCustomPlot has width or height zero
10663  qDebug() << Q_FUNC_INFO
10664  << "Couldn't activate painter on buffer. This usually happens "
10665  "because QCustomPlot has width or height zero.";
10666 
10667  emit afterReplot();
10668  mReplotting = false;
10669 }
10670 
10671 void QCustomPlot::showPointToolTip(QMouseEvent *event) {
10672  return;
10673 
10674  int evtX = event->pos().x();
10675  int evtY = event->pos().y();
10676 
10677  int x = this->xAxis->pixelToCoord(evtX);
10678  int y = this->yAxis->pixelToCoord(evtY);
10679 
10680  if (selectedGraphs().count() > 0) {
10681  QCPGraph *graph = selectedGraphs().first();
10682  QCPData data = graph->data()->lowerBound(x).value();
10683 
10684  double dbottom =
10685  graph->valueAxis()->range().lower; // Yaxis bottom value
10686  double dtop = graph->valueAxis()->range().upper;
10687 
10688  // Yaxis top value
10689  long ptop = graph->valueAxis()->axisRect()->top(); // graph top margin
10690  long pbottom = graph->valueAxis()
10691  ->axisRect()
10692  ->bottom(); // graph bottom position
10693  // result for Y axis
10694  double valueY = (evtY - ptop) / (double)(pbottom - ptop) *
10695  (double)(dbottom - dtop) +
10696  dtop;
10697 
10698  // or shortly for X-axis
10699  double valueX = (evtX - graph->keyAxis()
10700  ->axisRect()
10701  ->left()); // graph width in pixels
10702  double ratio = (double)(graph->keyAxis()->axisRect()->right() -
10703  graph->keyAxis()->axisRect()->left()) /
10704  (double)(graph->keyAxis()->range().lower -
10705  graph->keyAxis()
10706  ->range()
10707  .upper); // ratio px->graph width
10708 
10709  // and result for X-axis
10710  valueX = -valueX / ratio + graph->keyAxis()->range().lower;
10711 
10712  QPoint gPos = mapToGlobal(QPoint(evtX, evtY));
10713  QToolTip::showText(gPos, QString("%1 , %2").arg(valueX).arg(valueY));
10714  }
10715 }
10726 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables) {
10727  QList<QCPAxis *> allAxes;
10728  foreach (QCPAxisRect *rect, axisRects()) allAxes << rect->axes();
10729 
10730  foreach (QCPAxis *axis, allAxes) axis->rescale(onlyVisiblePlottables);
10731 }
10732 
10774 bool QCustomPlot::savePdf(const QString &fileName,
10775  bool noCosmeticPen,
10776  int width,
10777  int height,
10778  const QString &pdfCreator,
10779  const QString &pdfTitle) {
10780  bool success = false;
10781 #ifdef QT_NO_PRINTER
10782  Q_UNUSED(fileName)
10783  Q_UNUSED(noCosmeticPen)
10784  Q_UNUSED(width)
10785  Q_UNUSED(height)
10786  Q_UNUSED(pdfCreator)
10787  Q_UNUSED(pdfTitle)
10788  qDebug() << Q_FUNC_INFO
10789  << "Qt was built without printer support (QT_NO_PRINTER). PDF not "
10790  "created.";
10791 #else
10792  int newWidth, newHeight;
10793  if (width == 0 || height == 0) {
10794  newWidth = this->width();
10795  newHeight = this->height();
10796  } else {
10797  newWidth = width;
10798  newHeight = height;
10799  }
10800 
10801  QPrinter printer(QPrinter::ScreenResolution);
10802  printer.setOutputFileName(fileName);
10803  printer.setOutputFormat(QPrinter::PdfFormat);
10804  printer.setColorMode(QPrinter::Color);
10805  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10806  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName,
10807  pdfTitle);
10808  QRect oldViewport = viewport();
10809  setViewport(QRect(0, 0, newWidth, newHeight));
10810 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
10811  printer.setFullPage(true);
10812  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10813 #else
10814  QPageLayout pageLayout;
10815  pageLayout.setMode(QPageLayout::FullPageMode);
10816  pageLayout.setOrientation(QPageLayout::Portrait);
10817  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
10818  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point,
10819  QString(), QPageSize::ExactMatch));
10820  printer.setPageLayout(pageLayout);
10821 #endif
10822  QCPPainter printpainter;
10823  if (printpainter.begin(&printer)) {
10824  printpainter.setMode(QCPPainter::pmVectorized);
10825  printpainter.setMode(QCPPainter::pmNoCaching);
10826  printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10827  printpainter.setWindow(mViewport);
10828  if (mBackgroundBrush.style() != Qt::NoBrush &&
10829  mBackgroundBrush.color() != Qt::white &&
10830  mBackgroundBrush.color() != Qt::transparent &&
10831  mBackgroundBrush.color().alpha() >
10832  0) // draw pdf background color if not white/transparent
10833  printpainter.fillRect(viewport(), mBackgroundBrush);
10834  draw(&printpainter);
10835  printpainter.end();
10836  success = true;
10837  }
10838  setViewport(oldViewport);
10839 #endif // QT_NO_PRINTER
10840 
10841  return success;
10842 }
10843 
10887 bool QCustomPlot::savePng(const QString &fileName,
10888  int width,
10889  int height,
10890  double scale,
10891  int quality) {
10892  return saveRastered(fileName, width, height, scale, "PNG", quality);
10893 }
10894 
10934 bool QCustomPlot::saveJpg(const QString &fileName,
10935  int width,
10936  int height,
10937  double scale,
10938  int quality) {
10939  return saveRastered(fileName, width, height, scale, "JPG", quality);
10940 }
10941 
10978 bool QCustomPlot::saveBmp(const QString &fileName,
10979  int width,
10980  int height,
10981  double scale) {
10982  return saveRastered(fileName, width, height, scale, "BMP");
10983 }
10984 
10995 QSize QCustomPlot::minimumSizeHint() const {
10996  return sizeHint();
10997  // return mPlotLayout->minimumSizeHint();
10998 }
10999 
11005 QSize QCustomPlot::sizeHint() const {
11006  return QSize(200, 200);
11007  // return mPlotLayout->minimumSizeHint();
11008 }
11009 
11015 void QCustomPlot::paintEvent(QPaintEvent *event) {
11016  Q_UNUSED(event);
11017  QPainter painter(this);
11018  painter.drawPixmap(0, 0, mPaintBuffer);
11019 }
11020 
11028 void QCustomPlot::resizeEvent(QResizeEvent *event) {
11029  // resize and repaint the buffer:
11030  mPaintBuffer = QPixmap(event->size());
11031  setViewport(rect());
11032  replot(rpQueued); // queued update is important here, to prevent painting
11033  // issues in some contexts
11034 }
11035 
11045 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event) {
11046  emit mouseDoubleClick(event);
11047 
11048  QVariant details;
11049  QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
11050 
11051  // emit specialized object double click signals:
11052  if (QCPAbstractPlottable *ap =
11053  qobject_cast<QCPAbstractPlottable *>(clickedLayerable))
11054  emit plottableDoubleClick(ap, event);
11055  else if (QCPAxis *ax = qobject_cast<QCPAxis *>(clickedLayerable))
11056  emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(),
11057  event);
11058  else if (QCPAbstractItem *ai =
11059  qobject_cast<QCPAbstractItem *>(clickedLayerable))
11060  emit itemDoubleClick(ai, event);
11061  else if (QCPLegend *lg = qobject_cast<QCPLegend *>(clickedLayerable))
11062  emit legendDoubleClick(lg, 0, event);
11063  else if (QCPAbstractLegendItem *li =
11064  qobject_cast<QCPAbstractLegendItem *>(clickedLayerable))
11065  emit legendDoubleClick(li->parentLegend(), li, event);
11066  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle *>(clickedLayerable))
11067  emit titleDoubleClick(event, pt);
11068 
11069  // call double click event of affected layout element:
11070  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
11072 
11073  // call release event of affected layout element (as in mouseReleaseEvent,
11074  // since the mouseDoubleClick replaces the second release event in double
11075  // click case):
11076  if (mMouseEventElement) {
11077  mMouseEventElement->mouseReleaseEvent(event);
11078  mMouseEventElement = 0;
11079  }
11080 
11081  // QWidget::mouseDoubleClickEvent(event); don't call base class
11082  // implementation because it would just cause a mousePress/ReleaseEvent,
11083  // which we don't want.
11084 }
11085 
11093 void QCustomPlot::mousePressEvent(QMouseEvent *event) {
11094  emit mousePress(event);
11095  mMousePressPos = event->pos(); // need this to determine in releaseEvent
11096  // whether it was a click (no position
11097  // change between press and release)
11098 
11099  // call event of affected layout element:
11101  if (mMouseEventElement) mMouseEventElement->mousePressEvent(event);
11102 
11103  QWidget::mousePressEvent(event);
11104 }
11105 
11116 void QCustomPlot::mouseMoveEvent(QMouseEvent *event) {
11117  emit mouseMove(event);
11118 
11119  showPointToolTip(event);
11120 
11121  // call event of affected layout element:
11122  if (mMouseEventElement) mMouseEventElement->mouseMoveEvent(event);
11123 
11124  QWidget::mouseMoveEvent(event);
11125 }
11126 
11144 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) {
11145  emit mouseRelease(event);
11146  bool doReplot = false;
11147 
11148  if ((mMousePressPos - event->pos()).manhattanLength() <
11149  5) // determine whether it was a click operation
11150  {
11151  if (event->button() == Qt::LeftButton) {
11152  // handle selection mechanism:
11153  QVariant details;
11154  QCPLayerable *clickedLayerable =
11155  layerableAt(event->pos(), true, &details);
11156  bool selectionStateChanged = false;
11157  bool additive = mInteractions.testFlag(QCP::iMultiSelect) &&
11158  event->modifiers().testFlag(mMultiSelectModifier);
11159  // deselect all other layerables if not additive selection:
11160  if (!additive) {
11161  foreach (QCPLayer *layer, mLayers) {
11162  foreach (QCPLayerable *layerable, layer->children()) {
11163  if (layerable != clickedLayerable &&
11164  mInteractions.testFlag(
11165  layerable->selectionCategory())) {
11166  bool selChanged = false;
11167  layerable->deselectEvent(&selChanged);
11168  selectionStateChanged |= selChanged;
11169  }
11170  }
11171  }
11172  }
11173  if (clickedLayerable &&
11174  mInteractions.testFlag(clickedLayerable->selectionCategory())) {
11175  // a layerable was actually clicked, call its selectEvent:
11176  bool selChanged = false;
11177  clickedLayerable->selectEvent(event, additive, details,
11178  &selChanged);
11179  selectionStateChanged |= selChanged;
11180  }
11181  if (selectionStateChanged) {
11182  doReplot = true;
11183  emit selectionChangedByUser();
11184  }
11185  }
11186 
11187  // emit specialized object click signals:
11188  QVariant details;
11189  QCPLayerable *clickedLayerable = layerableAt(
11190  event->pos(), false,
11191  &details); // for these signals, selectability is ignored,
11192  // that's why we call this again with onlySelectable
11193  // set to false
11194  if (QCPAbstractPlottable *ap =
11195  qobject_cast<QCPAbstractPlottable *>(clickedLayerable))
11196  emit plottableClick(ap, event);
11197  else if (QCPAxis *ax = qobject_cast<QCPAxis *>(clickedLayerable))
11198  emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
11199  else if (QCPAbstractItem *ai =
11200  qobject_cast<QCPAbstractItem *>(clickedLayerable))
11201  emit itemClick(ai, event);
11202  else if (QCPLegend *lg = qobject_cast<QCPLegend *>(clickedLayerable))
11203  emit legendClick(lg, 0, event);
11204  else if (QCPAbstractLegendItem *li =
11205  qobject_cast<QCPAbstractLegendItem *>(
11206  clickedLayerable))
11207  emit legendClick(li->parentLegend(), li, event);
11208  else if (QCPPlotTitle *pt =
11209  qobject_cast<QCPPlotTitle *>(clickedLayerable))
11210  emit titleClick(event, pt);
11211  }
11212 
11213  // call event of affected layout element:
11214  if (mMouseEventElement) {
11215  mMouseEventElement->mouseReleaseEvent(event);
11216  mMouseEventElement = 0;
11217  }
11218 
11219  if (doReplot || noAntialiasingOnDrag()) replot();
11220 
11221  QWidget::mouseReleaseEvent(event);
11222 }
11223 
11231 void QCustomPlot::wheelEvent(QWheelEvent *event) {
11232  emit mouseWheel(event);
11233 
11234  // call event of affected layout element:
11236  el->wheelEvent(event);
11237 
11238  QWidget::wheelEvent(event);
11239 }
11240 
11249 void QCustomPlot::draw(QCPPainter *painter) {
11250  // run through layout phases:
11254 
11255  // draw viewport background pixmap:
11256  drawBackground(painter);
11257 
11258  // draw all layered objects (grid, axes, plottables, items, legend,...):
11259  foreach (QCPLayer *layer, mLayers) {
11260  foreach (QCPLayerable *child, layer->children()) {
11261  if (child->realVisibility()) {
11262  painter->save();
11263  painter->setClipRect(child->clipRect().translated(0, -1));
11264  child->applyDefaultAntialiasingHint(painter);
11265  child->draw(painter);
11266  painter->restore();
11267  }
11268  }
11269  }
11270 
11271  /* Debug code to draw all layout element rects
11272  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
11273  {
11274  painter->setBrush(Qt::NoBrush);
11275  painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
11276  painter->drawRect(el->rect());
11277  painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
11278  painter->drawRect(el->outerRect());
11279  }
11280  */
11281 }
11282 
11301 void QCustomPlot::drawBackground(QCPPainter *painter) {
11302  // Note: background color is handled in individual replot/save functions
11303 
11304  // draw background pixmap (on top of fill, if brush specified):
11305  if (!mBackgroundPixmap.isNull()) {
11306  if (mBackgroundScaled) {
11307  // check whether mScaledBackground needs to be updated:
11308  QSize scaledSize(mBackgroundPixmap.size());
11309  scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
11310  if (mScaledBackgroundPixmap.size() != scaledSize)
11313  Qt::SmoothTransformation);
11314  painter->drawPixmap(
11316  QRect(0, 0, mViewport.width(), mViewport.height()) &
11317  mScaledBackgroundPixmap.rect());
11318  } else {
11319  painter->drawPixmap(
11320  mViewport.topLeft(), mBackgroundPixmap,
11321  QRect(0, 0, mViewport.width(), mViewport.height()));
11322  }
11323  }
11324 }
11325 
11332 void QCustomPlot::axisRemoved(QCPAxis *axis) {
11333  if (xAxis == axis) xAxis = 0;
11334  if (xAxis2 == axis) xAxis2 = 0;
11335  if (yAxis == axis) yAxis = 0;
11336  if (yAxis2 == axis) yAxis2 = 0;
11337 
11338  // Note: No need to take care of range drag axes and range zoom axes,
11339  // because they are stored in smart pointers
11340 }
11341 
11347 void QCustomPlot::legendRemoved(QCPLegend *legend) {
11348  if (this->legend == legend) this->legend = 0;
11349 }
11350 
11357 void QCustomPlot::updateLayerIndices() const {
11358  for (int i = 0; i < mLayers.size(); ++i) mLayers.at(i)->mIndex = i;
11359 }
11360 
11374 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos,
11375  bool onlySelectable,
11376  QVariant *selectionDetails) const {
11377  for (int layerIndex = mLayers.size() - 1; layerIndex >= 0; --layerIndex) {
11378  const QList<QCPLayerable *> layerables =
11379  mLayers.at(layerIndex)->children();
11380  double minimumDistance = selectionTolerance() * 1.1;
11381  QCPLayerable *minimumDistanceLayerable = 0;
11382  for (int i = layerables.size() - 1; i >= 0; --i) {
11383  if (!layerables.at(i)->realVisibility()) continue;
11384  QVariant details;
11385  double dist =
11386  layerables.at(i)->selectTest(pos, onlySelectable, &details);
11387  if (dist >= 0 && dist < minimumDistance) {
11388  minimumDistance = dist;
11389  minimumDistanceLayerable = layerables.at(i);
11390  if (selectionDetails) *selectionDetails = details;
11391  }
11392  }
11393  if (minimumDistance < selectionTolerance())
11394  return minimumDistanceLayerable;
11395  }
11396  return 0;
11397 }
11398 
11412 bool QCustomPlot::saveRastered(const QString &fileName,
11413  int width,
11414  int height,
11415  double scale,
11416  const char *format,
11417  int quality) {
11418  QPixmap buffer = toPixmap(width, height, scale);
11419  if (!buffer.isNull())
11420  return buffer.save(fileName, format, quality);
11421  else
11422  return false;
11423 }
11424 
11434 QPixmap QCustomPlot::toPixmap(int width, int height, double scale) {
11435  // this method is somewhat similar to toPainter. Change something here, and
11436  // a change in toPainter might be necessary, too.
11437  int newWidth, newHeight;
11438  if (width == 0 || height == 0) {
11439  newWidth = this->width();
11440  newHeight = this->height();
11441  } else {
11442  newWidth = width;
11443  newHeight = height;
11444  }
11445  int scaledWidth = qRound(scale * newWidth);
11446  int scaledHeight = qRound(scale * newHeight);
11447 
11448  QPixmap result(scaledWidth, scaledHeight);
11449  result.fill(mBackgroundBrush.style() == Qt::SolidPattern
11450  ? mBackgroundBrush.color()
11451  : Qt::transparent); // if using non-solid pattern, make
11452  // transparent now and draw brush
11453  // pattern later
11454  QCPPainter painter;
11455  painter.begin(&result);
11456  if (painter.isActive()) {
11457  QRect oldViewport = viewport();
11458  setViewport(QRect(0, 0, newWidth, newHeight));
11460  if (!qFuzzyCompare(scale, 1.0)) {
11461  if (scale > 1.0) // for scale < 1 we always want cosmetic pens
11462  // where possible, because else lines might
11463  // disappear for very small scales
11465  painter.scale(scale, scale);
11466  }
11467  if (mBackgroundBrush.style() != Qt::SolidPattern &&
11468  mBackgroundBrush.style() !=
11469  Qt::NoBrush) // solid fills were done a few lines above
11470  // with QPixmap::fill
11471  painter.fillRect(mViewport, mBackgroundBrush);
11472  draw(&painter);
11473  setViewport(oldViewport);
11474  painter.end();
11475  } else // might happen if pixmap has width or height zero
11476  {
11477  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11478  return QPixmap();
11479  }
11480  return result;
11481 }
11482 
11497 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) {
11498  // this method is somewhat similar to toPixmap. Change something here, and a
11499  // change in toPixmap might be necessary, too.
11500  int newWidth, newHeight;
11501  if (width == 0 || height == 0) {
11502  newWidth = this->width();
11503  newHeight = this->height();
11504  } else {
11505  newWidth = width;
11506  newHeight = height;
11507  }
11508 
11509  if (painter->isActive()) {
11510  QRect oldViewport = viewport();
11511  setViewport(QRect(0, 0, newWidth, newHeight));
11512  painter->setMode(QCPPainter::pmNoCaching);
11513  if (mBackgroundBrush.style() !=
11514  Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for
11515  // Qt::SolidPattern brush style, so we also draw solid
11516  // fills with fillRect here
11517  painter->fillRect(mViewport, mBackgroundBrush);
11518  draw(painter);
11519  setViewport(oldViewport);
11520  } else
11521  qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11522 }
11523 
11527 
11563 QCPColorGradient::QCPColorGradient(GradientPreset preset)
11564  : mLevelCount(350),
11565  mColorInterpolation(ciRGB),
11566  mPeriodic(false),
11567  mColorBufferInvalidated(true) {
11568  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11569  loadPreset(preset);
11570 }
11571 
11572 /* undocumented operator */
11573 bool QCPColorGradient::operator==(const QCPColorGradient &other) const {
11574  return ((other.mLevelCount == this->mLevelCount) &&
11575  (other.mColorInterpolation == this->mColorInterpolation) &&
11576  (other.mPeriodic == this->mPeriodic) &&
11577  (other.mColorStops == this->mColorStops));
11578 }
11579 
11586 void QCPColorGradient::setLevelCount(int n) {
11587  if (n < 2) {
11588  qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11589  n = 2;
11590  }
11591  if (n != mLevelCount) {
11592  mLevelCount = n;
11593  mColorBufferInvalidated = true;
11594  }
11595 }
11596 
11609 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops) {
11611  mColorBufferInvalidated = true;
11612 }
11613 
11621 void QCPColorGradient::setColorStopAt(double position, const QColor &color) {
11622  mColorStops.insert(position, color);
11623  mColorBufferInvalidated = true;
11624 }
11625 
11634  QCPColorGradient::ColorInterpolation interpolation) {
11635  if (interpolation != mColorInterpolation) {
11636  mColorInterpolation = interpolation;
11637  mColorBufferInvalidated = true;
11638  }
11639 }
11640 
11659 void QCPColorGradient::setPeriodic(bool enabled) { mPeriodic = enabled; }
11660 
11675 void QCPColorGradient::colorize(const double *data,
11676  const QCPRange &range,
11677  QRgb *scanLine,
11678  int n,
11679  int dataIndexFactor,
11680  bool logarithmic) {
11681  // If you change something here, make sure to also adapt ::color()
11682  if (!data) {
11683  qDebug() << Q_FUNC_INFO << "null pointer given as data";
11684  return;
11685  }
11686  if (!scanLine) {
11687  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11688  return;
11689  }
11691 
11692  if (!logarithmic) {
11693  const double posToIndexFactor = (mLevelCount - 1) / range.size();
11694  if (mPeriodic) {
11695  for (int i = 0; i < n; ++i) {
11696  int index = (int)((data[dataIndexFactor * i] - range.lower) *
11697  posToIndexFactor) %
11698  mLevelCount;
11699  if (index < 0) index += mLevelCount;
11700  scanLine[i] = mColorBuffer.at(index);
11701  }
11702  } else {
11703  for (int i = 0; i < n; ++i) {
11704  int index = (data[dataIndexFactor * i] - range.lower) *
11705  posToIndexFactor;
11706  if (index < 0)
11707  index = 0;
11708  else if (index >= mLevelCount)
11709  index = mLevelCount - 1;
11710  scanLine[i] = mColorBuffer.at(index);
11711  }
11712  }
11713  } else // logarithmic == true
11714  {
11715  if (mPeriodic) {
11716  for (int i = 0; i < n; ++i) {
11717  int index = (int)(qLn(data[dataIndexFactor * i] / range.lower) /
11718  qLn(range.upper / range.lower) *
11719  (mLevelCount - 1)) %
11720  mLevelCount;
11721  if (index < 0) index += mLevelCount;
11722  scanLine[i] = mColorBuffer.at(index);
11723  }
11724  } else {
11725  for (int i = 0; i < n; ++i) {
11726  int index = qLn(data[dataIndexFactor * i] / range.lower) /
11727  qLn(range.upper / range.lower) * (mLevelCount - 1);
11728  if (index < 0)
11729  index = 0;
11730  else if (index >= mLevelCount)
11731  index = mLevelCount - 1;
11732  scanLine[i] = mColorBuffer.at(index);
11733  }
11734  }
11735  }
11736 }
11737 
11748 QRgb QCPColorGradient::color(double position,
11749  const QCPRange &range,
11750  bool logarithmic) {
11751  // If you change something here, make sure to also adapt ::colorize()
11753  int index = 0;
11754  if (!logarithmic)
11755  index = (position - range.lower) * (mLevelCount - 1) / range.size();
11756  else
11757  index = qLn(position / range.lower) / qLn(range.upper / range.lower) *
11758  (mLevelCount - 1);
11759  if (mPeriodic) {
11760  index = index % mLevelCount;
11761  if (index < 0) index += mLevelCount;
11762  } else {
11763  if (index < 0)
11764  index = 0;
11765  else if (index >= mLevelCount)
11766  index = mLevelCount - 1;
11767  }
11768  return mColorBuffer.at(index);
11769 }
11770 
11779 void QCPColorGradient::loadPreset(GradientPreset preset) {
11780  clearColorStops();
11781  switch (preset) {
11782  case gpGrayscale:
11786  break;
11787  case gpHot:
11789  setColorStopAt(0, QColor(50, 0, 0));
11790  setColorStopAt(0.2, QColor(180, 10, 0));
11791  setColorStopAt(0.4, QColor(245, 50, 0));
11792  setColorStopAt(0.6, QColor(255, 150, 10));
11793  setColorStopAt(0.8, QColor(255, 255, 50));
11794  setColorStopAt(1, QColor(255, 255, 255));
11795  break;
11796  case gpCold:
11798  setColorStopAt(0, QColor(0, 0, 50));
11799  setColorStopAt(0.2, QColor(0, 10, 180));
11800  setColorStopAt(0.4, QColor(0, 50, 245));
11801  setColorStopAt(0.6, QColor(10, 150, 255));
11802  setColorStopAt(0.8, QColor(50, 255, 255));
11803  setColorStopAt(1, QColor(255, 255, 255));
11804  break;
11805  case gpNight:
11807  setColorStopAt(0, QColor(10, 20, 30));
11808  setColorStopAt(1, QColor(250, 255, 250));
11809  break;
11810  case gpCandy:
11812  setColorStopAt(0, QColor(0, 0, 255));
11813  setColorStopAt(1, QColor(255, 250, 250));
11814  break;
11815  case gpGeography:
11817  setColorStopAt(0, QColor(70, 170, 210));
11818  setColorStopAt(0.20, QColor(90, 160, 180));
11819  setColorStopAt(0.25, QColor(45, 130, 175));
11820  setColorStopAt(0.30, QColor(100, 140, 125));
11821  setColorStopAt(0.5, QColor(100, 140, 100));
11822  setColorStopAt(0.6, QColor(130, 145, 120));
11823  setColorStopAt(0.7, QColor(140, 130, 120));
11824  setColorStopAt(0.9, QColor(180, 190, 190));
11825  setColorStopAt(1, QColor(210, 210, 230));
11826  break;
11827  case gpIon:
11829  setColorStopAt(0, QColor(50, 10, 10));
11830  setColorStopAt(0.45, QColor(0, 0, 255));
11831  setColorStopAt(0.8, QColor(0, 255, 255));
11832  setColorStopAt(1, QColor(0, 255, 0));
11833  break;
11834  case gpThermal:
11836  setColorStopAt(0, QColor(0, 0, 50));
11837  setColorStopAt(0.15, QColor(20, 0, 120));
11838  setColorStopAt(0.33, QColor(200, 30, 140));
11839  setColorStopAt(0.6, QColor(255, 100, 0));
11840  setColorStopAt(0.85, QColor(255, 255, 40));
11841  setColorStopAt(1, QColor(255, 255, 255));
11842  break;
11843  case gpPolar:
11845  setColorStopAt(0, QColor(50, 255, 255));
11846  setColorStopAt(0.18, QColor(10, 70, 255));
11847  setColorStopAt(0.28, QColor(10, 10, 190));
11848  setColorStopAt(0.5, QColor(0, 0, 0));
11849  setColorStopAt(0.72, QColor(190, 10, 10));
11850  setColorStopAt(0.82, QColor(255, 70, 10));
11851  setColorStopAt(1, QColor(255, 255, 50));
11852  break;
11853  case gpSpectrum:
11855  setColorStopAt(0, QColor(50, 0, 50));
11856  setColorStopAt(0.15, QColor(0, 0, 255));
11857  setColorStopAt(0.35, QColor(0, 255, 255));
11858  setColorStopAt(0.6, QColor(255, 255, 0));
11859  setColorStopAt(0.75, QColor(255, 30, 0));
11860  setColorStopAt(1, QColor(50, 0, 0));
11861  break;
11862  case gpJet:
11864  setColorStopAt(0, QColor(0, 0, 100));
11865  setColorStopAt(0.15, QColor(0, 50, 255));
11866  setColorStopAt(0.35, QColor(0, 255, 255));
11867  setColorStopAt(0.65, QColor(255, 255, 0));
11868  setColorStopAt(0.85, QColor(255, 30, 0));
11869  setColorStopAt(1, QColor(100, 0, 0));
11870  break;
11871  case gpHues:
11873  setColorStopAt(0, QColor(255, 0, 0));
11874  setColorStopAt(1.0 / 3.0, QColor(0, 0, 255));
11875  setColorStopAt(2.0 / 3.0, QColor(0, 255, 0));
11876  setColorStopAt(1, QColor(255, 0, 0));
11877  break;
11878  }
11879 }
11880 
11887  mColorStops.clear();
11888  mColorBufferInvalidated = true;
11889 }
11890 
11898  QCPColorGradient result(*this);
11899  result.clearColorStops();
11900  for (QMap<double, QColor>::const_iterator it = mColorStops.constBegin();
11901  it != mColorStops.constEnd(); ++it)
11902  result.setColorStopAt(1.0 - it.key(), it.value());
11903  return result;
11904 }
11905 
11913  if (mColorBuffer.size() != mLevelCount) mColorBuffer.resize(mLevelCount);
11914  if (mColorStops.size() > 1) {
11915  double indexToPosFactor = 1.0 / (double)(mLevelCount - 1);
11916  for (int i = 0; i < mLevelCount; ++i) {
11917  double position = i * indexToPosFactor;
11918  QMap<double, QColor>::const_iterator it =
11919  mColorStops.lowerBound(position);
11920  if (it == mColorStops.constEnd()) // position is on or after last
11921  // stop, use color of last stop
11922  {
11923  mColorBuffer[i] = (it - 1).value().rgb();
11924  } else if (it ==
11925  mColorStops
11926  .constBegin()) // position is on or before first
11927  // stop, use color of first stop
11928  {
11929  mColorBuffer[i] = it.value().rgb();
11930  } else // position is in between stops (or on an intermediate
11931  // stop), interpolate color
11932  {
11933  QMap<double, QColor>::const_iterator high = it;
11934  QMap<double, QColor>::const_iterator low = it - 1;
11935  double t =
11936  (position - low.key()) /
11937  (high.key() - low.key()); // interpolation factor 0..1
11938  switch (mColorInterpolation) {
11939  case ciRGB: {
11940  mColorBuffer[i] = qRgb((1 - t) * low.value().red() +
11941  t * high.value().red(),
11942  (1 - t) * low.value().green() +
11943  t * high.value().green(),
11944  (1 - t) * low.value().blue() +
11945  t * high.value().blue());
11946  break;
11947  }
11948  case ciHSV: {
11949  QColor lowHsv = low.value().toHsv();
11950  QColor highHsv = high.value().toHsv();
11951  double hue = 0;
11952  double hueDiff = highHsv.hueF() - lowHsv.hueF();
11953  if (hueDiff > 0.5)
11954  hue = lowHsv.hueF() - t * (1.0 - hueDiff);
11955  else if (hueDiff < -0.5)
11956  hue = lowHsv.hueF() + t * (1.0 + hueDiff);
11957  else
11958  hue = lowHsv.hueF() + t * hueDiff;
11959  if (hue < 0)
11960  hue += 1.0;
11961  else if (hue >= 1.0)
11962  hue -= 1.0;
11963  mColorBuffer[i] =
11964  QColor::fromHsvF(
11965  hue,
11966  (1 - t) * lowHsv.saturationF() +
11967  t * highHsv.saturationF(),
11968  (1 - t) * lowHsv.valueF() +
11969  t * highHsv.valueF())
11970  .rgb();
11971  break;
11972  }
11973  }
11974  }
11975  }
11976  } else if (mColorStops.size() == 1) {
11977  mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11978  } else // mColorStops is empty, fill color buffer with black
11979  {
11980  mColorBuffer.fill(qRgb(0, 0, 0));
11981  }
11982  mColorBufferInvalidated = false;
11983 }
11984 
11988 
12030 /* start documentation of inline functions */
12031 
12121 /* end documentation of inline functions */
12122 
12127 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes)
12128  : QCPLayoutElement(parentPlot),
12129  mBackgroundBrush(Qt::NoBrush),
12130  mBackgroundScaled(true),
12131  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
12132  mInsetLayout(new QCPLayoutInset),
12133  mRangeDrag(Qt::Horizontal | Qt::Vertical),
12134  mRangeZoom(Qt::Horizontal | Qt::Vertical),
12135  mRangeZoomFactorHorz(0.85),
12136  mRangeZoomFactorVert(0.85),
12137  mDragging(false) {
12138  mInsetLayout->initializeParentPlot(mParentPlot);
12139  mInsetLayout->setParentLayerable(this);
12140  mInsetLayout->setParent(this);
12141 
12142  setMinimumSize(50, 50);
12143  setMinimumMargins(QMargins(15, 15, 15, 15));
12144  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis *>());
12145  mAxes.insert(QCPAxis::atRight, QList<QCPAxis *>());
12146  mAxes.insert(QCPAxis::atTop, QList<QCPAxis *>());
12147  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis *>());
12148 
12149  if (setupDefaultAxes) {
12150  QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
12151  QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
12152  QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
12153  QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
12154  setRangeDragAxes(xAxis, yAxis);
12155  setRangeZoomAxes(xAxis, yAxis);
12156  xAxis2->setVisible(false);
12157  yAxis2->setVisible(false);
12158  xAxis->grid()->setVisible(true);
12159  yAxis->grid()->setVisible(true);
12160  xAxis2->grid()->setVisible(false);
12161  yAxis2->grid()->setVisible(false);
12162  xAxis2->grid()->setZeroLinePen(Qt::NoPen);
12163  yAxis2->grid()->setZeroLinePen(Qt::NoPen);
12164  xAxis2->grid()->setVisible(false);
12165  yAxis2->grid()->setVisible(false);
12166  }
12167 }
12168 
12170  delete mInsetLayout;
12171  mInsetLayout = 0;
12172 
12173  QList<QCPAxis *> axesList = axes();
12174  for (int i = 0; i < axesList.size(); ++i) removeAxis(axesList.at(i));
12175 }
12176 
12183  return mAxes.value(type).size();
12184 }
12185 
12192 QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const {
12193  QList<QCPAxis *> ax(mAxes.value(type));
12194  if (index >= 0 && index < ax.size()) {
12195  return ax.at(index);
12196  } else {
12197  qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
12198  return 0;
12199  }
12200 }
12201 
12210 QList<QCPAxis *> QCPAxisRect::axes(QCPAxis::AxisTypes types) const {
12211  QList<QCPAxis *> result;
12212  if (types.testFlag(QCPAxis::atLeft)) result << mAxes.value(QCPAxis::atLeft);
12213  if (types.testFlag(QCPAxis::atRight))
12214  result << mAxes.value(QCPAxis::atRight);
12215  if (types.testFlag(QCPAxis::atTop)) result << mAxes.value(QCPAxis::atTop);
12216  if (types.testFlag(QCPAxis::atBottom))
12217  result << mAxes.value(QCPAxis::atBottom);
12218  return result;
12219 }
12220 
12225 QList<QCPAxis *> QCPAxisRect::axes() const {
12226  QList<QCPAxis *> result;
12227  QHashIterator<QCPAxis::AxisType, QList<QCPAxis *>> it(mAxes);
12228  while (it.hasNext()) {
12229  it.next();
12230  result << it.value();
12231  }
12232  return result;
12233 }
12234 
12258  QCPAxis *newAxis = axis;
12259  if (!newAxis) {
12260  newAxis = new QCPAxis(this, type);
12261  } else // user provided existing axis instance, do some sanity checks
12262  {
12263  if (newAxis->axisType() != type) {
12264  qDebug() << Q_FUNC_INFO
12265  << "passed axis has different axis type than specified in "
12266  "type parameter";
12267  return 0;
12268  }
12269  if (newAxis->axisRect() != this) {
12270  qDebug() << Q_FUNC_INFO
12271  << "passed axis doesn't have this axis rect as parent "
12272  "axis rect";
12273  return 0;
12274  }
12275  if (axes().contains(newAxis)) {
12276  qDebug() << Q_FUNC_INFO
12277  << "passed axis is already owned by this axis rect";
12278  return 0;
12279  }
12280  }
12281  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis
12282  // ending to additional axes with offset
12283  {
12284  bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
12285  newAxis->setLowerEnding(
12286  QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
12287  newAxis->setUpperEnding(
12288  QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
12289  }
12290  mAxes[type].append(newAxis);
12291  return newAxis;
12292 }
12293 
12303 QList<QCPAxis *> QCPAxisRect::addAxes(QCPAxis::AxisTypes types) {
12304  QList<QCPAxis *> result;
12305  if (types.testFlag(QCPAxis::atLeft)) result << addAxis(QCPAxis::atLeft);
12306  if (types.testFlag(QCPAxis::atRight)) result << addAxis(QCPAxis::atRight);
12307  if (types.testFlag(QCPAxis::atTop)) result << addAxis(QCPAxis::atTop);
12308  if (types.testFlag(QCPAxis::atBottom)) result << addAxis(QCPAxis::atBottom);
12309  return result;
12310 }
12311 
12319 bool QCPAxisRect::removeAxis(QCPAxis *axis) {
12320  // don't access axis->axisType() to provide safety when axis is an invalid
12321  // pointer, rather go through all axis containers:
12322  QHashIterator<QCPAxis::AxisType, QList<QCPAxis *>> it(mAxes);
12323  while (it.hasNext()) {
12324  it.next();
12325  if (it.value().contains(axis)) {
12326  mAxes[it.key()].removeOne(axis);
12327  if (qobject_cast<QCustomPlot *>(
12328  parentPlot())) // make sure this isn't called from
12329  // QObject dtor when QCustomPlot is
12330  // already destructed (happens when the
12331  // axis rect is not in any layout and
12332  // thus QObject-child of QCustomPlot)
12334  delete axis;
12335  return true;
12336  }
12337  }
12338  qDebug() << Q_FUNC_INFO
12339  << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
12340  return false;
12341 }
12342 
12371 void QCPAxisRect::setupFullAxesBox(bool connectRanges) {
12372  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
12373  if (axisCount(QCPAxis::atBottom) == 0)
12374  xAxis = addAxis(QCPAxis::atBottom);
12375  else
12376  xAxis = axis(QCPAxis::atBottom);
12377 
12378  if (axisCount(QCPAxis::atLeft) == 0)
12379  yAxis = addAxis(QCPAxis::atLeft);
12380  else
12381  yAxis = axis(QCPAxis::atLeft);
12382 
12383  if (axisCount(QCPAxis::atTop) == 0)
12384  xAxis2 = addAxis(QCPAxis::atTop);
12385  else
12386  xAxis2 = axis(QCPAxis::atTop);
12387 
12388  if (axisCount(QCPAxis::atRight) == 0)
12389  yAxis2 = addAxis(QCPAxis::atRight);
12390  else
12391  yAxis2 = axis(QCPAxis::atRight);
12392 
12393  xAxis->setVisible(true);
12394  yAxis->setVisible(true);
12395  xAxis2->setVisible(true);
12396  yAxis2->setVisible(true);
12397  xAxis2->setTickLabels(false);
12398  yAxis2->setTickLabels(false);
12399 
12400  xAxis2->setRange(xAxis->range());
12401  xAxis2->setRangeReversed(xAxis->rangeReversed());
12402  xAxis2->setScaleType(xAxis->scaleType());
12403  xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12404  xAxis2->setTicks(xAxis->ticks());
12405  xAxis2->setAutoTickCount(xAxis->autoTickCount());
12406  xAxis2->setSubTickCount(xAxis->subTickCount());
12407  xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12408  xAxis2->setTickStep(xAxis->tickStep());
12409  xAxis2->setAutoTickStep(xAxis->autoTickStep());
12410  xAxis2->setNumberFormat(xAxis->numberFormat());
12411  xAxis2->setNumberPrecision(xAxis->numberPrecision());
12412  xAxis2->setTickLabelType(xAxis->tickLabelType());
12413  xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12414  xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12415 
12416  yAxis2->setRange(yAxis->range());
12417  yAxis2->setRangeReversed(yAxis->rangeReversed());
12418  yAxis2->setScaleType(yAxis->scaleType());
12419  yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12420  yAxis2->setTicks(yAxis->ticks());
12421  yAxis2->setAutoTickCount(yAxis->autoTickCount());
12422  yAxis2->setSubTickCount(yAxis->subTickCount());
12423  yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12424  yAxis2->setTickStep(yAxis->tickStep());
12425  yAxis2->setAutoTickStep(yAxis->autoTickStep());
12426  yAxis2->setNumberFormat(yAxis->numberFormat());
12427  yAxis2->setNumberPrecision(yAxis->numberPrecision());
12428  yAxis2->setTickLabelType(yAxis->tickLabelType());
12429  yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12430  yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12431 
12432  if (connectRanges) {
12433  connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2,
12434  SLOT(setRange(QCPRange)));
12435  connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2,
12436  SLOT(setRange(QCPRange)));
12437  }
12438 }
12439 
12448 QList<QCPAbstractPlottable *> QCPAxisRect::plottables() const {
12449  // Note: don't append all QCPAxis::plottables() into a list, because we
12450  // might get duplicate entries
12451  QList<QCPAbstractPlottable *> result;
12452  for (int i = 0; i < mParentPlot->mPlottables.size(); ++i) {
12453  if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||
12454  mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12455  result.append(mParentPlot->mPlottables.at(i));
12456  }
12457  return result;
12458 }
12459 
12468 QList<QCPGraph *> QCPAxisRect::graphs() const {
12469  // Note: don't append all QCPAxis::graphs() into a list, because we might
12470  // get duplicate entries
12471  QList<QCPGraph *> result;
12472  for (int i = 0; i < mParentPlot->mGraphs.size(); ++i) {
12473  if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this ||
12474  mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12475  result.append(mParentPlot->mGraphs.at(i));
12476  }
12477  return result;
12478 }
12479 
12491 QList<QCPAbstractItem *> QCPAxisRect::items() const {
12492  // Note: don't just append all QCPAxis::items() into a list, because we
12493  // might get duplicate entries
12494  // and miss those items that have this axis rect as clipAxisRect.
12495  QList<QCPAbstractItem *> result;
12496  for (int itemId = 0; itemId < mParentPlot->mItems.size(); ++itemId) {
12497  if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this) {
12498  result.append(mParentPlot->mItems.at(itemId));
12499  continue;
12500  }
12501  QList<QCPItemPosition *> positions =
12502  mParentPlot->mItems.at(itemId)->positions();
12503  for (int posId = 0; posId < positions.size(); ++posId) {
12504  if (positions.at(posId)->axisRect() == this ||
12505  positions.at(posId)->keyAxis()->axisRect() == this ||
12506  positions.at(posId)->valueAxis()->axisRect() == this) {
12507  result.append(mParentPlot->mItems.at(itemId));
12508  break;
12509  }
12510  }
12511  }
12512  return result;
12513 }
12514 
12523 void QCPAxisRect::update(UpdatePhase phase) {
12524  QCPLayoutElement::update(phase);
12525 
12526  switch (phase) {
12527  case upPreparation: {
12528  QList<QCPAxis *> allAxes = axes();
12529  for (int i = 0; i < allAxes.size(); ++i)
12530  allAxes.at(i)->setupTickVectors();
12531  break;
12532  }
12533  case upLayout: {
12535  break;
12536  }
12537  default:
12538  break;
12539  }
12540 
12541  // pass update call on to inset layout (doesn't happen automatically,
12542  // because QCPAxisRect doesn't derive from QCPLayout):
12543  mInsetLayout->update(phase);
12544 }
12545 
12546 /* inherits documentation from base class */
12547 QList<QCPLayoutElement *> QCPAxisRect::elements(bool recursive) const {
12548  QList<QCPLayoutElement *> result;
12549  if (mInsetLayout) {
12550  result << mInsetLayout;
12551  if (recursive) result << mInsetLayout->elements(recursive);
12552  }
12553  return result;
12554 }
12555 
12556 /* inherits documentation from base class */
12558  painter->setAntialiasing(false);
12559 }
12560 
12561 /* inherits documentation from base class */
12562 void QCPAxisRect::draw(QCPPainter *painter) { drawBackground(painter); }
12563 
12582 void QCPAxisRect::setBackground(const QPixmap &pm) {
12583  mBackgroundPixmap = pm;
12584  mScaledBackgroundPixmap = QPixmap();
12585 }
12586 
12600 void QCPAxisRect::setBackground(const QBrush &brush) {
12601  mBackgroundBrush = brush;
12602 }
12603 
12612 void QCPAxisRect::setBackground(const QPixmap &pm,
12613  bool scaled,
12614  Qt::AspectRatioMode mode) {
12615  mBackgroundPixmap = pm;
12616  mScaledBackgroundPixmap = QPixmap();
12617  mBackgroundScaled = scaled;
12618  mBackgroundScaledMode = mode;
12619 }
12620 
12633 void QCPAxisRect::setBackgroundScaled(bool scaled) {
12634  mBackgroundScaled = scaled;
12635 }
12636 
12643 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode) {
12644  mBackgroundScaledMode = mode;
12645 }
12646 
12652 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation) {
12653  return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data()
12654  : mRangeDragVertAxis.data());
12655 }
12656 
12662 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation) {
12663  return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data()
12664  : mRangeZoomVertAxis.data());
12665 }
12666 
12672 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation) {
12673  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz
12675 }
12676 
12695 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations) {
12696  mRangeDrag = orientations;
12697 }
12698 
12717 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations) {
12718  mRangeZoom = orientations;
12719 }
12720 
12727 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical) {
12728  mRangeDragHorzAxis = horizontal;
12729  mRangeDragVertAxis = vertical;
12730 }
12731 
12740 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical) {
12741  mRangeZoomHorzAxis = horizontal;
12742  mRangeZoomVertAxis = vertical;
12743 }
12744 
12756 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor,
12757  double verticalFactor) {
12758  mRangeZoomFactorHorz = horizontalFactor;
12759  mRangeZoomFactorVert = verticalFactor;
12760 }
12761 
12766 void QCPAxisRect::setRangeZoomFactor(double factor) {
12767  mRangeZoomFactorHorz = factor;
12768  mRangeZoomFactorVert = factor;
12769 }
12770 
12791 void QCPAxisRect::drawBackground(QCPPainter *painter) {
12792  // draw background fill:
12793  if (mBackgroundBrush != Qt::NoBrush)
12794  painter->fillRect(mRect, mBackgroundBrush);
12795 
12796  // draw background pixmap (on top of fill, if brush specified):
12797  if (!mBackgroundPixmap.isNull()) {
12798  if (mBackgroundScaled) {
12799  // check whether mScaledBackground needs to be updated:
12800  QSize scaledSize(mBackgroundPixmap.size());
12801  scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12802  if (mScaledBackgroundPixmap.size() != scaledSize)
12804  mRect.size(), mBackgroundScaledMode,
12805  Qt::SmoothTransformation);
12806  painter->drawPixmap(mRect.topLeft() + QPoint(0, -1),
12808  QRect(0, 0, mRect.width(), mRect.height()) &
12809  mScaledBackgroundPixmap.rect());
12810  } else {
12811  painter->drawPixmap(mRect.topLeft() + QPoint(0, -1),
12813  QRect(0, 0, mRect.width(), mRect.height()));
12814  }
12815  }
12816 }
12817 
12830  const QList<QCPAxis *> axesList = mAxes.value(type);
12831  if (axesList.isEmpty()) return;
12832 
12833  bool isFirstVisible =
12834  !axesList.first()
12835  ->visible(); // if the first axis is visible, the second
12836  // axis (which is where the loop starts)
12837  // isn't the first visible axis, so
12838  // initialize with false
12839  for (int i = 1; i < axesList.size(); ++i) {
12840  int offset = axesList.at(i - 1)->offset() +
12841  axesList.at(i - 1)->calculateMargin();
12842  if (axesList.at(i)->visible()) // only add inner tick length to offset
12843  // if this axis is visible and it's not
12844  // the first visible one (might happen
12845  // if true first axis is invisible)
12846  {
12847  if (!isFirstVisible) offset += axesList.at(i)->tickLengthIn();
12848  isFirstVisible = false;
12849  }
12850  axesList.at(i)->setOffset(offset);
12851  }
12852 }
12853 
12854 /* inherits documentation from base class */
12856  if (!mAutoMargins.testFlag(side))
12857  qDebug() << Q_FUNC_INFO
12858  << "Called with side that isn't specified as auto margin";
12859 
12861 
12862  // note: only need to look at the last (outer most) axis to determine the
12863  // total margin, due to updateAxisOffset call
12864  const QList<QCPAxis *> axesList =
12865  mAxes.value(QCPAxis::marginSideToAxisType(side));
12866  if (axesList.size() > 0)
12867  return axesList.last()->offset() + axesList.last()->calculateMargin();
12868  else
12869  return 0;
12870 }
12871 
12885  mDragStart = event->pos(); // need this even when not LeftButton is
12886  // pressed, to determine in releaseEvent whether
12887  // it was a full click (no position change
12888  // between press and release)
12889  if (event->buttons() & Qt::LeftButton) {
12890  mDragging = true;
12891  // initialize antialiasing backup in case we start dragging:
12895  }
12896  // Mouse range dragging interaction:
12897  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) {
12898  if (mRangeDragHorzAxis)
12899  mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12900  if (mRangeDragVertAxis)
12901  mDragStartVertRange = mRangeDragVertAxis.data()->range();
12902  }
12903  }
12904 }
12905 
12915  // Mouse range dragging interaction:
12916  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag)) {
12917  if (mRangeDrag.testFlag(Qt::Horizontal)) {
12918  if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data()) {
12919  if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear) {
12920  double diff =
12921  rangeDragHorzAxis->pixelToCoord(mDragStart.x()) -
12922  rangeDragHorzAxis->pixelToCoord(event->pos().x());
12923  rangeDragHorzAxis->setRange(
12924  mDragStartHorzRange.lower + diff,
12925  mDragStartHorzRange.upper + diff);
12926  } else if (rangeDragHorzAxis->mScaleType ==
12928  double diff =
12929  rangeDragHorzAxis->pixelToCoord(mDragStart.x()) /
12930  rangeDragHorzAxis->pixelToCoord(event->pos().x());
12931  rangeDragHorzAxis->setRange(
12932  mDragStartHorzRange.lower * diff,
12933  mDragStartHorzRange.upper * diff);
12934  }
12935  }
12936  }
12937  if (mRangeDrag.testFlag(Qt::Vertical)) {
12938  if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data()) {
12939  if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear) {
12940  double diff =
12941  rangeDragVertAxis->pixelToCoord(mDragStart.y()) -
12942  rangeDragVertAxis->pixelToCoord(event->pos().y());
12943  rangeDragVertAxis->setRange(
12944  mDragStartVertRange.lower + diff,
12945  mDragStartVertRange.upper + diff);
12946  } else if (rangeDragVertAxis->mScaleType ==
12948  double diff =
12949  rangeDragVertAxis->pixelToCoord(mDragStart.y()) /
12950  rangeDragVertAxis->pixelToCoord(event->pos().y());
12951  rangeDragVertAxis->setRange(
12952  mDragStartVertRange.lower * diff,
12953  mDragStartVertRange.upper * diff);
12954  }
12955  }
12956  }
12957  if (mRangeDrag != 0) // if either vertical or horizontal drag was
12958  // enabled, do a replot
12959  {
12962  mParentPlot->replot();
12963  }
12964  }
12965 }
12966 
12967 /* inherits documentation from base class */
12969  Q_UNUSED(event)
12970  mDragging = false;
12974  }
12975 }
12976 
12994 void QCPAxisRect::wheelEvent(QWheelEvent *event) {
12995  // Mouse range zooming interaction:
12996  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom)) {
12997  if (mRangeZoom != 0) {
12998  double factor;
12999  double wheelSteps = qtCompatWheelEventDelta(event) /
13000  120.0; // a single step delta is +/-120 usually
13001  QPointF wheelPos = qtCompatWheelEventPos(event);
13002  if (mRangeZoom.testFlag(Qt::Horizontal)) {
13003  factor = qPow(mRangeZoomFactorHorz, wheelSteps);
13004  if (mRangeZoomHorzAxis.data())
13005  mRangeZoomHorzAxis.data()->scaleRange(
13006  factor, mRangeZoomHorzAxis.data()->pixelToCoord(
13007  wheelPos.x()));
13008  }
13009  if (mRangeZoom.testFlag(Qt::Vertical)) {
13010  factor = qPow(mRangeZoomFactorVert, wheelSteps);
13011  if (mRangeZoomVertAxis.data())
13012  mRangeZoomVertAxis.data()->scaleRange(
13013  factor, mRangeZoomVertAxis.data()->pixelToCoord(
13014  wheelPos.y()));
13015  }
13016  mParentPlot->replot();
13017  }
13018  }
13019 }
13020 
13024 
13051 /* start of documentation of signals */
13052 
13059 /* end of documentation of signals */
13060 
13067  : QCPLayoutElement(parent->parentPlot()),
13068  mParentLegend(parent),
13069  mFont(parent->font()),
13070  mTextColor(parent->textColor()),
13071  mSelectedFont(parent->selectedFont()),
13072  mSelectedTextColor(parent->selectedTextColor()),
13073  mSelectable(true),
13074  mSelected(false) {
13075  setLayer(QLatin1String("legend"));
13076  setMargins(QMargins(8, 2, 8, 2));
13077 }
13078 
13084 void QCPAbstractLegendItem::setFont(const QFont &font) { mFont = font; }
13085 
13091 void QCPAbstractLegendItem::setTextColor(const QColor &color) {
13092  mTextColor = color;
13093 }
13094 
13101 void QCPAbstractLegendItem::setSelectedFont(const QFont &font) {
13102  mSelectedFont = font;
13103 }
13104 
13113 }
13114 
13120 void QCPAbstractLegendItem::setSelectable(bool selectable) {
13121  if (mSelectable != selectable) {
13124  }
13125 }
13126 
13135 void QCPAbstractLegendItem::setSelected(bool selected) {
13136  if (mSelected != selected) {
13137  mSelected = selected;
13139  }
13140 }
13141 
13142 /* inherits documentation from base class */
13143 double QCPAbstractLegendItem::selectTest(const QPointF &pos,
13144  bool onlySelectable,
13145  QVariant *details) const {
13146  Q_UNUSED(details)
13147  if (!mParentPlot) return -1;
13148  if (onlySelectable &&
13149  (!mSelectable ||
13151  return -1;
13152 
13153  if (mRect.contains(pos.toPoint()))
13154  return mParentPlot->selectionTolerance() * 0.99;
13155  else
13156  return -1;
13157 }
13158 
13159 /* inherits documentation from base class */
13161  QCPPainter *painter) const {
13163 }
13164 
13165 /* inherits documentation from base class */
13166 QRect QCPAbstractLegendItem::clipRect() const { return mOuterRect; }
13167 
13168 /* inherits documentation from base class */
13169 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event,
13170  bool additive,
13171  const QVariant &details,
13172  bool *selectionStateChanged) {
13173  Q_UNUSED(event)
13174  Q_UNUSED(details)
13175  if (mSelectable &&
13177  bool selBefore = mSelected;
13178  setSelected(additive ? !mSelected : true);
13179  if (selectionStateChanged)
13180  *selectionStateChanged = mSelected != selBefore;
13181  }
13182 }
13183 
13184 /* inherits documentation from base class */
13185 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged) {
13186  if (mSelectable &&
13188  bool selBefore = mSelected;
13189  setSelected(false);
13190  if (selectionStateChanged)
13191  *selectionStateChanged = mSelected != selBefore;
13192  }
13193 }
13194 
13198 
13238  QCPAbstractPlottable *plottable)
13239  : QCPAbstractLegendItem(parent), mPlottable(plottable) {}
13240 
13249 }
13250 
13256 QColor QCPPlottableLegendItem::getTextColor() const {
13258 }
13259 
13265 QFont QCPPlottableLegendItem::getFont() const {
13266  return mSelected ? mSelectedFont : mFont;
13267 }
13268 
13276  if (!mPlottable) return;
13277  painter->setFont(getFont());
13278  painter->setPen(QPen(getTextColor()));
13279  QSizeF iconSize = mParentLegend->iconSize();
13280  QRectF textRect = painter->fontMetrics().boundingRect(
13281  0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
13282  QRectF iconRect(mRect.topLeft(), iconSize);
13283  int textHeight =
13284  qMax(textRect.height(),
13285  iconSize.height()); // if text has smaller height than icon,
13286  // center text vertically in icon height,
13287  // else align tops
13288  painter->drawText(
13289  mRect.x() + iconSize.width() + mParentLegend->iconTextPadding(),
13290  mRect.y(), textRect.width(), textHeight, Qt::TextDontClip,
13291  mPlottable->name());
13292  // draw icon:
13293  painter->save();
13294  painter->setClipRect(iconRect, Qt::IntersectClip);
13295  mPlottable->drawLegendIcon(painter, iconRect);
13296  painter->restore();
13297  // draw icon border:
13298  if (getIconBorderPen().style() != Qt::NoPen) {
13299  painter->setPen(getIconBorderPen());
13300  painter->setBrush(Qt::NoBrush);
13301  painter->drawRect(iconRect);
13302  }
13303 }
13304 
13311  if (!mPlottable) return QSize();
13312  QSize result(0, 0);
13313  QRect textRect;
13314  QFontMetrics fontMetrics(getFont());
13315  QSize iconSize = mParentLegend->iconSize();
13316  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(),
13317  Qt::TextDontClip, mPlottable->name());
13318  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() +
13319  textRect.width() + mMargins.left() + mMargins.right());
13320  result.setHeight(qMax(textRect.height(), iconSize.height()) +
13321  mMargins.top() + mMargins.bottom());
13322  return result;
13323 }
13324 
13328 
13359 /* start of documentation of signals */
13360 
13368 /* end of documentation of signals */
13369 
13378  setRowSpacing(0);
13379  setColumnSpacing(10);
13380  setMargins(QMargins(2, 3, 2, 2));
13381  setAntialiased(false);
13382  setIconSize(32, 18);
13383 
13384  setIconTextPadding(7);
13385 
13388 
13389  setBorderPen(QPen(Qt::black));
13390  setSelectedBorderPen(QPen(Qt::blue, 2));
13391  setIconBorderPen(Qt::NoPen);
13397 }
13398 
13400  clearItems();
13401  if (qobject_cast<QCustomPlot *>(
13402  mParentPlot)) // make sure this isn't called from QObject dtor
13403  // when QCustomPlot is already destructed
13404  // (happens when the legend is not in any layout
13405  // and thus QObject-child of QCustomPlot)
13406  mParentPlot->legendRemoved(this);
13407 }
13408 
13409 /* no doc for getter, see setSelectedParts */
13410 QCPLegend::SelectableParts QCPLegend::selectedParts() const {
13411  // check whether any legend elements selected, if yes, add spItems to return
13412  // value
13413  bool hasSelectedItems = false;
13414  for (int i = 0; i < itemCount(); ++i) {
13415  if (item(i) && item(i)->selected()) {
13416  hasSelectedItems = true;
13417  break;
13418  }
13419  }
13420  if (hasSelectedItems)
13421  return mSelectedParts | spItems;
13422  else
13423  return mSelectedParts & ~spItems;
13424 }
13425 
13429 void QCPLegend::setBorderPen(const QPen &pen) { mBorderPen = pen; }
13430 
13434 void QCPLegend::setBrush(const QBrush &brush) { mBrush = brush; }
13435 
13445 void QCPLegend::setFont(const QFont &font) {
13446  mFont = font;
13447  for (int i = 0; i < itemCount(); ++i) {
13448  if (item(i)) item(i)->setFont(mFont);
13449  }
13450 }
13451 
13461 void QCPLegend::setTextColor(const QColor &color) {
13462  mTextColor = color;
13463  for (int i = 0; i < itemCount(); ++i) {
13464  if (item(i)) item(i)->setTextColor(color);
13465  }
13466 }
13467 
13472 void QCPLegend::setIconSize(const QSize &size) { mIconSize = size; }
13473 
13476 void QCPLegend::setIconSize(int width, int height) {
13477  mIconSize.setWidth(width);
13478  mIconSize.setHeight(height);
13479 }
13480 
13486 void QCPLegend::setIconTextPadding(int padding) { mIconTextPadding = padding; }
13487 
13495 void QCPLegend::setIconBorderPen(const QPen &pen) { mIconBorderPen = pen; }
13496 
13508 void QCPLegend::setSelectableParts(const SelectableParts &selectable) {
13509  if (mSelectableParts != selectable) {
13510  mSelectableParts = selectable;
13512  }
13513 }
13514 
13539 void QCPLegend::setSelectedParts(const SelectableParts &selected) {
13540  SelectableParts newSelected = selected;
13541  mSelectedParts = this->selectedParts(); // update mSelectedParts in case
13542  // item selection changed
13543 
13544  if (mSelectedParts != newSelected) {
13545  if (!mSelectedParts.testFlag(spItems) &&
13546  newSelected.testFlag(
13547  spItems)) // attempt to set spItems flag (can't do that)
13548  {
13549  qDebug() << Q_FUNC_INFO
13550  << "spItems flag can not be set, it can only be unset "
13551  "with this function";
13552  newSelected &= ~spItems;
13553  }
13554  if (mSelectedParts.testFlag(spItems) &&
13555  !newSelected.testFlag(spItems)) // spItems flag was unset, so clear
13556  // item selection
13557  {
13558  for (int i = 0; i < itemCount(); ++i) {
13559  if (item(i)) item(i)->setSelected(false);
13560  }
13561  }
13562  mSelectedParts = newSelected;
13564  }
13565 }
13566 
13573 void QCPLegend::setSelectedBorderPen(const QPen &pen) {
13574  mSelectedBorderPen = pen;
13575 }
13576 
13583 void QCPLegend::setSelectedIconBorderPen(const QPen &pen) {
13584  mSelectedIconBorderPen = pen;
13585 }
13586 
13593 void QCPLegend::setSelectedBrush(const QBrush &brush) {
13595 }
13596 
13604 void QCPLegend::setSelectedFont(const QFont &font) {
13605  mSelectedFont = font;
13606  for (int i = 0; i < itemCount(); ++i) {
13607  if (item(i)) item(i)->setSelectedFont(font);
13608  }
13609 }
13610 
13619 void QCPLegend::setSelectedTextColor(const QColor &color) {
13621  for (int i = 0; i < itemCount(); ++i) {
13622  if (item(i)) item(i)->setSelectedTextColor(color);
13623  }
13624 }
13625 
13631 QCPAbstractLegendItem *QCPLegend::item(int index) const {
13632  return qobject_cast<QCPAbstractLegendItem *>(elementAt(index));
13633 }
13634 
13642  const QCPAbstractPlottable *plottable) const {
13643  for (int i = 0; i < itemCount(); ++i) {
13644  if (QCPPlottableLegendItem *pli =
13645  qobject_cast<QCPPlottableLegendItem *>(item(i))) {
13646  if (pli->plottable() == plottable) return pli;
13647  }
13648  }
13649  return 0;
13650 }
13651 
13656 int QCPLegend::itemCount() const { return elementCount(); }
13657 
13661 bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const {
13662  for (int i = 0; i < itemCount(); ++i) {
13663  if (item == this->item(i)) return true;
13664  }
13665  return false;
13666 }
13667 
13676  const QCPAbstractPlottable *plottable) const {
13677  return itemWithPlottable(plottable);
13678 }
13679 
13689  if (!hasItem(item)) {
13690  return addElement(rowCount(), 0, item);
13691  } else
13692  return false;
13693 }
13694 
13702 bool QCPLegend::removeItem(int index) {
13703  if (QCPAbstractLegendItem *ali = item(index)) {
13704  bool success = remove(ali);
13705  simplify();
13706  return success;
13707  } else
13708  return false;
13709 }
13710 
13720  bool success = remove(item);
13721  simplify();
13722  return success;
13723 }
13724 
13728 void QCPLegend::clearItems() {
13729  for (int i = itemCount() - 1; i >= 0; --i) removeItem(i);
13730 }
13731 
13738 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const {
13739  QList<QCPAbstractLegendItem *> result;
13740  for (int i = 0; i < itemCount(); ++i) {
13741  if (QCPAbstractLegendItem *ali = item(i)) {
13742  if (ali->selected()) result.append(ali);
13743  }
13744  }
13745  return result;
13746 }
13747 
13764 }
13765 
13771 QPen QCPLegend::getBorderPen() const {
13772  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen
13773  : mBorderPen;
13774 }
13775 
13781 QBrush QCPLegend::getBrush() const {
13782  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13783 }
13784 
13790 void QCPLegend::draw(QCPPainter *painter) {
13791  // draw background rect:
13792  painter->setBrush(getBrush());
13793  painter->setPen(getBorderPen());
13794  painter->drawRect(mOuterRect);
13795 }
13796 
13797 /* inherits documentation from base class */
13798 double QCPLegend::selectTest(const QPointF &pos,
13799  bool onlySelectable,
13800  QVariant *details) const {
13801  if (!mParentPlot) return -1;
13802  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox)) return -1;
13803 
13804  if (mOuterRect.contains(pos.toPoint())) {
13805  if (details) details->setValue(spLegendBox);
13806  return mParentPlot->selectionTolerance() * 0.99;
13807  }
13808  return -1;
13809 }
13810 
13811 /* inherits documentation from base class */
13812 void QCPLegend::selectEvent(QMouseEvent *event,
13813  bool additive,
13814  const QVariant &details,
13815  bool *selectionStateChanged) {
13816  Q_UNUSED(event)
13817  mSelectedParts = selectedParts(); // in case item selection has changed
13818  if (details.value<SelectablePart>() == spLegendBox &&
13819  mSelectableParts.testFlag(spLegendBox)) {
13820  SelectableParts selBefore = mSelectedParts;
13822  additive ? mSelectedParts ^ spLegendBox
13823  : mSelectedParts |
13824  spLegendBox); // no need to unset spItems in
13825  // !additive case, because
13826  // they will be deselected by
13827  // QCustomPlot (they're normal
13828  // QCPLayerables with own
13829  // deselectEvent)
13830  if (selectionStateChanged)
13831  *selectionStateChanged = mSelectedParts != selBefore;
13832  }
13833 }
13834 
13835 /* inherits documentation from base class */
13836 void QCPLegend::deselectEvent(bool *selectionStateChanged) {
13837  mSelectedParts = selectedParts(); // in case item selection has changed
13838  if (mSelectableParts.testFlag(spLegendBox)) {
13839  SelectableParts selBefore = mSelectedParts;
13841  if (selectionStateChanged)
13842  *selectionStateChanged = mSelectedParts != selBefore;
13843  }
13844 }
13845 
13846 /* inherits documentation from base class */
13848  return QCP::iSelectLegend;
13849 }
13850 
13851 /* inherits documentation from base class */
13853  return QCP::iSelectLegend;
13854 }
13855 
13856 /* inherits documentation from base class */
13858  Q_UNUSED(parentPlot)
13859 }
13860 
13864 
13880 /* start documentation of signals */
13881 
13890 /* end documentation of signals */
13891 
13900  : QCPLayoutElement(parentPlot),
13901  mFont(QFont(QLatin1String("sans serif"), 13 * 1.5, QFont::Bold)),
13902  mTextColor(Qt::black),
13903  mSelectedFont(QFont(QLatin1String("sans serif"), 13 * 1.6, QFont::Bold)),
13904  mSelectedTextColor(Qt::blue),
13905  mSelectable(false),
13906  mSelected(false) {
13907  if (parentPlot) {
13909  mFont = QFont(parentPlot->font().family(),
13910  parentPlot->font().pointSize() * 1.5, QFont::Bold);
13911  mSelectedFont =
13912  QFont(parentPlot->font().family(),
13913  parentPlot->font().pointSize() * 1.6, QFont::Bold);
13914  }
13915  setMargins(QMargins(5, 5, 5, 0));
13916 }
13917 
13923 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text)
13924  : QCPLayoutElement(parentPlot),
13925  mText(text),
13926  mFont(QFont(parentPlot->font().family(),
13927  parentPlot->font().pointSize() * 1.5,
13928  QFont::Bold)),
13929  mTextColor(Qt::black),
13930  mSelectedFont(QFont(parentPlot->font().family(),
13931  parentPlot->font().pointSize() * 1.6,
13932  QFont::Bold)),
13933  mSelectedTextColor(Qt::blue),
13934  mSelectable(false),
13935  mSelected(false) {
13936  setLayer(QLatin1String("axes"));
13937  setMargins(QMargins(5, 5, 5, 0));
13938 }
13939 
13946 void QCPPlotTitle::setText(const QString &text) { mText = text; }
13947 
13953 void QCPPlotTitle::setFont(const QFont &font) { mFont = font; }
13954 
13961 
13968 void QCPPlotTitle::setSelectedFont(const QFont &font) { mSelectedFont = font; }
13969 
13978 }
13979 
13986 void QCPPlotTitle::setSelectable(bool selectable) {
13987  if (mSelectable != selectable) {
13990  }
13991 }
13992 
14000 void QCPPlotTitle::setSelected(bool selected) {
14001  if (mSelected != selected) {
14002  mSelected = selected;
14004  }
14005 }
14006 
14007 /* inherits documentation from base class */
14010 }
14011 
14012 /* inherits documentation from base class */
14014  painter->setFont(mainFont());
14015  painter->setPen(QPen(mainTextColor()));
14016  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
14017 }
14018 
14019 /* inherits documentation from base class */
14021  QFontMetrics metrics(mFont);
14022  QSize result =
14023  metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
14024  result.rwidth() += mMargins.left() + mMargins.right();
14025  result.rheight() += mMargins.top() + mMargins.bottom();
14026  return result;
14027 }
14028 
14029 /* inherits documentation from base class */
14031  QFontMetrics metrics(mFont);
14032  QSize result =
14033  metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
14034  result.rheight() += mMargins.top() + mMargins.bottom();
14035  result.setWidth(QWIDGETSIZE_MAX);
14036  return result;
14037 }
14038 
14039 /* inherits documentation from base class */
14041  bool additive,
14042  const QVariant &details,
14043  bool *selectionStateChanged) {
14044  Q_UNUSED(event)
14045  Q_UNUSED(details)
14046  if (mSelectable) {
14047  bool selBefore = mSelected;
14048  setSelected(additive ? !mSelected : true);
14049  if (selectionStateChanged)
14050  *selectionStateChanged = mSelected != selBefore;
14051  }
14052 }
14053 
14054 /* inherits documentation from base class */
14055 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged) {
14056  if (mSelectable) {
14057  bool selBefore = mSelected;
14058  setSelected(false);
14059  if (selectionStateChanged)
14060  *selectionStateChanged = mSelected != selBefore;
14061  }
14062 }
14063 
14064 /* inherits documentation from base class */
14065 double QCPPlotTitle::selectTest(const QPointF &pos,
14066  bool onlySelectable,
14067  QVariant *details) const {
14068  Q_UNUSED(details)
14069  if (onlySelectable && !mSelectable) return -1;
14070 
14071  if (mTextBoundingRect.contains(pos.toPoint()))
14072  return mParentPlot->selectionTolerance() * 0.99;
14073  else
14074  return -1;
14075 }
14076 
14082 QFont QCPPlotTitle::mainFont() const {
14083  return mSelected ? mSelectedFont : mFont;
14084 }
14085 
14093 }
14094 
14098 
14145 /* start documentation of inline functions */
14146 
14162 /* end documentation of signals */
14163 /* start documentation of signals */
14164 
14186 /* end documentation of signals */
14187 
14192  : QCPLayoutElement(parentPlot),
14193  mType(QCPAxis::atTop), // set to atTop such that
14194  // setType(QCPAxis::atRight) below doesn't skip
14195  // work because it thinks it's already atRight
14196  mDataScaleType(QCPAxis::stLinear),
14197  mBarWidth(20),
14198  mAxisRect(new QCPColorScaleAxisRectPrivate(this)) {
14199  setMinimumMargins(QMargins(
14200  0, 6, 0,
14201  6)); // for default right color scale types, keep some room at
14202  // bottom and top (important if no margin group is used)
14203  setType(QCPAxis::atRight);
14204  setDataRange(QCPRange(0, 6));
14205 }
14206 
14208 
14209 /* undocumented getter */
14210 QString QCPColorScale::label() const {
14211  if (!mColorAxis) {
14212  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
14213  return QString();
14214  }
14215 
14216  return mColorAxis.data()->label();
14217 }
14218 
14219 /* undocumented getter */
14220 bool QCPColorScale::rangeDrag() const {
14221  if (!mAxisRect) {
14222  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14223  return false;
14224  }
14225 
14226  return mAxisRect.data()->rangeDrag().testFlag(
14228  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
14229  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))
14230  ->orientation() == QCPAxis::orientation(mType);
14231 }
14232 
14233 /* undocumented getter */
14234 bool QCPColorScale::rangeZoom() const {
14235  if (!mAxisRect) {
14236  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14237  return false;
14238  }
14239 
14240  return mAxisRect.data()->rangeZoom().testFlag(
14242  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
14243  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))
14244  ->orientation() == QCPAxis::orientation(mType);
14245 }
14246 
14256  if (!mAxisRect) {
14257  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14258  return;
14259  }
14260  if (mType != type) {
14261  mType = type;
14262  QCPRange rangeTransfer(0, 6);
14263  double logBaseTransfer = 10;
14264  QString labelTransfer;
14265  // revert some settings on old axis:
14266  if (mColorAxis) {
14267  rangeTransfer = mColorAxis.data()->range();
14268  labelTransfer = mColorAxis.data()->label();
14269  logBaseTransfer = mColorAxis.data()->scaleLogBase();
14270  mColorAxis.data()->setLabel(QString());
14271  disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this,
14272  SLOT(setDataRange(QCPRange)));
14273  disconnect(mColorAxis.data(),
14274  SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this,
14276  }
14277  QList<QCPAxis::AxisType> allAxisTypes =
14278  QList<QCPAxis::AxisType>()
14280  << QCPAxis::atTop;
14281  foreach (QCPAxis::AxisType atype, allAxisTypes) {
14282  mAxisRect.data()->axis(atype)->setTicks(atype == mType);
14283  mAxisRect.data()->axis(atype)->setTickLabels(atype == mType);
14284  }
14285  // set new mColorAxis pointer:
14286  mColorAxis = mAxisRect.data()->axis(mType);
14287  // transfer settings to new axis:
14288  mColorAxis.data()->setRange(
14289  rangeTransfer); // transfer range of old axis to new one
14290  // (necessary if axis changes from vertical to
14291  // horizontal or vice versa)
14292  mColorAxis.data()->setLabel(labelTransfer);
14293  mColorAxis.data()->setScaleLogBase(
14294  logBaseTransfer); // scaleType is synchronized among axes in
14295  // realtime via signals (connected in
14296  // QCPColorScale ctor), so we only need to
14297  // take care of log base here
14298  connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this,
14299  SLOT(setDataRange(QCPRange)));
14300  connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
14301  this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
14302  mAxisRect.data()->setRangeDragAxes(
14303  QCPAxis::orientation(mType) == Qt::Horizontal
14304  ? mColorAxis.data()
14305  : 0,
14306  QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data()
14307  : 0);
14308  }
14309 }
14310 
14321 void QCPColorScale::setDataRange(const QCPRange &dataRange) {
14322  if (mDataRange.lower != dataRange.lower ||
14325  if (mColorAxis) mColorAxis.data()->setRange(mDataRange);
14327  }
14328 }
14329 
14341  if (mDataScaleType != scaleType) {
14342  mDataScaleType = scaleType;
14343  if (mColorAxis) mColorAxis.data()->setScaleType(mDataScaleType);
14347  }
14348 }
14349 
14358 void QCPColorScale::setGradient(const QCPColorGradient &gradient) {
14359  if (mGradient != gradient) {
14360  mGradient = gradient;
14361  if (mAxisRect) mAxisRect.data()->mGradientImageInvalidated = true;
14362  emit gradientChanged(mGradient);
14363  }
14364 }
14365 
14370 void QCPColorScale::setLabel(const QString &str) {
14371  if (!mColorAxis) {
14372  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
14373  return;
14374  }
14375 
14376  mColorAxis.data()->setLabel(str);
14377 }
14378 
14384 
14391 void QCPColorScale::setRangeDrag(bool enabled) {
14392  if (!mAxisRect) {
14393  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14394  return;
14395  }
14396 
14397  if (enabled)
14398  mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14399  else
14400  mAxisRect.data()->setRangeDrag(Qt::Orientation());
14401 }
14402 
14410 void QCPColorScale::setRangeZoom(bool enabled) {
14411  if (!mAxisRect) {
14412  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14413  return;
14414  }
14415 
14416  if (enabled)
14417  mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14418  else
14419  mAxisRect.data()->setRangeZoom(Qt::Orientation());
14420 }
14421 
14425 QList<QCPColorMap *> QCPColorScale::colorMaps() const {
14426  QList<QCPColorMap *> result;
14427  for (int i = 0; i < mParentPlot->plottableCount(); ++i) {
14428  if (QCPColorMap *cm =
14429  qobject_cast<QCPColorMap *>(mParentPlot->plottable(i)))
14430  if (cm->colorScale() == this) result.append(cm);
14431  }
14432  return result;
14433 }
14434 
14441 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps) {
14442  QList<QCPColorMap *> maps = colorMaps();
14443  QCPRange newRange;
14444  bool haveRange = false;
14445  int sign =
14446  0; // TODO: should change this to QCPAbstractPlottable::SignDomain
14447  // later (currently is protected, maybe move to QCP namespace)
14449  sign = (mDataRange.upper < 0 ? -1 : 1);
14450  for (int i = 0; i < maps.size(); ++i) {
14451  if (!maps.at(i)->realVisibility() && onlyVisibleMaps) continue;
14452  QCPRange mapRange;
14453  if (maps.at(i)->colorScale() == this) {
14454  bool currentFoundRange = true;
14455  mapRange = maps.at(i)->data()->dataBounds();
14456  if (sign == 1) {
14457  if (mapRange.lower <= 0 && mapRange.upper > 0)
14458  mapRange.lower = mapRange.upper * 1e-3;
14459  else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14460  currentFoundRange = false;
14461  } else if (sign == -1) {
14462  if (mapRange.upper >= 0 && mapRange.lower < 0)
14463  mapRange.upper = mapRange.lower * 1e-3;
14464  else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14465  currentFoundRange = false;
14466  }
14467  if (currentFoundRange) {
14468  if (!haveRange)
14469  newRange = mapRange;
14470  else
14471  newRange.expand(mapRange);
14472  haveRange = true;
14473  }
14474  }
14475  }
14476  if (haveRange) {
14477  if (!QCPRange::validRange(
14478  newRange)) // likely due to range being zero (plottable has
14479  // only constant data in this dimension), shift
14480  // current range to at least center the data
14481  {
14482  double center = (newRange.lower + newRange.upper) *
14483  0.5; // upper and lower should be equal anyway, but
14484  // just to make sure, incase validRange
14485  // returned false for other reason
14487  newRange.lower = center - mDataRange.size() / 2.0;
14488  newRange.upper = center + mDataRange.size() / 2.0;
14489  } else // mScaleType == stLogarithmic
14490  {
14491  newRange.lower =
14492  center / qSqrt(mDataRange.upper / mDataRange.lower);
14493  newRange.upper =
14494  center * qSqrt(mDataRange.upper / mDataRange.lower);
14495  }
14496  }
14497  setDataRange(newRange);
14498  }
14499 }
14500 
14501 /* inherits documentation from base class */
14502 void QCPColorScale::update(UpdatePhase phase) {
14503  QCPLayoutElement::update(phase);
14504  if (!mAxisRect) {
14505  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14506  return;
14507  }
14508 
14509  mAxisRect.data()->update(phase);
14510 
14511  switch (phase) {
14512  case upMargins: {
14514  setMaximumSize(QWIDGETSIZE_MAX,
14515  mBarWidth + mAxisRect.data()->margins().top() +
14516  mAxisRect.data()->margins().bottom() +
14517  margins().top() + margins().bottom());
14519  mAxisRect.data()->margins().top() +
14520  mAxisRect.data()->margins().bottom() +
14521  margins().top() + margins().bottom());
14522  } else {
14523  setMaximumSize(mBarWidth + mAxisRect.data()->margins().left() +
14524  mAxisRect.data()->margins().right() +
14525  margins().left() + margins().right(),
14526  QWIDGETSIZE_MAX);
14527  setMinimumSize(mBarWidth + mAxisRect.data()->margins().left() +
14528  mAxisRect.data()->margins().right() +
14529  margins().left() + margins().right(),
14530  0);
14531  }
14532  break;
14533  }
14534  case upLayout: {
14535  mAxisRect.data()->setOuterRect(rect());
14536  break;
14537  }
14538  default:
14539  break;
14540  }
14541 }
14542 
14543 /* inherits documentation from base class */
14545  painter->setAntialiasing(false);
14546 }
14547 
14548 /* inherits documentation from base class */
14550  if (!mAxisRect) {
14551  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14552  return;
14553  }
14554  mAxisRect.data()->mousePressEvent(event);
14555 }
14556 
14557 /* inherits documentation from base class */
14559  if (!mAxisRect) {
14560  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14561  return;
14562  }
14563  mAxisRect.data()->mouseMoveEvent(event);
14564 }
14565 
14566 /* inherits documentation from base class */
14568  if (!mAxisRect) {
14569  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14570  return;
14571  }
14572  mAxisRect.data()->mouseReleaseEvent(event);
14573 }
14574 
14575 /* inherits documentation from base class */
14576 void QCPColorScale::wheelEvent(QWheelEvent *event) {
14577  if (!mAxisRect) {
14578  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14579  return;
14580  }
14581  mAxisRect.data()->wheelEvent(event);
14582 }
14583 
14587 
14601 QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(
14602  QCPColorScale *parentColorScale)
14603  : QCPAxisRect(parentColorScale->parentPlot(), true),
14604  mParentColorScale(parentColorScale),
14605  mGradientImageInvalidated(true) {
14606  setParentLayerable(parentColorScale);
14607  setMinimumMargins(QMargins(0, 0, 0, 0));
14608  QList<QCPAxis::AxisType> allAxisTypes =
14609  QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop
14611  foreach (QCPAxis::AxisType type, allAxisTypes) {
14612  axis(type)->setVisible(true);
14613  axis(type)->grid()->setVisible(false);
14614  axis(type)->setPadding(0);
14615  connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)),
14616  this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14617  connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)),
14618  this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14619  }
14620 
14621  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)),
14622  axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14623  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)),
14624  axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14625  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)),
14626  axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14627  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)),
14628  axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14629  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
14630  axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14631  connect(axis(QCPAxis::atRight),
14632  SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft),
14633  SLOT(setScaleType(QCPAxis::ScaleType)));
14634  connect(axis(QCPAxis::atBottom),
14635  SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop),
14636  SLOT(setScaleType(QCPAxis::ScaleType)));
14637  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
14638  axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14639 
14640  // make layer transfers of color scale transfer to axis rect and axes
14641  // the axes must be set after axis rect, such that they appear above color
14642  // gradient drawn by axis rect:
14643  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer *)), this,
14644  SLOT(setLayer(QCPLayer *)));
14645  foreach (QCPAxis::AxisType type, allAxisTypes)
14646  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer *)), axis(type),
14647  SLOT(setLayer(QCPLayer *)));
14648 }
14649 
14656  if (mGradientImageInvalidated) updateGradientImage();
14657 
14658  bool mirrorHorz = false;
14659  bool mirrorVert = false;
14660  if (mParentColorScale->mColorAxis) {
14661  mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() &&
14662  (mParentColorScale->type() == QCPAxis::atBottom ||
14663  mParentColorScale->type() == QCPAxis::atTop);
14664  mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() &&
14665  (mParentColorScale->type() == QCPAxis::atLeft ||
14666  mParentColorScale->type() == QCPAxis::atRight);
14667  }
14668 
14669  painter->drawImage(rect().adjusted(0, -1, 0, -1),
14670  mGradientImage.mirrored(mirrorHorz, mirrorVert));
14671  QCPAxisRect::draw(painter);
14672 }
14673 
14680 void QCPColorScaleAxisRectPrivate::updateGradientImage() {
14681  if (rect().isEmpty()) return;
14682 
14683  int n = mParentColorScale->mGradient.levelCount();
14684  int w, h;
14685  QVector<double> data(n);
14686  for (int i = 0; i < n; ++i) data[i] = i;
14687  if (mParentColorScale->mType == QCPAxis::atBottom ||
14688  mParentColorScale->mType == QCPAxis::atTop) {
14689  w = n;
14690  h = rect().height();
14691  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14692  QVector<QRgb *> pixels;
14693  for (int y = 0; y < h; ++y)
14694  pixels.append(reinterpret_cast<QRgb *>(mGradientImage.scanLine(y)));
14695  mParentColorScale->mGradient.colorize(
14696  data.constData(), QCPRange(0, n - 1), pixels.first(), n);
14697  for (int y = 1; y < h; ++y)
14698  memcpy(pixels.at(y), pixels.first(), n * sizeof(QRgb));
14699  } else {
14700  w = rect().width();
14701  h = n;
14702  mGradientImage = QImage(w, h, QImage::Format_RGB32);
14703  for (int y = 0; y < h; ++y) {
14704  QRgb *pixels = reinterpret_cast<QRgb *>(mGradientImage.scanLine(y));
14705  const QRgb lineColor = mParentColorScale->mGradient.color(
14706  data[h - 1 - y], QCPRange(0, n - 1));
14707  for (int x = 0; x < w; ++x) pixels[x] = lineColor;
14708  }
14709  }
14710  mGradientImageInvalidated = false;
14711 }
14712 
14718 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(
14719  QCPAxis::SelectableParts selectedParts) {
14720  // axis bases of four axes shall always (de-)selected synchronously:
14721  QList<QCPAxis::AxisType> allAxisTypes =
14722  QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop
14724  foreach (QCPAxis::AxisType type, allAxisTypes) {
14725  if (QCPAxis *senderAxis = qobject_cast<QCPAxis *>(sender()))
14726  if (senderAxis->axisType() == type) continue;
14727 
14728  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) {
14729  if (selectedParts.testFlag(QCPAxis::spAxis))
14730  axis(type)->setSelectedParts(axis(type)->selectedParts() |
14731  QCPAxis::spAxis);
14732  else
14733  axis(type)->setSelectedParts(axis(type)->selectedParts() &
14734  ~QCPAxis::spAxis);
14735  }
14736  }
14737 }
14738 
14744 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(
14745  QCPAxis::SelectableParts selectableParts) {
14746  // synchronize axis base selectability:
14747  QList<QCPAxis::AxisType> allAxisTypes =
14748  QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop
14750  foreach (QCPAxis::AxisType type, allAxisTypes) {
14751  if (QCPAxis *senderAxis = qobject_cast<QCPAxis *>(sender()))
14752  if (senderAxis->axisType() == type) continue;
14753 
14754  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) {
14755  if (selectableParts.testFlag(QCPAxis::spAxis))
14756  axis(type)->setSelectableParts(axis(type)->selectableParts() |
14757  QCPAxis::spAxis);
14758  else
14759  axis(type)->setSelectableParts(axis(type)->selectableParts() &
14760  ~QCPAxis::spAxis);
14761  }
14762  }
14763 }
14764 
14768 
14789  : key(0),
14790  value(0),
14791  keyErrorPlus(0),
14792  keyErrorMinus(0),
14793  valueErrorPlus(0),
14794  valueErrorMinus(0) {}
14795 
14800 QCPData::QCPData(double key, double value)
14801  : key(key),
14802  value(value),
14803  keyErrorPlus(0),
14804  keyErrorMinus(0),
14805  valueErrorPlus(0),
14806  valueErrorMinus(0) {}
14807 
14811 
14853 /* start of documentation of inline functions */
14854 
14863 /* end of documentation of inline functions */
14864 
14878 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
14879  : QCPAbstractPlottable(keyAxis, valueAxis) {
14880  mData = new QCPDataMap;
14881 
14882  setPen(QPen(Qt::blue, 0));
14883  setErrorPen(QPen(Qt::black));
14884  setBrush(Qt::NoBrush);
14885  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14886  setSelectedBrush(Qt::NoBrush);
14887 
14888  setLineStyle(lsLine);
14889  setErrorType(etNone);
14890  setErrorBarSize(6);
14891  setErrorBarSkipSymbol(true);
14892  setChannelFillGraph(0);
14893  setAdaptiveSampling(true);
14894 }
14895 
14896 QCPGraph::~QCPGraph() { delete mData; }
14897 
14910  if (mData == data) {
14911  qDebug() << Q_FUNC_INFO
14912  << "The data pointer is already in (and owned by) this "
14913  "plottable"
14914  << reinterpret_cast<quintptr>(data);
14915  return;
14916  }
14917  if (copy) {
14918  *mData = *data;
14919  } else {
14920  delete mData;
14921  mData = data;
14922  }
14923 }
14924 
14931 void QCPGraph::setData(const QVector<double> &key,
14932  const QVector<double> &value) {
14933  mData->clear();
14934  int n = key.size();
14935  n = qMin(n, value.size());
14936  QCPData newData;
14937  for (int i = 0; i < n; ++i) {
14938  newData.key = key[i];
14939  newData.value = value[i];
14940  qtCompatMapInsertMulti(mData, newData.key, newData);
14941  }
14942 }
14943 
14954 void QCPGraph::setDataValueError(const QVector<double> &key,
14955  const QVector<double> &value,
14956  const QVector<double> &valueError) {
14957  mData->clear();
14958  int n = key.size();
14959  n = qMin(n, value.size());
14960  n = qMin(n, valueError.size());
14961  QCPData newData;
14962  for (int i = 0; i < n; ++i) {
14963  newData.key = key[i];
14964  newData.value = value[i];
14965  newData.valueErrorMinus = valueError[i];
14966  newData.valueErrorPlus = valueError[i];
14967  qtCompatMapInsertMulti(mData, key[i], newData);
14968  }
14969 }
14970 
14980 void QCPGraph::setDataValueError(const QVector<double> &key,
14981  const QVector<double> &value,
14982  const QVector<double> &valueErrorMinus,
14983  const QVector<double> &valueErrorPlus) {
14984  mData->clear();
14985  int n = key.size();
14986  n = qMin(n, value.size());
14987  n = qMin(n, valueErrorMinus.size());
14988  n = qMin(n, valueErrorPlus.size());
14989  QCPData newData;
14990  for (int i = 0; i < n; ++i) {
14991  newData.key = key[i];
14992  newData.value = value[i];
14993  newData.valueErrorMinus = valueErrorMinus[i];
14994  newData.valueErrorPlus = valueErrorPlus[i];
14995  qtCompatMapInsertMulti(mData, key[i], newData);
14996  }
14997 }
14998 
15009 void QCPGraph::setDataKeyError(const QVector<double> &key,
15010  const QVector<double> &value,
15011  const QVector<double> &keyError) {
15012  mData->clear();
15013  int n = key.size();
15014  n = qMin(n, value.size());
15015  n = qMin(n, keyError.size());
15016  QCPData newData;
15017  for (int i = 0; i < n; ++i) {
15018  newData.key = key[i];
15019  newData.value = value[i];
15020  newData.keyErrorMinus = keyError[i];
15021  newData.keyErrorPlus = keyError[i];
15022  qtCompatMapInsertMulti(mData, key[i], newData);
15023  }
15024 }
15025 
15035 void QCPGraph::setDataKeyError(const QVector<double> &key,
15036  const QVector<double> &value,
15037  const QVector<double> &keyErrorMinus,
15038  const QVector<double> &keyErrorPlus) {
15039  mData->clear();
15040  int n = key.size();
15041  n = qMin(n, value.size());
15042  n = qMin(n, keyErrorMinus.size());
15043  n = qMin(n, keyErrorPlus.size());
15044  QCPData newData;
15045  for (int i = 0; i < n; ++i) {
15046  newData.key = key[i];
15047  newData.value = value[i];
15048  newData.keyErrorMinus = keyErrorMinus[i];
15049  newData.keyErrorPlus = keyErrorPlus[i];
15050  qtCompatMapInsertMulti(mData, key[i], newData);
15051  }
15052 }
15053 
15065 void QCPGraph::setDataBothError(const QVector<double> &key,
15066  const QVector<double> &value,
15067  const QVector<double> &keyError,
15068  const QVector<double> &valueError) {
15069  mData->clear();
15070  int n = key.size();
15071  n = qMin(n, value.size());
15072  n = qMin(n, valueError.size());
15073  n = qMin(n, keyError.size());
15074  QCPData newData;
15075  for (int i = 0; i < n; ++i) {
15076  newData.key = key[i];
15077  newData.value = value[i];
15078  newData.keyErrorMinus = keyError[i];
15079  newData.keyErrorPlus = keyError[i];
15080  newData.valueErrorMinus = valueError[i];
15081  newData.valueErrorPlus = valueError[i];
15082  qtCompatMapInsertMulti(mData, key[i], newData);
15083  }
15084 }
15085 
15096 void QCPGraph::setDataBothError(const QVector<double> &key,
15097  const QVector<double> &value,
15098  const QVector<double> &keyErrorMinus,
15099  const QVector<double> &keyErrorPlus,
15100  const QVector<double> &valueErrorMinus,
15101  const QVector<double> &valueErrorPlus) {
15102  mData->clear();
15103  int n = key.size();
15104  n = qMin(n, value.size());
15105  n = qMin(n, valueErrorMinus.size());
15106  n = qMin(n, valueErrorPlus.size());
15107  n = qMin(n, keyErrorMinus.size());
15108  n = qMin(n, keyErrorPlus.size());
15109  QCPData newData;
15110  for (int i = 0; i < n; ++i) {
15111  newData.key = key[i];
15112  newData.value = value[i];
15113  newData.keyErrorMinus = keyErrorMinus[i];
15114  newData.keyErrorPlus = keyErrorPlus[i];
15115  newData.valueErrorMinus = valueErrorMinus[i];
15116  newData.valueErrorPlus = valueErrorPlus[i];
15117  qtCompatMapInsertMulti(mData, key[i], newData);
15118  }
15119 }
15120 
15128 void QCPGraph::setLineStyle(LineStyle ls) { mLineStyle = ls; }
15129 
15137 void QCPGraph::setScatterStyle(const QCPScatterStyle &style) {
15138  mScatterStyle = style;
15139 }
15140 
15151 
15156 void QCPGraph::setErrorPen(const QPen &pen) { mErrorPen = pen; }
15157 
15162 
15176  mErrorBarSkipSymbol = enabled;
15177 }
15178 
15188 void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph) {
15189  // prevent setting channel target to this graph itself:
15190  if (targetGraph == this) {
15191  qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
15192  mChannelFillGraph = 0;
15193  return;
15194  }
15195  // prevent setting channel target to a graph not in the plot:
15196  if (targetGraph && targetGraph->mParentPlot != mParentPlot) {
15197  qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
15198  mChannelFillGraph = 0;
15199  return;
15200  }
15201 
15202  mChannelFillGraph = targetGraph;
15203 }
15204 
15242 void QCPGraph::setAdaptiveSampling(bool enabled) {
15243  mAdaptiveSampling = enabled;
15244 }
15245 
15254 void QCPGraph::addData(const QCPDataMap &dataMap) {
15255  qtCompatMapUnite(mData, dataMap);
15256 }
15257 
15268 }
15269 
15279 void QCPGraph::addData(double key, double value) {
15280  QCPData newData;
15281  newData.key = key;
15282  newData.value = value;
15283  qtCompatMapInsertMulti(mData, newData.key, newData);
15284 }
15285 
15295 void QCPGraph::addData(const QVector<double> &keys,
15296  const QVector<double> &values) {
15297  int n = qMin(keys.size(), values.size());
15298  QCPData newData;
15299  for (int i = 0; i < n; ++i) {
15300  newData.key = keys[i];
15301  newData.value = values[i];
15302  qtCompatMapInsertMulti(mData, newData.key, newData);
15303  }
15304 }
15305 
15310 void QCPGraph::removeDataBefore(double key) {
15311  QCPDataMap::iterator it = mData->begin();
15312  while (it != mData->end() && it.key() < key) it = mData->erase(it);
15313 }
15314 
15319 void QCPGraph::removeDataAfter(double key) {
15320  if (mData->isEmpty()) return;
15321  QCPDataMap::iterator it = mData->upperBound(key);
15322  while (it != mData->end()) it = mData->erase(it);
15323 }
15324 
15332 void QCPGraph::removeData(double fromKey, double toKey) {
15333  if (fromKey >= toKey || mData->isEmpty()) return;
15334  QCPDataMap::iterator it = mData->upperBound(fromKey);
15335  QCPDataMap::iterator itEnd = mData->upperBound(toKey);
15336  while (it != itEnd) it = mData->erase(it);
15337 }
15338 
15348 void QCPGraph::removeData(double key) { mData->remove(key); }
15349 
15354 void QCPGraph::clearData() { mData->clear(); }
15355 
15356 /* inherits documentation from base class */
15357 double QCPGraph::selectTest(const QPointF &pos,
15358  bool onlySelectable,
15359  QVariant *details) const {
15360  Q_UNUSED(details)
15361  if ((onlySelectable && !mSelectable) || mData->isEmpty()) return -1;
15362  if (!mKeyAxis || !mValueAxis) {
15363  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15364  return -1;
15365  }
15366 
15367  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
15368  return pointDistance(pos);
15369  else
15370  return -1;
15371 }
15372 
15381 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const {
15382  rescaleKeyAxis(onlyEnlarge, includeErrorBars);
15383  rescaleValueAxis(onlyEnlarge, includeErrorBars);
15384 }
15385 
15393 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const {
15394  // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only
15395  // change that getKeyRange is passed the includeErrorBars value.
15396  if (mData->isEmpty()) return;
15397 
15398  QCPAxis *keyAxis = mKeyAxis.data();
15399  if (!keyAxis) {
15400  qDebug() << Q_FUNC_INFO << "invalid key axis";
15401  return;
15402  }
15403 
15404  SignDomain signDomain = sdBoth;
15406  signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15407 
15408  bool foundRange;
15409  QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15410 
15411  if (foundRange) {
15412  if (onlyEnlarge) {
15413  if (keyAxis->range().lower < newRange.lower)
15414  newRange.lower = keyAxis->range().lower;
15415  if (keyAxis->range().upper > newRange.upper)
15416  newRange.upper = keyAxis->range().upper;
15417  }
15418  keyAxis->setRange(newRange);
15419  }
15420 }
15421 
15429 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const {
15430  // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the
15431  // only change is that getValueRange is passed the includeErrorBars value.
15432  if (mData->isEmpty()) return;
15433 
15434  QCPAxis *valueAxis = mValueAxis.data();
15435  if (!valueAxis) {
15436  qDebug() << Q_FUNC_INFO << "invalid value axis";
15437  return;
15438  }
15439 
15440  SignDomain signDomain = sdBoth;
15442  signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15443 
15444  bool foundRange;
15445  QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15446 
15447  if (foundRange) {
15448  if (onlyEnlarge) {
15449  if (valueAxis->range().lower < newRange.lower)
15450  newRange.lower = valueAxis->range().lower;
15451  if (valueAxis->range().upper > newRange.upper)
15452  newRange.upper = valueAxis->range().upper;
15453  }
15454  valueAxis->setRange(newRange);
15455  }
15456 }
15457 
15458 /* inherits documentation from base class */
15459 void QCPGraph::draw(QCPPainter *painter) {
15460  if (!mKeyAxis || !mValueAxis) {
15461  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15462  return;
15463  }
15464  if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15465  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15466 
15467  // allocate line and (if necessary) point vectors:
15468  QVector<QPointF> *lineData = new QVector<QPointF>;
15469  QVector<QCPData> *scatterData = 0;
15470  if (!mScatterStyle.isNone()) scatterData = new QVector<QCPData>;
15471 
15472  // fill vectors with data appropriate to plot style:
15473  getPlotData(lineData, scatterData);
15474 
15475  // check data validity if flag set:
15476 #ifdef QCUSTOMPLOT_CHECK_DATA
15477  QCPDataMap::const_iterator it;
15478  for (it = mData->constBegin(); it != mData->constEnd(); ++it) {
15479  if (QCP::isInvalidData(it.value().key, it.value().value) ||
15480  QCP::isInvalidData(it.value().keyErrorPlus,
15481  it.value().keyErrorMinus) ||
15482  QCP::isInvalidData(it.value().valueErrorPlus,
15483  it.value().valueErrorPlus))
15484  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid."
15485  << "Plottable name:" << name();
15486  }
15487 #endif
15488 
15489  // draw fill of graph:
15490  drawFill(painter, lineData);
15491 
15492  // draw line:
15493  if (mLineStyle == lsImpulse)
15494  drawImpulsePlot(painter, lineData);
15495  else if (mLineStyle != lsNone)
15496  drawLinePlot(painter,
15497  lineData); // also step plots can be drawn as a line plot
15498 
15499  // draw scatters:
15500  if (scatterData) drawScatterPlot(painter, scatterData);
15501 
15502  // free allocated line and point vectors:
15503  delete lineData;
15504  if (scatterData) delete scatterData;
15505 }
15506 
15507 /* inherits documentation from base class */
15508 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const {
15509  // draw fill:
15510  if (mBrush.style() != Qt::NoBrush) {
15511  applyFillAntialiasingHint(painter);
15512  painter->fillRect(QRectF(rect.left(), rect.top() + rect.height() / 2.0,
15513  rect.width(), rect.height() / 3.0),
15514  mBrush);
15515  }
15516  // draw line vertically centered:
15517  if (mLineStyle != lsNone) {
15519  painter->setPen(mPen);
15520  painter->drawLine(QLineF(
15521  rect.left(), rect.top() + rect.height() / 2.0, rect.right() + 5,
15522  rect.top() + rect.height() /
15523  2.0)); // +5 on x2 else last segment is
15524  // missing from dashed/dotted pens
15525  }
15526  // draw scatter symbol:
15527  if (!mScatterStyle.isNone()) {
15529  // scale scatter pixmap if it's too large to fit in legend icon rect:
15531  (mScatterStyle.pixmap().size().width() > rect.width() ||
15532  mScatterStyle.pixmap().size().height() > rect.height())) {
15533  QCPScatterStyle scaledStyle(mScatterStyle);
15534  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(
15535  rect.size().toSize(), Qt::KeepAspectRatio,
15536  Qt::SmoothTransformation));
15537  scaledStyle.applyTo(painter, mPen);
15538  scaledStyle.drawShape(painter, QRectF(rect).center());
15539  } else {
15540  mScatterStyle.applyTo(painter, mPen);
15541  mScatterStyle.drawShape(painter, QRectF(rect).center());
15542  }
15543  }
15544 }
15545 
15566 void QCPGraph::getPlotData(QVector<QPointF> *lineData,
15567  QVector<QCPData> *scatterData) const {
15568  switch (mLineStyle) {
15569  case lsNone:
15570  getScatterPlotData(scatterData);
15571  break;
15572  case lsLine:
15573  getLinePlotData(lineData, scatterData);
15574  break;
15575  case lsStepLeft:
15576  getStepLeftPlotData(lineData, scatterData);
15577  break;
15578  case lsStepRight:
15579  getStepRightPlotData(lineData, scatterData);
15580  break;
15581  case lsStepCenter:
15582  getStepCenterPlotData(lineData, scatterData);
15583  break;
15584  case lsImpulse:
15585  getImpulsePlotData(lineData, scatterData);
15586  break;
15587  }
15588 }
15589 
15603 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const {
15604  getPreparedData(0, scatterData);
15605 }
15606 
15620 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData,
15621  QVector<QCPData> *scatterData) const {
15622  QCPAxis *keyAxis = mKeyAxis.data();
15623  QCPAxis *valueAxis = mValueAxis.data();
15624  if (!keyAxis || !valueAxis) {
15625  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15626  return;
15627  }
15628  if (!linePixelData) {
15629  qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData";
15630  return;
15631  }
15632 
15633  QVector<QCPData> lineData;
15634  getPreparedData(&lineData, scatterData);
15635  linePixelData->reserve(
15636  lineData.size() +
15637  2); // added 2 to reserve memory for lower/upper fill base points
15638  // that might be needed for fill
15639  linePixelData->resize(lineData.size());
15640 
15641  // transform lineData points to pixels:
15642  if (keyAxis->orientation() == Qt::Vertical) {
15643  for (int i = 0; i < lineData.size(); ++i) {
15644  (*linePixelData)[i].setX(
15645  valueAxis->coordToPixel(lineData.at(i).value));
15646  (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15647  }
15648  } else // key axis is horizontal
15649  {
15650  for (int i = 0; i < lineData.size(); ++i) {
15651  (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15652  (*linePixelData)[i].setY(
15653  valueAxis->coordToPixel(lineData.at(i).value));
15654  }
15655  }
15656 }
15657 
15671 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData,
15672  QVector<QCPData> *scatterData) const {
15673  QCPAxis *keyAxis = mKeyAxis.data();
15674  QCPAxis *valueAxis = mValueAxis.data();
15675  if (!keyAxis || !valueAxis) {
15676  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15677  return;
15678  }
15679  if (!linePixelData) {
15680  qDebug() << Q_FUNC_INFO << "null pointer passed as lineData";
15681  return;
15682  }
15683 
15684  QVector<QCPData> lineData;
15685  getPreparedData(&lineData, scatterData);
15686  linePixelData->reserve(
15687  lineData.size() * 2 +
15688  2); // added 2 to reserve memory for lower/upper fill base points
15689  // that might be needed for fill
15690  linePixelData->resize(lineData.size() * 2);
15691 
15692  // calculate steps from lineData and transform to pixel coordinates:
15693  if (keyAxis->orientation() == Qt::Vertical) {
15694  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15695  double key;
15696  for (int i = 0; i < lineData.size(); ++i) {
15697  key = keyAxis->coordToPixel(lineData.at(i).key);
15698  (*linePixelData)[i * 2 + 0].setX(lastValue);
15699  (*linePixelData)[i * 2 + 0].setY(key);
15700  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15701  (*linePixelData)[i * 2 + 1].setX(lastValue);
15702  (*linePixelData)[i * 2 + 1].setY(key);
15703  }
15704  } else // key axis is horizontal
15705  {
15706  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15707  double key;
15708  for (int i = 0; i < lineData.size(); ++i) {
15709  key = keyAxis->coordToPixel(lineData.at(i).key);
15710  (*linePixelData)[i * 2 + 0].setX(key);
15711  (*linePixelData)[i * 2 + 0].setY(lastValue);
15712  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15713  (*linePixelData)[i * 2 + 1].setX(key);
15714  (*linePixelData)[i * 2 + 1].setY(lastValue);
15715  }
15716  }
15717 }
15718 
15732 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData,
15733  QVector<QCPData> *scatterData) const {
15734  QCPAxis *keyAxis = mKeyAxis.data();
15735  QCPAxis *valueAxis = mValueAxis.data();
15736  if (!keyAxis || !valueAxis) {
15737  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15738  return;
15739  }
15740  if (!linePixelData) {
15741  qDebug() << Q_FUNC_INFO << "null pointer passed as lineData";
15742  return;
15743  }
15744 
15745  QVector<QCPData> lineData;
15746  getPreparedData(&lineData, scatterData);
15747  linePixelData->reserve(
15748  lineData.size() * 2 +
15749  2); // added 2 to reserve memory for lower/upper fill base points
15750  // that might be needed for fill
15751  linePixelData->resize(lineData.size() * 2);
15752 
15753  // calculate steps from lineData and transform to pixel coordinates:
15754  if (keyAxis->orientation() == Qt::Vertical) {
15755  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15756  double value;
15757  for (int i = 0; i < lineData.size(); ++i) {
15758  value = valueAxis->coordToPixel(lineData.at(i).value);
15759  (*linePixelData)[i * 2 + 0].setX(value);
15760  (*linePixelData)[i * 2 + 0].setY(lastKey);
15761  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15762  (*linePixelData)[i * 2 + 1].setX(value);
15763  (*linePixelData)[i * 2 + 1].setY(lastKey);
15764  }
15765  } else // key axis is horizontal
15766  {
15767  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15768  double value;
15769  for (int i = 0; i < lineData.size(); ++i) {
15770  value = valueAxis->coordToPixel(lineData.at(i).value);
15771  (*linePixelData)[i * 2 + 0].setX(lastKey);
15772  (*linePixelData)[i * 2 + 0].setY(value);
15773  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15774  (*linePixelData)[i * 2 + 1].setX(lastKey);
15775  (*linePixelData)[i * 2 + 1].setY(value);
15776  }
15777  }
15778 }
15779 
15793 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData,
15794  QVector<QCPData> *scatterData) const {
15795  QCPAxis *keyAxis = mKeyAxis.data();
15796  QCPAxis *valueAxis = mValueAxis.data();
15797  if (!keyAxis || !valueAxis) {
15798  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15799  return;
15800  }
15801  if (!linePixelData) {
15802  qDebug() << Q_FUNC_INFO << "null pointer passed as lineData";
15803  return;
15804  }
15805 
15806  QVector<QCPData> lineData;
15807  getPreparedData(&lineData, scatterData);
15808  linePixelData->reserve(
15809  lineData.size() * 2 +
15810  2); // added 2 to reserve memory for lower/upper fill base points
15811  // that might be needed for fill
15812  linePixelData->resize(lineData.size() * 2);
15813  // calculate steps from lineData and transform to pixel coordinates:
15814  if (keyAxis->orientation() == Qt::Vertical) {
15815  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15816  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15817  double key;
15818  (*linePixelData)[0].setX(lastValue);
15819  (*linePixelData)[0].setY(lastKey);
15820  for (int i = 1; i < lineData.size(); ++i) {
15821  key = (keyAxis->coordToPixel(lineData.at(i).key) + lastKey) * 0.5;
15822  (*linePixelData)[i * 2 - 1].setX(lastValue);
15823  (*linePixelData)[i * 2 - 1].setY(key);
15824  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15825  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15826  (*linePixelData)[i * 2 + 0].setX(lastValue);
15827  (*linePixelData)[i * 2 + 0].setY(key);
15828  }
15829  (*linePixelData)[lineData.size() * 2 - 1].setX(lastValue);
15830  (*linePixelData)[lineData.size() * 2 - 1].setY(lastKey);
15831  } else // key axis is horizontal
15832  {
15833  double lastKey = keyAxis->coordToPixel(lineData.first().key);
15834  double lastValue = valueAxis->coordToPixel(lineData.first().value);
15835  double key;
15836  (*linePixelData)[0].setX(lastKey);
15837  (*linePixelData)[0].setY(lastValue);
15838  for (int i = 1; i < lineData.size(); ++i) {
15839  key = (keyAxis->coordToPixel(lineData.at(i).key) + lastKey) * 0.5;
15840  (*linePixelData)[i * 2 - 1].setX(key);
15841  (*linePixelData)[i * 2 - 1].setY(lastValue);
15842  lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15843  lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15844  (*linePixelData)[i * 2 + 0].setX(key);
15845  (*linePixelData)[i * 2 + 0].setY(lastValue);
15846  }
15847  (*linePixelData)[lineData.size() * 2 - 1].setX(lastKey);
15848  (*linePixelData)[lineData.size() * 2 - 1].setY(lastValue);
15849  }
15850 }
15851 
15864 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData,
15865  QVector<QCPData> *scatterData) const {
15866  QCPAxis *keyAxis = mKeyAxis.data();
15867  QCPAxis *valueAxis = mValueAxis.data();
15868  if (!keyAxis || !valueAxis) {
15869  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15870  return;
15871  }
15872  if (!linePixelData) {
15873  qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData";
15874  return;
15875  }
15876 
15877  QVector<QCPData> lineData;
15878  getPreparedData(&lineData, scatterData);
15879  linePixelData->resize(lineData.size() *
15880  2); // no need to reserve 2 extra points because
15881  // impulse plot has no fill
15882 
15883  // transform lineData points to pixels:
15884  if (keyAxis->orientation() == Qt::Vertical) {
15885  double zeroPointX = valueAxis->coordToPixel(0);
15886  double key;
15887  for (int i = 0; i < lineData.size(); ++i) {
15888  key = keyAxis->coordToPixel(lineData.at(i).key);
15889  (*linePixelData)[i * 2 + 0].setX(zeroPointX);
15890  (*linePixelData)[i * 2 + 0].setY(key);
15891  (*linePixelData)[i * 2 + 1].setX(
15892  valueAxis->coordToPixel(lineData.at(i).value));
15893  (*linePixelData)[i * 2 + 1].setY(key);
15894  }
15895  } else // key axis is horizontal
15896  {
15897  double zeroPointY = valueAxis->coordToPixel(0);
15898  double key;
15899  for (int i = 0; i < lineData.size(); ++i) {
15900  key = keyAxis->coordToPixel(lineData.at(i).key);
15901  (*linePixelData)[i * 2 + 0].setX(key);
15902  (*linePixelData)[i * 2 + 0].setY(zeroPointY);
15903  (*linePixelData)[i * 2 + 1].setX(key);
15904  (*linePixelData)[i * 2 + 1].setY(
15905  valueAxis->coordToPixel(lineData.at(i).value));
15906  }
15907  }
15908 }
15909 
15925 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const {
15926  if (mLineStyle == lsImpulse)
15927  return; // fill doesn't make sense for impulse plot
15928  if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0)
15929  return;
15930 
15931  applyFillAntialiasingHint(painter);
15932  if (!mChannelFillGraph) {
15933  // draw base fill under graph, fill goes all the way to the
15934  // zero-value-line:
15935  addFillBasePoints(lineData);
15936  painter->setPen(Qt::NoPen);
15937  painter->setBrush(mainBrush());
15938  painter->drawPolygon(QPolygonF(*lineData));
15939  removeFillBasePoints(lineData);
15940  } else {
15941  // draw channel fill between this graph and mChannelFillGraph:
15942  painter->setPen(Qt::NoPen);
15943  painter->setBrush(mainBrush());
15944  painter->drawPolygon(getChannelFillPolygon(lineData));
15945  }
15946 }
15947 
15959  QVector<QCPData> *scatterData) const {
15960  QCPAxis *keyAxis = mKeyAxis.data();
15961  QCPAxis *valueAxis = mValueAxis.data();
15962  if (!keyAxis || !valueAxis) {
15963  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
15964  return;
15965  }
15966 
15967  // draw error bars:
15968  if (mErrorType != etNone) {
15970  painter->setPen(mErrorPen);
15971  if (keyAxis->orientation() == Qt::Vertical) {
15972  for (int i = 0; i < scatterData->size(); ++i)
15973  drawError(painter,
15974  valueAxis->coordToPixel(scatterData->at(i).value),
15975  keyAxis->coordToPixel(scatterData->at(i).key),
15976  scatterData->at(i));
15977  } else {
15978  for (int i = 0; i < scatterData->size(); ++i)
15979  drawError(painter,
15980  keyAxis->coordToPixel(scatterData->at(i).key),
15981  valueAxis->coordToPixel(scatterData->at(i).value),
15982  scatterData->at(i));
15983  }
15984  }
15985 
15986  // draw scatter point symbols:
15988  mScatterStyle.applyTo(painter, mPen);
15989  if (keyAxis->orientation() == Qt::Vertical) {
15990  for (int i = 0; i < scatterData->size(); ++i)
15991  if (!qIsNaN(scatterData->at(i).value))
15993  painter,
15994  valueAxis->coordToPixel(scatterData->at(i).value),
15995  keyAxis->coordToPixel(scatterData->at(i).key));
15996  } else {
15997  for (int i = 0; i < scatterData->size(); ++i)
15998  if (!qIsNaN(scatterData->at(i).value))
16000  painter, keyAxis->coordToPixel(scatterData->at(i).key),
16001  valueAxis->coordToPixel(scatterData->at(i).value));
16002  }
16003 }
16004 
16016  QVector<QPointF> *lineData) const {
16017  // draw line of graph:
16018  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) {
16020  painter->setPen(mainPen());
16021  painter->setBrush(Qt::NoBrush);
16022 
16023  /* Draws polyline in batches, currently not used:
16024  int p = 0;
16025  while (p < lineData->size())
16026  {
16027  int batch = qMin(25, lineData->size()-p);
16028  if (p != 0)
16029  {
16030  ++batch;
16031  --p; // to draw the connection lines between two batches
16032  }
16033  painter->drawPolyline(lineData->constData()+p, batch);
16034  p += batch;
16035  }
16036  */
16037 
16038  // if drawing solid line and not in PDF, use much faster line drawing
16039  // instead of polyline:
16040  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
16041  painter->pen().style() == Qt::SolidLine &&
16042  !painter->modes().testFlag(QCPPainter::pmVectorized) &&
16043  !painter->modes().testFlag(QCPPainter::pmNoCaching)) {
16044  int i = 0;
16045  bool lastIsNan = false;
16046  const int lineDataSize = lineData->size();
16047  while (i < lineDataSize &&
16048  (qIsNaN(lineData->at(i).y()) ||
16049  qIsNaN(lineData->at(i)
16050  .x()))) // make sure first point is not NaN
16051  ++i;
16052  ++i; // because drawing works in 1 point retrospect
16053  while (i < lineDataSize) {
16054  if (!qIsNaN(lineData->at(i).y()) &&
16055  !qIsNaN(lineData->at(i)
16056  .x())) // NaNs create a gap in the line
16057  {
16058  if (!lastIsNan)
16059  painter->drawLine(lineData->at(i - 1), lineData->at(i));
16060  else
16061  lastIsNan = false;
16062  } else
16063  lastIsNan = true;
16064  ++i;
16065  }
16066  } else {
16067  int segmentStart = 0;
16068  int i = 0;
16069  const int lineDataSize = lineData->size();
16070  while (i < lineDataSize) {
16071  if (qIsNaN(lineData->at(i).y()) ||
16072  qIsNaN(lineData->at(i)
16073  .x())) // NaNs create a gap in the line
16074  {
16075  painter->drawPolyline(
16076  lineData->constData() + segmentStart,
16077  i - segmentStart); // i, because we don't want to
16078  // include the current NaN point
16079  segmentStart = i + 1;
16080  }
16081  ++i;
16082  }
16083  // draw last segment:
16084  painter->drawPolyline(lineData->constData() + segmentStart,
16085  lineDataSize - segmentStart);
16086  }
16087  }
16088 }
16089 
16098  QVector<QPointF> *lineData) const {
16099  // draw impulses:
16100  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) {
16102  QPen pen = mainPen();
16103  pen.setCapStyle(
16104  Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
16105  painter->setPen(pen);
16106  painter->setBrush(Qt::NoBrush);
16107  painter->drawLines(*lineData);
16108  }
16109 }
16110 
16125 void QCPGraph::getPreparedData(QVector<QCPData> *lineData,
16126  QVector<QCPData> *scatterData) const {
16127  QCPAxis *keyAxis = mKeyAxis.data();
16128  QCPAxis *valueAxis = mValueAxis.data();
16129  if (!keyAxis || !valueAxis) {
16130  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
16131  return;
16132  }
16133  // get visible data range:
16134  QCPDataMap::const_iterator lower,
16135  upper; // note that upper is the actual upper point, and not 1 step
16136  // after the upper point
16137  getVisibleDataBounds(lower, upper);
16138  if (lower == mData->constEnd() || upper == mData->constEnd()) return;
16139 
16140  // count points in visible range, taking into account that we only need to
16141  // count to the limit maxCount if using adaptive sampling:
16142  int maxCount = std::numeric_limits<int>::max();
16143  if (mAdaptiveSampling) {
16144  int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key()) -
16145  keyAxis->coordToPixel(upper.key()));
16146  maxCount = 2 * keyPixelSpan + 2;
16147  }
16148  int dataCount = countDataInBounds(lower, upper, maxCount);
16149 
16150  if (mAdaptiveSampling &&
16151  dataCount >= maxCount) // use adaptive sampling only if there are at
16152  // least two points per pixel on average
16153  {
16154  if (lineData) {
16155  QCPDataMap::const_iterator it = lower;
16156  QCPDataMap::const_iterator upperEnd = upper + 1;
16157  double minValue = it.value().value;
16158  double maxValue = it.value().value;
16159  QCPDataMap::const_iterator currentIntervalFirstPoint = it;
16160  int reversedFactor =
16161  keyAxis->rangeReversed() !=
16162  (keyAxis->orientation() == Qt::Vertical)
16163  ? -1
16164  : 1; // is used to calculate keyEpsilon pixel into
16165  // the correct direction
16166  int reversedRound =
16167  keyAxis->rangeReversed() !=
16168  (keyAxis->orientation() == Qt::Vertical)
16169  ? 1
16170  : 0; // is used to switch between floor (normal)
16171  // and ceil (reversed) rounding of
16172  // currentIntervalStartKey
16173  double currentIntervalStartKey = keyAxis->pixelToCoord(
16174  (int)(keyAxis->coordToPixel(lower.key()) + reversedRound));
16175  double lastIntervalEndKey = currentIntervalStartKey;
16176  double keyEpsilon = qAbs(
16177  currentIntervalStartKey -
16179  keyAxis->coordToPixel(currentIntervalStartKey) +
16180  1.0 * reversedFactor)); // interval of one pixel on
16181  // screen when mapped to
16182  // plot key coordinates
16183  bool keyEpsilonVariable =
16184  keyAxis->scaleType() ==
16185  QCPAxis::stLogarithmic; // indicates whether keyEpsilon
16186  // needs to be updated after every
16187  // interval (for log axes)
16188  int intervalDataCount = 1;
16189  ++it; // advance iterator to second data point because adaptive
16190  // sampling works in 1 point retrospect
16191  while (it != upperEnd) {
16192  if (it.key() <
16193  currentIntervalStartKey +
16194  keyEpsilon) // data point is still within same
16195  // pixel, so skip it and expand value
16196  // span of this cluster if necessary
16197  {
16198  if (it.value().value < minValue)
16199  minValue = it.value().value;
16200  else if (it.value().value > maxValue)
16201  maxValue = it.value().value;
16202  ++intervalDataCount;
16203  } else // new pixel interval started
16204  {
16205  if (intervalDataCount >=
16206  2) // last pixel had multiple data points, consolidate
16207  // them to a cluster
16208  {
16209  if (lastIntervalEndKey <
16210  currentIntervalStartKey -
16211  keyEpsilon) // last point is further away,
16212  // so first point of this
16213  // cluster must be at a real
16214  // data point
16215  lineData->append(QCPData(
16216  currentIntervalStartKey + keyEpsilon * 0.2,
16217  currentIntervalFirstPoint.value().value));
16218  lineData->append(QCPData(
16219  currentIntervalStartKey + keyEpsilon * 0.25,
16220  minValue));
16221  lineData->append(QCPData(
16222  currentIntervalStartKey + keyEpsilon * 0.75,
16223  maxValue));
16224  if (it.key() >
16225  currentIntervalStartKey +
16226  keyEpsilon *
16227  2) // new pixel started further
16228  // away from previous cluster,
16229  // so make sure the last point
16230  // of the cluster is at a real
16231  // data point
16232  lineData->append(QCPData(
16233  currentIntervalStartKey + keyEpsilon * 0.8,
16234  (it - 1).value().value));
16235  } else
16236  lineData->append(QCPData(
16237  currentIntervalFirstPoint.key(),
16238  currentIntervalFirstPoint.value().value));
16239  lastIntervalEndKey = (it - 1).value().key;
16240  minValue = it.value().value;
16241  maxValue = it.value().value;
16242  currentIntervalFirstPoint = it;
16243  currentIntervalStartKey = keyAxis->pixelToCoord(
16244  (int)(keyAxis->coordToPixel(it.key()) +
16245  reversedRound));
16246  if (keyEpsilonVariable)
16247  keyEpsilon =
16248  qAbs(currentIntervalStartKey -
16251  currentIntervalStartKey) +
16252  1.0 * reversedFactor));
16253  intervalDataCount = 1;
16254  }
16255  ++it;
16256  }
16257  // handle last interval:
16258  if (intervalDataCount >= 2) // last pixel had multiple data points,
16259  // consolidate them to a cluster
16260  {
16261  if (lastIntervalEndKey <
16262  currentIntervalStartKey -
16263  keyEpsilon) // last point wasn't a cluster, so
16264  // first point of this cluster must be
16265  // at a real data point
16266  lineData->append(
16267  QCPData(currentIntervalStartKey + keyEpsilon * 0.2,
16268  currentIntervalFirstPoint.value().value));
16269  lineData->append(QCPData(
16270  currentIntervalStartKey + keyEpsilon * 0.25, minValue));
16271  lineData->append(QCPData(
16272  currentIntervalStartKey + keyEpsilon * 0.75, maxValue));
16273  } else
16274  lineData->append(
16275  QCPData(currentIntervalFirstPoint.key(),
16276  currentIntervalFirstPoint.value().value));
16277  }
16278 
16279  if (scatterData) {
16280  double valueMaxRange = valueAxis->range().upper;
16281  double valueMinRange = valueAxis->range().lower;
16282  QCPDataMap::const_iterator it = lower;
16283  QCPDataMap::const_iterator upperEnd = upper + 1;
16284  double minValue = it.value().value;
16285  double maxValue = it.value().value;
16286  QCPDataMap::const_iterator minValueIt = it;
16287  QCPDataMap::const_iterator maxValueIt = it;
16288  QCPDataMap::const_iterator currentIntervalStart = it;
16289  int reversedFactor =
16291  ? -1
16292  : 1; // is used to calculate keyEpsilon pixel into
16293  // the correct direction
16294  int reversedRound =
16296  ? 1
16297  : 0; // is used to switch between floor (normal)
16298  // and ceil (reversed) rounding of
16299  // currentIntervalStartKey
16300  double currentIntervalStartKey = keyAxis->pixelToCoord(
16301  (int)(keyAxis->coordToPixel(lower.key()) + reversedRound));
16302  double keyEpsilon = qAbs(
16303  currentIntervalStartKey -
16305  keyAxis->coordToPixel(currentIntervalStartKey) +
16306  1.0 * reversedFactor)); // interval of one pixel on
16307  // screen when mapped to
16308  // plot key coordinates
16309  bool keyEpsilonVariable =
16310  keyAxis->scaleType() ==
16311  QCPAxis::stLogarithmic; // indicates whether keyEpsilon
16312  // needs to be updated after every
16313  // interval (for log axes)
16314  int intervalDataCount = 1;
16315  ++it; // advance iterator to second data point because adaptive
16316  // sampling works in 1 point retrospect
16317  while (it != upperEnd) {
16318  if (it.key() <
16319  currentIntervalStartKey +
16320  keyEpsilon) // data point is still within same
16321  // pixel, so skip it and expand value
16322  // span of this pixel if necessary
16323  {
16324  if (it.value().value < minValue &&
16325  it.value().value > valueMinRange &&
16326  it.value().value < valueMaxRange) {
16327  minValue = it.value().value;
16328  minValueIt = it;
16329  } else if (it.value().value > maxValue &&
16330  it.value().value > valueMinRange &&
16331  it.value().value < valueMaxRange) {
16332  maxValue = it.value().value;
16333  maxValueIt = it;
16334  }
16335  ++intervalDataCount;
16336  } else // new pixel started
16337  {
16338  if (intervalDataCount >= 2) // last pixel had multiple data
16339  // points, consolidate them
16340  {
16341  // determine value pixel span and add as many points in
16342  // interval to maintain certain vertical data density
16343  // (this is specific to scatter plot):
16344  double valuePixelSpan =
16345  qAbs(valueAxis->coordToPixel(minValue) -
16346  valueAxis->coordToPixel(maxValue));
16347  int dataModulo = qMax(
16348  1, qRound(intervalDataCount /
16349  (valuePixelSpan /
16350  4.0))); // approximately every 4
16351  // value pixels one data
16352  // point on average
16353  QCPDataMap::const_iterator intervalIt =
16354  currentIntervalStart;
16355  int c = 0;
16356  while (intervalIt != it) {
16357  if ((c % dataModulo == 0 ||
16358  intervalIt == minValueIt ||
16359  intervalIt == maxValueIt) &&
16360  intervalIt.value().value > valueMinRange &&
16361  intervalIt.value().value < valueMaxRange)
16362  scatterData->append(intervalIt.value());
16363  ++c;
16364  ++intervalIt;
16365  }
16366  } else if (currentIntervalStart.value().value >
16367  valueMinRange &&
16368  currentIntervalStart.value().value <
16369  valueMaxRange)
16370  scatterData->append(currentIntervalStart.value());
16371  minValue = it.value().value;
16372  maxValue = it.value().value;
16373  currentIntervalStart = it;
16374  currentIntervalStartKey = keyAxis->pixelToCoord(
16375  (int)(keyAxis->coordToPixel(it.key()) +
16376  reversedRound));
16377  if (keyEpsilonVariable)
16378  keyEpsilon =
16379  qAbs(currentIntervalStartKey -
16382  currentIntervalStartKey) +
16383  1.0 * reversedFactor));
16384  intervalDataCount = 1;
16385  }
16386  ++it;
16387  }
16388  // handle last interval:
16389  if (intervalDataCount >=
16390  2) // last pixel had multiple data points, consolidate them
16391  {
16392  // determine value pixel span and add as many points in interval
16393  // to maintain certain vertical data density (this is specific
16394  // to scatter plot):
16395  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue) -
16396  valueAxis->coordToPixel(maxValue));
16397  int dataModulo = qMax(
16398  1, qRound(intervalDataCount /
16399  (valuePixelSpan /
16400  4.0))); // approximately every 4 value
16401  // pixels one data point on average
16402  QCPDataMap::const_iterator intervalIt = currentIntervalStart;
16403  int c = 0;
16404  while (intervalIt != it) {
16405  if ((c % dataModulo == 0 || intervalIt == minValueIt ||
16406  intervalIt == maxValueIt) &&
16407  intervalIt.value().value > valueMinRange &&
16408  intervalIt.value().value < valueMaxRange)
16409  scatterData->append(intervalIt.value());
16410  ++c;
16411  ++intervalIt;
16412  }
16413  } else if (currentIntervalStart.value().value > valueMinRange &&
16414  currentIntervalStart.value().value < valueMaxRange)
16415  scatterData->append(currentIntervalStart.value());
16416  }
16417  } else // don't use adaptive sampling algorithm, transfer points one-to-one
16418  // from the map into the output parameters
16419  {
16420  QVector<QCPData> *dataVector = 0;
16421  if (lineData)
16422  dataVector = lineData;
16423  else if (scatterData)
16424  dataVector = scatterData;
16425  if (dataVector) {
16426  QCPDataMap::const_iterator it = lower;
16427  QCPDataMap::const_iterator upperEnd = upper + 1;
16428  dataVector->reserve(dataCount +
16429  2); // +2 for possible fill end points
16430  while (it != upperEnd) {
16431  dataVector->append(it.value());
16432  ++it;
16433  }
16434  }
16435  if (lineData && scatterData) *scatterData = *dataVector;
16436  }
16437 }
16438 
16448  double x,
16449  double y,
16450  const QCPData &data) const {
16451  if (qIsNaN(data.value)) return;
16452  QCPAxis *keyAxis = mKeyAxis.data();
16453  QCPAxis *valueAxis = mValueAxis.data();
16454  if (!keyAxis || !valueAxis) {
16455  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
16456  return;
16457  }
16458 
16459  double a, b; // positions of error bar bounds in pixels
16460  double barWidthHalf = mErrorBarSize * 0.5;
16461  double skipSymbolMargin =
16462  mScatterStyle.size(); // pixels left blank per side, when
16463  // mErrorBarSkipSymbol is true
16464 
16465  if (keyAxis->orientation() == Qt::Vertical) {
16466  // draw key error vertically and value error horizontally
16467  if (mErrorType == etKey || mErrorType == etBoth) {
16468  a = keyAxis->coordToPixel(data.key - data.keyErrorMinus);
16469  b = keyAxis->coordToPixel(data.key + data.keyErrorPlus);
16470  if (keyAxis->rangeReversed()) qSwap(a, b);
16471  // draw spine:
16472  if (mErrorBarSkipSymbol) {
16473  if (a - y >
16474  skipSymbolMargin) // don't draw spine if error is so small
16475  // it's within skipSymbolmargin
16476  painter->drawLine(QLineF(x, a, x, y + skipSymbolMargin));
16477  if (y - b > skipSymbolMargin)
16478  painter->drawLine(QLineF(x, y - skipSymbolMargin, x, b));
16479  } else
16480  painter->drawLine(QLineF(x, a, x, b));
16481  // draw handles:
16482  painter->drawLine(QLineF(x - barWidthHalf, a, x + barWidthHalf, a));
16483  painter->drawLine(QLineF(x - barWidthHalf, b, x + barWidthHalf, b));
16484  }
16485  if (mErrorType == etValue || mErrorType == etBoth) {
16486  a = valueAxis->coordToPixel(data.value - data.valueErrorMinus);
16487  b = valueAxis->coordToPixel(data.value + data.valueErrorPlus);
16488  if (valueAxis->rangeReversed()) qSwap(a, b);
16489  // draw spine:
16490  if (mErrorBarSkipSymbol) {
16491  if (x - a >
16492  skipSymbolMargin) // don't draw spine if error is so small
16493  // it's within skipSymbolmargin
16494  painter->drawLine(QLineF(a, y, x - skipSymbolMargin, y));
16495  if (b - x > skipSymbolMargin)
16496  painter->drawLine(QLineF(x + skipSymbolMargin, y, b, y));
16497  } else
16498  painter->drawLine(QLineF(a, y, b, y));
16499  // draw handles:
16500  painter->drawLine(QLineF(a, y - barWidthHalf, a, y + barWidthHalf));
16501  painter->drawLine(QLineF(b, y - barWidthHalf, b, y + barWidthHalf));
16502  }
16503  } else // mKeyAxis->orientation() is Qt::Horizontal
16504  {
16505  // draw value error vertically and key error horizontally
16506  if (mErrorType == etKey || mErrorType == etBoth) {
16507  a = keyAxis->coordToPixel(data.key - data.keyErrorMinus);
16508  b = keyAxis->coordToPixel(data.key + data.keyErrorPlus);
16509  if (keyAxis->rangeReversed()) qSwap(a, b);
16510  // draw spine:
16511  if (mErrorBarSkipSymbol) {
16512  if (x - a >
16513  skipSymbolMargin) // don't draw spine if error is so small
16514  // it's within skipSymbolmargin
16515  painter->drawLine(QLineF(a, y, x - skipSymbolMargin, y));
16516  if (b - x > skipSymbolMargin)
16517  painter->drawLine(QLineF(x + skipSymbolMargin, y, b, y));
16518  } else
16519  painter->drawLine(QLineF(a, y, b, y));
16520  // draw handles:
16521  painter->drawLine(QLineF(a, y - barWidthHalf, a, y + barWidthHalf));
16522  painter->drawLine(QLineF(b, y - barWidthHalf, b, y + barWidthHalf));
16523  }
16524  if (mErrorType == etValue || mErrorType == etBoth) {
16525  a = valueAxis->coordToPixel(data.value - data.valueErrorMinus);
16526  b = valueAxis->coordToPixel(data.value + data.valueErrorPlus);
16527  if (valueAxis->rangeReversed()) qSwap(a, b);
16528  // draw spine:
16529  if (mErrorBarSkipSymbol) {
16530  if (a - y >
16531  skipSymbolMargin) // don't draw spine if error is so small
16532  // it's within skipSymbolmargin
16533  painter->drawLine(QLineF(x, a, x, y + skipSymbolMargin));
16534  if (y - b > skipSymbolMargin)
16535  painter->drawLine(QLineF(x, y - skipSymbolMargin, x, b));
16536  } else
16537  painter->drawLine(QLineF(x, a, x, b));
16538  // draw handles:
16539  painter->drawLine(QLineF(x - barWidthHalf, a, x + barWidthHalf, a));
16540  painter->drawLine(QLineF(x - barWidthHalf, b, x + barWidthHalf, b));
16541  }
16542  }
16543 }
16544 
16560 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower,
16561  QCPDataMap::const_iterator &upper) const {
16562  if (!mKeyAxis) {
16563  qDebug() << Q_FUNC_INFO << "invalid key axis";
16564  return;
16565  }
16566  if (mData->isEmpty()) {
16567  lower = mData->constEnd();
16568  upper = mData->constEnd();
16569  return;
16570  }
16571 
16572  // get visible data range as QMap iterators
16573  QCPDataMap::const_iterator lbound =
16574  mData->lowerBound(mKeyAxis.data()->range().lower);
16575  QCPDataMap::const_iterator ubound =
16576  mData->upperBound(mKeyAxis.data()->range().upper);
16577  bool lowoutlier =
16578  lbound != mData->constBegin(); // indicates whether there exist
16579  // points below axis range
16580  bool highoutlier =
16581  ubound != mData->constEnd(); // indicates whether there exist
16582  // points above axis range
16583 
16584  lower = (lowoutlier
16585  ? lbound - 1
16586  : lbound); // data point range that will be actually drawn
16587  upper = (highoutlier ? ubound : ubound - 1); // data point range that will
16588  // be actually drawn
16589 }
16590 
16602 int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower,
16603  const QCPDataMap::const_iterator &upper,
16604  int maxCount) const {
16605  if (upper == mData->constEnd() && lower == mData->constEnd()) return 0;
16606  QCPDataMap::const_iterator it = lower;
16607  int count = 1;
16608  while (it != upper && count < maxCount) {
16609  ++it;
16610  ++count;
16611  }
16612  return count;
16613 }
16614 
16631 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const {
16632  if (!mKeyAxis) {
16633  qDebug() << Q_FUNC_INFO << "invalid key axis";
16634  return;
16635  }
16636 
16637  // append points that close the polygon fill at the key axis:
16638  if (mKeyAxis.data()->orientation() == Qt::Vertical) {
16639  *lineData << upperFillBasePoint(lineData->last().y());
16640  *lineData << lowerFillBasePoint(lineData->first().y());
16641  } else {
16642  *lineData << upperFillBasePoint(lineData->last().x());
16643  *lineData << lowerFillBasePoint(lineData->first().x());
16644  }
16645 }
16646 
16654 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const {
16655  lineData->remove(lineData->size() - 2, 2);
16656 }
16657 
16673 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const {
16674  QCPAxis *keyAxis = mKeyAxis.data();
16675  QCPAxis *valueAxis = mValueAxis.data();
16676  if (!keyAxis || !valueAxis) {
16677  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
16678  return QPointF();
16679  }
16680 
16681  QPointF point;
16683  if (keyAxis->axisType() == QCPAxis::atLeft) {
16684  point.setX(valueAxis->coordToPixel(0));
16685  point.setY(lowerKey);
16686  } else if (keyAxis->axisType() == QCPAxis::atRight) {
16687  point.setX(valueAxis->coordToPixel(0));
16688  point.setY(lowerKey);
16689  } else if (keyAxis->axisType() == QCPAxis::atTop) {
16690  point.setX(lowerKey);
16691  point.setY(valueAxis->coordToPixel(0));
16692  } else if (keyAxis->axisType() == QCPAxis::atBottom) {
16693  point.setX(lowerKey);
16694  point.setY(valueAxis->coordToPixel(0));
16695  }
16696  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16697  {
16698  // In logarithmic scaling we can't just draw to value zero so we just
16699  // fill all the way to the axis which is in the direction towards zero
16700  if (keyAxis->orientation() == Qt::Vertical) {
16701  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16702  (valueAxis->range().upper > 0 &&
16703  valueAxis->rangeReversed())) // if range is negative, zero is
16704  // on opposite side of key axis
16705  point.setX(keyAxis->axisRect()->right());
16706  else
16707  point.setX(keyAxis->axisRect()->left());
16708  point.setY(lowerKey);
16709  } else if (keyAxis->axisType() == QCPAxis::atTop ||
16711  point.setX(lowerKey);
16712  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16713  (valueAxis->range().upper > 0 &&
16714  valueAxis->rangeReversed())) // if range is negative, zero is
16715  // on opposite side of key axis
16716  point.setY(keyAxis->axisRect()->top());
16717  else
16718  point.setY(keyAxis->axisRect()->bottom());
16719  }
16720  }
16721  return point;
16722 }
16723 
16739 QPointF QCPGraph::upperFillBasePoint(double upperKey) const {
16740  QCPAxis *keyAxis = mKeyAxis.data();
16741  QCPAxis *valueAxis = mValueAxis.data();
16742  if (!keyAxis || !valueAxis) {
16743  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
16744  return QPointF();
16745  }
16746 
16747  QPointF point;
16749  if (keyAxis->axisType() == QCPAxis::atLeft) {
16750  point.setX(valueAxis->coordToPixel(0));
16751  point.setY(upperKey);
16752  } else if (keyAxis->axisType() == QCPAxis::atRight) {
16753  point.setX(valueAxis->coordToPixel(0));
16754  point.setY(upperKey);
16755  } else if (keyAxis->axisType() == QCPAxis::atTop) {
16756  point.setX(upperKey);
16757  point.setY(valueAxis->coordToPixel(0));
16758  } else if (keyAxis->axisType() == QCPAxis::atBottom) {
16759  point.setX(upperKey);
16760  point.setY(valueAxis->coordToPixel(0));
16761  }
16762  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16763  {
16764  // In logarithmic scaling we can't just draw to value 0 so we just fill
16765  // all the way to the axis which is in the direction towards 0
16766  if (keyAxis->orientation() == Qt::Vertical) {
16767  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16768  (valueAxis->range().upper > 0 &&
16769  valueAxis->rangeReversed())) // if range is negative, zero is
16770  // on opposite side of key axis
16771  point.setX(keyAxis->axisRect()->right());
16772  else
16773  point.setX(keyAxis->axisRect()->left());
16774  point.setY(upperKey);
16775  } else if (keyAxis->axisType() == QCPAxis::atTop ||
16777  point.setX(upperKey);
16778  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16779  (valueAxis->range().upper > 0 &&
16780  valueAxis->rangeReversed())) // if range is negative, zero is
16781  // on opposite side of key axis
16782  point.setY(keyAxis->axisRect()->top());
16783  else
16784  point.setY(keyAxis->axisRect()->bottom());
16785  }
16786  }
16787  return point;
16788 }
16789 
16801  const QVector<QPointF> *lineData) const {
16802  if (!mChannelFillGraph) return QPolygonF();
16803 
16804  QCPAxis *keyAxis = mKeyAxis.data();
16805  QCPAxis *valueAxis = mValueAxis.data();
16806  if (!keyAxis || !valueAxis) {
16807  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
16808  return QPolygonF();
16809  }
16810  if (!mChannelFillGraph.data()->mKeyAxis) {
16811  qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid";
16812  return QPolygonF();
16813  }
16814 
16815  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() !=
16816  keyAxis->orientation())
16817  return QPolygonF(); // don't have same axis orientation, can't fill
16818  // that (Note: if keyAxis fits, valueAxis will fit
16819  // too, because it's always orthogonal to keyAxis)
16820 
16821  if (lineData->isEmpty()) return QPolygonF();
16822  QVector<QPointF> otherData;
16823  mChannelFillGraph.data()->getPlotData(&otherData, 0);
16824  if (otherData.isEmpty()) return QPolygonF();
16825  QVector<QPointF> thisData;
16826  thisData.reserve(lineData->size() +
16827  otherData.size()); // because we will join both vectors at
16828  // end of this function
16829  for (int i = 0; i < lineData->size();
16830  ++i) // don't use the vector<<(vector), it squeezes internally, which
16831  // ruins the performance tuning with reserve()
16832  thisData << lineData->at(i);
16833 
16834  // pointers to be able to swap them, depending which data range needs
16835  // cropping:
16836  QVector<QPointF> *staticData = &thisData;
16837  QVector<QPointF> *croppedData = &otherData;
16838 
16839  // crop both vectors to ranges in which the keys overlap (which coord is
16840  // key, depends on axisType):
16841  if (keyAxis->orientation() == Qt::Horizontal) {
16842  // x is key
16843  // if an axis range is reversed, the data point keys will be descending.
16844  // Reverse them, since following algorithm assumes ascending keys:
16845  if (staticData->first().x() > staticData->last().x()) {
16846  int size = staticData->size();
16847  for (int i = 0; i < size / 2; ++i)
16848  qSwap((*staticData)[i], (*staticData)[size - 1 - i]);
16849  }
16850  if (croppedData->first().x() > croppedData->last().x()) {
16851  int size = croppedData->size();
16852  for (int i = 0; i < size / 2; ++i)
16853  qSwap((*croppedData)[i], (*croppedData)[size - 1 - i]);
16854  }
16855  // crop lower bound:
16856  if (staticData->first().x() <
16857  croppedData->first().x()) // other one must be cropped
16858  qSwap(staticData, croppedData);
16859  int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16860  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16861  croppedData->remove(0, lowBound);
16862  // set lowest point of cropped data to fit exactly key position of first
16863  // static data point via linear interpolation:
16864  if (croppedData->size() < 2)
16865  return QPolygonF(); // need at least two points for interpolation
16866  double slope;
16867  if (croppedData->at(1).x() - croppedData->at(0).x() != 0)
16868  slope = (croppedData->at(1).y() - croppedData->at(0).y()) /
16869  (croppedData->at(1).x() - croppedData->at(0).x());
16870  else
16871  slope = 0;
16872  (*croppedData)[0].setY(
16873  croppedData->at(0).y() +
16874  slope * (staticData->first().x() - croppedData->at(0).x()));
16875  (*croppedData)[0].setX(staticData->first().x());
16876 
16877  // crop upper bound:
16878  if (staticData->last().x() >
16879  croppedData->last().x()) // other one must be cropped
16880  qSwap(staticData, croppedData);
16881  int highBound = findIndexAboveX(croppedData, staticData->last().x());
16882  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16883  croppedData->remove(highBound + 1,
16884  croppedData->size() - (highBound + 1));
16885  // set highest point of cropped data to fit exactly key position of last
16886  // static data point via linear interpolation:
16887  if (croppedData->size() < 2)
16888  return QPolygonF(); // need at least two points for interpolation
16889  int li = croppedData->size() - 1; // last index
16890  if (croppedData->at(li).x() - croppedData->at(li - 1).x() != 0)
16891  slope = (croppedData->at(li).y() - croppedData->at(li - 1).y()) /
16892  (croppedData->at(li).x() - croppedData->at(li - 1).x());
16893  else
16894  slope = 0;
16895  (*croppedData)[li].setY(
16896  croppedData->at(li - 1).y() +
16897  slope * (staticData->last().x() - croppedData->at(li - 1).x()));
16898  (*croppedData)[li].setX(staticData->last().x());
16899  } else // mKeyAxis->orientation() == Qt::Vertical
16900  {
16901  // y is key
16902  // similar to "x is key" but switched x,y. Further, lower/upper meaning
16903  // is inverted compared to x, because in pixel coordinates, y increases
16904  // from top to bottom, not bottom to top like data coordinate. if an
16905  // axis range is reversed, the data point keys will be descending.
16906  // Reverse them, since following algorithm assumes ascending keys:
16907  if (staticData->first().y() < staticData->last().y()) {
16908  int size = staticData->size();
16909  for (int i = 0; i < size / 2; ++i)
16910  qSwap((*staticData)[i], (*staticData)[size - 1 - i]);
16911  }
16912  if (croppedData->first().y() < croppedData->last().y()) {
16913  int size = croppedData->size();
16914  for (int i = 0; i < size / 2; ++i)
16915  qSwap((*croppedData)[i], (*croppedData)[size - 1 - i]);
16916  }
16917  // crop lower bound:
16918  if (staticData->first().y() >
16919  croppedData->first().y()) // other one must be cropped
16920  qSwap(staticData, croppedData);
16921  int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16922  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16923  croppedData->remove(0, lowBound);
16924  // set lowest point of cropped data to fit exactly key position of first
16925  // static data point via linear interpolation:
16926  if (croppedData->size() < 2)
16927  return QPolygonF(); // need at least two points for interpolation
16928  double slope;
16929  if (croppedData->at(1).y() - croppedData->at(0).y() !=
16930  0) // avoid division by zero in step plots
16931  slope = (croppedData->at(1).x() - croppedData->at(0).x()) /
16932  (croppedData->at(1).y() - croppedData->at(0).y());
16933  else
16934  slope = 0;
16935  (*croppedData)[0].setX(
16936  croppedData->at(0).x() +
16937  slope * (staticData->first().y() - croppedData->at(0).y()));
16938  (*croppedData)[0].setY(staticData->first().y());
16939 
16940  // crop upper bound:
16941  if (staticData->last().y() <
16942  croppedData->last().y()) // other one must be cropped
16943  qSwap(staticData, croppedData);
16944  int highBound = findIndexBelowY(croppedData, staticData->last().y());
16945  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16946  croppedData->remove(highBound + 1,
16947  croppedData->size() - (highBound + 1));
16948  // set highest point of cropped data to fit exactly key position of last
16949  // static data point via linear interpolation:
16950  if (croppedData->size() < 2)
16951  return QPolygonF(); // need at least two points for interpolation
16952  int li = croppedData->size() - 1; // last index
16953  if (croppedData->at(li).y() - croppedData->at(li - 1).y() !=
16954  0) // avoid division by zero in step plots
16955  slope = (croppedData->at(li).x() - croppedData->at(li - 1).x()) /
16956  (croppedData->at(li).y() - croppedData->at(li - 1).y());
16957  else
16958  slope = 0;
16959  (*croppedData)[li].setX(
16960  croppedData->at(li - 1).x() +
16961  slope * (staticData->last().y() - croppedData->at(li - 1).y()));
16962  (*croppedData)[li].setY(staticData->last().y());
16963  }
16964 
16965  // return joined:
16966  for (int i = otherData.size() - 1; i >= 0;
16967  --i) // insert reversed, otherwise the polygon will be twisted
16968  thisData << otherData.at(i);
16969  return QPolygonF(thisData);
16970 }
16971 
16980 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const {
16981  for (int i = data->size() - 1; i >= 0; --i) {
16982  if (data->at(i).x() < x) {
16983  if (i < data->size() - 1)
16984  return i + 1;
16985  else
16986  return data->size() - 1;
16987  }
16988  }
16989  return -1;
16990 }
16991 
17000 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const {
17001  for (int i = 0; i < data->size(); ++i) {
17002  if (data->at(i).x() > x) {
17003  if (i > 0)
17004  return i - 1;
17005  else
17006  return 0;
17007  }
17008  }
17009  return -1;
17010 }
17011 
17020 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const {
17021  for (int i = 0; i < data->size(); ++i) {
17022  if (data->at(i).y() < y) {
17023  if (i > 0)
17024  return i - 1;
17025  else
17026  return 0;
17027  }
17028  }
17029  return -1;
17030 }
17031 
17042 double QCPGraph::pointDistance(const QPointF &pixelPoint) const {
17043  if (mData->isEmpty()) return -1.0;
17044  if (mLineStyle == lsNone && mScatterStyle.isNone()) return -1.0;
17045 
17046  // calculate minimum distances to graph representation:
17047  if (mLineStyle == lsNone) {
17048  // no line displayed, only calculate distance to scatter points:
17049  QVector<QCPData> scatterData;
17050  getScatterPlotData(&scatterData);
17051  if (scatterData.size() > 0) {
17052  double minDistSqr = std::numeric_limits<double>::max();
17053  for (int i = 0; i < scatterData.size(); ++i) {
17054  double currentDistSqr =
17055  QVector2D(coordsToPixels(scatterData.at(i).key,
17056  scatterData.at(i).value) -
17057  pixelPoint)
17058  .lengthSquared();
17059  if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
17060  }
17061  return qSqrt(minDistSqr);
17062  } else // no data available in view to calculate distance to
17063  return -1.0;
17064  } else {
17065  // line displayed, calculate distance to line segments:
17066  QVector<QPointF> lineData;
17067  getPlotData(&lineData, 0); // unlike with getScatterPlotData we get
17068  // pixel coordinates here
17069  if (lineData.size() >
17070  1) // at least one line segment, compare distance to line segments
17071  {
17072  double minDistSqr = std::numeric_limits<double>::max();
17073  if (mLineStyle == lsImpulse) {
17074  // impulse plot differs from other line styles in that the
17075  // lineData points are only pairwise connected:
17076  for (int i = 0; i < lineData.size() - 1;
17077  i += 2) // iterate pairs
17078  {
17079  double currentDistSqr = distSqrToLine(
17080  lineData.at(i), lineData.at(i + 1), pixelPoint);
17081  if (currentDistSqr < minDistSqr)
17082  minDistSqr = currentDistSqr;
17083  }
17084  } else {
17085  // all other line plots (line and step) connect points directly:
17086  for (int i = 0; i < lineData.size() - 1; ++i) {
17087  double currentDistSqr = distSqrToLine(
17088  lineData.at(i), lineData.at(i + 1), pixelPoint);
17089  if (currentDistSqr < minDistSqr)
17090  minDistSqr = currentDistSqr;
17091  }
17092  }
17093  return qSqrt(minDistSqr);
17094  } else if (lineData.size() > 0) // only single data point, calculate
17095  // distance to that point
17096  {
17097  return QVector2D(lineData.at(0) - pixelPoint).length();
17098  } else // no data available in view to calculate distance to
17099  return -1.0;
17100  }
17101 }
17102 
17111 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const {
17112  for (int i = data->size() - 1; i >= 0; --i) {
17113  if (data->at(i).y() > y) {
17114  if (i < data->size() - 1)
17115  return i + 1;
17116  else
17117  return data->size() - 1;
17118  }
17119  }
17120  return -1;
17121 }
17122 
17123 /* inherits documentation from base class */
17125  SignDomain inSignDomain) const {
17126  // just call the specialized version which takes an additional argument
17127  // whether error bars should also be taken into consideration for range
17128  // calculation. We set this to true here.
17129  return getKeyRange(foundRange, inSignDomain, true);
17130 }
17131 
17132 /* inherits documentation from base class */
17134  SignDomain inSignDomain) const {
17135  // just call the specialized version which takes an additional argument
17136  // whether error bars should also be taken into consideration for range
17137  // calculation. We set this to true here.
17138  return getValueRange(foundRange, inSignDomain, true);
17139 }
17140 
17149  SignDomain inSignDomain,
17150  bool includeErrors) const {
17151  QCPRange range;
17152  bool haveLower = false;
17153  bool haveUpper = false;
17154 
17155  double current, currentErrorMinus, currentErrorPlus;
17156 
17157  if (inSignDomain == sdBoth) // range may be anywhere
17158  {
17159  QCPDataMap::const_iterator it = mData->constBegin();
17160  while (it != mData->constEnd()) {
17161  if (!qIsNaN(it.value().value)) {
17162  current = it.value().key;
17163  currentErrorMinus =
17164  (includeErrors ? it.value().keyErrorMinus : 0);
17165  currentErrorPlus =
17166  (includeErrors ? it.value().keyErrorPlus : 0);
17167  if (current - currentErrorMinus < range.lower || !haveLower) {
17168  range.lower = current - currentErrorMinus;
17169  haveLower = true;
17170  }
17171  if (current + currentErrorPlus > range.upper || !haveUpper) {
17172  range.upper = current + currentErrorPlus;
17173  haveUpper = true;
17174  }
17175  }
17176  ++it;
17177  }
17178  } else if (inSignDomain ==
17179  sdNegative) // range may only be in the negative sign domain
17180  {
17181  QCPDataMap::const_iterator it = mData->constBegin();
17182  while (it != mData->constEnd()) {
17183  if (!qIsNaN(it.value().value)) {
17184  current = it.value().key;
17185  currentErrorMinus =
17186  (includeErrors ? it.value().keyErrorMinus : 0);
17187  currentErrorPlus =
17188  (includeErrors ? it.value().keyErrorPlus : 0);
17189  if ((current - currentErrorMinus < range.lower || !haveLower) &&
17190  current - currentErrorMinus < 0) {
17191  range.lower = current - currentErrorMinus;
17192  haveLower = true;
17193  }
17194  if ((current + currentErrorPlus > range.upper || !haveUpper) &&
17195  current + currentErrorPlus < 0) {
17196  range.upper = current + currentErrorPlus;
17197  haveUpper = true;
17198  }
17199  if (includeErrors) // in case point is in valid sign domain but
17200  // errobars stretch beyond it, we still want
17201  // to geht that point.
17202  {
17203  if ((current < range.lower || !haveLower) && current < 0) {
17204  range.lower = current;
17205  haveLower = true;
17206  }
17207  if ((current > range.upper || !haveUpper) && current < 0) {
17208  range.upper = current;
17209  haveUpper = true;
17210  }
17211  }
17212  }
17213  ++it;
17214  }
17215  } else if (inSignDomain ==
17216  sdPositive) // range may only be in the positive sign domain
17217  {
17218  QCPDataMap::const_iterator it = mData->constBegin();
17219  while (it != mData->constEnd()) {
17220  if (!qIsNaN(it.value().value)) {
17221  current = it.value().key;
17222  currentErrorMinus =
17223  (includeErrors ? it.value().keyErrorMinus : 0);
17224  currentErrorPlus =
17225  (includeErrors ? it.value().keyErrorPlus : 0);
17226  if ((current - currentErrorMinus < range.lower || !haveLower) &&
17227  current - currentErrorMinus > 0) {
17228  range.lower = current - currentErrorMinus;
17229  haveLower = true;
17230  }
17231  if ((current + currentErrorPlus > range.upper || !haveUpper) &&
17232  current + currentErrorPlus > 0) {
17233  range.upper = current + currentErrorPlus;
17234  haveUpper = true;
17235  }
17236  if (includeErrors) // in case point is in valid sign domain but
17237  // errobars stretch beyond it, we still want
17238  // to get that point.
17239  {
17240  if ((current < range.lower || !haveLower) && current > 0) {
17241  range.lower = current;
17242  haveLower = true;
17243  }
17244  if ((current > range.upper || !haveUpper) && current > 0) {
17245  range.upper = current;
17246  haveUpper = true;
17247  }
17248  }
17249  }
17250  ++it;
17251  }
17252  }
17253 
17254  foundRange = haveLower && haveUpper;
17255  return range;
17256 }
17257 
17266  SignDomain inSignDomain,
17267  bool includeErrors) const {
17268  QCPRange range;
17269  bool haveLower = false;
17270  bool haveUpper = false;
17271 
17272  double current, currentErrorMinus, currentErrorPlus;
17273 
17274  if (inSignDomain == sdBoth) // range may be anywhere
17275  {
17276  QCPDataMap::const_iterator it = mData->constBegin();
17277  while (it != mData->constEnd()) {
17278  current = it.value().value;
17279  if (!qIsNaN(current)) {
17280  currentErrorMinus =
17281  (includeErrors ? it.value().valueErrorMinus : 0);
17282  currentErrorPlus =
17283  (includeErrors ? it.value().valueErrorPlus : 0);
17284  if (current - currentErrorMinus < range.lower || !haveLower) {
17285  range.lower = current - currentErrorMinus;
17286  haveLower = true;
17287  }
17288  if (current + currentErrorPlus > range.upper || !haveUpper) {
17289  range.upper = current + currentErrorPlus;
17290  haveUpper = true;
17291  }
17292  }
17293  ++it;
17294  }
17295  } else if (inSignDomain ==
17296  sdNegative) // range may only be in the negative sign domain
17297  {
17298  QCPDataMap::const_iterator it = mData->constBegin();
17299  while (it != mData->constEnd()) {
17300  current = it.value().value;
17301  if (!qIsNaN(current)) {
17302  currentErrorMinus =
17303  (includeErrors ? it.value().valueErrorMinus : 0);
17304  currentErrorPlus =
17305  (includeErrors ? it.value().valueErrorPlus : 0);
17306  if ((current - currentErrorMinus < range.lower || !haveLower) &&
17307  current - currentErrorMinus < 0) {
17308  range.lower = current - currentErrorMinus;
17309  haveLower = true;
17310  }
17311  if ((current + currentErrorPlus > range.upper || !haveUpper) &&
17312  current + currentErrorPlus < 0) {
17313  range.upper = current + currentErrorPlus;
17314  haveUpper = true;
17315  }
17316  if (includeErrors) // in case point is in valid sign domain but
17317  // errobars stretch beyond it, we still want
17318  // to get that point.
17319  {
17320  if ((current < range.lower || !haveLower) && current < 0) {
17321  range.lower = current;
17322  haveLower = true;
17323  }
17324  if ((current > range.upper || !haveUpper) && current < 0) {
17325  range.upper = current;
17326  haveUpper = true;
17327  }
17328  }
17329  }
17330  ++it;
17331  }
17332  } else if (inSignDomain ==
17333  sdPositive) // range may only be in the positive sign domain
17334  {
17335  QCPDataMap::const_iterator it = mData->constBegin();
17336  while (it != mData->constEnd()) {
17337  current = it.value().value;
17338  if (!qIsNaN(current)) {
17339  currentErrorMinus =
17340  (includeErrors ? it.value().valueErrorMinus : 0);
17341  currentErrorPlus =
17342  (includeErrors ? it.value().valueErrorPlus : 0);
17343  if ((current - currentErrorMinus < range.lower || !haveLower) &&
17344  current - currentErrorMinus > 0) {
17345  range.lower = current - currentErrorMinus;
17346  haveLower = true;
17347  }
17348  if ((current + currentErrorPlus > range.upper || !haveUpper) &&
17349  current + currentErrorPlus > 0) {
17350  range.upper = current + currentErrorPlus;
17351  haveUpper = true;
17352  }
17353  if (includeErrors) // in case point is in valid sign domain but
17354  // errobars stretch beyond it, we still want
17355  // to geht that point.
17356  {
17357  if ((current < range.lower || !haveLower) && current > 0) {
17358  range.lower = current;
17359  haveLower = true;
17360  }
17361  if ((current > range.upper || !haveUpper) && current > 0) {
17362  range.upper = current;
17363  haveUpper = true;
17364  }
17365  }
17366  }
17367  ++it;
17368  }
17369  }
17370 
17371  foundRange = haveLower && haveUpper;
17372  return range;
17373 }
17374 
17378 
17396 QCPCurveData::QCPCurveData() : t(0), key(0), value(0) {}
17397 
17401 QCPCurveData::QCPCurveData(double t, double key, double value)
17402  : t(t), key(key), value(value) {}
17403 
17407 
17451 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
17452  : QCPAbstractPlottable(keyAxis, valueAxis) {
17453  mData = new QCPCurveDataMap;
17454  mPen.setColor(Qt::blue);
17455  mPen.setStyle(Qt::SolidLine);
17456  mBrush.setColor(Qt::blue);
17457  mBrush.setStyle(Qt::NoBrush);
17458  mSelectedPen = mPen;
17459  mSelectedPen.setWidthF(2.5);
17460  mSelectedPen.setColor(
17461  QColor(80, 80, 255)); // lighter than Qt::blue of mPen
17463 
17466 }
17467 
17468 QCPCurve::~QCPCurve() { delete mData; }
17469 
17479  if (mData == data) {
17480  qDebug() << Q_FUNC_INFO
17481  << "The data pointer is already in (and owned by) this "
17482  "plottable"
17483  << reinterpret_cast<quintptr>(data);
17484  return;
17485  }
17486  if (copy) {
17487  *mData = *data;
17488  } else {
17489  delete mData;
17490  mData = data;
17491  }
17492 }
17493 
17500 void QCPCurve::setData(const QVector<double> &t,
17501  const QVector<double> &key,
17502  const QVector<double> &value) {
17503  mData->clear();
17504  int n = t.size();
17505  n = qMin(n, key.size());
17506  n = qMin(n, value.size());
17507  QCPCurveData newData;
17508  for (int i = 0; i < n; ++i) {
17509  newData.t = t[i];
17510  newData.key = key[i];
17511  newData.value = value[i];
17512  qtCompatMapInsertMulti(mData, newData.t, newData);
17513  }
17514 }
17515 
17522 void QCPCurve::setData(const QVector<double> &key,
17523  const QVector<double> &value) {
17524  mData->clear();
17525  int n = key.size();
17526  n = qMin(n, value.size());
17527  QCPCurveData newData;
17528  for (int i = 0; i < n; ++i) {
17529  newData.t = i; // no t vector given, so we assign t the index of the
17530  // key/value pair
17531  newData.key = key[i];
17532  newData.value = value[i];
17533  qtCompatMapInsertMulti(mData, newData.t, newData);
17534  }
17535 }
17536 
17544 void QCPCurve::setScatterStyle(const QCPScatterStyle &style) {
17545  mScatterStyle = style;
17546 }
17547 
17556 void QCPCurve::setLineStyle(QCPCurve::LineStyle style) { mLineStyle = style; }
17557 
17562 void QCPCurve::addData(const QCPCurveDataMap &dataMap) {
17563  qtCompatMapUnite(mData, dataMap);
17564 }
17565 
17572 }
17573 
17578 void QCPCurve::addData(double t, double key, double value) {
17579  QCPCurveData newData;
17580  newData.t = t;
17581  newData.key = key;
17582  newData.value = value;
17583  qtCompatMapInsertMulti(mData, newData.t, newData);
17584 }
17585 
17594 void QCPCurve::addData(double key, double value) {
17595  QCPCurveData newData;
17596  if (!mData->isEmpty())
17597  newData.t = (mData->constEnd() - 1).key() + 1;
17598  else
17599  newData.t = 0;
17600  newData.key = key;
17601  newData.value = value;
17602  qtCompatMapInsertMulti(mData, newData.t, newData);
17603 }
17604 
17609 void QCPCurve::addData(const QVector<double> &ts,
17610  const QVector<double> &keys,
17611  const QVector<double> &values) {
17612  int n = ts.size();
17613  n = qMin(n, keys.size());
17614  n = qMin(n, values.size());
17615  QCPCurveData newData;
17616  for (int i = 0; i < n; ++i) {
17617  newData.t = ts[i];
17618  newData.key = keys[i];
17619  newData.value = values[i];
17620  qtCompatMapInsertMulti(mData, newData.t, newData);
17621  }
17622 }
17623 
17629  QCPCurveDataMap::iterator it = mData->begin();
17630  while (it != mData->end() && it.key() < t) it = mData->erase(it);
17631 }
17632 
17638  if (mData->isEmpty()) return;
17639  QCPCurveDataMap::iterator it = mData->upperBound(t);
17640  while (it != mData->end()) it = mData->erase(it);
17641 }
17642 
17650 void QCPCurve::removeData(double fromt, double tot) {
17651  if (fromt >= tot || mData->isEmpty()) return;
17652  QCPCurveDataMap::iterator it = mData->upperBound(fromt);
17653  QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
17654  while (it != itEnd) it = mData->erase(it);
17655 }
17656 
17666 void QCPCurve::removeData(double t) { mData->remove(t); }
17667 
17672 void QCPCurve::clearData() { mData->clear(); }
17673 
17674 /* inherits documentation from base class */
17675 double QCPCurve::selectTest(const QPointF &pos,
17676  bool onlySelectable,
17677  QVariant *details) const {
17678  Q_UNUSED(details)
17679  if ((onlySelectable && !mSelectable) || mData->isEmpty()) return -1;
17680  if (!mKeyAxis || !mValueAxis) {
17681  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
17682  return -1;
17683  }
17684 
17685  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17686  return pointDistance(pos);
17687  else
17688  return -1;
17689 }
17690 
17691 /* inherits documentation from base class */
17692 void QCPCurve::draw(QCPPainter *painter) {
17693  if (mData->isEmpty()) return;
17694 
17695  // allocate line vector:
17696  QVector<QPointF> *lineData = new QVector<QPointF>;
17697 
17698  // fill with curve data:
17699  getCurveData(lineData);
17700 
17701  // check data validity if flag set:
17702 #ifdef QCUSTOMPLOT_CHECK_DATA
17703  QCPCurveDataMap::const_iterator it;
17704  for (it = mData->constBegin(); it != mData->constEnd(); ++it) {
17705  if (QCP::isInvalidData(it.value().t) ||
17706  QCP::isInvalidData(it.value().key, it.value().value))
17707  qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid."
17708  << "Plottable name:" << name();
17709  }
17710 #endif
17711 
17712  // draw curve fill:
17713  if (mainBrush().style() != Qt::NoBrush &&
17714  mainBrush().color().alpha() != 0) {
17715  applyFillAntialiasingHint(painter);
17716  painter->setPen(Qt::NoPen);
17717  painter->setBrush(mainBrush());
17718  painter->drawPolygon(QPolygonF(*lineData));
17719  }
17720 
17721  // draw curve line:
17722  if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen &&
17723  mainPen().color().alpha() != 0) {
17725  painter->setPen(mainPen());
17726  painter->setBrush(Qt::NoBrush);
17727  // if drawing solid line and not in PDF, use much faster line drawing
17728  // instead of polyline:
17729  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17730  painter->pen().style() == Qt::SolidLine &&
17731  !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17732  !painter->modes().testFlag(QCPPainter::pmNoCaching)) {
17733  int i = 0;
17734  bool lastIsNan = false;
17735  const int lineDataSize = lineData->size();
17736  while (i < lineDataSize &&
17737  (qIsNaN(lineData->at(i).y()) ||
17738  qIsNaN(lineData->at(i)
17739  .x()))) // make sure first point is not NaN
17740  ++i;
17741  ++i; // because drawing works in 1 point retrospect
17742  while (i < lineDataSize) {
17743  if (!qIsNaN(lineData->at(i).y()) &&
17744  !qIsNaN(lineData->at(i)
17745  .x())) // NaNs create a gap in the line
17746  {
17747  if (!lastIsNan)
17748  painter->drawLine(lineData->at(i - 1), lineData->at(i));
17749  else
17750  lastIsNan = false;
17751  } else
17752  lastIsNan = true;
17753  ++i;
17754  }
17755  } else {
17756  int segmentStart = 0;
17757  int i = 0;
17758  const int lineDataSize = lineData->size();
17759  while (i < lineDataSize) {
17760  if (qIsNaN(lineData->at(i).y()) ||
17761  qIsNaN(lineData->at(i)
17762  .x())) // NaNs create a gap in the line
17763  {
17764  painter->drawPolyline(
17765  lineData->constData() + segmentStart,
17766  i - segmentStart); // i, because we don't want to
17767  // include the current NaN point
17768  segmentStart = i + 1;
17769  }
17770  ++i;
17771  }
17772  // draw last segment:
17773  painter->drawPolyline(lineData->constData() + segmentStart,
17774  lineDataSize - segmentStart);
17775  }
17776  }
17777 
17778  // draw scatters:
17779  if (!mScatterStyle.isNone()) drawScatterPlot(painter, lineData);
17780 
17781  // free allocated line data:
17782  delete lineData;
17783 }
17784 
17785 /* inherits documentation from base class */
17786 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const {
17787  // draw fill:
17788  if (mBrush.style() != Qt::NoBrush) {
17789  applyFillAntialiasingHint(painter);
17790  painter->fillRect(QRectF(rect.left(), rect.top() + rect.height() / 2.0,
17791  rect.width(), rect.height() / 3.0),
17792  mBrush);
17793  }
17794  // draw line vertically centered:
17795  if (mLineStyle != lsNone) {
17797  painter->setPen(mPen);
17798  painter->drawLine(QLineF(
17799  rect.left(), rect.top() + rect.height() / 2.0, rect.right() + 5,
17800  rect.top() + rect.height() /
17801  2.0)); // +5 on x2 else last segment is
17802  // missing from dashed/dotted pens
17803  }
17804  // draw scatter symbol:
17805  if (!mScatterStyle.isNone()) {
17807  // scale scatter pixmap if it's too large to fit in legend icon rect:
17809  (mScatterStyle.pixmap().size().width() > rect.width() ||
17810  mScatterStyle.pixmap().size().height() > rect.height())) {
17811  QCPScatterStyle scaledStyle(mScatterStyle);
17812  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(
17813  rect.size().toSize(), Qt::KeepAspectRatio,
17814  Qt::SmoothTransformation));
17815  scaledStyle.applyTo(painter, mPen);
17816  scaledStyle.drawShape(painter, QRectF(rect).center());
17817  } else {
17818  mScatterStyle.applyTo(painter, mPen);
17819  mScatterStyle.drawShape(painter, QRectF(rect).center());
17820  }
17821  }
17822 }
17823 
17831  const QVector<QPointF> *pointData) const {
17832  // draw scatter point symbols:
17834  mScatterStyle.applyTo(painter, mPen);
17835  for (int i = 0; i < pointData->size(); ++i)
17836  if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
17837  mScatterStyle.drawShape(painter, pointData->at(i));
17838 }
17839 
17855 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const {
17856  QCPAxis *keyAxis = mKeyAxis.data();
17857  QCPAxis *valueAxis = mValueAxis.data();
17858  if (!keyAxis || !valueAxis) {
17859  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
17860  return;
17861  }
17862 
17863  // add margins to rect to compensate for stroke width
17864  double strokeMargin = qMax(
17865  qreal(1.0),
17866  qreal(mainPen().widthF() * 0.75)); // stroke radius + 50% safety
17867  if (!mScatterStyle.isNone())
17868  strokeMargin = qMax(strokeMargin, mScatterStyle.size());
17869  double rectLeft = keyAxis->pixelToCoord(
17871  strokeMargin * ((keyAxis->orientation() == Qt::Vertical) !=
17873  ? -1
17874  : 1));
17875  double rectRight = keyAxis->pixelToCoord(
17877  strokeMargin * ((keyAxis->orientation() == Qt::Vertical) !=
17879  ? -1
17880  : 1));
17881  double rectBottom = valueAxis->pixelToCoord(
17883  strokeMargin * ((valueAxis->orientation() == Qt::Horizontal) !=
17885  ? -1
17886  : 1));
17887  double rectTop = valueAxis->pixelToCoord(
17889  strokeMargin * ((valueAxis->orientation() == Qt::Horizontal) !=
17891  ? -1
17892  : 1));
17893  int currentRegion;
17894  QCPCurveDataMap::const_iterator it = mData->constBegin();
17895  QCPCurveDataMap::const_iterator prevIt = mData->constEnd() - 1;
17896  int prevRegion = getRegion(prevIt.value().key, prevIt.value().value,
17897  rectLeft, rectTop, rectRight, rectBottom);
17898  QVector<QPointF>
17899  trailingPoints; // points that must be applied after all other
17900  // points (are generated only when handling first
17901  // point to get virtual segment between last and
17902  // first point right)
17903  while (it != mData->constEnd()) {
17904  currentRegion = getRegion(it.value().key, it.value().value, rectLeft,
17905  rectTop, rectRight, rectBottom);
17906  if (currentRegion !=
17907  prevRegion) // changed region, possibly need to add some optimized
17908  // edge points or original points if entering R
17909  {
17910  if (currentRegion !=
17911  5) // segment doesn't end in R, so it's a candidate for removal
17912  {
17913  QPointF crossA, crossB;
17914  if (prevRegion ==
17915  5) // we're coming from R, so add this point optimized
17916  {
17917  lineData->append(getOptimizedPoint(
17918  currentRegion, it.value().key, it.value().value,
17919  prevIt.value().key, prevIt.value().value, rectLeft,
17920  rectTop, rectRight, rectBottom));
17921  // in the situations 5->1/7/9/3 the segment may leave R and
17922  // directly cross through two outer regions. In these cases
17923  // we need to add an additional corner point
17924  *lineData << getOptimizedCornerPoints(
17925  prevRegion, currentRegion, prevIt.value().key,
17926  prevIt.value().value, it.value().key,
17927  it.value().value, rectLeft, rectTop, rectRight,
17928  rectBottom);
17929  } else if (mayTraverse(prevRegion, currentRegion) &&
17930  getTraverse(prevIt.value().key, prevIt.value().value,
17931  it.value().key, it.value().value,
17932  rectLeft, rectTop, rectRight, rectBottom,
17933  crossA, crossB)) {
17934  // add the two cross points optimized if segment crosses R
17935  // and if segment isn't virtual zeroth segment between last
17936  // and first curve point:
17937  QVector<QPointF> beforeTraverseCornerPoints,
17938  afterTraverseCornerPoints;
17939  getTraverseCornerPoints(prevRegion, currentRegion, rectLeft,
17940  rectTop, rectRight, rectBottom,
17941  beforeTraverseCornerPoints,
17942  afterTraverseCornerPoints);
17943  if (it != mData->constBegin()) {
17944  *lineData << beforeTraverseCornerPoints;
17945  lineData->append(crossA);
17946  lineData->append(crossB);
17947  *lineData << afterTraverseCornerPoints;
17948  } else {
17949  lineData->append(crossB);
17950  *lineData << afterTraverseCornerPoints;
17951  trailingPoints << beforeTraverseCornerPoints << crossA;
17952  }
17953  } else // doesn't cross R, line is just moving around in
17954  // outside regions, so only need to add optimized
17955  // point(s) at the boundary corner(s)
17956  {
17957  *lineData << getOptimizedCornerPoints(
17958  prevRegion, currentRegion, prevIt.value().key,
17959  prevIt.value().value, it.value().key,
17960  it.value().value, rectLeft, rectTop, rectRight,
17961  rectBottom);
17962  }
17963  } else // segment does end in R, so we add previous point optimized
17964  // and this point at original position
17965  {
17966  if (it ==
17967  mData->constBegin()) // it is first point in curve and
17968  // prevIt is last one. So save
17969  // optimized point for adding it to
17970  // the lineData in the end
17971  trailingPoints << getOptimizedPoint(
17972  prevRegion, prevIt.value().key,
17973  prevIt.value().value, it.value().key,
17974  it.value().value, rectLeft, rectTop, rectRight,
17975  rectBottom);
17976  else
17977  lineData->append(getOptimizedPoint(
17978  prevRegion, prevIt.value().key,
17979  prevIt.value().value, it.value().key,
17980  it.value().value, rectLeft, rectTop, rectRight,
17981  rectBottom));
17982  lineData->append(
17983  coordsToPixels(it.value().key, it.value().value));
17984  }
17985  } else // region didn't change
17986  {
17987  if (currentRegion == 5) // still in R, keep adding original points
17988  {
17989  lineData->append(
17990  coordsToPixels(it.value().key, it.value().value));
17991  } else // still outside R, no need to add anything
17992  {
17993  // see how this is not doing anything? That's the main
17994  // optimization...
17995  }
17996  }
17997  prevIt = it;
17998  prevRegion = currentRegion;
17999  ++it;
18000  }
18001  *lineData << trailingPoints;
18002 }
18003 
18024 int QCPCurve::getRegion(double x,
18025  double y,
18026  double rectLeft,
18027  double rectTop,
18028  double rectRight,
18029  double rectBottom) const {
18030  if (x < rectLeft) // region 123
18031  {
18032  if (y > rectTop)
18033  return 1;
18034  else if (y < rectBottom)
18035  return 3;
18036  else
18037  return 2;
18038  } else if (x > rectRight) // region 789
18039  {
18040  if (y > rectTop)
18041  return 7;
18042  else if (y < rectBottom)
18043  return 9;
18044  else
18045  return 8;
18046  } else // region 456
18047  {
18048  if (y > rectTop)
18049  return 4;
18050  else if (y < rectBottom)
18051  return 6;
18052  else
18053  return 5;
18054  }
18055 }
18056 
18074 QPointF QCPCurve::getOptimizedPoint(int otherRegion,
18075  double otherKey,
18076  double otherValue,
18077  double key,
18078  double value,
18079  double rectLeft,
18080  double rectTop,
18081  double rectRight,
18082  double rectBottom) const {
18083  double intersectKey = rectLeft; // initial value is just fail-safe
18084  double intersectValue = rectTop; // initial value is just fail-safe
18085  switch (otherRegion) {
18086  case 1: // top and left edge
18087  {
18088  intersectValue = rectTop;
18089  intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
18090  (intersectValue - otherValue);
18091  if (intersectKey < rectLeft ||
18092  intersectKey > rectRight) // doesn't intersect, so must
18093  // intersect other:
18094  {
18095  intersectKey = rectLeft;
18096  intersectValue = otherValue + (value - otherValue) /
18097  (key - otherKey) *
18098  (intersectKey - otherKey);
18099  }
18100  break;
18101  }
18102  case 2: // left edge
18103  {
18104  intersectKey = rectLeft;
18105  intersectValue = otherValue + (value - otherValue) /
18106  (key - otherKey) *
18107  (intersectKey - otherKey);
18108  break;
18109  }
18110  case 3: // bottom and left edge
18111  {
18112  intersectValue = rectBottom;
18113  intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
18114  (intersectValue - otherValue);
18115  if (intersectKey < rectLeft ||
18116  intersectKey > rectRight) // doesn't intersect, so must
18117  // intersect other:
18118  {
18119  intersectKey = rectLeft;
18120  intersectValue = otherValue + (value - otherValue) /
18121  (key - otherKey) *
18122  (intersectKey - otherKey);
18123  }
18124  break;
18125  }
18126  case 4: // top edge
18127  {
18128  intersectValue = rectTop;
18129  intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
18130  (intersectValue - otherValue);
18131  break;
18132  }
18133  case 5: {
18134  break; // case 5 shouldn't happen for this function but we add it
18135  // anyway to prevent potential discontinuity in branch table
18136  }
18137  case 6: // bottom edge
18138  {
18139  intersectValue = rectBottom;
18140  intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
18141  (intersectValue - otherValue);
18142  break;
18143  }
18144  case 7: // top and right edge
18145  {
18146  intersectValue = rectTop;
18147  intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
18148  (intersectValue - otherValue);
18149  if (intersectKey < rectLeft ||
18150  intersectKey > rectRight) // doesn't intersect, so must
18151  // intersect other:
18152  {
18153  intersectKey = rectRight;
18154  intersectValue = otherValue + (value - otherValue) /
18155  (key - otherKey) *
18156  (intersectKey - otherKey);
18157  }
18158  break;
18159  }
18160  case 8: // right edge
18161  {
18162  intersectKey = rectRight;
18163  intersectValue = otherValue + (value - otherValue) /
18164  (key - otherKey) *
18165  (intersectKey - otherKey);
18166  break;
18167  }
18168  case 9: // bottom and right edge
18169  {
18170  intersectValue = rectBottom;
18171  intersectKey = otherKey + (key - otherKey) / (value - otherValue) *
18172  (intersectValue - otherValue);
18173  if (intersectKey < rectLeft ||
18174  intersectKey > rectRight) // doesn't intersect, so must
18175  // intersect other:
18176  {
18177  intersectKey = rectRight;
18178  intersectValue = otherValue + (value - otherValue) /
18179  (key - otherKey) *
18180  (intersectKey - otherKey);
18181  }
18182  break;
18183  }
18184  }
18185  return coordsToPixels(intersectKey, intersectValue);
18186 }
18187 
18209 QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion,
18210  int currentRegion,
18211  double prevKey,
18212  double prevValue,
18213  double key,
18214  double value,
18215  double rectLeft,
18216  double rectTop,
18217  double rectRight,
18218  double rectBottom) const {
18219  QVector<QPointF> result;
18220  switch (prevRegion) {
18221  case 1: {
18222  switch (currentRegion) {
18223  case 2: {
18224  result << coordsToPixels(rectLeft, rectTop);
18225  break;
18226  }
18227  case 4: {
18228  result << coordsToPixels(rectLeft, rectTop);
18229  break;
18230  }
18231  case 3: {
18232  result << coordsToPixels(rectLeft, rectTop)
18233  << coordsToPixels(rectLeft, rectBottom);
18234  break;
18235  }
18236  case 7: {
18237  result << coordsToPixels(rectLeft, rectTop)
18238  << coordsToPixels(rectRight, rectTop);
18239  break;
18240  }
18241  case 6: {
18242  result << coordsToPixels(rectLeft, rectTop)
18243  << coordsToPixels(rectLeft, rectBottom);
18244  result.append(result.last());
18245  break;
18246  }
18247  case 8: {
18248  result << coordsToPixels(rectLeft, rectTop)
18249  << coordsToPixels(rectRight, rectTop);
18250  result.append(result.last());
18251  break;
18252  }
18253  case 9: { // in this case we need another distinction of cases:
18254  // segment may pass below or above rect, requiring
18255  // either bottom right or top left corner points
18256  if ((value - prevValue) / (key - prevKey) *
18257  (rectLeft - key) +
18258  value <
18259  rectBottom) // segment passes below R
18260  {
18261  result << coordsToPixels(rectLeft, rectTop)
18262  << coordsToPixels(rectLeft, rectBottom);
18263  result.append(result.last());
18264  result << coordsToPixels(rectRight, rectBottom);
18265  } else {
18266  result << coordsToPixels(rectLeft, rectTop)
18267  << coordsToPixels(rectRight, rectTop);
18268  result.append(result.last());
18269  result << coordsToPixels(rectRight, rectBottom);
18270  }
18271  break;
18272  }
18273  }
18274  break;
18275  }
18276  case 2: {
18277  switch (currentRegion) {
18278  case 1: {
18279  result << coordsToPixels(rectLeft, rectTop);
18280  break;
18281  }
18282  case 3: {
18283  result << coordsToPixels(rectLeft, rectBottom);
18284  break;
18285  }
18286  case 4: {
18287  result << coordsToPixels(rectLeft, rectTop);
18288  result.append(result.last());
18289  break;
18290  }
18291  case 6: {
18292  result << coordsToPixels(rectLeft, rectBottom);
18293  result.append(result.last());
18294  break;
18295  }
18296  case 7: {
18297  result << coordsToPixels(rectLeft, rectTop);
18298  result.append(result.last());
18299  result << coordsToPixels(rectRight, rectTop);
18300  break;
18301  }
18302  case 9: {
18303  result << coordsToPixels(rectLeft, rectBottom);
18304  result.append(result.last());
18305  result << coordsToPixels(rectRight, rectBottom);
18306  break;
18307  }
18308  }
18309  break;
18310  }
18311  case 3: {
18312  switch (currentRegion) {
18313  case 2: {
18314  result << coordsToPixels(rectLeft, rectBottom);
18315  break;
18316  }
18317  case 6: {
18318  result << coordsToPixels(rectLeft, rectBottom);
18319  break;
18320  }
18321  case 1: {
18322  result << coordsToPixels(rectLeft, rectBottom)
18323  << coordsToPixels(rectLeft, rectTop);
18324  break;
18325  }
18326  case 9: {
18327  result << coordsToPixels(rectLeft, rectBottom)
18328  << coordsToPixels(rectRight, rectBottom);
18329  break;
18330  }
18331  case 4: {
18332  result << coordsToPixels(rectLeft, rectBottom)
18333  << coordsToPixels(rectLeft, rectTop);
18334  result.append(result.last());
18335  break;
18336  }
18337  case 8: {
18338  result << coordsToPixels(rectLeft, rectBottom)
18339  << coordsToPixels(rectRight, rectBottom);
18340  result.append(result.last());
18341  break;
18342  }
18343  case 7: { // in this case we need another distinction of cases:
18344  // segment may pass below or above rect, requiring
18345  // either bottom right or top left corner points
18346  if ((value - prevValue) / (key - prevKey) *
18347  (rectRight - key) +
18348  value <
18349  rectBottom) // segment passes below R
18350  {
18351  result << coordsToPixels(rectLeft, rectBottom)
18352  << coordsToPixels(rectRight, rectBottom);
18353  result.append(result.last());
18354  result << coordsToPixels(rectRight, rectTop);
18355  } else {
18356  result << coordsToPixels(rectLeft, rectBottom)
18357  << coordsToPixels(rectLeft, rectTop);
18358  result.append(result.last());
18359  result << coordsToPixels(rectRight, rectTop);
18360  }
18361  break;
18362  }
18363  }
18364  break;
18365  }
18366  case 4: {
18367  switch (currentRegion) {
18368  case 1: {
18369  result << coordsToPixels(rectLeft, rectTop);
18370  break;
18371  }
18372  case 7: {
18373  result << coordsToPixels(rectRight, rectTop);
18374  break;
18375  }
18376  case 2: {
18377  result << coordsToPixels(rectLeft, rectTop);
18378  result.append(result.last());
18379  break;
18380  }
18381  case 8: {
18382  result << coordsToPixels(rectRight, rectTop);
18383  result.append(result.last());
18384  break;
18385  }
18386  case 3: {
18387  result << coordsToPixels(rectLeft, rectTop);
18388  result.append(result.last());
18389  result << coordsToPixels(rectLeft, rectBottom);
18390  break;
18391  }
18392  case 9: {
18393  result << coordsToPixels(rectRight, rectTop);
18394  result.append(result.last());
18395  result << coordsToPixels(rectRight, rectBottom);
18396  break;
18397  }
18398  }
18399  break;
18400  }
18401  case 5: {
18402  switch (currentRegion) {
18403  case 1: {
18404  result << coordsToPixels(rectLeft, rectTop);
18405  break;
18406  }
18407  case 7: {
18408  result << coordsToPixels(rectRight, rectTop);
18409  break;
18410  }
18411  case 9: {
18412  result << coordsToPixels(rectRight, rectBottom);
18413  break;
18414  }
18415  case 3: {
18416  result << coordsToPixels(rectLeft, rectBottom);
18417  break;
18418  }
18419  }
18420  break;
18421  }
18422  case 6: {
18423  switch (currentRegion) {
18424  case 3: {
18425  result << coordsToPixels(rectLeft, rectBottom);
18426  break;
18427  }
18428  case 9: {
18429  result << coordsToPixels(rectRight, rectBottom);
18430  break;
18431  }
18432  case 2: {
18433  result << coordsToPixels(rectLeft, rectBottom);
18434  result.append(result.last());
18435  break;
18436  }
18437  case 8: {
18438  result << coordsToPixels(rectRight, rectBottom);
18439  result.append(result.last());
18440  break;
18441  }
18442  case 1: {
18443  result << coordsToPixels(rectLeft, rectBottom);
18444  result.append(result.last());
18445  result << coordsToPixels(rectLeft, rectTop);
18446  break;
18447  }
18448  case 7: {
18449  result << coordsToPixels(rectRight, rectBottom);
18450  result.append(result.last());
18451  result << coordsToPixels(rectRight, rectTop);
18452  break;
18453  }
18454  }
18455  break;
18456  }
18457  case 7: {
18458  switch (currentRegion) {
18459  case 4: {
18460  result << coordsToPixels(rectRight, rectTop);
18461  break;
18462  }
18463  case 8: {
18464  result << coordsToPixels(rectRight, rectTop);
18465  break;
18466  }
18467  case 1: {
18468  result << coordsToPixels(rectRight, rectTop)
18469  << coordsToPixels(rectLeft, rectTop);
18470  break;
18471  }
18472  case 9: {
18473  result << coordsToPixels(rectRight, rectTop)
18474  << coordsToPixels(rectRight, rectBottom);
18475  break;
18476  }
18477  case 2: {
18478  result << coordsToPixels(rectRight, rectTop)
18479  << coordsToPixels(rectLeft, rectTop);
18480  result.append(result.last());
18481  break;
18482  }
18483  case 6: {
18484  result << coordsToPixels(rectRight, rectTop)
18485  << coordsToPixels(rectRight, rectBottom);
18486  result.append(result.last());
18487  break;
18488  }
18489  case 3: { // in this case we need another distinction of cases:
18490  // segment may pass below or above rect, requiring
18491  // either bottom right or top left corner points
18492  if ((value - prevValue) / (key - prevKey) *
18493  (rectRight - key) +
18494  value <
18495  rectBottom) // segment passes below R
18496  {
18497  result << coordsToPixels(rectRight, rectTop)
18498  << coordsToPixels(rectRight, rectBottom);
18499  result.append(result.last());
18500  result << coordsToPixels(rectLeft, rectBottom);
18501  } else {
18502  result << coordsToPixels(rectRight, rectTop)
18503  << coordsToPixels(rectLeft, rectTop);
18504  result.append(result.last());
18505  result << coordsToPixels(rectLeft, rectBottom);
18506  }
18507  break;
18508  }
18509  }
18510  break;
18511  }
18512  case 8: {
18513  switch (currentRegion) {
18514  case 7: {
18515  result << coordsToPixels(rectRight, rectTop);
18516  break;
18517  }
18518  case 9: {
18519  result << coordsToPixels(rectRight, rectBottom);
18520  break;
18521  }
18522  case 4: {
18523  result << coordsToPixels(rectRight, rectTop);
18524  result.append(result.last());
18525  break;
18526  }
18527  case 6: {
18528  result << coordsToPixels(rectRight, rectBottom);
18529  result.append(result.last());
18530  break;
18531  }
18532  case 1: {
18533  result << coordsToPixels(rectRight, rectTop);
18534  result.append(result.last());
18535  result << coordsToPixels(rectLeft, rectTop);
18536  break;
18537  }
18538  case 3: {
18539  result << coordsToPixels(rectRight, rectBottom);
18540  result.append(result.last());
18541  result << coordsToPixels(rectLeft, rectBottom);
18542  break;
18543  }
18544  }
18545  break;
18546  }
18547  case 9: {
18548  switch (currentRegion) {
18549  case 6: {
18550  result << coordsToPixels(rectRight, rectBottom);
18551  break;
18552  }
18553  case 8: {
18554  result << coordsToPixels(rectRight, rectBottom);
18555  break;
18556  }
18557  case 3: {
18558  result << coordsToPixels(rectRight, rectBottom)
18559  << coordsToPixels(rectLeft, rectBottom);
18560  break;
18561  }
18562  case 7: {
18563  result << coordsToPixels(rectRight, rectBottom)
18564  << coordsToPixels(rectRight, rectTop);
18565  break;
18566  }
18567  case 2: {
18568  result << coordsToPixels(rectRight, rectBottom)
18569  << coordsToPixels(rectLeft, rectBottom);
18570  result.append(result.last());
18571  break;
18572  }
18573  case 4: {
18574  result << coordsToPixels(rectRight, rectBottom)
18575  << coordsToPixels(rectRight, rectTop);
18576  result.append(result.last());
18577  break;
18578  }
18579  case 1: { // in this case we need another distinction of cases:
18580  // segment may pass below or above rect, requiring
18581  // either bottom right or top left corner points
18582  if ((value - prevValue) / (key - prevKey) *
18583  (rectLeft - key) +
18584  value <
18585  rectBottom) // segment passes below R
18586  {
18587  result << coordsToPixels(rectRight, rectBottom)
18588  << coordsToPixels(rectLeft, rectBottom);
18589  result.append(result.last());
18590  result << coordsToPixels(rectLeft, rectTop);
18591  } else {
18592  result << coordsToPixels(rectRight, rectBottom)
18593  << coordsToPixels(rectRight, rectTop);
18594  result.append(result.last());
18595  result << coordsToPixels(rectLeft, rectTop);
18596  }
18597  break;
18598  }
18599  }
18600  break;
18601  }
18602  }
18603  return result;
18604 }
18605 
18619 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const {
18620  switch (prevRegion) {
18621  case 1: {
18622  switch (currentRegion) {
18623  case 4:
18624  case 7:
18625  case 2:
18626  case 3:
18627  return false;
18628  default:
18629  return true;
18630  }
18631  }
18632  case 2: {
18633  switch (currentRegion) {
18634  case 1:
18635  case 3:
18636  return false;
18637  default:
18638  return true;
18639  }
18640  }
18641  case 3: {
18642  switch (currentRegion) {
18643  case 1:
18644  case 2:
18645  case 6:
18646  case 9:
18647  return false;
18648  default:
18649  return true;
18650  }
18651  }
18652  case 4: {
18653  switch (currentRegion) {
18654  case 1:
18655  case 7:
18656  return false;
18657  default:
18658  return true;
18659  }
18660  }
18661  case 5:
18662  return false; // should never occur
18663  case 6: {
18664  switch (currentRegion) {
18665  case 3:
18666  case 9:
18667  return false;
18668  default:
18669  return true;
18670  }
18671  }
18672  case 7: {
18673  switch (currentRegion) {
18674  case 1:
18675  case 4:
18676  case 8:
18677  case 9:
18678  return false;
18679  default:
18680  return true;
18681  }
18682  }
18683  case 8: {
18684  switch (currentRegion) {
18685  case 7:
18686  case 9:
18687  return false;
18688  default:
18689  return true;
18690  }
18691  }
18692  case 9: {
18693  switch (currentRegion) {
18694  case 3:
18695  case 6:
18696  case 8:
18697  case 7:
18698  return false;
18699  default:
18700  return true;
18701  }
18702  }
18703  default:
18704  return true;
18705  }
18706 }
18707 
18724 bool QCPCurve::getTraverse(double prevKey,
18725  double prevValue,
18726  double key,
18727  double value,
18728  double rectLeft,
18729  double rectTop,
18730  double rectRight,
18731  double rectBottom,
18732  QPointF &crossA,
18733  QPointF &crossB) const {
18734  QList<QPointF>
18735  intersections; // x of QPointF corresponds to key and y to value
18736  if (qFuzzyIsNull(key - prevKey)) // line is parallel to value axis
18737  {
18738  // due to region filter in mayTraverseR(), if line is parallel to value
18739  // or key axis, R is traversed here
18740  intersections.append(
18741  QPointF(key, rectBottom)); // direction will be taken care of
18742  // at end of method
18743  intersections.append(QPointF(key, rectTop));
18744  } else if (qFuzzyIsNull(value - prevValue)) // line is parallel to key axis
18745  {
18746  // due to region filter in mayTraverseR(), if line is parallel to value
18747  // or key axis, R is traversed here
18748  intersections.append(QPointF(
18749  rectLeft,
18750  value)); // direction will be taken care of at end of method
18751  intersections.append(QPointF(rectRight, value));
18752  } else // line is skewed
18753  {
18754  double gamma;
18755  double keyPerValue = (key - prevKey) / (value - prevValue);
18756  // check top of rect:
18757  gamma = prevKey + (rectTop - prevValue) * keyPerValue;
18758  if (gamma >= rectLeft && gamma <= rectRight)
18759  intersections.append(QPointF(gamma, rectTop));
18760  // check bottom of rect:
18761  gamma = prevKey + (rectBottom - prevValue) * keyPerValue;
18762  if (gamma >= rectLeft && gamma <= rectRight)
18763  intersections.append(QPointF(gamma, rectBottom));
18764  double valuePerKey = 1.0 / keyPerValue;
18765  // check left of rect:
18766  gamma = prevValue + (rectLeft - prevKey) * valuePerKey;
18767  if (gamma >= rectBottom && gamma <= rectTop)
18768  intersections.append(QPointF(rectLeft, gamma));
18769  // check right of rect:
18770  gamma = prevValue + (rectRight - prevKey) * valuePerKey;
18771  if (gamma >= rectBottom && gamma <= rectTop)
18772  intersections.append(QPointF(rectRight, gamma));
18773  }
18774 
18775  // handle cases where found points isn't exactly 2:
18776  if (intersections.size() > 2) {
18777  // line probably goes through corner of rect, and we got duplicate
18778  // points there. single out the point pair with greatest distance in
18779  // between:
18780  double distSqrMax = 0;
18781  QPointF pv1, pv2;
18782  for (int i = 0; i < intersections.size() - 1; ++i) {
18783  for (int k = i + 1; k < intersections.size(); ++k) {
18784  QPointF distPoint = intersections.at(i) - intersections.at(k);
18785  double distSqr = distPoint.x() * distPoint.x() + distPoint.y() +
18786  distPoint.y();
18787  if (distSqr > distSqrMax) {
18788  pv1 = intersections.at(i);
18789  pv2 = intersections.at(k);
18790  distSqrMax = distSqr;
18791  }
18792  }
18793  }
18794  intersections = QList<QPointF>() << pv1 << pv2;
18795  } else if (intersections.size() != 2) {
18796  // one or even zero points found (shouldn't happen unless line perfectly
18797  // tangent to corner), no need to draw segment
18798  return false;
18799  }
18800 
18801  // possibly re-sort points so optimized point segment has same direction as
18802  // original segment:
18803  if ((key - prevKey) * (intersections.at(1).x() - intersections.at(0).x()) +
18804  (value - prevValue) *
18805  (intersections.at(1).y() - intersections.at(0).y()) <
18806  0) // scalar product of both segments < 0 -> opposite direction
18807  intersections.move(0, 1);
18808  crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
18809  crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
18810  return true;
18811 }
18812 
18842 void QCPCurve::getTraverseCornerPoints(int prevRegion,
18843  int currentRegion,
18844  double rectLeft,
18845  double rectTop,
18846  double rectRight,
18847  double rectBottom,
18848  QVector<QPointF> &beforeTraverse,
18849  QVector<QPointF> &afterTraverse) const {
18850  switch (prevRegion) {
18851  case 1: {
18852  switch (currentRegion) {
18853  case 6: {
18854  beforeTraverse << coordsToPixels(rectLeft, rectTop);
18855  break;
18856  }
18857  case 9: {
18858  beforeTraverse << coordsToPixels(rectLeft, rectTop);
18859  afterTraverse << coordsToPixels(rectRight, rectBottom);
18860  break;
18861  }
18862  case 8: {
18863  beforeTraverse << coordsToPixels(rectLeft, rectTop);
18864  break;
18865  }
18866  }
18867  break;
18868  }
18869  case 2: {
18870  switch (currentRegion) {
18871  case 7: {
18872  afterTraverse << coordsToPixels(rectRight, rectTop);
18873  break;
18874  }
18875  case 9: {
18876  afterTraverse << coordsToPixels(rectRight, rectBottom);
18877  break;
18878  }
18879  }
18880  break;
18881  }
18882  case 3: {
18883  switch (currentRegion) {
18884  case 4: {
18885  beforeTraverse << coordsToPixels(rectLeft, rectBottom);
18886  break;
18887  }
18888  case 7: {
18889  beforeTraverse << coordsToPixels(rectLeft, rectBottom);
18890  afterTraverse << coordsToPixels(rectRight, rectTop);
18891  break;
18892  }
18893  case 8: {
18894  beforeTraverse << coordsToPixels(rectLeft, rectBottom);
18895  break;
18896  }
18897  }
18898  break;
18899  }
18900  case 4: {
18901  switch (currentRegion) {
18902  case 3: {
18903  afterTraverse << coordsToPixels(rectLeft, rectBottom);
18904  break;
18905  }
18906  case 9: {
18907  afterTraverse << coordsToPixels(rectRight, rectBottom);
18908  break;
18909  }
18910  }
18911  break;
18912  }
18913  case 5: {
18914  break;
18915  } // shouldn't happen because this method only handles full traverses
18916  case 6: {
18917  switch (currentRegion) {
18918  case 1: {
18919  afterTraverse << coordsToPixels(rectLeft, rectTop);
18920  break;
18921  }
18922  case 7: {
18923  afterTraverse << coordsToPixels(rectRight, rectTop);
18924  break;
18925  }
18926  }
18927  break;
18928  }
18929  case 7: {
18930  switch (currentRegion) {
18931  case 2: {
18932  beforeTraverse << coordsToPixels(rectRight, rectTop);
18933  break;
18934  }
18935  case 3: {
18936  beforeTraverse << coordsToPixels(rectRight, rectTop);
18937  afterTraverse << coordsToPixels(rectLeft, rectBottom);
18938  break;
18939  }
18940  case 6: {
18941  beforeTraverse << coordsToPixels(rectRight, rectTop);
18942  break;
18943  }
18944  }
18945  break;
18946  }
18947  case 8: {
18948  switch (currentRegion) {
18949  case 1: {
18950  afterTraverse << coordsToPixels(rectLeft, rectTop);
18951  break;
18952  }
18953  case 3: {
18954  afterTraverse << coordsToPixels(rectLeft, rectBottom);
18955  break;
18956  }
18957  }
18958  break;
18959  }
18960  case 9: {
18961  switch (currentRegion) {
18962  case 2: {
18963  beforeTraverse << coordsToPixels(rectRight, rectBottom);
18964  break;
18965  }
18966  case 1: {
18967  beforeTraverse << coordsToPixels(rectRight, rectBottom);
18968  afterTraverse << coordsToPixels(rectLeft, rectTop);
18969  break;
18970  }
18971  case 4: {
18972  beforeTraverse << coordsToPixels(rectRight, rectBottom);
18973  break;
18974  }
18975  }
18976  break;
18977  }
18978  }
18979 }
18980 
18987 double QCPCurve::pointDistance(const QPointF &pixelPoint) const {
18988  if (mData->isEmpty()) {
18989  qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName
18990  << "without data";
18991  return 500;
18992  }
18993  if (mData->size() == 1) {
18994  QPointF dataPoint = coordsToPixels(mData->constBegin().key(),
18995  mData->constBegin().value().value);
18996  return QVector2D(dataPoint - pixelPoint).length();
18997  }
18998 
18999  // calculate minimum distance to line segments:
19000  QVector<QPointF> *lineData = new QVector<QPointF>;
19001  getCurveData(lineData);
19002  double minDistSqr = std::numeric_limits<double>::max();
19003  for (int i = 0; i < lineData->size() - 1; ++i) {
19004  double currentDistSqr =
19005  distSqrToLine(lineData->at(i), lineData->at(i + 1), pixelPoint);
19006  if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
19007  }
19008  delete lineData;
19009  return qSqrt(minDistSqr);
19010 }
19011 
19012 /* inherits documentation from base class */
19014  SignDomain inSignDomain) const {
19015  QCPRange range;
19016  bool haveLower = false;
19017  bool haveUpper = false;
19018 
19019  double current;
19020 
19021  QCPCurveDataMap::const_iterator it = mData->constBegin();
19022  while (it != mData->constEnd()) {
19023  current = it.value().key;
19024  if (!qIsNaN(current) && !qIsNaN(it.value().value)) {
19025  if (inSignDomain == sdBoth ||
19026  (inSignDomain == sdNegative && current < 0) ||
19027  (inSignDomain == sdPositive && current > 0)) {
19028  if (current < range.lower || !haveLower) {
19029  range.lower = current;
19030  haveLower = true;
19031  }
19032  if (current > range.upper || !haveUpper) {
19033  range.upper = current;
19034  haveUpper = true;
19035  }
19036  }
19037  }
19038  ++it;
19039  }
19040 
19041  foundRange = haveLower && haveUpper;
19042  return range;
19043 }
19044 
19045 /* inherits documentation from base class */
19047  SignDomain inSignDomain) const {
19048  QCPRange range;
19049  bool haveLower = false;
19050  bool haveUpper = false;
19051 
19052  double current;
19053 
19054  QCPCurveDataMap::const_iterator it = mData->constBegin();
19055  while (it != mData->constEnd()) {
19056  current = it.value().value;
19057  if (!qIsNaN(current) && !qIsNaN(it.value().key)) {
19058  if (inSignDomain == sdBoth ||
19059  (inSignDomain == sdNegative && current < 0) ||
19060  (inSignDomain == sdPositive && current > 0)) {
19061  if (current < range.lower || !haveLower) {
19062  range.lower = current;
19063  haveLower = true;
19064  }
19065  if (current > range.upper || !haveUpper) {
19066  range.upper = current;
19067  haveUpper = true;
19068  }
19069  }
19070  }
19071  ++it;
19072  }
19073 
19074  foundRange = haveLower && haveUpper;
19075  return range;
19076 }
19077 
19081 
19118 /* start of documentation of inline functions */
19119 
19146 /* end of documentation of inline functions */
19147 
19152  : QObject(parentPlot),
19153  mParentPlot(parentPlot),
19154  mSpacingType(stAbsolute),
19155  mSpacing(4) {}
19156 
19158 
19167 void QCPBarsGroup::setSpacingType(SpacingType spacingType) {
19169 }
19170 
19178 void QCPBarsGroup::setSpacing(double spacing) { mSpacing = spacing; }
19179 
19186 QCPBars *QCPBarsGroup::bars(int index) const {
19187  if (index >= 0 && index < mBars.size()) {
19188  return mBars.at(index);
19189  } else {
19190  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
19191  return 0;
19192  }
19193 }
19194 
19200 void QCPBarsGroup::clear() {
19201  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars
19202  // in the loop is okay
19203  bars->setBarsGroup(0); // removes itself via removeBars
19204 }
19205 
19212 void QCPBarsGroup::append(QCPBars *bars) {
19213  if (!bars) {
19214  qDebug() << Q_FUNC_INFO << "bars is 0";
19215  return;
19216  }
19217 
19218  if (!mBars.contains(bars))
19219  bars->setBarsGroup(this);
19220  else
19221  qDebug() << Q_FUNC_INFO
19222  << "bars plottable is already in this bars group:"
19223  << reinterpret_cast<quintptr>(bars);
19224 }
19225 
19235 void QCPBarsGroup::insert(int i, QCPBars *bars) {
19236  if (!bars) {
19237  qDebug() << Q_FUNC_INFO << "bars is 0";
19238  return;
19239  }
19240 
19241  // first append to bars list normally:
19242  if (!mBars.contains(bars)) bars->setBarsGroup(this);
19243  // then move to according position:
19244  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size() - 1));
19245 }
19246 
19252 void QCPBarsGroup::remove(QCPBars *bars) {
19253  if (!bars) {
19254  qDebug() << Q_FUNC_INFO << "bars is 0";
19255  return;
19256  }
19257 
19258  if (mBars.contains(bars))
19259  bars->setBarsGroup(0);
19260  else
19261  qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:"
19262  << reinterpret_cast<quintptr>(bars);
19263 }
19264 
19272 void QCPBarsGroup::registerBars(QCPBars *bars) {
19273  if (!mBars.contains(bars)) mBars.append(bars);
19274 }
19275 
19283 void QCPBarsGroup::unregisterBars(QCPBars *bars) { mBars.removeOne(bars); }
19284 
19291 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord) {
19292  // find list of all base bars in case some mBars are stacked:
19293  QList<const QCPBars *> baseBars;
19294  foreach (const QCPBars *b, mBars) {
19295  while (b->barBelow()) b = b->barBelow();
19296  if (!baseBars.contains(b)) baseBars.append(b);
19297  }
19298  // find base bar this "bars" is stacked on:
19299  const QCPBars *thisBase = bars;
19300  while (thisBase->barBelow()) thisBase = thisBase->barBelow();
19301 
19302  // determine key pixel offset of this base bars considering all other base
19303  // bars in this barsgroup:
19304  double result = 0;
19305  int index = baseBars.indexOf(thisBase);
19306  if (index >= 0) {
19307  int startIndex;
19308  double lowerPixelWidth, upperPixelWidth;
19309  if (baseBars.size() % 2 == 1 &&
19310  index == (baseBars.size() - 1) /
19311  2) // is center bar (int division on purpose)
19312  {
19313  return result;
19314  } else if (index <
19315  (baseBars.size() - 1) / 2.0) // bar is to the left of center
19316  {
19317  if (baseBars.size() % 2 == 0) // even number of bars
19318  {
19319  startIndex = baseBars.size() / 2 - 1;
19320  result -= getPixelSpacing(baseBars.at(startIndex), keyCoord) *
19321  0.5; // half of middle spacing
19322  } else // uneven number of bars
19323  {
19324  startIndex = (baseBars.size() - 1) / 2 - 1;
19325  baseBars.at((baseBars.size() - 1) / 2)
19326  ->getPixelWidth(keyCoord, lowerPixelWidth,
19327  upperPixelWidth);
19328  result -= qAbs(upperPixelWidth - lowerPixelWidth) *
19329  0.5; // half of center bar
19330  result -=
19331  getPixelSpacing(baseBars.at((baseBars.size() - 1) / 2),
19332  keyCoord); // center bar spacing
19333  }
19334  for (int i = startIndex; i > index;
19335  --i) // add widths and spacings of bars in between center and
19336  // our bars
19337  {
19338  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth,
19339  upperPixelWidth);
19340  result -= qAbs(upperPixelWidth - lowerPixelWidth);
19341  result -= getPixelSpacing(baseBars.at(i), keyCoord);
19342  }
19343  // finally half of our bars width:
19344  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth,
19345  upperPixelWidth);
19346  result -= qAbs(upperPixelWidth - lowerPixelWidth) * 0.5;
19347  } else // bar is to the right of center
19348  {
19349  if (baseBars.size() % 2 == 0) // even number of bars
19350  {
19351  startIndex = baseBars.size() / 2;
19352  result += getPixelSpacing(baseBars.at(startIndex), keyCoord) *
19353  0.5; // half of middle spacing
19354  } else // uneven number of bars
19355  {
19356  startIndex = (baseBars.size() - 1) / 2 + 1;
19357  baseBars.at((baseBars.size() - 1) / 2)
19358  ->getPixelWidth(keyCoord, lowerPixelWidth,
19359  upperPixelWidth);
19360  result += qAbs(upperPixelWidth - lowerPixelWidth) *
19361  0.5; // half of center bar
19362  result +=
19363  getPixelSpacing(baseBars.at((baseBars.size() - 1) / 2),
19364  keyCoord); // center bar spacing
19365  }
19366  for (int i = startIndex; i < index;
19367  ++i) // add widths and spacings of bars in between center and
19368  // our bars
19369  {
19370  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth,
19371  upperPixelWidth);
19372  result += qAbs(upperPixelWidth - lowerPixelWidth);
19373  result += getPixelSpacing(baseBars.at(i), keyCoord);
19374  }
19375  // finally half of our bars width:
19376  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth,
19377  upperPixelWidth);
19378  result += qAbs(upperPixelWidth - lowerPixelWidth) * 0.5;
19379  }
19380  }
19381  return result;
19382 }
19383 
19395 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord) {
19396  switch (mSpacingType) {
19397  case stAbsolute: {
19398  return mSpacing;
19399  }
19400  case stAxisRectRatio: {
19401  if (bars->keyAxis()->orientation() == Qt::Horizontal)
19402  return bars->keyAxis()->axisRect()->width() * mSpacing;
19403  else
19404  return bars->keyAxis()->axisRect()->height() * mSpacing;
19405  }
19406  case stPlotCoords: {
19407  double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
19408  return bars->keyAxis()->coordToPixel(keyCoord + mSpacing) -
19409  keyPixel;
19410  }
19411  }
19412  return 0;
19413 }
19414 
19418 
19434 QCPBarData::QCPBarData() : key(0), value(0) {}
19435 
19439 QCPBarData::QCPBarData(double key, double value) : key(key), value(value) {}
19440 
19444 
19482 /* start of documentation of inline functions */
19483 
19498 /* end of documentation of inline functions */
19499 
19510 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
19511  : QCPAbstractPlottable(keyAxis, valueAxis),
19512  mData(new QCPBarDataMap),
19513  mWidth(0.75),
19514  mWidthType(wtPlotCoords),
19515  mBarsGroup(0),
19516  mBaseValue(0) {
19517  // modify inherited properties from abstract plottable:
19518  mPen.setColor(Qt::blue);
19519  mPen.setStyle(Qt::SolidLine);
19520  mBrush.setColor(QColor(40, 50, 255, 30));
19521  mBrush.setStyle(Qt::SolidPattern);
19522  mSelectedPen = mPen;
19523  mSelectedPen.setWidthF(2.5);
19524  mSelectedPen.setColor(
19525  QColor(80, 80, 255)); // lighter than Qt::blue of mPen
19526  mSelectedBrush = mBrush;
19527 }
19528 
19530  setBarsGroup(0);
19531  if (mBarBelow || mBarAbove)
19532  connectBars(mBarBelow.data(),
19533  mBarAbove.data()); // take this bar out of any stacking
19534  delete mData;
19535 }
19536 
19544 void QCPBars::setWidth(double width) { mWidth = width; }
19545 
19554 void QCPBars::setWidthType(QCPBars::WidthType widthType) {
19556 }
19557 
19564 void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup) {
19565  // deregister at old group:
19566  if (mBarsGroup) mBarsGroup->unregisterBars(this);
19568  // register at new group:
19569  if (mBarsGroup) mBarsGroup->registerBars(this);
19570 }
19571 
19584 void QCPBars::setBaseValue(double baseValue) { mBaseValue = baseValue; }
19585 
19595  if (mData == data) {
19596  qDebug() << Q_FUNC_INFO
19597  << "The data pointer is already in (and owned by) this "
19598  "plottable"
19599  << reinterpret_cast<quintptr>(data);
19600  return;
19601  }
19602  if (copy) {
19603  *mData = *data;
19604  } else {
19605  delete mData;
19606  mData = data;
19607  }
19608 }
19609 
19616 void QCPBars::setData(const QVector<double> &key,
19617  const QVector<double> &value) {
19618  mData->clear();
19619  int n = key.size();
19620  n = qMin(n, value.size());
19621  QCPBarData newData;
19622  for (int i = 0; i < n; ++i) {
19623  newData.key = key[i];
19624  newData.value = value[i];
19625  qtCompatMapInsertMulti(mData, newData.key, newData);
19626  }
19627 }
19628 
19644 void QCPBars::moveBelow(QCPBars *bars) {
19645  if (bars == this) return;
19646  if (bars && (bars->keyAxis() != mKeyAxis.data() ||
19647  bars->valueAxis() != mValueAxis.data())) {
19648  qDebug() << Q_FUNC_INFO
19649  << "passed QCPBars* doesn't have same key and value axis as "
19650  "this QCPBars";
19651  return;
19652  }
19653  // remove from stacking:
19654  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one
19655  // (or both) of them is 0
19656  // if new bar given, insert this bar below it:
19657  if (bars) {
19658  if (bars->mBarBelow) connectBars(bars->mBarBelow.data(), this);
19659  connectBars(this, bars);
19660  }
19661 }
19662 
19678 void QCPBars::moveAbove(QCPBars *bars) {
19679  if (bars == this) return;
19680  if (bars && (bars->keyAxis() != mKeyAxis.data() ||
19681  bars->valueAxis() != mValueAxis.data())) {
19682  qDebug() << Q_FUNC_INFO
19683  << "passed QCPBars* doesn't have same key and value axis as "
19684  "this QCPBars";
19685  return;
19686  }
19687  // remove from stacking:
19688  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one
19689  // (or both) of them is 0
19690  // if new bar given, insert this bar above it:
19691  if (bars) {
19692  if (bars->mBarAbove) connectBars(this, bars->mBarAbove.data());
19693  connectBars(bars, this);
19694  }
19695 }
19696 
19701 void QCPBars::addData(const QCPBarDataMap &dataMap) {
19702  qtCompatMapUnite(mData, dataMap);
19703 }
19704 
19711 }
19712 
19717 void QCPBars::addData(double key, double value) {
19718  QCPBarData newData;
19719  newData.key = key;
19720  newData.value = value;
19721  qtCompatMapInsertMulti(mData, newData.key, newData);
19722 }
19723 
19728 void QCPBars::addData(const QVector<double> &keys,
19729  const QVector<double> &values) {
19730  int n = keys.size();
19731  n = qMin(n, values.size());
19732  QCPBarData newData;
19733  for (int i = 0; i < n; ++i) {
19734  newData.key = keys[i];
19735  newData.value = values[i];
19736  qtCompatMapInsertMulti(mData, newData.key, newData);
19737  }
19738 }
19739 
19744 void QCPBars::removeDataBefore(double key) {
19745  QCPBarDataMap::iterator it = mData->begin();
19746  while (it != mData->end() && it.key() < key) it = mData->erase(it);
19747 }
19748 
19753 void QCPBars::removeDataAfter(double key) {
19754  if (mData->isEmpty()) return;
19755  QCPBarDataMap::iterator it = mData->upperBound(key);
19756  while (it != mData->end()) it = mData->erase(it);
19757 }
19758 
19766 void QCPBars::removeData(double fromKey, double toKey) {
19767  if (fromKey >= toKey || mData->isEmpty()) return;
19768  QCPBarDataMap::iterator it = mData->upperBound(fromKey);
19769  QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
19770  while (it != itEnd) it = mData->erase(it);
19771 }
19772 
19782 void QCPBars::removeData(double key) { mData->remove(key); }
19783 
19788 void QCPBars::clearData() { mData->clear(); }
19789 
19790 /* inherits documentation from base class */
19791 double QCPBars::selectTest(const QPointF &pos,
19792  bool onlySelectable,
19793  QVariant *details) const {
19794  Q_UNUSED(details)
19795  if (onlySelectable && !mSelectable) return -1;
19796  if (!mKeyAxis || !mValueAxis) {
19797  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
19798  return -1;
19799  }
19800 
19801  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
19802  QCPBarDataMap::ConstIterator it;
19803  for (it = mData->constBegin(); it != mData->constEnd(); ++it) {
19804  if (getBarPolygon(it.value().key, it.value().value)
19805  .boundingRect()
19806  .contains(pos))
19807  return mParentPlot->selectionTolerance() * 0.99;
19808  }
19809  }
19810  return -1;
19811 }
19812 
19813 /* inherits documentation from base class */
19814 void QCPBars::draw(QCPPainter *painter) {
19815  if (!mKeyAxis || !mValueAxis) {
19816  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
19817  return;
19818  }
19819  if (mData->isEmpty()) return;
19820 
19821  QCPBarDataMap::const_iterator it, lower, upperEnd;
19822  getVisibleDataBounds(lower, upperEnd);
19823  for (it = lower; it != upperEnd; ++it) {
19824  // check data validity if flag set:
19825 #ifdef QCUSTOMPLOT_CHECK_DATA
19826  if (QCP::isInvalidData(it.value().key, it.value().value))
19827  qDebug() << Q_FUNC_INFO << "Data point at" << it.key()
19828  << "of drawn range invalid."
19829  << "Plottable name:" << name();
19830 #endif
19831  QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
19832  // draw bar fill:
19833  if (mainBrush().style() != Qt::NoBrush &&
19834  mainBrush().color().alpha() != 0) {
19835  applyFillAntialiasingHint(painter);
19836  painter->setPen(Qt::NoPen);
19837  painter->setBrush(mainBrush());
19838  painter->drawPolygon(barPolygon);
19839  }
19840  // draw bar line:
19841  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) {
19843  painter->setPen(mainPen());
19844  painter->setBrush(Qt::NoBrush);
19845  painter->drawPolyline(barPolygon);
19846  }
19847  }
19848 }
19849 
19850 /* inherits documentation from base class */
19851 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const {
19852  // draw filled rect:
19854  painter->setBrush(mBrush);
19855  painter->setPen(mPen);
19856  QRectF r = QRectF(0, 0, rect.width() * 0.67, rect.height() * 0.67);
19857  r.moveCenter(rect.center());
19858  painter->drawRect(r);
19859 }
19860 
19880  QCPBarDataMap::const_iterator &lower,
19881  QCPBarDataMap::const_iterator &upperEnd) const {
19882  if (!mKeyAxis) {
19883  qDebug() << Q_FUNC_INFO << "invalid key axis";
19884  return;
19885  }
19886  if (mData->isEmpty()) {
19887  lower = mData->constEnd();
19888  upperEnd = mData->constEnd();
19889  return;
19890  }
19891 
19892  // get visible data range as QMap iterators
19893  lower = mData->lowerBound(mKeyAxis.data()->range().lower);
19894  upperEnd = mData->upperBound(mKeyAxis.data()->range().upper);
19895  double lowerPixelBound =
19896  mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
19897  double upperPixelBound =
19898  mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
19899  bool isVisible = false;
19900  // walk left from lbound to find lower bar that actually is completely
19901  // outside visible pixel range:
19902  QCPBarDataMap::const_iterator it = lower;
19903  while (it != mData->constBegin()) {
19904  --it;
19905  QRectF barBounds =
19906  getBarPolygon(it.value().key, it.value().value).boundingRect();
19907  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
19908  isVisible = ((!mKeyAxis.data()->rangeReversed() &&
19909  barBounds.right() >= lowerPixelBound) ||
19910  (mKeyAxis.data()->rangeReversed() &&
19911  barBounds.left() <= lowerPixelBound));
19912  else // keyaxis is vertical
19913  isVisible = ((!mKeyAxis.data()->rangeReversed() &&
19914  barBounds.top() <= lowerPixelBound) ||
19915  (mKeyAxis.data()->rangeReversed() &&
19916  barBounds.bottom() >= lowerPixelBound));
19917  if (isVisible)
19918  lower = it;
19919  else
19920  break;
19921  }
19922  // walk right from ubound to find upper bar that actually is completely
19923  // outside visible pixel range:
19924  it = upperEnd;
19925  while (it != mData->constEnd()) {
19926  QRectF barBounds =
19927  getBarPolygon(upperEnd.value().key, upperEnd.value().value)
19928  .boundingRect();
19929  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
19930  isVisible = ((!mKeyAxis.data()->rangeReversed() &&
19931  barBounds.left() <= upperPixelBound) ||
19932  (mKeyAxis.data()->rangeReversed() &&
19933  barBounds.right() >= upperPixelBound));
19934  else // keyaxis is vertical
19935  isVisible = ((!mKeyAxis.data()->rangeReversed() &&
19936  barBounds.bottom() >= upperPixelBound) ||
19937  (mKeyAxis.data()->rangeReversed() &&
19938  barBounds.top() <= upperPixelBound));
19939  if (isVisible)
19940  upperEnd = it + 1;
19941  else
19942  break;
19943  ++it;
19944  }
19945 }
19946 
19953 QPolygonF QCPBars::getBarPolygon(double key, double value) const {
19954  QCPAxis *keyAxis = mKeyAxis.data();
19955  QCPAxis *valueAxis = mValueAxis.data();
19956  if (!keyAxis || !valueAxis) {
19957  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
19958  return QPolygonF();
19959  }
19960 
19961  QPolygonF result;
19962  double lowerPixelWidth, upperPixelWidth;
19963  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
19964  double base = getStackedBaseValue(key, value >= 0);
19965  double basePixel = valueAxis->coordToPixel(base);
19966  double valuePixel = valueAxis->coordToPixel(base + value);
19967  double keyPixel = keyAxis->coordToPixel(key);
19968  if (mBarsGroup) keyPixel += mBarsGroup->keyPixelOffset(this, key);
19969  if (keyAxis->orientation() == Qt::Horizontal) {
19970  result << QPointF(keyPixel + lowerPixelWidth, basePixel);
19971  result << QPointF(keyPixel + lowerPixelWidth, valuePixel);
19972  result << QPointF(keyPixel + upperPixelWidth, valuePixel);
19973  result << QPointF(keyPixel + upperPixelWidth, basePixel);
19974  } else {
19975  result << QPointF(basePixel, keyPixel + lowerPixelWidth);
19976  result << QPointF(valuePixel, keyPixel + lowerPixelWidth);
19977  result << QPointF(valuePixel, keyPixel + upperPixelWidth);
19978  result << QPointF(basePixel, keyPixel + upperPixelWidth);
19979  }
19980  return result;
19981 }
19982 
19994 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const {
19995  switch (mWidthType) {
19996  case wtAbsolute: {
19997  upper = mWidth * 0.5;
19998  lower = -upper;
19999  if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^
20000  (mKeyAxis.data()->orientation() == Qt::Vertical)))
20001  qSwap(lower, upper);
20002  break;
20003  }
20004  case wtAxisRectRatio: {
20005  if (mKeyAxis && mKeyAxis.data()->axisRect()) {
20006  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
20007  upper = mKeyAxis.data()->axisRect()->width() * mWidth * 0.5;
20008  else
20009  upper = mKeyAxis.data()->axisRect()->height() * mWidth *
20010  0.5;
20011  lower = -upper;
20012  if (mKeyAxis &&
20013  (mKeyAxis.data()->rangeReversed() ^
20014  (mKeyAxis.data()->orientation() == Qt::Vertical)))
20015  qSwap(lower, upper);
20016  } else
20017  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
20018  break;
20019  }
20020  case wtPlotCoords: {
20021  if (mKeyAxis) {
20022  double keyPixel = mKeyAxis.data()->coordToPixel(key);
20023  upper = mKeyAxis.data()->coordToPixel(key + mWidth * 0.5) -
20024  keyPixel;
20025  lower = mKeyAxis.data()->coordToPixel(key - mWidth * 0.5) -
20026  keyPixel;
20027  // no need to qSwap(lower, higher) when range reversed, because
20028  // higher/lower are gained by coordinate transform which
20029  // includes range direction
20030  } else
20031  qDebug() << Q_FUNC_INFO << "No key axis defined";
20032  break;
20033  }
20034  }
20035 }
20036 
20048 double QCPBars::getStackedBaseValue(double key, bool positive) const {
20049  if (mBarBelow) {
20050  double max = 0; // don't use mBaseValue here because only base value of
20051  // bottom-most bar has meaning in a bar stack
20052  // find bars of mBarBelow that are approximately at key and find largest
20053  // one:
20054  double epsilon = qAbs(key) * 1e-6; // should be safe even when changed
20055  // to use float at some point
20056  if (key == 0) epsilon = 1e-6;
20057  QCPBarDataMap::const_iterator it =
20058  mBarBelow.data()->mData->lowerBound(key - epsilon);
20059  QCPBarDataMap::const_iterator itEnd =
20060  mBarBelow.data()->mData->upperBound(key + epsilon);
20061  while (it != itEnd) {
20062  if ((positive && it.value().value > max) ||
20063  (!positive && it.value().value < max))
20064  max = it.value().value;
20065  ++it;
20066  }
20067  // recurse down the bar-stack to find the total height:
20068  return max + mBarBelow.data()->getStackedBaseValue(key, positive);
20069  } else
20070  return mBaseValue;
20071 }
20072 
20082 void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) {
20083  if (!lower && !upper) return;
20084 
20085  if (!lower) // disconnect upper at bottom
20086  {
20087  // disconnect old bar below upper:
20088  if (upper->mBarBelow &&
20089  upper->mBarBelow.data()->mBarAbove.data() == upper)
20090  upper->mBarBelow.data()->mBarAbove = 0;
20091  upper->mBarBelow = 0;
20092  } else if (!upper) // disconnect lower at top
20093  {
20094  // disconnect old bar above lower:
20095  if (lower->mBarAbove &&
20096  lower->mBarAbove.data()->mBarBelow.data() == lower)
20097  lower->mBarAbove.data()->mBarBelow = 0;
20098  lower->mBarAbove = 0;
20099  } else // connect lower and upper
20100  {
20101  // disconnect old bar above lower:
20102  if (lower->mBarAbove &&
20103  lower->mBarAbove.data()->mBarBelow.data() == lower)
20104  lower->mBarAbove.data()->mBarBelow = 0;
20105  // disconnect old bar below upper:
20106  if (upper->mBarBelow &&
20107  upper->mBarBelow.data()->mBarAbove.data() == upper)
20108  upper->mBarBelow.data()->mBarAbove = 0;
20109  lower->mBarAbove = upper;
20110  upper->mBarBelow = lower;
20111  }
20112 }
20113 
20114 /* inherits documentation from base class */
20115 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const {
20116  QCPRange range;
20117  bool haveLower = false;
20118  bool haveUpper = false;
20119 
20120  double current;
20121  QCPBarDataMap::const_iterator it = mData->constBegin();
20122  while (it != mData->constEnd()) {
20123  current = it.value().key;
20124  if (inSignDomain == sdBoth ||
20125  (inSignDomain == sdNegative && current < 0) ||
20126  (inSignDomain == sdPositive && current > 0)) {
20127  if (current < range.lower || !haveLower) {
20128  range.lower = current;
20129  haveLower = true;
20130  }
20131  if (current > range.upper || !haveUpper) {
20132  range.upper = current;
20133  haveUpper = true;
20134  }
20135  }
20136  ++it;
20137  }
20138  // determine exact range of bars by including bar width and barsgroup
20139  // offset:
20140  if (haveLower && mKeyAxis) {
20141  double lowerPixelWidth, upperPixelWidth, keyPixel;
20142  getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
20143  keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
20144  if (mBarsGroup)
20145  keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
20146  range.lower = mKeyAxis.data()->pixelToCoord(keyPixel);
20147  }
20148  if (haveUpper && mKeyAxis) {
20149  double lowerPixelWidth, upperPixelWidth, keyPixel;
20150  getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
20151  keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
20152  if (mBarsGroup)
20153  keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
20154  range.upper = mKeyAxis.data()->pixelToCoord(keyPixel);
20155  }
20156  foundRange = haveLower && haveUpper;
20157  return range;
20158 }
20159 
20160 /* inherits documentation from base class */
20162  SignDomain inSignDomain) const {
20163  QCPRange range;
20164  range.lower = mBaseValue;
20165  range.upper = mBaseValue;
20166  bool haveLower = true; // set to true, because baseValue should always be
20167  // visible in bar charts
20168  bool haveUpper = true; // set to true, because baseValue should always be
20169  // visible in bar charts
20170  double current;
20171 
20172  QCPBarDataMap::const_iterator it = mData->constBegin();
20173  while (it != mData->constEnd()) {
20174  current = it.value().value +
20175  getStackedBaseValue(it.value().key, it.value().value >= 0);
20176  if (inSignDomain == sdBoth ||
20177  (inSignDomain == sdNegative && current < 0) ||
20178  (inSignDomain == sdPositive && current > 0)) {
20179  if (current < range.lower || !haveLower) {
20180  range.lower = current;
20181  haveLower = true;
20182  }
20183  if (current > range.upper || !haveUpper) {
20184  range.upper = current;
20185  haveUpper = true;
20186  }
20187  }
20188  ++it;
20189  }
20190 
20191  foundRange = true; // return true because bar charts always have the 0-line
20192  // visible
20193  return range;
20194 }
20195 
20199 
20254  : QCPAbstractPlottable(keyAxis, valueAxis),
20255  mKey(0),
20256  mMinimum(0),
20257  mLowerQuartile(0),
20258  mMedian(0),
20259  mUpperQuartile(0),
20260  mMaximum(0) {
20261  setOutlierStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, 6));
20262  setWhiskerWidth(0.2);
20263  setWidth(0.5);
20264 
20265  setPen(QPen(Qt::black));
20266  setSelectedPen(QPen(Qt::blue, 2.5));
20267  setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
20268  setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
20269  setWhiskerBarPen(QPen(Qt::black));
20270  setBrush(Qt::NoBrush);
20271  setSelectedBrush(Qt::NoBrush);
20272 }
20273 
20277 void QCPStatisticalBox::setKey(double key) { mKey = key; }
20278 
20286 void QCPStatisticalBox::setMinimum(double value) { mMinimum = value; }
20287 
20297  mLowerQuartile = value;
20298 }
20299 
20307 void QCPStatisticalBox::setMedian(double value) { mMedian = value; }
20308 
20318  mUpperQuartile = value;
20319 }
20320 
20328 void QCPStatisticalBox::setMaximum(double value) { mMaximum = value; }
20329 
20337 void QCPStatisticalBox::setOutliers(const QVector<double> &values) {
20338  mOutliers = values;
20339 }
20340 
20348  double minimum,
20349  double lowerQuartile,
20350  double median,
20351  double upperQuartile,
20352  double maximum) {
20353  setKey(key);
20356  setMedian(median);
20359 }
20360 
20366 void QCPStatisticalBox::setWidth(double width) { mWidth = width; }
20367 
20375 
20386 void QCPStatisticalBox::setWhiskerPen(const QPen &pen) { mWhiskerPen = pen; }
20387 
20394 void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen) {
20395  mWhiskerBarPen = pen;
20396 }
20397 
20402 void QCPStatisticalBox::setMedianPen(const QPen &pen) { mMedianPen = pen; }
20403 
20410  mOutlierStyle = style;
20411 }
20412 
20413 /* inherits documentation from base class */
20415  setOutliers(QVector<double>());
20416  setKey(0);
20417  setMinimum(0);
20418  setLowerQuartile(0);
20419  setMedian(0);
20420  setUpperQuartile(0);
20421  setMaximum(0);
20422 }
20423 
20424 /* inherits documentation from base class */
20425 double QCPStatisticalBox::selectTest(const QPointF &pos,
20426  bool onlySelectable,
20427  QVariant *details) const {
20428  Q_UNUSED(details)
20429  if (onlySelectable && !mSelectable) return -1;
20430  if (!mKeyAxis || !mValueAxis) {
20431  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
20432  return -1;
20433  }
20434 
20435  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
20436  double posKey, posValue;
20437  pixelsToCoords(pos, posKey, posValue);
20438  // quartile box:
20439  QCPRange keyRange(mKey - mWidth * 0.5, mKey + mWidth * 0.5);
20440  QCPRange valueRange(mLowerQuartile, mUpperQuartile);
20441  if (keyRange.contains(posKey) && valueRange.contains(posValue))
20442  return mParentPlot->selectionTolerance() * 0.99;
20443 
20444  // min/max whiskers:
20445  if (QCPRange(mMinimum, mMaximum).contains(posValue))
20446  return qAbs(mKeyAxis.data()->coordToPixel(mKey) -
20447  mKeyAxis.data()->coordToPixel(posKey));
20448  }
20449  return -1;
20450 }
20451 
20452 /* inherits documentation from base class */
20453 void QCPStatisticalBox::draw(QCPPainter *painter) {
20454  if (!mKeyAxis || !mValueAxis) {
20455  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
20456  return;
20457  }
20458 
20459  // check data validity if flag set:
20460 #ifdef QCUSTOMPLOT_CHECK_DATA
20464  qDebug() << Q_FUNC_INFO << "Data point at" << mKey
20465  << "of drawn range has invalid data."
20466  << "Plottable name:" << name();
20467  for (int i = 0; i < mOutliers.size(); ++i)
20468  if (QCP::isInvalidData(mOutliers.at(i)))
20469  qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey
20470  << "of drawn range invalid."
20471  << "Plottable name:" << name();
20472 #endif
20473 
20474  QRectF quartileBox;
20475  drawQuartileBox(painter, &quartileBox);
20476 
20477  painter->save();
20478  painter->setClipRect(quartileBox, Qt::IntersectClip);
20479  drawMedian(painter);
20480  painter->restore();
20481 
20482  drawWhiskers(painter);
20483  drawOutliers(painter);
20484 }
20485 
20486 /* inherits documentation from base class */
20488  const QRectF &rect) const {
20489  // draw filled rect:
20491  painter->setPen(mPen);
20492  painter->setBrush(mBrush);
20493  QRectF r = QRectF(0, 0, rect.width() * 0.67, rect.height() * 0.67);
20494  r.moveCenter(rect.center());
20495  painter->drawRect(r);
20496 }
20497 
20506  QRectF *quartileBox) const {
20507  QRectF box;
20508  box.setTopLeft(coordsToPixels(mKey - mWidth * 0.5, mUpperQuartile));
20509  box.setBottomRight(coordsToPixels(mKey + mWidth * 0.5, mLowerQuartile));
20511  painter->setPen(mainPen());
20512  painter->setBrush(mainBrush());
20513  painter->drawRect(box);
20514  if (quartileBox) *quartileBox = box;
20515 }
20516 
20522  QLineF medianLine;
20523  medianLine.setP1(coordsToPixels(mKey - mWidth * 0.5, mMedian));
20524  medianLine.setP2(coordsToPixels(mKey + mWidth * 0.5, mMedian));
20526  painter->setPen(mMedianPen);
20527  painter->drawLine(medianLine);
20528 }
20529 
20535  QLineF backboneMin, backboneMax, barMin, barMax;
20536  backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile),
20538  backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile),
20540  barMax.setPoints(coordsToPixels(mKey - mWhiskerWidth * 0.5, mMaximum),
20542  barMin.setPoints(coordsToPixels(mKey - mWhiskerWidth * 0.5, mMinimum),
20545  painter->setPen(mWhiskerPen);
20546  painter->drawLine(backboneMin);
20547  painter->drawLine(backboneMax);
20548  painter->setPen(mWhiskerBarPen);
20549  painter->drawLine(barMin);
20550  painter->drawLine(barMax);
20551 }
20552 
20559  mOutlierStyle.applyTo(painter, mPen);
20560  for (int i = 0; i < mOutliers.size(); ++i)
20562 }
20563 
20564 /* inherits documentation from base class */
20566  SignDomain inSignDomain) const {
20567  foundRange = true;
20568  if (inSignDomain == sdBoth) {
20569  return QCPRange(mKey - mWidth * 0.5, mKey + mWidth * 0.5);
20570  } else if (inSignDomain == sdNegative) {
20571  if (mKey + mWidth * 0.5 < 0)
20572  return QCPRange(mKey - mWidth * 0.5, mKey + mWidth * 0.5);
20573  else if (mKey < 0)
20574  return QCPRange(mKey - mWidth * 0.5, mKey);
20575  else {
20576  foundRange = false;
20577  return QCPRange();
20578  }
20579  } else if (inSignDomain == sdPositive) {
20580  if (mKey - mWidth * 0.5 > 0)
20581  return QCPRange(mKey - mWidth * 0.5, mKey + mWidth * 0.5);
20582  else if (mKey > 0)
20583  return QCPRange(mKey, mKey + mWidth * 0.5);
20584  else {
20585  foundRange = false;
20586  return QCPRange();
20587  }
20588  }
20589  foundRange = false;
20590  return QCPRange();
20591 }
20592 
20593 /* inherits documentation from base class */
20595  SignDomain inSignDomain) const {
20596  QVector<double> values; // values that must be considered (i.e. all
20597  // outliers and the five box-parameters)
20598  values.reserve(mOutliers.size() + 5);
20599  values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile
20600  << mMinimum;
20601  values << mOutliers;
20602  // go through values and find the ones in legal range:
20603  bool haveUpper = false;
20604  bool haveLower = false;
20605  double upper = 0;
20606  double lower = 0;
20607  for (int i = 0; i < values.size(); ++i) {
20608  if ((inSignDomain == sdNegative && values.at(i) < 0) ||
20609  (inSignDomain == sdPositive && values.at(i) > 0) ||
20610  (inSignDomain == sdBoth)) {
20611  if (values.at(i) > upper || !haveUpper) {
20612  upper = values.at(i);
20613  haveUpper = true;
20614  }
20615  if (values.at(i) < lower || !haveLower) {
20616  lower = values.at(i);
20617  haveLower = true;
20618  }
20619  }
20620  }
20621  // return the bounds if we found some sensible values:
20622  if (haveLower && haveUpper) {
20623  foundRange = true;
20624  return QCPRange(lower, upper);
20625  } else // might happen if all values are in other sign domain
20626  {
20627  foundRange = false;
20628  return QCPRange();
20629  }
20630 }
20631 
20635 
20667 /* start of documentation of inline functions */
20668 
20675 /* end of documentation of inline functions */
20676 
20686  int valueSize,
20687  const QCPRange &keyRange,
20688  const QCPRange &valueRange)
20689  : mKeySize(0),
20690  mValueSize(0),
20691  mKeyRange(keyRange),
20692  mValueRange(valueRange),
20693  mIsEmpty(true),
20694  mData(0),
20695  mDataModified(true) {
20696  setSize(keySize, valueSize);
20697  fill(0);
20698 }
20699 
20701  if (mData) delete[] mData;
20702 }
20703 
20709  : mKeySize(0),
20710  mValueSize(0),
20711  mIsEmpty(true),
20712  mData(0),
20713  mDataModified(true) {
20714  *this = other;
20715 }
20716 
20721  if (&other != this) {
20722  const int keySize = other.keySize();
20723  const int valueSize = other.valueSize();
20725  setRange(other.keyRange(), other.valueRange());
20726  if (!mIsEmpty)
20727  memcpy(mData, other.mData, sizeof(mData[0]) * keySize * valueSize);
20728  mDataBounds = other.mDataBounds;
20729  mDataModified = true;
20730  }
20731  return *this;
20732 }
20733 
20734 /* undocumented getter */
20735 double QCPColorMapData::data(double key, double value) {
20736  int keyCell = (key - mKeyRange.lower) /
20737  (mKeyRange.upper - mKeyRange.lower) * (mKeySize - 1) +
20738  0.5;
20739  int valueCell = (value - mValueRange.lower) /
20741  (mValueSize - 1) +
20742  0.5;
20743  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 &&
20744  valueCell < mValueSize)
20745  return mData[valueCell * mKeySize + keyCell];
20746  else
20747  return 0;
20748 }
20749 
20750 /* undocumented getter */
20751 double QCPColorMapData::cell(int keyIndex, int valueIndex) {
20752  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 &&
20753  valueIndex < mValueSize)
20754  return mData[valueIndex * mKeySize + keyIndex];
20755  else
20756  return 0;
20757 }
20758 
20771 void QCPColorMapData::setSize(int keySize, int valueSize) {
20772  if (keySize != mKeySize || valueSize != mValueSize) {
20773  mKeySize = keySize;
20775  if (mData) delete[] mData;
20776  mIsEmpty = mKeySize == 0 || mValueSize == 0;
20777  if (!mIsEmpty) {
20778 #ifdef __EXCEPTIONS
20779  try { // 2D arrays get memory intensive fast. So if the allocation
20780  // fails, at least output debug message
20781 #endif
20782  mData = new double[mKeySize * mValueSize];
20783 #ifdef __EXCEPTIONS
20784  } catch (...) {
20785  mData = 0;
20786  }
20787 #endif
20788  if (mData)
20789  fill(0);
20790  else
20791  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "
20792  << mKeySize << "*" << mValueSize;
20793  } else
20794  mData = 0;
20795  mDataModified = true;
20796  }
20797 }
20798 
20810 void QCPColorMapData::setKeySize(int keySize) { setSize(keySize, mValueSize); }
20811 
20823 void QCPColorMapData::setValueSize(int valueSize) {
20825 }
20826 
20838 void QCPColorMapData::setRange(const QCPRange &keyRange,
20839  const QCPRange &valueRange) {
20842 }
20843 
20856 void QCPColorMapData::setKeyRange(const QCPRange &keyRange) {
20857  mKeyRange = keyRange;
20858 }
20859 
20872 void QCPColorMapData::setValueRange(const QCPRange &valueRange) {
20874 }
20875 
20889 void QCPColorMapData::setData(double key, double value, double z) {
20890  int keyCell = (key - mKeyRange.lower) /
20891  (mKeyRange.upper - mKeyRange.lower) * (mKeySize - 1) +
20892  0.5;
20893  int valueCell = (value - mValueRange.lower) /
20895  (mValueSize - 1) +
20896  0.5;
20897  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 &&
20898  valueCell < mValueSize) {
20899  mData[valueCell * mKeySize + keyCell] = z;
20900  if (z < mDataBounds.lower) mDataBounds.lower = z;
20901  if (z > mDataBounds.upper) mDataBounds.upper = z;
20902  mDataModified = true;
20903  }
20904 }
20905 
20918 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z) {
20919  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 &&
20920  valueIndex < mValueSize) {
20921  mData[valueIndex * mKeySize + keyIndex] = z;
20922  if (z < mDataBounds.lower) mDataBounds.lower = z;
20923  if (z > mDataBounds.upper) mDataBounds.upper = z;
20924  mDataModified = true;
20925  }
20926 }
20927 
20944  if (mKeySize > 0 && mValueSize > 0) {
20945  double minHeight = mData[0];
20946  double maxHeight = mData[0];
20947  const int dataCount = mValueSize * mKeySize;
20948  for (int i = 0; i < dataCount; ++i) {
20949  if (mData[i] > maxHeight) maxHeight = mData[i];
20950  if (mData[i] < minHeight) minHeight = mData[i];
20951  }
20952  mDataBounds.lower = minHeight;
20953  mDataBounds.upper = maxHeight;
20954  }
20955 }
20956 
20962 void QCPColorMapData::clear() { setSize(0, 0); }
20963 
20967 void QCPColorMapData::fill(double z) {
20968  const int dataCount = mValueSize * mKeySize;
20969  for (int i = 0; i < dataCount; ++i) mData[i] = z;
20970  mDataBounds = QCPRange(z, z);
20971  mDataModified = true;
20972 }
20973 
20993 void QCPColorMapData::coordToCell(double key,
20994  double value,
20995  int *keyIndex,
20996  int *valueIndex) const {
20997  if (keyIndex)
20998  *keyIndex = (key - mKeyRange.lower) /
21000  (mKeySize - 1) +
21001  0.5;
21002  if (valueIndex)
21003  *valueIndex = (value - mValueRange.lower) /
21005  (mValueSize - 1) +
21006  0.5;
21007 }
21008 
21025 void QCPColorMapData::cellToCoord(int keyIndex,
21026  int valueIndex,
21027  double *key,
21028  double *value) const {
21029  if (key)
21030  *key = keyIndex / (double)(mKeySize - 1) *
21032  mKeyRange.lower;
21033  if (value)
21034  *value = valueIndex / (double)(mValueSize - 1) *
21037 }
21038 
21042 
21105 /* start documentation of inline functions */
21106 
21115 /* end documentation of inline functions */
21116 
21117 /* start documentation of signals */
21118 
21140 /* end documentation of signals */
21141 
21148 QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
21149  : QCPAbstractPlottable(keyAxis, valueAxis),
21150  mDataScaleType(QCPAxis::stLinear),
21151  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
21152  mInterpolate(true),
21153  mTightBoundary(false),
21154  mMapImageInvalidated(true) {}
21155 
21156 QCPColorMap::~QCPColorMap() { delete mMapData; }
21157 
21167  if (mMapData == data) {
21168  qDebug() << Q_FUNC_INFO
21169  << "The data pointer is already in (and owned by) this "
21170  "plottable"
21171  << reinterpret_cast<quintptr>(data);
21172  return;
21173  }
21174  if (copy) {
21175  *mMapData = *data;
21176  } else {
21177  delete mMapData;
21178  mMapData = data;
21179  }
21180  mMapImageInvalidated = true;
21181 }
21182 
21192 void QCPColorMap::setDataRange(const QCPRange &dataRange) {
21193  if (!QCPRange::validRange(dataRange)) return;
21194  if (mDataRange.lower != dataRange.lower ||
21198  else
21200  mMapImageInvalidated = true;
21202  }
21203 }
21204 
21212  if (mDataScaleType != scaleType) {
21213  mDataScaleType = scaleType;
21214  mMapImageInvalidated = true;
21218  }
21219 }
21220 
21234 void QCPColorMap::setGradient(const QCPColorGradient &gradient) {
21235  if (mGradient != gradient) {
21236  mGradient = gradient;
21237  mMapImageInvalidated = true;
21238  emit gradientChanged(mGradient);
21239  }
21240 }
21241 
21250 void QCPColorMap::setInterpolate(bool enabled) {
21251  mInterpolate = enabled;
21253  true; // because oversampling factors might need to change
21254 }
21255 
21270 void QCPColorMap::setTightBoundary(bool enabled) { mTightBoundary = enabled; }
21271 
21289 void QCPColorMap::setColorScale(QCPColorScale *colorScale) {
21290  if (mColorScale) // unconnect signals from old color scale
21291  {
21292  disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(),
21293  SLOT(setDataRange(QCPRange)));
21294  disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)),
21295  mColorScale.data(),
21297  disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)),
21298  mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
21299  disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this,
21300  SLOT(setDataRange(QCPRange)));
21301  disconnect(mColorScale.data(),
21302  SIGNAL(gradientChanged(QCPColorGradient)), this,
21303  SLOT(setGradient(QCPColorGradient)));
21304  disconnect(mColorScale.data(),
21307  }
21309  if (mColorScale) // connect signals to new color scale
21310  {
21311  setGradient(mColorScale.data()->gradient());
21312  setDataRange(mColorScale.data()->dataRange());
21313  setDataScaleType(mColorScale.data()->dataScaleType());
21314  connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(),
21315  SLOT(setDataRange(QCPRange)));
21316  connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)),
21318  connect(this, SIGNAL(gradientChanged(QCPColorGradient)),
21319  mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
21320  connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this,
21321  SLOT(setDataRange(QCPRange)));
21322  connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)),
21323  this, SLOT(setGradient(QCPColorGradient)));
21324  connect(mColorScale.data(),
21327  }
21328 }
21329 
21353 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds) {
21354  if (recalculateDataBounds) mMapData->recalculateDataBounds();
21356 }
21357 
21373 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode,
21374  const QSize &thumbSize) {
21375  if (mMapImage.isNull() && !data()->isEmpty())
21376  updateMapImage(); // try to update map image if it's null (happens if
21377  // no draw has happened yet)
21378 
21379  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so
21380  // check here again
21381  {
21382  bool mirrorX =
21383  (keyAxis()->orientation() == Qt::Horizontal ? keyAxis()
21384  : valueAxis())
21385  ->rangeReversed();
21386  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis()
21387  : keyAxis())
21388  ->rangeReversed();
21389  mLegendIcon =
21390  QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY))
21391  .scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
21392  }
21393 }
21394 
21400 
21401 /* inherits documentation from base class */
21402 double QCPColorMap::selectTest(const QPointF &pos,
21403  bool onlySelectable,
21404  QVariant *details) const {
21405  Q_UNUSED(details)
21406  if (onlySelectable && !mSelectable) return -1;
21407  if (!mKeyAxis || !mValueAxis) {
21408  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21409  return -1;
21410  }
21411 
21412  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
21413  double posKey, posValue;
21414  pixelsToCoords(pos, posKey, posValue);
21415  if (mMapData->keyRange().contains(posKey) &&
21416  mMapData->valueRange().contains(posValue))
21417  return mParentPlot->selectionTolerance() * 0.99;
21418  }
21419  return -1;
21420 }
21421 
21438  QCPAxis *keyAxis = mKeyAxis.data();
21439  if (!keyAxis) return;
21440  if (mMapData->isEmpty()) return;
21441 
21442  const int keySize = mMapData->keySize();
21443  const int valueSize = mMapData->valueSize();
21444  int keyOversamplingFactor =
21445  mInterpolate
21446  ? 1
21447  : (int)(1.0 +
21448  100.0 / (double)keySize); // make mMapImage have at
21449  // least size 100, factor
21450  // becomes 1 if size >
21451  // 200 or interpolation
21452  // is on
21453  int valueOversamplingFactor =
21454  mInterpolate
21455  ? 1
21456  : (int)(1.0 +
21457  100.0 / (double)valueSize); // make mMapImage have
21458  // at least size 100,
21459  // factor becomes 1 if
21460  // size > 200 or
21461  // interpolation is on
21462 
21463  // resize mMapImage to correct dimensions including possible oversampling
21464  // factors, according to key/value axes orientation:
21465  if (keyAxis->orientation() == Qt::Horizontal &&
21466  (mMapImage.width() != keySize * keyOversamplingFactor ||
21467  mMapImage.height() != valueSize * valueOversamplingFactor))
21468  mMapImage = QImage(QSize(keySize * keyOversamplingFactor,
21469  valueSize * valueOversamplingFactor),
21470  QImage::Format_RGB32);
21471  else if (keyAxis->orientation() == Qt::Vertical &&
21472  (mMapImage.width() != valueSize * valueOversamplingFactor ||
21473  mMapImage.height() != keySize * keyOversamplingFactor))
21474  mMapImage = QImage(QSize(valueSize * valueOversamplingFactor,
21475  keySize * keyOversamplingFactor),
21476  QImage::Format_RGB32);
21477 
21478  QImage *localMapImage =
21479  &mMapImage; // this is the image on which the colorization
21480  // operates. Either the final mMapImage, or if we need
21481  // oversampling, mUndersampledMapImage
21482  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) {
21483  // resize undersampled map image to actual key/value cell sizes:
21484  if (keyAxis->orientation() == Qt::Horizontal &&
21485  (mUndersampledMapImage.width() != keySize ||
21486  mUndersampledMapImage.height() != valueSize))
21488  QImage(QSize(keySize, valueSize), QImage::Format_RGB32);
21489  else if (keyAxis->orientation() == Qt::Vertical &&
21490  (mUndersampledMapImage.width() != valueSize ||
21491  mUndersampledMapImage.height() != keySize))
21493  QImage(QSize(valueSize, keySize), QImage::Format_RGB32);
21494  localMapImage = &mUndersampledMapImage; // make the colorization run on
21495  // the undersampled image
21496  } else if (!mUndersampledMapImage.isNull())
21498  QImage(); // don't need oversampling mechanism anymore (map
21499  // size has changed) but mUndersampledMapImage still
21500  // has nonzero size, free it
21501 
21502  const double *rawData = mMapData->mData;
21503  if (keyAxis->orientation() == Qt::Horizontal) {
21504  const int lineCount = valueSize;
21505  const int rowCount = keySize;
21506  for (int line = 0; line < lineCount; ++line) {
21507  QRgb *pixels = reinterpret_cast<QRgb *>(localMapImage->scanLine(
21508  lineCount - 1 -
21509  line)); // invert scanline index because QImage counts
21510  // scanlines from top, but our vertical index
21511  // counts from bottom (mathematical coordinate
21512  // system)
21513  mGradient.colorize(rawData + line * rowCount, mDataRange, pixels,
21514  rowCount, 1,
21516  }
21517  } else // keyAxis->orientation() == Qt::Vertical
21518  {
21519  const int lineCount = keySize;
21520  const int rowCount = valueSize;
21521  for (int line = 0; line < lineCount; ++line) {
21522  QRgb *pixels = reinterpret_cast<QRgb *>(localMapImage->scanLine(
21523  lineCount - 1 -
21524  line)); // invert scanline index because QImage counts
21525  // scanlines from top, but our vertical index
21526  // counts from bottom (mathematical coordinate
21527  // system)
21528  mGradient.colorize(rawData + line, mDataRange, pixels, rowCount,
21529  lineCount,
21531  }
21532  }
21533 
21534  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) {
21535  if (keyAxis->orientation() == Qt::Horizontal)
21537  keySize * keyOversamplingFactor,
21538  valueSize * valueOversamplingFactor, Qt::IgnoreAspectRatio,
21539  Qt::FastTransformation);
21540  else
21542  valueSize * valueOversamplingFactor,
21543  keySize * keyOversamplingFactor, Qt::IgnoreAspectRatio,
21544  Qt::FastTransformation);
21545  }
21546  mMapData->mDataModified = false;
21547  mMapImageInvalidated = false;
21548 }
21549 
21550 /* inherits documentation from base class */
21551 void QCPColorMap::draw(QCPPainter *painter) {
21552  if (mMapData->isEmpty()) return;
21553  if (!mKeyAxis || !mValueAxis) return;
21555 
21557 
21558  // use buffer if painting vectorized (PDF):
21559  bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
21560  QCPPainter *localPainter = painter; // will be redirected to paint on
21561  // mapBuffer if painting vectorized
21562  QRectF mapBufferTarget; // the rect in absolute widget coordinates where
21563  // the visible map portion/buffer will end up in
21564  QPixmap mapBuffer;
21565  double mapBufferPixelRatio =
21566  3; // factor by which DPI is increased in embedded bitmaps
21567  if (useBuffer) {
21568  mapBufferTarget = painter->clipRegion().boundingRect();
21569  mapBuffer = QPixmap(
21570  (mapBufferTarget.size() * mapBufferPixelRatio).toSize());
21571  mapBuffer.fill(Qt::transparent);
21572  localPainter = new QCPPainter(&mapBuffer);
21573  localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
21574  localPainter->translate(-mapBufferTarget.topLeft());
21575  }
21576 
21577  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower,
21581  .normalized();
21582  // extend imageRect to contain outer halves/quarters of bordering/cornering
21583  // pixels (cells are centered on map range boundary):
21584  double halfCellWidth = 0; // in pixels
21585  double halfCellHeight = 0; // in pixels
21586  if (keyAxis()->orientation() == Qt::Horizontal) {
21587  if (mMapData->keySize() > 1)
21588  halfCellWidth =
21589  0.5 * imageRect.width() / (double)(mMapData->keySize() - 1);
21590  if (mMapData->valueSize() > 1)
21591  halfCellHeight = 0.5 * imageRect.height() /
21592  (double)(mMapData->valueSize() - 1);
21593  } else // keyAxis orientation is Qt::Vertical
21594  {
21595  if (mMapData->keySize() > 1)
21596  halfCellHeight = 0.5 * imageRect.height() /
21597  (double)(mMapData->keySize() - 1);
21598  if (mMapData->valueSize() > 1)
21599  halfCellWidth = 0.5 * imageRect.width() /
21600  (double)(mMapData->valueSize() - 1);
21601  }
21602  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth,
21603  halfCellHeight);
21604  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis()
21605  : valueAxis())
21606  ->rangeReversed();
21607  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis()
21608  : keyAxis())
21609  ->rangeReversed();
21610  bool smoothBackup = localPainter->renderHints().testFlag(
21611  QPainter::SmoothPixmapTransform);
21612  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
21613  QRegion clipBackup;
21614  if (mTightBoundary) {
21615  clipBackup = localPainter->clipRegion();
21616  QRectF tightClipRect =
21621  .normalized();
21622  localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
21623  }
21624  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
21625  if (mTightBoundary) localPainter->setClipRegion(clipBackup);
21626  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
21627 
21628  if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer
21629  // with original painter
21630  {
21631  delete localPainter;
21632  painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
21633  }
21634 }
21635 
21636 /* inherits documentation from base class */
21638  const QRectF &rect) const {
21640  // draw map thumbnail:
21641  if (!mLegendIcon.isNull()) {
21642  QPixmap scaledIcon =
21643  mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio,
21644  Qt::FastTransformation);
21645  QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
21646  iconRect.moveCenter(rect.center());
21647  painter->drawPixmap(iconRect.topLeft(), scaledIcon);
21648  }
21649  /*
21650  // draw frame:
21651  painter->setBrush(Qt::NoBrush);
21652  painter->setPen(Qt::black);
21653  painter->drawRect(rect.adjusted(1, 1, 0, 0));
21654  */
21655 }
21656 
21657 /* inherits documentation from base class */
21659  SignDomain inSignDomain) const {
21660  foundRange = true;
21662  result.normalize();
21663  if (inSignDomain == QCPAbstractPlottable::sdPositive) {
21664  if (result.lower <= 0 && result.upper > 0)
21665  result.lower = result.upper * 1e-3;
21666  else if (result.lower <= 0 && result.upper <= 0)
21667  foundRange = false;
21668  } else if (inSignDomain == QCPAbstractPlottable::sdNegative) {
21669  if (result.upper >= 0 && result.lower < 0)
21670  result.upper = result.lower * 1e-3;
21671  else if (result.upper >= 0 && result.lower >= 0)
21672  foundRange = false;
21673  }
21674  return result;
21675 }
21676 
21677 /* inherits documentation from base class */
21679  SignDomain inSignDomain) const {
21680  foundRange = true;
21682  result.normalize();
21683  if (inSignDomain == QCPAbstractPlottable::sdPositive) {
21684  if (result.lower <= 0 && result.upper > 0)
21685  result.lower = result.upper * 1e-3;
21686  else if (result.lower <= 0 && result.upper <= 0)
21687  foundRange = false;
21688  } else if (inSignDomain == QCPAbstractPlottable::sdNegative) {
21689  if (result.upper >= 0 && result.lower < 0)
21690  result.upper = result.lower * 1e-3;
21691  else if (result.upper >= 0 && result.lower >= 0)
21692  foundRange = false;
21693  }
21694  return result;
21695 }
21696 
21700 
21720  : key(0), open(0), high(0), low(0), close(0) {}
21721 
21726  double key, double open, double high, double low, double close)
21727  : key(key), open(open), high(high), low(low), close(close) {}
21728 
21732 
21770 /* start of documentation of inline functions */
21771 
21780 /* end of documentation of inline functions */
21781 
21793 QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
21794  : QCPAbstractPlottable(keyAxis, valueAxis),
21795  mData(0),
21796  mChartStyle(csOhlc),
21797  mWidth(0.5),
21798  mTwoColored(false),
21799  mBrushPositive(QBrush(QColor(210, 210, 255))),
21800  mBrushNegative(QBrush(QColor(255, 210, 210))),
21801  mPenPositive(QPen(QColor(10, 40, 180))),
21802  mPenNegative(QPen(QColor(180, 40, 10))) {
21803  mData = new QCPFinancialDataMap;
21804 
21805  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
21806  setSelectedBrush(QBrush(QColor(80, 80, 255)));
21807 }
21808 
21809 QCPFinancial::~QCPFinancial() { delete mData; }
21810 
21826  if (mData == data) {
21827  qDebug() << Q_FUNC_INFO
21828  << "The data pointer is already in (and owned by) this "
21829  "plottable"
21830  << reinterpret_cast<quintptr>(data);
21831  return;
21832  }
21833  if (copy) {
21834  *mData = *data;
21835  } else {
21836  delete mData;
21837  mData = data;
21838  }
21839 }
21840 
21849 void QCPFinancial::setData(const QVector<double> &key,
21850  const QVector<double> &open,
21851  const QVector<double> &high,
21852  const QVector<double> &low,
21853  const QVector<double> &close) {
21854  mData->clear();
21855  int n = key.size();
21856  n = qMin(n, open.size());
21857  n = qMin(n, high.size());
21858  n = qMin(n, low.size());
21859  n = qMin(n, close.size());
21860  for (int i = 0; i < n; ++i) {
21862  mData, key[i],
21863  QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
21864  }
21865 }
21866 
21871  mChartStyle = style;
21872 }
21873 
21881 void QCPFinancial::setWidth(double width) { mWidth = width; }
21882 
21892 void QCPFinancial::setTwoColored(bool twoColored) { mTwoColored = twoColored; }
21893 
21904 void QCPFinancial::setBrushPositive(const QBrush &brush) {
21906 }
21907 
21918 void QCPFinancial::setBrushNegative(const QBrush &brush) {
21920 }
21921 
21932 void QCPFinancial::setPenPositive(const QPen &pen) { mPenPositive = pen; }
21933 
21944 void QCPFinancial::setPenNegative(const QPen &pen) { mPenNegative = pen; }
21945 
21955  qtCompatMapUnite(mData, dataMap);
21956 }
21957 
21969 }
21970 
21982  double key, double open, double high, double low, double close) {
21984  QCPFinancialData(key, open, high, low, close));
21985 }
21986 
21996 void QCPFinancial::addData(const QVector<double> &key,
21997  const QVector<double> &open,
21998  const QVector<double> &high,
21999  const QVector<double> &low,
22000  const QVector<double> &close) {
22001  int n = key.size();
22002  n = qMin(n, open.size());
22003  n = qMin(n, high.size());
22004  n = qMin(n, low.size());
22005  n = qMin(n, close.size());
22006  for (int i = 0; i < n; ++i) {
22008  mData, key[i],
22009  QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
22010  }
22011 }
22012 
22019  QCPFinancialDataMap::iterator it = mData->begin();
22020  while (it != mData->end() && it.key() < key) it = mData->erase(it);
22021 }
22022 
22029  if (mData->isEmpty()) return;
22030  QCPFinancialDataMap::iterator it = mData->upperBound(key);
22031  while (it != mData->end()) it = mData->erase(it);
22032 }
22033 
22041 void QCPFinancial::removeData(double fromKey, double toKey) {
22042  if (fromKey >= toKey || mData->isEmpty()) return;
22043  QCPFinancialDataMap::iterator it = mData->upperBound(fromKey);
22044  QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey);
22045  while (it != itEnd) it = mData->erase(it);
22046 }
22047 
22057 void QCPFinancial::removeData(double key) { mData->remove(key); }
22058 
22064 void QCPFinancial::clearData() { mData->clear(); }
22065 
22066 /* inherits documentation from base class */
22067 double QCPFinancial::selectTest(const QPointF &pos,
22068  bool onlySelectable,
22069  QVariant *details) const {
22070  Q_UNUSED(details)
22071  if (onlySelectable && !mSelectable) return -1;
22072  if (!mKeyAxis || !mValueAxis) {
22073  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22074  return -1;
22075  }
22076 
22077  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
22078  // get visible data range:
22079  QCPFinancialDataMap::const_iterator lower,
22080  upper; // note that upper is the actual upper point, and not 1
22081  // step after the upper point
22082  getVisibleDataBounds(lower, upper);
22083  if (lower == mData->constEnd() || upper == mData->constEnd()) return -1;
22084  // perform select test according to configured style:
22085  switch (mChartStyle) {
22086  case QCPFinancial::csOhlc:
22087  return ohlcSelectTest(pos, lower, upper + 1);
22088  break;
22090  return candlestickSelectTest(pos, lower, upper + 1);
22091  break;
22092  }
22093  }
22094  return -1;
22095 }
22096 
22111 QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time,
22112  const QVector<double> &value,
22113  double timeBinSize,
22114  double timeBinOffset) {
22115  QCPFinancialDataMap map;
22116  int count = qMin(time.size(), value.size());
22117  if (count == 0) return QCPFinancialDataMap();
22118 
22119  QCPFinancialData currentBinData(0, value.first(), value.first(),
22120  value.first(), value.first());
22121  int currentBinIndex =
22122  qFloor((time.first() - timeBinOffset) / timeBinSize + 0.5);
22123  for (int i = 0; i < count; ++i) {
22124  int index = qFloor((time.at(i) - timeBinOffset) / timeBinSize + 0.5);
22125  if (currentBinIndex ==
22126  index) // data point still in current bin, extend high/low:
22127  {
22128  if (value.at(i) < currentBinData.low)
22129  currentBinData.low = value.at(i);
22130  if (value.at(i) > currentBinData.high)
22131  currentBinData.high = value.at(i);
22132  if (i ==
22133  count - 1) // last data point is in current bin, finalize bin:
22134  {
22135  currentBinData.close = value.at(i);
22136  currentBinData.key = timeBinOffset + (index)*timeBinSize;
22137  map.insert(currentBinData.key, currentBinData);
22138  }
22139  } else // data point not anymore in current bin, set close of old and
22140  // open of new bin, and add old to map:
22141  {
22142  // finalize current bin:
22143  currentBinData.close = value.at(i - 1);
22144  currentBinData.key = timeBinOffset + (index - 1) * timeBinSize;
22145  map.insert(currentBinData.key, currentBinData);
22146  // start next bin:
22147  currentBinIndex = index;
22148  currentBinData.open = value.at(i);
22149  currentBinData.high = value.at(i);
22150  currentBinData.low = value.at(i);
22151  }
22152  }
22153 
22154  return map;
22155 }
22156 
22157 /* inherits documentation from base class */
22158 void QCPFinancial::draw(QCPPainter *painter) {
22159  // get visible data range:
22160  QCPFinancialDataMap::const_iterator lower,
22161  upper; // note that upper is the actual upper point, and not 1 step
22162  // after the upper point
22163  getVisibleDataBounds(lower, upper);
22164  if (lower == mData->constEnd() || upper == mData->constEnd()) return;
22165 
22166  // draw visible data range according to configured style:
22167  switch (mChartStyle) {
22168  case QCPFinancial::csOhlc:
22169  drawOhlcPlot(painter, lower, upper + 1);
22170  break;
22172  drawCandlestickPlot(painter, lower, upper + 1);
22173  break;
22174  }
22175 }
22176 
22177 /* inherits documentation from base class */
22179  const QRectF &rect) const {
22180  painter->setAntialiasing(false); // legend icon especially of csCandlestick
22181  // looks better without antialiasing
22182  if (mChartStyle == csOhlc) {
22183  if (mTwoColored) {
22184  // draw upper left half icon with positive color:
22185  painter->setBrush(mBrushPositive);
22186  painter->setPen(mPenPositive);
22187  painter->setClipRegion(QRegion(QPolygon()
22188  << rect.bottomLeft().toPoint()
22189  << rect.topRight().toPoint()
22190  << rect.topLeft().toPoint()));
22191  painter->drawLine(QLineF(0, rect.height() * 0.5, rect.width(),
22192  rect.height() * 0.5)
22193  .translated(rect.topLeft()));
22194  painter->drawLine(QLineF(rect.width() * 0.2, rect.height() * 0.3,
22195  rect.width() * 0.2, rect.height() * 0.5)
22196  .translated(rect.topLeft()));
22197  painter->drawLine(QLineF(rect.width() * 0.8, rect.height() * 0.5,
22198  rect.width() * 0.8, rect.height() * 0.7)
22199  .translated(rect.topLeft()));
22200  // draw bottom right hald icon with negative color:
22201  painter->setBrush(mBrushNegative);
22202  painter->setPen(mPenNegative);
22203  painter->setClipRegion(QRegion(QPolygon()
22204  << rect.bottomLeft().toPoint()
22205  << rect.topRight().toPoint()
22206  << rect.bottomRight().toPoint()));
22207  painter->drawLine(QLineF(0, rect.height() * 0.5, rect.width(),
22208  rect.height() * 0.5)
22209  .translated(rect.topLeft()));
22210  painter->drawLine(QLineF(rect.width() * 0.2, rect.height() * 0.3,
22211  rect.width() * 0.2, rect.height() * 0.5)
22212  .translated(rect.topLeft()));
22213  painter->drawLine(QLineF(rect.width() * 0.8, rect.height() * 0.5,
22214  rect.width() * 0.8, rect.height() * 0.7)
22215  .translated(rect.topLeft()));
22216  } else {
22217  painter->setBrush(mBrush);
22218  painter->setPen(mPen);
22219  painter->drawLine(QLineF(0, rect.height() * 0.5, rect.width(),
22220  rect.height() * 0.5)
22221  .translated(rect.topLeft()));
22222  painter->drawLine(QLineF(rect.width() * 0.2, rect.height() * 0.3,
22223  rect.width() * 0.2, rect.height() * 0.5)
22224  .translated(rect.topLeft()));
22225  painter->drawLine(QLineF(rect.width() * 0.8, rect.height() * 0.5,
22226  rect.width() * 0.8, rect.height() * 0.7)
22227  .translated(rect.topLeft()));
22228  }
22229  } else if (mChartStyle == csCandlestick) {
22230  if (mTwoColored) {
22231  // draw upper left half icon with positive color:
22232  painter->setBrush(mBrushPositive);
22233  painter->setPen(mPenPositive);
22234  painter->setClipRegion(QRegion(QPolygon()
22235  << rect.bottomLeft().toPoint()
22236  << rect.topRight().toPoint()
22237  << rect.topLeft().toPoint()));
22238  painter->drawLine(QLineF(0, rect.height() * 0.5,
22239  rect.width() * 0.25, rect.height() * 0.5)
22240  .translated(rect.topLeft()));
22241  painter->drawLine(QLineF(rect.width() * 0.75, rect.height() * 0.5,
22242  rect.width(), rect.height() * 0.5)
22243  .translated(rect.topLeft()));
22244  painter->drawRect(QRectF(rect.width() * 0.25, rect.height() * 0.25,
22245  rect.width() * 0.5, rect.height() * 0.5)
22246  .translated(rect.topLeft()));
22247  // draw bottom right hald icon with negative color:
22248  painter->setBrush(mBrushNegative);
22249  painter->setPen(mPenNegative);
22250  painter->setClipRegion(QRegion(QPolygon()
22251  << rect.bottomLeft().toPoint()
22252  << rect.topRight().toPoint()
22253  << rect.bottomRight().toPoint()));
22254  painter->drawLine(QLineF(0, rect.height() * 0.5,
22255  rect.width() * 0.25, rect.height() * 0.5)
22256  .translated(rect.topLeft()));
22257  painter->drawLine(QLineF(rect.width() * 0.75, rect.height() * 0.5,
22258  rect.width(), rect.height() * 0.5)
22259  .translated(rect.topLeft()));
22260  painter->drawRect(QRectF(rect.width() * 0.25, rect.height() * 0.25,
22261  rect.width() * 0.5, rect.height() * 0.5)
22262  .translated(rect.topLeft()));
22263  } else {
22264  painter->setBrush(mBrush);
22265  painter->setPen(mPen);
22266  painter->drawLine(QLineF(0, rect.height() * 0.5,
22267  rect.width() * 0.25, rect.height() * 0.5)
22268  .translated(rect.topLeft()));
22269  painter->drawLine(QLineF(rect.width() * 0.75, rect.height() * 0.5,
22270  rect.width(), rect.height() * 0.5)
22271  .translated(rect.topLeft()));
22272  painter->drawRect(QRectF(rect.width() * 0.25, rect.height() * 0.25,
22273  rect.width() * 0.5, rect.height() * 0.5)
22274  .translated(rect.topLeft()));
22275  }
22276  }
22277 }
22278 
22279 /* inherits documentation from base class */
22281  bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const {
22282  QCPRange range;
22283  bool haveLower = false;
22284  bool haveUpper = false;
22285 
22286  double current;
22287  QCPFinancialDataMap::const_iterator it = mData->constBegin();
22288  while (it != mData->constEnd()) {
22289  current = it.value().key;
22290  if (inSignDomain == sdBoth ||
22291  (inSignDomain == sdNegative && current < 0) ||
22292  (inSignDomain == sdPositive && current > 0)) {
22293  if (current < range.lower || !haveLower) {
22294  range.lower = current;
22295  haveLower = true;
22296  }
22297  if (current > range.upper || !haveUpper) {
22298  range.upper = current;
22299  haveUpper = true;
22300  }
22301  }
22302  ++it;
22303  }
22304  // determine exact range by including width of bars/flags:
22305  if (haveLower && mKeyAxis) range.lower = range.lower - mWidth * 0.5;
22306  if (haveUpper && mKeyAxis) range.upper = range.upper + mWidth * 0.5;
22307  foundRange = haveLower && haveUpper;
22308  return range;
22309 }
22310 
22311 /* inherits documentation from base class */
22313  bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const {
22314  QCPRange range;
22315  bool haveLower = false;
22316  bool haveUpper = false;
22317 
22318  QCPFinancialDataMap::const_iterator it = mData->constBegin();
22319  while (it != mData->constEnd()) {
22320  // high:
22321  if (inSignDomain == sdBoth ||
22322  (inSignDomain == sdNegative && it.value().high < 0) ||
22323  (inSignDomain == sdPositive && it.value().high > 0)) {
22324  if (it.value().high < range.lower || !haveLower) {
22325  range.lower = it.value().high;
22326  haveLower = true;
22327  }
22328  if (it.value().high > range.upper || !haveUpper) {
22329  range.upper = it.value().high;
22330  haveUpper = true;
22331  }
22332  }
22333  // low:
22334  if (inSignDomain == sdBoth ||
22335  (inSignDomain == sdNegative && it.value().low < 0) ||
22336  (inSignDomain == sdPositive && it.value().low > 0)) {
22337  if (it.value().low < range.lower || !haveLower) {
22338  range.lower = it.value().low;
22339  haveLower = true;
22340  }
22341  if (it.value().low > range.upper || !haveUpper) {
22342  range.upper = it.value().low;
22343  haveUpper = true;
22344  }
22345  }
22346  ++it;
22347  }
22348 
22349  foundRange = haveLower && haveUpper;
22350  return range;
22351 }
22352 
22362  QCPPainter *painter,
22363  const QCPFinancialDataMap::const_iterator &begin,
22364  const QCPFinancialDataMap::const_iterator &end) {
22365  QCPAxis *keyAxis = mKeyAxis.data();
22366  QCPAxis *valueAxis = mValueAxis.data();
22367  if (!keyAxis || !valueAxis) {
22368  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22369  return;
22370  }
22371 
22372  QPen linePen;
22373 
22374  if (keyAxis->orientation() == Qt::Horizontal) {
22375  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) {
22376  if (mSelected)
22377  linePen = mSelectedPen;
22378  else if (mTwoColored)
22379  linePen = it.value().close >= it.value().open ? mPenPositive
22380  : mPenNegative;
22381  else
22382  linePen = mPen;
22383  painter->setPen(linePen);
22384  double keyPixel = keyAxis->coordToPixel(it.value().key);
22385  double openPixel = valueAxis->coordToPixel(it.value().open);
22386  double closePixel = valueAxis->coordToPixel(it.value().close);
22387  // draw backbone:
22388  painter->drawLine(
22389  QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)),
22390  QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
22391  // draw open:
22392  double keyWidthPixels =
22393  keyPixel -
22395  it.value().key -
22396  mWidth * 0.5); // sign of this makes sure
22397  // open/close are on correct sides
22398  painter->drawLine(QPointF(keyPixel - keyWidthPixels, openPixel),
22399  QPointF(keyPixel, openPixel));
22400  // draw close:
22401  painter->drawLine(QPointF(keyPixel, closePixel),
22402  QPointF(keyPixel + keyWidthPixels, closePixel));
22403  }
22404  } else {
22405  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) {
22406  if (mSelected)
22407  linePen = mSelectedPen;
22408  else if (mTwoColored)
22409  linePen = it.value().close >= it.value().open ? mPenPositive
22410  : mPenNegative;
22411  else
22412  linePen = mPen;
22413  painter->setPen(linePen);
22414  double keyPixel = keyAxis->coordToPixel(it.value().key);
22415  double openPixel = valueAxis->coordToPixel(it.value().open);
22416  double closePixel = valueAxis->coordToPixel(it.value().close);
22417  // draw backbone:
22418  painter->drawLine(
22419  QPointF(valueAxis->coordToPixel(it.value().high), keyPixel),
22420  QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
22421  // draw open:
22422  double keyWidthPixels =
22423  keyPixel -
22425  it.value().key -
22426  mWidth * 0.5); // sign of this makes sure
22427  // open/close are on correct sides
22428  painter->drawLine(QPointF(openPixel, keyPixel - keyWidthPixels),
22429  QPointF(openPixel, keyPixel));
22430  // draw close:
22431  painter->drawLine(QPointF(closePixel, keyPixel),
22432  QPointF(closePixel, keyPixel + keyWidthPixels));
22433  }
22434  }
22435 }
22436 
22446  QCPPainter *painter,
22447  const QCPFinancialDataMap::const_iterator &begin,
22448  const QCPFinancialDataMap::const_iterator &end) {
22449  QCPAxis *keyAxis = mKeyAxis.data();
22450  QCPAxis *valueAxis = mValueAxis.data();
22451  if (!keyAxis || !valueAxis) {
22452  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22453  return;
22454  }
22455 
22456  QPen linePen;
22457  QBrush boxBrush;
22458 
22459  if (keyAxis->orientation() == Qt::Horizontal) {
22460  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) {
22461  if (mSelected) {
22462  linePen = mSelectedPen;
22463  boxBrush = mSelectedBrush;
22464  } else if (mTwoColored) {
22465  if (it.value().close >= it.value().open) {
22466  linePen = mPenPositive;
22467  boxBrush = mBrushPositive;
22468  } else {
22469  linePen = mPenNegative;
22470  boxBrush = mBrushNegative;
22471  }
22472  } else {
22473  linePen = mPen;
22474  boxBrush = mBrush;
22475  }
22476  painter->setPen(linePen);
22477  painter->setBrush(boxBrush);
22478  double keyPixel = keyAxis->coordToPixel(it.value().key);
22479  double openPixel = valueAxis->coordToPixel(it.value().open);
22480  double closePixel = valueAxis->coordToPixel(it.value().close);
22481  // draw high:
22482  painter->drawLine(
22483  QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)),
22484  QPointF(keyPixel,
22486  qMax(it.value().open, it.value().close))));
22487  // draw low:
22488  painter->drawLine(
22489  QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)),
22490  QPointF(keyPixel,
22492  qMin(it.value().open, it.value().close))));
22493  // draw open-close box:
22494  double keyWidthPixels =
22495  keyPixel -
22496  keyAxis->coordToPixel(it.value().key - mWidth * 0.5);
22497  painter->drawRect(
22498  QRectF(QPointF(keyPixel - keyWidthPixels, closePixel),
22499  QPointF(keyPixel + keyWidthPixels, openPixel)));
22500  }
22501  } else // keyAxis->orientation() == Qt::Vertical
22502  {
22503  for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) {
22504  if (mSelected) {
22505  linePen = mSelectedPen;
22506  boxBrush = mSelectedBrush;
22507  } else if (mTwoColored) {
22508  if (it.value().close >= it.value().open) {
22509  linePen = mPenPositive;
22510  boxBrush = mBrushPositive;
22511  } else {
22512  linePen = mPenNegative;
22513  boxBrush = mBrushNegative;
22514  }
22515  } else {
22516  linePen = mPen;
22517  boxBrush = mBrush;
22518  }
22519  painter->setPen(linePen);
22520  painter->setBrush(boxBrush);
22521  double keyPixel = keyAxis->coordToPixel(it.value().key);
22522  double openPixel = valueAxis->coordToPixel(it.value().open);
22523  double closePixel = valueAxis->coordToPixel(it.value().close);
22524  // draw high:
22525  painter->drawLine(
22526  QPointF(valueAxis->coordToPixel(it.value().high), keyPixel),
22527  QPointF(valueAxis->coordToPixel(
22528  qMax(it.value().open, it.value().close)),
22529  keyPixel));
22530  // draw low:
22531  painter->drawLine(
22532  QPointF(valueAxis->coordToPixel(it.value().low), keyPixel),
22533  QPointF(valueAxis->coordToPixel(
22534  qMin(it.value().open, it.value().close)),
22535  keyPixel));
22536  // draw open-close box:
22537  double keyWidthPixels =
22538  keyPixel -
22539  keyAxis->coordToPixel(it.value().key - mWidth * 0.5);
22540  painter->drawRect(
22541  QRectF(QPointF(closePixel, keyPixel - keyWidthPixels),
22542  QPointF(openPixel, keyPixel + keyWidthPixels)));
22543  }
22544  }
22545 }
22546 
22554  const QPointF &pos,
22555  const QCPFinancialDataMap::const_iterator &begin,
22556  const QCPFinancialDataMap::const_iterator &end) const {
22557  QCPAxis *keyAxis = mKeyAxis.data();
22558  QCPAxis *valueAxis = mValueAxis.data();
22559  if (!keyAxis || !valueAxis) {
22560  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22561  return -1;
22562  }
22563 
22564  double minDistSqr = std::numeric_limits<double>::max();
22565  QCPFinancialDataMap::const_iterator it;
22566  if (keyAxis->orientation() == Qt::Horizontal) {
22567  for (it = begin; it != end; ++it) {
22568  double keyPixel = keyAxis->coordToPixel(it.value().key);
22569  // calculate distance to backbone:
22570  double currentDistSqr = distSqrToLine(
22571  QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)),
22572  QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)),
22573  pos);
22574  if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
22575  }
22576  } else // keyAxis->orientation() == Qt::Vertical
22577  {
22578  for (it = begin; it != end; ++it) {
22579  double keyPixel = keyAxis->coordToPixel(it.value().key);
22580  // calculate distance to backbone:
22581  double currentDistSqr = distSqrToLine(
22582  QPointF(valueAxis->coordToPixel(it.value().high), keyPixel),
22583  QPointF(valueAxis->coordToPixel(it.value().low), keyPixel),
22584  pos);
22585  if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
22586  }
22587  }
22588  return qSqrt(minDistSqr);
22589 }
22590 
22598  const QPointF &pos,
22599  const QCPFinancialDataMap::const_iterator &begin,
22600  const QCPFinancialDataMap::const_iterator &end) const {
22601  QCPAxis *keyAxis = mKeyAxis.data();
22602  QCPAxis *valueAxis = mValueAxis.data();
22603  if (!keyAxis || !valueAxis) {
22604  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22605  return -1;
22606  }
22607 
22608  double minDistSqr = std::numeric_limits<double>::max();
22609  QCPFinancialDataMap::const_iterator it;
22610  if (keyAxis->orientation() == Qt::Horizontal) {
22611  for (it = begin; it != end; ++it) {
22612  double currentDistSqr;
22613  // determine whether pos is in open-close-box:
22614  QCPRange boxKeyRange(it.value().key - mWidth * 0.5,
22615  it.value().key + mWidth * 0.5);
22616  QCPRange boxValueRange(it.value().close, it.value().open);
22617  double posKey, posValue;
22618  pixelsToCoords(pos, posKey, posValue);
22619  if (boxKeyRange.contains(posKey) &&
22620  boxValueRange.contains(posValue)) // is in open-close-box
22621  {
22622  currentDistSqr = mParentPlot->selectionTolerance() * 0.99 *
22623  mParentPlot->selectionTolerance() * 0.99;
22624  } else {
22625  // calculate distance to high/low lines:
22626  double keyPixel = keyAxis->coordToPixel(it.value().key);
22627  double highLineDistSqr = distSqrToLine(
22628  QPointF(keyPixel,
22629  valueAxis->coordToPixel(it.value().high)),
22630  QPointF(keyPixel,
22631  valueAxis->coordToPixel(qMax(
22632  it.value().open, it.value().close))),
22633  pos);
22634  double lowLineDistSqr = distSqrToLine(
22635  QPointF(keyPixel,
22636  valueAxis->coordToPixel(it.value().low)),
22637  QPointF(keyPixel,
22638  valueAxis->coordToPixel(qMin(
22639  it.value().open, it.value().close))),
22640  pos);
22641  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
22642  }
22643  if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
22644  }
22645  } else // keyAxis->orientation() == Qt::Vertical
22646  {
22647  for (it = begin; it != end; ++it) {
22648  double currentDistSqr;
22649  // determine whether pos is in open-close-box:
22650  QCPRange boxKeyRange(it.value().key - mWidth * 0.5,
22651  it.value().key + mWidth * 0.5);
22652  QCPRange boxValueRange(it.value().close, it.value().open);
22653  double posKey, posValue;
22654  pixelsToCoords(pos, posKey, posValue);
22655  if (boxKeyRange.contains(posKey) &&
22656  boxValueRange.contains(posValue)) // is in open-close-box
22657  {
22658  currentDistSqr = mParentPlot->selectionTolerance() * 0.99 *
22659  mParentPlot->selectionTolerance() * 0.99;
22660  } else {
22661  // calculate distance to high/low lines:
22662  double keyPixel = keyAxis->coordToPixel(it.value().key);
22663  double highLineDistSqr = distSqrToLine(
22664  QPointF(valueAxis->coordToPixel(it.value().high),
22665  keyPixel),
22666  QPointF(valueAxis->coordToPixel(qMax(it.value().open,
22667  it.value().close)),
22668  keyPixel),
22669  pos);
22670  double lowLineDistSqr = distSqrToLine(
22671  QPointF(valueAxis->coordToPixel(it.value().low),
22672  keyPixel),
22673  QPointF(valueAxis->coordToPixel(qMin(it.value().open,
22674  it.value().close)),
22675  keyPixel),
22676  pos);
22677  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
22678  }
22679  if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
22680  }
22681  }
22682  return qSqrt(minDistSqr);
22683 }
22684 
22704  QCPFinancialDataMap::const_iterator &lower,
22705  QCPFinancialDataMap::const_iterator &upper) const {
22706  if (!mKeyAxis) {
22707  qDebug() << Q_FUNC_INFO << "invalid key axis";
22708  return;
22709  }
22710  if (mData->isEmpty()) {
22711  lower = mData->constEnd();
22712  upper = mData->constEnd();
22713  return;
22714  }
22715 
22716  // get visible data range as QMap iterators
22717  QCPFinancialDataMap::const_iterator lbound =
22718  mData->lowerBound(mKeyAxis.data()->range().lower);
22719  QCPFinancialDataMap::const_iterator ubound =
22720  mData->upperBound(mKeyAxis.data()->range().upper);
22721  bool lowoutlier =
22722  lbound != mData->constBegin(); // indicates whether there exist
22723  // points below axis range
22724  bool highoutlier =
22725  ubound != mData->constEnd(); // indicates whether there exist
22726  // points above axis range
22727 
22728  lower = (lowoutlier
22729  ? lbound - 1
22730  : lbound); // data point range that will be actually drawn
22731  upper = (highoutlier ? ubound : ubound - 1); // data point range that will
22732  // be actually drawn
22733 }
22734 
22738 
22754  : QCPAbstractItem(parentPlot),
22755  point1(createPosition(QLatin1String("point1"))),
22756  point2(createPosition(QLatin1String("point2"))) {
22757  point1->setCoords(0, 0);
22758  point2->setCoords(1, 1);
22759 
22760  setPen(QPen(Qt::black));
22761  setSelectedPen(QPen(Qt::blue, 2));
22762 }
22763 
22765 
22771 void QCPItemStraightLine::setPen(const QPen &pen) { mPen = pen; }
22772 
22778 void QCPItemStraightLine::setSelectedPen(const QPen &pen) {
22779  mSelectedPen = pen;
22780 }
22781 
22782 /* inherits documentation from base class */
22783 double QCPItemStraightLine::selectTest(const QPointF &pos,
22784  bool onlySelectable,
22785  QVariant *details) const {
22786  Q_UNUSED(details)
22787  if (onlySelectable && !mSelectable) return -1;
22788 
22789  return distToStraightLine(
22790  QVector2D(point1->pixelPoint()),
22791  QVector2D(point2->pixelPoint() - point1->pixelPoint()),
22792  QVector2D(pos));
22793 }
22794 
22795 /* inherits documentation from base class */
22796 void QCPItemStraightLine::draw(QCPPainter *painter) {
22797  QVector2D start(point1->pixelPoint());
22798  QVector2D end(point2->pixelPoint());
22799  // get visible segment of straight line inside clipRect:
22800  double clipPad = mainPen().widthF();
22801  QLineF line = getRectClippedStraightLine(
22802  start, end - start,
22803  clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
22804  // paint visible segment, if existent:
22805  if (!line.isNull()) {
22806  painter->setPen(mainPen());
22807  painter->drawLine(line);
22808  }
22809 }
22810 
22818 double QCPItemStraightLine::distToStraightLine(const QVector2D &base,
22819  const QVector2D &vec,
22820  const QVector2D &point) const {
22821  return qAbs((base.y() - point.y()) * vec.x() -
22822  (base.x() - point.x()) * vec.y()) /
22823  vec.length();
22824 }
22825 
22834  const QVector2D &base, const QVector2D &vec, const QRect &rect) const {
22835  double bx, by;
22836  double gamma;
22837  QLineF result;
22838  if (vec.x() == 0 && vec.y() == 0) return result;
22839  if (qFuzzyIsNull(vec.x())) // line is vertical
22840  {
22841  // check top of rect:
22842  bx = rect.left();
22843  by = rect.top();
22844  gamma = base.x() - bx + (by - base.y()) * vec.x() / vec.y();
22845  if (gamma >= 0 && gamma <= rect.width())
22846  result.setLine(bx + gamma, rect.top(), bx + gamma,
22847  rect.bottom()); // no need to check bottom because
22848  // we know line is vertical
22849  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
22850  {
22851  // check left of rect:
22852  bx = rect.left();
22853  by = rect.top();
22854  gamma = base.y() - by + (bx - base.x()) * vec.y() / vec.x();
22855  if (gamma >= 0 && gamma <= rect.height())
22856  result.setLine(rect.left(), by + gamma, rect.right(),
22857  by + gamma); // no need to check right because we
22858  // know line is horizontal
22859  } else // line is skewed
22860  {
22861  QList<QVector2D> pointVectors;
22862  // check top of rect:
22863  bx = rect.left();
22864  by = rect.top();
22865  gamma = base.x() - bx + (by - base.y()) * vec.x() / vec.y();
22866  if (gamma >= 0 && gamma <= rect.width())
22867  pointVectors.append(QVector2D(bx + gamma, by));
22868  // check bottom of rect:
22869  bx = rect.left();
22870  by = rect.bottom();
22871  gamma = base.x() - bx + (by - base.y()) * vec.x() / vec.y();
22872  if (gamma >= 0 && gamma <= rect.width())
22873  pointVectors.append(QVector2D(bx + gamma, by));
22874  // check left of rect:
22875  bx = rect.left();
22876  by = rect.top();
22877  gamma = base.y() - by + (bx - base.x()) * vec.y() / vec.x();
22878  if (gamma >= 0 && gamma <= rect.height())
22879  pointVectors.append(QVector2D(bx, by + gamma));
22880  // check right of rect:
22881  bx = rect.right();
22882  by = rect.top();
22883  gamma = base.y() - by + (bx - base.x()) * vec.y() / vec.x();
22884  if (gamma >= 0 && gamma <= rect.height())
22885  pointVectors.append(QVector2D(bx, by + gamma));
22886 
22887  // evaluate points:
22888  if (pointVectors.size() == 2) {
22889  result.setPoints(pointVectors.at(0).toPointF(),
22890  pointVectors.at(1).toPointF());
22891  } else if (pointVectors.size() > 2) {
22892  // line probably goes through corner of rect, and we got two points
22893  // there. single out the point pair with greatest distance:
22894  double distSqrMax = 0;
22895  QVector2D pv1, pv2;
22896  for (int i = 0; i < pointVectors.size() - 1; ++i) {
22897  for (int k = i + 1; k < pointVectors.size(); ++k) {
22898  double distSqr = (pointVectors.at(i) - pointVectors.at(k))
22899  .lengthSquared();
22900  if (distSqr > distSqrMax) {
22901  pv1 = pointVectors.at(i);
22902  pv2 = pointVectors.at(k);
22903  distSqrMax = distSqr;
22904  }
22905  }
22906  }
22907  result.setPoints(pv1.toPointF(), pv2.toPointF());
22908  }
22909  }
22910  return result;
22911 }
22912 
22918 QPen QCPItemStraightLine::mainPen() const {
22919  return mSelected ? mSelectedPen : mPen;
22920 }
22921 
22925 
22945  : QCPAbstractItem(parentPlot),
22946  start(createPosition(QLatin1String("start"))),
22947  end(createPosition(QLatin1String("end"))) {
22948  start->setCoords(0, 0);
22949  end->setCoords(1, 1);
22950 
22951  setPen(QPen(Qt::black));
22952  setSelectedPen(QPen(Qt::blue, 2));
22953 }
22954 
22956 
22962 void QCPItemLine::setPen(const QPen &pen) { mPen = pen; }
22963 
22969 void QCPItemLine::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
22970 
22981 void QCPItemLine::setHead(const QCPLineEnding &head) { mHead = head; }
22982 
22993 void QCPItemLine::setTail(const QCPLineEnding &tail) { mTail = tail; }
22994 
22995 /* inherits documentation from base class */
22996 double QCPItemLine::selectTest(const QPointF &pos,
22997  bool onlySelectable,
22998  QVariant *details) const {
22999  Q_UNUSED(details)
23000  if (onlySelectable && !mSelectable) return -1;
23001 
23002  return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
23003 }
23004 
23005 /* inherits documentation from base class */
23006 void QCPItemLine::draw(QCPPainter *painter) {
23007  QVector2D startVec(start->pixelPoint());
23008  QVector2D endVec(end->pixelPoint());
23009  if (startVec.toPoint() == endVec.toPoint()) return;
23010  // get visible segment of straight line inside clipRect:
23011  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
23012  clipPad = qMax(clipPad, (double)mainPen().widthF());
23013  QLineF line = getRectClippedLine(
23014  startVec, endVec,
23015  clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
23016  // paint visible segment, if existent:
23017  if (!line.isNull()) {
23018  painter->setPen(mainPen());
23019  painter->drawLine(line);
23020  painter->setBrush(Qt::SolidPattern);
23022  mTail.draw(painter, startVec, startVec - endVec);
23024  mHead.draw(painter, endVec, endVec - startVec);
23025  }
23026 }
23027 
23035 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start,
23036  const QVector2D &end,
23037  const QRect &rect) const {
23038  bool containsStart = rect.contains(start.x(), start.y());
23039  bool containsEnd = rect.contains(end.x(), end.y());
23040  if (containsStart && containsEnd)
23041  return QLineF(start.toPointF(), end.toPointF());
23042 
23043  QVector2D base = start;
23044  QVector2D vec = end - start;
23045  double bx, by;
23046  double gamma, mu;
23047  QLineF result;
23048  QList<QVector2D> pointVectors;
23049 
23050  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
23051  {
23052  // check top of rect:
23053  bx = rect.left();
23054  by = rect.top();
23055  mu = (by - base.y()) / vec.y();
23056  if (mu >= 0 && mu <= 1) {
23057  gamma = base.x() - bx + mu * vec.x();
23058  if (gamma >= 0 && gamma <= rect.width())
23059  pointVectors.append(QVector2D(bx + gamma, by));
23060  }
23061  // check bottom of rect:
23062  bx = rect.left();
23063  by = rect.bottom();
23064  mu = (by - base.y()) / vec.y();
23065  if (mu >= 0 && mu <= 1) {
23066  gamma = base.x() - bx + mu * vec.x();
23067  if (gamma >= 0 && gamma <= rect.width())
23068  pointVectors.append(QVector2D(bx + gamma, by));
23069  }
23070  }
23071  if (!qFuzzyIsNull(vec.x())) // line is not vertical
23072  {
23073  // check left of rect:
23074  bx = rect.left();
23075  by = rect.top();
23076  mu = (bx - base.x()) / vec.x();
23077  if (mu >= 0 && mu <= 1) {
23078  gamma = base.y() - by + mu * vec.y();
23079  if (gamma >= 0 && gamma <= rect.height())
23080  pointVectors.append(QVector2D(bx, by + gamma));
23081  }
23082  // check right of rect:
23083  bx = rect.right();
23084  by = rect.top();
23085  mu = (bx - base.x()) / vec.x();
23086  if (mu >= 0 && mu <= 1) {
23087  gamma = base.y() - by + mu * vec.y();
23088  if (gamma >= 0 && gamma <= rect.height())
23089  pointVectors.append(QVector2D(bx, by + gamma));
23090  }
23091  }
23092 
23093  if (containsStart) pointVectors.append(start);
23094  if (containsEnd) pointVectors.append(end);
23095 
23096  // evaluate points:
23097  if (pointVectors.size() == 2) {
23098  result.setPoints(pointVectors.at(0).toPointF(),
23099  pointVectors.at(1).toPointF());
23100  } else if (pointVectors.size() > 2) {
23101  // line probably goes through corner of rect, and we got two points
23102  // there. single out the point pair with greatest distance:
23103  double distSqrMax = 0;
23104  QVector2D pv1, pv2;
23105  for (int i = 0; i < pointVectors.size() - 1; ++i) {
23106  for (int k = i + 1; k < pointVectors.size(); ++k) {
23107  double distSqr = (pointVectors.at(i) - pointVectors.at(k))
23108  .lengthSquared();
23109  if (distSqr > distSqrMax) {
23110  pv1 = pointVectors.at(i);
23111  pv2 = pointVectors.at(k);
23112  distSqrMax = distSqr;
23113  }
23114  }
23115  }
23116  result.setPoints(pv1.toPointF(), pv2.toPointF());
23117  }
23118  return result;
23119 }
23120 
23126 QPen QCPItemLine::mainPen() const { return mSelected ? mSelectedPen : mPen; }
23127 
23131 
23158  : QCPAbstractItem(parentPlot),
23159  start(createPosition(QLatin1String("start"))),
23160  startDir(createPosition(QLatin1String("startDir"))),
23161  endDir(createPosition(QLatin1String("endDir"))),
23162  end(createPosition(QLatin1String("end"))) {
23163  start->setCoords(0, 0);
23164  startDir->setCoords(0.5, 0);
23165  endDir->setCoords(0, 0.5);
23166  end->setCoords(1, 1);
23167 
23168  setPen(QPen(Qt::black));
23169  setSelectedPen(QPen(Qt::blue, 2));
23170 }
23171 
23173 
23179 void QCPItemCurve::setPen(const QPen &pen) { mPen = pen; }
23180 
23186 void QCPItemCurve::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
23187 
23198 void QCPItemCurve::setHead(const QCPLineEnding &head) { mHead = head; }
23199 
23210 void QCPItemCurve::setTail(const QCPLineEnding &tail) { mTail = tail; }
23211 
23212 /* inherits documentation from base class */
23213 double QCPItemCurve::selectTest(const QPointF &pos,
23214  bool onlySelectable,
23215  QVariant *details) const {
23216  Q_UNUSED(details)
23217  if (onlySelectable && !mSelectable) return -1;
23218 
23219  QPointF startVec(start->pixelPoint());
23220  QPointF startDirVec(startDir->pixelPoint());
23221  QPointF endDirVec(endDir->pixelPoint());
23222  QPointF endVec(end->pixelPoint());
23223 
23224  QPainterPath cubicPath(startVec);
23225  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
23226 
23227  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
23228  double minDistSqr = std::numeric_limits<double>::max();
23229  for (int i = 1; i < polygon.size(); ++i) {
23230  double distSqr = distSqrToLine(polygon.at(i - 1), polygon.at(i), pos);
23231  if (distSqr < minDistSqr) minDistSqr = distSqr;
23232  }
23233  return qSqrt(minDistSqr);
23234 }
23235 
23236 /* inherits documentation from base class */
23237 void QCPItemCurve::draw(QCPPainter *painter) {
23238  QPointF startVec(start->pixelPoint());
23239  QPointF startDirVec(startDir->pixelPoint());
23240  QPointF endDirVec(endDir->pixelPoint());
23241  QPointF endVec(end->pixelPoint());
23242  if (QVector2D(endVec - startVec).length() >
23243  1e10f) // too large curves cause crash
23244  return;
23245 
23246  QPainterPath cubicPath(startVec);
23247  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
23248 
23249  // paint visible segment, if existent:
23250  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(),
23251  mainPen().widthF(), mainPen().widthF());
23252  QRect cubicRect = cubicPath.controlPointRect().toRect();
23253  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x
23254  // or y position
23255  cubicRect.adjust(0, 0, 1, 1);
23256  if (clip.intersects(cubicRect)) {
23257  painter->setPen(mainPen());
23258  painter->drawPath(cubicPath);
23259  painter->setBrush(Qt::SolidPattern);
23261  mTail.draw(painter, QVector2D(startVec),
23262  M_PI - cubicPath.angleAtPercent(0) / 180.0 * M_PI);
23264  mHead.draw(painter, QVector2D(endVec),
23265  -cubicPath.angleAtPercent(1) / 180.0 * M_PI);
23266  }
23267 }
23268 
23274 QPen QCPItemCurve::mainPen() const { return mSelected ? mSelectedPen : mPen; }
23275 
23279 
23296  : QCPAbstractItem(parentPlot),
23297  topLeft(createPosition(QLatin1String("topLeft"))),
23298  bottomRight(createPosition(QLatin1String("bottomRight"))),
23299  top(createAnchor(QLatin1String("top"), aiTop)),
23300  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
23301  right(createAnchor(QLatin1String("right"), aiRight)),
23302  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
23303  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
23304  left(createAnchor(QLatin1String("left"), aiLeft)) {
23305  topLeft->setCoords(0, 1);
23306  bottomRight->setCoords(1, 0);
23307 
23308  setPen(QPen(Qt::black));
23309  setSelectedPen(QPen(Qt::blue, 2));
23310  setBrush(Qt::NoBrush);
23311  setSelectedBrush(Qt::NoBrush);
23312 }
23313 
23315 
23321 void QCPItemRect::setPen(const QPen &pen) { mPen = pen; }
23322 
23328 void QCPItemRect::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
23329 
23336 void QCPItemRect::setBrush(const QBrush &brush) { mBrush = brush; }
23337 
23344 void QCPItemRect::setSelectedBrush(const QBrush &brush) {
23346 }
23347 
23348 /* inherits documentation from base class */
23349 double QCPItemRect::selectTest(const QPointF &pos,
23350  bool onlySelectable,
23351  QVariant *details) const {
23352  Q_UNUSED(details)
23353  if (onlySelectable && !mSelectable) return -1;
23354 
23355  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint())
23356  .normalized();
23357  bool filledRect =
23358  mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
23359  return rectSelectTest(rect, pos, filledRect);
23360 }
23361 
23362 /* inherits documentation from base class */
23363 void QCPItemRect::draw(QCPPainter *painter) {
23364  QPointF p1 = topLeft->pixelPoint();
23365  QPointF p2 = bottomRight->pixelPoint();
23366  if (p1.toPoint() == p2.toPoint()) return;
23367  QRectF rect = QRectF(p1, p2).normalized();
23368  double clipPad = mainPen().widthF();
23369  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
23370  if (boundingRect.intersects(
23371  clipRect())) // only draw if bounding rect of rect item is
23372  // visible in cliprect
23373  {
23374  painter->setPen(mainPen());
23375  painter->setBrush(mainBrush());
23376  painter->drawRect(rect);
23377  }
23378 }
23379 
23380 /* inherits documentation from base class */
23381 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const {
23382  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
23383  switch (anchorId) {
23384  case aiTop:
23385  return (rect.topLeft() + rect.topRight()) * 0.5;
23386  case aiTopRight:
23387  return rect.topRight();
23388  case aiRight:
23389  return (rect.topRight() + rect.bottomRight()) * 0.5;
23390  case aiBottom:
23391  return (rect.bottomLeft() + rect.bottomRight()) * 0.5;
23392  case aiBottomLeft:
23393  return rect.bottomLeft();
23394  case aiLeft:
23395  return (rect.topLeft() + rect.bottomLeft()) * 0.5;
23396  }
23397 
23398  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23399  return QPointF();
23400 }
23401 
23407 QPen QCPItemRect::mainPen() const { return mSelected ? mSelectedPen : mPen; }
23408 
23414 QBrush QCPItemRect::mainBrush() const {
23415  return mSelected ? mSelectedBrush : mBrush;
23416 }
23417 
23421 
23444  : QCPAbstractItem(parentPlot),
23445  position(createPosition(QLatin1String("position"))),
23446  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
23447  top(createAnchor(QLatin1String("top"), aiTop)),
23448  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
23449  right(createAnchor(QLatin1String("right"), aiRight)),
23450  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
23451  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
23452  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
23453  left(createAnchor(QLatin1String("left"), aiLeft)) {
23454  position->setCoords(0, 0);
23455 
23456  setRotation(0);
23457  setTextAlignment(Qt::AlignTop | Qt::AlignHCenter);
23458  setPositionAlignment(Qt::AlignCenter);
23459  setText(QLatin1String("text"));
23460 
23461  setPen(Qt::NoPen);
23462  setSelectedPen(Qt::NoPen);
23463  setBrush(Qt::NoBrush);
23464  setSelectedBrush(Qt::NoBrush);
23466  setSelectedColor(Qt::blue);
23467 }
23468 
23470 
23474 void QCPItemText::setColor(const QColor &color) { mColor = color; }
23475 
23479 void QCPItemText::setSelectedColor(const QColor &color) {
23481 }
23482 
23489 void QCPItemText::setPen(const QPen &pen) { mPen = pen; }
23490 
23497 void QCPItemText::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
23498 
23505 void QCPItemText::setBrush(const QBrush &brush) { mBrush = brush; }
23506 
23513 void QCPItemText::setSelectedBrush(const QBrush &brush) {
23515 }
23516 
23522 void QCPItemText::setFont(const QFont &font) { mFont = font; }
23523 
23529 void QCPItemText::setSelectedFont(const QFont &font) { mSelectedFont = font; }
23530 
23537 void QCPItemText::setText(const QString &text) { mText = text; }
23538 
23553  mPositionAlignment = alignment;
23554 }
23555 
23561  mTextAlignment = alignment;
23562 }
23563 
23568 void QCPItemText::setRotation(double degrees) { mRotation = degrees; }
23569 
23575 void QCPItemText::setPadding(const QMargins &padding) { mPadding = padding; }
23576 
23577 /* inherits documentation from base class */
23578 double QCPItemText::selectTest(const QPointF &pos,
23579  bool onlySelectable,
23580  QVariant *details) const {
23581  Q_UNUSED(details)
23582  if (onlySelectable && !mSelectable) return -1;
23583 
23584  // The rect may be rotated, so we transform the actual clicked pos to the
23585  // rotated coordinate system, so we can use the normal rectSelectTest
23586  // function for non-rotated rects:
23587  QPointF positionPixels(position->pixelPoint());
23588  QTransform inputTransform;
23589  inputTransform.translate(positionPixels.x(), positionPixels.y());
23590  inputTransform.rotate(-mRotation);
23591  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
23592  QPointF rotatedPos = inputTransform.map(pos);
23593  QFontMetrics fontMetrics(mFont);
23594  QRect textRect = fontMetrics.boundingRect(
23595  0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, mText);
23596  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(),
23597  mPadding.right(), mPadding.bottom());
23598  QPointF textPos =
23599  getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
23600  textBoxRect.moveTopLeft(textPos.toPoint());
23601 
23602  return rectSelectTest(textBoxRect, rotatedPos, true);
23603 }
23604 
23605 /* inherits documentation from base class */
23606 void QCPItemText::draw(QCPPainter *painter) {
23607  QPointF pos(position->pixelPoint());
23608  QTransform transform = painter->transform();
23609  transform.translate(pos.x(), pos.y());
23610  if (!qFuzzyIsNull(mRotation)) transform.rotate(mRotation);
23611  painter->setFont(mainFont());
23612  QRect textRect = painter->fontMetrics().boundingRect(
23613  0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, mText);
23614  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(),
23615  mPadding.right(), mPadding.bottom());
23616  QPointF textPos =
23617  getTextDrawPoint(QPointF(0, 0), textBoxRect,
23618  mPositionAlignment); // 0, 0 because the transform
23619  // does the translation
23620  textRect.moveTopLeft(textPos.toPoint() +
23621  QPoint(mPadding.left(), mPadding.top()));
23622  textBoxRect.moveTopLeft(textPos.toPoint());
23623  double clipPad = mainPen().widthF();
23624  QRect boundingRect =
23625  textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
23626  if (transform.mapRect(boundingRect)
23627  .intersects(painter->transform().mapRect(clipRect()))) {
23628  painter->setTransform(transform);
23629  if ((mainBrush().style() != Qt::NoBrush &&
23630  mainBrush().color().alpha() != 0) ||
23631  (mainPen().style() != Qt::NoPen &&
23632  mainPen().color().alpha() != 0)) {
23633  painter->setPen(mainPen());
23634  painter->setBrush(mainBrush());
23635  painter->drawRect(textBoxRect);
23636  }
23637  painter->setBrush(Qt::NoBrush);
23638  painter->setPen(QPen(mainColor()));
23639  painter->drawText(textRect, Qt::TextDontClip | mTextAlignment, mText);
23640  }
23641 }
23642 
23643 /* inherits documentation from base class */
23644 QPointF QCPItemText::anchorPixelPoint(int anchorId) const {
23645  // get actual rect points (pretty much copied from draw function):
23646  QPointF pos(position->pixelPoint());
23647  QTransform transform;
23648  transform.translate(pos.x(), pos.y());
23649  if (!qFuzzyIsNull(mRotation)) transform.rotate(mRotation);
23650  QFontMetrics fontMetrics(mainFont());
23651  QRect textRect = fontMetrics.boundingRect(
23652  0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, mText);
23653  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(),
23654  mPadding.right(), mPadding.bottom());
23655  QPointF textPos =
23656  getTextDrawPoint(QPointF(0, 0), textBoxRect,
23657  mPositionAlignment); // 0, 0 because the transform
23658  // does the translation
23659  textBoxRect.moveTopLeft(textPos.toPoint());
23660  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
23661 
23662  switch (anchorId) {
23663  case aiTopLeft:
23664  return rectPoly.at(0);
23665  case aiTop:
23666  return (rectPoly.at(0) + rectPoly.at(1)) * 0.5;
23667  case aiTopRight:
23668  return rectPoly.at(1);
23669  case aiRight:
23670  return (rectPoly.at(1) + rectPoly.at(2)) * 0.5;
23671  case aiBottomRight:
23672  return rectPoly.at(2);
23673  case aiBottom:
23674  return (rectPoly.at(2) + rectPoly.at(3)) * 0.5;
23675  case aiBottomLeft:
23676  return rectPoly.at(3);
23677  case aiLeft:
23678  return (rectPoly.at(3) + rectPoly.at(0)) * 0.5;
23679  }
23680 
23681  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23682  return QPointF();
23683 }
23684 
23696 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos,
23697  const QRectF &rect,
23698  Qt::Alignment positionAlignment) const {
23699  if (positionAlignment == 0 ||
23700  positionAlignment == (Qt::AlignLeft | Qt::AlignTop))
23701  return pos;
23702 
23703  QPointF result = pos; // start at top left
23704  if (positionAlignment.testFlag(Qt::AlignHCenter))
23705  result.rx() -= rect.width() / 2.0;
23706  else if (positionAlignment.testFlag(Qt::AlignRight))
23707  result.rx() -= rect.width();
23708  if (positionAlignment.testFlag(Qt::AlignVCenter))
23709  result.ry() -= rect.height() / 2.0;
23710  else if (positionAlignment.testFlag(Qt::AlignBottom))
23711  result.ry() -= rect.height();
23712  return result;
23713 }
23714 
23720 QFont QCPItemText::mainFont() const {
23721  return mSelected ? mSelectedFont : mFont;
23722 }
23723 
23729 QColor QCPItemText::mainColor() const {
23730  return mSelected ? mSelectedColor : mColor;
23731 }
23732 
23738 QPen QCPItemText::mainPen() const { return mSelected ? mSelectedPen : mPen; }
23739 
23745 QBrush QCPItemText::mainBrush() const {
23746  return mSelected ? mSelectedBrush : mBrush;
23747 }
23748 
23752 
23769  : QCPAbstractItem(parentPlot),
23770  topLeft(createPosition(QLatin1String("topLeft"))),
23771  bottomRight(createPosition(QLatin1String("bottomRight"))),
23772  topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
23773  top(createAnchor(QLatin1String("top"), aiTop)),
23774  topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
23775  right(createAnchor(QLatin1String("right"), aiRight)),
23776  bottomRightRim(
23777  createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
23778  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
23779  bottomLeftRim(
23780  createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
23781  left(createAnchor(QLatin1String("left"), aiLeft)),
23782  center(createAnchor(QLatin1String("center"), aiCenter)) {
23783  topLeft->setCoords(0, 1);
23784  bottomRight->setCoords(1, 0);
23785 
23786  setPen(QPen(Qt::black));
23787  setSelectedPen(QPen(Qt::blue, 2));
23788  setBrush(Qt::NoBrush);
23789  setSelectedBrush(Qt::NoBrush);
23790 }
23791 
23793 
23799 void QCPItemEllipse::setPen(const QPen &pen) { mPen = pen; }
23800 
23806 void QCPItemEllipse::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
23807 
23814 void QCPItemEllipse::setBrush(const QBrush &brush) { mBrush = brush; }
23815 
23822 void QCPItemEllipse::setSelectedBrush(const QBrush &brush) {
23824 }
23825 
23826 /* inherits documentation from base class */
23827 double QCPItemEllipse::selectTest(const QPointF &pos,
23828  bool onlySelectable,
23829  QVariant *details) const {
23830  Q_UNUSED(details)
23831  if (onlySelectable && !mSelectable) return -1;
23832 
23833  double result = -1;
23834  QPointF p1 = topLeft->pixelPoint();
23835  QPointF p2 = bottomRight->pixelPoint();
23836  QPointF center((p1 + p2) / 2.0);
23837  double a = qAbs(p1.x() - p2.x()) / 2.0;
23838  double b = qAbs(p1.y() - p2.y()) / 2.0;
23839  double x = pos.x() - center.x();
23840  double y = pos.y() - center.y();
23841 
23842  // distance to border:
23843  double c = 1.0 / qSqrt(x * x / (a * a) + y * y / (b * b));
23844  result = qAbs(c - 1) * qSqrt(x * x + y * y);
23845  // filled ellipse, allow click inside to count as hit:
23846  if (result > mParentPlot->selectionTolerance() * 0.99 &&
23847  mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0) {
23848  if (x * x / (a * a) + y * y / (b * b) <= 1)
23850  }
23851  return result;
23852 }
23853 
23854 /* inherits documentation from base class */
23855 void QCPItemEllipse::draw(QCPPainter *painter) {
23856  QPointF p1 = topLeft->pixelPoint();
23857  QPointF p2 = bottomRight->pixelPoint();
23858  if (p1.toPoint() == p2.toPoint()) return;
23859  QRectF ellipseRect = QRectF(p1, p2).normalized();
23860  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(),
23861  mainPen().widthF(), mainPen().widthF());
23862  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse
23863  // is visible in cliprect
23864  {
23865  painter->setPen(mainPen());
23866  painter->setBrush(mainBrush());
23867 #ifdef __EXCEPTIONS
23868  try // drawEllipse sometimes throws exceptions if ellipse is too big
23869  {
23870 #endif
23871  painter->drawEllipse(ellipseRect);
23872 #ifdef __EXCEPTIONS
23873  } catch (...) {
23874  qDebug() << Q_FUNC_INFO
23875  << "Item too large for memory, setting invisible";
23876  setVisible(false);
23877  }
23878 #endif
23879  }
23880 }
23881 
23882 /* inherits documentation from base class */
23883 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const {
23884  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
23885  switch (anchorId) {
23886  case aiTopLeftRim:
23887  return rect.center() +
23888  (rect.topLeft() - rect.center()) * 1 / qSqrt(2);
23889  case aiTop:
23890  return (rect.topLeft() + rect.topRight()) * 0.5;
23891  case aiTopRightRim:
23892  return rect.center() +
23893  (rect.topRight() - rect.center()) * 1 / qSqrt(2);
23894  case aiRight:
23895  return (rect.topRight() + rect.bottomRight()) * 0.5;
23896  case aiBottomRightRim:
23897  return rect.center() +
23898  (rect.bottomRight() - rect.center()) * 1 / qSqrt(2);
23899  case aiBottom:
23900  return (rect.bottomLeft() + rect.bottomRight()) * 0.5;
23901  case aiBottomLeftRim:
23902  return rect.center() +
23903  (rect.bottomLeft() - rect.center()) * 1 / qSqrt(2);
23904  case aiLeft:
23905  return (rect.topLeft() + rect.bottomLeft()) * 0.5;
23906  case aiCenter:
23907  return (rect.topLeft() + rect.bottomRight()) * 0.5;
23908  }
23909 
23910  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23911  return QPointF();
23912 }
23913 
23919 QPen QCPItemEllipse::mainPen() const { return mSelected ? mSelectedPen : mPen; }
23920 
23926 QBrush QCPItemEllipse::mainBrush() const {
23927  return mSelected ? mSelectedBrush : mBrush;
23928 }
23929 
23933 
23956  : QCPAbstractItem(parentPlot),
23957  topLeft(createPosition(QLatin1String("topLeft"))),
23958  bottomRight(createPosition(QLatin1String("bottomRight"))),
23959  top(createAnchor(QLatin1String("top"), aiTop)),
23960  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
23961  right(createAnchor(QLatin1String("right"), aiRight)),
23962  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
23963  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
23964  left(createAnchor(QLatin1String("left"), aiLeft)) {
23965  topLeft->setCoords(0, 1);
23966  bottomRight->setCoords(1, 0);
23967 
23968  setPen(Qt::NoPen);
23969  setSelectedPen(QPen(Qt::blue));
23970  setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
23971 }
23972 
23974 
23978 void QCPItemPixmap::setPixmap(const QPixmap &pixmap) {
23979  mPixmap = pixmap;
23980  if (mPixmap.isNull()) qDebug() << Q_FUNC_INFO << "pixmap is null";
23981 }
23982 
23987 void QCPItemPixmap::setScaled(bool scaled,
23988  Qt::AspectRatioMode aspectRatioMode,
23989  Qt::TransformationMode transformationMode) {
23990  mScaled = scaled;
23994 }
23995 
24001 void QCPItemPixmap::setPen(const QPen &pen) { mPen = pen; }
24002 
24009 void QCPItemPixmap::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
24010 
24011 /* inherits documentation from base class */
24012 double QCPItemPixmap::selectTest(const QPointF &pos,
24013  bool onlySelectable,
24014  QVariant *details) const {
24015  Q_UNUSED(details)
24016  if (onlySelectable && !mSelectable) return -1;
24017 
24018  return rectSelectTest(getFinalRect(), pos, true);
24019 }
24020 
24021 /* inherits documentation from base class */
24022 void QCPItemPixmap::draw(QCPPainter *painter) {
24023  bool flipHorz = false;
24024  bool flipVert = false;
24025  QRect rect = getFinalRect(&flipHorz, &flipVert);
24026  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
24027  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
24028  if (boundingRect.intersects(clipRect())) {
24029  updateScaledPixmap(rect, flipHorz, flipVert);
24030  painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
24031  QPen pen = mainPen();
24032  if (pen.style() != Qt::NoPen) {
24033  painter->setPen(pen);
24034  painter->setBrush(Qt::NoBrush);
24035  painter->drawRect(rect);
24036  }
24037  }
24038 }
24039 
24040 /* inherits documentation from base class */
24041 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const {
24042  bool flipHorz;
24043  bool flipVert;
24044  QRect rect = getFinalRect(&flipHorz, &flipVert);
24045  // we actually want denormal rects (negative width/height) here, so restore
24046  // the flipped state:
24047  if (flipHorz) rect.adjust(rect.width(), 0, -rect.width(), 0);
24048  if (flipVert) rect.adjust(0, rect.height(), 0, -rect.height());
24049 
24050  switch (anchorId) {
24051  case aiTop:
24052  return (rect.topLeft() + rect.topRight()) * 0.5;
24053  case aiTopRight:
24054  return rect.topRight();
24055  case aiRight:
24056  return (rect.topRight() + rect.bottomRight()) * 0.5;
24057  case aiBottom:
24058  return (rect.bottomLeft() + rect.bottomRight()) * 0.5;
24059  case aiBottomLeft:
24060  return rect.bottomLeft();
24061  case aiLeft:
24062  return (rect.topLeft() + rect.bottomLeft()) * 0.5;
24063  ;
24064  }
24065 
24066  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
24067  return QPointF();
24068 }
24069 
24083 void QCPItemPixmap::updateScaledPixmap(QRect finalRect,
24084  bool flipHorz,
24085  bool flipVert) {
24086  if (mPixmap.isNull()) return;
24087 
24088  if (mScaled) {
24089  if (finalRect.isNull()) finalRect = getFinalRect(&flipHorz, &flipVert);
24090  if (finalRect.size() != mScaledPixmap.size()) {
24091  mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode,
24093  if (flipHorz || flipVert)
24094  mScaledPixmap = QPixmap::fromImage(
24095  mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
24096  }
24097  } else if (!mScaledPixmap.isNull())
24098  mScaledPixmap = QPixmap();
24099 }
24100 
24117 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const {
24118  QRect result;
24119  bool flipHorz = false;
24120  bool flipVert = false;
24121  QPoint p1 = topLeft->pixelPoint().toPoint();
24122  QPoint p2 = bottomRight->pixelPoint().toPoint();
24123  if (p1 == p2) return QRect(p1, QSize(0, 0));
24124  if (mScaled) {
24125  QSize newSize = QSize(p2.x() - p1.x(), p2.y() - p1.y());
24126  QPoint topLeft = p1;
24127  if (newSize.width() < 0) {
24128  flipHorz = true;
24129  newSize.rwidth() *= -1;
24130  topLeft.setX(p2.x());
24131  }
24132  if (newSize.height() < 0) {
24133  flipVert = true;
24134  newSize.rheight() *= -1;
24135  topLeft.setY(p2.y());
24136  }
24137  QSize scaledSize = mPixmap.size();
24138  scaledSize.scale(newSize, mAspectRatioMode);
24139  result = QRect(topLeft, scaledSize);
24140  } else {
24141  result = QRect(p1, mPixmap.size());
24142  }
24143  if (flippedHorz) *flippedHorz = flipHorz;
24144  if (flippedVert) *flippedVert = flipVert;
24145  return result;
24146 }
24147 
24153 QPen QCPItemPixmap::mainPen() const { return mSelected ? mSelectedPen : mPen; }
24154 
24158 
24198  : QCPAbstractItem(parentPlot),
24199  position(createPosition(QLatin1String("position"))),
24200  mGraph(0) {
24201  position->setCoords(0, 0);
24202 
24203  setBrush(Qt::NoBrush);
24204  setSelectedBrush(Qt::NoBrush);
24205  setPen(QPen(Qt::black));
24206  setSelectedPen(QPen(Qt::blue, 2));
24207  setStyle(tsCrosshair);
24208  setSize(6);
24209  setInterpolating(false);
24210  setGraphKey(0);
24211 }
24212 
24214 
24220 void QCPItemTracer::setPen(const QPen &pen) { mPen = pen; }
24221 
24227 void QCPItemTracer::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
24228 
24234 void QCPItemTracer::setBrush(const QBrush &brush) { mBrush = brush; }
24235 
24242 void QCPItemTracer::setSelectedBrush(const QBrush &brush) {
24244 }
24245 
24250 void QCPItemTracer::setSize(double size) { mSize = size; }
24251 
24259  mStyle = style;
24260 }
24261 
24273 void QCPItemTracer::setGraph(QCPGraph *graph) {
24274  if (graph) {
24275  if (graph->parentPlot() == mParentPlot) {
24278  mGraph = graph;
24279  updatePosition();
24280  } else
24281  qDebug() << Q_FUNC_INFO
24282  << "graph isn't in same QCustomPlot instance as this item";
24283  } else {
24284  mGraph = 0;
24285  }
24286 }
24287 
24298 void QCPItemTracer::setGraphKey(double key) { mGraphKey = key; }
24299 
24312 void QCPItemTracer::setInterpolating(bool enabled) { mInterpolating = enabled; }
24313 
24314 /* inherits documentation from base class */
24315 double QCPItemTracer::selectTest(const QPointF &pos,
24316  bool onlySelectable,
24317  QVariant *details) const {
24318  Q_UNUSED(details)
24319  if (onlySelectable && !mSelectable) return -1;
24320 
24321  QPointF center(position->pixelPoint());
24322  double w = mSize / 2.0;
24323  QRect clip = clipRect();
24324  switch (mStyle) {
24325  case tsNone:
24326  return -1;
24327  case tsPlus: {
24328  if (clipRect().intersects(
24329  QRectF(center - QPointF(w, w), center + QPointF(w, w))
24330  .toRect()))
24331  return qSqrt(qMin(distSqrToLine(center + QPointF(-w, 0),
24332  center + QPointF(w, 0), pos),
24333  distSqrToLine(center + QPointF(0, -w),
24334  center + QPointF(0, w), pos)));
24335  break;
24336  }
24337  case tsCrosshair: {
24338  return qSqrt(qMin(
24339  distSqrToLine(QPointF(clip.left(), center.y()),
24340  QPointF(clip.right(), center.y()), pos),
24341  distSqrToLine(QPointF(center.x(), clip.top()),
24342  QPointF(center.x(), clip.bottom()), pos)));
24343  }
24344  case tsCircle: {
24345  if (clip.intersects(
24346  QRectF(center - QPointF(w, w), center + QPointF(w, w))
24347  .toRect())) {
24348  // distance to border:
24349  double centerDist = QVector2D(center - pos).length();
24350  double circleLine = w;
24351  double result = qAbs(centerDist - circleLine);
24352  // filled ellipse, allow click inside to count as hit:
24353  if (result > mParentPlot->selectionTolerance() * 0.99 &&
24354  mBrush.style() != Qt::NoBrush &&
24355  mBrush.color().alpha() != 0) {
24356  if (centerDist <= circleLine)
24358  }
24359  return result;
24360  }
24361  break;
24362  }
24363  case tsSquare: {
24364  if (clip.intersects(
24365  QRectF(center - QPointF(w, w), center + QPointF(w, w))
24366  .toRect())) {
24367  QRectF rect =
24368  QRectF(center - QPointF(w, w), center + QPointF(w, w));
24369  bool filledRect = mBrush.style() != Qt::NoBrush &&
24370  mBrush.color().alpha() != 0;
24371  return rectSelectTest(rect, pos, filledRect);
24372  }
24373  break;
24374  }
24375  }
24376  return -1;
24377 }
24378 
24379 /* inherits documentation from base class */
24380 void QCPItemTracer::draw(QCPPainter *painter) {
24381  updatePosition();
24382  if (mStyle == tsNone) return;
24383 
24384  painter->setPen(mainPen());
24385  painter->setBrush(mainBrush());
24386  QPointF center(position->pixelPoint());
24387  double w = mSize / 2.0;
24388  QRect clip = clipRect();
24389  switch (mStyle) {
24390  case tsNone:
24391  return;
24392  case tsPlus: {
24393  if (clip.intersects(
24394  QRectF(center - QPointF(w, w), center + QPointF(w, w))
24395  .toRect())) {
24396  painter->drawLine(QLineF(center + QPointF(-w, 0),
24397  center + QPointF(w, 0)));
24398  painter->drawLine(QLineF(center + QPointF(0, -w),
24399  center + QPointF(0, w)));
24400  }
24401  break;
24402  }
24403  case tsCrosshair: {
24404  if (center.y() > clip.top() && center.y() < clip.bottom())
24405  painter->drawLine(QLineF(clip.left(), center.y(), clip.right(),
24406  center.y()));
24407  if (center.x() > clip.left() && center.x() < clip.right())
24408  painter->drawLine(QLineF(center.x(), clip.top(), center.x(),
24409  clip.bottom()));
24410  break;
24411  }
24412  case tsCircle: {
24413  if (clip.intersects(
24414  QRectF(center - QPointF(w, w), center + QPointF(w, w))
24415  .toRect()))
24416  painter->drawEllipse(center, w, w);
24417  break;
24418  }
24419  case tsSquare: {
24420  if (clip.intersects(
24421  QRectF(center - QPointF(w, w), center + QPointF(w, w))
24422  .toRect()))
24423  painter->drawRect(
24424  QRectF(center - QPointF(w, w), center + QPointF(w, w)));
24425  break;
24426  }
24427  }
24428 }
24429 
24445  if (mGraph) {
24447  if (mGraph->data()->size() > 1) {
24448  QCPDataMap::const_iterator first = mGraph->data()->constBegin();
24449  QCPDataMap::const_iterator last =
24450  mGraph->data()->constEnd() - 1;
24451  if (mGraphKey < first.key())
24452  position->setCoords(first.key(), first.value().value);
24453  else if (mGraphKey > last.key())
24454  position->setCoords(last.key(), last.value().value);
24455  else {
24456  QCPDataMap::const_iterator it =
24457  mGraph->data()->lowerBound(mGraphKey);
24458  if (it !=
24459  first) // mGraphKey is somewhere between iterators
24460  {
24461  QCPDataMap::const_iterator prevIt = it - 1;
24462  if (mInterpolating) {
24463  // interpolate between iterators around mGraphKey:
24464  double slope = 0;
24465  if (!qFuzzyCompare((double)it.key(),
24466  (double)prevIt.key()))
24467  slope = (it.value().value -
24468  prevIt.value().value) /
24469  (it.key() - prevIt.key());
24471  mGraphKey,
24472  (mGraphKey - prevIt.key()) * slope +
24473  prevIt.value().value);
24474  } else {
24475  // find iterator with key closest to mGraphKey:
24476  if (mGraphKey < (prevIt.key() + it.key()) * 0.5)
24477  it = prevIt;
24478  position->setCoords(it.key(), it.value().value);
24479  }
24480  } else // mGraphKey is exactly on first iterator
24481  position->setCoords(it.key(), it.value().value);
24482  }
24483  } else if (mGraph->data()->size() == 1) {
24484  QCPDataMap::const_iterator it = mGraph->data()->constBegin();
24485  position->setCoords(it.key(), it.value().value);
24486  } else
24487  qDebug() << Q_FUNC_INFO << "graph has no data";
24488  } else
24489  qDebug() << Q_FUNC_INFO
24490  << "graph not contained in QCustomPlot instance (anymore)";
24491  }
24492 }
24493 
24499 QPen QCPItemTracer::mainPen() const { return mSelected ? mSelectedPen : mPen; }
24500 
24506 QBrush QCPItemTracer::mainBrush() const {
24507  return mSelected ? mSelectedBrush : mBrush;
24508 }
24509 
24513 
24544  : QCPAbstractItem(parentPlot),
24545  left(createPosition(QLatin1String("left"))),
24546  right(createPosition(QLatin1String("right"))),
24547  center(createAnchor(QLatin1String("center"), aiCenter)) {
24548  left->setCoords(0, 0);
24549  right->setCoords(1, 1);
24550 
24551  setPen(QPen(Qt::black));
24552  setSelectedPen(QPen(Qt::blue, 2));
24553  setLength(8);
24554  setStyle(bsCalligraphic);
24555 }
24556 
24558 
24569 void QCPItemBracket::setPen(const QPen &pen) { mPen = pen; }
24570 
24576 void QCPItemBracket::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
24577 
24588 void QCPItemBracket::setLength(double length) { mLength = length; }
24589 
24596  mStyle = style;
24597 }
24598 
24599 /* inherits documentation from base class */
24600 double QCPItemBracket::selectTest(const QPointF &pos,
24601  bool onlySelectable,
24602  QVariant *details) const {
24603  Q_UNUSED(details)
24604  if (onlySelectable && !mSelectable) return -1;
24605 
24606  QVector2D leftVec(left->pixelPoint());
24607  QVector2D rightVec(right->pixelPoint());
24608  if (leftVec.toPoint() == rightVec.toPoint()) return -1;
24609 
24610  QVector2D widthVec = (rightVec - leftVec) * 0.5f;
24611  QVector2D lengthVec(-widthVec.y(), widthVec.x());
24612  lengthVec = lengthVec.normalized() * mLength;
24613  QVector2D centerVec = (rightVec + leftVec) * 0.5f - lengthVec;
24614 
24615  switch (mStyle) {
24617  case QCPItemBracket::bsRound: {
24618  double a = distSqrToLine((centerVec - widthVec).toPointF(),
24619  (centerVec + widthVec).toPointF(), pos);
24620  double b =
24621  distSqrToLine((centerVec - widthVec + lengthVec).toPointF(),
24622  (centerVec - widthVec).toPointF(), pos);
24623  double c =
24624  distSqrToLine((centerVec + widthVec + lengthVec).toPointF(),
24625  (centerVec + widthVec).toPointF(), pos);
24626  return qSqrt(qMin(qMin(a, b), c));
24627  }
24630  double a = distSqrToLine(
24631  (centerVec - widthVec * 0.75f + lengthVec * 0.15f)
24632  .toPointF(),
24633  (centerVec + lengthVec * 0.3f).toPointF(), pos);
24634  double b = distSqrToLine(
24635  (centerVec - widthVec + lengthVec * 0.7f).toPointF(),
24636  (centerVec - widthVec * 0.75f + lengthVec * 0.15f)
24637  .toPointF(),
24638  pos);
24639  double c = distSqrToLine(
24640  (centerVec + widthVec * 0.75f + lengthVec * 0.15f)
24641  .toPointF(),
24642  (centerVec + lengthVec * 0.3f).toPointF(), pos);
24643  double d = distSqrToLine(
24644  (centerVec + widthVec + lengthVec * 0.7f).toPointF(),
24645  (centerVec + widthVec * 0.75f + lengthVec * 0.15f)
24646  .toPointF(),
24647  pos);
24648  return qSqrt(qMin(qMin(a, b), qMin(c, d)));
24649  }
24650  }
24651  return -1;
24652 }
24653 
24654 /* inherits documentation from base class */
24655 void QCPItemBracket::draw(QCPPainter *painter) {
24656  QVector2D leftVec(left->pixelPoint());
24657  QVector2D rightVec(right->pixelPoint());
24658  if (leftVec.toPoint() == rightVec.toPoint()) return;
24659 
24660  QVector2D widthVec = (rightVec - leftVec) * 0.5f;
24661  QVector2D lengthVec(-widthVec.y(), widthVec.x());
24662  lengthVec = lengthVec.normalized() * mLength;
24663  QVector2D centerVec = (rightVec + leftVec) * 0.5f - lengthVec;
24664 
24665  QPolygon boundingPoly;
24666  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
24667  << (rightVec - lengthVec).toPoint()
24668  << (leftVec - lengthVec).toPoint();
24669  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(),
24670  mainPen().widthF(), mainPen().widthF());
24671  if (clip.intersects(boundingPoly.boundingRect())) {
24672  painter->setPen(mainPen());
24673  switch (mStyle) {
24674  case bsSquare: {
24675  painter->drawLine((centerVec + widthVec).toPointF(),
24676  (centerVec - widthVec).toPointF());
24677  painter->drawLine(
24678  (centerVec + widthVec).toPointF(),
24679  (centerVec + widthVec + lengthVec).toPointF());
24680  painter->drawLine(
24681  (centerVec - widthVec).toPointF(),
24682  (centerVec - widthVec + lengthVec).toPointF());
24683  break;
24684  }
24685  case bsRound: {
24686  painter->setBrush(Qt::NoBrush);
24687  QPainterPath path;
24688  path.moveTo((centerVec + widthVec + lengthVec).toPointF());
24689  path.cubicTo((centerVec + widthVec).toPointF(),
24690  (centerVec + widthVec).toPointF(),
24691  centerVec.toPointF());
24692  path.cubicTo((centerVec - widthVec).toPointF(),
24693  (centerVec - widthVec).toPointF(),
24694  (centerVec - widthVec + lengthVec).toPointF());
24695  painter->drawPath(path);
24696  break;
24697  }
24698  case bsCurly: {
24699  painter->setBrush(Qt::NoBrush);
24700  QPainterPath path;
24701  path.moveTo((centerVec + widthVec + lengthVec).toPointF());
24702  path.cubicTo(
24703  (centerVec + widthVec - lengthVec * 0.8f).toPointF(),
24704  (centerVec + 0.4f * widthVec + lengthVec).toPointF(),
24705  centerVec.toPointF());
24706  path.cubicTo(
24707  (centerVec - 0.4f * widthVec + lengthVec).toPointF(),
24708  (centerVec - widthVec - lengthVec * 0.8f).toPointF(),
24709  (centerVec - widthVec + lengthVec).toPointF());
24710  painter->drawPath(path);
24711  break;
24712  }
24713  case bsCalligraphic: {
24714  painter->setPen(Qt::NoPen);
24715  painter->setBrush(QBrush(mainPen().color()));
24716  QPainterPath path;
24717  path.moveTo((centerVec + widthVec + lengthVec).toPointF());
24718 
24719  path.cubicTo(
24720  (centerVec + widthVec - lengthVec * 0.8f).toPointF(),
24721  (centerVec + 0.4f * widthVec + 0.8f * lengthVec)
24722  .toPointF(),
24723  centerVec.toPointF());
24724  path.cubicTo(
24725  (centerVec - 0.4f * widthVec + 0.8f * lengthVec)
24726  .toPointF(),
24727  (centerVec - widthVec - lengthVec * 0.8f).toPointF(),
24728  (centerVec - widthVec + lengthVec).toPointF());
24729 
24730  path.cubicTo(
24731  (centerVec - widthVec - lengthVec * 0.5f).toPointF(),
24732  (centerVec - 0.2f * widthVec + 1.2f * lengthVec)
24733  .toPointF(),
24734  (centerVec + lengthVec * 0.2f).toPointF());
24735  path.cubicTo(
24736  (centerVec + 0.2f * widthVec + 1.2f * lengthVec)
24737  .toPointF(),
24738  (centerVec + widthVec - lengthVec * 0.5f).toPointF(),
24739  (centerVec + widthVec + lengthVec).toPointF());
24740 
24741  painter->drawPath(path);
24742  break;
24743  }
24744  }
24745  }
24746 }
24747 
24748 /* inherits documentation from base class */
24749 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const {
24750  QVector2D leftVec(left->pixelPoint());
24751  QVector2D rightVec(right->pixelPoint());
24752  if (leftVec.toPoint() == rightVec.toPoint()) return leftVec.toPointF();
24753 
24754  QVector2D widthVec = (rightVec - leftVec) * 0.5f;
24755  QVector2D lengthVec(-widthVec.y(), widthVec.x());
24756  lengthVec = lengthVec.normalized() * mLength;
24757  QVector2D centerVec = (rightVec + leftVec) * 0.5f - lengthVec;
24758 
24759  switch (anchorId) {
24760  case aiCenter:
24761  return centerVec.toPointF();
24762  }
24763  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
24764  return QPointF();
24765 }
24766 
24772 QPen QCPItemBracket::mainPen() const { return mSelected ? mSelectedPen : mPen; }
MouseEvent event
constexpr double M_PI
Pi.
Definition: CVConst.h:19
filament::Texture::InternalFormat format
int width
int size
std::string name
int height
int count
int offset
int points
char type
math::float4 color
math::float3 position
QPointF qtCompatWheelEventPos(const QWheelEvent *event) noexcept
Definition: QtCompat.h:760
void qtCompatMapInsertMulti(QMap< Key, T > *map, const Key &key, const T &value)
Definition: QtCompat.h:854
double qtCompatWheelEventDelta(const QWheelEvent *event) noexcept
Definition: QtCompat.h:751
void qtCompatMapUnite(QMap< Key, T > *map, const QMap< Key, T > &other)
Definition: QtCompat.h:872
boost::geometry::model::polygon< point_xy > polygon
Definition: TreeIso.cpp:37
Eigen::Vector3d origin
Definition: VoxelGridIO.cpp:26
core::Tensor result
Definition: VtkUtils.cpp:76
bool copy
Definition: VtkUtils.cpp:74
The abstract base class for all items in a plot.
Definition: qcustomplot.h:4081
QCPItemAnchor * anchor(const QString &name) const
Q_SLOT void setSelected(bool selected)
QCPItemPosition * position(const QString &name) const
double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
QList< QCPItemAnchor * > mAnchors
Definition: qcustomplot.h:4129
QPointer< QCPAxisRect > mClipAxisRect
Definition: qcustomplot.h:4127
virtual ~QCPAbstractItem()
void setClipToAxisRect(bool clip)
QList< QCPItemPosition * > mPositions
Definition: qcustomplot.h:4128
friend class QCPItemAnchor
Definition: qcustomplot.h:4159
bool clipToAxisRect() const
Definition: qcustomplot.h:4097
virtual QRect clipRect() const
virtual QPointF anchorPixelPoint(int anchorId) const
virtual QCP::Interaction selectionCategory() const
void selectableChanged(bool selectable)
QCPItemPosition * createPosition(const QString &name)
void setClipAxisRect(QCPAxisRect *rect)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
bool hasAnchor(const QString &name) const
Q_SLOT void setSelectable(bool selectable)
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
virtual void deselectEvent(bool *selectionStateChanged)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
QCPAbstractItem(QCustomPlot *parentPlot)
bool selected() const
Definition: qcustomplot.h:4100
void selectionChanged(bool selected)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPAxisRect * clipAxisRect() const
bool selectable() const
Definition: qcustomplot.h:4099
QCPItemAnchor * createAnchor(const QString &name, int anchorId)
The abstract base class for all entries in a QCPLegend.
Definition: qcustomplot.h:5512
void setFont(const QFont &font)
void setSelectedTextColor(const QColor &color)
QFont font() const
Definition: qcustomplot.h:5531
void setTextColor(const QColor &color)
Q_SLOT void setSelected(bool selected)
void selectionChanged(bool selected)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setSelectedFont(const QFont &font)
Q_SLOT void setSelectable(bool selectable)
virtual QCP::Interaction selectionCategory() const
void selectableChanged(bool selectable)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
virtual QRect clipRect() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
bool selected() const
Definition: qcustomplot.h:5536
virtual void deselectEvent(bool *selectionStateChanged)
QCPLegend * mParentLegend
Definition: qcustomplot.h:5557
bool selectable() const
Definition: qcustomplot.h:5535
QCPAbstractLegendItem(QCPLegend *parent)
The abstract base class for all data representing objects in a plot.
Definition: qcustomplot.h:3816
QCP::SelectionType selectable() const
Definition: qcustomplot.h:3847
void applyErrorBarsAntialiasingHint(QCPPainter *painter) const
void setAntialiasedFill(bool enabled)
bool selected() const
Definition: qcustomplot.h:3848
void rescaleAxes(bool onlyEnlarge=false) const
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPAxis * keyAxis() const
Definition: qcustomplot.h:3845
void setAntialiasedScatters(bool enabled)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
void pixelsToCoords(double x, double y, double &key, double &value) const
QCP::SelectionType mSelectable
Definition: qcustomplot.h:3910
void selectionChanged(bool selected)
bool removeFromLegend(QCPLegend *legend) const
friend class QCPPlottableLegendItem
Definition: qcustomplot.h:3940
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const =0
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const =0
QString name() const
Definition: qcustomplot.h:3840
void applyDefaultAntialiasingHint(QCPPainter *painter) const
void selectableChanged(QCP::SelectionType selectable)
void setSelectedPen(const QPen &pen)
virtual void deselectEvent(bool *selectionStateChanged)
void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const
void setValueAxis(QCPAxis *axis)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const =0
void setAntialiasedErrorBars(bool enabled)
void setBrush(const QBrush &brush)
void coordsToPixels(double key, double value, double &x, double &y) const
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:3909
void setKeyAxis(QCPAxis *axis)
QBrush brush() const
Definition: qcustomplot.h:3844
void applyFillAntialiasingHint(QCPPainter *painter) const
bool addToLegend(QCPLegend *legend)
virtual QRect clipRect() const
void setPen(const QPen &pen)
void setName(const QString &name)
Q_SLOT void setSelectable(QCP::SelectionType selectable)
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:3909
QBrush mainBrush() const
void applyScattersAntialiasingHint(QCPPainter *painter) const
bool removeFromLegend() const
void setSelectedBrush(const QBrush &brush)
void rescaleKeyAxis(bool onlyEnlarge=false) const
QCPAxis * valueAxis() const
Definition: qcustomplot.h:3846
QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
virtual QCP::Interaction selectionCategory() const
Q_SLOT void setSelected(bool selected)
Holds multiple axes and arranges them in a rectangular shape.
Definition: qcustomplot.h:5375
QPoint mDragStart
Definition: qcustomplot.h:2519
QList< QCPAbstractItem * > items() const
bool removeAxis(QCPAxis *axis)
QList< QCPAxis * > axes() const
int width() const
Definition: qcustomplot.h:5449
Qt::Orientations mRangeZoom
Definition: qcustomplot.h:5471
virtual void update(UpdatePhase phase)
QList< QCPRange > mDragStartHorzRange
Definition: qcustomplot.h:5477
QList< QCPGraph * > graphs() const
QCPAxis * addAxis(QCPAxis::AxisType type, QCPAxis *axis=0)
double mRangeZoomFactorVert
Definition: qcustomplot.h:5474
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:5466
QList< QPointer< QCPAxis > > mRangeDragVertAxis
Definition: qcustomplot.h:5472
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
int right() const
Definition: qcustomplot.h:5446
virtual QList< QCPLayoutElement * > elements(bool recursive) const
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details)
int top() const
Definition: qcustomplot.h:5447
virtual ~QCPAxisRect()
QBrush mBackgroundBrush
Definition: qcustomplot.h:5465
QCPAxis * axis(QCPAxis::AxisType type, int index=0) const
QList< QCPAbstractPlottable * > plottables() const
virtual void wheelEvent(QWheelEvent *event)
bool mBackgroundScaled
Definition: qcustomplot.h:5468
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
void setupFullAxesBox(bool connectRanges=false)
void updateAxesOffset(QCPAxis::AxisType type)
void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
QCPAxis * rangeZoomAxis(Qt::Orientation orientation)
QCPAxis * rangeDragAxis(Qt::Orientation orientation)
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:5478
QList< QCPAxis * > addAxes(QCPAxis::AxisTypes types)
void setRangeZoom(Qt::Orientations orientations)
QSize size() const
Definition: qcustomplot.h:5451
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:5469
int axisCount(QCPAxis::AxisType type) const
void setRangeZoomFactor(double horizontalFactor, double verticalFactor)
QList< QCPAxis * > axes(QCPAxis::AxisTypes types) const
void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
QCPLayoutInset * insetLayout() const
Definition: qcustomplot.h:5435
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
QList< QPointer< QCPAxis > > mRangeZoomHorzAxis
Definition: qcustomplot.h:5473
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:5478
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:5467
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
Qt::Orientations mRangeDrag
Definition: qcustomplot.h:5471
QList< QPointer< QCPAxis > > mRangeZoomVertAxis
Definition: qcustomplot.h:5473
QList< QCPRange > mDragStartVertRange
Definition: qcustomplot.h:5477
void drawBackground(QCPPainter *painter)
QList< QPointer< QCPAxis > > mRangeDragHorzAxis
Definition: qcustomplot.h:5472
int height() const
Definition: qcustomplot.h:5450
int bottom() const
Definition: qcustomplot.h:5448
double mRangeZoomFactorHorz
Definition: qcustomplot.h:5474
QHash< QCPAxis::AxisType, QList< QCPAxis * > > mAxes
Definition: qcustomplot.h:5480
double rangeZoomFactor(Qt::Orientation orientation)
void setRangeDrag(Qt::Orientations orientations)
void setBackgroundScaled(bool scaled)
virtual int calculateAutoMargin(QCP::MarginSide side)
QCPLayoutInset * mInsetLayout
Definition: qcustomplot.h:5470
void setBackground(const QPixmap &pm)
virtual void draw(QCPPainter *painter)
int left() const
Definition: qcustomplot.h:5445
Manages a single axis inside a QCustomPlot.
Definition: qcustomplot.h:2254
void setSelectedLabelFont(const QFont &font)
void setOffset(int offset)
virtual QCP::Interaction selectionCategory() const
void setTickLabels(bool show)
int padding() const
Definition: qcustomplot.h:2415
void rangeChanged(const QCPRange &newRange)
void setLowerEnding(const QCPLineEnding &ending)
QString mDateTimeFormat
Definition: qcustomplot.h:1526
LabelType tickLabelType() const
Definition: qcustomplot.h:1373
QCPLineEnding lowerEnding() const
QCPGrid * mGrid
Definition: qcustomplot.h:2552
void setTickLabelSide(LabelSide side)
bool autoTickStep() const
Definition: qcustomplot.h:1368
QVector< double > mSubTickVector
Definition: qcustomplot.h:2557
void moveRange(double diff)
void setTickLabelRotation(double degrees)
QPen mTickPen
Definition: qcustomplot.h:2544
LabelSide tickLabelSide() const
QCPRange mRange
Definition: qcustomplot.h:2547
QString numberFormat() const
void setRangeReversed(bool reversed)
void setNumberPrecision(int precision)
SelectablePart getPartAt(const QPointF &pos) const
@ lsOutside
Tick labels will be displayed outside the axis rect.
Definition: qcustomplot.h:2349
int numberPrecision() const
Definition: qcustomplot.h:2400
virtual void draw(QCPPainter *painter)
void setDateTimeSpec(const Qt::TimeSpec &timeSpec)
void setSelectedSubTickPen(const QPen &pen)
void setTickLabelFont(const QFont &font)
QString dateTimeFormat() const
Definition: qcustomplot.h:1378
bool mCachedMarginValid
Definition: qcustomplot.h:2558
void setDateTimeFormat(const QString &format)
void scaleRange(double factor)
QPen mSubTickPen
Definition: qcustomplot.h:2545
void setLabel(const QString &str)
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
@ stLinear
Linear scaling.
Definition: qcustomplot.h:2357
@ stLogarithmic
Definition: qcustomplot.h:2359
QFont mLabelFont
Definition: qcustomplot.h:2527
QLatin1Char mNumberFormatChar
Definition: qcustomplot.h:2536
void setTickLabelColor(const QColor &color)
void setTickLengthOut(int outside)
QColor mSelectedTickLabelColor
Definition: qcustomplot.h:2534
bool mTickLabels
Definition: qcustomplot.h:2531
int autoTickCount() const
Definition: qcustomplot.h:1366
QList< QCPAbstractItem * > items() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setLabelPadding(int padding)
QColor mLabelColor
Definition: qcustomplot.h:2528
virtual int calculateMargin()
int mCachedMargin
Definition: qcustomplot.h:2559
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void rescale(bool onlyVisiblePlottables=false)
int mAutoTickCount
Definition: qcustomplot.h:1535
void setSubTickCount(int count)
void setSubTickLengthOut(int outside)
QFont mSelectedTickLabelFont
Definition: qcustomplot.h:2533
double mTickStep
Definition: qcustomplot.h:1534
int mPadding
Definition: qcustomplot.h:2519
virtual void deselectEvent(bool *selectionStateChanged)
double pixelToCoord(double value) const
void setTickLabelType(LabelType type)
void setPadding(int padding)
void setupTickVectors()
bool ticks() const
Definition: qcustomplot.h:2392
double tickLabelRotation() const
bool mRangeReversed
Definition: qcustomplot.h:2548
void setSelectedLabelColor(const QColor &color)
virtual void generateAutoTicks()
void selectionChanged(const QCPAxis::SelectableParts &parts)
void setTickLength(int inside, int outside=0)
QColor mTickLabelColor
Definition: qcustomplot.h:2534
QCPGrid * grid() const
Definition: qcustomplot.h:2428
void setUpperEnding(const QCPLineEnding &ending)
QFont getTickLabelFont() const
void setLabelColor(const QColor &color)
int labelPadding() const
LabelType mTickLabelType
Definition: qcustomplot.h:1523
QVector< double > mTickVector
Definition: qcustomplot.h:2555
QCPAxisPainterPrivate * mAxisPainter
Definition: qcustomplot.h:2553
void setLabelFont(const QFont &font)
bool mAutoTickLabels
Definition: qcustomplot.h:1521
void setScaleLogBase(double base)
void setBasePen(const QPen &pen)
QCPAxisRect * mAxisRect
Definition: qcustomplot.h:2517
void setAutoTickCount(int approximateCount)
virtual ~QCPAxis()
QPen mSelectedBasePen
Definition: qcustomplot.h:2522
Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts)
void setSelectedTickPen(const QPen &pen)
void setSelectedTickLabelFont(const QFont &font)
void setTickVector(const QVector< double > &vec)
SelectableParts selectedParts() const
Definition: qcustomplot.h:2417
QPen getBasePen() const
QColor getTickLabelColor() const
double scaleLogBase() const
Definition: qcustomplot.h:1362
SelectableParts mSelectedParts
Definition: qcustomplot.h:2521
void setTickVectorLabels(const QVector< QString > &vec)
double mScaleLogBaseLogInv
Definition: qcustomplot.h:1545
QColor mSelectedLabelColor
Definition: qcustomplot.h:2528
double tickStep() const
Definition: qcustomplot.h:1382
QPen mSelectedTickPen
Definition: qcustomplot.h:2544
void setAutoTickStep(bool on)
void setSelectedTickLabelColor(const QColor &color)
QCPLineEnding upperEnding() const
AxisType axisType() const
Definition: qcustomplot.h:2386
QPen mSelectedSubTickPen
Definition: qcustomplot.h:2545
void selectableChanged(const QCPAxis::SelectableParts &parts)
static AxisType opposite(AxisType type)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
void setAutoTickLabels(bool on)
double basePow(double value) const
bool mAutoSubTicks
Definition: qcustomplot.h:1536
bool mAutoTicks
Definition: qcustomplot.h:1536
QPen getSubTickPen() const
bool mAutoTickStep
Definition: qcustomplot.h:1536
bool mTicks
Definition: qcustomplot.h:2540
Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts)
void setSubTickLength(int inside, int outside=0)
SelectableParts mSelectableParts
Definition: qcustomplot.h:2521
bool rangeReversed() const
Definition: qcustomplot.h:2390
Qt::Orientation orientation() const
Definition: qcustomplot.h:2483
int subTickCount() const
Definition: qcustomplot.h:1387
int mHighestVisibleTick
Definition: qcustomplot.h:1550
double mScaleLogBase
Definition: qcustomplot.h:1545
@ spAxis
The axis backbone and tick marks.
Definition: qcustomplot.h:2371
@ spAxisLabel
The axis label.
Definition: qcustomplot.h:2376
@ spNone
None of the selectable parts.
Definition: qcustomplot.h:2369
@ spTickLabels
Definition: qcustomplot.h:2373
static AxisType marginSideToAxisType(QCP::MarginSide side)
const QCPRange range() const
Definition: qcustomplot.h:2389
void setSubTickLengthIn(int inside)
QList< QCPAbstractPlottable * > plottables() const
QCPAxis(QCPAxisRect *parent, AxisType type)
void setTicks(bool show)
int subTickLengthOut() const
QVector< QString > mTickVectorLabels
Definition: qcustomplot.h:2556
virtual int calculateAutoSubTickCount(double tickStep) const
void setRangeUpper(double upper)
int mNumberPrecision
Definition: qcustomplot.h:2535
int tickLengthIn() const
ScaleType scaleType() const
Definition: qcustomplot.h:2388
int tickLengthOut() const
QList< QCPGraph * > graphs() const
QPen mBasePen
Definition: qcustomplot.h:2522
int mSubTickCount
Definition: qcustomplot.h:1535
ScaleType mScaleType
Definition: qcustomplot.h:2549
void setTickPen(const QPen &pen)
void setAutoSubTicks(bool on)
QFont mTickLabelFont
Definition: qcustomplot.h:2533
Q_SLOT void setScaleType(QCPAxis::ScaleType type)
bool autoSubTicks() const
Definition: qcustomplot.h:1369
bool tickLabels() const
Definition: qcustomplot.h:2393
QFont mSelectedLabelFont
Definition: qcustomplot.h:2527
void setNumberFormat(const QString &formatCode)
AxisType mAxisType
Definition: qcustomplot.h:2516
double baseLog(double value) const
QString mLabel
Definition: qcustomplot.h:2526
void setAutoTicks(bool on)
QColor getLabelColor() const
Qt::TimeSpec dateTimeSpec() const
Definition: qcustomplot.h:1379
QFont getLabelFont() const
void setSelectedBasePen(const QPen &pen)
int mLowestVisibleTick
Definition: qcustomplot.h:1550
Q_SLOT void setRange(const QCPRange &range)
void visibleTickBounds(int &lowIndex, int &highIndex) const
void setSubTickPen(const QPen &pen)
int offset() const
bool mNumberBeautifulPowers
Definition: qcustomplot.h:2537
double coordToPixel(double value) const
void setTickLabelPadding(int padding)
void ticksRequest()
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
void setTickStep(double step)
Qt::TimeSpec mDateTimeSpec
Definition: qcustomplot.h:1527
int subTickLengthIn() const
int tickLabelPadding() const
void setTickLengthIn(int inside)
QCPAxisRect * axisRect() const
Definition: qcustomplot.h:2387
void setRangeLower(double lower)
QPen getTickPen() const
Holds the data of one single data point (one bar) for QCPBars.
Definition: qcustomplot.h:3398
double value
Definition: qcustomplot.h:3402
double key
Definition: qcustomplot.h:3402
Groups multiple QCPBars together so they appear side by side.
Definition: qcustomplot.h:6345
double getPixelSpacing(const QCPBars *bars, double keyCoord)
void remove(QCPBars *bars)
void setSpacingType(SpacingType spacingType)
void insert(int i, QCPBars *bars)
double spacing() const
Definition: qcustomplot.h:6374
@ stAbsolute
Bar spacing is in absolute pixels.
Definition: qcustomplot.h:6359
double mSpacing
Definition: qcustomplot.h:6395
SpacingType mSpacingType
Definition: qcustomplot.h:6394
QList< QCPBars * > bars() const
Definition: qcustomplot.h:6381
void registerBars(QCPBars *bars)
void append(QCPBars *bars)
SpacingType spacingType() const
Definition: qcustomplot.h:6373
double keyPixelOffset(const QCPBars *bars, double keyCoord)
QCPBarsGroup(QCustomPlot *parentPlot)
void setSpacing(double spacing)
void unregisterBars(QCPBars *bars)
virtual ~QCPBarsGroup()
QList< QCPBars * > mBars
Definition: qcustomplot.h:6396
A plottable representing a bar chart in a plot.
Definition: qcustomplot.h:6449
double getStackedBaseValue(double key, bool positive) const
virtual void clearData()
QCPBars * barBelow() const
Definition: qcustomplot.h:6487
void removeData(double fromKey, double toKey)
double baseValue() const
Definition: qcustomplot.h:6485
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const
QPolygonF getBarPolygon(double key, double value) const
virtual void draw(QCPPainter *painter)
WidthType widthType() const
Definition: qcustomplot.h:6483
virtual ~QCPBars()
void setBaseValue(double baseValue)
QCPBarsGroup * barsGroup() const
Definition: qcustomplot.h:6484
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
@ wtPlotCoords
Definition: qcustomplot.h:6473
@ wtAxisRectRatio
Definition: qcustomplot.h:6470
@ wtAbsolute
Bar width is in absolute pixels.
Definition: qcustomplot.h:6468
QPointer< QCPBars > mBarAbove
Definition: qcustomplot.h:6532
void moveBelow(QCPBars *bars)
void setData(QSharedPointer< QCPBarsDataContainer > data)
static void connectBars(QCPBars *lower, QCPBars *upper)
double mWidth
Definition: qcustomplot.h:6527
QSharedPointer< QCPBarsDataContainer > data() const
Definition: qcustomplot.h:6489
WidthType mWidthType
Definition: qcustomplot.h:6528
void removeDataAfter(double key)
void removeDataBefore(double key)
double mBaseValue
Definition: qcustomplot.h:6530
QCPBarsGroup * mBarsGroup
Definition: qcustomplot.h:6529
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
double width() const
Definition: qcustomplot.h:6482
void moveAbove(QCPBars *bars)
void getVisibleDataBounds(QCPBarsDataContainer::const_iterator &begin, QCPBarsDataContainer::const_iterator &end) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QPointer< QCPBars > mBarBelow
Definition: qcustomplot.h:6532
void getPixelWidth(double key, double &lower, double &upper) const
void setWidthType(WidthType widthType)
void setBarsGroup(QCPBarsGroup *barsGroup)
QCPBarDataMap * mData
Definition: qcustomplot.h:3485
void setWidth(double width)
Defines a color gradient for use with e.g. QCPColorMap.
Definition: qcustomplot.h:5165
ColorInterpolation mColorInterpolation
Definition: qcustomplot.h:5277
QRgb color(double position, const QCPRange &range, bool logarithmic=false)
void setLevelCount(int n)
void setPeriodic(bool enabled)
void setColorStopAt(double position, const QColor &color)
void setColorStops(const QMap< double, QColor > &colorStops)
bool operator==(const QCPColorGradient &other) const
QMap< double, QColor > mColorStops
Definition: qcustomplot.h:5276
QCPColorGradient inverted() const
void loadPreset(GradientPreset preset)
void setColorInterpolation(ColorInterpolation interpolation)
QMap< double, QColor > colorStops() const
Definition: qcustomplot.h:5239
void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false)
bool mColorBufferInvalidated
Definition: qcustomplot.h:5284
@ ciRGB
Color channels red, green and blue are linearly interpolated.
Definition: qcustomplot.h:5175
QVector< QRgb > mColorBuffer
Definition: qcustomplot.h:5282
@ gpCandy
Blue over pink to white.
Definition: qcustomplot.h:5200
Holds the two-dimensional data of a QCPColorMap plottable.
Definition: qcustomplot.h:6720
void setKeyRange(const QCPRange &keyRange)
void setValueSize(int valueSize)
void setSize(int keySize, int valueSize)
QCPRange mDataBounds
Definition: qcustomplot.h:6776
QCPRange keyRange() const
Definition: qcustomplot.h:6733
QCPRange mValueRange
Definition: qcustomplot.h:6770
double data(double key, double value)
void fill(double z)
QCPRange valueRange() const
Definition: qcustomplot.h:6734
int valueSize() const
Definition: qcustomplot.h:6732
void setCell(int keyIndex, int valueIndex, double z)
QCPRange mKeyRange
Definition: qcustomplot.h:6770
QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange)
void setRange(const QCPRange &keyRange, const QCPRange &valueRange)
void recalculateDataBounds()
QCPRange dataBounds() const
Definition: qcustomplot.h:6735
int keySize() const
Definition: qcustomplot.h:6731
void setKeySize(int keySize)
void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
void setValueRange(const QCPRange &valueRange)
bool isEmpty() const
Definition: qcustomplot.h:6757
void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
double cell(int keyIndex, int valueIndex)
void setData(double key, double value, double z)
QCPColorMapData & operator=(const QCPColorMapData &other)
A plottable representing a two-dimensional color map in a plot.
Definition: qcustomplot.h:6784
QCPColorMapData * data() const
Definition: qcustomplot.h:6802
virtual void clearData()
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const
QCPColorMapData * mMapData
Definition: qcustomplot.h:6846
void gradientChanged(const QCPColorGradient &newGradient)
virtual void draw(QCPPainter *painter)
QPointer< QCPColorScale > mColorScale
Definition: qcustomplot.h:6850
void setInterpolate(bool enabled)
void setData(QCPColorMapData *data, bool copy=false)
Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18))
virtual void updateMapImage()
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QImage mMapImage
Definition: qcustomplot.h:6853
Q_SLOT void setGradient(const QCPColorGradient &gradient)
void dataRangeChanged(const QCPRange &newRange)
void rescaleDataRange(bool recalculateDataBounds=false)
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
Q_SLOT void setDataRange(const QCPRange &dataRange)
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
QCPColorScale * colorScale() const
Definition: qcustomplot.h:6808
QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setColorScale(QCPColorScale *colorScale)
QCPColorGradient mGradient
Definition: qcustomplot.h:6847
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:6845
QCPRange mDataRange
Definition: qcustomplot.h:6844
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
bool mTightBoundary
Definition: qcustomplot.h:6849
virtual ~QCPColorMap()
bool mMapImageInvalidated
Definition: qcustomplot.h:6855
QImage mUndersampledMapImage
Definition: qcustomplot.h:6853
QCPColorGradient gradient() const
Definition: qcustomplot.h:6807
void setTightBoundary(bool enabled)
QPixmap mLegendIcon
Definition: qcustomplot.h:6854
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
QCPRange dataRange() const
Definition: qcustomplot.h:6803
bool mInterpolate
Definition: qcustomplot.h:6848
A color scale for use with color coding data such as QCPColorMap.
Definition: qcustomplot.h:5876
void setType(QCPAxis::AxisType type)
Q_SLOT void setGradient(const QCPColorGradient &gradient)
void setRangeDrag(bool enabled)
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:5932
bool rangeDrag() const
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details)
QCPColorGradient gradient() const
Definition: qcustomplot.h:5900
QCPAxis * axis() const
Definition: qcustomplot.h:5896
QString label() const
void rescaleDataRange(bool onlyVisibleMaps)
virtual ~QCPColorScale()
QCPRange dataRange() const
Definition: qcustomplot.h:5898
QList< QCPColorMap * > colorMaps() const
QPointer< QCPColorScaleAxisRectPrivate > mAxisRect
Definition: qcustomplot.h:5937
QCPRange mDataRange
Definition: qcustomplot.h:5931
void gradientChanged(const QCPColorGradient &newGradient)
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
void dataRangeChanged(const QCPRange &newRange)
QCPAxis::AxisType mType
Definition: qcustomplot.h:5930
QCPAxis::AxisType type() const
Definition: qcustomplot.h:5897
void setRangeZoom(bool enabled)
QPointer< QCPAxis > mColorAxis
Definition: qcustomplot.h:5938
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
QCPColorScale(QCustomPlot *parentPlot)
virtual void wheelEvent(QWheelEvent *event)
virtual void update(UpdatePhase phase)
void setBarWidth(int width)
Q_SLOT void setDataRange(const QCPRange &dataRange)
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
bool rangeZoom() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QCPColorGradient mGradient
Definition: qcustomplot.h:5933
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
void setLabel(const QString &str)
Holds the data of one single data point for QCPCurve.
Definition: qcustomplot.h:6164
void removeDataAfter(double t)
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:6264
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > &points, const QCPScatterStyle &style) const
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const
QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
virtual void draw(QCPPainter *painter)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
@ lsNone
No line is drawn between data points (e.g. only scatters)
Definition: qcustomplot.h:6213
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
void getCurveData(QVector< QPointF > *lineData) const
void setData(QSharedPointer< QCPCurveDataContainer > data)
void setLineStyle(LineStyle style)
void getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector< QPointF > &beforeTraverse, QVector< QPointF > &afterTraverse) const
void setScatterStyle(const QCPScatterStyle &style)
QVector< QPointF > getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
void addData(const QVector< double > &t, const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
QSharedPointer< QCPCurveDataContainer > data() const
Definition: qcustomplot.h:6223
int getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPCurveDataMap * mData
Definition: qcustomplot.h:3263
virtual ~QCPCurve()
double pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void removeData(double fromt, double tot)
virtual void clearData()
LineStyle mLineStyle
Definition: qcustomplot.h:6266
bool mayTraverse(int prevRegion, int currentRegion) const
void removeDataBefore(double t)
bool getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:2952
double key
Definition: qcustomplot.h:2956
double valueErrorMinus
Definition: qcustomplot.h:2958
double valueErrorPlus
Definition: qcustomplot.h:2958
double keyErrorPlus
Definition: qcustomplot.h:2957
double value
Definition: qcustomplot.h:2956
double keyErrorMinus
Definition: qcustomplot.h:2957
Holds the data of one single data point for QCPFinancial.
Definition: qcustomplot.h:6874
void removeData(double fromKey, double toKey)
void removeDataBefore(double key)
static QCPFinancialDataContainer timeSeriesToOhlc(const QVector< double > &time, const QVector< double > &value, double timeBinSize, double timeBinOffset=0)
@ csOhlc
Open-High-Low-Close bar representation.
Definition: qcustomplot.h:6947
@ csCandlestick
Candlestick representation.
Definition: qcustomplot.h:6949
virtual void clearData()
double width() const
Definition: qcustomplot.h:6961
void setTwoColored(bool twoColored)
double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
void getVisibleDataBounds(QCPFinancialDataContainer::const_iterator &begin, QCPFinancialDataContainer::const_iterator &end) const
void addData(const QVector< double > &keys, const QVector< double > &open, const QVector< double > &high, const QVector< double > &low, const QVector< double > &close, bool alreadySorted=false)
QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QCPFinancialDataMap * mData
Definition: qcustomplot.h:3847
QSharedPointer< QCPFinancialDataContainer > data() const
Definition: qcustomplot.h:6957
void setChartStyle(ChartStyle style)
void setBrushPositive(const QBrush &brush)
void setData(QSharedPointer< QCPFinancialDataContainer > data)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
void setBrushNegative(const QBrush &brush)
double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
void setWidth(double width)
void removeDataAfter(double key)
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const
ChartStyle mChartStyle
Definition: qcustomplot.h:7019
QBrush mBrushPositive
Definition: qcustomplot.h:7023
void setPenPositive(const QPen &pen)
QBrush mBrushNegative
Definition: qcustomplot.h:7023
virtual ~QCPFinancial()
virtual void draw(QCPPainter *painter)
void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
bool twoColored() const
Definition: qcustomplot.h:6963
void setPenNegative(const QPen &pen)
A plottable representing a graph in a plot.
Definition: qcustomplot.h:5996
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setErrorBarSize(double size)
void setScatterStyle(const QCPScatterStyle &style)
void rescaleAxes(bool onlyEnlarge=false) const
void setData(QSharedPointer< QCPGraphDataContainer > data)
ErrorType errorType() const
Definition: qcustomplot.h:3038
virtual void drawImpulsePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
QPointer< QCPGraph > mChannelFillGraph
Definition: qcustomplot.h:6083
ErrorType mErrorType
Definition: qcustomplot.h:3112
void setChannelFillGraph(QCPGraph *targetGraph)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void removeData(double fromKey, double toKey)
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:6081
virtual void drawLinePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
void setLineStyle(LineStyle ls)
void getStepRightPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
int findIndexBelowY(const QVector< QPointF > *data, double y) const
void getPlotData(QVector< QPointF > *lineData, QVector< QCPData > *scatterData) const
virtual void draw(QCPPainter *painter)
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lines) const
void getScatterPlotData(QVector< QCPData > *scatterData) const
void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const
void getLinePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QPointF upperFillBasePoint(double upperKey) const
double mErrorBarSize
Definition: qcustomplot.h:3113
QCPDataMap * mData
Definition: qcustomplot.h:3108
void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
LineStyle mLineStyle
Definition: qcustomplot.h:6080
void setDataBothError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError, const QVector< double > &valueError)
double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
int findIndexAboveY(const QVector< QPointF > *data, double y) const
int findIndexBelowX(const QVector< QPointF > *data, double x) const
void addFillBasePoints(QVector< QPointF > *lineData) const
void getStepLeftPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lineData, QCPDataRange thisSegment, const QVector< QPointF > *otherData, QCPDataRange otherSegment) const
void removeDataBefore(double key)
QPen mErrorPen
Definition: qcustomplot.h:3109
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const
bool mAdaptiveSampling
Definition: qcustomplot.h:6084
int findIndexAboveX(const QVector< QPointF > *data, double x) const
void getImpulsePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
void setErrorBarSkipSymbol(bool enabled)
void setAdaptiveSampling(bool enabled)
void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
void getStepCenterPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
void setDataKeyError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError)
void setErrorPen(const QPen &pen)
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > &scatters, const QCPScatterStyle &style) const
void setErrorType(ErrorType errorType)
void setDataValueError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &valueError)
QSharedPointer< QCPGraphDataContainer > data() const
Definition: qcustomplot.h:6040
QPointF lowerFillBasePoint(double lowerKey) const
bool mErrorBarSkipSymbol
Definition: qcustomplot.h:3114
@ etKey
Error bars for the key dimension of the data point are shown.
Definition: qcustomplot.h:3021
@ etNone
No error bars are shown.
Definition: qcustomplot.h:3019
virtual void clearData()
@ lsLine
data points are connected by a straight line
Definition: qcustomplot.h:6019
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
void removeDataAfter(double key)
void getPreparedData(QVector< QCPData > *lineData, QVector< QCPData > *scatterData) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void rescaleKeyAxis(bool onlyEnlarge=false) const
virtual ~QCPGraph()
void removeFillBasePoints(QVector< QPointF > *lineData) const
Responsible for drawing the grid of a QCPAxis.
Definition: qcustomplot.h:2202
QPen mPen
Definition: qcustomplot.h:2237
QPen pen() const
Definition: qcustomplot.h:2221
void setZeroLinePen(const QPen &pen)
QPen mZeroLinePen
Definition: qcustomplot.h:2237
void setAntialiasedZeroLine(bool enabled)
QCPAxis * mParentAxis
Definition: qcustomplot.h:2240
bool mSubGridVisible
Definition: qcustomplot.h:2235
void setAntialiasedSubGrid(bool enabled)
bool mAntialiasedSubGrid
Definition: qcustomplot.h:2236
void drawSubGridLines(QCPPainter *painter) const
bool mAntialiasedZeroLine
Definition: qcustomplot.h:2236
void setSubGridPen(const QPen &pen)
void setPen(const QPen &pen)
QPen mSubGridPen
Definition: qcustomplot.h:2237
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QCPGrid(QCPAxis *parentAxis)
virtual void draw(QCPPainter *painter)
void setSubGridVisible(bool visible)
void drawGridLines(QCPPainter *painter) const
An anchor of an item to which positions can be attached to.
Definition: qcustomplot.h:3948
virtual ~QCPItemAnchor()
void removeChildX(QCPItemPosition *pos)
QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId=-1)
QCPAbstractItem * mParentItem
Definition: qcustomplot.h:3967
QSet< QCPItemPosition * > mChildrenY
Definition: qcustomplot.h:3969
QCustomPlot * mParentPlot
Definition: qcustomplot.h:3966
friend class QCPItemPosition
Definition: qcustomplot.h:3987
void removeChildY(QCPItemPosition *pos)
virtual QCPItemPosition * toQCPItemPosition()
Definition: qcustomplot.h:3972
virtual QPointF pixelPoint() const
QSet< QCPItemPosition * > mChildrenX
Definition: qcustomplot.h:3969
void addChildX(QCPItemPosition *pos)
void addChildY(QCPItemPosition *pos)
void setSelectedPen(const QPen &pen)
QCPItemBracket(QCustomPlot *parentPlot)
virtual QPointF anchorPixelPoint(int anchorId) const
BracketStyle style() const
Definition: qcustomplot.h:7829
QPen pen() const
Definition: qcustomplot.h:7826
void setStyle(BracketStyle style)
QCPItemPosition *const left
Definition: qcustomplot.h:7842
@ bsCurly
A curly brace.
Definition: qcustomplot.h:7815
@ bsSquare
A brace with angled edges.
Definition: qcustomplot.h:7811
@ bsRound
A brace with round edges.
Definition: qcustomplot.h:7813
virtual void draw(QCPPainter *painter)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setPen(const QPen &pen)
QCPItemPosition *const right
Definition: qcustomplot.h:7843
void setLength(double length)
BracketStyle mStyle
Definition: qcustomplot.h:7851
virtual ~QCPItemBracket()
QPen mainPen() const
double length() const
Definition: qcustomplot.h:7828
QCPItemPosition *const startDir
Definition: qcustomplot.h:7349
void setPen(const QPen &pen)
QCPItemPosition *const start
Definition: qcustomplot.h:7348
void setHead(const QCPLineEnding &head)
QCPItemPosition *const end
Definition: qcustomplot.h:7351
void setSelectedPen(const QPen &pen)
QPen mainPen() const
virtual void draw(QCPPainter *painter)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPLineEnding head() const
Definition: qcustomplot.h:7334
QCPItemPosition *const endDir
Definition: qcustomplot.h:7350
QCPLineEnding tail() const
Definition: qcustomplot.h:7335
void setTail(const QCPLineEnding &tail)
QCPItemCurve(QCustomPlot *parentPlot)
virtual ~QCPItemCurve()
QPen pen() const
Definition: qcustomplot.h:7332
QCPLineEnding mTail
Definition: qcustomplot.h:7356
QCPLineEnding mHead
Definition: qcustomplot.h:7356
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:7576
QBrush mSelectedBrush
Definition: qcustomplot.h:7602
QCPItemPosition *const topLeft
Definition: qcustomplot.h:7575
virtual ~QCPItemEllipse()
void setBrush(const QBrush &brush)
QBrush mainBrush() const
void setSelectedPen(const QPen &pen)
QCPItemEllipse(QCustomPlot *parentPlot)
QPen pen() const
Definition: qcustomplot.h:7559
void setSelectedBrush(const QBrush &brush)
QPen mainPen() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QBrush brush() const
Definition: qcustomplot.h:7561
QCPItemAnchor *const center
Definition: qcustomplot.h:7585
virtual QPointF anchorPixelPoint(int anchorId) const
void setPen(const QPen &pen)
virtual void draw(QCPPainter *painter)
QCPItemLine(QCustomPlot *parentPlot)
virtual void draw(QCPPainter *painter)
QCPItemPosition *const start
Definition: qcustomplot.h:7296
void setSelectedPen(const QPen &pen)
QCPItemPosition *const end
Definition: qcustomplot.h:7297
QCPLineEnding mHead
Definition: qcustomplot.h:7302
void setPen(const QPen &pen)
QCPLineEnding head() const
Definition: qcustomplot.h:7282
QPen pen() const
Definition: qcustomplot.h:7280
QLineF getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const
virtual ~QCPItemLine()
QCPLineEnding mTail
Definition: qcustomplot.h:7302
QCPLineEnding tail() const
Definition: qcustomplot.h:7283
void setTail(const QCPLineEnding &tail)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setHead(const QCPLineEnding &head)
QPen mainPen() const
QPen mSelectedPen
Definition: qcustomplot.h:7301
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:7658
QPixmap mPixmap
Definition: qcustomplot.h:7677
virtual QPointF anchorPixelPoint(int anchorId) const
QPixmap mScaledPixmap
Definition: qcustomplot.h:7678
QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const
Qt::AspectRatioMode aspectRatioMode() const
Definition: qcustomplot.h:7636
QPen pen() const
Definition: qcustomplot.h:7640
QCPItemAnchor *const left
Definition: qcustomplot.h:7664
void setPixmap(const QPixmap &pixmap)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QPixmap pixmap() const
Definition: qcustomplot.h:7634
virtual ~QCPItemPixmap()
bool scaled() const
Definition: qcustomplot.h:7635
virtual void draw(QCPPainter *painter)
QCPItemAnchor *const right
Definition: qcustomplot.h:7661
void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false)
Qt::AspectRatioMode mAspectRatioMode
Definition: qcustomplot.h:7681
QCPItemPixmap(QCustomPlot *parentPlot)
QPen mainPen() const
void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation)
Qt::TransformationMode transformationMode() const
Definition: qcustomplot.h:7637
Qt::TransformationMode mTransformationMode
Definition: qcustomplot.h:7682
void setPen(const QPen &pen)
QCPItemPosition *const topLeft
Definition: qcustomplot.h:7657
void setSelectedPen(const QPen &pen)
Manages the position of an item.
Definition: qcustomplot.h:3990
QCPItemAnchor * mParentAnchorX
Definition: qcustomplot.h:4069
QCPItemAnchor * parentAnchor() const
Definition: qcustomplot.h:4036
void setAxisRect(QCPAxisRect *axisRect)
void setTypeX(PositionType type)
void setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:4066
QCPAxis * valueAxis() const
Definition: qcustomplot.h:4043
PositionType mPositionTypeY
Definition: qcustomplot.h:4065
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:4066
QCPItemAnchor * parentAnchorX() const
Definition: qcustomplot.h:4037
QPointer< QCPAxisRect > mAxisRect
Definition: qcustomplot.h:4067
virtual QPointF pixelPoint() const
double key() const
Definition: qcustomplot.h:4039
QCPAxis * keyAxis() const
Definition: qcustomplot.h:4042
QCPItemAnchor * parentAnchorY() const
Definition: qcustomplot.h:4038
void setType(PositionType type)
QPointF coords() const
Definition: qcustomplot.h:4041
void setCoords(double key, double value)
void setPixelPoint(const QPointF &pixelPoint)
PositionType type() const
Definition: qcustomplot.h:4033
bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
void setTypeY(PositionType type)
double value() const
Definition: qcustomplot.h:4040
virtual ~QCPItemPosition()
bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
PositionType mPositionTypeX
Definition: qcustomplot.h:4065
QCPAxisRect * axisRect() const
QCPItemAnchor * mParentAnchorY
Definition: qcustomplot.h:4069
virtual void draw(QCPPainter *painter)
QBrush mSelectedBrush
Definition: qcustomplot.h:7420
QBrush mBrush
Definition: qcustomplot.h:7420
QPen pen() const
Definition: qcustomplot.h:7383
QCPItemRect(QCustomPlot *parentPlot)
void setPen(const QPen &pen)
QBrush brush() const
Definition: qcustomplot.h:7385
void setSelectedPen(const QPen &pen)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:7400
QPen mSelectedPen
Definition: qcustomplot.h:7419
QBrush mainBrush() const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:7399
void setBrush(const QBrush &brush)
void setSelectedBrush(const QBrush &brush)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual QPointF anchorPixelPoint(int anchorId) const
QPen mainPen() const
virtual ~QCPItemRect()
double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual ~QCPItemStraightLine()
virtual void draw(QCPPainter *painter)
QCPItemPosition *const point1
Definition: qcustomplot.h:7245
QCPItemStraightLine(QCustomPlot *parentPlot)
void setSelectedPen(const QPen &pen)
QCPItemPosition *const point2
Definition: qcustomplot.h:7246
void setPen(const QPen &pen)
QPen pen() const
Definition: qcustomplot.h:7233
QLineF getRectClippedStraightLine(const QCPVector2D &point1, const QCPVector2D &vec, const QRect &rect) const
QColor color() const
Definition: qcustomplot.h:7460
void setSelectedFont(const QFont &font)
Qt::Alignment positionAlignment() const
Definition: qcustomplot.h:7469
void setBrush(const QBrush &brush)
virtual ~QCPItemText()
QBrush mBrush
Definition: qcustomplot.h:7519
QBrush brush() const
Definition: qcustomplot.h:7464
QCPItemPosition *const position
Definition: qcustomplot.h:7494
QBrush mSelectedBrush
Definition: qcustomplot.h:7519
void setSelectedPen(const QPen &pen)
QString mText
Definition: qcustomplot.h:7521
QPen mainPen() const
void setText(const QString &text)
virtual QPointF anchorPixelPoint(int anchorId) const
QFont font() const
Definition: qcustomplot.h:7466
void setRotation(double degrees)
QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
QMargins padding() const
Definition: qcustomplot.h:7472
QFont mSelectedFont
Definition: qcustomplot.h:7520
void setSelectedBrush(const QBrush &brush)
QCPItemAnchor *const bottomRight
Definition: qcustomplot.h:7499
Qt::Alignment mPositionAlignment
Definition: qcustomplot.h:7522
QPen pen() const
Definition: qcustomplot.h:7462
QCPItemText(QCustomPlot *parentPlot)
void setPositionAlignment(Qt::Alignment alignment)
QColor mSelectedColor
Definition: qcustomplot.h:7517
QColor mColor
Definition: qcustomplot.h:7517
virtual void draw(QCPPainter *painter)
QPen mSelectedPen
Definition: qcustomplot.h:7518
void setFont(const QFont &font)
void setPen(const QPen &pen)
QCPItemAnchor *const topLeft
Definition: qcustomplot.h:7495
void setColor(const QColor &color)
void setTextAlignment(Qt::Alignment alignment)
QColor mainColor() const
double mRotation
Definition: qcustomplot.h:7524
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Qt::Alignment mTextAlignment
Definition: qcustomplot.h:7523
QBrush mainBrush() const
QString text() const
Definition: qcustomplot.h:7468
QMargins mPadding
Definition: qcustomplot.h:7525
void setSelectedColor(const QColor &color)
void setPadding(const QMargins &padding)
QFont mainFont() const
void setSelectedBrush(const QBrush &brush)
QBrush mSelectedBrush
Definition: qcustomplot.h:7774
void setBrush(const QBrush &brush)
@ tsCircle
A circle.
Definition: qcustomplot.h:7730
@ tsPlus
A plus shaped crosshair with limited size.
Definition: qcustomplot.h:7725
@ tsSquare
A square.
Definition: qcustomplot.h:7732
@ tsNone
The tracer is not visible.
Definition: qcustomplot.h:7723
void setStyle(TracerStyle style)
virtual ~QCPItemTracer()
double size() const
Definition: qcustomplot.h:7744
void setGraphKey(double key)
void setInterpolating(bool enabled)
QBrush brush() const
Definition: qcustomplot.h:7742
QPen pen() const
Definition: qcustomplot.h:7740
double mGraphKey
Definition: qcustomplot.h:7778
QBrush mainBrush() const
virtual void draw(QCPPainter *painter)
QCPGraph * mGraph
Definition: qcustomplot.h:7777
QPen mainPen() const
QCPGraph * graph() const
Definition: qcustomplot.h:7746
QCPItemTracer(QCustomPlot *parentPlot)
void setSelectedPen(const QPen &pen)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setSize(double size)
QCPItemPosition *const position
Definition: qcustomplot.h:7769
void setGraph(QCPGraph *graph)
void setPen(const QPen &pen)
TracerStyle mStyle
Definition: qcustomplot.h:7776
TracerStyle style() const
Definition: qcustomplot.h:7745
A layer that may contain objects, to control the rendering order.
Definition: qcustomplot.h:816
QList< QCPLayerable * > mChildren
Definition: qcustomplot.h:866
QList< QCPLayerable * > children() const
Definition: qcustomplot.h:850
bool mVisible
Definition: qcustomplot.h:867
QString name() const
Definition: qcustomplot.h:848
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:847
void addChild(QCPLayerable *layerable, bool prepend)
QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
QCustomPlot * mParentPlot
Definition: qcustomplot.h:863
void setVisible(bool visible)
void removeChild(QCPLayerable *layerable)
bool visible() const
Definition: qcustomplot.h:851
int index() const
Definition: qcustomplot.h:849
virtual ~QCPLayer()
Base class for all drawable objects.
Definition: qcustomplot.h:887
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const =0
QCustomPlot * mParentPlot
Definition: qcustomplot.h:929
friend class QCPAxisRect
Definition: qcustomplot.h:967
bool mAntialiased
Definition: qcustomplot.h:932
void setVisible(bool on)
virtual ~QCPLayerable()
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:904
void setAntialiased(bool enabled)
friend class QCPLayer
Definition: qcustomplot.h:966
QCPLayer * layer() const
Definition: qcustomplot.h:906
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0)
void initializeParentPlot(QCustomPlot *parentPlot)
virtual QCP::Interaction selectionCategory() const
void setParentLayerable(QCPLayerable *parentLayerable)
QCPLayerable * parentLayerable() const
Definition: qcustomplot.h:905
bool realVisibility() const
Q_SLOT bool setLayer(QCPLayer *layer)
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
QCPLayer * mLayer
Definition: qcustomplot.h:931
void layerChanged(QCPLayer *newLayer)
void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
virtual QRect clipRect() const
virtual void draw(QCPPainter *painter)=0
virtual void deselectEvent(bool *selectionStateChanged)
QPointer< QCPLayerable > mParentLayerable
Definition: qcustomplot.h:930
bool visible() const
Definition: qcustomplot.h:903
bool moveToLayer(QCPLayer *layer, bool prepend)
The abstract base class for all objects that form the layout system.
Definition: qcustomplot.h:1409
virtual int calculateAutoMargin(QCP::MarginSide side)
void setMinimumMargins(const QMargins &margins)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
@ upMargins
Phase in which the margins are calculated and set.
Definition: qcustomplot.h:1433
virtual ~QCPLayoutElement()
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
QRect rect() const
Definition: qcustomplot.h:1461
QHash< QCP::MarginSide, QCPMarginGroup * > mMarginGroups
Definition: qcustomplot.h:1509
virtual void wheelEvent(QWheelEvent *event)
Definition: qcustomplot.h:894
void setOuterRect(const QRect &rect)
QCPLayout * layout() const
Definition: qcustomplot.h:1460
void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
QMargins mMinimumMargins
Definition: qcustomplot.h:1507
void setMinimumSize(const QSize &size)
void setMaximumSize(const QSize &size)
virtual QList< QCPLayoutElement * > elements(bool recursive) const
QCPLayoutElement(QCustomPlot *parentPlot=0)
QCPMarginGroup * marginGroup(QCP::MarginSide side) const
Definition: qcustomplot.h:1471
void setMargins(const QMargins &margins)
virtual void update(UpdatePhase phase)
virtual void mouseDoubleClickEvent(QMouseEvent *event)
Definition: qcustomplot.h:893
virtual QSize minimumSizeHint() const
virtual QSize maximumSizeHint() const
QCPLayout * mParentLayout
Definition: qcustomplot.h:1503
void setAutoMargins(QCP::MarginSides sides)
QMargins margins() const
Definition: qcustomplot.h:1463
QCP::MarginSides mAutoMargins
Definition: qcustomplot.h:1508
A layout that arranges child elements in a grid.
Definition: qcustomplot.h:1574
virtual void updateLayout()
virtual void simplify()
int rowCount() const
Definition: qcustomplot.h:1611
int columnCount() const
Definition: qcustomplot.h:1612
void insertColumn(int newIndex)
void setRowStretchFactors(const QList< double > &factors)
virtual QList< QCPLayoutElement * > elements(bool recursive) const
virtual QSize maximumSizeHint() const
void setColumnSpacing(int pixels)
void insertRow(int newIndex)
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
QCPLayoutElement * element(int row, int column) const
virtual bool take(QCPLayoutElement *element)
void setColumnStretchFactors(const QList< double > &factors)
virtual int elementCount() const
Definition: qcustomplot.h:1634
void setRowStretchFactor(int row, double factor)
void expandTo(int newRowCount, int newColumnCount)
QList< QList< QCPLayoutElement * > > mElements
Definition: qcustomplot.h:1659
virtual QCPLayoutElement * elementAt(int index) const
void getMaximumRowColSizes(QVector< int > *maxColWidths, QVector< int > *maxRowHeights) const
virtual QSize minimumSizeHint() const
QList< double > mColumnStretchFactors
Definition: qcustomplot.h:1660
void setRowSpacing(int pixels)
bool hasElement(int row, int column)
virtual QCPLayoutElement * takeAt(int index)
bool addElement(int row, int column, QCPLayoutElement *element)
void setColumnStretchFactor(int column, double factor)
virtual ~QCPLayoutGrid()
QList< double > mRowStretchFactors
Definition: qcustomplot.h:1661
A layout that places child elements aligned to the border or arbitrarily positioned.
Definition: qcustomplot.h:1677
virtual int elementCount() const
QList< QCPLayoutElement * > mElements
Definition: qcustomplot.h:1723
QList< InsetPlacement > mInsetPlacement
Definition: qcustomplot.h:1724
QList< Qt::Alignment > mInsetAlignment
Definition: qcustomplot.h:1725
Qt::Alignment insetAlignment(int index) const
void setInsetAlignment(int index, Qt::Alignment alignment)
void setInsetPlacement(int index, InsetPlacement placement)
InsetPlacement insetPlacement(int index) const
virtual void updateLayout()
virtual ~QCPLayoutInset()
virtual QCPLayoutElement * elementAt(int index) const
virtual bool take(QCPLayoutElement *element)
QList< QRectF > mInsetRect
Definition: qcustomplot.h:1726
void setInsetRect(int index, const QRectF &rect)
QRectF insetRect(int index) const
void addElement(QCPLayoutElement *element, Qt::Alignment alignment)
virtual QCPLayoutElement * takeAt(int index)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
The abstract base class for layouts.
Definition: qcustomplot.h:1532
void clear()
virtual void updateLayout()
bool removeAt(int index)
virtual void update(UpdatePhase phase)
virtual int elementCount() const =0
QVector< int > getSectionSizes(QVector< int > maxSizes, QVector< int > minSizes, QVector< double > stretchFactors, int totalSize) const
virtual void simplify()
void releaseElement(QCPLayoutElement *el)
virtual QCPLayoutElement * takeAt(int index)=0
bool remove(QCPLayoutElement *element)
virtual bool take(QCPLayoutElement *element)=0
virtual QList< QCPLayoutElement * > elements(bool recursive) const
void sizeConstraintsChanged() const
void adoptElement(QCPLayoutElement *el)
virtual QCPLayoutElement * elementAt(int index) const =0
Manages a legend inside a QCustomPlot.
Definition: qcustomplot.h:5605
SelectableParts mSelectableParts
Definition: qcustomplot.h:5713
int iconTextPadding() const
Definition: qcustomplot.h:5656
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QPen getBorderPen() const
void clearItems()
Q_SLOT void setSelectedParts(const SelectableParts &selectedParts)
void setSelectedBorderPen(const QPen &pen)
void setIconBorderPen(const QPen &pen)
QSize mIconSize
Definition: qcustomplot.h:5711
bool addItem(QCPAbstractLegendItem *item)
SelectableParts selectedParts() const
virtual void draw(QCPPainter *painter)
QColor mTextColor
Definition: qcustomplot.h:5710
void setBrush(const QBrush &brush)
bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
virtual void deselectEvent(bool *selectionStateChanged)
QPen mBorderPen
Definition: qcustomplot.h:5707
virtual ~QCPLegend()
@ spLegendBox
0x001 The legend box (frame)
Definition: qcustomplot.h:5638
@ spNone
0x000 None
Definition: qcustomplot.h:5636
QFont mFont
Definition: qcustomplot.h:5709
int itemCount() const
QPen iconBorderPen() const
Definition: qcustomplot.h:5657
QPen mSelectedBorderPen
Definition: qcustomplot.h:5714
void setIconTextPadding(int padding)
QColor mSelectedTextColor
Definition: qcustomplot.h:5717
QPen mSelectedIconBorderPen
Definition: qcustomplot.h:5714
void setSelectedTextColor(const QColor &color)
QPen mIconBorderPen
Definition: qcustomplot.h:5707
void selectionChanged(QCPLegend::SelectableParts parts)
void setBorderPen(const QPen &pen)
QFont mSelectedFont
Definition: qcustomplot.h:5716
void setSelectedBrush(const QBrush &brush)
void selectableChanged(QCPLegend::SelectableParts parts)
int mIconTextPadding
Definition: qcustomplot.h:5712
void setIconSize(const QSize &size)
virtual QCP::Interaction selectionCategory() const
SelectableParts mSelectedParts
Definition: qcustomplot.h:5713
QCPPlottableLegendItem * itemWithPlottable(const QCPAbstractPlottable *plottable) const
QBrush mBrush
Definition: qcustomplot.h:5708
Q_SLOT void setSelectableParts(const SelectableParts &selectableParts)
void setFont(const QFont &font)
QBrush brush() const
Definition: qcustomplot.h:5652
QBrush getBrush() const
QBrush mSelectedBrush
Definition: qcustomplot.h:5715
void setSelectedFont(const QFont &font)
QList< QCPAbstractLegendItem * > selectedItems() const
bool removeItem(int index)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPAbstractLegendItem * item(int index) const
SelectableParts selectableParts() const
Definition: qcustomplot.h:5658
bool hasItem(QCPAbstractLegendItem *item) const
QPen selectedIconBorderPen() const
Definition: qcustomplot.h:5661
void setSelectedIconBorderPen(const QPen &pen)
void setTextColor(const QColor &color)
QFont font() const
Definition: qcustomplot.h:5653
QSize iconSize() const
Definition: qcustomplot.h:5655
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
Handles the different ending decorations for line-like items.
Definition: qcustomplot.h:1738
EndingStyle style() const
Definition: qcustomplot.h:1788
double boundingDistance() const
bool inverted() const
Definition: qcustomplot.h:1791
void setWidth(double width)
EndingStyle mStyle
Definition: qcustomplot.h:1809
void draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const
void setStyle(EndingStyle style)
void setInverted(bool inverted)
@ esDiamond
A filled diamond (45 degrees rotated square)
Definition: qcustomplot.h:1768
@ esBar
A bar perpendicular to the line.
Definition: qcustomplot.h:1770
@ esNone
No ending decoration.
Definition: qcustomplot.h:1755
@ esDisc
A filled circle.
Definition: qcustomplot.h:1764
@ esLineArrow
A non-filled arrow head with open back.
Definition: qcustomplot.h:1762
@ esSpikeArrow
A filled arrow head with an indented back.
Definition: qcustomplot.h:1760
@ esSquare
A filled square.
Definition: qcustomplot.h:1766
double realLength() const
void setLength(double length)
double width() const
Definition: qcustomplot.h:1789
double length() const
Definition: qcustomplot.h:1790
A margin group allows synchronization of margin sides if working with multiple layout elements.
Definition: qcustomplot.h:1378
void removeChild(QCP::MarginSide side, QCPLayoutElement *element)
QList< QCPLayoutElement * > elements(QCP::MarginSide side) const
Definition: qcustomplot.h:1385
QHash< QCP::MarginSide, QList< QCPLayoutElement * > > mChildren
Definition: qcustomplot.h:1394
virtual ~QCPMarginGroup()
QCPMarginGroup(QCustomPlot *parentPlot)
void addChild(QCP::MarginSide side, QCPLayoutElement *element)
bool isEmpty() const
virtual int commonMargin(QCP::MarginSide side) const
QPainter subclass used internally.
Definition: qcustomplot.h:637
bool begin(QPaintDevice *device)
void drawLine(const QLineF &line)
QStack< bool > mAntialiasingStack
Definition: qcustomplot.h:700
bool antialiasing() const
Definition: qcustomplot.h:670
void setModes(PainterModes modes)
void restore()
bool mIsAntialiasing
Definition: qcustomplot.h:697
void makeNonCosmetic()
void setAntialiasing(bool enabled)
PainterModes modes() const
Definition: qcustomplot.h:671
PainterModes mModes
Definition: qcustomplot.h:696
void setMode(PainterMode mode, bool enabled=true)
void setPen(const QPen &pen)
A layout element displaying a plot title text.
Definition: qcustomplot.h:2773
void setSelectedTextColor(const QColor &color)
QString mText
Definition: qcustomplot.h:2820
void setFont(const QFont &font)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QFont font() const
Definition: qcustomplot.h:2793
void selectionChanged(bool selected)
void setSelectedFont(const QFont &font)
QFont mainFont() const
QColor mTextColor
Definition: qcustomplot.h:2822
void selectableChanged(bool selectable)
bool selected() const
Definition: qcustomplot.h:2798
void setTextColor(const QColor &color)
QRect mTextBoundingRect
Definition: qcustomplot.h:2825
Q_SLOT void setSelectable(bool selectable)
QColor mSelectedTextColor
Definition: qcustomplot.h:2824
Q_SLOT void setSelected(bool selected)
QFont mSelectedFont
Definition: qcustomplot.h:2823
QCPPlotTitle(QCustomPlot *parentPlot)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
void setText(const QString &text)
QColor mainTextColor() const
QString text() const
Definition: qcustomplot.h:2792
bool selectable() const
Definition: qcustomplot.h:2797
virtual void deselectEvent(bool *selectionStateChanged)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
virtual QSize maximumSizeHint() const
virtual void draw(QCPPainter *painter)
virtual QSize minimumSizeHint() const
A legend item representing a plottable with an icon and the plottable name.
Definition: qcustomplot.h:5583
virtual QSize minimumSizeHint() const
QColor getTextColor() const
virtual void draw(QCPPainter *painter)
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:5593
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
QPen getIconBorderPen() const
Represents the range an axis is encompassing.
Definition: qcustomplot.h:975
void expand(const QCPRange &otherRange)
static const double maxRange
Definition: qcustomplot.h:1033
QCPRange sanitizedForLogScale() const
double size() const
Definition: qcustomplot.h:1014
QCPRange sanitizedForLinScale() const
static const double minRange
Definition: qcustomplot.h:1032
QCPRange expanded(const QCPRange &otherRange) const
double lower
Definition: qcustomplot.h:977
static bool validRange(double lower, double upper)
double upper
Definition: qcustomplot.h:977
bool contains(double value) const
Definition: qcustomplot.h:1026
double center() const
Definition: qcustomplot.h:1015
void normalize()
Definition: qcustomplot.h:1016
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:2695
double size() const
Definition: qcustomplot.h:2803
void setPixmap(const QPixmap &pixmap)
bool isNone() const
Definition: qcustomplot.h:2821
void setBrush(const QBrush &brush)
void setPen(const QPen &pen)
void setShape(ScatterShape shape)
QPainterPath mCustomPath
Definition: qcustomplot.h:2835
void drawShape(QCPPainter *painter, const QPointF &pos) const
void setCustomPath(const QPainterPath &customPath)
QPixmap pixmap() const
Definition: qcustomplot.h:2807
void setSize(double size)
QPen pen() const
Definition: qcustomplot.h:2805
@ ssPlus
\enumimage{ssPlus.png} a plus
Definition: qcustomplot.h:2742
@ ssCircle
\enumimage{ssCircle.png} a circle
Definition: qcustomplot.h:2744
@ ssSquare
\enumimage{ssSquare.png} a square
Definition: qcustomplot.h:2749
@ ssCross
\enumimage{ssCross.png} a cross
Definition: qcustomplot.h:2740
@ ssDiamond
\enumimage{ssDiamond.png} a diamond
Definition: qcustomplot.h:2751
QBrush brush() const
Definition: qcustomplot.h:2806
QPainterPath customPath() const
Definition: qcustomplot.h:2808
ScatterShape shape() const
Definition: qcustomplot.h:2804
ScatterShape mShape
Definition: qcustomplot.h:2831
void applyTo(QCPPainter *painter, const QPen &defaultPen) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
double key() const
Definition: qcustomplot.h:3537
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const
void setData(QSharedPointer< QCPStatisticalBoxDataContainer > data)
void setWidth(double width)
virtual void clearData()
QVector< double > mOutliers
Definition: qcustomplot.h:3580
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void setWhiskerPen(const QPen &pen)
double maximum() const
Definition: qcustomplot.h:3542
virtual void drawMedian(QCPPainter *painter) const
void setMedian(double value)
void setUpperQuartile(double value)
void setLowerQuartile(double value)
virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const
void setMedianPen(const QPen &pen)
virtual void draw(QCPPainter *painter)
QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setKey(double key)
void setMinimum(double value)
virtual void drawOutliers(QCPPainter *painter) const
void setWhiskerBarPen(const QPen &pen)
double minimum() const
Definition: qcustomplot.h:3538
double median() const
Definition: qcustomplot.h:3540
void setMaximum(double value)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
void setOutlierStyle(const QCPScatterStyle &style)
void setWhiskerWidth(double width)
QCPScatterStyle mOutlierStyle
Definition: qcustomplot.h:6687
double width() const
Definition: qcustomplot.h:6625
virtual void drawWhiskers(QCPPainter *painter) const
void setOutliers(const QVector< double > &values)
double lowerQuartile() const
Definition: qcustomplot.h:3539
double upperQuartile() const
Definition: qcustomplot.h:3541
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:4167
void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
QCPLayer * currentLayer() const
void drawBackground(QCPPainter *painter)
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:4445
QCPLayer * layer(const QString &name) const
void beforeReplot()
Qt::KeyboardModifier mMultiSelectModifier
Definition: qcustomplot.h:4450
virtual QSize minimumSizeHint() const
QCPLayerable * layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const
QList< QCPGraph * > mGraphs
Definition: qcustomplot.h:4435
QList< QCPAxisRect * > axisRects() const
QCPAbstractItem * item() const
void setBackground(const QPixmap &pm)
virtual void resizeEvent(QResizeEvent *event)
int itemCount() const
QRect viewport() const
Definition: qcustomplot.h:4232
void toPainter(QCPPainter *painter, int width=0, int height=0)
void titleClick(QMouseEvent *event, QCPPlotTitle *title)
friend class QCPLayer
Definition: qcustomplot.h:4515
QCP::AntialiasedElements mNotAntialiasedElements
Definition: qcustomplot.h:4439
virtual void paintEvent(QPaintEvent *event)
const QCP::Interactions interactions() const
Definition: qcustomplot.h:4247
QPointer< QCPLayoutElement > mMouseEventElement
Definition: qcustomplot.h:2282
QCPAbstractPlottable * plottable(int index)
void setBackgroundScaled(bool scaled)
QBrush mBackgroundBrush
Definition: qcustomplot.h:4443
void setPlottingHint(QCP::PlottingHint hint, bool enabled=true)
QCPLayer * mCurrentLayer
Definition: qcustomplot.h:4448
void setViewport(const QRect &rect)
bool removeLayer(QCPLayer *layer)
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
QCustomPlot(QWidget *parent=0)
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
void setSelectionTolerance(int pixels)
QCPLegend * legend
Definition: qcustomplot.h:4394
void selectionChangedByUser()
virtual QSize sizeHint() const
int selectionTolerance() const
Definition: qcustomplot.h:4248
int graphCount() const
void setInteractions(const QCP::Interactions &interactions)
int plottableCount() const
bool mBackgroundScaled
Definition: qcustomplot.h:4446
QCP::AntialiasedElements antialiasedElements() const
Definition: qcustomplot.h:4240
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
void afterReplot()
QCPGraph * addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0)
virtual void mouseReleaseEvent(QMouseEvent *event)
bool hasPlottable(QCPAbstractPlottable *plottable) const
bool setCurrentLayer(const QString &name)
void mouseMove(QMouseEvent *event)
QList< QCPAbstractPlottable * > selectedPlottables() const
QPixmap mPaintBuffer
Definition: qcustomplot.h:2280
QCP::AntialiasedElements notAntialiasedElements() const
Definition: qcustomplot.h:4243
@ limAbove
Layer is inserted above other layer.
Definition: qcustomplot.h:4196
virtual ~QCustomPlot()
bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
virtual void mouseDoubleClickEvent(QMouseEvent *event)
void setNoAntialiasingOnDrag(bool enabled)
QList< QCPAbstractItem * > mItems
Definition: qcustomplot.h:4437
void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
virtual void wheelEvent(QWheelEvent *event)
QList< QCPAxis * > selectedAxes() const
void updateLayerIndices() const
void plottableDoubleClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)
virtual void axisRemoved(QCPAxis *axis)
int axisRectCount() const
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
bool removeGraph(QCPGraph *graph)
void setPlottingHints(const QCP::PlottingHints &hints)
int clearPlottables()
QCPAxis * xAxis
Definition: qcustomplot.h:4393
void mouseDoubleClick(QMouseEvent *event)
virtual void legendRemoved(QCPLegend *legend)
Q_SLOT void deselectAll()
QCP::PlottingHints mPlottingHints
Definition: qcustomplot.h:4449
QCP::AntialiasedElements mAntialiasedElements
Definition: qcustomplot.h:4439
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint)
bool addItem(QCPAbstractItem *item)
QPixmap toPixmap(int width=0, int height=0, double scale=1.0)
QCPGraph * graph() const
QList< QCPAbstractPlottable * > mPlottables
Definition: qcustomplot.h:4434
bool mAutoAddPlottableToLegend
Definition: qcustomplot.h:4433
bool mReplotting
Definition: qcustomplot.h:4463
bool addPlottable(QCPAbstractPlottable *plottable)
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:4447
QCPLayoutGrid * mPlotLayout
Definition: qcustomplot.h:4432
int mSelectionTolerance
Definition: qcustomplot.h:4441
virtual void mousePressEvent(QMouseEvent *event)
void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
QCPAbstractItem * itemAt(const QPointF &pos, bool onlySelectable=false) const
QRect mViewport
Definition: qcustomplot.h:4430
QPoint mMousePressPos
Definition: qcustomplot.h:4457
virtual void mouseMoveEvent(QMouseEvent *event)
QCP::PlottingHints plottingHints() const
Definition: qcustomplot.h:4250
void mouseWheel(QWheelEvent *event)
void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
bool mNoAntialiasingOnDrag
Definition: qcustomplot.h:4442
QList< QCPLegend * > selectedLegends() const
void mouseRelease(QMouseEvent *event)
bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
bool noAntialiasingOnDrag() const
Definition: qcustomplot.h:4249
void mousePress(QMouseEvent *event)
QCPAbstractPlottable * plottableAt(const QPointF &pos, bool onlySelectable=false) const
QList< QCPLayer * > mLayers
Definition: qcustomplot.h:4438
friend class QCPGraph
Definition: qcustomplot.h:4518
QList< QCPGraph * > selectedGraphs() const
void titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove)
bool savePdf(const QString &fileName, int width=0, int height=0, QCP::ExportPen exportPen=QCP::epAllowCosmetic, const QString &pdfCreator=QString(), const QString &pdfTitle=QString())
QCP::Interactions mInteractions
Definition: qcustomplot.h:4440
bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
virtual void draw(QCPPainter *painter)
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
void setAutoAddPlottableToLegend(bool on)
QCPAxis * xAxis2
Definition: qcustomplot.h:4393
QCPAbstractPlottable * plottable()
bool removeItem(QCPAbstractItem *item)
void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
void itemClick(QCPAbstractItem *item, QMouseEvent *event)
bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
QCPAxisRect * axisRect(int index=0) const
bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove)
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:4444
void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true)
bool hasItem(QCPAbstractItem *item) const
QCPAxis * yAxis2
Definition: qcustomplot.h:4393
bool removePlottable(QCPAbstractPlottable *plottable)
void plottableClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)
void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
QCPAxis * yAxis
Definition: qcustomplot.h:4393
int layerCount() const
QCPLayoutElement * layoutElementAt(const QPointF &pos) const
void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true)
QList< QCPAbstractItem * > selectedItems() const
a[190]
const double * e
GraphType data
Definition: graph_cut.cc:138
QMap< double, QCPCurveData > QCPCurveDataMap
Definition: qcustomplot.h:3203
QMap< double, QCPFinancialData > QCPFinancialDataMap
Definition: qcustomplot.h:3761
QMap< double, QCPData > QCPDataMap
Definition: qcustomplot.h:2969
QMap< double, QCPBarData > QCPBarDataMap
Definition: qcustomplot.h:3413
normal_z y
float
normal_z x
normal_z z
bool isInvalidData(double value)
Definition: qcustomplot.h:425
int getMarginValue(const QMargins &margins, QCP::MarginSide side)
Definition: qcustomplot.h:473
Interaction
Definition: qcustomplot.h:320
@ iRangeDrag
Definition: qcustomplot.h:321
@ iSelectItems
Definition: qcustomplot.h:344
@ iSelectLegend
Definition: qcustomplot.h:341
@ iSelectOther
Definition: qcustomplot.h:347
@ iSelectAxes
Definition: qcustomplot.h:338
@ iSelectPlottables
Definition: qcustomplot.h:334
@ iMultiSelect
Definition: qcustomplot.h:329
@ iRangeZoom
Definition: qcustomplot.h:325
PlottingHint
Definition: qcustomplot.h:288
@ phFastPolylines
Definition: qcustomplot.h:291
@ phCacheLabels
Definition: qcustomplot.h:308
@ phForceRepaint
Definition: qcustomplot.h:214
MarginSide
Definition: qcustomplot.h:223
@ msTop
0x04 top margin
Definition: qcustomplot.h:228
@ msNone
0x00 no margin
Definition: qcustomplot.h:234
@ msRight
0x02 right margin
Definition: qcustomplot.h:226
@ msAll
0xFF all margins
Definition: qcustomplot.h:232
@ msLeft
0x01 left margin
Definition: qcustomplot.h:224
@ msBottom
0x08 bottom margin
Definition: qcustomplot.h:230
AntialiasedElement
Definition: qcustomplot.h:249
@ aeAxes
0x0001 Axis base line and tick marks
Definition: qcustomplot.h:250
@ aeItems
0x0040 Main lines of items
Definition: qcustomplot.h:262
@ aeScatters
Definition: qcustomplot.h:264
@ aeLegendItems
0x0010 Legend items
Definition: qcustomplot.h:258
@ aeErrorBars
0x0100 Error bars
Definition: qcustomplot.h:186
@ aePlottables
0x0020 Main lines of plottables
Definition: qcustomplot.h:260
@ aeNone
0x0000 No elements
Definition: qcustomplot.h:278
@ aeSubGrid
0x0004 Sub grid lines
Definition: qcustomplot.h:254
@ aeFills
Definition: qcustomplot.h:267
@ aeAll
0xFFFF All elements
Definition: qcustomplot.h:276
@ aeGrid
0x0002 Grid lines
Definition: qcustomplot.h:252
@ aeZeroLine
Definition: qcustomplot.h:270
@ aeLegend
0x0008 Legend box
Definition: qcustomplot.h:256
void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
Definition: qcustomplot.h:444
bool setColor(ccHObject::Container selectedEntities, bool colorize, QWidget *parent)
static const std::string path
Definition: PointCloud.cpp:59
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
constexpr Rgb black(0, 0, 0)
constexpr Rgb white(MAX, MAX, MAX)
constexpr Rgb blue(0, 0, MAX)