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 #include <QtCompat.h>
11 
12 /* including file 'src/vector2d.cpp', size 7340 */
13 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
14 
18 
27 /* start documentation of inline functions */
28 
91 /* end documentation of inline functions */
92 
96 QCPVector2D::QCPVector2D() : mX(0), mY(0) {}
97 
102 QCPVector2D::QCPVector2D(double x, double y) : mX(x), mY(y) {}
103 
108 QCPVector2D::QCPVector2D(const QPoint &point) : mX(point.x()), mY(point.y()) {}
109 
114 QCPVector2D::QCPVector2D(const QPointF &point) : mX(point.x()), mY(point.y()) {}
115 
123  double len = length();
124  mX /= len;
125  mY /= len;
126 }
127 
135  QCPVector2D result(mX, mY);
136  result.normalize();
137  return result;
138 }
139 
148  const QCPVector2D &end) const {
149  QCPVector2D v(end - start);
150  double vLengthSqr = v.lengthSquared();
151  if (!qFuzzyIsNull(vLengthSqr)) {
152  double mu = v.dot(*this - start) / vLengthSqr;
153  if (mu < 0)
154  return (*this - start).lengthSquared();
155  else if (mu > 1)
156  return (*this - end).lengthSquared();
157  else
158  return ((start + mu * v) - *this).lengthSquared();
159  } else
160  return (*this - start).lengthSquared();
161 }
162 
170 double QCPVector2D::distanceSquaredToLine(const QLineF &line) const {
171  return distanceSquaredToLine(QCPVector2D(line.p1()),
172  QCPVector2D(line.p2()));
173 }
174 
182  const QCPVector2D &direction) const {
183  return qAbs((*this - base).dot(direction.perpendicular())) /
184  direction.length();
185 }
186 
192  mX *= factor;
193  mY *= factor;
194  return *this;
195 }
196 
202  mX /= divisor;
203  mY /= divisor;
204  return *this;
205 }
206 
211  mX += vector.mX;
212  mY += vector.mY;
213  return *this;
214 }
215 
220  mX -= vector.mX;
221  mY -= vector.mY;
222  return *this;
223 }
224 /* end of 'src/vector2d.cpp' */
225 
226 /* including file 'src/painter.cpp', size 8670 */
227 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
228 
232 
252  : QPainter(), mModes(pmDefault), mIsAntialiasing(false) {
253  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter
254  // isn't active yet and a call to begin() will follow
255 }
256 
265 QCPPainter::QCPPainter(QPaintDevice *device)
266  : QPainter(device), mModes(pmDefault), mIsAntialiasing(false) {
267 #if QT_VERSION < \
268  QT_VERSION_CHECK(5, 0, \
269  0) // before Qt5, default pens used to be cosmetic if
270  // NonCosmeticDefaultPen flag isn't set. So we set
271  // it to get consistency across Qt versions.
272  if (isActive()) setRenderHint(QPainter::NonCosmeticDefaultPen);
273 #endif
274 }
275 
282 void QCPPainter::setPen(const QPen &pen) {
283  QPainter::setPen(pen);
284  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
285 }
286 
294 void QCPPainter::setPen(const QColor &color) {
295  QPainter::setPen(color);
296  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
297 }
298 
306 void QCPPainter::setPen(Qt::PenStyle penStyle) {
307  QPainter::setPen(penStyle);
308  if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic();
309 }
310 
320 void QCPPainter::drawLine(const QLineF &line) {
321  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
322  QPainter::drawLine(line);
323  else
324  QPainter::drawLine(line.toLine());
325 }
326 
334 void QCPPainter::setAntialiasing(bool enabled) {
335  setRenderHint(QPainter::Antialiasing, enabled);
336  if (mIsAntialiasing != enabled) {
337  mIsAntialiasing = enabled;
338  if (!mModes.testFlag(
339  pmVectorized)) // antialiasing half-pixel shift only needed
340  // for rasterized outputs
341  {
342  if (mIsAntialiasing)
343  translate(0.5, 0.5);
344  else
345  translate(-0.5, -0.5);
346  }
347  }
348 }
349 
354 void QCPPainter::setModes(QCPPainter::PainterModes modes) { mModes = modes; }
355 
367 bool QCPPainter::begin(QPaintDevice *device) {
368  bool result = QPainter::begin(device);
369 #if QT_VERSION < \
370  QT_VERSION_CHECK(5, 0, \
371  0) // before Qt5, default pens used to be cosmetic if
372  // NonCosmeticDefaultPen flag isn't set. So we set
373  // it to get consistency across Qt versions.
374  if (result) setRenderHint(QPainter::NonCosmeticDefaultPen);
375 #endif
376  return result;
377 }
378 
385  if (!enabled && mModes.testFlag(mode))
386  mModes &= ~mode;
387  else if (enabled && !mModes.testFlag(mode))
388  mModes |= mode;
389 }
390 
402  QPainter::save();
403 }
404 
415  if (!mAntialiasingStack.isEmpty())
417  else
418  qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
419  QPainter::restore();
420 }
421 
427  if (qFuzzyIsNull(pen().widthF())) {
428  QPen p = pen();
429  p.setWidth(1);
430  QPainter::setPen(p);
431  }
432 }
433 /* end of 'src/painter.cpp' */
434 
435 /* including file 'src/paintbuffer.cpp', size 18502 */
436 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
437 
441 
465 /* start documentation of pure virtual functions */
466 
511 /* end documentation of pure virtual functions */
512 /* start documentation of inline functions */
513 
524 /* end documentation of inline functions */
525 
534  double devicePixelRatio)
535  : mSize(size), mDevicePixelRatio(devicePixelRatio), mInvalidated(true) {}
536 
538 
549  if (mSize != size) {
550  mSize = size;
552  }
553 }
554 
575 }
576 
589  if (!qFuzzyCompare(ratio, mDevicePixelRatio)) {
590 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
591  mDevicePixelRatio = ratio;
593 #else
594  qDebug() << Q_FUNC_INFO
595  << "Device pixel ratios not supported for Qt versions before "
596  "5.4";
597  mDevicePixelRatio = 1.0;
598 #endif
599  }
600 }
601 
605 
619  double devicePixelRatio)
620  : QCPAbstractPaintBuffer(size, devicePixelRatio) {
622 }
623 
625 
626 /* inherits documentation from base class */
629  result->setRenderHint(QPainter::Antialiasing);
630  return result;
631 }
632 
633 /* inherits documentation from base class */
635  if (painter && painter->isActive())
636  painter->drawPixmap(0, 0, mBuffer);
637  else
638  qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
639 }
640 
641 /* inherits documentation from base class */
642 void QCPPaintBufferPixmap::clear(const QColor &color) { mBuffer.fill(color); }
643 
644 /* inherits documentation from base class */
646  setInvalidated();
647  if (!qFuzzyCompare(1.0, mDevicePixelRatio)) {
648 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
649  mBuffer = QPixmap(mSize * mDevicePixelRatio);
650  mBuffer.setDevicePixelRatio(mDevicePixelRatio);
651 #else
652  qDebug() << Q_FUNC_INFO
653  << "Device pixel ratios not supported for Qt versions before "
654  "5.4";
655  mDevicePixelRatio = 1.0;
656  mBuffer = QPixmap(mSize);
657 #endif
658  } else {
659  mBuffer = QPixmap(mSize);
660  }
661 }
662 
663 #ifdef QCP_OPENGL_PBUFFER
667 
690 QCPPaintBufferGlPbuffer::QCPPaintBufferGlPbuffer(const QSize &size,
691  double devicePixelRatio,
692  int multisamples)
693  : QCPAbstractPaintBuffer(size, devicePixelRatio),
694  mGlPBuffer(0),
695  mMultisamples(qMax(0, multisamples)) {
696  QCPPaintBufferGlPbuffer::reallocateBuffer();
697 }
698 
699 QCPPaintBufferGlPbuffer::~QCPPaintBufferGlPbuffer() {
700  if (mGlPBuffer) delete mGlPBuffer;
701 }
702 
703 /* inherits documentation from base class */
704 QCPPainter *QCPPaintBufferGlPbuffer::startPainting() {
705  if (!mGlPBuffer->isValid()) {
706  qDebug() << Q_FUNC_INFO
707  << "OpenGL frame buffer object doesn't exist, "
708  "reallocateBuffer was not called?";
709  return 0;
710  }
711 
712  QCPPainter *result = new QCPPainter(mGlPBuffer);
713  result->setRenderHint(QPainter::Antialiasing);
714  return result;
715 }
716 
717 /* inherits documentation from base class */
718 void QCPPaintBufferGlPbuffer::draw(QCPPainter *painter) const {
719  if (!painter || !painter->isActive()) {
720  qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
721  return;
722  }
723  if (!mGlPBuffer->isValid()) {
724  qDebug() << Q_FUNC_INFO
725  << "OpenGL pbuffer isn't valid, reallocateBuffer was not "
726  "called?";
727  return;
728  }
729  painter->drawImage(0, 0, mGlPBuffer->toImage());
730 }
731 
732 /* inherits documentation from base class */
733 void QCPPaintBufferGlPbuffer::clear(const QColor &color) {
734  if (mGlPBuffer->isValid()) {
735  mGlPBuffer->makeCurrent();
736  glClearColor(color.redF(), color.greenF(), color.blueF(),
737  color.alphaF());
738  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
739  mGlPBuffer->doneCurrent();
740  } else
741  qDebug() << Q_FUNC_INFO
742  << "OpenGL pbuffer invalid or context not current";
743 }
744 
745 /* inherits documentation from base class */
746 void QCPPaintBufferGlPbuffer::reallocateBuffer() {
747  if (mGlPBuffer) delete mGlPBuffer;
748 
749  QGLFormat format;
750  format.setAlpha(true);
751  format.setSamples(mMultisamples);
752  mGlPBuffer = new QGLPixelBuffer(mSize, format);
753 }
754 #endif // QCP_OPENGL_PBUFFER
755 
756 #ifdef QCP_OPENGL_FBO
760 
783 QCPPaintBufferGlFbo::QCPPaintBufferGlFbo(
784  const QSize &size,
785  double devicePixelRatio,
786  QWeakPointer<QOpenGLContext> glContext,
787  QWeakPointer<QOpenGLPaintDevice> glPaintDevice)
788  : QCPAbstractPaintBuffer(size, devicePixelRatio),
789  mGlContext(glContext),
790  mGlPaintDevice(glPaintDevice),
791  mGlFrameBuffer(0) {
792  QCPPaintBufferGlFbo::reallocateBuffer();
793 }
794 
795 QCPPaintBufferGlFbo::~QCPPaintBufferGlFbo() {
796  if (mGlFrameBuffer) delete mGlFrameBuffer;
797 }
798 
799 /* inherits documentation from base class */
800 QCPPainter *QCPPaintBufferGlFbo::startPainting() {
801  if (mGlPaintDevice.isNull()) {
802  qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
803  return 0;
804  }
805  if (!mGlFrameBuffer) {
806  qDebug() << Q_FUNC_INFO
807  << "OpenGL frame buffer object doesn't exist, "
808  "reallocateBuffer was not called?";
809  return 0;
810  }
811 
812  if (QOpenGLContext::currentContext() != mGlContext.data())
813  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
814  mGlFrameBuffer->bind();
815  QCPPainter *result = new QCPPainter(mGlPaintDevice.data());
816  result->setRenderHint(QPainter::Antialiasing);
817  return result;
818 }
819 
820 /* inherits documentation from base class */
821 void QCPPaintBufferGlFbo::donePainting() {
822  if (mGlFrameBuffer && mGlFrameBuffer->isBound())
823  mGlFrameBuffer->release();
824  else
825  qDebug() << Q_FUNC_INFO
826  << "Either OpenGL frame buffer not valid or was not bound";
827 }
828 
829 /* inherits documentation from base class */
830 void QCPPaintBufferGlFbo::draw(QCPPainter *painter) const {
831  if (!painter || !painter->isActive()) {
832  qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
833  return;
834  }
835  if (!mGlFrameBuffer) {
836  qDebug() << Q_FUNC_INFO
837  << "OpenGL frame buffer object doesn't exist, "
838  "reallocateBuffer was not called?";
839  return;
840  }
841  painter->drawImage(0, 0, mGlFrameBuffer->toImage());
842 }
843 
844 /* inherits documentation from base class */
845 void QCPPaintBufferGlFbo::clear(const QColor &color) {
846  if (mGlContext.isNull()) {
847  qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
848  return;
849  }
850  if (!mGlFrameBuffer) {
851  qDebug() << Q_FUNC_INFO
852  << "OpenGL frame buffer object doesn't exist, "
853  "reallocateBuffer was not called?";
854  return;
855  }
856 
857  if (QOpenGLContext::currentContext() != mGlContext.data())
858  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
859  mGlFrameBuffer->bind();
860  glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
861  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
862  mGlFrameBuffer->release();
863 }
864 
865 /* inherits documentation from base class */
866 void QCPPaintBufferGlFbo::reallocateBuffer() {
867  // release and delete possibly existing framebuffer:
868  if (mGlFrameBuffer) {
869  if (mGlFrameBuffer->isBound()) mGlFrameBuffer->release();
870  delete mGlFrameBuffer;
871  mGlFrameBuffer = 0;
872  }
873 
874  if (mGlContext.isNull()) {
875  qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
876  return;
877  }
878  if (mGlPaintDevice.isNull()) {
879  qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
880  return;
881  }
882 
883  // create new fbo with appropriate size:
884  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
885  QOpenGLFramebufferObjectFormat frameBufferFormat;
886  frameBufferFormat.setSamples(mGlContext.data()->format().samples());
887  frameBufferFormat.setAttachment(
888  QOpenGLFramebufferObject::CombinedDepthStencil);
889  mGlFrameBuffer = new QOpenGLFramebufferObject(mSize * mDevicePixelRatio,
890  frameBufferFormat);
891  if (mGlPaintDevice.data()->size() != mSize * mDevicePixelRatio)
892  mGlPaintDevice.data()->setSize(mSize * mDevicePixelRatio);
893 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
894  mGlPaintDevice.data()->setDevicePixelRatio(mDevicePixelRatio);
895 #endif
896 }
897 #endif // QCP_OPENGL_FBO
898 /* end of 'src/paintbuffer.cpp' */
899 
900 /* including file 'src/layer.cpp', size 37304 */
901 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
902 
906 
971 /* start documentation of inline functions */
972 
988 /* end documentation of inline functions */
989 
999 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
1000  : QObject(parentPlot),
1001  mParentPlot(parentPlot),
1002  mName(layerName),
1003  mIndex(-1), // will be set to a proper value by the QCustomPlot layer
1004  // creation function
1005  mVisible(true),
1006  mMode(lmLogical) {
1007  // Note: no need to make sure layerName is unique, because layer
1008  // management is done with QCustomPlot functions.
1009 }
1010 
1012  // If child layerables are still on this layer, detach them, so they don't
1013  // try to reach back to this then invalid layer once they get deleted/moved
1014  // themselves. This only happens when layers are deleted directly, like in
1015  // the QCustomPlot destructor. (The regular layer removal procedure for the
1016  // user is to call QCustomPlot::removeLayer, which moves all layerables off
1017  // this layer before deleting it.)
1018 
1019  while (!mChildren.isEmpty())
1020  mChildren.last()->setLayer(
1021  0); // removes itself from mChildren via removeChild()
1022 
1023  if (mParentPlot->currentLayer() == this)
1024  qDebug() << Q_FUNC_INFO
1025  << "The parent plot's mCurrentLayer will be a dangling "
1026  "pointer. Should have been set to a valid layer or 0 "
1027  "beforehand.";
1028 }
1029 
1038 void QCPLayer::setVisible(bool visible) { mVisible = visible; }
1039 
1064  if (mMode != mode) {
1065  mMode = mode;
1066  if (!mPaintBuffer.isNull())
1067  mPaintBuffer.toStrongRef()->setInvalidated();
1068  }
1069 }
1070 
1077 void QCPLayer::draw(QCPPainter *painter) {
1078  foreach (QCPLayerable *child, mChildren) {
1079  if (child->realVisibility()) {
1080  painter->save();
1081  painter->setClipRect(child->clipRect().translated(0, -1));
1082  child->applyDefaultAntialiasingHint(painter);
1083  child->draw(painter);
1084  painter->restore();
1085  }
1086  }
1087 }
1088 
1098  if (!mPaintBuffer.isNull()) {
1099  if (QCPPainter *painter = mPaintBuffer.toStrongRef()->startPainting()) {
1100  if (painter->isActive())
1101  draw(painter);
1102  else
1103  qDebug() << Q_FUNC_INFO
1104  << "paint buffer returned inactive painter";
1105  delete painter;
1106  mPaintBuffer.toStrongRef()->donePainting();
1107  } else
1108  qDebug() << Q_FUNC_INFO << "paint buffer returned zero painter";
1109  } else
1110  qDebug() << Q_FUNC_INFO
1111  << "no valid paint buffer associated with this layer";
1112 }
1113 
1130  if (!mPaintBuffer.isNull()) {
1131  mPaintBuffer.toStrongRef()->clear(Qt::transparent);
1133  mPaintBuffer.toStrongRef()->setInvalidated(false);
1134  mParentPlot->update();
1135  } else
1136  qDebug() << Q_FUNC_INFO
1137  << "no valid paint buffer associated with this layer";
1138  } else if (mMode == lmLogical)
1139  mParentPlot->replot();
1140 }
1141 
1154 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend) {
1155  if (!mChildren.contains(layerable)) {
1156  if (prepend)
1157  mChildren.prepend(layerable);
1158  else
1159  mChildren.append(layerable);
1160  if (!mPaintBuffer.isNull())
1161  mPaintBuffer.toStrongRef()->setInvalidated();
1162  } else
1163  qDebug() << Q_FUNC_INFO << "layerable is already child of this layer"
1164  << reinterpret_cast<quintptr>(layerable);
1165 }
1166 
1177  if (mChildren.removeOne(layerable)) {
1178  if (!mPaintBuffer.isNull())
1179  mPaintBuffer.toStrongRef()->setInvalidated();
1180  } else
1181  qDebug() << Q_FUNC_INFO << "layerable is not child of this layer"
1182  << reinterpret_cast<quintptr>(layerable);
1183 }
1184 
1188 
1201 /* start documentation of inline functions */
1202 
1218 /* end documentation of inline functions */
1219 /* start documentation of pure virtual functions */
1220 
1266 /* end documentation of pure virtual functions */
1267 /* start documentation of signals */
1268 
1277 /* end documentation of signals */
1278 
1301  QString targetLayer,
1302  QCPLayerable *parentLayerable)
1303  : QObject(plot),
1304  mVisible(true),
1305  mParentPlot(plot),
1306  mParentLayerable(parentLayerable),
1307  mLayer(0),
1308  mAntialiased(true) {
1309  if (mParentPlot) {
1310  if (targetLayer.isEmpty())
1312  else if (!setLayer(targetLayer))
1313  qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to"
1314  << targetLayer << "failed.";
1315  }
1316 }
1317 
1319  if (mLayer) {
1320  mLayer->removeChild(this);
1321  mLayer = 0;
1322  }
1323 }
1324 
1330 void QCPLayerable::setVisible(bool on) { mVisible = on; }
1331 
1343  return moveToLayer(layer, false);
1344 }
1345 
1351 bool QCPLayerable::setLayer(const QString &layerName) {
1352  if (!mParentPlot) {
1353  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1354  return false;
1355  }
1356  if (QCPLayer *layer = mParentPlot->layer(layerName)) {
1357  return setLayer(layer);
1358  } else {
1359  qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
1360  return false;
1361  }
1362 }
1363 
1371 void QCPLayerable::setAntialiased(bool enabled) { mAntialiased = enabled; }
1372 
1385  return mVisible && (!mLayer || mLayer->visible()) &&
1386  (!mParentLayerable || mParentLayerable.data()->realVisibility());
1387 }
1388 
1435 double QCPLayerable::selectTest(const QPointF &pos,
1436  bool onlySelectable,
1437  QVariant *details) const {
1438  Q_UNUSED(pos)
1439  Q_UNUSED(onlySelectable)
1440  Q_UNUSED(details)
1441  return -1.0;
1442 }
1443 
1463  if (mParentPlot) {
1464  qDebug() << Q_FUNC_INFO
1465  << "called with mParentPlot already initialized";
1466  return;
1467  }
1468 
1469  if (!parentPlot) qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1470 
1473 }
1474 
1489 }
1490 
1499 bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend) {
1500  if (layer && !mParentPlot) {
1501  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1502  return false;
1503  }
1504  if (layer && layer->parentPlot() != mParentPlot) {
1505  qDebug() << Q_FUNC_INFO << "layer" << layer->name()
1506  << "is not in same QCustomPlot as this layerable";
1507  return false;
1508  }
1509 
1510  QCPLayer *oldLayer = mLayer;
1511  if (mLayer) mLayer->removeChild(this);
1512  mLayer = layer;
1513  if (mLayer) mLayer->addChild(this, prepend);
1514  if (mLayer != oldLayer) emit layerChanged(mLayer);
1515  return true;
1516 }
1517 
1527  QCPPainter *painter,
1528  bool localAntialiased,
1529  QCP::AntialiasedElement overrideElement) const {
1530  if (mParentPlot &&
1531  mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1532  painter->setAntialiasing(false);
1533  else if (mParentPlot &&
1534  mParentPlot->antialiasedElements().testFlag(overrideElement))
1535  painter->setAntialiasing(true);
1536  else
1537  painter->setAntialiasing(localAntialiased);
1538 }
1539 
1558  Q_UNUSED(parentPlot)
1559 }
1560 
1573  return QCP::iSelectOther;
1574 }
1575 
1585 QRect QCPLayerable::clipRect() const {
1586  if (mParentPlot)
1587  return mParentPlot->viewport();
1588  else
1589  return QRect();
1590 }
1591 
1626  bool additive,
1627  const QVariant &details,
1628  bool *selectionStateChanged) {
1629  Q_UNUSED(event)
1630  Q_UNUSED(additive)
1631  Q_UNUSED(details)
1632  Q_UNUSED(selectionStateChanged)
1633 }
1634 
1647 void QCPLayerable::deselectEvent(bool *selectionStateChanged) {
1648  Q_UNUSED(selectionStateChanged)
1649 }
1650 
1681  const QVariant &details) {
1682  Q_UNUSED(details)
1683  event->ignore();
1684 }
1685 
1700 void QCPLayerable::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) {
1701  Q_UNUSED(startPos)
1702  event->ignore();
1703 }
1704 
1720  const QPointF &startPos) {
1721  Q_UNUSED(startPos)
1722  event->ignore();
1723 }
1724 
1756  const QVariant &details) {
1757  Q_UNUSED(details)
1758  event->ignore();
1759 }
1760 
1779 void QCPLayerable::wheelEvent(QWheelEvent *event) { event->ignore(); }
1780 /* end of 'src/layer.cpp' */
1781 
1782 /* including file 'src/axis/range.cpp', size 12221 */
1783 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
1784 
1788 
1797 /* start of documentation of inline functions */
1798 
1840 /* end of documentation of inline functions */
1841 
1855 const double QCPRange::minRange = 1e-280;
1856 
1870 const double QCPRange::maxRange = 1e250;
1871 
1875 QCPRange::QCPRange() : lower(0), upper(0) {}
1876 
1884 QCPRange::QCPRange(double lower, double upper) : lower(lower), upper(upper) {
1885  normalize();
1886 }
1887 
1902 void QCPRange::expand(const QCPRange &otherRange) {
1903  if (lower > otherRange.lower || qIsNaN(lower)) lower = otherRange.lower;
1904  if (upper < otherRange.upper || qIsNaN(upper)) upper = otherRange.upper;
1905 }
1906 
1920 void QCPRange::expand(double includeCoord) {
1921  if (lower > includeCoord || qIsNaN(lower)) lower = includeCoord;
1922  if (upper < includeCoord || qIsNaN(upper)) upper = includeCoord;
1923 }
1924 
1935 QCPRange QCPRange::expanded(const QCPRange &otherRange) const {
1936  QCPRange result = *this;
1937  result.expand(otherRange);
1938  return result;
1939 }
1940 
1951 QCPRange QCPRange::expanded(double includeCoord) const {
1952  QCPRange result = *this;
1953  result.expand(includeCoord);
1954  return result;
1955 }
1956 
1966 QCPRange QCPRange::bounded(double lowerBound, double upperBound) const {
1967  if (lowerBound > upperBound) qSwap(lowerBound, upperBound);
1968 
1970  if (result.lower < lowerBound) {
1971  result.lower = lowerBound;
1972  result.upper = lowerBound + size();
1973  if (result.upper > upperBound ||
1974  qFuzzyCompare(size(), upperBound - lowerBound))
1975  result.upper = upperBound;
1976  } else if (result.upper > upperBound) {
1977  result.upper = upperBound;
1978  result.lower = upperBound - size();
1979  if (result.lower < lowerBound ||
1980  qFuzzyCompare(size(), upperBound - lowerBound))
1981  result.lower = lowerBound;
1982  }
1983 
1984  return result;
1985 }
1986 
2002  double rangeFac = 1e-3;
2003  QCPRange sanitizedRange(lower, upper);
2004  sanitizedRange.normalize();
2005  // can't have range spanning negative and positive values in log plot, so
2006  // change range to fix it
2007  // if (qFuzzyCompare(sanitizedRange.lower+1, 1) &&
2008  // !qFuzzyCompare(sanitizedRange.upper+1, 1))
2009  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0) {
2010  // case lower is 0
2011  if (rangeFac < sanitizedRange.upper * rangeFac)
2012  sanitizedRange.lower = rangeFac;
2013  else
2014  sanitizedRange.lower = sanitizedRange.upper * rangeFac;
2015  } // else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
2016  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0) {
2017  // case upper is 0
2018  if (-rangeFac > sanitizedRange.lower * rangeFac)
2019  sanitizedRange.upper = -rangeFac;
2020  else
2021  sanitizedRange.upper = sanitizedRange.lower * rangeFac;
2022  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0) {
2023  // find out whether negative or positive interval is wider to decide
2024  // which sign domain will be chosen
2025  if (-sanitizedRange.lower > sanitizedRange.upper) {
2026  // negative is wider, do same as in case upper is 0
2027  if (-rangeFac > sanitizedRange.lower * rangeFac)
2028  sanitizedRange.upper = -rangeFac;
2029  else
2030  sanitizedRange.upper = sanitizedRange.lower * rangeFac;
2031  } else {
2032  // positive is wider, do same as in case lower is 0
2033  if (rangeFac < sanitizedRange.upper * rangeFac)
2034  sanitizedRange.lower = rangeFac;
2035  else
2036  sanitizedRange.lower = sanitizedRange.upper * rangeFac;
2037  }
2038  }
2039  // due to normalization, case lower>0 && upper<0 should never occur, because
2040  // that implies upper<lower
2041  return sanitizedRange;
2042 }
2043 
2049  QCPRange sanitizedRange(lower, upper);
2050  sanitizedRange.normalize();
2051  return sanitizedRange;
2052 }
2053 
2062 bool QCPRange::validRange(double lower, double upper) {
2063  return (lower > -maxRange && upper < maxRange &&
2064  qAbs(lower - upper) > minRange && qAbs(lower - upper) < maxRange &&
2065  !(lower > 0 && qIsInf(upper / lower)) &&
2066  !(upper < 0 && qIsInf(lower / upper)));
2067 }
2068 
2078 bool QCPRange::validRange(const QCPRange &range) {
2079  return (range.lower > -maxRange && range.upper < maxRange &&
2080  qAbs(range.lower - range.upper) > minRange &&
2081  qAbs(range.lower - range.upper) < maxRange &&
2082  !(range.lower > 0 && qIsInf(range.upper / range.lower)) &&
2083  !(range.upper < 0 && qIsInf(range.lower / range.upper)));
2084 }
2085 /* end of 'src/axis/range.cpp' */
2086 
2087 /* including file 'src/selection.cpp', size 21941 */
2088 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
2089 
2093 
2123 /* start documentation of inline functions */
2124 
2189 /* end documentation of inline functions */
2190 
2194 QCPDataRange::QCPDataRange() : mBegin(0), mEnd(0) {}
2195 
2202 QCPDataRange::QCPDataRange(int begin, int end) : mBegin(begin), mEnd(end) {}
2203 
2216  if (result.isEmpty()) // no intersection, preserve respective bounding side
2217  // of otherRange as both begin and end of return
2218  // value
2219  {
2220  if (mEnd <= other.mBegin)
2221  result = QCPDataRange(other.mBegin, other.mBegin);
2222  else
2223  result = QCPDataRange(other.mEnd, other.mEnd);
2224  }
2225  return result;
2226 }
2227 
2232  return QCPDataRange(qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd));
2233 }
2234 
2248  QCPDataRange result(qMax(mBegin, other.mBegin), qMin(mEnd, other.mEnd));
2249  if (result.isValid())
2250  return result;
2251  else
2252  return QCPDataRange();
2253 }
2254 
2260 bool QCPDataRange::intersects(const QCPDataRange &other) const {
2261  return !((mBegin > other.mBegin && mBegin >= other.mEnd) ||
2262  (mEnd <= other.mBegin && mEnd < other.mEnd));
2263 }
2264 
2271 bool QCPDataRange::contains(const QCPDataRange &other) const {
2272  return mBegin <= other.mBegin && mEnd >= other.mEnd;
2273 }
2274 
2278 
2315 /* start documentation of inline functions */
2316 
2342 /* end documentation of inline functions */
2343 
2348 
2353  mDataRanges.append(range);
2354 }
2355 
2364  if (mDataRanges.size() != other.mDataRanges.size()) return false;
2365  for (int i = 0; i < mDataRanges.size(); ++i) {
2366  if (mDataRanges.at(i) != other.mDataRanges.at(i)) return false;
2367  }
2368  return true;
2369 }
2370 
2376  mDataRanges << other.mDataRanges;
2377  simplify();
2378  return *this;
2379 }
2380 
2386  addDataRange(other);
2387  return *this;
2388 }
2389 
2395  for (int i = 0; i < other.dataRangeCount(); ++i)
2396  *this -= other.dataRange(i);
2397 
2398  return *this;
2399 }
2400 
2406  if (other.isEmpty() || isEmpty()) return *this;
2407 
2408  simplify();
2409  int i = 0;
2410  while (i < mDataRanges.size()) {
2411  const int thisBegin = mDataRanges.at(i).begin();
2412  const int thisEnd = mDataRanges.at(i).end();
2413  if (thisBegin >= other.end())
2414  break; // since data ranges are sorted after the simplify() call,
2415  // no ranges which contain other will come after this
2416 
2417  if (thisEnd >
2418  other.begin()) // ranges which don't fulfill this are entirely
2419  // before other and can be ignored
2420  {
2421  if (thisBegin >=
2422  other.begin()) // range leading segment is encompassed
2423  {
2424  if (thisEnd <=
2425  other.end()) // range fully encompassed, remove completely
2426  {
2427  mDataRanges.removeAt(i);
2428  continue;
2429  } else // only leading segment is encompassed, trim accordingly
2430  mDataRanges[i].setBegin(other.end());
2431  } else // leading segment is not encompassed
2432  {
2433  if (thisEnd <= other.end()) // only trailing segment is
2434  // encompassed, trim accordingly
2435  {
2436  mDataRanges[i].setEnd(other.begin());
2437  } else // other lies inside this range, so split range
2438  {
2439  mDataRanges[i].setEnd(other.begin());
2440  mDataRanges.insert(i + 1,
2441  QCPDataRange(other.end(), thisEnd));
2442  break; // since data ranges are sorted (and don't overlap)
2443  // after simplify() call, we're done here
2444  }
2445  }
2446  }
2447  ++i;
2448  }
2449 
2450  return *this;
2451 }
2452 
2458  int result = 0;
2459  for (int i = 0; i < mDataRanges.size(); ++i)
2460  result += mDataRanges.at(i).length();
2461  return result;
2462 }
2463 
2473  if (index >= 0 && index < mDataRanges.size()) {
2474  return mDataRanges.at(index);
2475  } else {
2476  qDebug() << Q_FUNC_INFO << "index out of range:" << index;
2477  return QCPDataRange();
2478  }
2479 }
2480 
2487  if (isEmpty())
2488  return QCPDataRange();
2489  else
2490  return QCPDataRange(mDataRanges.first().begin(),
2491  mDataRanges.last().end());
2492 }
2493 
2502  bool simplify) {
2503  mDataRanges.append(dataRange);
2504  if (simplify) this->simplify();
2505 }
2506 
2512 void QCPDataSelection::clear() { mDataRanges.clear(); }
2513 
2526  // remove any empty ranges:
2527  for (int i = mDataRanges.size() - 1; i >= 0; --i) {
2528  if (mDataRanges.at(i).isEmpty()) mDataRanges.removeAt(i);
2529  }
2530  if (mDataRanges.isEmpty()) return;
2531 
2532  // sort ranges by starting value, ascending:
2533  std::sort(mDataRanges.begin(), mDataRanges.end(), lessThanDataRangeBegin);
2534 
2535  // join overlapping/contiguous ranges:
2536  int i = 1;
2537  while (i < mDataRanges.size()) {
2538  if (mDataRanges.at(i - 1).end() >=
2539  mDataRanges.at(i).begin()) // range i overlaps/joins with i-1, so
2540  // expand range i-1 appropriately and
2541  // remove range i from list
2542  {
2543  mDataRanges[i - 1].setEnd(
2544  qMax(mDataRanges.at(i - 1).end(), mDataRanges.at(i).end()));
2545  mDataRanges.removeAt(i);
2546  } else
2547  ++i;
2548  }
2549 }
2550 
2563  simplify();
2564  switch (type) {
2565  case QCP::stNone: {
2566  mDataRanges.clear();
2567  break;
2568  }
2569  case QCP::stWhole: {
2570  // whole selection isn't defined by data range, so don't change
2571  // anything (is handled in plottable methods)
2572  break;
2573  }
2574  case QCP::stSingleData: {
2575  // reduce all data ranges to the single first data point:
2576  if (!mDataRanges.isEmpty()) {
2577  if (mDataRanges.size() > 1)
2578  mDataRanges = QList<QCPDataRange>() << mDataRanges.first();
2579  if (mDataRanges.first().length() > 1)
2580  mDataRanges.first().setEnd(mDataRanges.first().begin() + 1);
2581  }
2582  break;
2583  }
2584  case QCP::stDataRange: {
2585  if (!isEmpty()) mDataRanges = QList<QCPDataRange>() << span();
2586  break;
2587  }
2589  // this is the selection type that allows all concievable
2590  // combinations of ranges, so do nothing
2591  break;
2592  }
2593  }
2594 }
2595 
2604  if (other.isEmpty()) return false;
2605 
2606  int otherIndex = 0;
2607  int thisIndex = 0;
2608  while (thisIndex < mDataRanges.size() &&
2609  otherIndex < other.mDataRanges.size()) {
2610  if (mDataRanges.at(thisIndex).contains(
2611  other.mDataRanges.at(otherIndex)))
2612  ++otherIndex;
2613  else
2614  ++thisIndex;
2615  }
2616  return thisIndex <
2617  mDataRanges.size(); // if thisIndex ran all the way to the end to
2618  // find a containing range for the current
2619  // otherIndex, other is not contained in this
2620 }
2621 
2632  const QCPDataRange &other) const {
2634  for (int i = 0; i < mDataRanges.size(); ++i)
2635  result.addDataRange(mDataRanges.at(i).intersection(other), false);
2636  result.simplify();
2637  return result;
2638 }
2639 
2645  const QCPDataSelection &other) const {
2647  for (int i = 0; i < other.dataRangeCount(); ++i)
2648  result += intersection(other.dataRange(i));
2649  result.simplify();
2650  return result;
2651 }
2652 
2664  const QCPDataRange &outerRange) const {
2665  if (isEmpty()) return QCPDataSelection(outerRange);
2666  QCPDataRange fullRange = outerRange.expanded(span());
2667 
2669  // first unselected segment:
2670  if (mDataRanges.first().begin() != fullRange.begin())
2671  result.addDataRange(
2672  QCPDataRange(fullRange.begin(), mDataRanges.first().begin()),
2673  false);
2674  // intermediate unselected segments:
2675  for (int i = 1; i < mDataRanges.size(); ++i)
2676  result.addDataRange(QCPDataRange(mDataRanges.at(i - 1).end(),
2677  mDataRanges.at(i).begin()),
2678  false);
2679  // last unselected segment:
2680  if (mDataRanges.last().end() != fullRange.end())
2681  result.addDataRange(
2682  QCPDataRange(mDataRanges.last().end(), fullRange.end()), false);
2683  result.simplify();
2684  return result;
2685 }
2686 /* end of 'src/selection.cpp' */
2687 
2688 /* including file 'src/selectionrect.cpp', size 9224 */
2689 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
2690 
2694 
2723 /* start of documentation of inline functions */
2724 
2733 /* end of documentation of inline functions */
2734 /* start documentation of signals */
2735 
2773 /* end documentation of signals */
2774 
2781  : QCPLayerable(parentPlot),
2782  mPen(QBrush(Qt::gray), 0, Qt::DashLine),
2783  mBrush(Qt::NoBrush),
2784  mActive(false) {}
2785 
2787 
2793  if (axis) {
2794  if (axis->orientation() == Qt::Horizontal)
2795  return QCPRange(axis->pixelToCoord(mRect.left()),
2796  axis->pixelToCoord(mRect.left() + mRect.width()));
2797  else
2798  return QCPRange(axis->pixelToCoord(mRect.top() + mRect.height()),
2799  axis->pixelToCoord(mRect.top()));
2800  } else {
2801  qDebug() << Q_FUNC_INFO << "called with axis zero";
2802  return QCPRange();
2803  }
2804 }
2805 
2811 void QCPSelectionRect::setPen(const QPen &pen) { mPen = pen; }
2812 
2819 void QCPSelectionRect::setBrush(const QBrush &brush) { mBrush = brush; }
2820 
2827  if (mActive) {
2828  mActive = false;
2829  emit canceled(mRect, 0);
2830  }
2831 }
2832 
2841  mActive = true;
2842  mRect = QRect(event->pos(), event->pos());
2843  emit started(event);
2844 }
2845 
2853  mRect.setBottomRight(event->pos());
2854  emit changed(mRect, event);
2855  layer()->replot();
2856 }
2857 
2866  mRect.setBottomRight(event->pos());
2867  mActive = false;
2868  emit accepted(mRect, event);
2869 }
2870 
2878  if (event->key() == Qt::Key_Escape && mActive) {
2879  mActive = false;
2880  emit canceled(mRect, event);
2881  }
2882 }
2883 
2884 /* inherits documentation from base class */
2887 }
2888 
2897  if (mActive) {
2898  painter->setPen(mPen);
2899  painter->setBrush(mBrush);
2900  painter->drawRect(mRect);
2901  }
2902 }
2903 /* end of 'src/selectionrect.cpp' */
2904 
2905 /* including file 'src/layout.cpp', size 79139 */
2906 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
2907 
2911 
2947 /* start documentation of inline functions */
2948 
2956 /* end documentation of inline functions */
2957 
2962  : QObject(parentPlot), mParentPlot(parentPlot) {
2963  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement *>());
2964  mChildren.insert(QCP::msRight, QList<QCPLayoutElement *>());
2965  mChildren.insert(QCP::msTop, QList<QCPLayoutElement *>());
2966  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement *>());
2967 }
2968 
2970 
2976  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *>> it(mChildren);
2977  while (it.hasNext()) {
2978  it.next();
2979  if (!it.value().isEmpty()) return false;
2980  }
2981  return true;
2982 }
2983 
2990  // make all children remove themselves from this margin group:
2991  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *>> it(mChildren);
2992  while (it.hasNext()) {
2993  it.next();
2994  const QList<QCPLayoutElement *> elements = it.value();
2995  for (int i = elements.size() - 1; i >= 0; --i)
2996  elements.at(i)->setMarginGroup(
2997  it.key(),
2998  0); // removes itself from mChildren via removeChild
2999  }
3000 }
3001 
3014  // query all automatic margins of the layout elements in this margin group
3015  // side and find maximum:
3016  int result = 0;
3017  const QList<QCPLayoutElement *> elements = mChildren.value(side);
3018  for (int i = 0; i < elements.size(); ++i) {
3019  if (!elements.at(i)->autoMargins().testFlag(side)) continue;
3020  int m = qMax(
3021  elements.at(i)->calculateAutoMargin(side),
3022  QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
3023  if (m > result) result = m;
3024  }
3025  return result;
3026 }
3027 
3036  if (!mChildren[side].contains(element))
3037  mChildren[side].append(element);
3038  else
3039  qDebug() << Q_FUNC_INFO
3040  << "element is already child of this margin group side"
3041  << reinterpret_cast<quintptr>(element);
3042 }
3043 
3052  QCPLayoutElement *element) {
3053  if (!mChildren[side].removeOne(element))
3054  qDebug() << Q_FUNC_INFO
3055  << "element is not child of this margin group side"
3056  << reinterpret_cast<quintptr>(element);
3057 }
3058 
3062 
3095 /* start documentation of inline functions */
3096 
3130 /* end documentation of inline functions */
3131 
3136  : QCPLayerable(parentPlot), // parenthood is changed as soon as layout
3137  // element gets inserted into a layout (except
3138  // for top level layout)
3139  mParentLayout(0),
3140  mMinimumSize(),
3141  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
3142  mSizeConstraintRect(scrInnerRect),
3143  mRect(0, 0, 0, 0),
3144  mOuterRect(0, 0, 0, 0),
3145  mMargins(0, 0, 0, 0),
3146  mMinimumMargins(0, 0, 0, 0),
3147  mAutoMargins(QCP::msAll) {}
3148 
3151  0); // unregister at margin groups, if there are any
3152  // unregister at layout:
3153  if (qobject_cast<QCPLayout *>(
3154  mParentLayout)) // the qobject_cast is just a safeguard in case
3155  // the layout forgets to call clear() in its
3156  // dtor and this dtor is called by QObject dtor
3157  mParentLayout->take(this);
3158 }
3159 
3173 void QCPLayoutElement::setOuterRect(const QRect &rect) {
3174  if (mOuterRect != rect) {
3175  mOuterRect = rect;
3176  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(),
3177  -mMargins.right(), -mMargins.bottom());
3178  }
3179 }
3180 
3193 void QCPLayoutElement::setMargins(const QMargins &margins) {
3194  if (mMargins != margins) {
3195  mMargins = margins;
3196  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(),
3197  -mMargins.right(), -mMargins.bottom());
3198  }
3199 }
3200 
3210 void QCPLayoutElement::setMinimumMargins(const QMargins &margins) {
3211  if (mMinimumMargins != margins) {
3213  }
3214 }
3215 
3228 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides) {
3229  mAutoMargins = sides;
3230 }
3231 
3246  if (mMinimumSize != size) {
3247  mMinimumSize = size;
3249  }
3250 }
3251 
3260  setMinimumSize(QSize(width, height));
3261 }
3262 
3271  if (mMaximumSize != size) {
3272  mMaximumSize = size;
3274  }
3275 }
3276 
3285  setMaximumSize(QSize(width, height));
3286 }
3287 
3298  SizeConstraintRect constraintRect) {
3299  if (mSizeConstraintRect != constraintRect) {
3300  mSizeConstraintRect = constraintRect;
3302  }
3303 }
3304 
3318 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides,
3319  QCPMarginGroup *group) {
3320  QVector<QCP::MarginSide> sideVector;
3321  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
3322  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
3323  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
3324  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
3325 
3326  for (int i = 0; i < sideVector.size(); ++i) {
3327  QCP::MarginSide side = sideVector.at(i);
3328  if (marginGroup(side) != group) {
3329  QCPMarginGroup *oldGroup = marginGroup(side);
3330  if (oldGroup) // unregister at old group
3331  oldGroup->removeChild(side, this);
3332 
3333  if (!group) // if setting to 0, remove hash entry. Else set hash
3334  // entry to new group and register there
3335  {
3336  mMarginGroups.remove(side);
3337  } else // setting to a new group
3338  {
3339  mMarginGroups[side] = group;
3340  group->addChild(side, this);
3341  }
3342  }
3343  }
3344 }
3345 
3361  if (phase == upMargins) {
3362  if (mAutoMargins != QCP::msNone) {
3363  // set the margins of this layout element according to automatic
3364  // margin calculation, either directly or via a margin group:
3365  QMargins newMargins = mMargins;
3366  QList<QCP::MarginSide> allMarginSides =
3367  QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight
3368  << QCP::msTop << QCP::msBottom;
3369  foreach (QCP::MarginSide side, allMarginSides) {
3370  if (mAutoMargins.testFlag(side)) // this side's margin shall be
3371  // calculated automatically
3372  {
3373  if (mMarginGroups.contains(side))
3375  newMargins, side,
3376  mMarginGroups[side]->commonMargin(
3377  side)); // this side is part of a
3378  // margin group, so get the
3379  // margin value from that group
3380  else
3382  newMargins, side,
3384  side)); // this side is not part of a
3385  // group, so calculate the
3386  // value directly
3387  // apply minimum margin restrictions:
3388  if (QCP::getMarginValue(newMargins, side) <
3391  newMargins, side,
3393  }
3394  }
3395  setMargins(newMargins);
3396  }
3397  }
3398 }
3399 
3417  return QSize(mMargins.left() + mMargins.right(),
3418  mMargins.top() + mMargins.bottom());
3419 }
3420 
3439  return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
3440 }
3441 
3449 QList<QCPLayoutElement *> QCPLayoutElement::elements(bool recursive) const {
3450  Q_UNUSED(recursive)
3451  return QList<QCPLayoutElement *>();
3452 }
3453 
3466 double QCPLayoutElement::selectTest(const QPointF &pos,
3467  bool onlySelectable,
3468  QVariant *details) const {
3469  Q_UNUSED(details)
3470 
3471  if (onlySelectable) return -1;
3472 
3473  if (QRectF(mOuterRect).contains(pos)) {
3474  if (mParentPlot)
3475  return mParentPlot->selectionTolerance() * 0.99;
3476  else {
3477  qDebug() << Q_FUNC_INFO << "parent plot not defined";
3478  return -1;
3479  }
3480  } else
3481  return -1;
3482 }
3483 
3490  foreach (QCPLayoutElement *el, elements(false)) {
3491  if (!el->parentPlot()) el->initializeParentPlot(parentPlot);
3492  }
3493 }
3494 
3506  return qMax(QCP::getMarginValue(mMargins, side),
3508 }
3509 
3523 
3527 
3554 /* start documentation of pure virtual functions */
3555 
3601 /* end documentation of pure virtual functions */
3602 
3608 
3620  QCPLayoutElement::update(phase);
3621 
3622  // set child element rects according to layout:
3623  if (phase == upLayout) updateLayout();
3624 
3625  // propagate update call to child elements:
3626  const int elCount = elementCount();
3627  for (int i = 0; i < elCount; ++i) {
3628  if (QCPLayoutElement *el = elementAt(i)) el->update(phase);
3629  }
3630 }
3631 
3632 /* inherits documentation from base class */
3633 QList<QCPLayoutElement *> QCPLayout::elements(bool recursive) const {
3634  const int c = elementCount();
3635  QList<QCPLayoutElement *> result;
3636 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
3637  result.reserve(c);
3638 #endif
3639  for (int i = 0; i < c; ++i) result.append(elementAt(i));
3640  if (recursive) {
3641  for (int i = 0; i < c; ++i) {
3642  if (result.at(i)) result << result.at(i)->elements(recursive);
3643  }
3644  }
3645  return result;
3646 }
3647 
3656 
3668 bool QCPLayout::removeAt(int index) {
3669  if (QCPLayoutElement *el = takeAt(index)) {
3670  delete el;
3671  return true;
3672  } else
3673  return false;
3674 }
3675 
3688  if (take(element)) {
3689  delete element;
3690  return true;
3691  } else
3692  return false;
3693 }
3694 
3702  for (int i = elementCount() - 1; i >= 0; --i) {
3703  if (elementAt(i)) removeAt(i);
3704  }
3705  simplify();
3706 }
3707 
3719  if (QWidget *w = qobject_cast<QWidget *>(parent()))
3720  w->updateGeometry();
3721  else if (QCPLayout *l = qobject_cast<QCPLayout *>(parent()))
3722  l->sizeConstraintsChanged();
3723 }
3724 
3739 
3755  if (el) {
3756  el->mParentLayout = this;
3757  el->setParentLayerable(this);
3758  el->setParent(this);
3759  if (!el->parentPlot()) el->initializeParentPlot(mParentPlot);
3760  el->layoutChanged();
3761  } else
3762  qDebug() << Q_FUNC_INFO << "Null element passed";
3763 }
3764 
3777  if (el) {
3778  el->mParentLayout = 0;
3779  el->setParentLayerable(0);
3780  el->setParent(mParentPlot);
3781  // Note: Don't initializeParentPlot(0) here, because layout element will
3782  // stay in same parent plot
3783  } else
3784  qDebug() << Q_FUNC_INFO << "Null element passed";
3785 }
3786 
3822 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes,
3823  QVector<int> minSizes,
3824  QVector<double> stretchFactors,
3825  int totalSize) const {
3826  if (maxSizes.size() != minSizes.size() ||
3827  minSizes.size() != stretchFactors.size()) {
3828  qDebug() << Q_FUNC_INFO
3829  << "Passed vector sizes aren't equal:" << maxSizes << minSizes
3830  << stretchFactors;
3831  return QVector<int>();
3832  }
3833  if (stretchFactors.isEmpty()) return QVector<int>();
3834  int sectionCount = stretchFactors.size();
3835  QVector<double> sectionSizes(sectionCount);
3836  // if provided total size is forced smaller than total minimum size, ignore
3837  // minimum sizes (squeeze sections):
3838  int minSizeSum = 0;
3839  for (int i = 0; i < sectionCount; ++i) minSizeSum += minSizes.at(i);
3840  if (totalSize < minSizeSum) {
3841  // new stretch factors are minimum sizes and minimum sizes are set to
3842  // zero:
3843  for (int i = 0; i < sectionCount; ++i) {
3844  stretchFactors[i] = minSizes.at(i);
3845  minSizes[i] = 0;
3846  }
3847  }
3848 
3849  QList<int> minimumLockedSections;
3850  QList<int> unfinishedSections;
3851  for (int i = 0; i < sectionCount; ++i) unfinishedSections.append(i);
3852  double freeSize = totalSize;
3853 
3854  int outerIterations = 0;
3855  while (!unfinishedSections.isEmpty() &&
3856  outerIterations <
3857  sectionCount *
3858  2) // the iteration check ist just a failsafe in
3859  // case something really strange happens
3860  {
3861  ++outerIterations;
3862  int innerIterations = 0;
3863  while (!unfinishedSections.isEmpty() &&
3864  innerIterations <
3865  sectionCount *
3866  2) // the iteration check ist just a failsafe in
3867  // case something really strange happens
3868  {
3869  ++innerIterations;
3870  // find section that hits its maximum next:
3871  int nextId = -1;
3872  double nextMax = 1e12;
3873  for (int i = 0; i < unfinishedSections.size(); ++i) {
3874  int secId = unfinishedSections.at(i);
3875  double hitsMaxAt =
3876  (maxSizes.at(secId) - sectionSizes.at(secId)) /
3877  stretchFactors.at(secId);
3878  if (hitsMaxAt < nextMax) {
3879  nextMax = hitsMaxAt;
3880  nextId = secId;
3881  }
3882  }
3883  // check if that maximum is actually within the bounds of the total
3884  // size (i.e. can we stretch all remaining sections so far that the
3885  // found section actually hits its maximum, without exceeding the
3886  // total size when we add up all sections)
3887  double stretchFactorSum = 0;
3888  for (int i = 0; i < unfinishedSections.size(); ++i)
3889  stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
3890  double nextMaxLimit = freeSize / stretchFactorSum;
3891  if (nextMax <
3892  nextMaxLimit) // next maximum is actually hit, move forward to
3893  // that point and fix the size of that section
3894  {
3895  for (int i = 0; i < unfinishedSections.size(); ++i) {
3896  sectionSizes[unfinishedSections.at(i)] +=
3897  nextMax * stretchFactors.at(unfinishedSections.at(
3898  i)); // increment all sections
3899  freeSize -= nextMax *
3900  stretchFactors.at(unfinishedSections.at(i));
3901  }
3902  unfinishedSections.removeOne(
3903  nextId); // exclude the section that is now at maximum
3904  // from further changes
3905  } else // next maximum isn't hit, just distribute rest of free
3906  // space on remaining sections
3907  {
3908  for (int i = 0; i < unfinishedSections.size(); ++i)
3909  sectionSizes[unfinishedSections.at(i)] +=
3910  nextMaxLimit *
3911  stretchFactors.at(unfinishedSections.at(
3912  i)); // increment all sections
3913  unfinishedSections.clear();
3914  }
3915  }
3916  if (innerIterations == sectionCount * 2)
3917  qDebug() << Q_FUNC_INFO
3918  << "Exceeded maximum expected inner iteration count, "
3919  "layouting aborted. Input was:"
3920  << maxSizes << minSizes << stretchFactors << totalSize;
3921 
3922  // now check whether the resulting section sizes violate minimum
3923  // restrictions:
3924  bool foundMinimumViolation = false;
3925  for (int i = 0; i < sectionSizes.size(); ++i) {
3926  if (minimumLockedSections.contains(i)) continue;
3927  if (sectionSizes.at(i) <
3928  minSizes.at(i)) // section violates minimum
3929  {
3930  sectionSizes[i] = minSizes.at(i); // set it to minimum
3931  foundMinimumViolation = true; // make sure we repeat the whole
3932  // optimization process
3933  minimumLockedSections.append(i);
3934  }
3935  }
3936  if (foundMinimumViolation) {
3937  freeSize = totalSize;
3938  for (int i = 0; i < sectionCount; ++i) {
3939  if (!minimumLockedSections.contains(
3940  i)) // only put sections that haven't hit their
3941  // minimum back into the pool
3942  unfinishedSections.append(i);
3943  else
3944  freeSize -= sectionSizes.at(
3945  i); // remove size of minimum locked sections from
3946  // available space in next round
3947  }
3948  // reset all section sizes to zero that are in unfinished sections
3949  // (all others have been set to their minimum):
3950  for (int i = 0; i < unfinishedSections.size(); ++i)
3951  sectionSizes[unfinishedSections.at(i)] = 0;
3952  }
3953  }
3954  if (outerIterations == sectionCount * 2)
3955  qDebug() << Q_FUNC_INFO
3956  << "Exceeded maximum expected outer iteration count, "
3957  "layouting aborted. Input was:"
3958  << maxSizes << minSizes << stretchFactors << totalSize;
3959 
3960  QVector<int> result(sectionCount);
3961  for (int i = 0; i < sectionCount; ++i)
3962  result[i] = qRound(sectionSizes.at(i));
3963  return result;
3964 }
3965 
3979  QSize minOuterHint = el->minimumOuterSizeHint();
3980  QSize minOuter =
3981  el->minimumSize(); // depending on sizeConstraitRect this might be
3982  // with respect to inner rect, so possibly add
3983  // margins in next four lines (preserving unset
3984  // minimum of 0)
3985  if (minOuter.width() > 0 &&
3987  minOuter.rwidth() += el->margins().left() + el->margins().right();
3988  if (minOuter.height() > 0 &&
3990  minOuter.rheight() += el->margins().top() + el->margins().bottom();
3991 
3992  return QSize(
3993  minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(),
3994  minOuter.height() > 0 ? minOuter.height() : minOuterHint.height());
3995  ;
3996 }
3997 
4011  QSize maxOuterHint = el->maximumOuterSizeHint();
4012  QSize maxOuter =
4013  el->maximumSize(); // depending on sizeConstraitRect this might be
4014  // with respect to inner rect, so possibly add
4015  // margins in next four lines (preserving unset
4016  // maximum of QWIDGETSIZE_MAX)
4017  if (maxOuter.width() < QWIDGETSIZE_MAX &&
4019  maxOuter.rwidth() += el->margins().left() + el->margins().right();
4020  if (maxOuter.height() < QWIDGETSIZE_MAX &&
4022  maxOuter.rheight() += el->margins().top() + el->margins().bottom();
4023 
4024  return QSize(maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width()
4025  : maxOuterHint.width(),
4026  maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height()
4027  : maxOuterHint.height());
4028 }
4029 
4033 
4057 /* start documentation of inline functions */
4058 
4073 /* end documentation of inline functions */
4074 
4079  : mColumnSpacing(5), mRowSpacing(5), mWrap(0), mFillOrder(foColumnsFirst) {}
4080 
4082  // clear all child layout elements. This is important because only the
4083  // specific layouts know how to handle removing elements (clear calls
4084  // virtual removeAt method to do that).
4085  clear();
4086 }
4087 
4097 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const {
4098  if (row >= 0 && row < mElements.size()) {
4099  if (column >= 0 && column < mElements.first().size()) {
4100  if (QCPLayoutElement *result = mElements.at(row).at(column))
4101  return result;
4102  else
4103  qDebug() << Q_FUNC_INFO
4104  << "Requested cell is empty. Row:" << row
4105  << "Column:" << column;
4106  } else
4107  qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row
4108  << "Column:" << column;
4109  } else
4110  qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row
4111  << "Column:" << column;
4112  return 0;
4113 }
4114 
4129 bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element) {
4130  if (!hasElement(row, column)) {
4131  if (element && element->layout()) // remove from old layout first
4132  element->layout()->take(element);
4133  expandTo(row + 1, column + 1);
4134  mElements[row][column] = element;
4136  return true;
4137  } else
4138  qDebug() << Q_FUNC_INFO
4139  << "There is already an element in the specified row/column:"
4140  << row << column;
4141  return false;
4142 }
4143 
4156  int rowIndex = 0;
4157  int colIndex = 0;
4158  if (mFillOrder == foColumnsFirst) {
4159  while (hasElement(rowIndex, colIndex)) {
4160  ++colIndex;
4161  if (colIndex >= mWrap && mWrap > 0) {
4162  colIndex = 0;
4163  ++rowIndex;
4164  }
4165  }
4166  } else {
4167  while (hasElement(rowIndex, colIndex)) {
4168  ++rowIndex;
4169  if (rowIndex >= mWrap && mWrap > 0) {
4170  rowIndex = 0;
4171  ++colIndex;
4172  }
4173  }
4174  }
4175  return addElement(rowIndex, colIndex, element);
4176 }
4177 
4184 bool QCPLayoutGrid::hasElement(int row, int column) {
4185  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
4186  return mElements.at(row).at(column);
4187  else
4188  return false;
4189 }
4190 
4204 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor) {
4205  if (column >= 0 && column < columnCount()) {
4206  if (factor > 0)
4207  mColumnStretchFactors[column] = factor;
4208  else
4209  qDebug() << Q_FUNC_INFO
4210  << "Invalid stretch factor, must be positive:" << factor;
4211  } else
4212  qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
4213 }
4214 
4229 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors) {
4230  if (factors.size() == mColumnStretchFactors.size()) {
4231  mColumnStretchFactors = factors;
4232  for (int i = 0; i < mColumnStretchFactors.size(); ++i) {
4233  if (mColumnStretchFactors.at(i) <= 0) {
4234  qDebug() << Q_FUNC_INFO
4235  << "Invalid stretch factor, must be positive:"
4236  << mColumnStretchFactors.at(i);
4237  mColumnStretchFactors[i] = 1;
4238  }
4239  }
4240  } else
4241  qDebug() << Q_FUNC_INFO
4242  << "Column count not equal to passed stretch factor count:"
4243  << factors;
4244 }
4245 
4259 void QCPLayoutGrid::setRowStretchFactor(int row, double factor) {
4260  if (row >= 0 && row < rowCount()) {
4261  if (factor > 0)
4262  mRowStretchFactors[row] = factor;
4263  else
4264  qDebug() << Q_FUNC_INFO
4265  << "Invalid stretch factor, must be positive:" << factor;
4266  } else
4267  qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
4268 }
4269 
4284 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors) {
4285  if (factors.size() == mRowStretchFactors.size()) {
4286  mRowStretchFactors = factors;
4287  for (int i = 0; i < mRowStretchFactors.size(); ++i) {
4288  if (mRowStretchFactors.at(i) <= 0) {
4289  qDebug() << Q_FUNC_INFO
4290  << "Invalid stretch factor, must be positive:"
4291  << mRowStretchFactors.at(i);
4292  mRowStretchFactors[i] = 1;
4293  }
4294  }
4295  } else
4296  qDebug() << Q_FUNC_INFO
4297  << "Row count not equal to passed stretch factor count:"
4298  << factors;
4299 }
4300 
4306 void QCPLayoutGrid::setColumnSpacing(int pixels) { mColumnSpacing = pixels; }
4307 
4313 void QCPLayoutGrid::setRowSpacing(int pixels) { mRowSpacing = pixels; }
4314 
4333 void QCPLayoutGrid::setWrap(int count) { mWrap = qMax(0, count); }
4334 
4363 void QCPLayoutGrid::setFillOrder(FillOrder order, bool rearrange) {
4364  // if rearranging, take all elements via linear index of old fill order:
4365  const int elCount = elementCount();
4366  QVector<QCPLayoutElement *> tempElements;
4367  if (rearrange) {
4368  tempElements.reserve(elCount);
4369  for (int i = 0; i < elCount; ++i) {
4370  if (elementAt(i)) tempElements.append(takeAt(i));
4371  }
4372  simplify();
4373  }
4374  // change fill order as requested:
4375  mFillOrder = order;
4376  // if rearranging, re-insert via linear index according to new fill order:
4377  if (rearrange) {
4378  for (int i = 0; i < tempElements.size(); ++i)
4379  addElement(tempElements.at(i));
4380  }
4381 }
4382 
4398 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount) {
4399  // add rows as necessary:
4400  while (rowCount() < newRowCount) {
4401  mElements.append(QList<QCPLayoutElement *>());
4402  mRowStretchFactors.append(1);
4403  }
4404  // go through rows and expand columns as necessary:
4405  int newColCount = qMax(columnCount(), newColumnCount);
4406  for (int i = 0; i < rowCount(); ++i) {
4407  while (mElements.at(i).size() < newColCount) mElements[i].append(0);
4408  }
4409  while (mColumnStretchFactors.size() < newColCount)
4410  mColumnStretchFactors.append(1);
4411 }
4412 
4420 void QCPLayoutGrid::insertRow(int newIndex) {
4421  if (mElements.isEmpty() ||
4422  mElements.first()
4423  .isEmpty()) // if grid is completely empty, add first cell
4424  {
4425  expandTo(1, 1);
4426  return;
4427  }
4428 
4429  if (newIndex < 0) newIndex = 0;
4430  if (newIndex > rowCount()) newIndex = rowCount();
4431 
4432  mRowStretchFactors.insert(newIndex, 1);
4433  QList<QCPLayoutElement *> newRow;
4434  for (int col = 0; col < columnCount(); ++col)
4435  newRow.append((QCPLayoutElement *)0);
4436  mElements.insert(newIndex, newRow);
4437 }
4438 
4446 void QCPLayoutGrid::insertColumn(int newIndex) {
4447  if (mElements.isEmpty() ||
4448  mElements.first()
4449  .isEmpty()) // if grid is completely empty, add first cell
4450  {
4451  expandTo(1, 1);
4452  return;
4453  }
4454 
4455  if (newIndex < 0) newIndex = 0;
4456  if (newIndex > columnCount()) newIndex = columnCount();
4457 
4458  mColumnStretchFactors.insert(newIndex, 1);
4459  for (int row = 0; row < rowCount(); ++row)
4460  mElements[row].insert(newIndex, (QCPLayoutElement *)0);
4461 }
4462 
4478 int QCPLayoutGrid::rowColToIndex(int row, int column) const {
4479  if (row >= 0 && row < rowCount()) {
4480  if (column >= 0 && column < columnCount()) {
4481  switch (mFillOrder) {
4482  case foRowsFirst:
4483  return column * rowCount() + row;
4484  case foColumnsFirst:
4485  return row * columnCount() + column;
4486  }
4487  } else
4488  qDebug() << Q_FUNC_INFO << "row index out of bounds:" << row;
4489  } else
4490  qDebug() << Q_FUNC_INFO << "column index out of bounds:" << column;
4491  return 0;
4492 }
4493 
4512 void QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const {
4513  row = -1;
4514  column = -1;
4515  const int nCols = columnCount();
4516  const int nRows = rowCount();
4517  if (nCols == 0 || nRows == 0) return;
4518  if (index < 0 || index >= elementCount()) {
4519  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
4520  return;
4521  }
4522 
4523  switch (mFillOrder) {
4524  case foRowsFirst: {
4525  column = index / nRows;
4526  row = index % nRows;
4527  break;
4528  }
4529  case foColumnsFirst: {
4530  row = index / nCols;
4531  column = index % nCols;
4532  break;
4533  }
4534  }
4535 }
4536 
4537 /* inherits documentation from base class */
4539  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
4540  getMinimumRowColSizes(&minColWidths, &minRowHeights);
4541  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4542 
4543  int totalRowSpacing = (rowCount() - 1) * mRowSpacing;
4544  int totalColSpacing = (columnCount() - 1) * mColumnSpacing;
4545  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths,
4546  mColumnStretchFactors.toVector(),
4547  mRect.width() - totalColSpacing);
4548  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights,
4549  mRowStretchFactors.toVector(),
4550  mRect.height() - totalRowSpacing);
4551 
4552  // go through cells and set rects accordingly:
4553  int yOffset = mRect.top();
4554  for (int row = 0; row < rowCount(); ++row) {
4555  if (row > 0) yOffset += rowHeights.at(row - 1) + mRowSpacing;
4556  int xOffset = mRect.left();
4557  for (int col = 0; col < columnCount(); ++col) {
4558  if (col > 0) xOffset += colWidths.at(col - 1) + mColumnSpacing;
4559  if (mElements.at(row).at(col))
4560  mElements.at(row).at(col)->setOuterRect(
4561  QRect(xOffset, yOffset, colWidths.at(col),
4562  rowHeights.at(row)));
4563  }
4564  }
4565 }
4566 
4576  if (index >= 0 && index < elementCount()) {
4577  int row, col;
4578  indexToRowCol(index, row, col);
4579  return mElements.at(row).at(col);
4580  } else
4581  return 0;
4582 }
4583 
4593  if (QCPLayoutElement *el = elementAt(index)) {
4594  releaseElement(el);
4595  int row, col;
4596  indexToRowCol(index, row, col);
4597  mElements[row][col] = 0;
4598  return el;
4599  } else {
4600  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
4601  return 0;
4602  }
4603 }
4604 
4605 /* inherits documentation from base class */
4607  if (element) {
4608  for (int i = 0; i < elementCount(); ++i) {
4609  if (elementAt(i) == element) {
4610  takeAt(i);
4611  return true;
4612  }
4613  }
4614  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
4615  } else
4616  qDebug() << Q_FUNC_INFO << "Can't take null element";
4617  return false;
4618 }
4619 
4620 /* inherits documentation from base class */
4621 QList<QCPLayoutElement *> QCPLayoutGrid::elements(bool recursive) const {
4622  QList<QCPLayoutElement *> result;
4623  const int elCount = elementCount();
4624 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
4625  result.reserve(elCount);
4626 #endif
4627  for (int i = 0; i < elCount; ++i) result.append(elementAt(i));
4628  if (recursive) {
4629  for (int i = 0; i < elCount; ++i) {
4630  if (result.at(i)) result << result.at(i)->elements(recursive);
4631  }
4632  }
4633  return result;
4634 }
4635 
4641  // remove rows with only empty cells:
4642  for (int row = rowCount() - 1; row >= 0; --row) {
4643  bool hasElements = false;
4644  for (int col = 0; col < columnCount(); ++col) {
4645  if (mElements.at(row).at(col)) {
4646  hasElements = true;
4647  break;
4648  }
4649  }
4650  if (!hasElements) {
4651  mRowStretchFactors.removeAt(row);
4652  mElements.removeAt(row);
4653  if (mElements.isEmpty()) // removed last element, also remove
4654  // stretch factor (wouldn't happen below
4655  // because also columnCount changed to 0
4656  // now)
4657  mColumnStretchFactors.clear();
4658  }
4659  }
4660 
4661  // remove columns with only empty cells:
4662  for (int col = columnCount() - 1; col >= 0; --col) {
4663  bool hasElements = false;
4664  for (int row = 0; row < rowCount(); ++row) {
4665  if (mElements.at(row).at(col)) {
4666  hasElements = true;
4667  break;
4668  }
4669  }
4670  if (!hasElements) {
4671  mColumnStretchFactors.removeAt(col);
4672  for (int row = 0; row < rowCount(); ++row)
4673  mElements[row].removeAt(col);
4674  }
4675  }
4676 }
4677 
4678 /* inherits documentation from base class */
4680  QVector<int> minColWidths, minRowHeights;
4681  getMinimumRowColSizes(&minColWidths, &minRowHeights);
4682  QSize result(0, 0);
4683  for (int i = 0; i < minColWidths.size(); ++i)
4684  result.rwidth() += minColWidths.at(i);
4685  for (int i = 0; i < minRowHeights.size(); ++i)
4686  result.rheight() += minRowHeights.at(i);
4687  result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing;
4688  result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing;
4689  result.rwidth() += mMargins.left() + mMargins.right();
4690  result.rheight() += mMargins.top() + mMargins.bottom();
4691  return result;
4692 }
4693 
4694 /* inherits documentation from base class */
4696  QVector<int> maxColWidths, maxRowHeights;
4697  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4698 
4699  QSize result(0, 0);
4700  for (int i = 0; i < maxColWidths.size(); ++i)
4701  result.setWidth(
4702  qMin(result.width() + maxColWidths.at(i), QWIDGETSIZE_MAX));
4703  for (int i = 0; i < maxRowHeights.size(); ++i)
4704  result.setHeight(
4705  qMin(result.height() + maxRowHeights.at(i), QWIDGETSIZE_MAX));
4706  result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing;
4707  result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing;
4708  result.rwidth() += mMargins.left() + mMargins.right();
4709  result.rheight() += mMargins.top() + mMargins.bottom();
4710  if (result.height() > QWIDGETSIZE_MAX) result.setHeight(QWIDGETSIZE_MAX);
4711  if (result.width() > QWIDGETSIZE_MAX) result.setWidth(QWIDGETSIZE_MAX);
4712  return result;
4713 }
4714 
4728 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths,
4729  QVector<int> *minRowHeights) const {
4730  *minColWidths = QVector<int>(columnCount(), 0);
4731  *minRowHeights = QVector<int>(rowCount(), 0);
4732  for (int row = 0; row < rowCount(); ++row) {
4733  for (int col = 0; col < columnCount(); ++col) {
4734  if (QCPLayoutElement *el = mElements.at(row).at(col)) {
4735  QSize minSize = getFinalMinimumOuterSize(el);
4736  if (minColWidths->at(col) < minSize.width())
4737  (*minColWidths)[col] = minSize.width();
4738  if (minRowHeights->at(row) < minSize.height())
4739  (*minRowHeights)[row] = minSize.height();
4740  }
4741  }
4742  }
4743 }
4744 
4758 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths,
4759  QVector<int> *maxRowHeights) const {
4760  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
4761  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
4762  for (int row = 0; row < rowCount(); ++row) {
4763  for (int col = 0; col < columnCount(); ++col) {
4764  if (QCPLayoutElement *el = mElements.at(row).at(col)) {
4765  QSize maxSize = getFinalMaximumOuterSize(el);
4766  if (maxColWidths->at(col) > maxSize.width())
4767  (*maxColWidths)[col] = maxSize.width();
4768  if (maxRowHeights->at(row) > maxSize.height())
4769  (*maxRowHeights)[row] = maxSize.height();
4770  }
4771  }
4772  }
4773 }
4774 
4778 
4800 /* start documentation of inline functions */
4801 
4808 /* end documentation of inline functions */
4809 
4814 
4816  // clear all child layout elements. This is important because only the
4817  // specific layouts know how to handle removing elements (clear calls
4818  // virtual removeAt method to do that).
4819  clear();
4820 }
4821 
4826  if (elementAt(index))
4827  return mInsetPlacement.at(index);
4828  else {
4829  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4830  return ipFree;
4831  }
4832 }
4833 
4840  if (elementAt(index))
4841  return mInsetAlignment.at(index);
4842  else {
4843  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4844 #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
4845  return nullptr;
4846 #else
4847  return {};
4848 #endif
4849  }
4850 }
4851 
4856 QRectF QCPLayoutInset::insetRect(int index) const {
4857  if (elementAt(index))
4858  return mInsetRect.at(index);
4859  else {
4860  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4861  return QRectF();
4862  }
4863 }
4864 
4872  int index, QCPLayoutInset::InsetPlacement placement) {
4873  if (elementAt(index))
4874  mInsetPlacement[index] = placement;
4875  else
4876  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4877 }
4878 
4889  if (elementAt(index))
4890  mInsetAlignment[index] = alignment;
4891  else
4892  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4893 }
4894 
4909 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect) {
4910  if (elementAt(index))
4911  mInsetRect[index] = rect;
4912  else
4913  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4914 }
4915 
4916 /* inherits documentation from base class */
4918  for (int i = 0; i < mElements.size(); ++i) {
4919  QCPLayoutElement *el = mElements.at(i);
4920  QRect insetRect;
4921  QSize finalMinSize = getFinalMinimumOuterSize(el);
4922  QSize finalMaxSize = getFinalMaximumOuterSize(el);
4923  if (mInsetPlacement.at(i) == ipFree) {
4924  insetRect =
4925  QRect(rect().x() + rect().width() * mInsetRect.at(i).x(),
4926  rect().y() + rect().height() * mInsetRect.at(i).y(),
4927  rect().width() * mInsetRect.at(i).width(),
4928  rect().height() * mInsetRect.at(i).height());
4929  if (insetRect.size().width() < finalMinSize.width())
4930  insetRect.setWidth(finalMinSize.width());
4931  if (insetRect.size().height() < finalMinSize.height())
4932  insetRect.setHeight(finalMinSize.height());
4933  if (insetRect.size().width() > finalMaxSize.width())
4934  insetRect.setWidth(finalMaxSize.width());
4935  if (insetRect.size().height() > finalMaxSize.height())
4936  insetRect.setHeight(finalMaxSize.height());
4937  } else if (mInsetPlacement.at(i) == ipBorderAligned) {
4938  insetRect.setSize(finalMinSize);
4939  Qt::Alignment al = mInsetAlignment.at(i);
4940  if (al.testFlag(Qt::AlignLeft))
4941  insetRect.moveLeft(rect().x());
4942  else if (al.testFlag(Qt::AlignRight))
4943  insetRect.moveRight(rect().x() + rect().width());
4944  else
4945  insetRect.moveLeft(rect().x() + rect().width() * 0.5 -
4946  finalMinSize.width() *
4947  0.5); // default to Qt::AlignHCenter
4948  if (al.testFlag(Qt::AlignTop))
4949  insetRect.moveTop(rect().y());
4950  else if (al.testFlag(Qt::AlignBottom))
4951  insetRect.moveBottom(rect().y() + rect().height());
4952  else
4953  insetRect.moveTop(rect().y() + rect().height() * 0.5 -
4954  finalMinSize.height() *
4955  0.5); // default to Qt::AlignVCenter
4956  }
4957  mElements.at(i)->setOuterRect(insetRect);
4958  }
4959 }
4960 
4961 /* inherits documentation from base class */
4962 int QCPLayoutInset::elementCount() const { return mElements.size(); }
4963 
4964 /* inherits documentation from base class */
4966  if (index >= 0 && index < mElements.size())
4967  return mElements.at(index);
4968  else
4969  return 0;
4970 }
4971 
4972 /* inherits documentation from base class */
4974  if (QCPLayoutElement *el = elementAt(index)) {
4975  releaseElement(el);
4976  mElements.removeAt(index);
4977  mInsetPlacement.removeAt(index);
4978  mInsetAlignment.removeAt(index);
4979  mInsetRect.removeAt(index);
4980  return el;
4981  } else {
4982  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
4983  return 0;
4984  }
4985 }
4986 
4987 /* inherits documentation from base class */
4989  if (element) {
4990  for (int i = 0; i < elementCount(); ++i) {
4991  if (elementAt(i) == element) {
4992  takeAt(i);
4993  return true;
4994  }
4995  }
4996  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
4997  } else
4998  qDebug() << Q_FUNC_INFO << "Can't take null element";
4999  return false;
5000 }
5001 
5013 double QCPLayoutInset::selectTest(const QPointF &pos,
5014  bool onlySelectable,
5015  QVariant *details) const {
5016  Q_UNUSED(details)
5017  if (onlySelectable) return -1;
5018 
5019  for (int i = 0; i < mElements.size(); ++i) {
5020  // inset layout shall only return positive selectTest, if actually an
5021  // inset object is at pos else it would block the entire underlying
5022  // QCPAxisRect with its surface.
5023  if (mElements.at(i)->realVisibility() &&
5024  mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
5025  return mParentPlot->selectionTolerance() * 0.99;
5026  }
5027  return -1;
5028 }
5029 
5042  Qt::Alignment alignment) {
5043  if (element) {
5044  if (element->layout()) // remove from old layout first
5045  element->layout()->take(element);
5046  mElements.append(element);
5048  mInsetAlignment.append(alignment);
5049  mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
5050  adoptElement(element);
5051  } else
5052  qDebug() << Q_FUNC_INFO << "Can't add null element";
5053 }
5054 
5067 void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) {
5068  if (element) {
5069  if (element->layout()) // remove from old layout first
5070  element->layout()->take(element);
5071  mElements.append(element);
5072  mInsetPlacement.append(ipFree);
5073  mInsetAlignment.append(Qt::AlignRight | Qt::AlignTop);
5074  mInsetRect.append(rect);
5075  adoptElement(element);
5076  } else
5077  qDebug() << Q_FUNC_INFO << "Can't add null element";
5078 }
5079 /* end of 'src/layout.cpp' */
5080 
5081 /* including file 'src/lineending.cpp', size 11536 */
5082 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
5083 
5087 
5115  : mStyle(esNone), mWidth(8), mLength(10), mInverted(false) {}
5116 
5121  double width,
5122  double length,
5123  bool inverted)
5124  : mStyle(style), mWidth(width), mLength(length), mInverted(inverted) {}
5125 
5130  mStyle = style;
5131 }
5132 
5141 
5148 void QCPLineEnding::setLength(double length) { mLength = length; }
5149 
5160 
5172  switch (mStyle) {
5173  case esNone:
5174  return 0;
5175 
5176  case esFlatArrow:
5177  case esSpikeArrow:
5178  case esLineArrow:
5179  case esSkewedBar:
5180  return qSqrt(mWidth * mWidth +
5181  mLength *
5182  mLength); // items that have width and length
5183 
5184  case esDisc:
5185  case esSquare:
5186  case esDiamond:
5187  case esBar:
5188  case esHalfBar:
5189  return mWidth *
5190  1.42; // items that only have a width -> width*sqrt(2)
5191  }
5192  return 0;
5193 }
5194 
5208  switch (mStyle) {
5209  case esNone:
5210  case esLineArrow:
5211  case esSkewedBar:
5212  case esBar:
5213  case esHalfBar:
5214  return 0;
5215 
5216  case esFlatArrow:
5217  return mLength;
5218 
5219  case esDisc:
5220  case esSquare:
5221  case esDiamond:
5222  return mWidth * 0.5;
5223 
5224  case esSpikeArrow:
5225  return mLength * 0.8;
5226  }
5227  return 0;
5228 }
5229 
5236  const QCPVector2D &pos,
5237  const QCPVector2D &dir) const {
5238  if (mStyle == esNone) return;
5239 
5240  QCPVector2D lengthVec = dir.normalized() * mLength * (mInverted ? -1 : 1);
5241  if (lengthVec.isNull()) lengthVec = QCPVector2D(1, 0);
5242  QCPVector2D widthVec = dir.normalized().perpendicular() * mWidth * 0.5 *
5243  (mInverted ? -1 : 1);
5244 
5245  QPen penBackup = painter->pen();
5246  QBrush brushBackup = painter->brush();
5247  QPen miterPen = penBackup;
5248  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
5249  QBrush brush(painter->pen().color(), Qt::SolidPattern);
5250  switch (mStyle) {
5251  case esNone:
5252  break;
5253  case esFlatArrow: {
5254  QPointF points[3] = {pos.toPointF(),
5255  (pos - lengthVec + widthVec).toPointF(),
5256  (pos - lengthVec - widthVec).toPointF()};
5257  painter->setPen(miterPen);
5258  painter->setBrush(brush);
5259  painter->drawConvexPolygon(points, 3);
5260  painter->setBrush(brushBackup);
5261  painter->setPen(penBackup);
5262  break;
5263  }
5264  case esSpikeArrow: {
5265  QPointF points[4] = {pos.toPointF(),
5266  (pos - lengthVec + widthVec).toPointF(),
5267  (pos - lengthVec * 0.8).toPointF(),
5268  (pos - lengthVec - widthVec).toPointF()};
5269  painter->setPen(miterPen);
5270  painter->setBrush(brush);
5271  painter->drawConvexPolygon(points, 4);
5272  painter->setBrush(brushBackup);
5273  painter->setPen(penBackup);
5274  break;
5275  }
5276  case esLineArrow: {
5277  QPointF points[3] = {(pos - lengthVec + widthVec).toPointF(),
5278  pos.toPointF(),
5279  (pos - lengthVec - widthVec).toPointF()};
5280  painter->setPen(miterPen);
5281  painter->drawPolyline(points, 3);
5282  painter->setPen(penBackup);
5283  break;
5284  }
5285  case esDisc: {
5286  painter->setBrush(brush);
5287  painter->drawEllipse(pos.toPointF(), mWidth * 0.5, mWidth * 0.5);
5288  painter->setBrush(brushBackup);
5289  break;
5290  }
5291  case esSquare: {
5292  QCPVector2D widthVecPerp = widthVec.perpendicular();
5293  QPointF points[4] = {(pos - widthVecPerp + widthVec).toPointF(),
5294  (pos - widthVecPerp - widthVec).toPointF(),
5295  (pos + widthVecPerp - widthVec).toPointF(),
5296  (pos + widthVecPerp + widthVec).toPointF()};
5297  painter->setPen(miterPen);
5298  painter->setBrush(brush);
5299  painter->drawConvexPolygon(points, 4);
5300  painter->setBrush(brushBackup);
5301  painter->setPen(penBackup);
5302  break;
5303  }
5304  case esDiamond: {
5305  QCPVector2D widthVecPerp = widthVec.perpendicular();
5306  QPointF points[4] = {(pos - widthVecPerp).toPointF(),
5307  (pos - widthVec).toPointF(),
5308  (pos + widthVecPerp).toPointF(),
5309  (pos + widthVec).toPointF()};
5310  painter->setPen(miterPen);
5311  painter->setBrush(brush);
5312  painter->drawConvexPolygon(points, 4);
5313  painter->setBrush(brushBackup);
5314  painter->setPen(penBackup);
5315  break;
5316  }
5317  case esBar: {
5318  painter->drawLine((pos + widthVec).toPointF(),
5319  (pos - widthVec).toPointF());
5320  break;
5321  }
5322  case esHalfBar: {
5323  painter->drawLine((pos + widthVec).toPointF(), pos.toPointF());
5324  break;
5325  }
5326  case esSkewedBar: {
5327  if (qFuzzyIsNull(painter->pen().widthF()) &&
5328  !painter->modes().testFlag(QCPPainter::pmNonCosmetic)) {
5329  // if drawing with cosmetic pen (perfectly thin stroke, happens
5330  // only in vector exports), draw bar exactly on tip of line
5331  painter->drawLine((pos + widthVec +
5332  lengthVec * 0.2 * (mInverted ? -1 : 1))
5333  .toPointF(),
5334  (pos - widthVec -
5335  lengthVec * 0.2 * (mInverted ? -1 : 1))
5336  .toPointF());
5337  } else {
5338  // if drawing with thick (non-cosmetic) pen, shift bar a little
5339  // in line direction to prevent line from sticking through bar
5340  // slightly
5341  painter->drawLine(
5342  (pos + widthVec +
5343  lengthVec * 0.2 * (mInverted ? -1 : 1) +
5344  dir.normalized() *
5345  qMax(1.0f, (float)painter->pen().widthF()) *
5346  0.5f)
5347  .toPointF(),
5348  (pos - widthVec -
5349  lengthVec * 0.2 * (mInverted ? -1 : 1) +
5350  dir.normalized() *
5351  qMax(1.0f, (float)painter->pen().widthF()) *
5352  0.5f)
5353  .toPointF());
5354  }
5355  break;
5356  }
5357  }
5358 }
5359 
5367  const QCPVector2D &pos,
5368  double angle) const {
5369  draw(painter, pos, QCPVector2D(qCos(angle), qSin(angle)));
5370 }
5371 /* end of 'src/lineending.cpp' */
5372 
5373 /* including file 'src/axis/axisticker.cpp', size 18664 */
5374 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
5375 
5379 
5450  : mTickStepStrategy(tssReadability), mTickCount(5), mTickOrigin(0) {}
5451 
5453 
5460  mTickStepStrategy = strategy;
5461 }
5462 
5472  if (count > 0)
5473  mTickCount = count;
5474  else
5475  qDebug() << Q_FUNC_INFO
5476  << "tick count must be greater than zero:" << count;
5477 }
5478 
5490 
5507  const QLocale &locale,
5508  QChar formatChar,
5509  int precision,
5510  QVector<double> &ticks,
5511  QVector<double> *subTicks,
5512  QVector<QString> *tickLabels) {
5513  // generate (major) ticks:
5514  double tickStep = getTickStep(range);
5515  ticks = createTickVector(tickStep, range);
5516  trimTicks(range, ticks,
5517  true); // trim ticks to visible range plus one outer tick on each
5518  // side (incase a subclass createTickVector creates more)
5519 
5520  // generate sub ticks between major ticks:
5521  if (subTicks) {
5522  if (ticks.size() > 0) {
5523  *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks);
5524  trimTicks(range, *subTicks, false);
5525  } else
5526  *subTicks = QVector<double>();
5527  }
5528 
5529  // finally trim also outliers (no further clipping happens in axis drawing):
5530  trimTicks(range, ticks, false);
5531  // generate labels for visible ticks if requested:
5532  if (tickLabels)
5533  *tickLabels = createLabelVector(ticks, locale, formatChar, precision);
5534 }
5535 
5546 double QCPAxisTicker::getTickStep(const QCPRange &range) {
5547  double exactStep =
5548  range.size() /
5549  (double)(mTickCount +
5550  1e-10); // mTickCount ticks on average, the small addition
5551  // is to prevent jitter on exact integers
5552  return cleanMantissa(exactStep);
5553 }
5554 
5563 int QCPAxisTicker::getSubTickCount(double tickStep) {
5564  int result = 1; // default to 1, if no proper value can be found
5565 
5566  // separate integer and fractional part of mantissa:
5567  double epsilon = 0.01;
5568  double intPartf;
5569  int intPart;
5570  double fracPart = modf(getMantissa(tickStep), &intPartf);
5571  intPart = intPartf;
5572 
5573  // handle cases with (almost) integer mantissa:
5574  if (fracPart < epsilon || 1.0 - fracPart < epsilon) {
5575  if (1.0 - fracPart < epsilon) ++intPart;
5576  switch (intPart) {
5577  case 1:
5578  result = 4;
5579  break; // 1.0 -> 0.2 substep
5580  case 2:
5581  result = 3;
5582  break; // 2.0 -> 0.5 substep
5583  case 3:
5584  result = 2;
5585  break; // 3.0 -> 1.0 substep
5586  case 4:
5587  result = 3;
5588  break; // 4.0 -> 1.0 substep
5589  case 5:
5590  result = 4;
5591  break; // 5.0 -> 1.0 substep
5592  case 6:
5593  result = 2;
5594  break; // 6.0 -> 2.0 substep
5595  case 7:
5596  result = 6;
5597  break; // 7.0 -> 1.0 substep
5598  case 8:
5599  result = 3;
5600  break; // 8.0 -> 2.0 substep
5601  case 9:
5602  result = 2;
5603  break; // 9.0 -> 3.0 substep
5604  }
5605  } else {
5606  // handle cases with significantly fractional mantissa:
5607  if (qAbs(fracPart - 0.5) < epsilon) // *.5 mantissa
5608  {
5609  switch (intPart) {
5610  case 1:
5611  result = 2;
5612  break; // 1.5 -> 0.5 substep
5613  case 2:
5614  result = 4;
5615  break; // 2.5 -> 0.5 substep
5616  case 3:
5617  result = 4;
5618  break; // 3.5 -> 0.7 substep
5619  case 4:
5620  result = 2;
5621  break; // 4.5 -> 1.5 substep
5622  case 5:
5623  result = 4;
5624  break; // 5.5 -> 1.1 substep (won't occur with default
5625  // getTickStep from here on)
5626  case 6:
5627  result = 4;
5628  break; // 6.5 -> 1.3 substep
5629  case 7:
5630  result = 2;
5631  break; // 7.5 -> 2.5 substep
5632  case 8:
5633  result = 4;
5634  break; // 8.5 -> 1.7 substep
5635  case 9:
5636  result = 4;
5637  break; // 9.5 -> 1.9 substep
5638  }
5639  }
5640  // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub
5641  // tick marks, leave default
5642  }
5643 
5644  return result;
5645 }
5646 
5659 QString QCPAxisTicker::getTickLabel(double tick,
5660  const QLocale &locale,
5661  QChar formatChar,
5662  int precision) {
5663  return locale.toString(tick, formatChar.toLatin1(), precision);
5664 }
5665 
5678  int subTickCount, const QVector<double> &ticks) {
5679  QVector<double> result;
5680  if (subTickCount <= 0 || ticks.size() < 2) return result;
5681 
5682  result.reserve((ticks.size() - 1) * subTickCount);
5683  for (int i = 1; i < ticks.size(); ++i) {
5684  double subTickStep =
5685  (ticks.at(i) - ticks.at(i - 1)) / (double)(subTickCount + 1);
5686  for (int k = 1; k <= subTickCount; ++k)
5687  result.append(ticks.at(i - 1) + k * subTickStep);
5688  }
5689  return result;
5690 }
5691 
5709 QVector<double> QCPAxisTicker::createTickVector(double tickStep,
5710  const QCPRange &range) {
5711  QVector<double> result;
5712  // Generate tick positions according to tickStep:
5713  qint64 firstStep = floor((range.lower - mTickOrigin) /
5714  tickStep); // do not use qFloor here, or we'll
5715  // lose 64 bit precision
5716  qint64 lastStep = ceil(
5717  (range.upper - mTickOrigin) /
5718  tickStep); // do not use qCeil here, or we'll lose 64 bit precision
5719  int tickcount = lastStep - firstStep + 1;
5720  if (tickcount < 0) tickcount = 0;
5721  result.resize(tickcount);
5722  for (int i = 0; i < tickcount; ++i)
5723  result[i] = mTickOrigin + (firstStep + i) * tickStep;
5724  return result;
5725 }
5726 
5737 QVector<QString> QCPAxisTicker::createLabelVector(const QVector<double> &ticks,
5738  const QLocale &locale,
5739  QChar formatChar,
5740  int precision) {
5741  QVector<QString> result;
5742  result.reserve(ticks.size());
5743  for (int i = 0; i < ticks.size(); ++i)
5744  result.append(getTickLabel(ticks.at(i), locale, formatChar, precision));
5745  return result;
5746 }
5747 
5757  QVector<double> &ticks,
5758  bool keepOneOutlier) const {
5759  bool lowFound = false;
5760  bool highFound = false;
5761  int lowIndex = 0;
5762  int highIndex = -1;
5763 
5764  for (int i = 0; i < ticks.size(); ++i) {
5765  if (ticks.at(i) >= range.lower) {
5766  lowFound = true;
5767  lowIndex = i;
5768  break;
5769  }
5770  }
5771  for (int i = ticks.size() - 1; i >= 0; --i) {
5772  if (ticks.at(i) <= range.upper) {
5773  highFound = true;
5774  highIndex = i;
5775  break;
5776  }
5777  }
5778 
5779  if (highFound && lowFound) {
5780  int trimFront = qMax(0, lowIndex - (keepOneOutlier ? 1 : 0));
5781  int trimBack =
5782  qMax(0, ticks.size() - (keepOneOutlier ? 2 : 1) - highIndex);
5783  if (trimFront > 0 || trimBack > 0)
5784  ticks = ticks.mid(trimFront, ticks.size() - trimFront - trimBack);
5785  } else // all ticks are either all below or all above the range
5786  ticks.clear();
5787 }
5788 
5796 double QCPAxisTicker::pickClosest(double target,
5797  const QVector<double> &candidates) const {
5798  if (candidates.size() == 1) return candidates.first();
5799  QVector<double>::const_iterator it = std::lower_bound(
5800  candidates.constBegin(), candidates.constEnd(), target);
5801  if (it == candidates.constEnd())
5802  return *(it - 1);
5803  else if (it == candidates.constBegin())
5804  return *it;
5805  else
5806  return target - *(it - 1) < *it - target ? *(it - 1) : *it;
5807 }
5808 
5817 double QCPAxisTicker::getMantissa(double input, double *magnitude) const {
5818  const double mag = qPow(10.0, qFloor(qLn(input) / qLn(10.0)));
5819  if (magnitude) *magnitude = mag;
5820  return input / mag;
5821 }
5822 
5830 double QCPAxisTicker::cleanMantissa(double input) const {
5831  double magnitude;
5832  const double mantissa = getMantissa(input, &magnitude);
5833  switch (mTickStepStrategy) {
5834  case tssReadability: {
5835  return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5
5836  << 5.0 << 10.0) *
5837  magnitude;
5838  }
5839  case tssMeetTickCount: {
5840  // this gives effectively a mantissa
5841  // of 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 8.0, 10.0
5842  if (mantissa <= 5.0)
5843  return (int)(mantissa * 2) / 2.0 *
5844  magnitude; // round digit after decimal point to 0.5
5845  else
5846  return (int)(mantissa / 2.0) * 2.0 *
5847  magnitude; // round to first digit in multiples of 2
5848  }
5849  }
5850  return input;
5851 }
5852 /* end of 'src/axis/axisticker.cpp' */
5853 
5854 /* including file 'src/axis/axistickerdatetime.cpp', size 14443 */
5855 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
5856 
5860 
5908  : mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
5909  mDateTimeSpec(Qt::LocalTime),
5910  mDateStrategy(dsNone) {
5911  setTickCount(4);
5912 }
5913 
5925 }
5926 
5939  mDateTimeSpec = spec;
5940 }
5941 
5955 }
5956 
5968 }
5969 
5985  double result =
5986  range.size() /
5987  (double)(mTickCount +
5988  1e-10); // mTickCount ticks on average, the small addition
5989  // is to prevent jitter on exact integers
5990 
5992  if (result < 1) // ideal tick step is below 1 second -> use normal clean
5993  // mantissa algorithm in units of seconds
5994  {
5996  } else if (result < 86400 * 30.4375 * 12) // below a year
5997  {
5998  result = pickClosest(
5999  result,
6000  QVector<double>()
6001  << 1 << 2.5 << 5 << 10 << 15 << 30 << 60 << 2.5 * 60
6002  << 5 * 60 << 10 * 60 << 15 * 60 << 30 * 60
6003  << 60 * 60 // second, minute, hour range
6004  << 3600 * 2 << 3600 * 3 << 3600 * 6 << 3600 * 12
6005  << 3600 * 24 // hour to day range
6006  << 86400 * 2 << 86400 * 5 << 86400 * 7 << 86400 * 14
6007  << 86400 * 30.4375 << 86400 * 30.4375 * 2
6008  << 86400 * 30.4375 * 3 << 86400 * 30.4375 * 6
6009  << 86400 * 30.4375 *
6010  12); // day, week, month range (avg. days
6011  // per month includes leap years)
6012  if (result > 86400 * 30.4375 - 1) // month tick intervals or larger
6014  else if (result > 3600 * 24 - 1) // day tick intervals or larger
6016  } else // more than a year, go back to normal clean mantissa algorithm but
6017  // in units of years
6018  {
6019  const double secondsPerYear =
6020  86400 * 30.4375 * 12; // average including leap years
6021  result = cleanMantissa(result / secondsPerYear) * secondsPerYear;
6023  }
6024  return result;
6025 }
6026 
6035  int result = QCPAxisTicker::getSubTickCount(tickStep);
6036  switch (qRound(tickStep)) // hand chosen subticks for specific
6037  // minute/hour/day/week/month range (as specified
6038  // in getTickStep)
6039  {
6040  case 5 * 60:
6041  result = 4;
6042  break;
6043  case 10 * 60:
6044  result = 1;
6045  break;
6046  case 15 * 60:
6047  result = 2;
6048  break;
6049  case 30 * 60:
6050  result = 1;
6051  break;
6052  case 60 * 60:
6053  result = 3;
6054  break;
6055  case 3600 * 2:
6056  result = 3;
6057  break;
6058  case 3600 * 3:
6059  result = 2;
6060  break;
6061  case 3600 * 6:
6062  result = 1;
6063  break;
6064  case 3600 * 12:
6065  result = 3;
6066  break;
6067  case 3600 * 24:
6068  result = 3;
6069  break;
6070  case 86400 * 2:
6071  result = 1;
6072  break;
6073  case 86400 * 5:
6074  result = 4;
6075  break;
6076  case 86400 * 7:
6077  result = 6;
6078  break;
6079  case 86400 * 14:
6080  result = 1;
6081  break;
6082  case (int)(86400 * 30.4375 + 0.5):
6083  result = 3;
6084  break;
6085  case (int)(86400 * 30.4375 * 2 + 0.5):
6086  result = 1;
6087  break;
6088  case (int)(86400 * 30.4375 * 3 + 0.5):
6089  result = 2;
6090  break;
6091  case (int)(86400 * 30.4375 * 6 + 0.5):
6092  result = 5;
6093  break;
6094  case (int)(86400 * 30.4375 * 12 + 0.5):
6095  result = 3;
6096  break;
6097  }
6098  return result;
6099 }
6100 
6110  const QLocale &locale,
6111  QChar formatChar,
6112  int precision) {
6113  Q_UNUSED(precision)
6114  Q_UNUSED(formatChar)
6115  return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec),
6116  mDateTimeFormat);
6117 }
6118 
6127 QVector<double> QCPAxisTickerDateTime::createTickVector(double tickStep,
6128  const QCPRange &range) {
6129  QVector<double> result = QCPAxisTicker::createTickVector(tickStep, range);
6130  if (!result.isEmpty()) {
6132  QDateTime uniformDateTime = keyToDateTime(
6133  mTickOrigin); // the time of this datetime will be set for
6134  // all other ticks, if possible
6135  QDateTime tickDateTime;
6136  for (int i = 0; i < result.size(); ++i) {
6137  tickDateTime = keyToDateTime(result.at(i));
6138  tickDateTime.setTime(uniformDateTime.time());
6139  result[i] = dateTimeToKey(tickDateTime);
6140  }
6141  } else if (mDateStrategy == dsUniformDayInMonth) {
6142  QDateTime uniformDateTime = keyToDateTime(
6143  mTickOrigin); // this day (in month) and time will be set
6144  // for all other ticks, if possible
6145  QDateTime tickDateTime;
6146  for (int i = 0; i < result.size(); ++i) {
6147  tickDateTime = keyToDateTime(result.at(i));
6148  tickDateTime.setTime(uniformDateTime.time());
6149  int thisUniformDay =
6150  uniformDateTime.date().day() <=
6151  tickDateTime.date().daysInMonth()
6152  ? uniformDateTime.date().day()
6153  : tickDateTime.date()
6154  .daysInMonth(); // don't exceed month
6155  // (e.g. try to set
6156  // day 31 in
6157  // February)
6158  if (thisUniformDay - tickDateTime.date().day() <
6159  -15) // with leap years involved, date month may jump
6160  // backwards or forwards, and needs to be corrected
6161  // before setting day
6162  tickDateTime = tickDateTime.addMonths(1);
6163  else if (thisUniformDay - tickDateTime.date().day() >
6164  15) // with leap years involved, date month may jump
6165  // backwards or forwards, and needs to be
6166  // corrected before setting day
6167  tickDateTime = tickDateTime.addMonths(-1);
6168  tickDateTime.setDate(QDate(tickDateTime.date().year(),
6169  tickDateTime.date().month(),
6170  thisUniformDay));
6171  result[i] = dateTimeToKey(tickDateTime);
6172  }
6173  }
6174  }
6175  return result;
6176 }
6177 
6190 #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6191  return QDateTime::fromTime_t(key).addMSecs((key - (qint64)key) * 1000);
6192 #else
6193  return QDateTime::fromMSecsSinceEpoch(key * 1000.0);
6194 #endif
6195 }
6196 
6209 double QCPAxisTickerDateTime::dateTimeToKey(const QDateTime dateTime) {
6210 #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6211  return dateTime.toTime_t() + dateTime.time().msec() / 1000.0;
6212 #else
6213  return dateTime.toMSecsSinceEpoch() / 1000.0;
6214 #endif
6215 }
6216 
6225 double QCPAxisTickerDateTime::dateTimeToKey(const QDate date) {
6226 #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6227  return QDateTime(date).toTime_t();
6228 #elif QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
6229  return QDateTime(date, QTime()).toMSecsSinceEpoch() / 1000.0;
6230 #else
6231  return QDateTime(date).toMSecsSinceEpoch() / 1000.0;
6232 #endif
6233 }
6234 /* end of 'src/axis/axistickerdatetime.cpp' */
6235 
6236 /* including file 'src/axis/axistickertime.cpp', size 11747 */
6237 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
6238 
6242 
6289  : mTimeFormat(QLatin1String("%h:%m:%s")),
6290  mSmallestUnit(tuSeconds),
6291  mBiggestUnit(tuHours) {
6292  setTickCount(4);
6294  mFieldWidth[tuSeconds] = 2;
6295  mFieldWidth[tuMinutes] = 2;
6296  mFieldWidth[tuHours] = 2;
6297  mFieldWidth[tuDays] = 1;
6298 
6299  mFormatPattern[tuMilliseconds] = QLatin1String("%z");
6300  mFormatPattern[tuSeconds] = QLatin1String("%s");
6301  mFormatPattern[tuMinutes] = QLatin1String("%m");
6302  mFormatPattern[tuHours] = QLatin1String("%h");
6303  mFormatPattern[tuDays] = QLatin1String("%d");
6304 }
6305 
6327  mTimeFormat = format;
6328 
6329  // determine smallest and biggest unit in format, to optimize unit
6330  // replacement and allow biggest unit to consume remaining time of a tick
6331  // value and grow beyond its modulo (e.g. min > 59)
6334  bool hasSmallest = false;
6335  for (int i = tuMilliseconds; i <= tuDays; ++i) {
6336  TimeUnit unit = static_cast<TimeUnit>(i);
6337  if (mTimeFormat.contains(mFormatPattern.value(unit))) {
6338  if (!hasSmallest) {
6339  mSmallestUnit = unit;
6340  hasSmallest = true;
6341  }
6342  mBiggestUnit = unit;
6343  }
6344  }
6345 }
6346 
6356  int width) {
6357  mFieldWidth[unit] = qMax(width, 1);
6358 }
6359 
6371  double result =
6372  range.size() /
6373  (double)(mTickCount +
6374  1e-10); // mTickCount ticks on average, the small addition
6375  // is to prevent jitter on exact integers
6376 
6377  if (result < 1) // ideal tick step is below 1 second -> use normal clean
6378  // mantissa algorithm in units of seconds
6379  {
6381  result = qMax(cleanMantissa(result),
6382  0.001); // smallest tick step is 1 millisecond
6383  else // have no milliseconds available in format, so stick with 1
6384  // second tickstep
6385  result = 1.0;
6386  } else if (result < 3600 * 24) // below a day
6387  {
6388  // the filling of availableSteps seems a bit contorted but it fills in a
6389  // sorted fashion and thus saves a post-fill sorting run
6390  QVector<double> availableSteps;
6391  // seconds range:
6392  if (mSmallestUnit <= tuSeconds) availableSteps << 1;
6394  availableSteps << 2.5; // only allow half second steps if
6395  // milliseconds are there to display it
6396  else if (mSmallestUnit == tuSeconds)
6397  availableSteps << 2;
6398  if (mSmallestUnit <= tuSeconds) availableSteps << 5 << 10 << 15 << 30;
6399  // minutes range:
6400  if (mSmallestUnit <= tuMinutes) availableSteps << 1 * 60;
6401  if (mSmallestUnit <= tuSeconds)
6402  availableSteps << 2.5 * 60; // only allow half minute steps if
6403  // seconds are there to display it
6404  else if (mSmallestUnit == tuMinutes)
6405  availableSteps << 2 * 60;
6406  if (mSmallestUnit <= tuMinutes)
6407  availableSteps << 5 * 60 << 10 * 60 << 15 * 60 << 30 * 60;
6408  // hours range:
6409  if (mSmallestUnit <= tuHours)
6410  availableSteps << 1 * 3600 << 2 * 3600 << 3 * 3600 << 6 * 3600
6411  << 12 * 3600 << 24 * 3600;
6412  // pick available step that is most appropriate to approximate ideal
6413  // step:
6414  result = pickClosest(result, availableSteps);
6415  } else // more than a day, go back to normal clean mantissa algorithm but
6416  // in units of days
6417  {
6418  const double secondsPerDay = 3600 * 24;
6419  result = cleanMantissa(result / secondsPerDay) * secondsPerDay;
6420  }
6421  return result;
6422 }
6423 
6432  int result = QCPAxisTicker::getSubTickCount(tickStep);
6433  switch (qRound(
6434  tickStep)) // hand chosen subticks for specific minute/hour/day
6435  // range (as specified in getTickStep)
6436  {
6437  case 5 * 60:
6438  result = 4;
6439  break;
6440  case 10 * 60:
6441  result = 1;
6442  break;
6443  case 15 * 60:
6444  result = 2;
6445  break;
6446  case 30 * 60:
6447  result = 1;
6448  break;
6449  case 60 * 60:
6450  result = 3;
6451  break;
6452  case 3600 * 2:
6453  result = 3;
6454  break;
6455  case 3600 * 3:
6456  result = 2;
6457  break;
6458  case 3600 * 6:
6459  result = 1;
6460  break;
6461  case 3600 * 12:
6462  result = 3;
6463  break;
6464  case 3600 * 24:
6465  result = 3;
6466  break;
6467  }
6468  return result;
6469 }
6470 
6479  const QLocale &locale,
6480  QChar formatChar,
6481  int precision) {
6482  Q_UNUSED(precision)
6483  Q_UNUSED(formatChar)
6484  Q_UNUSED(locale)
6485  bool negative = tick < 0;
6486  if (negative) tick *= -1;
6487  double values[tuDays + 1]; // contains the msec/sec/min/... value with its
6488  // respective modulo (e.g. minute 0..59)
6489  double restValues[tuDays + 1]; // contains the msec/sec/min/... value as if
6490  // it's the largest available unit and thus
6491  // consumes the remaining time
6492 
6493  restValues[tuMilliseconds] = tick * 1000;
6494  values[tuMilliseconds] =
6495  modf(restValues[tuMilliseconds] / 1000, &restValues[tuSeconds]) *
6496  1000;
6497  values[tuSeconds] =
6498  modf(restValues[tuSeconds] / 60, &restValues[tuMinutes]) * 60;
6499  values[tuMinutes] =
6500  modf(restValues[tuMinutes] / 60, &restValues[tuHours]) * 60;
6501  values[tuHours] = modf(restValues[tuHours] / 24, &restValues[tuDays]) * 24;
6502  // no need to set values[tuDays] because days are always a rest value (there
6503  // is no higher unit so it consumes all remaining time)
6504 
6505  QString result = mTimeFormat;
6506  for (int i = mSmallestUnit; i <= mBiggestUnit; ++i) {
6507  TimeUnit iUnit = static_cast<TimeUnit>(i);
6508  replaceUnit(result, iUnit,
6509  qRound(iUnit == mBiggestUnit ? restValues[iUnit]
6510  : values[iUnit]));
6511  }
6512  if (negative) result.prepend(QLatin1Char('-'));
6513  return result;
6514 }
6515 
6524  int value) const {
6525  QString valueStr = QString::number(value);
6526  while (valueStr.size() < mFieldWidth.value(unit))
6527  valueStr.prepend(QLatin1Char('0'));
6528 
6529  text.replace(mFormatPattern.value(unit), valueStr);
6530 }
6531 /* end of 'src/axis/axistickertime.cpp' */
6532 
6533 /* including file 'src/axis/axistickerfixed.cpp', size 5583 */
6534 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
6535 
6539 
6568  : mTickStep(1.0), mScaleStrategy(ssNone) {}
6569 
6581  if (step > 0)
6582  mTickStep = step;
6583  else
6584  qDebug() << Q_FUNC_INFO
6585  << "tick step must be greater than zero:" << step;
6586 }
6587 
6599  mScaleStrategy = strategy;
6600 }
6601 
6614  switch (mScaleStrategy) {
6615  case ssNone: {
6616  return mTickStep;
6617  }
6618  case ssMultiples: {
6619  double exactStep =
6620  range.size() /
6621  (double)(mTickCount +
6622  1e-10); // mTickCount ticks on average, the small
6623  // addition is to prevent jitter on exact
6624  // integers
6625  if (exactStep < mTickStep)
6626  return mTickStep;
6627  else
6628  return (qint64)(cleanMantissa(exactStep / mTickStep) + 0.5) *
6629  mTickStep;
6630  }
6631  case ssPowers: {
6632  double exactStep =
6633  range.size() /
6634  (double)(mTickCount +
6635  1e-10); // mTickCount ticks on average, the small
6636  // addition is to prevent jitter on exact
6637  // integers
6638  return qPow(mTickStep,
6639  (int)(qLn(exactStep) / qLn(mTickStep) + 0.5));
6640  }
6641  }
6642  return mTickStep;
6643 }
6644 /* end of 'src/axis/axistickerfixed.cpp' */
6645 
6646 /* including file 'src/axis/axistickertext.cpp', size 8661 */
6647 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
6648 
6652 
6676 /* start of documentation of inline functions */
6677 
6688 /* end of documentation of inline functions */
6689 
6696 
6708 void QCPAxisTickerText::setTicks(const QMap<double, QString> &ticks) {
6709  mTicks = ticks;
6710 }
6711 
6720 void QCPAxisTickerText::setTicks(const QVector<double> &positions,
6721  const QVector<QString> &labels) {
6722  clear();
6723  addTicks(positions, labels);
6724 }
6725 
6732  if (subTicks >= 0)
6733  mSubTickCount = subTicks;
6734  else
6735  qDebug() << Q_FUNC_INFO
6736  << "sub tick count can't be negative:" << subTicks;
6737 }
6738 
6748 
6755 void QCPAxisTickerText::addTick(double position, const QString &label) {
6756  mTicks.insert(position, label);
6757 }
6758 
6770 void QCPAxisTickerText::addTicks(const QMap<double, QString> &ticks) {
6771 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
6772  mTicks.unite(ticks);
6773 #else
6774  mTicks.insert(ticks);
6775 #endif
6776 }
6777 
6789 void QCPAxisTickerText::addTicks(const QVector<double> &positions,
6790  const QVector<QString> &labels) {
6791  if (positions.size() != labels.size())
6792  qDebug() << Q_FUNC_INFO
6793  << "passed unequal length vectors for positions and labels:"
6794  << positions.size() << labels.size();
6795  int n = qMin(positions.size(), labels.size());
6796  for (int i = 0; i < n; ++i) mTicks.insert(positions.at(i), labels.at(i));
6797 }
6798 
6806  // text axis ticker has manual tick positions, so doesn't need this method
6807  Q_UNUSED(range)
6808  return 1.0;
6809 }
6810 
6817  Q_UNUSED(tickStep)
6818  return mSubTickCount;
6819 }
6820 
6829  const QLocale &locale,
6830  QChar formatChar,
6831  int precision) {
6832  Q_UNUSED(locale)
6833  Q_UNUSED(formatChar)
6834  Q_UNUSED(precision)
6835  return mTicks.value(tick);
6836 }
6837 
6846 QVector<double> QCPAxisTickerText::createTickVector(double tickStep,
6847  const QCPRange &range) {
6848  Q_UNUSED(tickStep)
6849  QVector<double> result;
6850  if (mTicks.isEmpty()) return result;
6851 
6852  QMap<double, QString>::const_iterator start =
6853  mTicks.lowerBound(range.lower);
6854  QMap<double, QString>::const_iterator end = mTicks.upperBound(range.upper);
6855  // this method should try to give one tick outside of range so proper
6856  // subticks can be generated:
6857  if (start != mTicks.constBegin()) --start;
6858  if (end != mTicks.constEnd()) ++end;
6859  for (QMap<double, QString>::const_iterator it = start; it != end; ++it)
6860  result.append(it.key());
6861 
6862  return result;
6863 }
6864 /* end of 'src/axis/axistickertext.cpp' */
6865 
6866 /* including file 'src/axis/axistickerpi.cpp', size 11170 */
6867 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
6868 
6872 
6898  : mPiSymbol(QLatin1String(" ") + QChar(0x03C0)),
6899  mPiValue(M_PI),
6900  mPeriodicity(0),
6901  mFractionStyle(fsUnicodeFractions),
6902  mPiTickStep(0) {
6903  setTickCount(4);
6904 }
6905 
6913 void QCPAxisTickerPi::setPiSymbol(QString symbol) { mPiSymbol = symbol; }
6914 
6921 void QCPAxisTickerPi::setPiValue(double pi) { mPiValue = pi; }
6922 
6932 void QCPAxisTickerPi::setPeriodicity(int multiplesOfPi) {
6933  mPeriodicity = qAbs(multiplesOfPi);
6934 }
6935 
6941  mFractionStyle = style;
6942 }
6943 
6953  mPiTickStep =
6954  range.size() / mPiValue /
6955  (double)(mTickCount +
6956  1e-10); // mTickCount ticks on average, the small addition
6957  // is to prevent jitter on exact integers
6959  return mPiTickStep * mPiValue;
6960 }
6961 
6970 int QCPAxisTickerPi::getSubTickCount(double tickStep) {
6971  return QCPAxisTicker::getSubTickCount(tickStep / mPiValue);
6972 }
6973 
6982 QString QCPAxisTickerPi::getTickLabel(double tick,
6983  const QLocale &locale,
6984  QChar formatChar,
6985  int precision) {
6986  double tickInPis = tick / mPiValue;
6987  if (mPeriodicity > 0) tickInPis = fmod(tickInPis, mPeriodicity);
6988 
6989  if (mFractionStyle != fsFloatingPoint && mPiTickStep > 0.09 &&
6990  mPiTickStep < 50) {
6991  // simply construct fraction from decimal like 1.234 -> 1234/1000 and
6992  // then simplify fraction, smaller digits are irrelevant due to
6993  // mPiTickStep conditional above
6994  int denominator = 1000;
6995  int numerator = qRound(tickInPis * denominator);
6996  simplifyFraction(numerator, denominator);
6997  if (qAbs(numerator) == 1 && denominator == 1)
6998  return (numerator < 0 ? QLatin1String("-") : QLatin1String("")) +
6999  mPiSymbol.trimmed();
7000  else if (numerator == 0)
7001  return QLatin1String("0");
7002  else
7003  return fractionToString(numerator, denominator) + mPiSymbol;
7004  } else {
7005  if (qFuzzyIsNull(tickInPis))
7006  return QLatin1String("0");
7007  else if (qFuzzyCompare(qAbs(tickInPis), 1.0))
7008  return (tickInPis < 0 ? QLatin1String("-") : QLatin1String("")) +
7009  mPiSymbol.trimmed();
7010  else
7011  return QCPAxisTicker::getTickLabel(tickInPis, locale, formatChar,
7012  precision) +
7013  mPiSymbol;
7014  }
7015 }
7016 
7023 void QCPAxisTickerPi::simplifyFraction(int &numerator, int &denominator) const {
7024  if (numerator == 0 || denominator == 0) return;
7025 
7026  int num = numerator;
7027  int denom = denominator;
7028  while (denom != 0) // euclidean gcd algorithm
7029  {
7030  int oldDenom = denom;
7031  denom = num % denom;
7032  num = oldDenom;
7033  }
7034  // num is now gcd of numerator and denominator
7035  numerator /= num;
7036  denominator /= num;
7037 }
7038 
7051  int denominator) const {
7052  if (denominator == 0) {
7053  qDebug() << Q_FUNC_INFO << "called with zero denominator";
7054  return QString();
7055  }
7056  if (mFractionStyle ==
7057  fsFloatingPoint) // should never be the case when calling this function
7058  {
7059  qDebug() << Q_FUNC_INFO
7060  << "shouldn't be called with fraction style fsDecimal";
7061  return QString::number(numerator / (double)denominator); // failsafe
7062  }
7063  int sign = numerator * denominator < 0 ? -1 : 1;
7064  numerator = qAbs(numerator);
7065  denominator = qAbs(denominator);
7066 
7067  if (denominator == 1) {
7068  return QString::number(sign * numerator);
7069  } else {
7070  int integerPart = numerator / denominator;
7071  int remainder = numerator % denominator;
7072  if (remainder == 0) {
7073  return QString::number(sign * integerPart);
7074  } else {
7076  return QString(QLatin1String("%1%2%3/%4"))
7077  .arg(sign == -1 ? QLatin1String("-")
7078  : QLatin1String(""))
7079  .arg(integerPart > 0
7080  ? (QStringLiteral("%1 ").arg(
7081  QString::number(integerPart)))
7082  : QLatin1String(""))
7083  .arg(remainder)
7084  .arg(denominator);
7085  } else if (mFractionStyle == fsUnicodeFractions) {
7086  return QString(QLatin1String("%1%2%3"))
7087  .arg(sign == -1 ? QLatin1String("-")
7088  : QLatin1String(""))
7089  .arg(integerPart > 0 ? QString::number(integerPart)
7090  : QLatin1String(""))
7091  .arg(unicodeFraction(remainder, denominator));
7092  }
7093  }
7094  }
7095  return QString();
7096 }
7097 
7109 QString QCPAxisTickerPi::unicodeFraction(int numerator, int denominator) const {
7110  return unicodeSuperscript(numerator) + QChar(0x2044) +
7111  unicodeSubscript(denominator);
7112 }
7113 
7119 QString QCPAxisTickerPi::unicodeSuperscript(int number) const {
7120  if (number == 0) return QString(QChar(0x2070));
7121 
7122  QString result;
7123  while (number > 0) {
7124  const int digit = number % 10;
7125  switch (digit) {
7126  case 1: {
7127  result.prepend(QChar(0x00B9));
7128  break;
7129  }
7130  case 2: {
7131  result.prepend(QChar(0x00B2));
7132  break;
7133  }
7134  case 3: {
7135  result.prepend(QChar(0x00B3));
7136  break;
7137  }
7138  default: {
7139  result.prepend(QChar(0x2070 + digit));
7140  break;
7141  }
7142  }
7143  number /= 10;
7144  }
7145  return result;
7146 }
7147 
7153 QString QCPAxisTickerPi::unicodeSubscript(int number) const {
7154  if (number == 0) return QString(QChar(0x2080));
7155 
7156  QString result;
7157  while (number > 0) {
7158  result.prepend(QChar(0x2080 + number % 10));
7159  number /= 10;
7160  }
7161  return result;
7162 }
7163 /* end of 'src/axis/axistickerpi.cpp' */
7164 
7165 /* including file 'src/axis/axistickerlog.cpp', size 7106 */
7166 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
7167 
7171 
7201  : mLogBase(10.0),
7202  mSubTickCount(8), // generates 10 intervals
7203  mLogBaseLnInv(1.0 / qLn(mLogBase)) {}
7204 
7209 void QCPAxisTickerLog::setLogBase(double base) {
7210  if (base > 0) {
7211  mLogBase = base;
7212  mLogBaseLnInv = 1.0 / qLn(mLogBase);
7213  } else
7214  qDebug() << Q_FUNC_INFO
7215  << "log base has to be greater than zero:" << base;
7216 }
7217 
7230  if (subTicks >= 0)
7231  mSubTickCount = subTicks;
7232  else
7233  qDebug() << Q_FUNC_INFO
7234  << "sub tick count can't be negative:" << subTicks;
7235 }
7236 
7245  // Logarithmic axis ticker has unequal tick spacing, so doesn't need this
7246  // method
7247  Q_UNUSED(range)
7248  return 1.0;
7249 }
7250 
7259  Q_UNUSED(tickStep)
7260  return mSubTickCount;
7261 }
7262 
7273 QVector<double> QCPAxisTickerLog::createTickVector(double tickStep,
7274  const QCPRange &range) {
7275  Q_UNUSED(tickStep)
7276  QVector<double> result;
7277  if (range.lower > 0 && range.upper > 0) // positive range
7278  {
7279  double exactPowerStep = qLn(range.upper / range.lower) * mLogBaseLnInv /
7280  (double)(mTickCount + 1e-10);
7281  double newLogBase =
7282  qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
7283  double currentTick =
7284  qPow(newLogBase, qFloor(qLn(range.lower) / qLn(newLogBase)));
7285  result.append(currentTick);
7286  while (currentTick < range.upper &&
7287  currentTick > 0) // currentMag might be zero for ranges ~1e-300,
7288  // just cancel in that case
7289  {
7290  currentTick *= newLogBase;
7291  result.append(currentTick);
7292  }
7293  } else if (range.lower < 0 && range.upper < 0) // negative range
7294  {
7295  double exactPowerStep = qLn(range.lower / range.upper) * mLogBaseLnInv /
7296  (double)(mTickCount + 1e-10);
7297  double newLogBase =
7298  qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
7299  double currentTick =
7300  -qPow(newLogBase, qCeil(qLn(-range.lower) / qLn(newLogBase)));
7301  result.append(currentTick);
7302  while (currentTick < range.upper &&
7303  currentTick < 0) // currentMag might be zero for ranges ~1e-300,
7304  // just cancel in that case
7305  {
7306  currentTick /= newLogBase;
7307  result.append(currentTick);
7308  }
7309  } else // invalid range for logarithmic scale, because lower and upper have
7310  // different sign
7311  {
7312  qDebug() << Q_FUNC_INFO
7313  << "Invalid range for logarithmic plot: " << range.lower
7314  << ".." << range.upper;
7315  }
7316 
7317  return result;
7318 }
7319 /* end of 'src/axis/axistickerlog.cpp' */
7320 
7321 /* including file 'src/axis/axis.cpp', size 99515 */
7322 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
7323 
7327 
7350  : QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
7351  mParentAxis(parentAxis) {
7352  // warning: this is called in QCPAxis constructor, so parentAxis members
7353  // should not be accessed/called
7354  setParent(parentAxis);
7355  setPen(QPen(QColor(200, 200, 200), 0, Qt::DotLine));
7356  setSubGridPen(QPen(QColor(220, 220, 220), 0, Qt::DotLine));
7357  setZeroLinePen(QPen(QColor(200, 200, 200), 0, Qt::SolidLine));
7358  setSubGridVisible(false);
7359  setAntialiased(false);
7360  setAntialiasedSubGrid(false);
7361  setAntialiasedZeroLine(false);
7362 }
7363 
7370 
7375  mAntialiasedSubGrid = enabled;
7376 }
7377 
7382  mAntialiasedZeroLine = enabled;
7383 }
7384 
7388 void QCPGrid::setPen(const QPen &pen) { mPen = pen; }
7389 
7393 void QCPGrid::setSubGridPen(const QPen &pen) { mSubGridPen = pen; }
7394 
7402 void QCPGrid::setZeroLinePen(const QPen &pen) { mZeroLinePen = pen; }
7403 
7420 }
7421 
7428 void QCPGrid::draw(QCPPainter *painter) {
7429  if (!mParentAxis) {
7430  qDebug() << Q_FUNC_INFO << "invalid parent axis";
7431  return;
7432  }
7433 
7435  drawGridLines(painter);
7436 }
7437 
7444 void QCPGrid::drawGridLines(QCPPainter *painter) const {
7445  if (!mParentAxis) {
7446  qDebug() << Q_FUNC_INFO << "invalid parent axis";
7447  return;
7448  }
7449 
7450  const int tickCount = mParentAxis->mTickVector.size();
7451  double t; // helper variable, result of coordinate-to-pixel transforms
7452  if (mParentAxis->orientation() == Qt::Horizontal) {
7453  // draw zeroline:
7454  int zeroLineIndex = -1;
7455  if (mZeroLinePen.style() != Qt::NoPen &&
7458  QCP::aeZeroLine);
7459  painter->setPen(mZeroLinePen);
7460  double epsilon = mParentAxis->range().size() *
7461  1E-6; // for comparing double to zero
7462  for (int i = 0; i < tickCount; ++i) {
7463  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) {
7464  zeroLineIndex = i;
7466  mParentAxis->mTickVector.at(i)); // x
7467  painter->drawLine(QLineF(t,
7469  t, mParentAxis->mAxisRect->top()));
7470  break;
7471  }
7472  }
7473  }
7474  // draw grid lines:
7476  painter->setPen(mPen);
7477  for (int i = 0; i < tickCount; ++i) {
7478  if (i == zeroLineIndex)
7479  continue; // don't draw a gridline on top of the zeroline
7480  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
7481  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
7482  mParentAxis->mAxisRect->top()));
7483  }
7484  } else {
7485  // draw zeroline:
7486  int zeroLineIndex = -1;
7487  if (mZeroLinePen.style() != Qt::NoPen &&
7490  QCP::aeZeroLine);
7491  painter->setPen(mZeroLinePen);
7492  double epsilon = mParentAxis->mRange.size() *
7493  1E-6; // for comparing double to zero
7494  for (int i = 0; i < tickCount; ++i) {
7495  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) {
7496  zeroLineIndex = i;
7498  mParentAxis->mTickVector.at(i)); // y
7499  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
7501  t));
7502  break;
7503  }
7504  }
7505  }
7506  // draw grid lines:
7508  painter->setPen(mPen);
7509  for (int i = 0; i < tickCount; ++i) {
7510  if (i == zeroLineIndex)
7511  continue; // don't draw a gridline on top of the zeroline
7512  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
7513  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
7514  mParentAxis->mAxisRect->right(), t));
7515  }
7516  }
7517 }
7518 
7526  if (!mParentAxis) {
7527  qDebug() << Q_FUNC_INFO << "invalid parent axis";
7528  return;
7529  }
7530 
7532  double t; // helper variable, result of coordinate-to-pixel transforms
7533  painter->setPen(mSubGridPen);
7534  if (mParentAxis->orientation() == Qt::Horizontal) {
7535  for (int i = 0; i < mParentAxis->mSubTickVector.size(); ++i) {
7537  mParentAxis->mSubTickVector.at(i)); // x
7538  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t,
7539  mParentAxis->mAxisRect->top()));
7540  }
7541  } else {
7542  for (int i = 0; i < mParentAxis->mSubTickVector.size(); ++i) {
7544  mParentAxis->mSubTickVector.at(i)); // y
7545  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t,
7546  mParentAxis->mAxisRect->right(), t));
7547  }
7548  }
7549 }
7550 
7554 
7579 /* start of documentation of inline functions */
7580 
7645 /* end of documentation of inline functions */
7646 /* start of documentation of signals */
7647 
7688 /* end of documentation of signals */
7689 
7699  : QCPLayerable(parent->parentPlot(), QString(), parent),
7700  // axis base:
7701  mAxisType(type),
7702  mAxisRect(parent),
7703  mPadding(5),
7704  mOrientation(orientation(type)),
7705  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
7706  mSelectedParts(spNone),
7707  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7708  mSelectedBasePen(QPen(Qt::blue, 2)),
7709  // axis label:
7710  mLabel(),
7711  mLabelFont(mParentPlot->font()),
7712  mSelectedLabelFont(
7713  QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
7714  mLabelColor(Qt::black),
7715  mSelectedLabelColor(Qt::blue),
7716  // tick labels:
7717  mTickLabels(true),
7718  mTickLabelFont(mParentPlot->font()),
7719  mSelectedTickLabelFont(QFont(mTickLabelFont.family(),
7720  mTickLabelFont.pointSize(),
7721  QFont::Bold)),
7722  mTickLabelColor(Qt::black),
7723  mSelectedTickLabelColor(Qt::blue),
7724  mNumberPrecision(6),
7725  mNumberFormatChar('g'),
7726  mNumberBeautifulPowers(true),
7727  // ticks and subticks:
7728  mTicks(true),
7729  mSubTicks(true),
7730  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7731  mSelectedTickPen(QPen(Qt::blue, 2)),
7732  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7733  mSelectedSubTickPen(QPen(Qt::blue, 2)),
7734  // scale and range:
7735  mRange(0, 5),
7736  mRangeReversed(false),
7737  mScaleType(stLinear),
7738  // internal members:
7739  mGrid(new QCPGrid(this)),
7740  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
7741  mTicker(new QCPAxisTicker),
7742  mCachedMarginValid(false),
7743  mCachedMargin(0) {
7744  setParent(parent);
7745  mGrid->setVisible(false);
7746  setAntialiased(false);
7748  ->currentLayer()); // it's actually on that layer already,
7749  // but we want it in front of the grid,
7750  // so we place it on there again
7751 
7752  if (type == atTop) {
7754  setLabelPadding(6);
7755  } else if (type == atRight) {
7757  setLabelPadding(12);
7758  } else if (type == atBottom) {
7760  setLabelPadding(3);
7761  } else if (type == atLeft) {
7763  setLabelPadding(10);
7764  }
7765 }
7766 
7768  delete mAxisPainter;
7769  delete mGrid; // delete grid here instead of via parent ~QObject for better
7770  // defined deletion order
7771 }
7772 
7773 /* No documentation as it is a property getter */
7774 int QCPAxis::tickLabelPadding() const { return mAxisPainter->tickLabelPadding; }
7775 
7776 /* No documentation as it is a property getter */
7778  return mAxisPainter->tickLabelRotation;
7779 }
7780 
7781 /* No documentation as it is a property getter */
7783  return mAxisPainter->tickLabelSide;
7784 }
7785 
7786 /* No documentation as it is a property getter */
7787 QString QCPAxis::numberFormat() const {
7788  QString result;
7789  result.append(mNumberFormatChar);
7790  if (mNumberBeautifulPowers) {
7791  result.append(QLatin1Char('b'));
7792  if (mAxisPainter->numberMultiplyCross) result.append(QLatin1Char('c'));
7793  }
7794  return result;
7795 }
7796 
7797 /* No documentation as it is a property getter */
7798 int QCPAxis::tickLengthIn() const { return mAxisPainter->tickLengthIn; }
7799 
7800 /* No documentation as it is a property getter */
7801 int QCPAxis::tickLengthOut() const { return mAxisPainter->tickLengthOut; }
7802 
7803 /* No documentation as it is a property getter */
7804 int QCPAxis::subTickLengthIn() const { return mAxisPainter->subTickLengthIn; }
7805 
7806 /* No documentation as it is a property getter */
7807 int QCPAxis::subTickLengthOut() const { return mAxisPainter->subTickLengthOut; }
7808 
7809 /* No documentation as it is a property getter */
7810 int QCPAxis::labelPadding() const { return mAxisPainter->labelPadding; }
7811 
7812 /* No documentation as it is a property getter */
7813 int QCPAxis::offset() const { return mAxisPainter->offset; }
7814 
7815 /* No documentation as it is a property getter */
7816 QCPLineEnding QCPAxis::lowerEnding() const { return mAxisPainter->lowerEnding; }
7817 
7818 /* No documentation as it is a property getter */
7819 QCPLineEnding QCPAxis::upperEnding() const { return mAxisPainter->upperEnding; }
7820 
7838  if (mScaleType != type) {
7839  mScaleType = type;
7840  if (mScaleType == stLogarithmic)
7842  mCachedMarginValid = false;
7844  }
7845 }
7846 
7856 void QCPAxis::setRange(const QCPRange &range) {
7857  if (range.lower == mRange.lower && range.upper == mRange.upper) return;
7858 
7859  if (!QCPRange::validRange(range)) return;
7860  QCPRange oldRange = mRange;
7861  if (mScaleType == stLogarithmic) {
7863  } else {
7865  }
7866  emit rangeChanged(mRange);
7867  emit rangeChanged(mRange, oldRange);
7868 }
7869 
7881 void QCPAxis::setSelectableParts(const SelectableParts &selectable) {
7882  if (mSelectableParts != selectable) {
7883  mSelectableParts = selectable;
7885  }
7886 }
7887 
7906 void QCPAxis::setSelectedParts(const SelectableParts &selected) {
7907  if (mSelectedParts != selected) {
7908  mSelectedParts = selected;
7910  }
7911 }
7912 
7922 void QCPAxis::setRange(double lower, double upper) {
7923  if (lower == mRange.lower && upper == mRange.upper) return;
7924 
7925  if (!QCPRange::validRange(lower, upper)) return;
7926  QCPRange oldRange = mRange;
7927  mRange.lower = lower;
7928  mRange.upper = upper;
7929  if (mScaleType == stLogarithmic) {
7931  } else {
7933  }
7934  emit rangeChanged(mRange);
7935  emit rangeChanged(mRange, oldRange);
7936 }
7937 
7951  double size,
7952  Qt::AlignmentFlag alignment) {
7953  if (alignment == Qt::AlignLeft)
7955  else if (alignment == Qt::AlignRight)
7957  else // alignment == Qt::AlignCenter
7958  setRange(position - size / 2.0, position + size / 2.0);
7959 }
7960 
7965 void QCPAxis::setRangeLower(double lower) {
7966  if (mRange.lower == lower) return;
7967 
7968  QCPRange oldRange = mRange;
7969  mRange.lower = lower;
7970  if (mScaleType == stLogarithmic) {
7972  } else {
7974  }
7975  emit rangeChanged(mRange);
7976  emit rangeChanged(mRange, oldRange);
7977 }
7978 
7983 void QCPAxis::setRangeUpper(double upper) {
7984  if (mRange.upper == upper) return;
7985 
7986  QCPRange oldRange = mRange;
7987  mRange.upper = upper;
7988  if (mScaleType == stLogarithmic) {
7990  } else {
7992  }
7993  emit rangeChanged(mRange);
7994  emit rangeChanged(mRange, oldRange);
7995 }
7996 
8007 void QCPAxis::setRangeReversed(bool reversed) { mRangeReversed = reversed; }
8008 
8024 void QCPAxis::setTicker(QSharedPointer<QCPAxisTicker> ticker) {
8025  if (ticker)
8026  mTicker = ticker;
8027  else
8028  qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker";
8029  // no need to invalidate margin cache here because produced tick labels are
8030  // checked for changes in setupTickVector
8031 }
8032 
8041 void QCPAxis::setTicks(bool show) {
8042  if (mTicks != show) {
8043  mTicks = show;
8044  mCachedMarginValid = false;
8045  }
8046 }
8047 
8052 void QCPAxis::setTickLabels(bool show) {
8053  if (mTickLabels != show) {
8054  mTickLabels = show;
8055  mCachedMarginValid = false;
8056  if (!mTickLabels) mTickVectorLabels.clear();
8057  }
8058 }
8059 
8064 void QCPAxis::setTickLabelPadding(int padding) {
8065  if (mAxisPainter->tickLabelPadding != padding) {
8066  mAxisPainter->tickLabelPadding = padding;
8067  mCachedMarginValid = false;
8068  }
8069 }
8070 
8076 void QCPAxis::setTickLabelFont(const QFont &font) {
8077  if (font != mTickLabelFont) {
8078  mTickLabelFont = font;
8079  mCachedMarginValid = false;
8080  }
8081 }
8082 
8088 void QCPAxis::setTickLabelColor(const QColor &color) {
8090 }
8091 
8101 void QCPAxis::setTickLabelRotation(double degrees) {
8102  if (!qFuzzyIsNull(degrees - mAxisPainter->tickLabelRotation)) {
8103  mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
8104  mCachedMarginValid = false;
8105  }
8106 }
8107 
8118  mAxisPainter->tickLabelSide = side;
8119  mCachedMarginValid = false;
8120 }
8121 
8155 void QCPAxis::setNumberFormat(const QString &formatCode) {
8156  if (formatCode.isEmpty()) {
8157  qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
8158  return;
8159  }
8160  mCachedMarginValid = false;
8161 
8162  // interpret first char as number format char:
8163  QString allowedFormatChars(QLatin1String("eEfgG"));
8164  if (allowedFormatChars.contains(formatCode.at(0))) {
8165  mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
8166  } else {
8167  qDebug() << Q_FUNC_INFO
8168  << "Invalid number format code (first char not in 'eEfgG'):"
8169  << formatCode;
8170  return;
8171  }
8172  if (formatCode.length() < 2) {
8173  mNumberBeautifulPowers = false;
8174  mAxisPainter->numberMultiplyCross = false;
8175  return;
8176  }
8177 
8178  // interpret second char as indicator for beautiful decimal powers:
8179  if (formatCode.at(1) == QLatin1Char('b') &&
8180  (mNumberFormatChar == QLatin1Char('e') ||
8181  mNumberFormatChar == QLatin1Char('g'))) {
8182  mNumberBeautifulPowers = true;
8183  } else {
8184  qDebug() << Q_FUNC_INFO
8185  << "Invalid number format code (second char not 'b' or first "
8186  "char neither 'e' nor 'g'):"
8187  << formatCode;
8188  return;
8189  }
8190  if (formatCode.length() < 3) {
8191  mAxisPainter->numberMultiplyCross = false;
8192  return;
8193  }
8194 
8195  // interpret third char as indicator for dot or cross multiplication symbol:
8196  if (formatCode.at(2) == QLatin1Char('c')) {
8197  mAxisPainter->numberMultiplyCross = true;
8198  } else if (formatCode.at(2) == QLatin1Char('d')) {
8199  mAxisPainter->numberMultiplyCross = false;
8200  } else {
8201  qDebug() << Q_FUNC_INFO
8202  << "Invalid number format code (third char neither 'c' nor "
8203  "'d'):"
8204  << formatCode;
8205  return;
8206  }
8207 }
8208 
8214 void QCPAxis::setNumberPrecision(int precision) {
8215  if (mNumberPrecision != precision) {
8216  mNumberPrecision = precision;
8217  mCachedMarginValid = false;
8218  }
8219 }
8220 
8230 void QCPAxis::setTickLength(int inside, int outside) {
8231  setTickLengthIn(inside);
8232  setTickLengthOut(outside);
8233 }
8234 
8241 void QCPAxis::setTickLengthIn(int inside) {
8242  if (mAxisPainter->tickLengthIn != inside) {
8243  mAxisPainter->tickLengthIn = inside;
8244  }
8245 }
8246 
8255 void QCPAxis::setTickLengthOut(int outside) {
8256  if (mAxisPainter->tickLengthOut != outside) {
8257  mAxisPainter->tickLengthOut = outside;
8259  false; // only outside tick length can change margin
8260  }
8261 }
8262 
8271 void QCPAxis::setSubTicks(bool show) {
8272  if (mSubTicks != show) {
8273  mSubTicks = show;
8274  mCachedMarginValid = false;
8275  }
8276 }
8277 
8287 void QCPAxis::setSubTickLength(int inside, int outside) {
8288  setSubTickLengthIn(inside);
8289  setSubTickLengthOut(outside);
8290 }
8291 
8299  if (mAxisPainter->subTickLengthIn != inside) {
8300  mAxisPainter->subTickLengthIn = inside;
8301  }
8302 }
8303 
8312 void QCPAxis::setSubTickLengthOut(int outside) {
8313  if (mAxisPainter->subTickLengthOut != outside) {
8314  mAxisPainter->subTickLengthOut = outside;
8316  false; // only outside tick length can change margin
8317  }
8318 }
8319 
8325 void QCPAxis::setBasePen(const QPen &pen) { mBasePen = pen; }
8326 
8332 void QCPAxis::setTickPen(const QPen &pen) { mTickPen = pen; }
8333 
8339 void QCPAxis::setSubTickPen(const QPen &pen) { mSubTickPen = pen; }
8340 
8346 void QCPAxis::setLabelFont(const QFont &font) {
8347  if (mLabelFont != font) {
8348  mLabelFont = font;
8349  mCachedMarginValid = false;
8350  }
8351 }
8352 
8358 void QCPAxis::setLabelColor(const QColor &color) { mLabelColor = color; }
8359 
8365 void QCPAxis::setLabel(const QString &str) {
8366  if (mLabel != str) {
8367  mLabel = str;
8368  mCachedMarginValid = false;
8369  }
8370 }
8371 
8377 void QCPAxis::setLabelPadding(int padding) {
8378  if (mAxisPainter->labelPadding != padding) {
8379  mAxisPainter->labelPadding = padding;
8380  mCachedMarginValid = false;
8381  }
8382 }
8383 
8395 void QCPAxis::setPadding(int padding) {
8396  if (mPadding != padding) {
8397  mPadding = padding;
8398  mCachedMarginValid = false;
8399  }
8400 }
8401 
8411 
8418 void QCPAxis::setSelectedTickLabelFont(const QFont &font) {
8419  if (font != mSelectedTickLabelFont) {
8420  mSelectedTickLabelFont = font;
8421  // don't set mCachedMarginValid to false here because margin calculation
8422  // is always done with non-selected fonts
8423  }
8424 }
8425 
8432 void QCPAxis::setSelectedLabelFont(const QFont &font) {
8433  mSelectedLabelFont = font;
8434  // don't set mCachedMarginValid to false here because margin calculation is
8435  // always done with non-selected fonts
8436 }
8437 
8445  if (color != mSelectedTickLabelColor) {
8447  }
8448 }
8449 
8458 }
8459 
8466 void QCPAxis::setSelectedBasePen(const QPen &pen) { mSelectedBasePen = pen; }
8467 
8474 void QCPAxis::setSelectedTickPen(const QPen &pen) { mSelectedTickPen = pen; }
8475 
8482 void QCPAxis::setSelectedSubTickPen(const QPen &pen) {
8483  mSelectedSubTickPen = pen;
8484 }
8485 
8497  mAxisPainter->lowerEnding = ending;
8498 }
8499 
8511  mAxisPainter->upperEnding = ending;
8512 }
8513 
8522 void QCPAxis::moveRange(double diff) {
8523  QCPRange oldRange = mRange;
8524  if (mScaleType == stLinear) {
8525  mRange.lower += diff;
8526  mRange.upper += diff;
8527  } else // mScaleType == stLogarithmic
8528  {
8529  mRange.lower *= diff;
8530  mRange.upper *= diff;
8531  }
8532  emit rangeChanged(mRange);
8533  emit rangeChanged(mRange, oldRange);
8534 }
8535 
8546 void QCPAxis::scaleRange(double factor) {
8547  scaleRange(factor, range().center());
8548 }
8549 
8560 void QCPAxis::scaleRange(double factor, double center) {
8561  QCPRange oldRange = mRange;
8562  if (mScaleType == stLinear) {
8563  QCPRange newRange;
8564  newRange.lower = (mRange.lower - center) * factor + center;
8565  newRange.upper = (mRange.upper - center) * factor + center;
8566  if (QCPRange::validRange(newRange))
8567  mRange = newRange.sanitizedForLinScale();
8568  } else // mScaleType == stLogarithmic
8569  {
8570  if ((mRange.upper < 0 && center < 0) ||
8571  (mRange.upper > 0 &&
8572  center > 0)) // make sure center has same sign as range
8573  {
8574  QCPRange newRange;
8575  newRange.lower = qPow(mRange.lower / center, factor) * center;
8576  newRange.upper = qPow(mRange.upper / center, factor) * center;
8577  if (QCPRange::validRange(newRange))
8578  mRange = newRange.sanitizedForLogScale();
8579  } else
8580  qDebug() << Q_FUNC_INFO
8581  << "Center of scaling operation doesn't lie in same "
8582  "logarithmic sign domain as range:"
8583  << center;
8584  }
8585  emit rangeChanged(mRange);
8586  emit rangeChanged(mRange, oldRange);
8587 }
8588 
8603 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio) {
8604  int otherPixelSize, ownPixelSize;
8605 
8606  if (otherAxis->orientation() == Qt::Horizontal)
8607  otherPixelSize = otherAxis->axisRect()->width();
8608  else
8609  otherPixelSize = otherAxis->axisRect()->height();
8610 
8611  if (orientation() == Qt::Horizontal)
8612  ownPixelSize = axisRect()->width();
8613  else
8614  ownPixelSize = axisRect()->height();
8615 
8616  double newRangeSize = ratio * otherAxis->range().size() * ownPixelSize /
8617  (double)otherPixelSize;
8618  setRange(range().center(), newRangeSize, Qt::AlignCenter);
8619 }
8620 
8627 void QCPAxis::rescale(bool onlyVisiblePlottables) {
8628  QList<QCPAbstractPlottable *> p = plottables();
8629  QCPRange newRange;
8630  bool haveRange = false;
8631  for (int i = 0; i < p.size(); ++i) {
8632  if (!p.at(i)->realVisibility() && onlyVisiblePlottables) continue;
8633  QCPRange plottableRange;
8634  bool currentFoundRange;
8635  QCP::SignDomain signDomain = QCP::sdBoth;
8636  if (mScaleType == stLogarithmic)
8637  signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);
8638  if (p.at(i)->keyAxis() == this)
8639  plottableRange =
8640  p.at(i)->getKeyRange(currentFoundRange, signDomain);
8641  else
8642  plottableRange =
8643  p.at(i)->getValueRange(currentFoundRange, signDomain);
8644  if (currentFoundRange) {
8645  if (!haveRange)
8646  newRange = plottableRange;
8647  else
8648  newRange.expand(plottableRange);
8649  haveRange = true;
8650  }
8651  }
8652  if (haveRange) {
8653  if (!QCPRange::validRange(
8654  newRange)) // likely due to range being zero (plottable has
8655  // only constant data in this axis dimension),
8656  // shift current range to at least center the
8657  // plottable
8658  {
8659  double center = (newRange.lower + newRange.upper) *
8660  0.5; // upper and lower should be equal anyway, but
8661  // just to make sure, incase validRange
8662  // returned false for other reason
8663  if (mScaleType == stLinear) {
8664  newRange.lower = center - mRange.size() / 2.0;
8665  newRange.upper = center + mRange.size() / 2.0;
8666  } else // mScaleType == stLogarithmic
8667  {
8668  newRange.lower = center / qSqrt(mRange.upper / mRange.lower);
8669  newRange.upper = center * qSqrt(mRange.upper / mRange.lower);
8670  }
8671  }
8672  setRange(newRange);
8673  }
8674 }
8675 
8680 double QCPAxis::pixelToCoord(double value) const {
8681  if (orientation() == Qt::Horizontal) {
8682  if (mScaleType == stLinear) {
8683  if (!mRangeReversed)
8684  return (value - mAxisRect->left()) /
8685  (double)mAxisRect->width() * mRange.size() +
8686  mRange.lower;
8687  else
8688  return -(value - mAxisRect->left()) /
8689  (double)mAxisRect->width() * mRange.size() +
8690  mRange.upper;
8691  } else // mScaleType == stLogarithmic
8692  {
8693  if (!mRangeReversed)
8694  return qPow(mRange.upper / mRange.lower,
8695  (value - mAxisRect->left()) /
8696  (double)mAxisRect->width()) *
8697  mRange.lower;
8698  else
8699  return qPow(mRange.upper / mRange.lower,
8700  (mAxisRect->left() - value) /
8701  (double)mAxisRect->width()) *
8702  mRange.upper;
8703  }
8704  } else // orientation() == Qt::Vertical
8705  {
8706  if (mScaleType == stLinear) {
8707  if (!mRangeReversed)
8708  return (mAxisRect->bottom() - value) /
8709  (double)mAxisRect->height() * mRange.size() +
8710  mRange.lower;
8711  else
8712  return -(mAxisRect->bottom() - value) /
8713  (double)mAxisRect->height() * mRange.size() +
8714  mRange.upper;
8715  } else // mScaleType == stLogarithmic
8716  {
8717  if (!mRangeReversed)
8718  return qPow(mRange.upper / mRange.lower,
8719  (mAxisRect->bottom() - value) /
8720  (double)mAxisRect->height()) *
8721  mRange.lower;
8722  else
8723  return qPow(mRange.upper / mRange.lower,
8724  (value - mAxisRect->bottom()) /
8725  (double)mAxisRect->height()) *
8726  mRange.upper;
8727  }
8728  }
8729 }
8730 
8735 double QCPAxis::coordToPixel(double value) const {
8736  if (orientation() == Qt::Horizontal) {
8737  if (mScaleType == stLinear) {
8738  if (!mRangeReversed)
8739  return (value - mRange.lower) / mRange.size() *
8740  mAxisRect->width() +
8741  mAxisRect->left();
8742  else
8743  return (mRange.upper - value) / mRange.size() *
8744  mAxisRect->width() +
8745  mAxisRect->left();
8746  } else // mScaleType == stLogarithmic
8747  {
8748  if (value >= 0.0 &&
8749  mRange.upper < 0.0) // invalid value for logarithmic scale,
8750  // just draw it outside visible range
8751  return !mRangeReversed ? mAxisRect->right() + 200
8752  : mAxisRect->left() - 200;
8753  else if (value <= 0.0 &&
8754  mRange.upper >=
8755  0.0) // invalid value for logarithmic scale, just
8756  // draw it outside visible range
8757  return !mRangeReversed ? mAxisRect->left() - 200
8758  : mAxisRect->right() + 200;
8759  else {
8760  if (!mRangeReversed)
8761  return qLn(value / mRange.lower) /
8762  qLn(mRange.upper / mRange.lower) *
8763  mAxisRect->width() +
8764  mAxisRect->left();
8765  else
8766  return qLn(mRange.upper / value) /
8767  qLn(mRange.upper / mRange.lower) *
8768  mAxisRect->width() +
8769  mAxisRect->left();
8770  }
8771  }
8772  } else // orientation() == Qt::Vertical
8773  {
8774  if (mScaleType == stLinear) {
8775  if (!mRangeReversed)
8776  return mAxisRect->bottom() - (value - mRange.lower) /
8777  mRange.size() *
8778  mAxisRect->height();
8779  else
8780  return mAxisRect->bottom() - (mRange.upper - value) /
8781  mRange.size() *
8782  mAxisRect->height();
8783  } else // mScaleType == stLogarithmic
8784  {
8785  if (value >= 0.0 &&
8786  mRange.upper < 0.0) // invalid value for logarithmic scale,
8787  // just draw it outside visible range
8788  return !mRangeReversed ? mAxisRect->top() - 200
8789  : mAxisRect->bottom() + 200;
8790  else if (value <= 0.0 &&
8791  mRange.upper >=
8792  0.0) // invalid value for logarithmic scale, just
8793  // draw it outside visible range
8794  return !mRangeReversed ? mAxisRect->bottom() + 200
8795  : mAxisRect->top() - 200;
8796  else {
8797  if (!mRangeReversed)
8798  return mAxisRect->bottom() -
8799  qLn(value / mRange.lower) /
8800  qLn(mRange.upper / mRange.lower) *
8801  mAxisRect->height();
8802  else
8803  return mAxisRect->bottom() -
8804  qLn(mRange.upper / value) /
8805  qLn(mRange.upper / mRange.lower) *
8806  mAxisRect->height();
8807  }
8808  }
8809  }
8810 }
8811 
8823 QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const {
8824  if (!mVisible) return spNone;
8825 
8826  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
8827  return spAxis;
8828  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
8829  return spTickLabels;
8830  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
8831  return spAxisLabel;
8832  else
8833  return spNone;
8834 }
8835 
8836 /* inherits documentation from base class */
8837 double QCPAxis::selectTest(const QPointF &pos,
8838  bool onlySelectable,
8839  QVariant *details) const {
8840  if (!mParentPlot) return -1;
8841  SelectablePart part = getPartAt(pos);
8842  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
8843  return -1;
8844 
8845  if (details) details->setValue(part);
8846  return mParentPlot->selectionTolerance() * 0.99;
8847 }
8848 
8856 QList<QCPAbstractPlottable *> QCPAxis::plottables() const {
8857  QList<QCPAbstractPlottable *> result;
8858  if (!mParentPlot) return result;
8859 
8860  for (int i = 0; i < mParentPlot->mPlottables.size(); ++i) {
8861  if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||
8862  mParentPlot->mPlottables.at(i)->valueAxis() == this)
8863  result.append(mParentPlot->mPlottables.at(i));
8864  }
8865  return result;
8866 }
8867 
8873 QList<QCPGraph *> QCPAxis::graphs() const {
8874  QList<QCPGraph *> result;
8875  if (!mParentPlot) return result;
8876 
8877  for (int i = 0; i < mParentPlot->mGraphs.size(); ++i) {
8878  if (mParentPlot->mGraphs.at(i)->keyAxis() == this ||
8879  mParentPlot->mGraphs.at(i)->valueAxis() == this)
8880  result.append(mParentPlot->mGraphs.at(i));
8881  }
8882  return result;
8883 }
8884 
8892 QList<QCPAbstractItem *> QCPAxis::items() const {
8893  QList<QCPAbstractItem *> result;
8894  if (!mParentPlot) return result;
8895 
8896  for (int itemId = 0; itemId < mParentPlot->mItems.size(); ++itemId) {
8897  QList<QCPItemPosition *> positions =
8898  mParentPlot->mItems.at(itemId)->positions();
8899  for (int posId = 0; posId < positions.size(); ++posId) {
8900  if (positions.at(posId)->keyAxis() == this ||
8901  positions.at(posId)->valueAxis() == this) {
8902  result.append(mParentPlot->mItems.at(itemId));
8903  break;
8904  }
8905  }
8906  }
8907  return result;
8908 }
8909 
8915  switch (side) {
8916  case QCP::msLeft:
8917  return atLeft;
8918  case QCP::msRight:
8919  return atRight;
8920  case QCP::msTop:
8921  return atTop;
8922  case QCP::msBottom:
8923  return atBottom;
8924  default:
8925  break;
8926  }
8927  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
8928  return atLeft;
8929 }
8930 
8936  switch (type) {
8937  case atLeft:
8938  return atRight;
8939  break;
8940  case atRight:
8941  return atLeft;
8942  break;
8943  case atBottom:
8944  return atTop;
8945  break;
8946  case atTop:
8947  return atBottom;
8948  break;
8949  default:
8950  qDebug() << Q_FUNC_INFO << "invalid axis type";
8951  return atLeft;
8952  break;
8953  }
8954 }
8955 
8956 /* inherits documentation from base class */
8957 void QCPAxis::selectEvent(QMouseEvent *event,
8958  bool additive,
8959  const QVariant &details,
8960  bool *selectionStateChanged) {
8961  Q_UNUSED(event)
8962  SelectablePart part = details.value<SelectablePart>();
8963  if (mSelectableParts.testFlag(part)) {
8964  SelectableParts selBefore = mSelectedParts;
8965  setSelectedParts(additive ? mSelectedParts ^ part : part);
8966  if (selectionStateChanged)
8967  *selectionStateChanged = mSelectedParts != selBefore;
8968  }
8969 }
8970 
8971 /* inherits documentation from base class */
8972 void QCPAxis::deselectEvent(bool *selectionStateChanged) {
8973  SelectableParts selBefore = mSelectedParts;
8975  if (selectionStateChanged)
8976  *selectionStateChanged = mSelectedParts != selBefore;
8977 }
8978 
8996 void QCPAxis::mousePressEvent(QMouseEvent *event, const QVariant &details) {
8997  Q_UNUSED(details)
8998  if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag) ||
8999  !mAxisRect->rangeDrag().testFlag(orientation()) ||
9000  !mAxisRect->rangeDragAxes(orientation()).contains(this)) {
9001  event->ignore();
9002  return;
9003  }
9004 
9005  if (event->buttons() & Qt::LeftButton) {
9006  mDragging = true;
9007  // initialize antialiasing backup in case we start dragging:
9011  }
9012  // Mouse range dragging interaction:
9013  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
9015  }
9016 }
9017 
9031 void QCPAxis::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) {
9032  if (mDragging) {
9033  const double startPixel =
9034  orientation() == Qt::Horizontal ? startPos.x() : startPos.y();
9035  const double currentPixel = orientation() == Qt::Horizontal
9036  ? event->pos().x()
9037  : event->pos().y();
9038  if (mScaleType == QCPAxis::stLinear) {
9039  const double diff =
9040  pixelToCoord(startPixel) - pixelToCoord(currentPixel);
9042  mDragStartRange.upper + diff);
9043  } else if (mScaleType == QCPAxis::stLogarithmic) {
9044  const double diff =
9045  pixelToCoord(startPixel) / pixelToCoord(currentPixel);
9047  mDragStartRange.upper * diff);
9048  }
9049 
9053  }
9054 }
9055 
9069 void QCPAxis::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) {
9070  Q_UNUSED(event)
9071  Q_UNUSED(startPos)
9072  mDragging = false;
9076  }
9077 }
9078 
9097 void QCPAxis::wheelEvent(QWheelEvent *event) {
9098  // Mouse range zooming interaction:
9099  if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom) ||
9100  !mAxisRect->rangeZoom().testFlag(orientation()) ||
9101  !mAxisRect->rangeZoomAxes(orientation()).contains(this)) {
9102  event->ignore();
9103  return;
9104  }
9105 
9106  const double delta = qtCompatWheelEventDelta(event);
9107  const QPointF pos = qtCompatWheelEventPos(event);
9108 
9109  const double wheelSteps =
9110  delta / 120.0; // a single step delta is +/-120 usually
9111  const double factor =
9112  qPow(mAxisRect->rangeZoomFactor(orientation()), wheelSteps);
9113  scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? pos.x()
9114  : pos.y()));
9115  mParentPlot->replot();
9116 }
9117 
9136 }
9137 
9145 void QCPAxis::draw(QCPPainter *painter) {
9146  QVector<double> subTickPositions; // the final coordToPixel transformed
9147  // vector passed to QCPAxisPainter
9148  QVector<double> tickPositions; // the final coordToPixel transformed vector
9149  // passed to QCPAxisPainter
9150  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
9151  tickPositions.reserve(mTickVector.size());
9152  tickLabels.reserve(mTickVector.size());
9153  subTickPositions.reserve(mSubTickVector.size());
9154 
9155  if (mTicks) {
9156  for (int i = 0; i < mTickVector.size(); ++i) {
9157  tickPositions.append(coordToPixel(mTickVector.at(i)));
9158  if (mTickLabels) tickLabels.append(mTickVectorLabels.at(i));
9159  }
9160 
9161  if (mSubTicks) {
9162  const int subTickCount = mSubTickVector.size();
9163  for (int i = 0; i < subTickCount; ++i)
9164  subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
9165  }
9166  }
9167 
9168  // transfer all properties of this axis to QCPAxisPainterPrivate which it
9169  // needs to draw the axis. Note that some axis painter properties are
9170  // already set by direct feed-through with QCPAxis setters
9171  mAxisPainter->type = mAxisType;
9172  mAxisPainter->basePen = getBasePen();
9173  mAxisPainter->labelFont = getLabelFont();
9174  mAxisPainter->labelColor = getLabelColor();
9175  mAxisPainter->label = mLabel;
9176  mAxisPainter->substituteExponent = mNumberBeautifulPowers;
9177  mAxisPainter->tickPen = getTickPen();
9178  mAxisPainter->subTickPen = getSubTickPen();
9179  mAxisPainter->tickLabelFont = getTickLabelFont();
9180  mAxisPainter->tickLabelColor = getTickLabelColor();
9181  mAxisPainter->axisRect = mAxisRect->rect();
9182  mAxisPainter->viewportRect = mParentPlot->viewport();
9183  mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
9184  mAxisPainter->reversedEndings = mRangeReversed;
9185  mAxisPainter->tickPositions = tickPositions;
9186  mAxisPainter->tickLabels = tickLabels;
9187  mAxisPainter->subTickPositions = subTickPositions;
9188  mAxisPainter->draw(painter);
9189 }
9190 
9201  if (!mParentPlot) return;
9202  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0)
9203  return;
9204 
9205  QVector<QString> oldLabels = mTickVectorLabels;
9206  mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar,
9208  mSubTicks ? &mSubTickVector : 0,
9211  mTickVectorLabels == oldLabels; // if labels have changed, margin
9212  // might have changed, too
9213 }
9214 
9220 QPen QCPAxis::getBasePen() const {
9221  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
9222 }
9223 
9229 QPen QCPAxis::getTickPen() const {
9230  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
9231 }
9232 
9240 }
9241 
9249  : mTickLabelFont;
9250 }
9251 
9257 QFont QCPAxis::getLabelFont() const {
9258  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont
9259  : mLabelFont;
9260 }
9261 
9269  : mTickLabelColor;
9270 }
9271 
9277 QColor QCPAxis::getLabelColor() const {
9279  : mLabelColor;
9280 }
9281 
9299  if (!mVisible) // if not visible, directly return 0, don't cache 0 because
9300  // we can't react to setVisible in QCPAxis
9301  return 0;
9302 
9303  if (mCachedMarginValid) return mCachedMargin;
9304 
9305  // run through similar steps as QCPAxis::draw, and calculate margin needed
9306  // to fit axis and its labels
9307  int margin = 0;
9308 
9309  QVector<double> tickPositions; // the final coordToPixel transformed vector
9310  // passed to QCPAxisPainter
9311  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
9312  tickPositions.reserve(mTickVector.size());
9313  tickLabels.reserve(mTickVector.size());
9314 
9315  if (mTicks) {
9316  for (int i = 0; i < mTickVector.size(); ++i) {
9317  tickPositions.append(coordToPixel(mTickVector.at(i)));
9318  if (mTickLabels) tickLabels.append(mTickVectorLabels.at(i));
9319  }
9320  }
9321  // transfer all properties of this axis to QCPAxisPainterPrivate which it
9322  // needs to calculate the size. Note that some axis painter properties are
9323  // already set by direct feed-through with QCPAxis setters
9324  mAxisPainter->type = mAxisType;
9325  mAxisPainter->labelFont = getLabelFont();
9326  mAxisPainter->label = mLabel;
9327  mAxisPainter->tickLabelFont = mTickLabelFont;
9328  mAxisPainter->axisRect = mAxisRect->rect();
9329  mAxisPainter->viewportRect = mParentPlot->viewport();
9330  mAxisPainter->tickPositions = tickPositions;
9331  mAxisPainter->tickLabels = tickLabels;
9332  margin += mAxisPainter->size();
9333  margin += mPadding;
9334 
9335  mCachedMargin = margin;
9336  mCachedMarginValid = true;
9337  return margin;
9338 }
9339 
9340 /* inherits documentation from base class */
9342 
9346 
9364 QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot)
9365  : type(QCPAxis::atLeft),
9366  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
9367  lowerEnding(QCPLineEnding::esNone),
9368  upperEnding(QCPLineEnding::esNone),
9369  labelPadding(0),
9370  tickLabelPadding(0),
9371  tickLabelRotation(0),
9372  tickLabelSide(QCPAxis::lsOutside),
9373  substituteExponent(true),
9374  numberMultiplyCross(false),
9375  tickLengthIn(5),
9376  tickLengthOut(0),
9377  subTickLengthIn(2),
9378  subTickLengthOut(0),
9379  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
9380  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
9381  offset(0),
9382  abbreviateDecimalPowers(false),
9383  reversedEndings(false),
9384  mParentPlot(parentPlot),
9385  mLabelCache(16) // cache at most 16 (tick) labels
9386 {}
9387 
9388 QCPAxisPainterPrivate::~QCPAxisPainterPrivate() {}
9389 
9397 void QCPAxisPainterPrivate::draw(QCPPainter *painter) {
9398  QByteArray newHash = generateLabelParameterHash();
9399  if (newHash != mLabelParameterHash) {
9400  mLabelCache.clear();
9401  mLabelParameterHash = newHash;
9402  }
9403 
9404  QPoint origin;
9405  switch (type) {
9406  case QCPAxis::atLeft:
9407  origin = axisRect.bottomLeft() + QPoint(-offset, 0);
9408  break;
9409  case QCPAxis::atRight:
9410  origin = axisRect.bottomRight() + QPoint(+offset, 0);
9411  break;
9412  case QCPAxis::atTop:
9413  origin = axisRect.topLeft() + QPoint(0, -offset);
9414  break;
9415  case QCPAxis::atBottom:
9416  origin = axisRect.bottomLeft() + QPoint(0, +offset);
9417  break;
9418  }
9419 
9420  double xCor = 0,
9421  yCor = 0; // paint system correction, for pixel exact matches
9422  // (affects baselines and ticks of top/right axes)
9423  switch (type) {
9424  case QCPAxis::atTop:
9425  yCor = -1;
9426  break;
9427  case QCPAxis::atRight:
9428  xCor = 1;
9429  break;
9430  default:
9431  break;
9432  }
9433  int margin = 0;
9434  // draw baseline:
9435  QLineF baseLine;
9436  painter->setPen(basePen);
9437  if (QCPAxis::orientation(type) == Qt::Horizontal)
9438  baseLine.setPoints(origin + QPointF(xCor, yCor),
9439  origin + QPointF(axisRect.width() + xCor, yCor));
9440  else
9441  baseLine.setPoints(origin + QPointF(xCor, yCor),
9442  origin + QPointF(xCor, -axisRect.height() + yCor));
9443  if (reversedEndings)
9444  baseLine = QLineF(baseLine.p2(),
9445  baseLine.p1()); // won't make a difference for line
9446  // itself, but for line endings later
9447  painter->drawLine(baseLine);
9448 
9449  // draw ticks:
9450  if (!tickPositions.isEmpty()) {
9451  painter->setPen(tickPen);
9452  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight)
9453  ? -1
9454  : 1; // direction of ticks ("inward" is right for
9455  // left axis and left for right axis)
9456  if (QCPAxis::orientation(type) == Qt::Horizontal) {
9457  for (int i = 0; i < tickPositions.size(); ++i)
9458  painter->drawLine(
9459  QLineF(tickPositions.at(i) + xCor,
9460  origin.y() - tickLengthOut * tickDir + yCor,
9461  tickPositions.at(i) + xCor,
9462  origin.y() + tickLengthIn * tickDir + yCor));
9463  } else {
9464  for (int i = 0; i < tickPositions.size(); ++i)
9465  painter->drawLine(
9466  QLineF(origin.x() - tickLengthOut * tickDir + xCor,
9467  tickPositions.at(i) + yCor,
9468  origin.x() + tickLengthIn * tickDir + xCor,
9469  tickPositions.at(i) + yCor));
9470  }
9471  }
9472 
9473  // draw subticks:
9474  if (!subTickPositions.isEmpty()) {
9475  painter->setPen(subTickPen);
9476  // direction of ticks ("inward" is right for left axis and left for
9477  // right axis)
9478  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight)
9479  ? -1
9480  : 1;
9481  if (QCPAxis::orientation(type) == Qt::Horizontal) {
9482  for (int i = 0; i < subTickPositions.size(); ++i)
9483  painter->drawLine(
9484  QLineF(subTickPositions.at(i) + xCor,
9485  origin.y() - subTickLengthOut * tickDir + yCor,
9486  subTickPositions.at(i) + xCor,
9487  origin.y() + subTickLengthIn * tickDir + yCor));
9488  } else {
9489  for (int i = 0; i < subTickPositions.size(); ++i)
9490  painter->drawLine(
9491  QLineF(origin.x() - subTickLengthOut * tickDir + xCor,
9492  subTickPositions.at(i) + yCor,
9493  origin.x() + subTickLengthIn * tickDir + xCor,
9494  subTickPositions.at(i) + yCor));
9495  }
9496  }
9497  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
9498 
9499  // draw axis base endings:
9500  bool antialiasingBackup = painter->antialiasing();
9501  painter->setAntialiasing(true); // always want endings to be antialiased,
9502  // even if base and ticks themselves aren't
9503  painter->setBrush(QBrush(basePen.color()));
9504  QCPVector2D baseLineVector(baseLine.dx(), baseLine.dy());
9505  if (lowerEnding.style() != QCPLineEnding::esNone)
9506  lowerEnding.draw(painter,
9507  QCPVector2D(baseLine.p1()) -
9508  baseLineVector.normalized() *
9509  lowerEnding.realLength() *
9510  (lowerEnding.inverted() ? -1 : 1),
9511  -baseLineVector);
9512  if (upperEnding.style() != QCPLineEnding::esNone)
9513  upperEnding.draw(painter,
9514  QCPVector2D(baseLine.p2()) +
9515  baseLineVector.normalized() *
9516  upperEnding.realLength() *
9517  (upperEnding.inverted() ? -1 : 1),
9518  baseLineVector);
9519  painter->setAntialiasing(antialiasingBackup);
9520 
9521  // tick labels:
9522  QRect oldClipRect;
9523  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip
9524  // them to the axis rect
9525  {
9526  oldClipRect = painter->clipRegion().boundingRect();
9527  painter->setClipRect(axisRect);
9528  }
9529  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset
9530  // calculation of axis label
9531  if (!tickLabels.isEmpty()) {
9532  if (tickLabelSide == QCPAxis::lsOutside) margin += tickLabelPadding;
9533  painter->setFont(tickLabelFont);
9534  painter->setPen(QPen(tickLabelColor));
9535  const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
9536  int distanceToAxis = margin;
9537  if (tickLabelSide == QCPAxis::lsInside)
9538  distanceToAxis =
9539  -(qMax(tickLengthIn, subTickLengthIn) + tickLabelPadding);
9540  for (int i = 0; i < maxLabelIndex; ++i)
9541  placeTickLabel(painter, tickPositions.at(i), distanceToAxis,
9542  tickLabels.at(i), &tickLabelsSize);
9543  if (tickLabelSide == QCPAxis::lsOutside)
9544  margin += (QCPAxis::orientation(type) == Qt::Horizontal)
9545  ? tickLabelsSize.height()
9546  : tickLabelsSize.width();
9547  }
9548  if (tickLabelSide == QCPAxis::lsInside) painter->setClipRect(oldClipRect);
9549 
9550  // axis label:
9551  QRect labelBounds;
9552  if (!label.isEmpty()) {
9553  margin += labelPadding;
9554  painter->setFont(labelFont);
9555  painter->setPen(QPen(labelColor));
9556  labelBounds = painter->fontMetrics().boundingRect(
9557  0, 0, 0, 0, Qt::TextDontClip, label);
9558  if (type == QCPAxis::atLeft) {
9559  QTransform oldTransform = painter->transform();
9560  painter->translate((origin.x() - margin - labelBounds.height()),
9561  origin.y());
9562  painter->rotate(-90);
9563  painter->drawText(0, 0, axisRect.height(), labelBounds.height(),
9564  Qt::TextDontClip | Qt::AlignCenter, label);
9565  painter->setTransform(oldTransform);
9566  } else if (type == QCPAxis::atRight) {
9567  QTransform oldTransform = painter->transform();
9568  painter->translate((origin.x() + margin + labelBounds.height()),
9569  origin.y() - axisRect.height());
9570  painter->rotate(90);
9571  painter->drawText(0, 0, axisRect.height(), labelBounds.height(),
9572  Qt::TextDontClip | Qt::AlignCenter, label);
9573  painter->setTransform(oldTransform);
9574  } else if (type == QCPAxis::atTop)
9575  painter->drawText(origin.x(),
9576  origin.y() - margin - labelBounds.height(),
9577  axisRect.width(), labelBounds.height(),
9578  Qt::TextDontClip | Qt::AlignCenter, label);
9579  else if (type == QCPAxis::atBottom)
9580  painter->drawText(origin.x(), origin.y() + margin, axisRect.width(),
9581  labelBounds.height(),
9582  Qt::TextDontClip | Qt::AlignCenter, label);
9583  }
9584 
9585  // set selection boxes:
9586  int selectionTolerance = 0;
9587  if (mParentPlot)
9588  selectionTolerance = mParentPlot->selectionTolerance();
9589  else
9590  qDebug() << Q_FUNC_INFO << "mParentPlot is null";
9591  int selAxisOutSize =
9592  qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
9593  int selAxisInSize = selectionTolerance;
9594  int selTickLabelSize;
9595  int selTickLabelOffset;
9596  if (tickLabelSide == QCPAxis::lsOutside) {
9597  selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal
9598  ? tickLabelsSize.height()
9599  : tickLabelsSize.width());
9600  selTickLabelOffset =
9601  qMax(tickLengthOut, subTickLengthOut) + tickLabelPadding;
9602  } else {
9603  selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal
9604  ? tickLabelsSize.height()
9605  : tickLabelsSize.width());
9606  selTickLabelOffset =
9607  -(qMax(tickLengthIn, subTickLengthIn) + tickLabelPadding);
9608  }
9609  int selLabelSize = labelBounds.height();
9610  int selLabelOffset =
9611  qMax(tickLengthOut, subTickLengthOut) +
9612  (!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside
9613  ? tickLabelPadding + selTickLabelSize
9614  : 0) +
9615  labelPadding;
9616  if (type == QCPAxis::atLeft) {
9617  mAxisSelectionBox.setCoords(origin.x() - selAxisOutSize, axisRect.top(),
9618  origin.x() + selAxisInSize,
9619  axisRect.bottom());
9620  mTickLabelsSelectionBox.setCoords(
9621  origin.x() - selTickLabelOffset - selTickLabelSize,
9622  axisRect.top(), origin.x() - selTickLabelOffset,
9623  axisRect.bottom());
9624  mLabelSelectionBox.setCoords(
9625  origin.x() - selLabelOffset - selLabelSize, axisRect.top(),
9626  origin.x() - selLabelOffset, axisRect.bottom());
9627  } else if (type == QCPAxis::atRight) {
9628  mAxisSelectionBox.setCoords(origin.x() - selAxisInSize, axisRect.top(),
9629  origin.x() + selAxisOutSize,
9630  axisRect.bottom());
9631  mTickLabelsSelectionBox.setCoords(
9632  origin.x() + selTickLabelOffset + selTickLabelSize,
9633  axisRect.top(), origin.x() + selTickLabelOffset,
9634  axisRect.bottom());
9635  mLabelSelectionBox.setCoords(
9636  origin.x() + selLabelOffset + selLabelSize, axisRect.top(),
9637  origin.x() + selLabelOffset, axisRect.bottom());
9638  } else if (type == QCPAxis::atTop) {
9639  mAxisSelectionBox.setCoords(
9640  axisRect.left(), origin.y() - selAxisOutSize, axisRect.right(),
9641  origin.y() + selAxisInSize);
9642  mTickLabelsSelectionBox.setCoords(
9643  axisRect.left(),
9644  origin.y() - selTickLabelOffset - selTickLabelSize,
9645  axisRect.right(), origin.y() - selTickLabelOffset);
9646  mLabelSelectionBox.setCoords(
9647  axisRect.left(), origin.y() - selLabelOffset - selLabelSize,
9648  axisRect.right(), origin.y() - selLabelOffset);
9649  } else if (type == QCPAxis::atBottom) {
9650  mAxisSelectionBox.setCoords(axisRect.left(), origin.y() - selAxisInSize,
9651  axisRect.right(),
9652  origin.y() + selAxisOutSize);
9653  mTickLabelsSelectionBox.setCoords(
9654  axisRect.left(),
9655  origin.y() + selTickLabelOffset + selTickLabelSize,
9656  axisRect.right(), origin.y() + selTickLabelOffset);
9657  mLabelSelectionBox.setCoords(
9658  axisRect.left(), origin.y() + selLabelOffset + selLabelSize,
9659  axisRect.right(), origin.y() + selLabelOffset);
9660  }
9661  mAxisSelectionBox = mAxisSelectionBox.normalized();
9662  mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
9663  mLabelSelectionBox = mLabelSelectionBox.normalized();
9664  // draw hitboxes for debug purposes:
9665  // painter->setBrush(Qt::NoBrush);
9666  // painter->drawRects(QVector<QRect>() << mAxisSelectionBox <<
9667  // mTickLabelsSelectionBox << mLabelSelectionBox);
9668 }
9669 
9675 int QCPAxisPainterPrivate::size() const {
9676  int result = 0;
9677 
9678  // get length of tick marks pointing outwards:
9679  if (!tickPositions.isEmpty())
9680  result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
9681 
9682  // calculate size of tick labels:
9683  if (tickLabelSide == QCPAxis::lsOutside) {
9684  QSize tickLabelsSize(0, 0);
9685  if (!tickLabels.isEmpty()) {
9686  for (int i = 0; i < tickLabels.size(); ++i)
9687  getMaxTickLabelSize(tickLabelFont, tickLabels.at(i),
9688  &tickLabelsSize);
9689  result += QCPAxis::orientation(type) == Qt::Horizontal
9690  ? tickLabelsSize.height()
9691  : tickLabelsSize.width();
9692  result += tickLabelPadding;
9693  }
9694  }
9695 
9696  // calculate size of axis label (only height needed, because left/right
9697  // labels are rotated by 90 degrees):
9698  if (!label.isEmpty()) {
9699  QFontMetrics fontMetrics(labelFont);
9700  QRect bounds;
9701  bounds = fontMetrics.boundingRect(
9702  0, 0, 0, 0,
9703  Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
9704  result += bounds.height() + labelPadding;
9705  }
9706 
9707  return result;
9708 }
9709 
9717 void QCPAxisPainterPrivate::clearCache() { mLabelCache.clear(); }
9718 
9727 QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const {
9728  QByteArray result;
9729  result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));
9730  result.append(QByteArray::number(tickLabelRotation));
9731  result.append(QByteArray::number((int)tickLabelSide));
9732  result.append(QByteArray::number((int)substituteExponent));
9733  result.append(QByteArray::number((int)numberMultiplyCross));
9734  result.append(tickLabelColor.name().toLatin1() +
9735  QByteArray::number(tickLabelColor.alpha(), 16));
9736  result.append(tickLabelFont.toString().toLatin1());
9737  return result;
9738 }
9739 
9761 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter,
9762  double position,
9763  int distanceToAxis,
9764  const QString &text,
9765  QSize *tickLabelsSize) {
9766  // warning: if you change anything here, also adapt getMaxTickLabelSize()
9767  // accordingly!
9768  if (text.isEmpty()) return;
9769  QSize finalSize;
9770  QPointF labelAnchor;
9771  switch (type) {
9772  case QCPAxis::atLeft:
9773  labelAnchor = QPointF(axisRect.left() - distanceToAxis - offset,
9774  position);
9775  break;
9776  case QCPAxis::atRight:
9777  labelAnchor = QPointF(axisRect.right() + distanceToAxis + offset,
9778  position);
9779  break;
9780  case QCPAxis::atTop:
9781  labelAnchor =
9782  QPointF(position, axisRect.top() - distanceToAxis - offset);
9783  break;
9784  case QCPAxis::atBottom:
9785  labelAnchor = QPointF(position,
9786  axisRect.bottom() + distanceToAxis + offset);
9787  break;
9788  }
9789  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) &&
9790  !painter->modes().testFlag(
9791  QCPPainter::pmNoCaching)) // label caching enabled
9792  {
9793  CachedLabel *cachedLabel =
9794  mLabelCache.take(text); // attempt to get label from cache
9795  if (!cachedLabel) // no cached label existed, create it
9796  {
9797  cachedLabel = new CachedLabel;
9798  TickLabelData labelData = getTickLabelData(painter->font(), text);
9799  cachedLabel->offset = getTickLabelDrawOffset(labelData) +
9800  labelData.rotatedTotalBounds.topLeft();
9801  if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio())) {
9802  cachedLabel->pixmap =
9803  QPixmap(labelData.rotatedTotalBounds.size() *
9804  mParentPlot->bufferDevicePixelRatio());
9805 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
9806 #ifdef QCP_DEVICEPIXELRATIO_FLOAT
9807  cachedLabel->pixmap.setDevicePixelRatio(
9808  mParentPlot->devicePixelRatioF());
9809 #else
9810  cachedLabel->pixmap.setDevicePixelRatio(
9811  mParentPlot->devicePixelRatio());
9812 #endif
9813 #endif
9814  } else
9815  cachedLabel->pixmap =
9816  QPixmap(labelData.rotatedTotalBounds.size());
9817  cachedLabel->pixmap.fill(Qt::transparent);
9818  QCPPainter cachePainter(&cachedLabel->pixmap);
9819  cachePainter.setPen(painter->pen());
9820  drawTickLabel(
9821  &cachePainter, -labelData.rotatedTotalBounds.topLeft().x(),
9822  -labelData.rotatedTotalBounds.topLeft().y(), labelData);
9823  }
9824  // if label would be partly clipped by widget border on sides, don't
9825  // draw it (only for outside tick labels):
9826  bool labelClippedByBorder = false;
9827  if (tickLabelSide == QCPAxis::lsOutside) {
9828  if (QCPAxis::orientation(type) == Qt::Horizontal)
9829  labelClippedByBorder =
9830  labelAnchor.x() + cachedLabel->offset.x() +
9831  cachedLabel->pixmap.width() /
9832  mParentPlot
9833  ->bufferDevicePixelRatio() >
9834  viewportRect.right() ||
9835  labelAnchor.x() + cachedLabel->offset.x() <
9836  viewportRect.left();
9837  else
9838  labelClippedByBorder =
9839  labelAnchor.y() + cachedLabel->offset.y() +
9840  cachedLabel->pixmap.height() /
9841  mParentPlot
9842  ->bufferDevicePixelRatio() >
9843  viewportRect.bottom() ||
9844  labelAnchor.y() + cachedLabel->offset.y() <
9845  viewportRect.top();
9846  }
9847  if (!labelClippedByBorder) {
9848  painter->drawPixmap(labelAnchor + cachedLabel->offset,
9849  cachedLabel->pixmap);
9850  finalSize = cachedLabel->pixmap.size() /
9851  mParentPlot->bufferDevicePixelRatio();
9852  }
9853  mLabelCache.insert(text,
9854  cachedLabel); // return label to cache or insert for
9855  // the first time if newly created
9856  } else // label caching disabled, draw text directly on surface:
9857  {
9858  TickLabelData labelData = getTickLabelData(painter->font(), text);
9859  QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
9860  // if label would be partly clipped by widget border on sides, don't
9861  // draw it (only for outside tick labels):
9862  bool labelClippedByBorder = false;
9863  if (tickLabelSide == QCPAxis::lsOutside) {
9864  if (QCPAxis::orientation(type) == Qt::Horizontal)
9865  labelClippedByBorder =
9866  finalPosition.x() +
9867  (labelData.rotatedTotalBounds.width() +
9868  labelData.rotatedTotalBounds.left()) >
9869  viewportRect.right() ||
9870  finalPosition.x() +
9871  labelData.rotatedTotalBounds.left() <
9872  viewportRect.left();
9873  else
9874  labelClippedByBorder =
9875  finalPosition.y() +
9876  (labelData.rotatedTotalBounds.height() +
9877  labelData.rotatedTotalBounds.top()) >
9878  viewportRect.bottom() ||
9879  finalPosition.y() + labelData.rotatedTotalBounds.top() <
9880  viewportRect.top();
9881  }
9882  if (!labelClippedByBorder) {
9883  drawTickLabel(painter, finalPosition.x(), finalPosition.y(),
9884  labelData);
9885  finalSize = labelData.rotatedTotalBounds.size();
9886  }
9887  }
9888 
9889  // expand passed tickLabelsSize if current tick label is larger:
9890  if (finalSize.width() > tickLabelsSize->width())
9891  tickLabelsSize->setWidth(finalSize.width());
9892  if (finalSize.height() > tickLabelsSize->height())
9893  tickLabelsSize->setHeight(finalSize.height());
9894 }
9895 
9906 void QCPAxisPainterPrivate::drawTickLabel(
9907  QCPPainter *painter,
9908  double x,
9909  double y,
9910  const TickLabelData &labelData) const {
9911  // backup painter settings that we're about to change:
9912  QTransform oldTransform = painter->transform();
9913  QFont oldFont = painter->font();
9914 
9915  // transform painter to position/rotation:
9916  painter->translate(x, y);
9917  if (!qFuzzyIsNull(tickLabelRotation)) painter->rotate(tickLabelRotation);
9918 
9919  // draw text:
9920  if (!labelData.expPart
9921  .isEmpty()) // indicator that beautiful powers must be used
9922  {
9923  painter->setFont(labelData.baseFont);
9924  painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
9925  if (!labelData.suffixPart.isEmpty())
9926  painter->drawText(labelData.baseBounds.width() + 1 +
9927  labelData.expBounds.width(),
9928  0, 0, 0, Qt::TextDontClip, labelData.suffixPart);
9929  painter->setFont(labelData.expFont);
9930  painter->drawText(labelData.baseBounds.width() + 1, 0,
9931  labelData.expBounds.width(),
9932  labelData.expBounds.height(), Qt::TextDontClip,
9933  labelData.expPart);
9934  } else {
9935  painter->setFont(labelData.baseFont);
9936  painter->drawText(0, 0, labelData.totalBounds.width(),
9937  labelData.totalBounds.height(),
9938  Qt::TextDontClip | Qt::AlignHCenter,
9939  labelData.basePart);
9940  }
9941 
9942  // reset painter settings to what it was before:
9943  painter->setTransform(oldTransform);
9944  painter->setFont(oldFont);
9945 }
9946 
9956 QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(
9957  const QFont &font, const QString &text) const {
9958  TickLabelData result;
9959 
9960  // determine whether beautiful decimal powers should be used
9961  bool useBeautifulPowers = false;
9962  int ePos = -1; // first index of exponent part, text before that will be
9963  // basePart, text until eLast will be expPart
9964  int eLast = -1; // last index of exponent part, rest of text after this
9965  // will be suffixPart
9966  if (substituteExponent) {
9967  ePos = text.indexOf(QLatin1Char('e'));
9968  if (ePos > 0 && text.at(ePos - 1).isDigit()) {
9969  eLast = ePos;
9970  while (eLast + 1 < text.size() &&
9971  (text.at(eLast + 1) == QLatin1Char('+') ||
9972  text.at(eLast + 1) == QLatin1Char('-') ||
9973  text.at(eLast + 1).isDigit()))
9974  ++eLast;
9975  if (eLast > ePos) // only if also to right of 'e' is a digit/+/-
9976  // interpret it as beautifiable power
9977  useBeautifulPowers = true;
9978  }
9979  }
9980 
9981  // calculate text bounding rects and do string preparation for beautiful
9982  // decimal powers:
9983  result.baseFont = font;
9984  if (result.baseFont.pointSizeF() >
9985  0) // might return -1 if specified with setPixelSize, in that case we
9986  // can't do correction in next line
9987  result.baseFont.setPointSizeF(
9988  result.baseFont.pointSizeF() +
9989  0.05); // QFontMetrics.boundingRect has a bug for exact point
9990  // sizes that make the results oscillate due to internal
9991  // rounding
9992  if (useBeautifulPowers) {
9993  // split text into parts of number/symbol that will be drawn normally
9994  // and part that will be drawn as exponent:
9995  result.basePart = text.left(ePos);
9996  result.suffixPart =
9997  text.mid(eLast + 1); // also drawn normally but after exponent
9998  // in log scaling, we want to turn "1*10^n" into "10^n", else add
9999  // multiplication sign and decimal base:
10000  if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
10001  result.basePart = QLatin1String("10");
10002  else
10003  result.basePart += (numberMultiplyCross ? QString(QChar(215))
10004  : QString(QChar(183))) +
10005  QLatin1String("10");
10006  result.expPart = text.mid(ePos + 1, eLast - ePos);
10007  // clip "+" and leading zeros off expPart:
10008  while (result.expPart.length() > 2 &&
10009  result.expPart.at(1) ==
10010  QLatin1Char('0')) // length > 2 so we leave one zero
10011  // when numberFormatChar is 'e'
10012  result.expPart.remove(1, 1);
10013  if (!result.expPart.isEmpty() &&
10014  result.expPart.at(0) == QLatin1Char('+'))
10015  result.expPart.remove(0, 1);
10016  // prepare smaller font for exponent:
10017  result.expFont = font;
10018  if (result.expFont.pointSize() > 0)
10019  result.expFont.setPointSize(result.expFont.pointSize() * 0.75);
10020  else
10021  result.expFont.setPixelSize(result.expFont.pixelSize() * 0.75);
10022  // calculate bounding rects of base part(s), exponent part and total
10023  // one:
10024  result.baseBounds = QFontMetrics(result.baseFont)
10025  .boundingRect(0, 0, 0, 0, Qt::TextDontClip,
10026  result.basePart);
10027  result.expBounds = QFontMetrics(result.expFont)
10028  .boundingRect(0, 0, 0, 0, Qt::TextDontClip,
10029  result.expPart);
10030  if (!result.suffixPart.isEmpty())
10031  result.suffixBounds =
10032  QFontMetrics(result.baseFont)
10033  .boundingRect(0, 0, 0, 0, Qt::TextDontClip,
10034  result.suffixPart);
10035  result.totalBounds = result.baseBounds.adjusted(
10036  0, 0,
10037  result.expBounds.width() + result.suffixBounds.width() + 2,
10038  0); // +2 consists of the 1 pixel spacing between base and
10039  // exponent (see drawTickLabel) and an extra pixel to
10040  // include AA
10041  } else // useBeautifulPowers == false
10042  {
10043  result.basePart = text;
10044  result.totalBounds =
10045  QFontMetrics(result.baseFont)
10046  .boundingRect(0, 0, 0, 0,
10047  Qt::TextDontClip | Qt::AlignHCenter,
10048  result.basePart);
10049  }
10050  result.totalBounds.moveTopLeft(QPoint(
10051  0,
10052  0)); // want bounding box aligned top left at origin, independent
10053  // of how it was created, to make further processing simpler
10054 
10055  // calculate possibly different bounding rect after rotation:
10056  result.rotatedTotalBounds = result.totalBounds;
10057  if (!qFuzzyIsNull(tickLabelRotation)) {
10058  QTransform transform;
10059  transform.rotate(tickLabelRotation);
10060  result.rotatedTotalBounds =
10061  transform.mapRect(result.rotatedTotalBounds);
10062  }
10063 
10064  return result;
10065 }
10066 
10078 QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(
10079  const TickLabelData &labelData) const {
10080  /*
10081  calculate label offset from base point at tick (non-trivial, for best
10082  visual appearance): short explanation for bottom axis: The anchor, i.e.
10083  the point in the label that is placed horizontally under the corresponding
10084  tick is always on the label side that is closer to the axis (e.g. the left
10085  side of the text when we're rotating clockwise). On that side, the height
10086  is halved and the resulting point is defined the anchor. This way, a 90
10087  degree rotated text will be centered under the tick (i.e. displaced
10088  horizontally by half its height). At the same time, a 45 degree rotated
10089  text will "point toward" its tick, as is typical for rotated tick labels.
10090  */
10091  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
10092  bool flip =
10093  qFuzzyCompare(qAbs(tickLabelRotation),
10094  90.0); // perfect +/-90 degree flip. Indicates
10095  // vertical label centering on vertical axes.
10096  double radians = tickLabelRotation / 180.0 * M_PI;
10097  int x = 0, y = 0;
10098  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) ||
10099  (type == QCPAxis::atRight &&
10100  tickLabelSide ==
10101  QCPAxis::lsInside)) // Anchor at right side of tick label
10102  {
10103  if (doRotation) {
10104  if (tickLabelRotation > 0) {
10105  x = -qCos(radians) * labelData.totalBounds.width();
10106  y = flip ? -labelData.totalBounds.width() / 2.0
10107  : -qSin(radians) * labelData.totalBounds.width() -
10108  qCos(radians) *
10109  labelData.totalBounds.height() /
10110  2.0;
10111  } else {
10112  x = -qCos(-radians) * labelData.totalBounds.width() -
10113  qSin(-radians) * labelData.totalBounds.height();
10114  y = flip ? +labelData.totalBounds.width() / 2.0
10115  : +qSin(-radians) * labelData.totalBounds.width() -
10116  qCos(-radians) *
10117  labelData.totalBounds.height() /
10118  2.0;
10119  }
10120  } else {
10121  x = -labelData.totalBounds.width();
10122  y = -labelData.totalBounds.height() / 2.0;
10123  }
10124  } else if ((type == QCPAxis::atRight &&
10125  tickLabelSide == QCPAxis::lsOutside) ||
10126  (type == QCPAxis::atLeft &&
10127  tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of
10128  // tick label
10129  {
10130  if (doRotation) {
10131  if (tickLabelRotation > 0) {
10132  x = +qSin(radians) * labelData.totalBounds.height();
10133  y = flip ? -labelData.totalBounds.width() / 2.0
10134  : -qCos(radians) * labelData.totalBounds.height() /
10135  2.0;
10136  } else {
10137  x = 0;
10138  y = flip ? +labelData.totalBounds.width() / 2.0
10139  : -qCos(-radians) * labelData.totalBounds.height() /
10140  2.0;
10141  }
10142  } else {
10143  x = 0;
10144  y = -labelData.totalBounds.height() / 2.0;
10145  }
10146  } else if ((type == QCPAxis::atTop &&
10147  tickLabelSide == QCPAxis::lsOutside) ||
10148  (type == QCPAxis::atBottom &&
10149  tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side
10150  // of tick label
10151  {
10152  if (doRotation) {
10153  if (tickLabelRotation > 0) {
10154  x = -qCos(radians) * labelData.totalBounds.width() +
10155  qSin(radians) * labelData.totalBounds.height() / 2.0;
10156  y = -qSin(radians) * labelData.totalBounds.width() -
10157  qCos(radians) * labelData.totalBounds.height();
10158  } else {
10159  x = -qSin(-radians) * labelData.totalBounds.height() / 2.0;
10160  y = -qCos(-radians) * labelData.totalBounds.height();
10161  }
10162  } else {
10163  x = -labelData.totalBounds.width() / 2.0;
10164  y = -labelData.totalBounds.height();
10165  }
10166  } else if ((type == QCPAxis::atBottom &&
10167  tickLabelSide == QCPAxis::lsOutside) ||
10168  (type == QCPAxis::atTop &&
10169  tickLabelSide ==
10170  QCPAxis::lsInside)) // Anchor at top side of tick label
10171  {
10172  if (doRotation) {
10173  if (tickLabelRotation > 0) {
10174  x = +qSin(radians) * labelData.totalBounds.height() / 2.0;
10175  y = 0;
10176  } else {
10177  x = -qCos(-radians) * labelData.totalBounds.width() -
10178  qSin(-radians) * labelData.totalBounds.height() / 2.0;
10179  y = +qSin(-radians) * labelData.totalBounds.width();
10180  }
10181  } else {
10182  x = -labelData.totalBounds.width() / 2.0;
10183  y = 0;
10184  }
10185  }
10186 
10187  return QPointF(x, y);
10188 }
10189 
10198 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font,
10199  const QString &text,
10200  QSize *tickLabelsSize) const {
10201  // note: this function must return the same tick label sizes as the
10202  // placeTickLabel function.
10203  QSize finalSize;
10204  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) &&
10205  mLabelCache.contains(
10206  text)) // label caching enabled and have cached label
10207  {
10208  const CachedLabel *cachedLabel = mLabelCache.object(text);
10209  finalSize = cachedLabel->pixmap.size() /
10210  mParentPlot->bufferDevicePixelRatio();
10211  } else // label caching disabled or no label with this text cached:
10212  {
10213  TickLabelData labelData = getTickLabelData(font, text);
10214  finalSize = labelData.rotatedTotalBounds.size();
10215  }
10216 
10217  // expand passed tickLabelsSize if current tick label is larger:
10218  if (finalSize.width() > tickLabelsSize->width())
10219  tickLabelsSize->setWidth(finalSize.width());
10220  if (finalSize.height() > tickLabelsSize->height())
10221  tickLabelsSize->setHeight(finalSize.height());
10222 }
10223 /* end of 'src/axis/axis.cpp' */
10224 
10225 /* including file 'src/scatterstyle.cpp', size 17450 */
10226 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
10227 
10231 
10291 /* start documentation of inline functions */
10292 
10315 /* end documentation of inline functions */
10316 
10325  : mSize(6),
10326  mShape(ssNone),
10327  mPen(Qt::NoPen),
10328  mBrush(Qt::NoBrush),
10329  mPenDefined(false) {}
10330 
10339  : mSize(size),
10340  mShape(shape),
10341  mPen(Qt::NoPen),
10342  mBrush(Qt::NoBrush),
10343  mPenDefined(false) {}
10344 
10351  const QColor &color,
10352  double size)
10353  : mSize(size),
10354  mShape(shape),
10355  mPen(QPen(color)),
10356  mBrush(Qt::NoBrush),
10357  mPenDefined(true) {}
10358 
10365  const QColor &color,
10366  const QColor &fill,
10367  double size)
10368  : mSize(size),
10369  mShape(shape),
10370  mPen(QPen(color)),
10371  mBrush(QBrush(fill)),
10372  mPenDefined(true) {}
10373 
10391  const QPen &pen,
10392  const QBrush &brush,
10393  double size)
10394  : mSize(size),
10395  mShape(shape),
10396  mPen(pen),
10397  mBrush(brush),
10398  mPenDefined(pen.style() != Qt::NoPen) {}
10399 
10404 QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap)
10405  : mSize(5),
10406  mShape(ssPixmap),
10407  mPen(Qt::NoPen),
10408  mBrush(Qt::NoBrush),
10409  mPixmap(pixmap),
10410  mPenDefined(false) {}
10411 
10422 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath,
10423  const QPen &pen,
10424  const QBrush &brush,
10425  double size)
10426  : mSize(size),
10427  mShape(ssCustom),
10428  mPen(pen),
10429  mBrush(brush),
10430  mCustomPath(customPath),
10431  mPenDefined(pen.style() != Qt::NoPen) {}
10432 
10438  ScatterProperties properties) {
10439  if (properties.testFlag(spPen)) {
10440  setPen(other.pen());
10441  if (!other.isPenDefined()) undefinePen();
10442  }
10443  if (properties.testFlag(spBrush)) setBrush(other.brush());
10444  if (properties.testFlag(spSize)) setSize(other.size());
10445  if (properties.testFlag(spShape)) {
10446  setShape(other.shape());
10447  if (other.shape() == ssPixmap)
10448  setPixmap(other.pixmap());
10449  else if (other.shape() == ssCustom)
10450  setCustomPath(other.customPath());
10451  }
10452 }
10453 
10460 
10470  mShape = shape;
10471 }
10472 
10483 void QCPScatterStyle::setPen(const QPen &pen) {
10484  mPenDefined = true;
10485  mPen = pen;
10486 }
10487 
10495 void QCPScatterStyle::setBrush(const QBrush &brush) { mBrush = brush; }
10496 
10504 void QCPScatterStyle::setPixmap(const QPixmap &pixmap) {
10505  setShape(ssPixmap);
10506  mPixmap = pixmap;
10507 }
10508 
10514 void QCPScatterStyle::setCustomPath(const QPainterPath &customPath) {
10515  setShape(ssCustom);
10517 }
10518 
10526 
10539  const QPen &defaultPen) const {
10540  painter->setPen(mPenDefined ? mPen : defaultPen);
10541  painter->setBrush(mBrush);
10542 }
10543 
10553 void QCPScatterStyle::drawShape(QCPPainter *painter, const QPointF &pos) const {
10554  drawShape(painter, pos.x(), pos.y());
10555 }
10556 
10560 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const {
10561  double w = mSize / 2.0;
10562  switch (mShape) {
10563  case ssNone:
10564  break;
10565  case ssDot: {
10566  painter->drawLine(QPointF(x, y), QPointF(x + 0.0001, y));
10567  break;
10568  }
10569  case ssCross: {
10570  painter->drawLine(QLineF(x - w, y - w, x + w, y + w));
10571  painter->drawLine(QLineF(x - w, y + w, x + w, y - w));
10572  break;
10573  }
10574  case ssPlus: {
10575  painter->drawLine(QLineF(x - w, y, x + w, y));
10576  painter->drawLine(QLineF(x, y + w, x, y - w));
10577  break;
10578  }
10579  case ssCircle: {
10580  painter->drawEllipse(QPointF(x, y), w, w);
10581  break;
10582  }
10583  case ssDisc: {
10584  QBrush b = painter->brush();
10585  painter->setBrush(painter->pen().color());
10586  painter->drawEllipse(QPointF(x, y), w, w);
10587  painter->setBrush(b);
10588  break;
10589  }
10590  case ssSquare: {
10591  painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
10592  break;
10593  }
10594  case ssDiamond: {
10595  QPointF lineArray[4] = {QPointF(x - w, y), QPointF(x, y - w),
10596  QPointF(x + w, y), QPointF(x, y + w)};
10597  painter->drawPolygon(lineArray, 4);
10598  break;
10599  }
10600  case ssStar: {
10601  painter->drawLine(QLineF(x - w, y, x + w, y));
10602  painter->drawLine(QLineF(x, y + w, x, y - w));
10603  painter->drawLine(QLineF(x - w * 0.707, y - w * 0.707,
10604  x + w * 0.707, y + w * 0.707));
10605  painter->drawLine(QLineF(x - w * 0.707, y + w * 0.707,
10606  x + w * 0.707, y - w * 0.707));
10607  break;
10608  }
10609  case ssTriangle: {
10610  QPointF lineArray[3] = {QPointF(x - w, y + 0.755 * w),
10611  QPointF(x + w, y + 0.755 * w),
10612  QPointF(x, y - 0.977 * w)};
10613  painter->drawPolygon(lineArray, 3);
10614  break;
10615  }
10616  case ssTriangleInverted: {
10617  QPointF lineArray[3] = {QPointF(x - w, y - 0.755 * w),
10618  QPointF(x + w, y - 0.755 * w),
10619  QPointF(x, y + 0.977 * w)};
10620  painter->drawPolygon(lineArray, 3);
10621  break;
10622  }
10623  case ssCrossSquare: {
10624  painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
10625  painter->drawLine(QLineF(x - w, y - w, x + w * 0.95, y + w * 0.95));
10626  painter->drawLine(QLineF(x - w, y + w * 0.95, x + w * 0.95, y - w));
10627  break;
10628  }
10629  case ssPlusSquare: {
10630  painter->drawRect(QRectF(x - w, y - w, mSize, mSize));
10631  painter->drawLine(QLineF(x - w, y, x + w * 0.95, y));
10632  painter->drawLine(QLineF(x, y + w, x, y - w));
10633  break;
10634  }
10635  case ssCrossCircle: {
10636  painter->drawEllipse(QPointF(x, y), w, w);
10637  painter->drawLine(QLineF(x - w * 0.707, y - w * 0.707,
10638  x + w * 0.670, y + w * 0.670));
10639  painter->drawLine(QLineF(x - w * 0.707, y + w * 0.670,
10640  x + w * 0.670, y - w * 0.707));
10641  break;
10642  }
10643  case ssPlusCircle: {
10644  painter->drawEllipse(QPointF(x, y), w, w);
10645  painter->drawLine(QLineF(x - w, y, x + w, y));
10646  painter->drawLine(QLineF(x, y + w, x, y - w));
10647  break;
10648  }
10649  case ssPeace: {
10650  painter->drawEllipse(QPointF(x, y), w, w);
10651  painter->drawLine(QLineF(x, y - w, x, y + w));
10652  painter->drawLine(QLineF(x, y, x - w * 0.707, y + w * 0.707));
10653  painter->drawLine(QLineF(x, y, x + w * 0.707, y + w * 0.707));
10654  break;
10655  }
10656  case ssPixmap: {
10657  const double widthHalf = mPixmap.width() * 0.5;
10658  const double heightHalf = mPixmap.height() * 0.5;
10659 #if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
10660  const QRectF clipRect =
10661  painter->clipRegion().boundingRect().adjusted(
10662  -widthHalf, -heightHalf, widthHalf, heightHalf);
10663 #else
10664  const QRectF clipRect = painter->clipBoundingRect().adjusted(
10665  -widthHalf, -heightHalf, widthHalf, heightHalf);
10666 #endif
10667  if (clipRect.contains(x, y))
10668  painter->drawPixmap(x - widthHalf, y - heightHalf, mPixmap);
10669  break;
10670  }
10671  case ssCustom: {
10672  QTransform oldTransform = painter->transform();
10673  painter->translate(x, y);
10674  painter->scale(mSize / 6.0, mSize / 6.0);
10675  painter->drawPath(mCustomPath);
10676  painter->setTransform(oldTransform);
10677  break;
10678  }
10679  }
10680 }
10681 /* end of 'src/scatterstyle.cpp' */
10682 
10683 // amalgamation: add datacontainer.cpp
10684 
10685 /* including file 'src/plottable.cpp', size 38845 */
10686 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
10687 
10691 
10726  : mPen(QColor(80, 80, 255), 2.5),
10727  mBrush(Qt::NoBrush),
10728  mScatterStyle(),
10729  mUsedScatterProperties(QCPScatterStyle::spNone),
10730  mPlottable(0) {}
10731 
10733 
10738 void QCPSelectionDecorator::setPen(const QPen &pen) { mPen = pen; }
10739 
10744 void QCPSelectionDecorator::setBrush(const QBrush &brush) { mBrush = brush; }
10745 
10755  const QCPScatterStyle &scatterStyle,
10756  QCPScatterStyle::ScatterProperties usedProperties) {
10758  setUsedScatterProperties(usedProperties);
10759 }
10760 
10770  const QCPScatterStyle::ScatterProperties &properties) {
10771  mUsedScatterProperties = properties;
10772 }
10773 
10780  painter->setPen(mPen);
10781 }
10782 
10789  painter->setBrush(mBrush);
10790 }
10791 
10803  const QCPScatterStyle &unselectedStyle) const {
10804  QCPScatterStyle result(unselectedStyle);
10806 
10807  // if style shall inherit pen from plottable (has no own pen defined), give
10808  // it the selected plottable pen explicitly, so it doesn't use the
10809  // unselected plottable pen when used in the plottable:
10810  if (!result.isPenDefined()) result.setPen(mPen);
10811 
10812  return result;
10813 }
10814 
10820  setPen(other->pen());
10821  setBrush(other->brush());
10823 }
10824 
10835  QCPDataSelection selection) {
10836  Q_UNUSED(painter)
10837  Q_UNUSED(selection)
10838 }
10839 
10852  QCPAbstractPlottable *plottable) {
10853  if (!mPlottable) {
10854  mPlottable = plottable;
10855  return true;
10856  } else {
10857  qDebug() << Q_FUNC_INFO
10858  << "This selection decorator is already registered with "
10859  "plottable:"
10860  << reinterpret_cast<quintptr>(mPlottable);
10861  return false;
10862  }
10863 }
10864 
10868 
10958 /* start of documentation of inline functions */
10959 
10994 /* end of documentation of inline functions */
10995 /* start of documentation of pure virtual functions */
10996 
11054 /* end of documentation of pure virtual functions */
11055 /* start of documentation of signals */
11056 
11084 /* end of documentation of signals */
11085 
11100  : QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
11101  mName(),
11102  mAntialiasedFill(true),
11103  mAntialiasedScatters(true),
11104  mPen(Qt::black),
11105  mBrush(Qt::NoBrush),
11106  mKeyAxis(keyAxis),
11107  mValueAxis(valueAxis),
11108  mSelectable(QCP::stWhole),
11109  mSelectionDecorator(0) {
11110  if (keyAxis->parentPlot() != valueAxis->parentPlot())
11111  qDebug() << Q_FUNC_INFO
11112  << "Parent plot of keyAxis is not the same as that of "
11113  "valueAxis.";
11115  qDebug() << Q_FUNC_INFO
11116  << "keyAxis and valueAxis must be orthogonal to each other.";
11117 
11120 }
11121 
11123  if (mSelectionDecorator) {
11124  delete mSelectionDecorator;
11125  mSelectionDecorator = 0;
11126  }
11127 }
11128 
11134 void QCPAbstractPlottable::setName(const QString &name) { mName = name; }
11135 
11144  mAntialiasedFill = enabled;
11145 }
11146 
11156  mAntialiasedScatters = enabled;
11157 }
11158 
11167 void QCPAbstractPlottable::setPen(const QPen &pen) { mPen = pen; }
11168 
11178 void QCPAbstractPlottable::setBrush(const QBrush &brush) { mBrush = brush; }
11179 
11194 
11209 
11233  if (mSelection != selection) {
11235  emit selectionChanged(selected());
11237  }
11238 }
11239 
11250  QCPSelectionDecorator *decorator) {
11251  if (decorator) {
11252  if (decorator->registerWithPlottable(this)) {
11253  if (mSelectionDecorator) // delete old decorator if necessary
11254  delete mSelectionDecorator;
11255  mSelectionDecorator = decorator;
11256  }
11257  } else if (mSelectionDecorator) // just clear decorator
11258  {
11259  delete mSelectionDecorator;
11260  mSelectionDecorator = 0;
11261  }
11262 }
11263 
11275  if (mSelectable != selectable) {
11277  QCPDataSelection oldSelection = mSelection;
11280  if (mSelection != oldSelection) {
11281  emit selectionChanged(selected());
11283  }
11284  }
11285 }
11286 
11298  double value,
11299  double &x,
11300  double &y) const {
11301  QCPAxis *keyAxis = mKeyAxis.data();
11302  QCPAxis *valueAxis = mValueAxis.data();
11303  if (!keyAxis || !valueAxis) {
11304  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
11305  return;
11306  }
11307 
11308  if (keyAxis->orientation() == Qt::Horizontal) {
11309  x = keyAxis->coordToPixel(key);
11310  y = valueAxis->coordToPixel(value);
11311  } else {
11312  y = keyAxis->coordToPixel(key);
11313  x = valueAxis->coordToPixel(value);
11314  }
11315 }
11316 
11322 const QPointF QCPAbstractPlottable::coordsToPixels(double key,
11323  double value) const {
11324  QCPAxis *keyAxis = mKeyAxis.data();
11325  QCPAxis *valueAxis = mValueAxis.data();
11326  if (!keyAxis || !valueAxis) {
11327  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
11328  return QPointF();
11329  }
11330 
11331  if (keyAxis->orientation() == Qt::Horizontal)
11332  return QPointF(keyAxis->coordToPixel(key),
11333  valueAxis->coordToPixel(value));
11334  else
11335  return QPointF(valueAxis->coordToPixel(value),
11336  keyAxis->coordToPixel(key));
11337 }
11338 
11350  double y,
11351  double &key,
11352  double &value) const {
11353  QCPAxis *keyAxis = mKeyAxis.data();
11354  QCPAxis *valueAxis = mValueAxis.data();
11355  if (!keyAxis || !valueAxis) {
11356  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
11357  return;
11358  }
11359 
11360  if (keyAxis->orientation() == Qt::Horizontal) {
11361  key = keyAxis->pixelToCoord(x);
11362  value = valueAxis->pixelToCoord(y);
11363  } else {
11364  key = keyAxis->pixelToCoord(y);
11365  value = valueAxis->pixelToCoord(x);
11366  }
11367 }
11368 
11373 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos,
11374  double &key,
11375  double &value) const {
11376  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
11377 }
11378 
11395 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const {
11396  rescaleKeyAxis(onlyEnlarge);
11397  rescaleValueAxis(onlyEnlarge);
11398 }
11399 
11405 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const {
11406  QCPAxis *keyAxis = mKeyAxis.data();
11407  if (!keyAxis) {
11408  qDebug() << Q_FUNC_INFO << "invalid key axis";
11409  return;
11410  }
11411 
11412  QCP::SignDomain signDomain = QCP::sdBoth;
11414  signDomain = (keyAxis->range().upper < 0 ? QCP::sdNegative
11415  : QCP::sdPositive);
11416 
11417  bool foundRange;
11418  QCPRange newRange = getKeyRange(foundRange, signDomain);
11419  if (foundRange) {
11420  if (onlyEnlarge) newRange.expand(keyAxis->range());
11421  if (!QCPRange::validRange(
11422  newRange)) // likely due to range being zero (plottable has
11423  // only constant data in this axis dimension),
11424  // shift current range to at least center the
11425  // plottable
11426  {
11427  double center = (newRange.lower + newRange.upper) *
11428  0.5; // upper and lower should be equal anyway, but
11429  // just to make sure, incase validRange
11430  // returned false for other reason
11431  if (keyAxis->scaleType() == QCPAxis::stLinear) {
11432  newRange.lower = center - keyAxis->range().size() / 2.0;
11433  newRange.upper = center + keyAxis->range().size() / 2.0;
11434  } else // scaleType() == stLogarithmic
11435  {
11436  newRange.lower = center / qSqrt(keyAxis->range().upper /
11437  keyAxis->range().lower);
11438  newRange.upper = center * qSqrt(keyAxis->range().upper /
11439  keyAxis->range().lower);
11440  }
11441  }
11442  keyAxis->setRange(newRange);
11443  }
11444 }
11445 
11457  bool inKeyRange) const {
11458  QCPAxis *keyAxis = mKeyAxis.data();
11459  QCPAxis *valueAxis = mValueAxis.data();
11460  if (!keyAxis || !valueAxis) {
11461  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
11462  return;
11463  }
11464 
11465  QCP::SignDomain signDomain = QCP::sdBoth;
11467  signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative
11468  : QCP::sdPositive);
11469 
11470  bool foundRange;
11471  QCPRange newRange = getValueRange(
11472  foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange());
11473  if (foundRange) {
11474  if (onlyEnlarge) newRange.expand(valueAxis->range());
11475  if (!QCPRange::validRange(
11476  newRange)) // likely due to range being zero (plottable has
11477  // only constant data in this axis dimension),
11478  // shift current range to at least center the
11479  // plottable
11480  {
11481  double center = (newRange.lower + newRange.upper) *
11482  0.5; // upper and lower should be equal anyway, but
11483  // just to make sure, incase validRange
11484  // returned false for other reason
11486  newRange.lower = center - valueAxis->range().size() / 2.0;
11487  newRange.upper = center + valueAxis->range().size() / 2.0;
11488  } else // scaleType() == stLogarithmic
11489  {
11490  newRange.lower = center / qSqrt(valueAxis->range().upper /
11491  valueAxis->range().lower);
11492  newRange.upper = center * qSqrt(valueAxis->range().upper /
11493  valueAxis->range().lower);
11494  }
11495  }
11496  valueAxis->setRange(newRange);
11497  }
11498 }
11499 
11515  if (!legend) {
11516  qDebug() << Q_FUNC_INFO << "passed legend is null";
11517  return false;
11518  }
11519  if (legend->parentPlot() != mParentPlot) {
11520  qDebug() << Q_FUNC_INFO
11521  << "passed legend isn't in the same QCustomPlot as this "
11522  "plottable";
11523  return false;
11524  }
11525 
11526  if (!legend->hasItemWithPlottable(this)) {
11527  legend->addItem(new QCPPlottableLegendItem(legend, this));
11528  return true;
11529  } else
11530  return false;
11531 }
11532 
11541  if (!mParentPlot || !mParentPlot->legend)
11542  return false;
11543  else
11544  return addToLegend(mParentPlot->legend);
11545 }
11546 
11558  if (!legend) {
11559  qDebug() << Q_FUNC_INFO << "passed legend is null";
11560  return false;
11561  }
11562 
11563  if (QCPPlottableLegendItem *lip = legend->itemWithPlottable(this))
11564  return legend->removeItem(lip);
11565  else
11566  return false;
11567 }
11568 
11576  if (!mParentPlot || !mParentPlot->legend)
11577  return false;
11578  else
11580 }
11581 
11582 /* inherits documentation from base class */
11584  if (mKeyAxis && mValueAxis)
11585  return mKeyAxis.data()->axisRect()->rect() &
11586  mValueAxis.data()->axisRect()->rect();
11587  else
11588  return QRect();
11589 }
11590 
11591 /* inherits documentation from base class */
11593  return QCP::iSelectPlottables;
11594 }
11595 
11613  QCPPainter *painter) const {
11615 }
11616 
11630  QCPPainter *painter) const {
11632 }
11633 
11646  QCPPainter *painter) const {
11648 }
11649 
11650 /* inherits documentation from base class */
11652  bool additive,
11653  const QVariant &details,
11654  bool *selectionStateChanged) {
11655  Q_UNUSED(event)
11656 
11657  if (mSelectable != QCP::stNone) {
11658  QCPDataSelection newSelection = details.value<QCPDataSelection>();
11659  QCPDataSelection selectionBefore = mSelection;
11660  if (additive) {
11661  if (mSelectable ==
11662  QCP::stWhole) // in whole selection mode, we toggle to no
11663  // selection even if currently unselected point
11664  // was hit
11665  {
11666  if (selected())
11668  else
11669  setSelection(newSelection);
11670  } else // in all other selection modes we toggle selections of
11671  // homogeneously selected/unselected segments
11672  {
11673  if (mSelection.contains(
11674  newSelection)) // if entire newSelection is already
11675  // selected, toggle selection
11676  setSelection(mSelection - newSelection);
11677  else
11678  setSelection(mSelection + newSelection);
11679  }
11680  } else
11681  setSelection(newSelection);
11682  if (selectionStateChanged)
11683  *selectionStateChanged = mSelection != selectionBefore;
11684  }
11685 }
11686 
11687 /* inherits documentation from base class */
11688 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) {
11689  if (mSelectable != QCP::stNone) {
11690  QCPDataSelection selectionBefore = mSelection;
11692  if (selectionStateChanged)
11693  *selectionStateChanged = mSelection != selectionBefore;
11694  }
11695 }
11696 /* end of 'src/plottable.cpp' */
11697 
11698 /* including file 'src/item.cpp', size 49271 */
11699 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
11700 
11704 
11727 /* start documentation of inline functions */
11728 
11740 /* end documentation of inline functions */
11741 
11749  QCPAbstractItem *parentItem,
11750  const QString &name,
11751  int anchorId)
11752  : mName(name),
11753  mParentPlot(parentPlot),
11754  mParentItem(parentItem),
11755  mAnchorId(anchorId) {}
11756 
11758  // unregister as parent at children:
11759  foreach (QCPItemPosition *child, mChildrenX.values()) {
11760  if (child->parentAnchorX() == this)
11761  child->setParentAnchorX(0); // this acts back on this anchor and
11762  // child removes itself from mChildrenX
11763  }
11764  foreach (QCPItemPosition *child, mChildrenY.values()) {
11765  if (child->parentAnchorY() == this)
11766  child->setParentAnchorY(0); // this acts back on this anchor and
11767  // child removes itself from mChildrenY
11768  }
11769 }
11770 
11780  if (mParentItem) {
11781  if (mAnchorId > -1) {
11783  } else {
11784  qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
11785  return QPointF();
11786  }
11787  } else {
11788  qDebug() << Q_FUNC_INFO << "no parent item set";
11789  return QPointF();
11790  }
11791 }
11792 
11802  if (!mChildrenX.contains(pos))
11803  mChildrenX.insert(pos);
11804  else
11805  qDebug() << Q_FUNC_INFO << "provided pos is child already"
11806  << reinterpret_cast<quintptr>(pos);
11807 }
11808 
11816  if (!mChildrenX.remove(pos))
11817  qDebug() << Q_FUNC_INFO << "provided pos isn't child"
11818  << reinterpret_cast<quintptr>(pos);
11819 }
11820 
11830  if (!mChildrenY.contains(pos))
11831  mChildrenY.insert(pos);
11832  else
11833  qDebug() << Q_FUNC_INFO << "provided pos is child already"
11834  << reinterpret_cast<quintptr>(pos);
11835 }
11836 
11844  if (!mChildrenY.remove(pos))
11845  qDebug() << Q_FUNC_INFO << "provided pos isn't child"
11846  << reinterpret_cast<quintptr>(pos);
11847 }
11848 
11852 
11893 /* start documentation of inline functions */
11894 
11917 /* end documentation of inline functions */
11918 
11926  QCPAbstractItem *parentItem,
11927  const QString &name)
11928  : QCPItemAnchor(parentPlot, parentItem, name),
11929  mPositionTypeX(ptAbsolute),
11930  mPositionTypeY(ptAbsolute),
11931  mKey(0),
11932  mValue(0),
11933  mParentAnchorX(0),
11934  mParentAnchorY(0) {}
11935 
11937  // unregister as parent at children:
11938  // Note: this is done in ~QCPItemAnchor again, but it's important
11939  // QCPItemPosition does it itself, because only then
11940  // the setParentAnchor(0) call the correct
11941  // QCPItemPosition::pixelPosition function instead of
11942  // QCPItemAnchor::pixelPosition
11943  foreach (QCPItemPosition *child, mChildrenX.values()) {
11944  if (child->parentAnchorX() == this)
11945  child->setParentAnchorX(0); // this acts back on this anchor and
11946  // child removes itself from mChildrenX
11947  }
11948  foreach (QCPItemPosition *child, mChildrenY.values()) {
11949  if (child->parentAnchorY() == this)
11950  child->setParentAnchorY(0); // this acts back on this anchor and
11951  // child removes itself from mChildrenY
11952  }
11953  // unregister as child in parent:
11956 }
11957 
11958 /* can't make this a header inline function, because QPointer breaks with
11959  * forward declared types, see QTBUG-29588 */
11961 
11992  setTypeX(type);
11993  setTypeY(type);
11994 }
11995 
12005  if (mPositionTypeX != type) {
12006  // if switching from or to coordinate type that isn't valid (e.g.
12007  // because axes or axis rect were deleted), don't try to recover the
12008  // pixelPosition() because it would output a qDebug warning.
12009  bool retainPixelPosition = true;
12010  if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) &&
12011  (!mKeyAxis || !mValueAxis))
12012  retainPixelPosition = false;
12014  (!mAxisRect))
12015  retainPixelPosition = false;
12016 
12017  QPointF pixel;
12018  if (retainPixelPosition) pixel = pixelPosition();
12019 
12020  mPositionTypeX = type;
12021 
12022  if (retainPixelPosition) setPixelPosition(pixel);
12023  }
12024 }
12025 
12035  if (mPositionTypeY != type) {
12036  // if switching from or to coordinate type that isn't valid (e.g.
12037  // because axes or axis rect were deleted), don't try to recover the
12038  // pixelPosition() because it would output a qDebug warning.
12039  bool retainPixelPosition = true;
12040  if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) &&
12041  (!mKeyAxis || !mValueAxis))
12042  retainPixelPosition = false;
12044  (!mAxisRect))
12045  retainPixelPosition = false;
12046 
12047  QPointF pixel;
12048  if (retainPixelPosition) pixel = pixelPosition();
12049 
12050  mPositionTypeY = type;
12051 
12052  if (retainPixelPosition) setPixelPosition(pixel);
12053  }
12054 }
12055 
12080  bool keepPixelPosition) {
12081  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
12082  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
12083  return successX && successY;
12084 }
12085 
12095  bool keepPixelPosition) {
12096  // make sure self is not assigned as parent:
12097  if (parentAnchor == this) {
12098  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor"
12099  << reinterpret_cast<quintptr>(parentAnchor);
12100  return false;
12101  }
12102  // make sure no recursive parent-child-relationships are created:
12103  QCPItemAnchor *currentParent = parentAnchor;
12104  while (currentParent) {
12105  if (QCPItemPosition *currentParentPos =
12106  currentParent->toQCPItemPosition()) {
12107  // is a QCPItemPosition, might have further parent, so keep
12108  // iterating
12109  if (currentParentPos == this) {
12110  qDebug() << Q_FUNC_INFO
12111  << "can't create recursive parent-child-relationship"
12112  << reinterpret_cast<quintptr>(parentAnchor);
12113  return false;
12114  }
12115  currentParent = currentParentPos->parentAnchorX();
12116  } else {
12117  // is a QCPItemAnchor, can't have further parent. Now make sure the
12118  // parent items aren't the same, to prevent a position being child
12119  // of an anchor which itself depends on the position, because
12120  // they're both on the same item:
12121  if (currentParent->mParentItem == mParentItem) {
12122  qDebug() << Q_FUNC_INFO
12123  << "can't set parent to be an anchor which itself "
12124  "depends on this position"
12125  << reinterpret_cast<quintptr>(parentAnchor);
12126  return false;
12127  }
12128  break;
12129  }
12130  }
12131 
12132  // if previously no parent set and PosType is still ptPlotCoords, set to
12133  // ptAbsolute:
12135 
12136  // save pixel position:
12137  QPointF pixelP;
12138  if (keepPixelPosition) pixelP = pixelPosition();
12139  // unregister at current parent anchor:
12141  // register at new parent anchor:
12142  if (parentAnchor) parentAnchor->addChildX(this);
12144  // restore pixel position under new parent:
12145  if (keepPixelPosition)
12146  setPixelPosition(pixelP);
12147  else
12148  setCoords(0, coords().y());
12149  return true;
12150 }
12151 
12161  bool keepPixelPosition) {
12162  // make sure self is not assigned as parent:
12163  if (parentAnchor == this) {
12164  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor"
12165  << reinterpret_cast<quintptr>(parentAnchor);
12166  return false;
12167  }
12168  // make sure no recursive parent-child-relationships are created:
12169  QCPItemAnchor *currentParent = parentAnchor;
12170  while (currentParent) {
12171  if (QCPItemPosition *currentParentPos =
12172  currentParent->toQCPItemPosition()) {
12173  // is a QCPItemPosition, might have further parent, so keep
12174  // iterating
12175  if (currentParentPos == this) {
12176  qDebug() << Q_FUNC_INFO
12177  << "can't create recursive parent-child-relationship"
12178  << reinterpret_cast<quintptr>(parentAnchor);
12179  return false;
12180  }
12181  currentParent = currentParentPos->parentAnchorY();
12182  } else {
12183  // is a QCPItemAnchor, can't have further parent. Now make sure the
12184  // parent items aren't the same, to prevent a position being child
12185  // of an anchor which itself depends on the position, because
12186  // they're both on the same item:
12187  if (currentParent->mParentItem == mParentItem) {
12188  qDebug() << Q_FUNC_INFO
12189  << "can't set parent to be an anchor which itself "
12190  "depends on this position"
12191  << reinterpret_cast<quintptr>(parentAnchor);
12192  return false;
12193  }
12194  break;
12195  }
12196  }
12197 
12198  // if previously no parent set and PosType is still ptPlotCoords, set to
12199  // ptAbsolute:
12201 
12202  // save pixel position:
12203  QPointF pixelP;
12204  if (keepPixelPosition) pixelP = pixelPosition();
12205  // unregister at current parent anchor:
12207  // register at new parent anchor:
12208  if (parentAnchor) parentAnchor->addChildY(this);
12210  // restore pixel position under new parent:
12211  if (keepPixelPosition)
12212  setPixelPosition(pixelP);
12213  else
12214  setCoords(coords().x(), 0);
12215  return true;
12216 }
12217 
12237 void QCPItemPosition::setCoords(double key, double value) {
12238  mKey = key;
12239  mValue = value;
12240 }
12241 
12248 void QCPItemPosition::setCoords(const QPointF &pos) {
12249  setCoords(pos.x(), pos.y());
12250 }
12251 
12260  QPointF result;
12261 
12262  // determine X:
12263  switch (mPositionTypeX) {
12264  case ptAbsolute: {
12265  result.rx() = mKey;
12266  if (mParentAnchorX)
12267  result.rx() += mParentAnchorX->pixelPosition().x();
12268  break;
12269  }
12270  case ptViewportRatio: {
12271  result.rx() = mKey * mParentPlot->viewport().width();
12272  if (mParentAnchorX)
12273  result.rx() += mParentAnchorX->pixelPosition().x();
12274  else
12275  result.rx() += mParentPlot->viewport().left();
12276  break;
12277  }
12278  case ptAxisRectRatio: {
12279  if (mAxisRect) {
12280  result.rx() = mKey * mAxisRect.data()->width();
12281  if (mParentAnchorX)
12282  result.rx() += mParentAnchorX->pixelPosition().x();
12283  else
12284  result.rx() += mAxisRect.data()->left();
12285  } else
12286  qDebug() << Q_FUNC_INFO
12287  << "Item position type x is ptAxisRectRatio, but no "
12288  "axis rect was defined";
12289  break;
12290  }
12291  case ptPlotCoords: {
12292  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
12293  result.rx() = mKeyAxis.data()->coordToPixel(mKey);
12294  else if (mValueAxis &&
12295  mValueAxis.data()->orientation() == Qt::Horizontal)
12296  result.rx() = mValueAxis.data()->coordToPixel(mValue);
12297  else
12298  qDebug() << Q_FUNC_INFO
12299  << "Item position type x is ptPlotCoords, but no axes "
12300  "were defined";
12301  break;
12302  }
12303  }
12304 
12305  // determine Y:
12306  switch (mPositionTypeY) {
12307  case ptAbsolute: {
12308  result.ry() = mValue;
12309  if (mParentAnchorY)
12310  result.ry() += mParentAnchorY->pixelPosition().y();
12311  break;
12312  }
12313  case ptViewportRatio: {
12314  result.ry() = mValue * mParentPlot->viewport().height();
12315  if (mParentAnchorY)
12316  result.ry() += mParentAnchorY->pixelPosition().y();
12317  else
12318  result.ry() += mParentPlot->viewport().top();
12319  break;
12320  }
12321  case ptAxisRectRatio: {
12322  if (mAxisRect) {
12323  result.ry() = mValue * mAxisRect.data()->height();
12324  if (mParentAnchorY)
12325  result.ry() += mParentAnchorY->pixelPosition().y();
12326  else
12327  result.ry() += mAxisRect.data()->top();
12328  } else
12329  qDebug() << Q_FUNC_INFO
12330  << "Item position type y is ptAxisRectRatio, but no "
12331  "axis rect was defined";
12332  break;
12333  }
12334  case ptPlotCoords: {
12335  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
12336  result.ry() = mKeyAxis.data()->coordToPixel(mKey);
12337  else if (mValueAxis &&
12338  mValueAxis.data()->orientation() == Qt::Vertical)
12339  result.ry() = mValueAxis.data()->coordToPixel(mValue);
12340  else
12341  qDebug() << Q_FUNC_INFO
12342  << "Item position type y is ptPlotCoords, but no axes "
12343  "were defined";
12344  break;
12345  }
12346  }
12347 
12348  return result;
12349 }
12350 
12356 void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis) {
12357  mKeyAxis = keyAxis;
12359 }
12360 
12367  mAxisRect = axisRect;
12368 }
12369 
12381 void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition) {
12382  double x = pixelPosition.x();
12383  double y = pixelPosition.y();
12384 
12385  switch (mPositionTypeX) {
12386  case ptAbsolute: {
12388  break;
12389  }
12390  case ptViewportRatio: {
12391  if (mParentAnchorX)
12392  x -= mParentAnchorX->pixelPosition().x();
12393  else
12394  x -= mParentPlot->viewport().left();
12395  x /= (double)mParentPlot->viewport().width();
12396  break;
12397  }
12398  case ptAxisRectRatio: {
12399  if (mAxisRect) {
12400  if (mParentAnchorX)
12401  x -= mParentAnchorX->pixelPosition().x();
12402  else
12403  x -= mAxisRect.data()->left();
12404  x /= (double)mAxisRect.data()->width();
12405  } else
12406  qDebug() << Q_FUNC_INFO
12407  << "Item position type x is ptAxisRectRatio, but no "
12408  "axis rect was defined";
12409  break;
12410  }
12411  case ptPlotCoords: {
12412  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
12413  x = mKeyAxis.data()->pixelToCoord(x);
12414  else if (mValueAxis &&
12415  mValueAxis.data()->orientation() == Qt::Horizontal)
12416  y = mValueAxis.data()->pixelToCoord(x);
12417  else
12418  qDebug() << Q_FUNC_INFO
12419  << "Item position type x is ptPlotCoords, but no axes "
12420  "were defined";
12421  break;
12422  }
12423  }
12424 
12425  switch (mPositionTypeY) {
12426  case ptAbsolute: {
12428  break;
12429  }
12430  case ptViewportRatio: {
12431  if (mParentAnchorY)
12432  y -= mParentAnchorY->pixelPosition().y();
12433  else
12434  y -= mParentPlot->viewport().top();
12435  y /= (double)mParentPlot->viewport().height();
12436  break;
12437  }
12438  case ptAxisRectRatio: {
12439  if (mAxisRect) {
12440  if (mParentAnchorY)
12441  y -= mParentAnchorY->pixelPosition().y();
12442  else
12443  y -= mAxisRect.data()->top();
12444  y /= (double)mAxisRect.data()->height();
12445  } else
12446  qDebug() << Q_FUNC_INFO
12447  << "Item position type y is ptAxisRectRatio, but no "
12448  "axis rect was defined";
12449  break;
12450  }
12451  case ptPlotCoords: {
12452  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
12453  x = mKeyAxis.data()->pixelToCoord(y);
12454  else if (mValueAxis &&
12455  mValueAxis.data()->orientation() == Qt::Vertical)
12456  y = mValueAxis.data()->pixelToCoord(y);
12457  else
12458  qDebug() << Q_FUNC_INFO
12459  << "Item position type y is ptPlotCoords, but no axes "
12460  "were defined";
12461  break;
12462  }
12463  }
12464 
12465  setCoords(x, y);
12466 }
12467 
12471 
12613 /* start of documentation of inline functions */
12614 
12631 /* end of documentation of inline functions */
12632 /* start documentation of pure virtual functions */
12633 
12644 /* end documentation of pure virtual functions */
12645 /* start documentation of signals */
12646 
12652 /* end documentation of signals */
12653 
12658  : QCPLayerable(parentPlot),
12659  mClipToAxisRect(false),
12660  mSelectable(true),
12661  mSelected(false) {
12662  parentPlot->registerItem(this);
12663 
12664  QList<QCPAxisRect *> rects = parentPlot->axisRects();
12665  if (rects.size() > 0) {
12666  setClipToAxisRect(true);
12667  setClipAxisRect(rects.first());
12668  }
12669 }
12670 
12672  // don't delete mPositions because every position is also an anchor and thus
12673  // in mAnchors
12674  qDeleteAll(mAnchors);
12675 }
12676 
12677 /* can't make this a header inline function, because QPointer breaks with
12678  * forward declared types, see QTBUG-29588 */
12680  return mClipAxisRect.data();
12681 }
12682 
12691  mClipToAxisRect = clip;
12693 }
12694 
12702  mClipAxisRect = rect;
12704 }
12705 
12716 void QCPAbstractItem::setSelectable(bool selectable) {
12717  if (mSelectable != selectable) {
12720  }
12721 }
12722 
12740 void QCPAbstractItem::setSelected(bool selected) {
12741  if (mSelected != selected) {
12742  mSelected = selected;
12744  }
12745 }
12746 
12758  for (int i = 0; i < mPositions.size(); ++i) {
12759  if (mPositions.at(i)->name() == name) return mPositions.at(i);
12760  }
12761  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
12762  return 0;
12763 }
12764 
12776  for (int i = 0; i < mAnchors.size(); ++i) {
12777  if (mAnchors.at(i)->name() == name) return mAnchors.at(i);
12778  }
12779  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
12780  return 0;
12781 }
12782 
12792 bool QCPAbstractItem::hasAnchor(const QString &name) const {
12793  for (int i = 0; i < mAnchors.size(); ++i) {
12794  if (mAnchors.at(i)->name() == name) return true;
12795  }
12796  return false;
12797 }
12798 
12812  return mClipAxisRect.data()->rect();
12813  else
12814  return mParentPlot->viewport();
12815 }
12816 
12833 }
12834 
12849 double QCPAbstractItem::rectDistance(const QRectF &rect,
12850  const QPointF &pos,
12851  bool filledRect) const {
12852  double result = -1;
12853 
12854  // distance to border:
12855  QList<QLineF> lines;
12856  lines << QLineF(rect.topLeft(), rect.topRight())
12857  << QLineF(rect.bottomLeft(), rect.bottomRight())
12858  << QLineF(rect.topLeft(), rect.bottomLeft())
12859  << QLineF(rect.topRight(), rect.bottomRight());
12860  double minDistSqr = (std::numeric_limits<double>::max)();
12861  for (int i = 0; i < lines.size(); ++i) {
12862  double distSqr = QCPVector2D(pos).distanceSquaredToLine(
12863  lines.at(i).p1(), lines.at(i).p2());
12864  if (distSqr < minDistSqr) minDistSqr = distSqr;
12865  }
12866  result = qSqrt(minDistSqr);
12867 
12868  // filled rect, allow click inside to count as hit:
12869  if (filledRect && result > mParentPlot->selectionTolerance() * 0.99) {
12870  if (rect.contains(pos))
12872  }
12873  return result;
12874 }
12875 
12888 QPointF QCPAbstractItem::anchorPixelPosition(int anchorId) const {
12889  qDebug() << Q_FUNC_INFO
12890  << "called on item which shouldn't have any anchors (this method "
12891  "not reimplemented). anchorId"
12892  << anchorId;
12893  return QPointF();
12894 }
12895 
12913  if (hasAnchor(name))
12914  qDebug() << Q_FUNC_INFO
12915  << "anchor/position with name exists already:" << name;
12916  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
12917  mPositions.append(newPosition);
12918  mAnchors.append(newPosition); // every position is also an anchor
12919  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
12920  newPosition->setType(QCPItemPosition::ptPlotCoords);
12921  if (mParentPlot->axisRect())
12922  newPosition->setAxisRect(mParentPlot->axisRect());
12923  newPosition->setCoords(0, 0);
12924  return newPosition;
12925 }
12926 
12950  int anchorId) {
12951  if (hasAnchor(name))
12952  qDebug() << Q_FUNC_INFO
12953  << "anchor/position with name exists already:" << name;
12954  QCPItemAnchor *newAnchor =
12955  new QCPItemAnchor(mParentPlot, this, name, anchorId);
12956  mAnchors.append(newAnchor);
12957  return newAnchor;
12958 }
12959 
12960 /* inherits documentation from base class */
12962  bool additive,
12963  const QVariant &details,
12964  bool *selectionStateChanged) {
12965  Q_UNUSED(event)
12966  Q_UNUSED(details)
12967  if (mSelectable) {
12968  bool selBefore = mSelected;
12969  setSelected(additive ? !mSelected : true);
12970  if (selectionStateChanged)
12971  *selectionStateChanged = mSelected != selBefore;
12972  }
12973 }
12974 
12975 /* inherits documentation from base class */
12976 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged) {
12977  if (mSelectable) {
12978  bool selBefore = mSelected;
12979  setSelected(false);
12980  if (selectionStateChanged)
12981  *selectionStateChanged = mSelected != selBefore;
12982  }
12983 }
12984 
12985 /* inherits documentation from base class */
12987  return QCP::iSelectItems;
12988 }
12989 /* end of 'src/item.cpp' */
12990 
12991 /* including file 'src/core.cpp', size 126207 */
12992 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
12993 
12997 
13007 /* start of documentation of inline functions */
13008 
13025 /* end of documentation of inline functions */
13026 /* start of documentation of signals */
13027 
13219 /* end of documentation of signals */
13220 /* start of documentation of public members */
13221 
13329 /* end of documentation of public members */
13330 
13335  : QWidget(parent),
13336  xAxis(0),
13337  yAxis(0),
13338  xAxis2(0),
13339  yAxis2(0),
13340  legend(0),
13341  mBufferDevicePixelRatio(1.0), // will be adapted to primary screen below
13342  mPlotLayout(0),
13343  mAutoAddPlottableToLegend(true),
13344  mAntialiasedElements(QCP::aeNone),
13345  mNotAntialiasedElements(QCP::aeNone),
13346  mInteractions(0),
13347  mSelectionTolerance(8),
13348  mNoAntialiasingOnDrag(false),
13349  mBackgroundBrush(Qt::white, Qt::SolidPattern),
13350  mBackgroundScaled(true),
13351  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
13352  mCurrentLayer(0),
13353  mPlottingHints(QCP::phCacheLabels | QCP::phImmediateRefresh),
13354  mMultiSelectModifier(Qt::ControlModifier),
13355  mSelectionRectMode(QCP::srmNone),
13356  mSelectionRect(0),
13357  mOpenGl(false),
13358  mMouseHasMoved(false),
13359  mMouseEventLayerable(0),
13360  mMouseSignalLayerable(0),
13361  mReplotting(false),
13362  mReplotQueued(false),
13363  mOpenGlMultisamples(16),
13364  mOpenGlAntialiasedElementsBackup(QCP::aeNone),
13365  mOpenGlCacheLabelsBackup(true) {
13366  setAttribute(Qt::WA_NoMousePropagation);
13367  setAttribute(Qt::WA_OpaquePaintEvent);
13368  setFocusPolicy(Qt::ClickFocus);
13369  setMouseTracking(true);
13370  QLocale currentLocale = locale();
13371  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
13372  setLocale(currentLocale);
13373 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
13374 #ifdef QCP_DEVICEPIXELRATIO_FLOAT
13375  setBufferDevicePixelRatio(QWidget::devicePixelRatioF());
13376 #else
13377  setBufferDevicePixelRatio(QWidget::devicePixelRatio());
13378 #endif
13379 #endif
13380 
13383  // create initial layers:
13384  mLayers.append(new QCPLayer(this, QLatin1String("background")));
13385  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
13386  mLayers.append(new QCPLayer(this, QLatin1String("main")));
13387  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
13388  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
13389  mLayers.append(new QCPLayer(this, QLatin1String("overlay")));
13391  setCurrentLayer(QLatin1String("main"));
13392  layer(QLatin1String("overlay"))->setMode(QCPLayer::lmBuffered);
13393 
13394  // create initial layout, axis rect and legend:
13395  mPlotLayout = new QCPLayoutGrid;
13397  mPlotLayout->setParent(this); // important because if parent is QWidget,
13398  // QCPLayout::sizeConstraintsChanged will
13399  // call QWidget::updateGeometry
13400  mPlotLayout->setLayer(QLatin1String("main"));
13401  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
13402  mPlotLayout->addElement(0, 0, defaultAxisRect);
13403  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
13404  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
13405  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
13406  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
13407  legend = new QCPLegend;
13408  legend->setVisible(false);
13409  defaultAxisRect->insetLayout()->addElement(legend,
13410  Qt::AlignRight | Qt::AlignTop);
13411  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
13412 
13413  defaultAxisRect->setLayer(QLatin1String("background"));
13414  xAxis->setLayer(QLatin1String("axes"));
13415  yAxis->setLayer(QLatin1String("axes"));
13416  xAxis2->setLayer(QLatin1String("axes"));
13417  yAxis2->setLayer(QLatin1String("axes"));
13418  xAxis->grid()->setLayer(QLatin1String("grid"));
13419  yAxis->grid()->setLayer(QLatin1String("grid"));
13420  xAxis2->grid()->setLayer(QLatin1String("grid"));
13421  yAxis2->grid()->setLayer(QLatin1String("grid"));
13422  legend->setLayer(QLatin1String("legend"));
13423 
13424  // create selection rect instance:
13425  mSelectionRect = new QCPSelectionRect(this);
13426  mSelectionRect->setLayer(QLatin1String("overlay"));
13427 
13428  setViewport(
13429  rect()); // needs to be called after mPlotLayout has been created
13430 
13432 }
13433 
13435  clearPlottables();
13436  clearItems();
13437 
13438  if (mPlotLayout) {
13439  delete mPlotLayout;
13440  mPlotLayout = 0;
13441  }
13442 
13443  mCurrentLayer = 0;
13444  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the
13445  // last layer to be removed
13446  mLayers.clear();
13447 }
13448 
13469  const QCP::AntialiasedElements &antialiasedElements) {
13471 
13472  // make sure elements aren't in mNotAntialiasedElements and
13473  // mAntialiasedElements simultaneously:
13476 }
13477 
13487  QCP::AntialiasedElement antialiasedElement, bool enabled) {
13488  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
13489  mAntialiasedElements &= ~antialiasedElement;
13490  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
13491  mAntialiasedElements |= antialiasedElement;
13492 
13493  // make sure elements aren't in mNotAntialiasedElements and
13494  // mAntialiasedElements simultaneously:
13497 }
13498 
13519  const QCP::AntialiasedElements &notAntialiasedElements) {
13521 
13522  // make sure elements aren't in mNotAntialiasedElements and
13523  // mAntialiasedElements simultaneously:
13526 }
13527 
13537  QCP::AntialiasedElement notAntialiasedElement, bool enabled) {
13538  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
13539  mNotAntialiasedElements &= ~notAntialiasedElement;
13540  else if (enabled &&
13541  !mNotAntialiasedElements.testFlag(notAntialiasedElement))
13542  mNotAntialiasedElements |= notAntialiasedElement;
13543 
13544  // make sure elements aren't in mNotAntialiasedElements and
13545  // mAntialiasedElements simultaneously:
13548 }
13549 
13558 }
13559 
13623 void QCustomPlot::setInteractions(const QCP::Interactions &interactions) {
13625 }
13626 
13635  bool enabled) {
13636  if (!enabled && mInteractions.testFlag(interaction))
13637  mInteractions &= ~interaction;
13638  else if (enabled && !mInteractions.testFlag(interaction))
13639  mInteractions |= interaction;
13640 }
13641 
13657  mSelectionTolerance = pixels;
13658 }
13659 
13671  mNoAntialiasingOnDrag = enabled;
13672 }
13673 
13680 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints) {
13681  mPlottingHints = hints;
13682 }
13683 
13690  QCP::PlottingHints newHints = mPlottingHints;
13691  if (!enabled)
13692  newHints &= ~hint;
13693  else
13694  newHints |= hint;
13695 
13696  if (newHints != mPlottingHints) setPlottingHints(newHints);
13697 }
13698 
13710 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier) {
13711  mMultiSelectModifier = modifier;
13712 }
13713 
13740  if (mSelectionRect) {
13741  if (mode == QCP::srmNone)
13743  ->cancel(); // when switching to none, we immediately want
13744  // to abort a potentially active selection rect
13745 
13746  // disconnect old connections:
13748  disconnect(mSelectionRect, SIGNAL(accepted(QRect, QMouseEvent *)),
13749  this, SLOT(processRectSelection(QRect, QMouseEvent *)));
13750  else if (mSelectionRectMode == QCP::srmZoom)
13751  disconnect(mSelectionRect, SIGNAL(accepted(QRect, QMouseEvent *)),
13752  this, SLOT(processRectZoom(QRect, QMouseEvent *)));
13753 
13754  // establish new ones:
13755  if (mode == QCP::srmSelect)
13756  connect(mSelectionRect, SIGNAL(accepted(QRect, QMouseEvent *)),
13757  this, SLOT(processRectSelection(QRect, QMouseEvent *)));
13758  else if (mode == QCP::srmZoom)
13759  connect(mSelectionRect, SIGNAL(accepted(QRect, QMouseEvent *)),
13760  this, SLOT(processRectZoom(QRect, QMouseEvent *)));
13761  }
13762 
13763  mSelectionRectMode = mode;
13764 }
13765 
13779  if (mSelectionRect) delete mSelectionRect;
13780 
13782 
13783  if (mSelectionRect) {
13784  // establish connections with new selection rect:
13786  connect(mSelectionRect, SIGNAL(accepted(QRect, QMouseEvent *)),
13787  this, SLOT(processRectSelection(QRect, QMouseEvent *)));
13788  else if (mSelectionRectMode == QCP::srmZoom)
13789  connect(mSelectionRect, SIGNAL(accepted(QRect, QMouseEvent *)),
13790  this, SLOT(processRectZoom(QRect, QMouseEvent *)));
13791  }
13792 }
13793 
13836 void QCustomPlot::setOpenGl(bool enabled, int multisampling) {
13837  mOpenGlMultisamples = qMax(0, multisampling);
13838 #ifdef QCUSTOMPLOT_USE_OPENGL
13839  mOpenGl = enabled;
13840  if (mOpenGl) {
13841  if (setupOpenGl()) {
13842  // backup antialiasing override and labelcaching setting so we can
13843  // restore upon disabling OpenGL
13847  // set antialiasing override to antialias all (aligns gl pixel grid
13848  // properly), and disable label caching (would use software
13849  // rasterizer for pixmap caches):
13852  } else {
13853  qDebug() << Q_FUNC_INFO
13854  << "Failed to enable OpenGL, continuing plotting without "
13855  "hardware acceleration.";
13856  mOpenGl = false;
13857  }
13858  } else {
13859  // restore antialiasing override and labelcaching to what it was before
13860  // enabling OpenGL, if nobody changed it in the meantime:
13863  if (!mPlottingHints.testFlag(QCP::phCacheLabels))
13865  freeOpenGl();
13866  }
13867  // recreate all paint buffers:
13868  mPaintBuffers.clear();
13870 #else
13871  Q_UNUSED(enabled)
13872  qDebug() << Q_FUNC_INFO
13873  << "QCustomPlot can't use OpenGL because QCUSTOMPLOT_USE_OPENGL "
13874  "was not defined during compilation (add 'DEFINES += "
13875  "QCUSTOMPLOT_USE_OPENGL' to your qmake .pro file)";
13876 #endif
13877 }
13878 
13897 void QCustomPlot::setViewport(const QRect &rect) {
13898  mViewport = rect;
13900 }
13901 
13916  if (!qFuzzyCompare(ratio, mBufferDevicePixelRatio)) {
13917 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
13918  mBufferDevicePixelRatio = ratio;
13919  for (int i = 0; i < mPaintBuffers.size(); ++i)
13920  mPaintBuffers.at(i)->setDevicePixelRatio(mBufferDevicePixelRatio);
13921  // Note: axis label cache has devicePixelRatio as part of cache
13922  // hash, so no need to manually clear cache here
13923 #else
13924  qDebug() << Q_FUNC_INFO
13925  << "Device pixel ratios not supported for Qt versions before "
13926  "5.4";
13928 #endif
13929  }
13930 }
13931 
13949 void QCustomPlot::setBackground(const QPixmap &pm) {
13950  mBackgroundPixmap = pm;
13951  mScaledBackgroundPixmap = QPixmap();
13952 }
13953 
13968 void QCustomPlot::setBackground(const QBrush &brush) {
13969  mBackgroundBrush = brush;
13970 }
13971 
13980 void QCustomPlot::setBackground(const QPixmap &pm,
13981  bool scaled,
13982  Qt::AspectRatioMode mode) {
13983  mBackgroundPixmap = pm;
13984  mScaledBackgroundPixmap = QPixmap();
13985  mBackgroundScaled = scaled;
13986  mBackgroundScaledMode = mode;
13987 }
13988 
14001  mBackgroundScaled = scaled;
14002 }
14003 
14011 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode) {
14012  mBackgroundScaledMode = mode;
14013 }
14014 
14024  if (index >= 0 && index < mPlottables.size()) {
14025  return mPlottables.at(index);
14026  } else {
14027  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
14028  return 0;
14029  }
14030 }
14031 
14040  if (!mPlottables.isEmpty()) {
14041  return mPlottables.last();
14042  } else
14043  return 0;
14044 }
14045 
14056  if (!mPlottables.contains(plottable)) {
14057  qDebug() << Q_FUNC_INFO << "plottable not in list:"
14058  << reinterpret_cast<quintptr>(plottable);
14059  return false;
14060  }
14061 
14062  // remove plottable from legend:
14064  // special handling for QCPGraphs to maintain the simple graph interface:
14065  if (QCPGraph *graph = qobject_cast<QCPGraph *>(plottable))
14066  mGraphs.removeOne(graph);
14067  // remove plottable:
14068  delete plottable;
14069  mPlottables.removeOne(plottable);
14070  return true;
14071 }
14072 
14078  if (index >= 0 && index < mPlottables.size())
14079  return removePlottable(mPlottables[index]);
14080  else {
14081  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
14082  return false;
14083  }
14084 }
14085 
14095  int c = mPlottables.size();
14096  for (int i = c - 1; i >= 0; --i) removePlottable(mPlottables[i]);
14097  return c;
14098 }
14099 
14105 int QCustomPlot::plottableCount() const { return mPlottables.size(); }
14106 
14117 QList<QCPAbstractPlottable *> QCustomPlot::selectedPlottables() const {
14118  QList<QCPAbstractPlottable *> result;
14120  if (plottable->selected()) result.append(plottable);
14121  }
14122  return result;
14123 }
14124 
14139  bool onlySelectable) const {
14140  QCPAbstractPlottable *resultPlottable = 0;
14141  double resultDistance =
14142  mSelectionTolerance; // only regard clicks with distances smaller
14143  // than mSelectionTolerance as selections, so
14144  // initialize with that value
14145 
14147  if (onlySelectable &&
14148  !plottable->selectable()) // we could have also passed
14149  // onlySelectable to the selectTest
14150  // function, but checking here is faster,
14151  // because we have access to
14152  // QCPabstractPlottable::selectable
14153  continue;
14154  if ((plottable->keyAxis()->axisRect()->rect() &
14155  plottable->valueAxis()->axisRect()->rect())
14156  .contains(pos.toPoint())) // only consider clicks inside
14157  // the rect that is spanned by
14158  // the plottable's key/value axes
14159  {
14160  double currentDistance = plottable->selectTest(pos, false);
14161  if (currentDistance >= 0 && currentDistance < resultDistance) {
14162  resultPlottable = plottable;
14163  resultDistance = currentDistance;
14164  }
14165  }
14166  }
14167 
14168  return resultPlottable;
14169 }
14170 
14175  return mPlottables.contains(plottable);
14176 }
14177 
14186 QCPGraph *QCustomPlot::graph(int index) const {
14187  if (index >= 0 && index < mGraphs.size()) {
14188  return mGraphs.at(index);
14189  } else {
14190  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
14191  return 0;
14192  }
14193 }
14194 
14203  if (!mGraphs.isEmpty()) {
14204  return mGraphs.last();
14205  } else
14206  return 0;
14207 }
14208 
14223  if (!keyAxis) keyAxis = xAxis;
14224  if (!valueAxis) valueAxis = yAxis;
14225  if (!keyAxis || !valueAxis) {
14226  qDebug() << Q_FUNC_INFO
14227  << "can't use default QCustomPlot xAxis or yAxis, because at "
14228  "least one is invalid (has been deleted)";
14229  return 0;
14230  }
14231  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this) {
14232  qDebug() << Q_FUNC_INFO
14233  << "passed keyAxis or valueAxis doesn't have this QCustomPlot "
14234  "as parent";
14235  return 0;
14236  }
14237 
14238  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
14239  newGraph->setName(QLatin1String("Graph ") +
14240  QString::number(mGraphs.size()));
14241  return newGraph;
14242 }
14243 
14256  return removePlottable(graph);
14257 }
14258 
14263 bool QCustomPlot::removeGraph(int index) {
14264  if (index >= 0 && index < mGraphs.size())
14265  return removeGraph(mGraphs[index]);
14266  else
14267  return false;
14268 }
14269 
14279  int c = mGraphs.size();
14280  for (int i = c - 1; i >= 0; --i) removeGraph(mGraphs[i]);
14281  return c;
14282 }
14283 
14289 int QCustomPlot::graphCount() const { return mGraphs.size(); }
14290 
14301 QList<QCPGraph *> QCustomPlot::selectedGraphs() const {
14302  QList<QCPGraph *> result;
14303  foreach (QCPGraph *graph, mGraphs) {
14304  if (graph->selected()) result.append(graph);
14305  }
14306  return result;
14307 }
14308 
14318  if (index >= 0 && index < mItems.size()) {
14319  return mItems.at(index);
14320  } else {
14321  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
14322  return 0;
14323  }
14324 }
14325 
14334  if (!mItems.isEmpty()) {
14335  return mItems.last();
14336  } else
14337  return 0;
14338 }
14339 
14348  if (mItems.contains(item)) {
14349  delete item;
14350  mItems.removeOne(item);
14351  return true;
14352  } else {
14353  qDebug() << Q_FUNC_INFO
14354  << "item not in list:" << reinterpret_cast<quintptr>(item);
14355  return false;
14356  }
14357 }
14358 
14363 bool QCustomPlot::removeItem(int index) {
14364  if (index >= 0 && index < mItems.size())
14365  return removeItem(mItems[index]);
14366  else {
14367  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
14368  return false;
14369  }
14370 }
14371 
14380  int c = mItems.size();
14381  for (int i = c - 1; i >= 0; --i) removeItem(mItems[i]);
14382  return c;
14383 }
14384 
14390 int QCustomPlot::itemCount() const { return mItems.size(); }
14391 
14399 QList<QCPAbstractItem *> QCustomPlot::selectedItems() const {
14400  QList<QCPAbstractItem *> result;
14401  foreach (QCPAbstractItem *item, mItems) {
14402  if (item->selected()) result.append(item);
14403  }
14404  return result;
14405 }
14406 
14421  bool onlySelectable) const {
14422  QCPAbstractItem *resultItem = 0;
14423  double resultDistance =
14424  mSelectionTolerance; // only regard clicks with distances smaller
14425  // than mSelectionTolerance as selections, so
14426  // initialize with that value
14427 
14428  foreach (QCPAbstractItem *item, mItems) {
14429  if (onlySelectable &&
14430  !item->selectable()) // we could have also passed onlySelectable to
14431  // the selectTest function, but checking here
14432  // is faster, because we have access to
14433  // QCPAbstractItem::selectable
14434  continue;
14435  if (!item->clipToAxisRect() ||
14436  item->clipRect().contains(
14437  pos.toPoint())) // only consider clicks inside axis
14438  // cliprect of the item if actually clipped
14439  // to it
14440  {
14441  double currentDistance = item->selectTest(pos, false);
14442  if (currentDistance >= 0 && currentDistance < resultDistance) {
14443  resultItem = item;
14444  resultDistance = currentDistance;
14445  }
14446  }
14447  }
14448 
14449  return resultItem;
14450 }
14451 
14458  return mItems.contains(item);
14459 }
14460 
14469 QCPLayer *QCustomPlot::layer(const QString &name) const {
14470  foreach (QCPLayer *layer, mLayers) {
14471  if (layer->name() == name) return layer;
14472  }
14473  return 0;
14474 }
14475 
14482 QCPLayer *QCustomPlot::layer(int index) const {
14483  if (index >= 0 && index < mLayers.size()) {
14484  return mLayers.at(index);
14485  } else {
14486  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
14487  return 0;
14488  }
14489 }
14490 
14495 
14508 bool QCustomPlot::setCurrentLayer(const QString &name) {
14509  if (QCPLayer *newCurrentLayer = layer(name)) {
14510  return setCurrentLayer(newCurrentLayer);
14511  } else {
14512  qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
14513  return false;
14514  }
14515 }
14516 
14527  if (!mLayers.contains(layer)) {
14528  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:"
14529  << reinterpret_cast<quintptr>(layer);
14530  return false;
14531  }
14532 
14533  mCurrentLayer = layer;
14534  return true;
14535 }
14536 
14542 int QCustomPlot::layerCount() const { return mLayers.size(); }
14543 
14559 bool QCustomPlot::addLayer(const QString &name,
14560  QCPLayer *otherLayer,
14561  QCustomPlot::LayerInsertMode insertMode) {
14562  if (!otherLayer) otherLayer = mLayers.last();
14563  if (!mLayers.contains(otherLayer)) {
14564  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:"
14565  << reinterpret_cast<quintptr>(otherLayer);
14566  return false;
14567  }
14568  if (layer(name)) {
14569  qDebug() << Q_FUNC_INFO << "A layer exists already with the name"
14570  << name;
14571  return false;
14572  }
14573 
14574  QCPLayer *newLayer = new QCPLayer(this, name);
14575  mLayers.insert(otherLayer->index() + (insertMode == limAbove ? 1 : 0),
14576  newLayer);
14578  setupPaintBuffers(); // associates new layer with the appropriate paint
14579  // buffer
14580  return true;
14581 }
14582 
14599  if (!mLayers.contains(layer)) {
14600  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:"
14601  << reinterpret_cast<quintptr>(layer);
14602  return false;
14603  }
14604  if (mLayers.size() < 2) {
14605  qDebug() << Q_FUNC_INFO << "can't remove last layer";
14606  return false;
14607  }
14608 
14609  // append all children of this layer to layer below (if this is lowest
14610  // layer, prepend to layer above)
14611  int removedIndex = layer->index();
14612  bool isFirstLayer = removedIndex == 0;
14613  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex + 1)
14614  : mLayers.at(removedIndex - 1);
14615  QList<QCPLayerable *> children = layer->children();
14616  if (isFirstLayer) // prepend in reverse order (so order relative to each
14617  // other stays the same)
14618  {
14619  for (int i = children.size() - 1; i >= 0; --i)
14620  children.at(i)->moveToLayer(targetLayer, true);
14621  } else // append normally
14622  {
14623  for (int i = 0; i < children.size(); ++i)
14624  children.at(i)->moveToLayer(targetLayer, false);
14625  }
14626  // if removed layer is current layer, change current layer to layer
14627  // below/above:
14628  if (layer == mCurrentLayer) setCurrentLayer(targetLayer);
14629  // invalidate the paint buffer that was responsible for this layer:
14630  if (!layer->mPaintBuffer.isNull())
14631  layer->mPaintBuffer.toStrongRef()->setInvalidated();
14632  // remove layer:
14633  delete layer;
14634  mLayers.removeOne(layer);
14636  return true;
14637 }
14638 
14649  QCPLayer *otherLayer,
14650  QCustomPlot::LayerInsertMode insertMode) {
14651  if (!mLayers.contains(layer)) {
14652  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:"
14653  << reinterpret_cast<quintptr>(layer);
14654  return false;
14655  }
14656  if (!mLayers.contains(otherLayer)) {
14657  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:"
14658  << reinterpret_cast<quintptr>(otherLayer);
14659  return false;
14660  }
14661 
14662  if (layer->index() > otherLayer->index())
14663  mLayers.move(layer->index(),
14664  otherLayer->index() + (insertMode == limAbove ? 1 : 0));
14665  else if (layer->index() < otherLayer->index())
14666  mLayers.move(layer->index(),
14667  otherLayer->index() + (insertMode == limAbove ? 0 : -1));
14668 
14669  // invalidate the paint buffers that are responsible for the layers:
14670  if (!layer->mPaintBuffer.isNull())
14671  layer->mPaintBuffer.toStrongRef()->setInvalidated();
14672  if (!otherLayer->mPaintBuffer.isNull())
14673  otherLayer->mPaintBuffer.toStrongRef()->setInvalidated();
14674 
14676  return true;
14677 }
14678 
14688 int QCustomPlot::axisRectCount() const { return axisRects().size(); }
14689 
14713  const QList<QCPAxisRect *> rectList = axisRects();
14714  if (index >= 0 && index < rectList.size()) {
14715  return rectList.at(index);
14716  } else {
14717  qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
14718  return 0;
14719  }
14720 }
14721 
14734 QList<QCPAxisRect *> QCustomPlot::axisRects() const {
14735  QList<QCPAxisRect *> result;
14736  QStack<QCPLayoutElement *> elementStack;
14737  if (mPlotLayout) elementStack.push(mPlotLayout);
14738 
14739  while (!elementStack.isEmpty()) {
14740  foreach (QCPLayoutElement *element,
14741  elementStack.pop()->elements(false)) {
14742  if (element) {
14743  elementStack.push(element);
14744  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect *>(element))
14745  result.append(ar);
14746  }
14747  }
14748  }
14749 
14750  return result;
14751 }
14752 
14764  QCPLayoutElement *currentElement = mPlotLayout;
14765  bool searchSubElements = true;
14766  while (searchSubElements && currentElement) {
14767  searchSubElements = false;
14768  foreach (QCPLayoutElement *subElement,
14769  currentElement->elements(false)) {
14770  if (subElement && subElement->realVisibility() &&
14771  subElement->selectTest(pos, false) >= 0) {
14772  currentElement = subElement;
14773  searchSubElements = true;
14774  break;
14775  }
14776  }
14777  }
14778  return currentElement;
14779 }
14780 
14793 QCPAxisRect *QCustomPlot::axisRectAt(const QPointF &pos) const {
14794  QCPAxisRect *result = 0;
14795  QCPLayoutElement *currentElement = mPlotLayout;
14796  bool searchSubElements = true;
14797  while (searchSubElements && currentElement) {
14798  searchSubElements = false;
14799  foreach (QCPLayoutElement *subElement,
14800  currentElement->elements(false)) {
14801  if (subElement && subElement->realVisibility() &&
14802  subElement->selectTest(pos, false) >= 0) {
14803  currentElement = subElement;
14804  searchSubElements = true;
14805  if (QCPAxisRect *ar =
14806  qobject_cast<QCPAxisRect *>(currentElement))
14807  result = ar;
14808  break;
14809  }
14810  }
14811  }
14812  return result;
14813 }
14814 
14822 QList<QCPAxis *> QCustomPlot::selectedAxes() const {
14823  QList<QCPAxis *> result, allAxes;
14824  foreach (QCPAxisRect *rect, axisRects()) allAxes << rect->axes();
14825 
14826  foreach (QCPAxis *axis, allAxes) {
14827  if (axis->selectedParts() != QCPAxis::spNone) result.append(axis);
14828  }
14829 
14830  return result;
14831 }
14832 
14841 QList<QCPLegend *> QCustomPlot::selectedLegends() const {
14842  QList<QCPLegend *> result;
14843 
14844  QStack<QCPLayoutElement *> elementStack;
14845  if (mPlotLayout) elementStack.push(mPlotLayout);
14846 
14847  while (!elementStack.isEmpty()) {
14848  foreach (QCPLayoutElement *subElement,
14849  elementStack.pop()->elements(false)) {
14850  if (subElement) {
14851  elementStack.push(subElement);
14852  if (QCPLegend *leg = qobject_cast<QCPLegend *>(subElement)) {
14853  if (leg->selectedParts() != QCPLegend::spNone)
14854  result.append(leg);
14855  }
14856  }
14857  }
14858  }
14859 
14860  return result;
14861 }
14862 
14875  foreach (QCPLayer *layer, mLayers) {
14876  foreach (QCPLayerable *layerable, layer->children())
14877  layerable->deselectEvent(0);
14878  }
14879 }
14880 
14910  if (refreshPriority == QCustomPlot::rpQueuedReplot) {
14911  if (!mReplotQueued) {
14912  mReplotQueued = true;
14913  QTimer::singleShot(0, this, SLOT(replot()));
14914  }
14915  return;
14916  }
14917 
14918  if (mReplotting) // incase signals loop back to replot slot
14919  return;
14920  mReplotting = true;
14921  mReplotQueued = false;
14922  emit beforeReplot();
14923 
14924  updateLayout();
14925  // draw all layered objects (grid, axes, plottables, items, legend,...) into
14926  // their buffers:
14928  foreach (QCPLayer *layer, mLayers) layer->drawToPaintBuffer();
14929  for (int i = 0; i < mPaintBuffers.size(); ++i)
14930  mPaintBuffers.at(i)->setInvalidated(false);
14931 
14932  if ((refreshPriority == rpRefreshHint &&
14934  refreshPriority == rpImmediateRefresh)
14935  repaint();
14936  else
14937  update();
14938 
14939  emit afterReplot();
14940  mReplotting = false;
14941 }
14942 
14953 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables) {
14954  QList<QCPAxis *> allAxes;
14955  foreach (QCPAxisRect *rect, axisRects()) allAxes << rect->axes();
14956 
14957  foreach (QCPAxis *axis, allAxes) axis->rescale(onlyVisiblePlottables);
14958 }
14959 
15001 bool QCustomPlot::savePdf(const QString &fileName,
15002  int width,
15003  int height,
15004  QCP::ExportPen exportPen,
15005  const QString &pdfCreator,
15006  const QString &pdfTitle) {
15007  bool success = false;
15008 #ifdef QT_NO_PRINTER
15009  Q_UNUSED(fileName)
15010  Q_UNUSED(exportPen)
15011  Q_UNUSED(width)
15012  Q_UNUSED(height)
15013  Q_UNUSED(pdfCreator)
15014  Q_UNUSED(pdfTitle)
15015  qDebug() << Q_FUNC_INFO
15016  << "Qt was built without printer support (QT_NO_PRINTER). PDF not "
15017  "created.";
15018 #else
15019  int newWidth, newHeight;
15020  if (width == 0 || height == 0) {
15021  newWidth = this->width();
15022  newHeight = this->height();
15023  } else {
15024  newWidth = width;
15025  newHeight = height;
15026  }
15027 
15028  QPrinter printer(QPrinter::ScreenResolution);
15029  printer.setOutputFileName(fileName);
15030  printer.setOutputFormat(QPrinter::PdfFormat);
15031  printer.setColorMode(QPrinter::Color);
15032  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
15033  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName,
15034  pdfTitle);
15035  QRect oldViewport = viewport();
15036  setViewport(QRect(0, 0, newWidth, newHeight));
15037 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
15038  printer.setFullPage(true);
15039  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
15040 #else
15041  QPageLayout pageLayout;
15042  pageLayout.setMode(QPageLayout::FullPageMode);
15043  pageLayout.setOrientation(QPageLayout::Portrait);
15044  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
15045  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point,
15046  QString(), QPageSize::ExactMatch));
15047  printer.setPageLayout(pageLayout);
15048 #endif
15049  QCPPainter printpainter;
15050  if (printpainter.begin(&printer)) {
15051  printpainter.setMode(QCPPainter::pmVectorized);
15052  printpainter.setMode(QCPPainter::pmNoCaching);
15053  printpainter.setMode(QCPPainter::pmNonCosmetic,
15054  exportPen == QCP::epNoCosmetic);
15055  printpainter.setWindow(mViewport);
15056  if (mBackgroundBrush.style() != Qt::NoBrush &&
15057  mBackgroundBrush.color() != Qt::white &&
15058  mBackgroundBrush.color() != Qt::transparent &&
15059  mBackgroundBrush.color().alpha() >
15060  0) // draw pdf background color if not white/transparent
15061  printpainter.fillRect(viewport(), mBackgroundBrush);
15062  draw(&printpainter);
15063  printpainter.end();
15064  success = true;
15065  }
15066  setViewport(oldViewport);
15067 #endif // QT_NO_PRINTER
15068  return success;
15069 }
15070 
15123 bool QCustomPlot::savePng(const QString &fileName,
15124  int width,
15125  int height,
15126  double scale,
15127  int quality,
15128  int resolution,
15129  QCP::ResolutionUnit resolutionUnit) {
15130  return saveRastered(fileName, width, height, scale, "PNG", quality,
15131  resolution, resolutionUnit);
15132 }
15133 
15182 bool QCustomPlot::saveJpg(const QString &fileName,
15183  int width,
15184  int height,
15185  double scale,
15186  int quality,
15187  int resolution,
15188  QCP::ResolutionUnit resolutionUnit) {
15189  return saveRastered(fileName, width, height, scale, "JPG", quality,
15190  resolution, resolutionUnit);
15191 }
15192 
15238 bool QCustomPlot::saveBmp(const QString &fileName,
15239  int width,
15240  int height,
15241  double scale,
15242  int resolution,
15243  QCP::ResolutionUnit resolutionUnit) {
15244  return saveRastered(fileName, width, height, scale, "BMP", -1, resolution,
15245  resolutionUnit);
15246 }
15247 
15260 }
15261 
15267 QSize QCustomPlot::sizeHint() const {
15269 }
15270 
15276 void QCustomPlot::paintEvent(QPaintEvent *event) {
15277  Q_UNUSED(event);
15278  QCPPainter painter(this);
15279  if (painter.isActive()) {
15280  painter.setRenderHint(QPainter::Antialiasing);
15281  if (mBackgroundBrush.style() != Qt::NoBrush)
15282  painter.fillRect(mViewport, mBackgroundBrush);
15283  drawBackground(&painter);
15284  for (int bufferIndex = 0; bufferIndex < mPaintBuffers.size();
15285  ++bufferIndex)
15286  mPaintBuffers.at(bufferIndex)->draw(&painter);
15287  }
15288 }
15289 
15296 void QCustomPlot::resizeEvent(QResizeEvent *event) {
15297  Q_UNUSED(event)
15298  // resize and repaint the buffer:
15299  setViewport(rect());
15300  replot(rpQueuedRefresh); // queued refresh is important here, to prevent
15301  // painting issues in some contexts (e.g. MDI
15302  // subwindow)
15303 }
15304 
15315  emit mouseDoubleClick(event);
15316  mMouseHasMoved = false;
15317  mMousePressPos = event->pos();
15318 
15319  // determine layerable under the cursor (this event is called instead of the
15320  // second press event in a double-click):
15321  QList<QVariant> details;
15322  QList<QCPLayerable *> candidates =
15323  layerableListAt(mMousePressPos, false, &details);
15324  for (int i = 0; i < candidates.size(); ++i) {
15325  event->accept(); // default impl of QCPLayerable's mouse events ignore
15326  // the event, in that case propagate to next candidate
15327  // in list
15328  candidates.at(i)->mouseDoubleClickEvent(event, details.at(i));
15329  if (event->isAccepted()) {
15330  mMouseEventLayerable = candidates.at(i);
15331  mMouseEventLayerableDetails = details.at(i);
15332  break;
15333  }
15334  }
15335 
15336  // emit specialized object double click signals:
15337  if (!candidates.isEmpty()) {
15338  if (QCPAbstractPlottable *ap =
15339  qobject_cast<QCPAbstractPlottable *>(candidates.first())) {
15340  int dataIndex = 0;
15341  if (!details.first().value<QCPDataSelection>().isEmpty())
15342  dataIndex = details.first()
15343  .value<QCPDataSelection>()
15344  .dataRange()
15345  .begin();
15346  emit plottableDoubleClick(ap, dataIndex, event);
15347  } else if (QCPAxis *ax = qobject_cast<QCPAxis *>(candidates.first()))
15348  emit axisDoubleClick(
15349  ax, details.first().value<QCPAxis::SelectablePart>(),
15350  event);
15351  else if (QCPAbstractItem *ai =
15352  qobject_cast<QCPAbstractItem *>(candidates.first()))
15353  emit itemDoubleClick(ai, event);
15354  else if (QCPLegend *lg = qobject_cast<QCPLegend *>(candidates.first()))
15355  emit legendDoubleClick(lg, 0, event);
15356  else if (QCPAbstractLegendItem *li =
15357  qobject_cast<QCPAbstractLegendItem *>(
15358  candidates.first()))
15359  emit legendDoubleClick(li->parentLegend(), li, event);
15360  }
15361 
15362  event->accept(); // in case QCPLayerable reimplementation manipulates event
15363  // accepted state. In QWidget event system, QCustomPlot
15364  // wants to accept the event.
15365 }
15366 
15378  emit mousePress(event);
15379  // save some state to tell in releaseEvent whether it was a click:
15380  mMouseHasMoved = false;
15381  mMousePressPos = event->pos();
15382 
15385  qobject_cast<QCPAxisRect *>(axisRectAt(
15386  mMousePressPos))) // in zoom mode only activate selection
15387  // rect if on an axis rect
15389  } else {
15390  // no selection rect interaction, prepare for click signal emission and
15391  // forward event to layerable under the cursor:
15392  QList<QVariant> details;
15393  QList<QCPLayerable *> candidates =
15394  layerableListAt(mMousePressPos, false, &details);
15395  if (!candidates.isEmpty()) {
15397  candidates.first(); // candidate for signal emission is
15398  // always topmost hit layerable (signal
15399  // emitted in release event)
15400  mMouseSignalLayerableDetails = details.first();
15401  }
15402  // forward event to topmost candidate which accepts the event:
15403  for (int i = 0; i < candidates.size(); ++i) {
15404  event->accept(); // default impl of QCPLayerable's mouse events
15405  // call ignore() on the event, in that case
15406  // propagate to next candidate in list
15407  candidates.at(i)->mousePressEvent(event, details.at(i));
15408  if (event->isAccepted()) {
15409  mMouseEventLayerable = candidates.at(i);
15410  mMouseEventLayerableDetails = details.at(i);
15411  break;
15412  }
15413  }
15414  }
15415 
15416  event->accept(); // in case QCPLayerable reimplementation manipulates event
15417  // accepted state. In QWidget event system, QCustomPlot
15418  // wants to accept the event.
15419 }
15420 
15435  emit mouseMove(event);
15436 
15437  if (!mMouseHasMoved &&
15438  (mMousePressPos - event->pos()).manhattanLength() > 3)
15439  mMouseHasMoved = true; // moved too far from mouse press position,
15440  // don't handle as click on mouse release
15441 
15444  else if (mMouseEventLayerable) // call event of affected layerable:
15445  mMouseEventLayerable->mouseMoveEvent(event, mMousePressPos);
15446 
15447  event->accept(); // in case QCPLayerable reimplementation manipulates event
15448  // accepted state. In QWidget event system, QCustomPlot
15449  // wants to accept the event.
15450 }
15451 
15470  emit mouseRelease(event);
15471 
15472  if (!mMouseHasMoved) // mouse hasn't moved (much) between press and
15473  // release, so handle as click
15474  {
15475  if (mSelectionRect &&
15477  ->isActive()) // a simple click shouldn't successfully
15478  // finish a selection rect, so cancel it here
15480  if (event->button() == Qt::LeftButton) processPointSelection(event);
15481 
15482  // emit specialized click signals of QCustomPlot instance:
15483  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable *>(
15485  int dataIndex = 0;
15487  .isEmpty())
15488  dataIndex =
15490  .dataRange()
15491  .begin();
15492  emit plottableClick(ap, dataIndex, event);
15493  } else if (QCPAxis *ax = qobject_cast<QCPAxis *>(mMouseSignalLayerable))
15494  emit axisClick(ax,
15496  .value<QCPAxis::SelectablePart>(),
15497  event);
15498  else if (QCPAbstractItem *ai =
15499  qobject_cast<QCPAbstractItem *>(mMouseSignalLayerable))
15500  emit itemClick(ai, event);
15501  else if (QCPLegend *lg =
15502  qobject_cast<QCPLegend *>(mMouseSignalLayerable))
15503  emit legendClick(lg, 0, event);
15504  else if (QCPAbstractLegendItem *li =
15505  qobject_cast<QCPAbstractLegendItem *>(
15507  emit legendClick(li->parentLegend(), li, event);
15509  }
15510 
15511  if (mSelectionRect &&
15512  mSelectionRect->isActive()) // Note: if a click was detected above, the
15513  // selection rect is canceled there
15514  {
15515  // finish selection rect, the appropriate action will be taken via
15516  // signal-slot connection:
15518  } else {
15519  // call event of affected layerable:
15520  if (mMouseEventLayerable) {
15521  mMouseEventLayerable->mouseReleaseEvent(event, mMousePressPos);
15523  }
15524  }
15525 
15527 
15528  event->accept(); // in case QCPLayerable reimplementation manipulates event
15529  // accepted state. In QWidget event system, QCustomPlot
15530  // wants to accept the event.
15531 }
15532 
15538 void QCustomPlot::wheelEvent(QWheelEvent *event) {
15539  emit mouseWheel(event);
15540 
15541 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
15542  const QPointF pos = event->pos();
15543 #else
15544  const QPointF pos = event->position();
15545 #endif
15546 
15547  // forward event to layerable under cursor:
15548  foreach (QCPLayerable *candidate, layerableListAt(pos, false)) {
15549  event->accept(); // default impl of QCPLayerable's mouse events ignore
15550  // the event, in that case propagate to next candidate
15551  // in list
15552  candidate->wheelEvent(event);
15553  if (event->isAccepted()) break;
15554  }
15555  event->accept(); // in case QCPLayerable reimplementation manipulates event
15556  // accepted state. In QWidget event system, QCustomPlot
15557  // wants to accept the event.
15558 }
15559 
15572  updateLayout();
15573 
15574  // draw viewport background pixmap:
15575  drawBackground(painter);
15576 
15577  // draw all layered objects (grid, axes, plottables, items, legend,...):
15578  foreach (QCPLayer *layer, mLayers) layer->draw(painter);
15579 
15580  /* Debug code to draw all layout element rects
15581  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
15582  {
15583  painter->setBrush(Qt::NoBrush);
15584  painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
15585  painter->drawRect(el->rect());
15586  painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
15587  painter->drawRect(el->outerRect());
15588  }
15589  */
15590 }
15591 
15602  // run through layout phases:
15606 }
15607 
15627  // Note: background color is handled in individual replot/save functions
15628 
15629  // draw background pixmap (on top of fill, if brush specified):
15630  if (!mBackgroundPixmap.isNull()) {
15631  if (mBackgroundScaled) {
15632  // check whether mScaledBackground needs to be updated:
15633  QSize scaledSize(mBackgroundPixmap.size());
15634  scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
15635  if (mScaledBackgroundPixmap.size() != scaledSize)
15638  Qt::SmoothTransformation);
15639  painter->drawPixmap(
15641  QRect(0, 0, mViewport.width(), mViewport.height()) &
15642  mScaledBackgroundPixmap.rect());
15643  } else {
15644  painter->drawPixmap(
15645  mViewport.topLeft(), mBackgroundPixmap,
15646  QRect(0, 0, mViewport.width(), mViewport.height()));
15647  }
15648  }
15649 }
15650 
15674  int bufferIndex = 0;
15675  if (mPaintBuffers.isEmpty())
15676  mPaintBuffers.append(
15677  QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
15678 
15679  for (int layerIndex = 0; layerIndex < mLayers.size(); ++layerIndex) {
15680  QCPLayer *layer = mLayers.at(layerIndex);
15681  if (layer->mode() == QCPLayer::lmLogical) {
15682  layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
15683  } else if (layer->mode() == QCPLayer::lmBuffered) {
15684  ++bufferIndex;
15685  if (bufferIndex >= mPaintBuffers.size())
15686  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(
15687  createPaintBuffer()));
15688  layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
15689  if (layerIndex < mLayers.size() - 1 &&
15690  mLayers.at(layerIndex + 1)->mode() ==
15691  QCPLayer::lmLogical) // not last layer, and next one is
15692  // logical, so prepare another
15693  // buffer for next layerables
15694  {
15695  ++bufferIndex;
15696  if (bufferIndex >= mPaintBuffers.size())
15697  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(
15698  createPaintBuffer()));
15699  }
15700  }
15701  }
15702  // remove unneeded buffers:
15703  while (mPaintBuffers.size() - 1 > bufferIndex) mPaintBuffers.removeLast();
15704  // resize buffers to viewport size and clear contents:
15705  for (int i = 0; i < mPaintBuffers.size(); ++i) {
15706  mPaintBuffers.at(i)->setSize(
15707  viewport()
15708  .size()); // won't do anything if already correct size
15709  mPaintBuffers.at(i)->clear(Qt::transparent);
15710  mPaintBuffers.at(i)->setInvalidated();
15711  }
15712 }
15713 
15725  if (mOpenGl) {
15726 #if defined(QCP_OPENGL_FBO)
15727  return new QCPPaintBufferGlFbo(viewport().size(),
15728  mBufferDevicePixelRatio, mGlContext,
15729  mGlPaintDevice);
15730 #elif defined(QCP_OPENGL_PBUFFER)
15731  return new QCPPaintBufferGlPbuffer(viewport().size(),
15734 #else
15735  qDebug() << Q_FUNC_INFO
15736  << "OpenGL enabled even though no support for it compiled in, "
15737  "this shouldn't have happened. Falling back to pixmap "
15738  "paint buffer.";
15739  return new QCPPaintBufferPixmap(viewport().size(),
15741 #endif
15742  } else
15743  return new QCPPaintBufferPixmap(viewport().size(),
15745 }
15746 
15760  for (int i = 0; i < mPaintBuffers.size(); ++i) {
15761  if (mPaintBuffers.at(i)->invalidated()) return true;
15762  }
15763  return false;
15764 }
15765 
15781 #ifdef QCP_OPENGL_FBO
15782  freeOpenGl();
15783  QSurfaceFormat proposedSurfaceFormat;
15784  proposedSurfaceFormat.setSamples(mOpenGlMultisamples);
15785 #ifdef QCP_OPENGL_OFFSCREENSURFACE
15786  QOffscreenSurface *surface = new QOffscreenSurface;
15787 #else
15788  QWindow *surface = new QWindow;
15789  surface->setSurfaceType(QSurface::OpenGLSurface);
15790 #endif
15791  surface->setFormat(proposedSurfaceFormat);
15792  surface->create();
15793  mGlSurface = QSharedPointer<QSurface>(surface);
15794  mGlContext = QSharedPointer<QOpenGLContext>(new QOpenGLContext);
15795  mGlContext->setFormat(mGlSurface->format());
15796  if (!mGlContext->create()) {
15797  qDebug() << Q_FUNC_INFO << "Failed to create OpenGL context";
15798  mGlContext.clear();
15799  mGlSurface.clear();
15800  return false;
15801  }
15802  if (!mGlContext->makeCurrent(
15803  mGlSurface.data())) // context needs to be current to create
15804  // paint device
15805  {
15806  qDebug() << Q_FUNC_INFO << "Failed to make opengl context current";
15807  mGlContext.clear();
15808  mGlSurface.clear();
15809  return false;
15810  }
15811  if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()) {
15812  qDebug()
15813  << Q_FUNC_INFO
15814  << "OpenGL of this system doesn't support frame buffer objects";
15815  mGlContext.clear();
15816  mGlSurface.clear();
15817  return false;
15818  }
15819  mGlPaintDevice = QSharedPointer<QOpenGLPaintDevice>(new QOpenGLPaintDevice);
15820  return true;
15821 #elif defined(QCP_OPENGL_PBUFFER)
15822  return QGLFormat::hasOpenGL();
15823 #else
15824  return false;
15825 #endif
15826 }
15827 
15841 #ifdef QCP_OPENGL_FBO
15842  mGlPaintDevice.clear();
15843  mGlContext.clear();
15844  mGlSurface.clear();
15845 #endif
15846 }
15847 
15855  if (xAxis == axis) xAxis = 0;
15856  if (xAxis2 == axis) xAxis2 = 0;
15857  if (yAxis == axis) yAxis = 0;
15858  if (yAxis2 == axis) yAxis2 = 0;
15859 
15860  // Note: No need to take care of range drag axes and range zoom axes,
15861  // because they are stored in smart pointers
15862 }
15863 
15870  if (this->legend == legend) this->legend = 0;
15871 }
15872 
15892 void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event) {
15893  bool selectionStateChanged = false;
15894 
15895  if (mInteractions.testFlag(QCP::iSelectPlottables)) {
15896  QMap<int, QPair<QCPAbstractPlottable *, QCPDataSelection>>
15897  potentialSelections; // map key is number of selected data
15898  // points, so we have selections sorted by
15899  // size
15900  QRectF rectF(rect.normalized());
15901  if (QCPAxisRect *affectedAxisRect = axisRectAt(rectF.topLeft())) {
15902  // determine plottables that were hit by the rect and thus are
15903  // candidates for selection:
15904  foreach (QCPAbstractPlottable *plottable,
15905  affectedAxisRect->plottables()) {
15906  if (QCPPlottableInterface1D *plottableInterface =
15907  plottable->interface1D()) {
15908  QCPDataSelection dataSel =
15909  plottableInterface->selectTestRect(rectF, true);
15910  if (!dataSel.isEmpty()) {
15911 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
15912  potentialSelections.insertMulti(
15913  dataSel.dataPointCount(),
15914  QPair<QCPAbstractPlottable *, QCPDataSelection>(
15915  plottable, dataSel));
15916 #else
15917  potentialSelections.insert(
15918  dataSel.dataPointCount(),
15919  QPair<QCPAbstractPlottable *, QCPDataSelection>(
15920  plottable, dataSel));
15921 #endif
15922  }
15923  }
15924  }
15925 
15926  if (!mInteractions.testFlag(QCP::iMultiSelect)) {
15927  // only leave plottable with most selected points in map, since
15928  // we will only select a single plottable:
15929  if (!potentialSelections.isEmpty()) {
15930  QMap<int, QPair<QCPAbstractPlottable *, QCPDataSelection>>::
15931  iterator it = potentialSelections.begin();
15932  while (it != potentialSelections.end() -
15933  1) // erase all except last element
15934  it = potentialSelections.erase(it);
15935  }
15936  }
15937 
15938  bool additive = event->modifiers().testFlag(mMultiSelectModifier);
15939  // deselect all other layerables if not additive selection:
15940  if (!additive) {
15941  // emit deselection except to those plottables who will be
15942  // selected afterwards:
15943  foreach (QCPLayer *layer, mLayers) {
15944  foreach (QCPLayerable *layerable, layer->children()) {
15945  if ((potentialSelections.isEmpty() ||
15946  potentialSelections.constBegin()->first !=
15947  layerable) &&
15948  mInteractions.testFlag(
15949  layerable->selectionCategory())) {
15950  bool selChanged = false;
15951  layerable->deselectEvent(&selChanged);
15952  selectionStateChanged |= selChanged;
15953  }
15954  }
15955  }
15956  }
15957 
15958  // go through selections in reverse (largest selection first) and
15959  // emit select events:
15960  QMap<int, QPair<QCPAbstractPlottable *, QCPDataSelection>>::
15961  const_iterator it = potentialSelections.constEnd();
15962  while (it != potentialSelections.constBegin()) {
15963  --it;
15964  if (mInteractions.testFlag(
15965  it.value().first->selectionCategory())) {
15966  bool selChanged = false;
15967  it.value().first->selectEvent(
15968  event, additive,
15969  QVariant::fromValue(it.value().second),
15970  &selChanged);
15971  selectionStateChanged |= selChanged;
15972  }
15973  }
15974  }
15975  }
15976 
15977  if (selectionStateChanged) {
15978  emit selectionChangedByUser();
15980  } else if (mSelectionRect)
15981  mSelectionRect->layer()->replot();
15982 }
15983 
15996 void QCustomPlot::processRectZoom(QRect rect, QMouseEvent *event) {
15997  Q_UNUSED(event)
15998  if (QCPAxisRect *axisRect = axisRectAt(rect.topLeft())) {
15999  QList<QCPAxis *> affectedAxes =
16000  QList<QCPAxis *>() << axisRect->rangeZoomAxes(Qt::Horizontal)
16001  << axisRect->rangeZoomAxes(Qt::Vertical);
16002  affectedAxes.removeAll(static_cast<QCPAxis *>(0));
16003  axisRect->zoom(QRectF(rect), affectedAxes);
16004  }
16005  replot(rpQueuedReplot); // always replot to make selection rect disappear
16006 }
16007 
16030  QVariant details;
16031  QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
16032  bool selectionStateChanged = false;
16033  bool additive = mInteractions.testFlag(QCP::iMultiSelect) &&
16034  event->modifiers().testFlag(mMultiSelectModifier);
16035  // deselect all other layerables if not additive selection:
16036  if (!additive) {
16037  foreach (QCPLayer *layer, mLayers) {
16038  foreach (QCPLayerable *layerable, layer->children()) {
16039  if (layerable != clickedLayerable &&
16040  mInteractions.testFlag(layerable->selectionCategory())) {
16041  bool selChanged = false;
16042  layerable->deselectEvent(&selChanged);
16043  selectionStateChanged |= selChanged;
16044  }
16045  }
16046  }
16047  }
16048  if (clickedLayerable &&
16049  mInteractions.testFlag(clickedLayerable->selectionCategory())) {
16050  // a layerable was actually clicked, call its selectEvent:
16051  bool selChanged = false;
16052  clickedLayerable->selectEvent(event, additive, details, &selChanged);
16053  selectionStateChanged |= selChanged;
16054  }
16055  if (selectionStateChanged) {
16056  emit selectionChangedByUser();
16058  }
16059 }
16060 
16074  if (mPlottables.contains(plottable)) {
16075  qDebug() << Q_FUNC_INFO
16076  << "plottable already added to this QCustomPlot:"
16077  << reinterpret_cast<quintptr>(plottable);
16078  return false;
16079  }
16080  if (plottable->parentPlot() != this) {
16081  qDebug() << Q_FUNC_INFO
16082  << "plottable not created with this QCustomPlot as parent:"
16083  << reinterpret_cast<quintptr>(plottable);
16084  return false;
16085  }
16086 
16087  mPlottables.append(plottable);
16088  // possibly add plottable to legend:
16090  if (!plottable->layer()) // usually the layer is already set in the
16091  // constructor of the plottable (via QCPLayerable
16092  // constructor)
16094  return true;
16095 }
16096 
16108  if (!graph) {
16109  qDebug() << Q_FUNC_INFO << "passed graph is zero";
16110  return false;
16111  }
16112  if (mGraphs.contains(graph)) {
16113  qDebug() << Q_FUNC_INFO
16114  << "graph already registered with this QCustomPlot";
16115  return false;
16116  }
16117 
16118  mGraphs.append(graph);
16119  return true;
16120 }
16121 
16134  if (mItems.contains(item)) {
16135  qDebug() << Q_FUNC_INFO << "item already added to this QCustomPlot:"
16136  << reinterpret_cast<quintptr>(item);
16137  return false;
16138  }
16139  if (item->parentPlot() != this) {
16140  qDebug() << Q_FUNC_INFO
16141  << "item not created with this QCustomPlot as parent:"
16142  << reinterpret_cast<quintptr>(item);
16143  return false;
16144  }
16145 
16146  mItems.append(item);
16147  if (!item->layer()) // usually the layer is already set in the constructor
16148  // of the item (via QCPLayerable constructor)
16150  return true;
16151 }
16152 
16160  for (int i = 0; i < mLayers.size(); ++i) mLayers.at(i)->mIndex = i;
16161 }
16162 
16182  bool onlySelectable,
16183  QVariant *selectionDetails) const {
16184  QList<QVariant> details;
16185  QList<QCPLayerable *> candidates = layerableListAt(
16186  pos, onlySelectable, selectionDetails ? &details : 0);
16187  if (selectionDetails && !details.isEmpty())
16188  *selectionDetails = details.first();
16189  if (!candidates.isEmpty())
16190  return candidates.first();
16191  else
16192  return 0;
16193 }
16194 
16216 QList<QCPLayerable *> QCustomPlot::layerableListAt(
16217  const QPointF &pos,
16218  bool onlySelectable,
16219  QList<QVariant> *selectionDetails) const {
16220  QList<QCPLayerable *> result;
16221  for (int layerIndex = mLayers.size() - 1; layerIndex >= 0; --layerIndex) {
16222  const QList<QCPLayerable *> layerables =
16223  mLayers.at(layerIndex)->children();
16224  for (int i = layerables.size() - 1; i >= 0; --i) {
16225  if (!layerables.at(i)->realVisibility()) continue;
16226  QVariant details;
16227  double dist = layerables.at(i)->selectTest(
16228  pos, onlySelectable, selectionDetails ? &details : 0);
16229  if (dist >= 0 && dist < selectionTolerance()) {
16230  result.append(layerables.at(i));
16231  if (selectionDetails) selectionDetails->append(details);
16232  }
16233  }
16234  }
16235  return result;
16236 }
16237 
16260 bool QCustomPlot::saveRastered(const QString &fileName,
16261  int width,
16262  int height,
16263  double scale,
16264  const char *format,
16265  int quality,
16266  int resolution,
16267  QCP::ResolutionUnit resolutionUnit) {
16268  QImage buffer = toPixmap(width, height, scale).toImage();
16269 
16270  int dotsPerMeter = 0;
16271  switch (resolutionUnit) {
16272  case QCP::ruDotsPerMeter:
16273  dotsPerMeter = resolution;
16274  break;
16276  dotsPerMeter = resolution * 100;
16277  break;
16278  case QCP::ruDotsPerInch:
16279  dotsPerMeter = resolution / 0.0254;
16280  break;
16281  }
16282  buffer.setDotsPerMeterX(
16283  dotsPerMeter); // this is saved together with some image formats,
16284  // e.g. PNG, and is relevant when opening image in
16285  // other tools
16286  buffer.setDotsPerMeterY(
16287  dotsPerMeter); // this is saved together with some image formats,
16288  // e.g. PNG, and is relevant when opening image in
16289  // other tools
16290  if (!buffer.isNull())
16291  return buffer.save(fileName, format, quality);
16292  else
16293  return false;
16294 }
16295 
16305 QPixmap QCustomPlot::toPixmap(int width, int height, double scale) {
16306  // this method is somewhat similar to toPainter. Change something here, and
16307  // a change in toPainter might be necessary, too.
16308  int newWidth, newHeight;
16309  if (width == 0 || height == 0) {
16310  newWidth = this->width();
16311  newHeight = this->height();
16312  } else {
16313  newWidth = width;
16314  newHeight = height;
16315  }
16316  int scaledWidth = qRound(scale * newWidth);
16317  int scaledHeight = qRound(scale * newHeight);
16318 
16319  QPixmap result(scaledWidth, scaledHeight);
16320  result.fill(mBackgroundBrush.style() == Qt::SolidPattern
16321  ? mBackgroundBrush.color()
16322  : Qt::transparent); // if using non-solid pattern, make
16323  // transparent now and draw brush
16324  // pattern later
16325  QCPPainter painter;
16326  painter.begin(&result);
16327  if (painter.isActive()) {
16328  QRect oldViewport = viewport();
16329  setViewport(QRect(0, 0, newWidth, newHeight));
16331  if (!qFuzzyCompare(scale, 1.0)) {
16332  if (scale > 1.0) // for scale < 1 we always want cosmetic pens
16333  // where possible, because else lines might
16334  // disappear for very small scales
16336  painter.scale(scale, scale);
16337  }
16338  if (mBackgroundBrush.style() != Qt::SolidPattern &&
16339  mBackgroundBrush.style() !=
16340  Qt::NoBrush) // solid fills were done a few lines above
16341  // with QPixmap::fill
16342  painter.fillRect(mViewport, mBackgroundBrush);
16343  draw(&painter);
16344  setViewport(oldViewport);
16345  painter.end();
16346  } else // might happen if pixmap has width or height zero
16347  {
16348  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
16349  return QPixmap();
16350  }
16351  return result;
16352 }
16353 
16368 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) {
16369  // this method is somewhat similar to toPixmap. Change something here, and a
16370  // change in toPixmap might be necessary, too.
16371  int newWidth, newHeight;
16372  if (width == 0 || height == 0) {
16373  newWidth = this->width();
16374  newHeight = this->height();
16375  } else {
16376  newWidth = width;
16377  newHeight = height;
16378  }
16379 
16380  if (painter->isActive()) {
16381  QRect oldViewport = viewport();
16382  setViewport(QRect(0, 0, newWidth, newHeight));
16383  painter->setMode(QCPPainter::pmNoCaching);
16384  if (mBackgroundBrush.style() !=
16385  Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for
16386  // Qt::SolidPattern brush style, so we also draw solid
16387  // fills with fillRect here
16388  painter->fillRect(mViewport, mBackgroundBrush);
16389  draw(painter);
16390  setViewport(oldViewport);
16391  } else
16392  qDebug() << Q_FUNC_INFO << "Passed painter is not active";
16393 }
16394 /* end of 'src/core.cpp' */
16395 
16396 // amalgamation: add plottable1d.cpp
16397 
16398 /* including file 'src/colorgradient.cpp', size 25342 */
16399 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
16400 
16404 
16445  : mLevelCount(350),
16446  mColorInterpolation(ciRGB),
16447  mPeriodic(false),
16448  mColorBufferInvalidated(true) {
16449  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
16450 }
16451 
16459  : mLevelCount(350),
16460  mColorInterpolation(ciRGB),
16461  mPeriodic(false),
16462  mColorBufferInvalidated(true) {
16463  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
16464  loadPreset(preset);
16465 }
16466 
16467 /* undocumented operator */
16469  return ((other.mLevelCount == this->mLevelCount) &&
16470  (other.mColorInterpolation == this->mColorInterpolation) &&
16471  (other.mPeriodic == this->mPeriodic) &&
16472  (other.mColorStops == this->mColorStops));
16473 }
16474 
16483  if (n < 2) {
16484  qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
16485  n = 2;
16486  }
16487  if (n != mLevelCount) {
16488  mLevelCount = n;
16489  mColorBufferInvalidated = true;
16490  }
16491 }
16492 
16505 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops) {
16507  mColorBufferInvalidated = true;
16508 }
16509 
16517 void QCPColorGradient::setColorStopAt(double position, const QColor &color) {
16518  mColorStops.insert(position, color);
16519  mColorBufferInvalidated = true;
16520 }
16521 
16530  QCPColorGradient::ColorInterpolation interpolation) {
16531  if (interpolation != mColorInterpolation) {
16532  mColorInterpolation = interpolation;
16533  mColorBufferInvalidated = true;
16534  }
16535 }
16536 
16555 void QCPColorGradient::setPeriodic(bool enabled) { mPeriodic = enabled; }
16556 
16578  const QCPRange &range,
16579  QRgb *scanLine,
16580  int n,
16581  int dataIndexFactor,
16582  bool logarithmic) {
16583  // If you change something here, make sure to also adapt color() and the
16584  // other colorize() overload
16585  if (!data) {
16586  qDebug() << Q_FUNC_INFO << "null pointer given as data";
16587  return;
16588  }
16589  if (!scanLine) {
16590  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
16591  return;
16592  }
16594 
16595  if (!logarithmic) {
16596  const double posToIndexFactor = (mLevelCount - 1) / range.size();
16597  if (mPeriodic) {
16598  for (int i = 0; i < n; ++i) {
16599  int index = (int)((data[dataIndexFactor * i] - range.lower) *
16600  posToIndexFactor) %
16601  mLevelCount;
16602  if (index < 0) index += mLevelCount;
16603  scanLine[i] = mColorBuffer.at(index);
16604  }
16605  } else {
16606  for (int i = 0; i < n; ++i) {
16607  int index = (data[dataIndexFactor * i] - range.lower) *
16608  posToIndexFactor;
16609  if (index < 0)
16610  index = 0;
16611  else if (index >= mLevelCount)
16612  index = mLevelCount - 1;
16613  scanLine[i] = mColorBuffer.at(index);
16614  }
16615  }
16616  } else // logarithmic == true
16617  {
16618  if (mPeriodic) {
16619  for (int i = 0; i < n; ++i) {
16620  int index = (int)(qLn(data[dataIndexFactor * i] / range.lower) /
16621  qLn(range.upper / range.lower) *
16622  (mLevelCount - 1)) %
16623  mLevelCount;
16624  if (index < 0) index += mLevelCount;
16625  scanLine[i] = mColorBuffer.at(index);
16626  }
16627  } else {
16628  for (int i = 0; i < n; ++i) {
16629  int index = qLn(data[dataIndexFactor * i] / range.lower) /
16630  qLn(range.upper / range.lower) * (mLevelCount - 1);
16631  if (index < 0)
16632  index = 0;
16633  else if (index >= mLevelCount)
16634  index = mLevelCount - 1;
16635  scanLine[i] = mColorBuffer.at(index);
16636  }
16637  }
16638  }
16639 }
16640 
16651  const unsigned char *alpha,
16652  const QCPRange &range,
16653  QRgb *scanLine,
16654  int n,
16655  int dataIndexFactor,
16656  bool logarithmic) {
16657  // If you change something here, make sure to also adapt color() and the
16658  // other colorize() overload
16659  if (!data) {
16660  qDebug() << Q_FUNC_INFO << "null pointer given as data";
16661  return;
16662  }
16663  if (!alpha) {
16664  qDebug() << Q_FUNC_INFO << "null pointer given as alpha";
16665  return;
16666  }
16667  if (!scanLine) {
16668  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
16669  return;
16670  }
16672 
16673  if (!logarithmic) {
16674  const double posToIndexFactor = (mLevelCount - 1) / range.size();
16675  if (mPeriodic) {
16676  for (int i = 0; i < n; ++i) {
16677  int index = (int)((data[dataIndexFactor * i] - range.lower) *
16678  posToIndexFactor) %
16679  mLevelCount;
16680  if (index < 0) index += mLevelCount;
16681  if (alpha[dataIndexFactor * i] == 255) {
16682  scanLine[i] = mColorBuffer.at(index);
16683  } else {
16684  const QRgb rgb = mColorBuffer.at(index);
16685  const float alphaF = alpha[dataIndexFactor * i] / 255.0f;
16686  scanLine[i] =
16687  qRgba(qRed(rgb) * alphaF, qGreen(rgb) * alphaF,
16688  qBlue(rgb) * alphaF, qAlpha(rgb) * alphaF);
16689  }
16690  }
16691  } else {
16692  for (int i = 0; i < n; ++i) {
16693  int index = (data[dataIndexFactor * i] - range.lower) *
16694  posToIndexFactor;
16695  if (index < 0)
16696  index = 0;
16697  else if (index >= mLevelCount)
16698  index = mLevelCount - 1;
16699  if (alpha[dataIndexFactor * i] == 255) {
16700  scanLine[i] = mColorBuffer.at(index);
16701  } else {
16702  const QRgb rgb = mColorBuffer.at(index);
16703  const float alphaF = alpha[dataIndexFactor * i] / 255.0f;
16704  scanLine[i] =
16705  qRgba(qRed(rgb) * alphaF, qGreen(rgb) * alphaF,
16706  qBlue(rgb) * alphaF, qAlpha(rgb) * alphaF);
16707  }
16708  }
16709  }
16710  } else // logarithmic == true
16711  {
16712  if (mPeriodic) {
16713  for (int i = 0; i < n; ++i) {
16714  int index = (int)(qLn(data[dataIndexFactor * i] / range.lower) /
16715  qLn(range.upper / range.lower) *
16716  (mLevelCount - 1)) %
16717  mLevelCount;
16718  if (index < 0) index += mLevelCount;
16719  if (alpha[dataIndexFactor * i] == 255) {
16720  scanLine[i] = mColorBuffer.at(index);
16721  } else {
16722  const QRgb rgb = mColorBuffer.at(index);
16723  const float alphaF = alpha[dataIndexFactor * i] / 255.0f;
16724  scanLine[i] =
16725  qRgba(qRed(rgb) * alphaF, qGreen(rgb) * alphaF,
16726  qBlue(rgb) * alphaF, qAlpha(rgb) * alphaF);
16727  }
16728  }
16729  } else {
16730  for (int i = 0; i < n; ++i) {
16731  int index = qLn(data[dataIndexFactor * i] / range.lower) /
16732  qLn(range.upper / range.lower) * (mLevelCount - 1);
16733  if (index < 0)
16734  index = 0;
16735  else if (index >= mLevelCount)
16736  index = mLevelCount - 1;
16737  if (alpha[dataIndexFactor * i] == 255) {
16738  scanLine[i] = mColorBuffer.at(index);
16739  } else {
16740  const QRgb rgb = mColorBuffer.at(index);
16741  const float alphaF = alpha[dataIndexFactor * i] / 255.0f;
16742  scanLine[i] =
16743  qRgba(qRed(rgb) * alphaF, qGreen(rgb) * alphaF,
16744  qBlue(rgb) * alphaF, qAlpha(rgb) * alphaF);
16745  }
16746  }
16747  }
16748  }
16749 }
16750 
16765  const QCPRange &range,
16766  bool logarithmic) {
16767  // If you change something here, make sure to also adapt ::colorize()
16769  int index = 0;
16770  if (!logarithmic)
16771  index = (position - range.lower) * (mLevelCount - 1) / range.size();
16772  else
16773  index = qLn(position / range.lower) / qLn(range.upper / range.lower) *
16774  (mLevelCount - 1);
16775  if (mPeriodic) {
16776  index = index % mLevelCount;
16777  if (index < 0) index += mLevelCount;
16778  } else {
16779  if (index < 0)
16780  index = 0;
16781  else if (index >= mLevelCount)
16782  index = mLevelCount - 1;
16783  }
16784  return mColorBuffer.at(index);
16785 }
16786 
16796  clearColorStops();
16797  switch (preset) {
16798  case gpGrayscale:
16802  break;
16803  case gpHot:
16805  setColorStopAt(0, QColor(50, 0, 0));
16806  setColorStopAt(0.2, QColor(180, 10, 0));
16807  setColorStopAt(0.4, QColor(245, 50, 0));
16808  setColorStopAt(0.6, QColor(255, 150, 10));
16809  setColorStopAt(0.8, QColor(255, 255, 50));
16810  setColorStopAt(1, QColor(255, 255, 255));
16811  break;
16812  case gpCold:
16814  setColorStopAt(0, QColor(0, 0, 50));
16815  setColorStopAt(0.2, QColor(0, 10, 180));
16816  setColorStopAt(0.4, QColor(0, 50, 245));
16817  setColorStopAt(0.6, QColor(10, 150, 255));
16818  setColorStopAt(0.8, QColor(50, 255, 255));
16819  setColorStopAt(1, QColor(255, 255, 255));
16820  break;
16821  case gpNight:
16823  setColorStopAt(0, QColor(10, 20, 30));
16824  setColorStopAt(1, QColor(250, 255, 250));
16825  break;
16826  case gpCandy:
16828  setColorStopAt(0, QColor(0, 0, 255));
16829  setColorStopAt(1, QColor(255, 250, 250));
16830  break;
16831  case gpGeography:
16833  setColorStopAt(0, QColor(70, 170, 210));
16834  setColorStopAt(0.20, QColor(90, 160, 180));
16835  setColorStopAt(0.25, QColor(45, 130, 175));
16836  setColorStopAt(0.30, QColor(100, 140, 125));
16837  setColorStopAt(0.5, QColor(100, 140, 100));
16838  setColorStopAt(0.6, QColor(130, 145, 120));
16839  setColorStopAt(0.7, QColor(140, 130, 120));
16840  setColorStopAt(0.9, QColor(180, 190, 190));
16841  setColorStopAt(1, QColor(210, 210, 230));
16842  break;
16843  case gpIon:
16845  setColorStopAt(0, QColor(50, 10, 10));
16846  setColorStopAt(0.45, QColor(0, 0, 255));
16847  setColorStopAt(0.8, QColor(0, 255, 255));
16848  setColorStopAt(1, QColor(0, 255, 0));
16849  break;
16850  case gpThermal:
16852  setColorStopAt(0, QColor(0, 0, 50));
16853  setColorStopAt(0.15, QColor(20, 0, 120));
16854  setColorStopAt(0.33, QColor(200, 30, 140));
16855  setColorStopAt(0.6, QColor(255, 100, 0));
16856  setColorStopAt(0.85, QColor(255, 255, 40));
16857  setColorStopAt(1, QColor(255, 255, 255));
16858  break;
16859  case gpPolar:
16861  setColorStopAt(0, QColor(50, 255, 255));
16862  setColorStopAt(0.18, QColor(10, 70, 255));
16863  setColorStopAt(0.28, QColor(10, 10, 190));
16864  setColorStopAt(0.5, QColor(0, 0, 0));
16865  setColorStopAt(0.72, QColor(190, 10, 10));
16866  setColorStopAt(0.82, QColor(255, 70, 10));
16867  setColorStopAt(1, QColor(255, 255, 50));
16868  break;
16869  case gpSpectrum:
16871  setColorStopAt(0, QColor(50, 0, 50));
16872  setColorStopAt(0.15, QColor(0, 0, 255));
16873  setColorStopAt(0.35, QColor(0, 255, 255));
16874  setColorStopAt(0.6, QColor(255, 255, 0));
16875  setColorStopAt(0.75, QColor(255, 30, 0));
16876  setColorStopAt(1, QColor(50, 0, 0));
16877  break;
16878  case gpJet:
16880  setColorStopAt(0, QColor(0, 0, 100));
16881  setColorStopAt(0.15, QColor(0, 50, 255));
16882  setColorStopAt(0.35, QColor(0, 255, 255));
16883  setColorStopAt(0.65, QColor(255, 255, 0));
16884  setColorStopAt(0.85, QColor(255, 30, 0));
16885  setColorStopAt(1, QColor(100, 0, 0));
16886  break;
16887  case gpHues:
16889  setColorStopAt(0, QColor(255, 0, 0));
16890  setColorStopAt(1.0 / 3.0, QColor(0, 0, 255));
16891  setColorStopAt(2.0 / 3.0, QColor(0, 255, 0));
16892  setColorStopAt(1, QColor(255, 0, 0));
16893  break;
16894  }
16895 }
16896 
16903  mColorStops.clear();
16904  mColorBufferInvalidated = true;
16905 }
16906 
16914  QCPColorGradient result(*this);
16915  result.clearColorStops();
16916  for (QMap<double, QColor>::const_iterator it = mColorStops.constBegin();
16917  it != mColorStops.constEnd(); ++it)
16918  result.setColorStopAt(1.0 - it.key(), it.value());
16919  return result;
16920 }
16921 
16928  for (QMap<double, QColor>::const_iterator it = mColorStops.constBegin();
16929  it != mColorStops.constEnd(); ++it) {
16930  if (it.value().alpha() < 255) return true;
16931  }
16932  return false;
16933 }
16934 
16942  if (mColorBuffer.size() != mLevelCount) mColorBuffer.resize(mLevelCount);
16943  if (mColorStops.size() > 1) {
16944  double indexToPosFactor = 1.0 / (double)(mLevelCount - 1);
16945  const bool useAlpha = stopsUseAlpha();
16946  for (int i = 0; i < mLevelCount; ++i) {
16947  double position = i * indexToPosFactor;
16948  QMap<double, QColor>::const_iterator it =
16949  mColorStops.lowerBound(position);
16950  if (it == mColorStops.constEnd()) // position is on or after last
16951  // stop, use color of last stop
16952  {
16953  if (useAlpha) {
16954  const QColor col = (it - 1).value();
16955  const float alphaPremultiplier =
16956  col.alpha() /
16957  255.0f; // since we use
16958  // QImage::Format_ARGB32_Premultiplied
16959  mColorBuffer[i] =
16960  qRgba(col.red() * alphaPremultiplier,
16961  col.green() * alphaPremultiplier,
16962  col.blue() * alphaPremultiplier, col.alpha());
16963  } else
16964  mColorBuffer[i] = (it - 1).value().rgba();
16965  } else if (it ==
16966  mColorStops
16967  .constBegin()) // position is on or before first
16968  // stop, use color of first stop
16969  {
16970  if (useAlpha) {
16971  const QColor col = it.value();
16972  const float alphaPremultiplier =
16973  col.alpha() /
16974  255.0f; // since we use
16975  // QImage::Format_ARGB32_Premultiplied
16976  mColorBuffer[i] =
16977  qRgba(col.red() * alphaPremultiplier,
16978  col.green() * alphaPremultiplier,
16979  col.blue() * alphaPremultiplier, col.alpha());
16980  } else
16981  mColorBuffer[i] = it.value().rgba();
16982  } else // position is in between stops (or on an intermediate
16983  // stop), interpolate color
16984  {
16985  QMap<double, QColor>::const_iterator high = it;
16986  QMap<double, QColor>::const_iterator low = it - 1;
16987  double t =
16988  (position - low.key()) /
16989  (high.key() - low.key()); // interpolation factor 0..1
16990  switch (mColorInterpolation) {
16991  case ciRGB: {
16992  if (useAlpha) {
16993  const int alpha = (1 - t) * low.value().alpha() +
16994  t * high.value().alpha();
16995  const float alphaPremultiplier =
16996  alpha /
16997  255.0f; // since we use
16998  // QImage::Format_ARGB32_Premultiplied
16999  mColorBuffer[i] =
17000  qRgba(((1 - t) * low.value().red() +
17001  t * high.value().red()) *
17002  alphaPremultiplier,
17003  ((1 - t) * low.value().green() +
17004  t * high.value().green()) *
17005  alphaPremultiplier,
17006  ((1 - t) * low.value().blue() +
17007  t * high.value().blue()) *
17008  alphaPremultiplier,
17009  alpha);
17010  } else {
17011  mColorBuffer[i] =
17012  qRgb(((1 - t) * low.value().red() +
17013  t * high.value().red()),
17014  ((1 - t) * low.value().green() +
17015  t * high.value().green()),
17016  ((1 - t) * low.value().blue() +
17017  t * high.value().blue()));
17018  }
17019  break;
17020  }
17021  case ciHSV: {
17022  QColor lowHsv = low.value().toHsv();
17023  QColor highHsv = high.value().toHsv();
17024  double hue = 0;
17025  double hueDiff = highHsv.hueF() - lowHsv.hueF();
17026  if (hueDiff > 0.5)
17027  hue = lowHsv.hueF() - t * (1.0 - hueDiff);
17028  else if (hueDiff < -0.5)
17029  hue = lowHsv.hueF() + t * (1.0 + hueDiff);
17030  else
17031  hue = lowHsv.hueF() + t * hueDiff;
17032  if (hue < 0)
17033  hue += 1.0;
17034  else if (hue >= 1.0)
17035  hue -= 1.0;
17036  if (useAlpha) {
17037  const QRgb rgb =
17038  QColor::fromHsvF(
17039  hue,
17040  (1 - t) * lowHsv.saturationF() +
17041  t * highHsv.saturationF(),
17042  (1 - t) * lowHsv.valueF() +
17043  t * highHsv.valueF())
17044  .rgb();
17045  const float alpha = (1 - t) * lowHsv.alphaF() +
17046  t * highHsv.alphaF();
17047  mColorBuffer[i] = qRgba(
17048  qRed(rgb) * alpha, qGreen(rgb) * alpha,
17049  qBlue(rgb) * alpha, 255 * alpha);
17050  } else {
17051  mColorBuffer[i] =
17052  QColor::fromHsvF(
17053  hue,
17054  (1 - t) * lowHsv.saturationF() +
17055  t * highHsv.saturationF(),
17056  (1 - t) * lowHsv.valueF() +
17057  t * highHsv.valueF())
17058  .rgb();
17059  }
17060  break;
17061  }
17062  }
17063  }
17064  }
17065  } else if (mColorStops.size() == 1) {
17066  const QRgb rgb = mColorStops.constBegin().value().rgb();
17067  const float alpha = mColorStops.constBegin().value().alphaF();
17068  mColorBuffer.fill(qRgba(qRed(rgb) * alpha, qGreen(rgb) * alpha,
17069  qBlue(rgb) * alpha, 255 * alpha));
17070  } else // mColorStops is empty, fill color buffer with black
17071  {
17072  mColorBuffer.fill(qRgb(0, 0, 0));
17073  }
17074  mColorBufferInvalidated = false;
17075 }
17076 /* end of 'src/colorgradient.cpp' */
17077 
17078 /* including file 'src/selectiondecorator-bracket.cpp', size 12313 */
17079 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
17080 
17084 
17106  : mBracketPen(QPen(Qt::black)),
17107  mBracketBrush(Qt::NoBrush),
17108  mBracketWidth(5),
17109  mBracketHeight(50),
17110  mBracketStyle(bsSquareBracket),
17111  mTangentToData(false),
17112  mTangentAverage(2) {}
17113 
17115 
17121  mBracketPen = pen;
17122 }
17123 
17129  mBracketBrush = brush;
17130 }
17131 
17138  mBracketWidth = width;
17139 }
17140 
17148 }
17149 
17157  mBracketStyle = style;
17158 }
17159 
17168  mTangentToData = enabled;
17169 }
17170 
17180  mTangentAverage = pointCount;
17181  if (mTangentAverage < 1) mTangentAverage = 1;
17182 }
17183 
17199  int direction) const {
17200  switch (mBracketStyle) {
17201  case bsSquareBracket: {
17202  painter->drawLine(QLineF(mBracketWidth * direction,
17203  -mBracketHeight * 0.5, 0,
17204  -mBracketHeight * 0.5));
17205  painter->drawLine(QLineF(mBracketWidth * direction,
17206  mBracketHeight * 0.5, 0,
17207  mBracketHeight * 0.5));
17208  painter->drawLine(
17209  QLineF(0, -mBracketHeight * 0.5, 0, mBracketHeight * 0.5));
17210  break;
17211  }
17212  case bsHalfEllipse: {
17213  painter->drawArc(-mBracketWidth * 0.5, -mBracketHeight * 0.5,
17214  mBracketWidth, mBracketHeight, -90 * 16,
17215  -180 * 16 * direction);
17216  break;
17217  }
17218  case bsEllipse: {
17219  painter->drawEllipse(-mBracketWidth * 0.5, -mBracketHeight * 0.5,
17221  break;
17222  }
17223  case bsPlus: {
17224  painter->drawLine(
17225  QLineF(0, -mBracketHeight * 0.5, 0, mBracketHeight * 0.5));
17226  painter->drawLine(
17227  QLineF(-mBracketWidth * 0.5, 0, mBracketWidth * 0.5, 0));
17228  break;
17229  }
17230  default: {
17231  qDebug() << Q_FUNC_INFO
17232  << "unknown/custom bracket style can't be handeld by "
17233  "default implementation:"
17234  << static_cast<int>(mBracketStyle);
17235  break;
17236  }
17237  }
17238 }
17239 
17249  QCPDataSelection selection) {
17250  if (!mPlottable || selection.isEmpty()) return;
17251 
17252  if (QCPPlottableInterface1D *interface1d = mPlottable->interface1D()) {
17253  foreach (const QCPDataRange &dataRange, selection.dataRanges()) {
17254  // determine position and (if tangent mode is enabled) angle of
17255  // brackets:
17256  int openBracketDir = (mPlottable->keyAxis() &&
17258  ? 1
17259  : -1;
17260  int closeBracketDir = -openBracketDir;
17261  QPointF openBracketPos =
17262  getPixelCoordinates(interface1d, dataRange.begin());
17263  QPointF closeBracketPos =
17264  getPixelCoordinates(interface1d, dataRange.end() - 1);
17265  double openBracketAngle = 0;
17266  double closeBracketAngle = 0;
17267  if (mTangentToData) {
17268  openBracketAngle = getTangentAngle(
17269  interface1d, dataRange.begin(), openBracketDir);
17270  closeBracketAngle = getTangentAngle(
17271  interface1d, dataRange.end() - 1, closeBracketDir);
17272  }
17273  // draw opening bracket:
17274  QTransform oldTransform = painter->transform();
17275  painter->setPen(mBracketPen);
17276  painter->setBrush(mBracketBrush);
17277  painter->translate(openBracketPos);
17278  painter->rotate(openBracketAngle / M_PI * 180.0);
17279  drawBracket(painter, openBracketDir);
17280  painter->setTransform(oldTransform);
17281  // draw closing bracket:
17282  painter->setPen(mBracketPen);
17283  painter->setBrush(mBracketBrush);
17284  painter->translate(closeBracketPos);
17285  painter->rotate(closeBracketAngle / M_PI * 180.0);
17286  drawBracket(painter, closeBracketDir);
17287  painter->setTransform(oldTransform);
17288  }
17289  }
17290 }
17291 
17308  const QCPPlottableInterface1D *interface1d,
17309  int dataIndex,
17310  int direction) const {
17311  if (!interface1d || dataIndex < 0 || dataIndex >= interface1d->dataCount())
17312  return 0;
17313  direction = direction < 0 ? -1 : 1; // enforce direction is either -1 or 1
17314 
17315  // how many steps we can actually go from index in the given direction
17316  // without exceeding data bounds:
17317  int averageCount;
17318  if (direction < 0)
17319  averageCount = qMin(mTangentAverage, dataIndex);
17320  else
17321  averageCount =
17322  qMin(mTangentAverage, interface1d->dataCount() - 1 - dataIndex);
17323  qDebug() << averageCount;
17324  // calculate point average of averageCount points:
17325  QVector<QPointF> points(averageCount);
17326  QPointF pointsAverage;
17327  int currentIndex = dataIndex;
17328  for (int i = 0; i < averageCount; ++i) {
17329  points[i] = getPixelCoordinates(interface1d, currentIndex);
17330  pointsAverage += points[i];
17331  currentIndex += direction;
17332  }
17333  pointsAverage /= (double)averageCount;
17334 
17335  // calculate slope of linear regression through points:
17336  double numSum = 0;
17337  double denomSum = 0;
17338  for (int i = 0; i < averageCount; ++i) {
17339  const double dx = points.at(i).x() - pointsAverage.x();
17340  const double dy = points.at(i).y() - pointsAverage.y();
17341  numSum += dx * dy;
17342  denomSum += dx * dx;
17343  }
17344  if (!qFuzzyIsNull(denomSum) && !qFuzzyIsNull(numSum)) {
17345  return qAtan2(numSum, denomSum);
17346  } else // undetermined angle, probably mTangentAverage == 1, so using only
17347  // one data point
17348  return 0;
17349 }
17350 
17357  const QCPPlottableInterface1D *interface1d, int dataIndex) const {
17358  QCPAxis *keyAxis = mPlottable->keyAxis();
17359  QCPAxis *valueAxis = mPlottable->valueAxis();
17360  if (!keyAxis || !valueAxis) {
17361  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
17362  return QPointF(0, 0);
17363  }
17364 
17365  if (keyAxis->orientation() == Qt::Horizontal)
17366  return QPointF(
17367  keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)),
17368  valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)));
17369  else
17370  return QPointF(
17371  valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)),
17372  keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)));
17373 }
17374 /* end of 'src/selectiondecorator-bracket.cpp' */
17375 
17376 /* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47584 */
17377 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
17378 
17382 
17424 /* start documentation of inline functions */
17425 
17515 /* end documentation of inline functions */
17516 
17521 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes)
17522  : QCPLayoutElement(parentPlot),
17523  mBackgroundBrush(Qt::NoBrush),
17524  mBackgroundScaled(true),
17525  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
17526  mInsetLayout(new QCPLayoutInset),
17527  mRangeDrag(Qt::Horizontal | Qt::Vertical),
17528  mRangeZoom(Qt::Horizontal | Qt::Vertical),
17529  mRangeZoomFactorHorz(0.85),
17530  mRangeZoomFactorVert(0.85),
17531  mDragging(false) {
17534  mInsetLayout->setParent(this);
17535 
17536  setMinimumSize(50, 50);
17537  setMinimumMargins(QMargins(15, 15, 15, 15));
17538  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis *>());
17539  mAxes.insert(QCPAxis::atRight, QList<QCPAxis *>());
17540  mAxes.insert(QCPAxis::atTop, QList<QCPAxis *>());
17541  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis *>());
17542 
17543  if (setupDefaultAxes) {
17544  QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
17545  QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
17546  QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
17547  QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
17548  setRangeDragAxes(xAxis, yAxis);
17549  setRangeZoomAxes(xAxis, yAxis);
17550  xAxis2->setVisible(false);
17551  yAxis2->setVisible(false);
17552  xAxis->grid()->setVisible(true);
17553  yAxis->grid()->setVisible(true);
17554  xAxis2->grid()->setVisible(false);
17555  yAxis2->grid()->setVisible(false);
17556  xAxis2->grid()->setZeroLinePen(Qt::NoPen);
17557  yAxis2->grid()->setZeroLinePen(Qt::NoPen);
17558  xAxis2->grid()->setVisible(false);
17559  yAxis2->grid()->setVisible(false);
17560  }
17561 }
17562 
17564  delete mInsetLayout;
17565  mInsetLayout = 0;
17566 
17567  QList<QCPAxis *> axesList = axes();
17568  for (int i = 0; i < axesList.size(); ++i) removeAxis(axesList.at(i));
17569 }
17570 
17577  return mAxes.value(type).size();
17578 }
17579 
17587  QList<QCPAxis *> ax(mAxes.value(type));
17588  if (index >= 0 && index < ax.size()) {
17589  return ax.at(index);
17590  } else {
17591  qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
17592  return 0;
17593  }
17594 }
17595 
17604 QList<QCPAxis *> QCPAxisRect::axes(QCPAxis::AxisTypes types) const {
17605  QList<QCPAxis *> result;
17606  if (types.testFlag(QCPAxis::atLeft)) result << mAxes.value(QCPAxis::atLeft);
17607  if (types.testFlag(QCPAxis::atRight))
17608  result << mAxes.value(QCPAxis::atRight);
17609  if (types.testFlag(QCPAxis::atTop)) result << mAxes.value(QCPAxis::atTop);
17610  if (types.testFlag(QCPAxis::atBottom))
17611  result << mAxes.value(QCPAxis::atBottom);
17612  return result;
17613 }
17614 
17619 QList<QCPAxis *> QCPAxisRect::axes() const {
17620  QList<QCPAxis *> result;
17621  QHashIterator<QCPAxis::AxisType, QList<QCPAxis *>> it(mAxes);
17622  while (it.hasNext()) {
17623  it.next();
17624  result << it.value();
17625  }
17626  return result;
17627 }
17628 
17654  QCPAxis *newAxis = axis;
17655  if (!newAxis) {
17656  newAxis = new QCPAxis(this, type);
17657  } else // user provided existing axis instance, do some sanity checks
17658  {
17659  if (newAxis->axisType() != type) {
17660  qDebug() << Q_FUNC_INFO
17661  << "passed axis has different axis type than specified in "
17662  "type parameter";
17663  return 0;
17664  }
17665  if (newAxis->axisRect() != this) {
17666  qDebug() << Q_FUNC_INFO
17667  << "passed axis doesn't have this axis rect as parent "
17668  "axis rect";
17669  return 0;
17670  }
17671  if (axes().contains(newAxis)) {
17672  qDebug() << Q_FUNC_INFO
17673  << "passed axis is already owned by this axis rect";
17674  return 0;
17675  }
17676  }
17677  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis
17678  // ending to additional axes with offset
17679  {
17680  bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
17681  newAxis->setLowerEnding(
17682  QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
17683  newAxis->setUpperEnding(
17684  QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
17685  }
17686  mAxes[type].append(newAxis);
17687 
17688  // reset convenience axis pointers on parent QCustomPlot if they are unset:
17689  if (mParentPlot && mParentPlot->axisRectCount() > 0 &&
17690  mParentPlot->axisRect(0) == this) {
17691  switch (type) {
17692  case QCPAxis::atBottom: {
17693  if (!mParentPlot->xAxis) mParentPlot->xAxis = newAxis;
17694  break;
17695  }
17696  case QCPAxis::atLeft: {
17697  if (!mParentPlot->yAxis) mParentPlot->yAxis = newAxis;
17698  break;
17699  }
17700  case QCPAxis::atTop: {
17701  if (!mParentPlot->xAxis2) mParentPlot->xAxis2 = newAxis;
17702  break;
17703  }
17704  case QCPAxis::atRight: {
17705  if (!mParentPlot->yAxis2) mParentPlot->yAxis2 = newAxis;
17706  break;
17707  }
17708  }
17709  }
17710 
17711  return newAxis;
17712 }
17713 
17723 QList<QCPAxis *> QCPAxisRect::addAxes(QCPAxis::AxisTypes types) {
17724  QList<QCPAxis *> result;
17725  if (types.testFlag(QCPAxis::atLeft)) result << addAxis(QCPAxis::atLeft);
17726  if (types.testFlag(QCPAxis::atRight)) result << addAxis(QCPAxis::atRight);
17727  if (types.testFlag(QCPAxis::atTop)) result << addAxis(QCPAxis::atTop);
17728  if (types.testFlag(QCPAxis::atBottom)) result << addAxis(QCPAxis::atBottom);
17729  return result;
17730 }
17731 
17740  // don't access axis->axisType() to provide safety when axis is an invalid
17741  // pointer, rather go through all axis containers:
17742  QHashIterator<QCPAxis::AxisType, QList<QCPAxis *>> it(mAxes);
17743  while (it.hasNext()) {
17744  it.next();
17745  if (it.value().contains(axis)) {
17746  if (it.value().first() == axis &&
17747  it.value().size() >
17748  1) // if removing first axis, transfer axis offset to
17749  // the new first axis (which at this point is the
17750  // second axis, if it exists)
17751  it.value()[1]->setOffset(axis->offset());
17752  mAxes[it.key()].removeOne(axis);
17753  if (qobject_cast<QCustomPlot *>(
17754  parentPlot())) // make sure this isn't called from
17755  // QObject dtor when QCustomPlot is
17756  // already destructed (happens when the
17757  // axis rect is not in any layout and
17758  // thus QObject-child of QCustomPlot)
17760  delete axis;
17761  return true;
17762  }
17763  }
17764  qDebug() << Q_FUNC_INFO
17765  << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
17766  return false;
17767 }
17768 
17778 void QCPAxisRect::zoom(const QRectF &pixelRect) { zoom(pixelRect, axes()); }
17779 
17790 void QCPAxisRect::zoom(const QRectF &pixelRect,
17791  const QList<QCPAxis *> &affectedAxes) {
17792  foreach (QCPAxis *axis, affectedAxes) {
17793  if (!axis) {
17794  qDebug() << Q_FUNC_INFO << "a passed axis was zero";
17795  continue;
17796  }
17797  QCPRange pixelRange;
17798  if (axis->orientation() == Qt::Horizontal)
17799  pixelRange = QCPRange(pixelRect.left(), pixelRect.right());
17800  else
17801  pixelRange = QCPRange(pixelRect.top(), pixelRect.bottom());
17802  axis->setRange(axis->pixelToCoord(pixelRange.lower),
17803  axis->pixelToCoord(pixelRange.upper));
17804  }
17805 }
17806 
17828 void QCPAxisRect::setupFullAxesBox(bool connectRanges) {
17829  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
17830  if (axisCount(QCPAxis::atBottom) == 0)
17831  xAxis = addAxis(QCPAxis::atBottom);
17832  else
17833  xAxis = axis(QCPAxis::atBottom);
17834 
17835  if (axisCount(QCPAxis::atLeft) == 0)
17836  yAxis = addAxis(QCPAxis::atLeft);
17837  else
17838  yAxis = axis(QCPAxis::atLeft);
17839 
17840  if (axisCount(QCPAxis::atTop) == 0)
17841  xAxis2 = addAxis(QCPAxis::atTop);
17842  else
17843  xAxis2 = axis(QCPAxis::atTop);
17844 
17845  if (axisCount(QCPAxis::atRight) == 0)
17846  yAxis2 = addAxis(QCPAxis::atRight);
17847  else
17848  yAxis2 = axis(QCPAxis::atRight);
17849 
17850  xAxis->setVisible(true);
17851  yAxis->setVisible(true);
17852  xAxis2->setVisible(true);
17853  yAxis2->setVisible(true);
17854  xAxis2->setTickLabels(false);
17855  yAxis2->setTickLabels(false);
17856 
17857  xAxis2->setRange(xAxis->range());
17858  xAxis2->setRangeReversed(xAxis->rangeReversed());
17859  xAxis2->setScaleType(xAxis->scaleType());
17860  xAxis2->setTicks(xAxis->ticks());
17861  xAxis2->setNumberFormat(xAxis->numberFormat());
17862  xAxis2->setNumberPrecision(xAxis->numberPrecision());
17863  xAxis2->ticker()->setTickCount(xAxis->ticker()->tickCount());
17864  xAxis2->ticker()->setTickOrigin(xAxis->ticker()->tickOrigin());
17865 
17866  yAxis2->setRange(yAxis->range());
17867  yAxis2->setRangeReversed(yAxis->rangeReversed());
17868  yAxis2->setScaleType(yAxis->scaleType());
17869  yAxis2->setTicks(yAxis->ticks());
17870  yAxis2->setNumberFormat(yAxis->numberFormat());
17871  yAxis2->setNumberPrecision(yAxis->numberPrecision());
17872  yAxis2->ticker()->setTickCount(yAxis->ticker()->tickCount());
17873  yAxis2->ticker()->setTickOrigin(yAxis->ticker()->tickOrigin());
17874 
17875  if (connectRanges) {
17876  connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2,
17877  SLOT(setRange(QCPRange)));
17878  connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2,
17879  SLOT(setRange(QCPRange)));
17880  }
17881 }
17882 
17891 QList<QCPAbstractPlottable *> QCPAxisRect::plottables() const {
17892  // Note: don't append all QCPAxis::plottables() into a list, because we
17893  // might get duplicate entries
17894  QList<QCPAbstractPlottable *> result;
17895  for (int i = 0; i < mParentPlot->mPlottables.size(); ++i) {
17896  if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||
17897  mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
17898  result.append(mParentPlot->mPlottables.at(i));
17899  }
17900  return result;
17901 }
17902 
17911 QList<QCPGraph *> QCPAxisRect::graphs() const {
17912  // Note: don't append all QCPAxis::graphs() into a list, because we might
17913  // get duplicate entries
17914  QList<QCPGraph *> result;
17915  for (int i = 0; i < mParentPlot->mGraphs.size(); ++i) {
17916  if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this ||
17917  mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
17918  result.append(mParentPlot->mGraphs.at(i));
17919  }
17920  return result;
17921 }
17922 
17934 QList<QCPAbstractItem *> QCPAxisRect::items() const {
17935  // Note: don't just append all QCPAxis::items() into a list, because we
17936  // might get duplicate entries
17937  // and miss those items that have this axis rect as clipAxisRect.
17938  QList<QCPAbstractItem *> result;
17939  for (int itemId = 0; itemId < mParentPlot->mItems.size(); ++itemId) {
17940  if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this) {
17941  result.append(mParentPlot->mItems.at(itemId));
17942  continue;
17943  }
17944  QList<QCPItemPosition *> positions =
17945  mParentPlot->mItems.at(itemId)->positions();
17946  for (int posId = 0; posId < positions.size(); ++posId) {
17947  if (positions.at(posId)->axisRect() == this ||
17948  positions.at(posId)->keyAxis()->axisRect() == this ||
17949  positions.at(posId)->valueAxis()->axisRect() == this) {
17950  result.append(mParentPlot->mItems.at(itemId));
17951  break;
17952  }
17953  }
17954  }
17955  return result;
17956 }
17957 
17969  QCPLayoutElement::update(phase);
17970 
17971  switch (phase) {
17972  case upPreparation: {
17973  QList<QCPAxis *> allAxes = axes();
17974  for (int i = 0; i < allAxes.size(); ++i)
17975  allAxes.at(i)->setupTickVectors();
17976  break;
17977  }
17978  case upLayout: {
17980  break;
17981  }
17982  default:
17983  break;
17984  }
17985 
17986  // pass update call on to inset layout (doesn't happen automatically,
17987  // because QCPAxisRect doesn't derive from QCPLayout):
17988  mInsetLayout->update(phase);
17989 }
17990 
17991 /* inherits documentation from base class */
17992 QList<QCPLayoutElement *> QCPAxisRect::elements(bool recursive) const {
17993  QList<QCPLayoutElement *> result;
17994  if (mInsetLayout) {
17995  result << mInsetLayout;
17996  if (recursive) result << mInsetLayout->elements(recursive);
17997  }
17998  return result;
17999 }
18000 
18001 /* inherits documentation from base class */
18003  painter->setAntialiasing(false);
18004 }
18005 
18006 /* inherits documentation from base class */
18007 void QCPAxisRect::draw(QCPPainter *painter) { drawBackground(painter); }
18008 
18027 void QCPAxisRect::setBackground(const QPixmap &pm) {
18028  mBackgroundPixmap = pm;
18029  mScaledBackgroundPixmap = QPixmap();
18030 }
18031 
18045 void QCPAxisRect::setBackground(const QBrush &brush) {
18046  mBackgroundBrush = brush;
18047 }
18048 
18057 void QCPAxisRect::setBackground(const QPixmap &pm,
18058  bool scaled,
18059  Qt::AspectRatioMode mode) {
18060  mBackgroundPixmap = pm;
18061  mScaledBackgroundPixmap = QPixmap();
18062  mBackgroundScaled = scaled;
18063  mBackgroundScaledMode = mode;
18064 }
18065 
18079  mBackgroundScaled = scaled;
18080 }
18081 
18088 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode) {
18089  mBackgroundScaledMode = mode;
18090 }
18091 
18099 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation) {
18100  if (orientation == Qt::Horizontal)
18101  return mRangeDragHorzAxis.isEmpty() ? 0
18102  : mRangeDragHorzAxis.first().data();
18103  else
18104  return mRangeDragVertAxis.isEmpty() ? 0
18105  : mRangeDragVertAxis.first().data();
18106 }
18107 
18115 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation) {
18116  if (orientation == Qt::Horizontal)
18117  return mRangeZoomHorzAxis.isEmpty() ? 0
18118  : mRangeZoomHorzAxis.first().data();
18119  else
18120  return mRangeZoomVertAxis.isEmpty() ? 0
18121  : mRangeZoomVertAxis.first().data();
18122 }
18123 
18129 QList<QCPAxis *> QCPAxisRect::rangeDragAxes(Qt::Orientation orientation) {
18130  QList<QCPAxis *> result;
18131  if (orientation == Qt::Horizontal) {
18132  for (int i = 0; i < mRangeDragHorzAxis.size(); ++i) {
18133  if (!mRangeDragHorzAxis.at(i).isNull())
18134  result.append(mRangeDragHorzAxis.at(i).data());
18135  }
18136  } else {
18137  for (int i = 0; i < mRangeDragVertAxis.size(); ++i) {
18138  if (!mRangeDragVertAxis.at(i).isNull())
18139  result.append(mRangeDragVertAxis.at(i).data());
18140  }
18141  }
18142  return result;
18143 }
18144 
18150 QList<QCPAxis *> QCPAxisRect::rangeZoomAxes(Qt::Orientation orientation) {
18151  QList<QCPAxis *> result;
18152  if (orientation == Qt::Horizontal) {
18153  for (int i = 0; i < mRangeZoomHorzAxis.size(); ++i) {
18154  if (!mRangeZoomHorzAxis.at(i).isNull())
18155  result.append(mRangeZoomHorzAxis.at(i).data());
18156  }
18157  } else {
18158  for (int i = 0; i < mRangeZoomVertAxis.size(); ++i) {
18159  if (!mRangeZoomVertAxis.at(i).isNull())
18160  result.append(mRangeZoomVertAxis.at(i).data());
18161  }
18162  }
18163  return result;
18164 }
18165 
18171 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation) {
18172  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz
18174 }
18175 
18194 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations) {
18195  mRangeDrag = orientations;
18196 }
18197 
18216 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations) {
18217  mRangeZoom = orientations;
18218 }
18219 
18231 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical) {
18232  QList<QCPAxis *> horz, vert;
18233  if (horizontal) horz.append(horizontal);
18234  if (vertical) vert.append(vertical);
18235  setRangeDragAxes(horz, vert);
18236 }
18237 
18248 void QCPAxisRect::setRangeDragAxes(QList<QCPAxis *> axes) {
18249  QList<QCPAxis *> horz, vert;
18250  foreach (QCPAxis *ax, axes) {
18251  if (ax->orientation() == Qt::Horizontal)
18252  horz.append(ax);
18253  else
18254  vert.append(ax);
18255  }
18256  setRangeDragAxes(horz, vert);
18257 }
18258 
18265 void QCPAxisRect::setRangeDragAxes(QList<QCPAxis *> horizontal,
18266  QList<QCPAxis *> vertical) {
18267  mRangeDragHorzAxis.clear();
18268  foreach (QCPAxis *ax, horizontal) {
18269  QPointer<QCPAxis> axPointer(ax);
18270  if (!axPointer.isNull())
18271  mRangeDragHorzAxis.append(axPointer);
18272  else
18273  qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:"
18274  << reinterpret_cast<quintptr>(ax);
18275  }
18276  mRangeDragVertAxis.clear();
18277  foreach (QCPAxis *ax, vertical) {
18278  QPointer<QCPAxis> axPointer(ax);
18279  if (!axPointer.isNull())
18280  mRangeDragVertAxis.append(axPointer);
18281  else
18282  qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:"
18283  << reinterpret_cast<quintptr>(ax);
18284  }
18285 }
18286 
18301 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical) {
18302  QList<QCPAxis *> horz, vert;
18303  if (horizontal) horz.append(horizontal);
18304  if (vertical) vert.append(vertical);
18305  setRangeZoomAxes(horz, vert);
18306 }
18307 
18318 void QCPAxisRect::setRangeZoomAxes(QList<QCPAxis *> axes) {
18319  QList<QCPAxis *> horz, vert;
18320  foreach (QCPAxis *ax, axes) {
18321  if (ax->orientation() == Qt::Horizontal)
18322  horz.append(ax);
18323  else
18324  vert.append(ax);
18325  }
18326  setRangeZoomAxes(horz, vert);
18327 }
18328 
18335 void QCPAxisRect::setRangeZoomAxes(QList<QCPAxis *> horizontal,
18336  QList<QCPAxis *> vertical) {
18337  mRangeZoomHorzAxis.clear();
18338  foreach (QCPAxis *ax, horizontal) {
18339  QPointer<QCPAxis> axPointer(ax);
18340  if (!axPointer.isNull())
18341  mRangeZoomHorzAxis.append(axPointer);
18342  else
18343  qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:"
18344  << reinterpret_cast<quintptr>(ax);
18345  }
18346  mRangeZoomVertAxis.clear();
18347  foreach (QCPAxis *ax, vertical) {
18348  QPointer<QCPAxis> axPointer(ax);
18349  if (!axPointer.isNull())
18350  mRangeZoomVertAxis.append(axPointer);
18351  else
18352  qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:"
18353  << reinterpret_cast<quintptr>(ax);
18354  }
18355 }
18356 
18368 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor,
18369  double verticalFactor) {
18370  mRangeZoomFactorHorz = horizontalFactor;
18371  mRangeZoomFactorVert = verticalFactor;
18372 }
18373 
18378 void QCPAxisRect::setRangeZoomFactor(double factor) {
18379  mRangeZoomFactorHorz = factor;
18380  mRangeZoomFactorVert = factor;
18381 }
18382 
18404  // draw background fill:
18405  if (mBackgroundBrush != Qt::NoBrush)
18406  painter->fillRect(mRect, mBackgroundBrush);
18407 
18408  // draw background pixmap (on top of fill, if brush specified):
18409  if (!mBackgroundPixmap.isNull()) {
18410  if (mBackgroundScaled) {
18411  // check whether mScaledBackground needs to be updated:
18412  QSize scaledSize(mBackgroundPixmap.size());
18413  scaledSize.scale(mRect.size(), mBackgroundScaledMode);
18414  if (mScaledBackgroundPixmap.size() != scaledSize)
18416  mRect.size(), mBackgroundScaledMode,
18417  Qt::SmoothTransformation);
18418  painter->drawPixmap(mRect.topLeft() + QPoint(0, -1),
18420  QRect(0, 0, mRect.width(), mRect.height()) &
18421  mScaledBackgroundPixmap.rect());
18422  } else {
18423  painter->drawPixmap(mRect.topLeft() + QPoint(0, -1),
18425  QRect(0, 0, mRect.width(), mRect.height()));
18426  }
18427  }
18428 }
18429 
18442  const QList<QCPAxis *> axesList = mAxes.value(type);
18443  if (axesList.isEmpty()) return;
18444 
18445  bool isFirstVisible =
18446  !axesList.first()
18447  ->visible(); // if the first axis is visible, the second
18448  // axis (which is where the loop starts)
18449  // isn't the first visible axis, so
18450  // initialize with false
18451  for (int i = 1; i < axesList.size(); ++i) {
18452  int offset = axesList.at(i - 1)->offset() +
18453  axesList.at(i - 1)->calculateMargin();
18454  if (axesList.at(i)->visible()) // only add inner tick length to offset
18455  // if this axis is visible and it's not
18456  // the first visible one (might happen
18457  // if true first axis is invisible)
18458  {
18459  if (!isFirstVisible) offset += axesList.at(i)->tickLengthIn();
18460  isFirstVisible = false;
18461  }
18462  axesList.at(i)->setOffset(offset);
18463  }
18464 }
18465 
18466 /* inherits documentation from base class */
18468  if (!mAutoMargins.testFlag(side))
18469  qDebug() << Q_FUNC_INFO
18470  << "Called with side that isn't specified as auto margin";
18471 
18473 
18474  // note: only need to look at the last (outer most) axis to determine the
18475  // total margin, due to updateAxisOffset call
18476  const QList<QCPAxis *> axesList =
18477  mAxes.value(QCPAxis::marginSideToAxisType(side));
18478  if (axesList.size() > 0)
18479  return axesList.last()->offset() + axesList.last()->calculateMargin();
18480  else
18481  return 0;
18482 }
18483 
18496  if (mParentPlot && mParentPlot->axisRectCount() > 0 &&
18497  mParentPlot->axisRect(0) == this) {
18506  }
18507 }
18508 
18521 void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details) {
18522  Q_UNUSED(details)
18523  if (event->buttons() & Qt::LeftButton) {
18524  mDragging = true;
18525  // initialize antialiasing backup in case we start dragging:
18529  }
18530  // Mouse range dragging interaction:
18531  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) {
18532  mDragStartHorzRange.clear();
18533  for (int i = 0; i < mRangeDragHorzAxis.size(); ++i)
18534  mDragStartHorzRange.append(
18535  mRangeDragHorzAxis.at(i).isNull()
18536  ? QCPRange()
18537  : mRangeDragHorzAxis.at(i)->range());
18538  mDragStartVertRange.clear();
18539  for (int i = 0; i < mRangeDragVertAxis.size(); ++i)
18540  mDragStartVertRange.append(
18541  mRangeDragVertAxis.at(i).isNull()
18542  ? QCPRange()
18543  : mRangeDragVertAxis.at(i)->range());
18544  }
18545  }
18546 }
18547 
18556 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) {
18557  Q_UNUSED(startPos)
18558  // Mouse range dragging interaction:
18559  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag)) {
18560  if (mRangeDrag.testFlag(Qt::Horizontal)) {
18561  for (int i = 0; i < mRangeDragHorzAxis.size(); ++i) {
18562  QCPAxis *ax = mRangeDragHorzAxis.at(i).data();
18563  if (!ax) continue;
18564  if (i >= mDragStartHorzRange.size()) break;
18565  if (ax->mScaleType == QCPAxis::stLinear) {
18566  double diff = ax->pixelToCoord(startPos.x()) -
18567  ax->pixelToCoord(event->pos().x());
18568  ax->setRange(mDragStartHorzRange.at(i).lower + diff,
18569  mDragStartHorzRange.at(i).upper + diff);
18570  } else if (ax->mScaleType == QCPAxis::stLogarithmic) {
18571  double diff = ax->pixelToCoord(startPos.x()) /
18572  ax->pixelToCoord(event->pos().x());
18573  ax->setRange(mDragStartHorzRange.at(i).lower * diff,
18574  mDragStartHorzRange.at(i).upper * diff);
18575  }
18576  }
18577  }
18578 
18579  if (mRangeDrag.testFlag(Qt::Vertical)) {
18580  for (int i = 0; i < mRangeDragVertAxis.size(); ++i) {
18581  QCPAxis *ax = mRangeDragVertAxis.at(i).data();
18582  if (!ax) continue;
18583  if (i >= mDragStartVertRange.size()) break;
18584  if (ax->mScaleType == QCPAxis::stLinear) {
18585  double diff = ax->pixelToCoord(startPos.y()) -
18586  ax->pixelToCoord(event->pos().y());
18587  ax->setRange(mDragStartVertRange.at(i).lower + diff,
18588  mDragStartVertRange.at(i).upper + diff);
18589  } else if (ax->mScaleType == QCPAxis::stLogarithmic) {
18590  double diff = ax->pixelToCoord(startPos.y()) /
18591  ax->pixelToCoord(event->pos().y());
18592  ax->setRange(mDragStartVertRange.at(i).lower * diff,
18593  mDragStartVertRange.at(i).upper * diff);
18594  }
18595  }
18596  }
18597 
18598  if (mRangeDrag != 0) // if either vertical or horizontal drag was
18599  // enabled, do a replot
18600  {
18604  }
18605  }
18606 }
18607 
18608 /* inherits documentation from base class */
18610  const QPointF &startPos) {
18611  Q_UNUSED(event)
18612  Q_UNUSED(startPos)
18613  mDragging = false;
18617  }
18618 }
18619 
18637 void QCPAxisRect::wheelEvent(QWheelEvent *event) {
18638  const double delta = qtCompatWheelEventDelta(event);
18639  const QPointF pos = qtCompatWheelEventPos(event);
18640 
18641  // Mouse range zooming interaction:
18642  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom)) {
18643  if (mRangeZoom != 0) {
18644  double factor;
18645  double wheelSteps =
18646  delta / 120.0; // a single step delta is +/-120 usually
18647  if (mRangeZoom.testFlag(Qt::Horizontal)) {
18648  factor = qPow(mRangeZoomFactorHorz, wheelSteps);
18649  foreach (QPointer<QCPAxis> axis, mRangeZoomHorzAxis) {
18650  if (!axis.isNull())
18651  axis->scaleRange(factor, axis->pixelToCoord(pos.x()));
18652  }
18653  }
18654  if (mRangeZoom.testFlag(Qt::Vertical)) {
18655  factor = qPow(mRangeZoomFactorVert, wheelSteps);
18656  foreach (QPointer<QCPAxis> axis, mRangeZoomVertAxis) {
18657  if (!axis.isNull())
18658  axis->scaleRange(factor, axis->pixelToCoord(pos.y()));
18659  }
18660  }
18661  mParentPlot->replot();
18662  }
18663  }
18664 }
18665 /* end of 'src/layoutelements/layoutelement-axisrect.cpp' */
18666 
18667 /* including file 'src/layoutelements/layoutelement-legend.cpp', size 31153 */
18668 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
18669 
18673 
18700 /* start of documentation of signals */
18701 
18708 /* end of documentation of signals */
18709 
18716  : QCPLayoutElement(parent->parentPlot()),
18717  mParentLegend(parent),
18718  mFont(parent->font()),
18719  mTextColor(parent->textColor()),
18720  mSelectedFont(parent->selectedFont()),
18721  mSelectedTextColor(parent->selectedTextColor()),
18722  mSelectable(true),
18723  mSelected(false) {
18724  setLayer(QLatin1String("legend"));
18725  setMargins(QMargins(0, 0, 0, 0));
18726 }
18727 
18733 void QCPAbstractLegendItem::setFont(const QFont &font) { mFont = font; }
18734 
18741  mTextColor = color;
18742 }
18743 
18751  mSelectedFont = font;
18752 }
18753 
18762 }
18763 
18770  if (mSelectable != selectable) {
18773  }
18774 }
18775 
18785  if (mSelected != selected) {
18786  mSelected = selected;
18788  }
18789 }
18790 
18791 /* inherits documentation from base class */
18792 double QCPAbstractLegendItem::selectTest(const QPointF &pos,
18793  bool onlySelectable,
18794  QVariant *details) const {
18795  Q_UNUSED(details)
18796  if (!mParentPlot) return -1;
18797  if (onlySelectable &&
18798  (!mSelectable ||
18800  return -1;
18801 
18802  if (mRect.contains(pos.toPoint()))
18803  return mParentPlot->selectionTolerance() * 0.99;
18804  else
18805  return -1;
18806 }
18807 
18808 /* inherits documentation from base class */
18810  QCPPainter *painter) const {
18812 }
18813 
18814 /* inherits documentation from base class */
18816 
18817 /* inherits documentation from base class */
18819  bool additive,
18820  const QVariant &details,
18821  bool *selectionStateChanged) {
18822  Q_UNUSED(event)
18823  Q_UNUSED(details)
18824  if (mSelectable &&
18826  bool selBefore = mSelected;
18827  setSelected(additive ? !mSelected : true);
18828  if (selectionStateChanged)
18829  *selectionStateChanged = mSelected != selBefore;
18830  }
18831 }
18832 
18833 /* inherits documentation from base class */
18834 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged) {
18835  if (mSelectable &&
18837  bool selBefore = mSelected;
18838  setSelected(false);
18839  if (selectionStateChanged)
18840  *selectionStateChanged = mSelected != selBefore;
18841  }
18842 }
18843 
18847 
18885  QCPAbstractPlottable *plottable)
18886  : QCPAbstractLegendItem(parent), mPlottable(plottable) {
18887  setAntialiased(false);
18888 }
18889 
18898 }
18899 
18907 }
18908 
18915  return mSelected ? mSelectedFont : mFont;
18916 }
18917 
18925  if (!mPlottable) return;
18926  painter->setFont(getFont());
18927  painter->setPen(QPen(getTextColor()));
18928  QSizeF iconSize = mParentLegend->iconSize();
18929  QRectF textRect = painter->fontMetrics().boundingRect(
18930  0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
18931  QRectF iconRect(mRect.topLeft(), iconSize);
18932  int textHeight =
18933  qMax(textRect.height(),
18934  iconSize.height()); // if text has smaller height than icon,
18935  // center text vertically in icon height,
18936  // else align tops
18937  painter->drawText(
18938  mRect.x() + iconSize.width() + mParentLegend->iconTextPadding(),
18939  mRect.y(), textRect.width(), textHeight, Qt::TextDontClip,
18940  mPlottable->name());
18941  // draw icon:
18942  painter->save();
18943  painter->setClipRect(iconRect, Qt::IntersectClip);
18944  mPlottable->drawLegendIcon(painter, iconRect);
18945  painter->restore();
18946  // draw icon border:
18947  if (getIconBorderPen().style() != Qt::NoPen) {
18948  painter->setPen(getIconBorderPen());
18949  painter->setBrush(Qt::NoBrush);
18950  int halfPen = qCeil(painter->pen().widthF() * 0.5) + 1;
18951  painter->setClipRect(mOuterRect.adjusted(
18952  -halfPen, -halfPen, halfPen,
18953  halfPen)); // extend default clip rect so thicker pens
18954  // (especially during selection) are not clipped
18955  painter->drawRect(iconRect);
18956  }
18957 }
18958 
18967  if (!mPlottable) return QSize();
18968  QSize result(0, 0);
18969  QRect textRect;
18970  QFontMetrics fontMetrics(getFont());
18971  QSize iconSize = mParentLegend->iconSize();
18972  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(),
18973  Qt::TextDontClip, mPlottable->name());
18974  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() +
18975  textRect.width());
18976  result.setHeight(qMax(textRect.height(), iconSize.height()));
18977  result.rwidth() += mMargins.left() + mMargins.right();
18978  result.rheight() += mMargins.top() + mMargins.bottom();
18979  return result;
18980 }
18981 
18985 
19026 /* start of documentation of signals */
19027 
19035 /* end of documentation of signals */
19036 
19045  setWrap(0);
19046 
19047  setRowSpacing(3);
19048  setColumnSpacing(8);
19049  setMargins(QMargins(7, 5, 7, 4));
19050  setAntialiased(false);
19051  setIconSize(32, 18);
19052 
19053  setIconTextPadding(7);
19054 
19057 
19058  setBorderPen(QPen(Qt::black, 0));
19059  setSelectedBorderPen(QPen(Qt::blue, 2));
19060  setIconBorderPen(Qt::NoPen);
19066 }
19067 
19069  clearItems();
19070  if (qobject_cast<QCustomPlot *>(
19071  mParentPlot)) // make sure this isn't called from QObject dtor
19072  // when QCustomPlot is already destructed
19073  // (happens when the legend is not in any layout
19074  // and thus QObject-child of QCustomPlot)
19075  mParentPlot->legendRemoved(this);
19076 }
19077 
19078 /* no doc for getter, see setSelectedParts */
19079 QCPLegend::SelectableParts QCPLegend::selectedParts() const {
19080  // check whether any legend elements selected, if yes, add spItems to return
19081  // value
19082  bool hasSelectedItems = false;
19083  for (int i = 0; i < itemCount(); ++i) {
19084  if (item(i) && item(i)->selected()) {
19085  hasSelectedItems = true;
19086  break;
19087  }
19088  }
19089  if (hasSelectedItems)
19090  return mSelectedParts | spItems;
19091  else
19092  return mSelectedParts & ~spItems;
19093 }
19094 
19098 void QCPLegend::setBorderPen(const QPen &pen) { mBorderPen = pen; }
19099 
19103 void QCPLegend::setBrush(const QBrush &brush) { mBrush = brush; }
19104 
19114 void QCPLegend::setFont(const QFont &font) {
19115  mFont = font;
19116  for (int i = 0; i < itemCount(); ++i) {
19117  if (item(i)) item(i)->setFont(mFont);
19118  }
19119 }
19120 
19130 void QCPLegend::setTextColor(const QColor &color) {
19131  mTextColor = color;
19132  for (int i = 0; i < itemCount(); ++i) {
19133  if (item(i)) item(i)->setTextColor(color);
19134  }
19135 }
19136 
19141 void QCPLegend::setIconSize(const QSize &size) { mIconSize = size; }
19142 
19146  mIconSize.setWidth(width);
19147  mIconSize.setHeight(height);
19148 }
19149 
19155 void QCPLegend::setIconTextPadding(int padding) { mIconTextPadding = padding; }
19156 
19164 void QCPLegend::setIconBorderPen(const QPen &pen) { mIconBorderPen = pen; }
19165 
19177 void QCPLegend::setSelectableParts(const SelectableParts &selectable) {
19178  if (mSelectableParts != selectable) {
19179  mSelectableParts = selectable;
19181  }
19182 }
19183 
19208 void QCPLegend::setSelectedParts(const SelectableParts &selected) {
19209  SelectableParts newSelected = selected;
19210  mSelectedParts = this->selectedParts(); // update mSelectedParts in case
19211  // item selection changed
19212 
19213  if (mSelectedParts != newSelected) {
19214  if (!mSelectedParts.testFlag(spItems) &&
19215  newSelected.testFlag(
19216  spItems)) // attempt to set spItems flag (can't do that)
19217  {
19218  qDebug() << Q_FUNC_INFO
19219  << "spItems flag can not be set, it can only be unset "
19220  "with this function";
19221  newSelected &= ~spItems;
19222  }
19223  if (mSelectedParts.testFlag(spItems) &&
19224  !newSelected.testFlag(spItems)) // spItems flag was unset, so clear
19225  // item selection
19226  {
19227  for (int i = 0; i < itemCount(); ++i) {
19228  if (item(i)) item(i)->setSelected(false);
19229  }
19230  }
19231  mSelectedParts = newSelected;
19233  }
19234 }
19235 
19242 void QCPLegend::setSelectedBorderPen(const QPen &pen) {
19243  mSelectedBorderPen = pen;
19244 }
19245 
19253  mSelectedIconBorderPen = pen;
19254 }
19255 
19262 void QCPLegend::setSelectedBrush(const QBrush &brush) {
19264 }
19265 
19273 void QCPLegend::setSelectedFont(const QFont &font) {
19274  mSelectedFont = font;
19275  for (int i = 0; i < itemCount(); ++i) {
19276  if (item(i)) item(i)->setSelectedFont(font);
19277  }
19278 }
19279 
19290  for (int i = 0; i < itemCount(); ++i) {
19291  if (item(i)) item(i)->setSelectedTextColor(color);
19292  }
19293 }
19294 
19304  return qobject_cast<QCPAbstractLegendItem *>(elementAt(index));
19305 }
19306 
19314  const QCPAbstractPlottable *plottable) const {
19315  for (int i = 0; i < itemCount(); ++i) {
19316  if (QCPPlottableLegendItem *pli =
19317  qobject_cast<QCPPlottableLegendItem *>(item(i))) {
19318  if (pli->plottable() == plottable) return pli;
19319  }
19320  }
19321  return 0;
19322 }
19323 
19333 int QCPLegend::itemCount() const { return elementCount(); }
19334 
19341  for (int i = 0; i < itemCount(); ++i) {
19342  if (item == this->item(i)) return true;
19343  }
19344  return false;
19345 }
19346 
19355  const QCPAbstractPlottable *plottable) const {
19356  return itemWithPlottable(plottable);
19357 }
19358 
19372  return addElement(item);
19373 }
19374 
19389 bool QCPLegend::removeItem(int index) {
19390  if (QCPAbstractLegendItem *ali = item(index)) {
19391  bool success = remove(ali);
19392  if (success)
19394  true); // gets rid of empty cell by reordering
19395  return success;
19396  } else
19397  return false;
19398 }
19399 
19414  bool success = remove(item);
19415  if (success)
19417  true); // gets rid of empty cell by reordering
19418  return success;
19419 }
19420 
19425  for (int i = itemCount() - 1; i >= 0; --i) removeItem(i);
19426 }
19427 
19434 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const {
19435  QList<QCPAbstractLegendItem *> result;
19436  for (int i = 0; i < itemCount(); ++i) {
19437  if (QCPAbstractLegendItem *ali = item(i)) {
19438  if (ali->selected()) result.append(ali);
19439  }
19440  }
19441  return result;
19442 }
19443 
19462 }
19463 
19470  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen
19471  : mBorderPen;
19472 }
19473 
19479 QBrush QCPLegend::getBrush() const {
19480  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
19481 }
19482 
19488 void QCPLegend::draw(QCPPainter *painter) {
19489  // draw background rect:
19490  painter->setBrush(getBrush());
19491  painter->setPen(getBorderPen());
19492  painter->drawRect(mOuterRect);
19493 }
19494 
19495 /* inherits documentation from base class */
19496 double QCPLegend::selectTest(const QPointF &pos,
19497  bool onlySelectable,
19498  QVariant *details) const {
19499  if (!mParentPlot) return -1;
19500  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox)) return -1;
19501 
19502  if (mOuterRect.contains(pos.toPoint())) {
19503  if (details) details->setValue(spLegendBox);
19504  return mParentPlot->selectionTolerance() * 0.99;
19505  }
19506  return -1;
19507 }
19508 
19509 /* inherits documentation from base class */
19510 void QCPLegend::selectEvent(QMouseEvent *event,
19511  bool additive,
19512  const QVariant &details,
19513  bool *selectionStateChanged) {
19514  Q_UNUSED(event)
19515  mSelectedParts = selectedParts(); // in case item selection has changed
19516  if (details.value<SelectablePart>() == spLegendBox &&
19517  mSelectableParts.testFlag(spLegendBox)) {
19518  SelectableParts selBefore = mSelectedParts;
19520  additive ? mSelectedParts ^ spLegendBox
19521  : mSelectedParts |
19522  spLegendBox); // no need to unset spItems in
19523  // !additive case, because
19524  // they will be deselected by
19525  // QCustomPlot (they're normal
19526  // QCPLayerables with own
19527  // deselectEvent)
19528  if (selectionStateChanged)
19529  *selectionStateChanged = mSelectedParts != selBefore;
19530  }
19531 }
19532 
19533 /* inherits documentation from base class */
19534 void QCPLegend::deselectEvent(bool *selectionStateChanged) {
19535  mSelectedParts = selectedParts(); // in case item selection has changed
19536  if (mSelectableParts.testFlag(spLegendBox)) {
19537  SelectableParts selBefore = mSelectedParts;
19539  if (selectionStateChanged)
19540  *selectionStateChanged = mSelectedParts != selBefore;
19541  }
19542 }
19543 
19544 /* inherits documentation from base class */
19546  return QCP::iSelectLegend;
19547 }
19548 
19549 /* inherits documentation from base class */
19551  return QCP::iSelectLegend;
19552 }
19553 
19554 /* inherits documentation from base class */
19556  if (parentPlot && !parentPlot->legend) parentPlot->legend = this;
19557 }
19558 /* end of 'src/layoutelements/layoutelement-legend.cpp' */
19559 
19560 /* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12761
19561  */
19562 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
19563 
19567 
19579 /* start documentation of signals */
19580 
19603 /* end documentation of signals */
19604 
19611  : QCPLayoutElement(parentPlot),
19612  mText(),
19613  mTextFlags(Qt::AlignCenter | Qt::TextWordWrap),
19614  mFont(QFont(
19615  QLatin1String("sans serif"),
19616  12)), // will be taken from parentPlot if available, see below
19617  mTextColor(Qt::black),
19618  mSelectedFont(QFont(
19619  QLatin1String("sans serif"),
19620  12)), // will be taken from parentPlot if available, see below
19621  mSelectedTextColor(Qt::blue),
19622  mSelectable(false),
19623  mSelected(false) {
19624  if (parentPlot) {
19625  mFont = parentPlot->font();
19626  mSelectedFont = parentPlot->font();
19627  }
19628  setMargins(QMargins(2, 2, 2, 2));
19629 }
19630 
19637 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text)
19638  : QCPLayoutElement(parentPlot),
19639  mText(text),
19640  mTextFlags(Qt::AlignCenter | Qt::TextWordWrap),
19641  mFont(QFont(
19642  QLatin1String("sans serif"),
19643  12)), // will be taken from parentPlot if available, see below
19644  mTextColor(Qt::black),
19645  mSelectedFont(QFont(
19646  QLatin1String("sans serif"),
19647  12)), // will be taken from parentPlot if available, see below
19648  mSelectedTextColor(Qt::blue),
19649  mSelectable(false),
19650  mSelected(false) {
19651  if (parentPlot) {
19652  mFont = parentPlot->font();
19653  mSelectedFont = parentPlot->font();
19654  }
19655  setMargins(QMargins(2, 2, 2, 2));
19656 }
19657 
19665  const QString &text,
19666  double pointSize)
19667  : QCPLayoutElement(parentPlot),
19668  mText(text),
19669  mTextFlags(Qt::AlignCenter | Qt::TextWordWrap),
19670  mFont(QFont(QLatin1String("sans serif"),
19671  pointSize)), // will be taken from parentPlot if available,
19672  // see below
19673  mTextColor(Qt::black),
19674  mSelectedFont(QFont(QLatin1String("sans serif"),
19675  pointSize)), // will be taken from parentPlot if
19676  // available, see below
19677  mSelectedTextColor(Qt::blue),
19678  mSelectable(false),
19679  mSelected(false) {
19680  if (parentPlot) {
19681  mFont = parentPlot->font();
19682  mFont.setPointSizeF(pointSize);
19683  mSelectedFont = parentPlot->font();
19684  mSelectedFont.setPointSizeF(pointSize);
19685  }
19686  setMargins(QMargins(2, 2, 2, 2));
19687 }
19688 
19697  const QString &text,
19698  const QString &fontFamily,
19699  double pointSize)
19700  : QCPLayoutElement(parentPlot),
19701  mText(text),
19702  mTextFlags(Qt::AlignCenter | Qt::TextWordWrap),
19703  mFont(QFont(fontFamily, pointSize)),
19704  mTextColor(Qt::black),
19705  mSelectedFont(QFont(fontFamily, pointSize)),
19706  mSelectedTextColor(Qt::blue),
19707  mSelectable(false),
19708  mSelected(false) {
19709  setMargins(QMargins(2, 2, 2, 2));
19710 }
19711 
19719  const QString &text,
19720  const QFont &font)
19721  : QCPLayoutElement(parentPlot),
19722  mText(text),
19723  mTextFlags(Qt::AlignCenter | Qt::TextWordWrap),
19724  mFont(font),
19725  mTextColor(Qt::black),
19726  mSelectedFont(font),
19727  mSelectedTextColor(Qt::blue),
19728  mSelectable(false),
19729  mSelected(false) {
19730  setMargins(QMargins(2, 2, 2, 2));
19731 }
19732 
19739 void QCPTextElement::setText(const QString &text) { mText = text; }
19740 
19761 void QCPTextElement::setTextFlags(int flags) { mTextFlags = flags; }
19762 
19768 void QCPTextElement::setFont(const QFont &font) { mFont = font; }
19769 
19776 
19783 void QCPTextElement::setSelectedFont(const QFont &font) {
19784  mSelectedFont = font;
19785 }
19786 
19795 }
19796 
19803 void QCPTextElement::setSelectable(bool selectable) {
19804  if (mSelectable != selectable) {
19807  }
19808 }
19809 
19817 void QCPTextElement::setSelected(bool selected) {
19818  if (mSelected != selected) {
19819  mSelected = selected;
19821  }
19822 }
19823 
19824 /* inherits documentation from base class */
19827 }
19828 
19829 /* inherits documentation from base class */
19831  painter->setFont(mainFont());
19832  painter->setPen(QPen(mainTextColor()));
19833  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
19834 }
19835 
19836 /* inherits documentation from base class */
19838  QFontMetrics metrics(mFont);
19839  QSize result(
19840  metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size());
19841  result.rwidth() += mMargins.left() + mMargins.right();
19842  result.rheight() += mMargins.top() + mMargins.bottom();
19843  return result;
19844 }
19845 
19846 /* inherits documentation from base class */
19848  QFontMetrics metrics(mFont);
19849  QSize result(
19850  metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size());
19851  result.setWidth(QWIDGETSIZE_MAX);
19852  result.rheight() += mMargins.top() + mMargins.bottom();
19853  return result;
19854 }
19855 
19856 /* inherits documentation from base class */
19858  bool additive,
19859  const QVariant &details,
19860  bool *selectionStateChanged) {
19861  Q_UNUSED(event)
19862  Q_UNUSED(details)
19863  if (mSelectable) {
19864  bool selBefore = mSelected;
19865  setSelected(additive ? !mSelected : true);
19866  if (selectionStateChanged)
19867  *selectionStateChanged = mSelected != selBefore;
19868  }
19869 }
19870 
19871 /* inherits documentation from base class */
19872 void QCPTextElement::deselectEvent(bool *selectionStateChanged) {
19873  if (mSelectable) {
19874  bool selBefore = mSelected;
19875  setSelected(false);
19876  if (selectionStateChanged)
19877  *selectionStateChanged = mSelected != selBefore;
19878  }
19879 }
19880 
19891 double QCPTextElement::selectTest(const QPointF &pos,
19892  bool onlySelectable,
19893  QVariant *details) const {
19894  Q_UNUSED(details)
19895  if (onlySelectable && !mSelectable) return -1;
19896 
19897  if (mTextBoundingRect.contains(pos.toPoint()))
19898  return mParentPlot->selectionTolerance() * 0.99;
19899  else
19900  return -1;
19901 }
19902 
19910  const QVariant &details) {
19911  Q_UNUSED(details)
19912  event->accept();
19913 }
19914 
19922  const QPointF &startPos) {
19923  if ((QPointF(event->pos()) - startPos).manhattanLength() <= 3)
19924  emit clicked(event);
19925 }
19926 
19933  const QVariant &details) {
19934  Q_UNUSED(details)
19935  emit doubleClicked(event);
19936 }
19937 
19944  return mSelected ? mSelectedFont : mFont;
19945 }
19946 
19954 }
19955 /* end of 'src/layoutelements/layoutelement-textelement.cpp' */
19956 
19957 /* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 26246
19958  */
19959 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
19960 
19964 
20011 /* start documentation of inline functions */
20012 
20028 /* end documentation of signals */
20029 /* start documentation of signals */
20030 
20053 /* end documentation of signals */
20054 
20059  : QCPLayoutElement(parentPlot),
20060  mType(QCPAxis::atTop), // set to atTop such that
20061  // setType(QCPAxis::atRight) below doesn't skip
20062  // work because it thinks it's already atRight
20063  mDataScaleType(QCPAxis::stLinear),
20064  mBarWidth(20),
20065  mAxisRect(new QCPColorScaleAxisRectPrivate(this)) {
20066  setMinimumMargins(QMargins(
20067  0, 6, 0,
20068  6)); // for default right color scale types, keep some room at
20069  // bottom and top (important if no margin group is used)
20071  setDataRange(QCPRange(0, 6));
20072 }
20073 
20075 
20076 /* undocumented getter */
20077 QString QCPColorScale::label() const {
20078  if (!mColorAxis) {
20079  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
20080  return QString();
20081  }
20082 
20083  return mColorAxis.data()->label();
20084 }
20085 
20086 /* undocumented getter */
20088  if (!mAxisRect) {
20089  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20090  return false;
20091  }
20092 
20093  return mAxisRect.data()->rangeDrag().testFlag(
20095  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
20096  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))
20097  ->orientation() == QCPAxis::orientation(mType);
20098 }
20099 
20100 /* undocumented getter */
20102  if (!mAxisRect) {
20103  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20104  return false;
20105  }
20106 
20107  return mAxisRect.data()->rangeZoom().testFlag(
20109  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
20110  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))
20111  ->orientation() == QCPAxis::orientation(mType);
20112 }
20113 
20124  if (!mAxisRect) {
20125  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20126  return;
20127  }
20128  if (mType != type) {
20129  mType = type;
20130  QCPRange rangeTransfer(0, 6);
20131  QString labelTransfer;
20132  QSharedPointer<QCPAxisTicker> tickerTransfer;
20133  // transfer/revert some settings on old axis if it exists:
20134  bool doTransfer = (bool)mColorAxis;
20135  if (doTransfer) {
20136  rangeTransfer = mColorAxis.data()->range();
20137  labelTransfer = mColorAxis.data()->label();
20138  tickerTransfer = mColorAxis.data()->ticker();
20139  mColorAxis.data()->setLabel(QString());
20140  disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this,
20141  SLOT(setDataRange(QCPRange)));
20142  disconnect(mColorAxis.data(),
20143  SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this,
20145  }
20146  QList<QCPAxis::AxisType> allAxisTypes =
20147  QList<QCPAxis::AxisType>()
20149  << QCPAxis::atTop;
20150  foreach (QCPAxis::AxisType atype, allAxisTypes) {
20151  mAxisRect.data()->axis(atype)->setTicks(atype == mType);
20152  mAxisRect.data()->axis(atype)->setTickLabels(atype == mType);
20153  }
20154  // set new mColorAxis pointer:
20155  mColorAxis = mAxisRect.data()->axis(mType);
20156  // transfer settings to new axis:
20157  if (doTransfer) {
20158  mColorAxis.data()->setRange(
20159  rangeTransfer); // range transfer necessary if axis changes
20160  // from vertical to horizontal or vice
20161  // versa (axes with same orientation are
20162  // synchronized via signals)
20163  mColorAxis.data()->setLabel(labelTransfer);
20164  mColorAxis.data()->setTicker(tickerTransfer);
20165  }
20166  connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this,
20167  SLOT(setDataRange(QCPRange)));
20168  connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
20169  this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20170  mAxisRect.data()->setRangeDragAxes(QList<QCPAxis *>()
20171  << mColorAxis.data());
20172  }
20173 }
20174 
20185 void QCPColorScale::setDataRange(const QCPRange &dataRange) {
20186  if (mDataRange.lower != dataRange.lower ||
20189  if (mColorAxis) mColorAxis.data()->setRange(mDataRange);
20191  }
20192 }
20193 
20216  if (mDataScaleType != scaleType) {
20217  mDataScaleType = scaleType;
20218  if (mColorAxis) mColorAxis.data()->setScaleType(mDataScaleType);
20222  }
20223 }
20224 
20234  if (mGradient != gradient) {
20235  mGradient = gradient;
20236  if (mAxisRect) mAxisRect.data()->mGradientImageInvalidated = true;
20237  emit gradientChanged(mGradient);
20238  }
20239 }
20240 
20245 void QCPColorScale::setLabel(const QString &str) {
20246  if (!mColorAxis) {
20247  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
20248  return;
20249  }
20250 
20251  mColorAxis.data()->setLabel(str);
20252 }
20253 
20259 
20266 void QCPColorScale::setRangeDrag(bool enabled) {
20267  if (!mAxisRect) {
20268  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20269  return;
20270  }
20271 
20272  if (enabled) {
20273  mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
20274  } else {
20275 #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
20276  mAxisRect.data()->setRangeDrag(nullptr);
20277 #else
20278  mAxisRect.data()->setRangeDrag({});
20279 #endif
20280  }
20281 }
20282 
20290 void QCPColorScale::setRangeZoom(bool enabled) {
20291  if (!mAxisRect) {
20292  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20293  return;
20294  }
20295 
20296  if (enabled) {
20297  mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
20298  } else {
20299 #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
20300  mAxisRect.data()->setRangeDrag(nullptr);
20301 #else
20302  mAxisRect.data()->setRangeZoom({});
20303 #endif
20304  }
20305 }
20306 
20310 QList<QCPColorMap *> QCPColorScale::colorMaps() const {
20311  QList<QCPColorMap *> result;
20312  for (int i = 0; i < mParentPlot->plottableCount(); ++i) {
20313  if (QCPColorMap *cm =
20314  qobject_cast<QCPColorMap *>(mParentPlot->plottable(i)))
20315  if (cm->colorScale() == this) result.append(cm);
20316  }
20317  return result;
20318 }
20319 
20326 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps) {
20327  QList<QCPColorMap *> maps = colorMaps();
20328  QCPRange newRange;
20329  bool haveRange = false;
20333  for (int i = 0; i < maps.size(); ++i) {
20334  if (!maps.at(i)->realVisibility() && onlyVisibleMaps) continue;
20335  QCPRange mapRange;
20336  if (maps.at(i)->colorScale() == this) {
20337  bool currentFoundRange = true;
20338  mapRange = maps.at(i)->data()->dataBounds();
20339  if (sign == QCP::sdPositive) {
20340  if (mapRange.lower <= 0 && mapRange.upper > 0)
20341  mapRange.lower = mapRange.upper * 1e-3;
20342  else if (mapRange.lower <= 0 && mapRange.upper <= 0)
20343  currentFoundRange = false;
20344  } else if (sign == QCP::sdNegative) {
20345  if (mapRange.upper >= 0 && mapRange.lower < 0)
20346  mapRange.upper = mapRange.lower * 1e-3;
20347  else if (mapRange.upper >= 0 && mapRange.lower >= 0)
20348  currentFoundRange = false;
20349  }
20350  if (currentFoundRange) {
20351  if (!haveRange)
20352  newRange = mapRange;
20353  else
20354  newRange.expand(mapRange);
20355  haveRange = true;
20356  }
20357  }
20358  }
20359  if (haveRange) {
20360  if (!QCPRange::validRange(
20361  newRange)) // likely due to range being zero (plottable has
20362  // only constant data in this dimension), shift
20363  // current range to at least center the data
20364  {
20365  double center = (newRange.lower + newRange.upper) *
20366  0.5; // upper and lower should be equal anyway, but
20367  // just to make sure, incase validRange
20368  // returned false for other reason
20370  newRange.lower = center - mDataRange.size() / 2.0;
20371  newRange.upper = center + mDataRange.size() / 2.0;
20372  } else // mScaleType == stLogarithmic
20373  {
20374  newRange.lower =
20375  center / qSqrt(mDataRange.upper / mDataRange.lower);
20376  newRange.upper =
20377  center * qSqrt(mDataRange.upper / mDataRange.lower);
20378  }
20379  }
20380  setDataRange(newRange);
20381  }
20382 }
20383 
20384 /* inherits documentation from base class */
20386  QCPLayoutElement::update(phase);
20387  if (!mAxisRect) {
20388  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20389  return;
20390  }
20391 
20392  mAxisRect.data()->update(phase);
20393 
20394  switch (phase) {
20395  case upMargins: {
20397  setMaximumSize(QWIDGETSIZE_MAX,
20398  mBarWidth + mAxisRect.data()->margins().top() +
20399  mAxisRect.data()->margins().bottom());
20401  mAxisRect.data()->margins().top() +
20402  mAxisRect.data()->margins().bottom());
20403  } else {
20404  setMaximumSize(mBarWidth + mAxisRect.data()->margins().left() +
20405  mAxisRect.data()->margins().right(),
20406  QWIDGETSIZE_MAX);
20407  setMinimumSize(mBarWidth + mAxisRect.data()->margins().left() +
20408  mAxisRect.data()->margins().right(),
20409  0);
20410  }
20411  break;
20412  }
20413  case upLayout: {
20414  mAxisRect.data()->setOuterRect(rect());
20415  break;
20416  }
20417  default:
20418  break;
20419  }
20420 }
20421 
20422 /* inherits documentation from base class */
20424  painter->setAntialiasing(false);
20425 }
20426 
20427 /* inherits documentation from base class */
20429  const QVariant &details) {
20430  if (!mAxisRect) {
20431  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20432  return;
20433  }
20434  mAxisRect.data()->mousePressEvent(event, details);
20435 }
20436 
20437 /* inherits documentation from base class */
20439  const QPointF &startPos) {
20440  if (!mAxisRect) {
20441  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20442  return;
20443  }
20444  mAxisRect.data()->mouseMoveEvent(event, startPos);
20445 }
20446 
20447 /* inherits documentation from base class */
20449  const QPointF &startPos) {
20450  if (!mAxisRect) {
20451  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20452  return;
20453  }
20454  mAxisRect.data()->mouseReleaseEvent(event, startPos);
20455 }
20456 
20457 /* inherits documentation from base class */
20458 void QCPColorScale::wheelEvent(QWheelEvent *event) {
20459  if (!mAxisRect) {
20460  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
20461  return;
20462  }
20463  mAxisRect.data()->wheelEvent(event);
20464 }
20465 
20469 
20483 QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(
20484  QCPColorScale *parentColorScale)
20485  : QCPAxisRect(parentColorScale->parentPlot(), true),
20486  mParentColorScale(parentColorScale),
20487  mGradientImageInvalidated(true) {
20488  setParentLayerable(parentColorScale);
20489  setMinimumMargins(QMargins(0, 0, 0, 0));
20490  QList<QCPAxis::AxisType> allAxisTypes =
20491  QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop
20493  foreach (QCPAxis::AxisType type, allAxisTypes) {
20494  axis(type)->setVisible(true);
20495  axis(type)->grid()->setVisible(false);
20496  axis(type)->setPadding(0);
20497  connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)),
20498  this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
20499  connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)),
20500  this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
20501  }
20502 
20503  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)),
20504  axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
20505  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)),
20506  axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
20507  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)),
20508  axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
20509  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)),
20510  axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
20511  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
20512  axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
20513  connect(axis(QCPAxis::atRight),
20514  SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft),
20515  SLOT(setScaleType(QCPAxis::ScaleType)));
20516  connect(axis(QCPAxis::atBottom),
20517  SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop),
20518  SLOT(setScaleType(QCPAxis::ScaleType)));
20519  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)),
20520  axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
20521 
20522  // make layer transfers of color scale transfer to axis rect and axes
20523  // the axes must be set after axis rect, such that they appear above color
20524  // gradient drawn by axis rect:
20525  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer *)), this,
20526  SLOT(setLayer(QCPLayer *)));
20527  foreach (QCPAxis::AxisType type, allAxisTypes)
20528  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer *)), axis(type),
20529  SLOT(setLayer(QCPLayer *)));
20530 }
20531 
20541  if (mGradientImageInvalidated) updateGradientImage();
20542 
20543  bool mirrorHorz = false;
20544  bool mirrorVert = false;
20545  if (mParentColorScale->mColorAxis) {
20546  mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() &&
20547  (mParentColorScale->type() == QCPAxis::atBottom ||
20548  mParentColorScale->type() == QCPAxis::atTop);
20549  mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() &&
20550  (mParentColorScale->type() == QCPAxis::atLeft ||
20551  mParentColorScale->type() == QCPAxis::atRight);
20552  }
20553 
20554  painter->drawImage(rect().adjusted(0, -1, 0, -1),
20555  mGradientImage.mirrored(mirrorHorz, mirrorVert));
20556  QCPAxisRect::draw(painter);
20557 }
20558 
20565 void QCPColorScaleAxisRectPrivate::updateGradientImage() {
20566  if (rect().isEmpty()) return;
20567 
20568  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
20569  int n = mParentColorScale->mGradient.levelCount();
20570  int w, h;
20571  QVector<double> data(n);
20572  for (int i = 0; i < n; ++i) data[i] = i;
20573  if (mParentColorScale->mType == QCPAxis::atBottom ||
20574  mParentColorScale->mType == QCPAxis::atTop) {
20575  w = n;
20576  h = rect().height();
20577  mGradientImage = QImage(w, h, format);
20578  QVector<QRgb *> pixels;
20579  for (int y = 0; y < h; ++y)
20580  pixels.append(reinterpret_cast<QRgb *>(mGradientImage.scanLine(y)));
20581  mParentColorScale->mGradient.colorize(
20582  data.constData(), QCPRange(0, n - 1), pixels.first(), n);
20583  for (int y = 1; y < h; ++y)
20584  memcpy(pixels.at(y), pixels.first(), n * sizeof(QRgb));
20585  } else {
20586  w = rect().width();
20587  h = n;
20588  mGradientImage = QImage(w, h, format);
20589  for (int y = 0; y < h; ++y) {
20590  QRgb *pixels = reinterpret_cast<QRgb *>(mGradientImage.scanLine(y));
20591  const QRgb lineColor = mParentColorScale->mGradient.color(
20592  data[h - 1 - y], QCPRange(0, n - 1));
20593  for (int x = 0; x < w; ++x) pixels[x] = lineColor;
20594  }
20595  }
20596  mGradientImageInvalidated = false;
20597 }
20598 
20604 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(
20605  QCPAxis::SelectableParts selectedParts) {
20606  // axis bases of four axes shall always (de-)selected synchronously:
20607  QList<QCPAxis::AxisType> allAxisTypes =
20608  QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop
20610  foreach (QCPAxis::AxisType type, allAxisTypes) {
20611  if (QCPAxis *senderAxis = qobject_cast<QCPAxis *>(sender()))
20612  if (senderAxis->axisType() == type) continue;
20613 
20614  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) {
20615  if (selectedParts.testFlag(QCPAxis::spAxis))
20616  axis(type)->setSelectedParts(axis(type)->selectedParts() |
20617  QCPAxis::spAxis);
20618  else
20619  axis(type)->setSelectedParts(axis(type)->selectedParts() &
20620  ~QCPAxis::spAxis);
20621  }
20622  }
20623 }
20624 
20630 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(
20631  QCPAxis::SelectableParts selectableParts) {
20632  // synchronize axis base selectability:
20633  QList<QCPAxis::AxisType> allAxisTypes =
20634  QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop
20636  foreach (QCPAxis::AxisType type, allAxisTypes) {
20637  if (QCPAxis *senderAxis = qobject_cast<QCPAxis *>(sender()))
20638  if (senderAxis->axisType() == type) continue;
20639 
20640  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) {
20641  if (selectableParts.testFlag(QCPAxis::spAxis))
20642  axis(type)->setSelectableParts(axis(type)->selectableParts() |
20643  QCPAxis::spAxis);
20644  else
20645  axis(type)->setSelectableParts(axis(type)->selectableParts() &
20646  ~QCPAxis::spAxis);
20647  }
20648  }
20649 }
20650 /* end of 'src/layoutelements/layoutelement-colorscale.cpp' */
20651 
20652 /* including file 'src/plottables/plottable-graph.cpp', size 74194 */
20653 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
20654 
20658 
20675 /* start documentation of inline functions */
20676 
20728 /* end documentation of inline functions */
20729 
20733 QCPGraphData::QCPGraphData() : key(0), value(0) {}
20734 
20738 QCPGraphData::QCPGraphData(double key, double value) : key(key), value(value) {}
20739 
20743 
20785 /* start of documentation of inline functions */
20786 
20795 /* end of documentation of inline functions */
20796 
20812 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
20813  : QCPAbstractPlottable1D<QCPGraphData>(keyAxis, valueAxis) {
20814  // special handling for QCPGraphs to maintain the simple graph interface:
20815  mParentPlot->registerGraph(this);
20816 
20817  setPen(QPen(Qt::blue, 0));
20818  setBrush(Qt::NoBrush);
20819 
20821  setScatterSkip(0);
20823  setAdaptiveSampling(true);
20824 }
20825 
20827 
20845 void QCPGraph::setData(QSharedPointer<QCPGraphDataContainer> data) {
20846  mDataContainer = data;
20847 }
20848 
20861 void QCPGraph::setData(const QVector<double> &keys,
20862  const QVector<double> &values,
20863  bool alreadySorted) {
20864  mDataContainer->clear();
20865  addData(keys, values, alreadySorted);
20866 }
20867 
20876 
20885  mScatterStyle = style;
20886 }
20887 
20900 void QCPGraph::setScatterSkip(int skip) { mScatterSkip = qMax(0, skip); }
20901 
20912  // prevent setting channel target to this graph itself:
20913  if (targetGraph == this) {
20914  qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
20915  mChannelFillGraph = 0;
20916  return;
20917  }
20918  // prevent setting channel target to a graph not in the plot:
20919  if (targetGraph && targetGraph->mParentPlot != mParentPlot) {
20920  qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
20921  mChannelFillGraph = 0;
20922  return;
20923  }
20924 
20925  mChannelFillGraph = targetGraph;
20926 }
20927 
20965 void QCPGraph::setAdaptiveSampling(bool enabled) {
20966  mAdaptiveSampling = enabled;
20967 }
20968 
20982 void QCPGraph::addData(const QVector<double> &keys,
20983  const QVector<double> &values,
20984  bool alreadySorted) {
20985  if (keys.size() != values.size())
20986  qDebug() << Q_FUNC_INFO
20987  << "keys and values have different sizes:" << keys.size()
20988  << values.size();
20989  const int n = qMin(keys.size(), values.size());
20990  QVector<QCPGraphData> tempData(n);
20991  QVector<QCPGraphData>::iterator it = tempData.begin();
20992  const QVector<QCPGraphData>::iterator itEnd = tempData.end();
20993  int i = 0;
20994  while (it != itEnd) {
20995  it->key = keys[i];
20996  it->value = values[i];
20997  ++it;
20998  ++i;
20999  }
21000  mDataContainer->add(tempData,
21001  alreadySorted); // don't modify tempData beyond this to
21002  // prevent copy on write
21003 }
21004 
21012 void QCPGraph::addData(double key, double value) {
21013  mDataContainer->add(QCPGraphData(key, value));
21014 }
21015 
21024 double QCPGraph::selectTest(const QPointF &pos,
21025  bool onlySelectable,
21026  QVariant *details) const {
21027  if ((onlySelectable && mSelectable == QCP::stNone) ||
21028  mDataContainer->isEmpty())
21029  return -1;
21030  if (!mKeyAxis || !mValueAxis) return -1;
21031 
21032  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
21033  QCPGraphDataContainer::const_iterator closestDataPoint =
21034  mDataContainer->constEnd();
21035  double result = pointDistance(pos, closestDataPoint);
21036  if (details) {
21037  int pointIndex = closestDataPoint - mDataContainer->constBegin();
21038  details->setValue(
21039  QCPDataSelection(QCPDataRange(pointIndex, pointIndex + 1)));
21040  }
21041  return result;
21042  } else
21043  return -1;
21044 }
21045 
21046 /* inherits documentation from base class */
21048  QCP::SignDomain inSignDomain) const {
21049  return mDataContainer->keyRange(foundRange, inSignDomain);
21050 }
21051 
21052 /* inherits documentation from base class */
21054  QCP::SignDomain inSignDomain,
21055  const QCPRange &inKeyRange) const {
21056  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
21057 }
21058 
21059 /* inherits documentation from base class */
21060 void QCPGraph::draw(QCPPainter *painter) {
21061  if (!mKeyAxis || !mValueAxis) {
21062  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21063  return;
21064  }
21065  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty())
21066  return;
21067  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
21068 
21069  QVector<QPointF> lines,
21070  scatters; // line and (if necessary) scatter pixel coordinates will
21071  // be stored here while iterating over segments
21072 
21073  // loop over and draw segments of unselected/selected data:
21074  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
21075  getDataSegments(selectedSegments, unselectedSegments);
21076  allSegments << unselectedSegments << selectedSegments;
21077  for (int i = 0; i < allSegments.size(); ++i) {
21078  bool isSelectedSegment = i >= unselectedSegments.size();
21079  // get line pixel points appropriate to line style:
21080  QCPDataRange lineDataRange =
21081  isSelectedSegment
21082  ? allSegments.at(i)
21083  : allSegments.at(i).adjusted(
21084  -1,
21085  1); // unselected segments extend lines to
21086  // bordering selected data point (safe to
21087  // exceed total data bounds in first/last
21088  // segment, getLines takes care)
21089  getLines(&lines, lineDataRange);
21090 
21091  // check data validity if flag set:
21092 #ifdef QCUSTOMPLOT_CHECK_DATA
21094  for (it = mDataContainer->constBegin();
21095  it != mDataContainer->constEnd(); ++it) {
21096  if (QCP::isInvalidData(it->key, it->value))
21097  qDebug() << Q_FUNC_INFO << "Data point at" << it->key
21098  << "invalid."
21099  << "Plottable name:" << name();
21100  }
21101 #endif
21102 
21103  // draw fill of graph:
21104  if (isSelectedSegment && mSelectionDecorator)
21105  mSelectionDecorator->applyBrush(painter);
21106  else
21107  painter->setBrush(mBrush);
21108  painter->setPen(Qt::NoPen);
21109  drawFill(painter, &lines);
21110 
21111  // draw line:
21112  if (mLineStyle != lsNone) {
21113  if (isSelectedSegment && mSelectionDecorator)
21114  mSelectionDecorator->applyPen(painter);
21115  else
21116  painter->setPen(mPen);
21117  painter->setBrush(Qt::NoBrush);
21118  if (mLineStyle == lsImpulse)
21119  drawImpulsePlot(painter, lines);
21120  else
21121  drawLinePlot(
21122  painter,
21123  lines); // also step plots can be drawn as a line plot
21124  }
21125 
21126  // draw scatters:
21127  QCPScatterStyle finalScatterStyle = mScatterStyle;
21128  if (isSelectedSegment && mSelectionDecorator)
21129  finalScatterStyle =
21131  if (!finalScatterStyle.isNone()) {
21132  getScatters(&scatters, allSegments.at(i));
21133  drawScatterPlot(painter, scatters, finalScatterStyle);
21134  }
21135  }
21136 
21137  // draw other selection decoration that isn't just line/scatter pens and
21138  // brushes:
21139  if (mSelectionDecorator)
21141 }
21142 
21143 /* inherits documentation from base class */
21144 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const {
21145  // draw fill:
21146  if (mBrush.style() != Qt::NoBrush) {
21147  applyFillAntialiasingHint(painter);
21148  painter->fillRect(QRectF(rect.left(), rect.top() + rect.height() / 2.0,
21149  rect.width(), rect.height() / 3.0),
21150  mBrush);
21151  }
21152  // draw line vertically centered:
21153  if (mLineStyle != lsNone) {
21155  painter->setPen(mPen);
21156  painter->drawLine(QLineF(
21157  rect.left(), rect.top() + rect.height() / 2.0, rect.right() + 5,
21158  rect.top() + rect.height() /
21159  2.0)); // +5 on x2 else last segment is
21160  // missing from dashed/dotted pens
21161  }
21162  // draw scatter symbol:
21163  if (!mScatterStyle.isNone()) {
21165  // scale scatter pixmap if it's too large to fit in legend icon rect:
21167  (mScatterStyle.pixmap().size().width() > rect.width() ||
21168  mScatterStyle.pixmap().size().height() > rect.height())) {
21169  QCPScatterStyle scaledStyle(mScatterStyle);
21170  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(
21171  rect.size().toSize(), Qt::KeepAspectRatio,
21172  Qt::SmoothTransformation));
21173  scaledStyle.applyTo(painter, mPen);
21174  scaledStyle.drawShape(painter, QRectF(rect).center());
21175  } else {
21176  mScatterStyle.applyTo(painter, mPen);
21177  mScatterStyle.drawShape(painter, QRectF(rect).center());
21178  }
21179  }
21180 }
21181 
21205 void QCPGraph::getLines(QVector<QPointF> *lines,
21206  const QCPDataRange &dataRange) const {
21207  if (!lines) return;
21209  getVisibleDataBounds(begin, end, dataRange);
21210  if (begin == end) {
21211  lines->clear();
21212  return;
21213  }
21214 
21215  QVector<QCPGraphData> lineData;
21216  if (mLineStyle != lsNone) getOptimizedLineData(&lineData, begin, end);
21217 
21218  if (mKeyAxis->rangeReversed() !=
21219  (mKeyAxis->orientation() ==
21220  Qt::Vertical)) // make sure key pixels are sorted ascending in
21221  // lineData (significantly simplifies following
21222  // processing)
21223  std::reverse(lineData.begin(), lineData.end());
21224 
21225  switch (mLineStyle) {
21226  case lsNone:
21227  lines->clear();
21228  break;
21229  case lsLine:
21230  *lines = dataToLines(lineData);
21231  break;
21232  case lsStepLeft:
21233  *lines = dataToStepLeftLines(lineData);
21234  break;
21235  case lsStepRight:
21236  *lines = dataToStepRightLines(lineData);
21237  break;
21238  case lsStepCenter:
21239  *lines = dataToStepCenterLines(lineData);
21240  break;
21241  case lsImpulse:
21242  *lines = dataToImpulseLines(lineData);
21243  break;
21244  }
21245 }
21246 
21261 void QCPGraph::getScatters(QVector<QPointF> *scatters,
21262  const QCPDataRange &dataRange) const {
21263  if (!scatters) return;
21264  QCPAxis *keyAxis = mKeyAxis.data();
21265  QCPAxis *valueAxis = mValueAxis.data();
21266  if (!keyAxis || !valueAxis) {
21267  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21268  scatters->clear();
21269  return;
21270  }
21271 
21273  getVisibleDataBounds(begin, end, dataRange);
21274  if (begin == end) {
21275  scatters->clear();
21276  return;
21277  }
21278 
21279  QVector<QCPGraphData> data;
21280  getOptimizedScatterData(&data, begin, end);
21281 
21282  if (mKeyAxis->rangeReversed() !=
21283  (mKeyAxis->orientation() ==
21284  Qt::Vertical)) // make sure key pixels are sorted ascending in data
21285  // (significantly simplifies following processing)
21286  std::reverse(data.begin(), data.end());
21287 
21288  scatters->resize(data.size());
21289  if (keyAxis->orientation() == Qt::Vertical) {
21290  for (int i = 0; i < data.size(); ++i) {
21291  if (!qIsNaN(data.at(i).value)) {
21292  (*scatters)[i].setX(valueAxis->coordToPixel(data.at(i).value));
21293  (*scatters)[i].setY(keyAxis->coordToPixel(data.at(i).key));
21294  }
21295  }
21296  } else {
21297  for (int i = 0; i < data.size(); ++i) {
21298  if (!qIsNaN(data.at(i).value)) {
21299  (*scatters)[i].setX(keyAxis->coordToPixel(data.at(i).key));
21300  (*scatters)[i].setY(valueAxis->coordToPixel(data.at(i).value));
21301  }
21302  }
21303  }
21304 }
21305 
21318 QVector<QPointF> QCPGraph::dataToLines(
21319  const QVector<QCPGraphData> &data) const {
21320  QVector<QPointF> result;
21321  QCPAxis *keyAxis = mKeyAxis.data();
21322  QCPAxis *valueAxis = mValueAxis.data();
21323  if (!keyAxis || !valueAxis) {
21324  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21325  return result;
21326  }
21327 
21328  result.resize(data.size());
21329 
21330  // transform data points to pixels:
21331  if (keyAxis->orientation() == Qt::Vertical) {
21332  for (int i = 0; i < data.size(); ++i) {
21333  result[i].setX(valueAxis->coordToPixel(data.at(i).value));
21334  result[i].setY(keyAxis->coordToPixel(data.at(i).key));
21335  }
21336  } else // key axis is horizontal
21337  {
21338  for (int i = 0; i < data.size(); ++i) {
21339  result[i].setX(keyAxis->coordToPixel(data.at(i).key));
21340  result[i].setY(valueAxis->coordToPixel(data.at(i).value));
21341  }
21342  }
21343  return result;
21344 }
21345 
21359  const QVector<QCPGraphData> &data) const {
21360  QVector<QPointF> result;
21361  QCPAxis *keyAxis = mKeyAxis.data();
21362  QCPAxis *valueAxis = mValueAxis.data();
21363  if (!keyAxis || !valueAxis) {
21364  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21365  return result;
21366  }
21367 
21368  result.resize(data.size() * 2);
21369 
21370  // calculate steps from data and transform to pixel coordinates:
21371  if (keyAxis->orientation() == Qt::Vertical) {
21372  double lastValue = valueAxis->coordToPixel(data.first().value);
21373  for (int i = 0; i < data.size(); ++i) {
21374  const double key = keyAxis->coordToPixel(data.at(i).key);
21375  result[i * 2 + 0].setX(lastValue);
21376  result[i * 2 + 0].setY(key);
21377  lastValue = valueAxis->coordToPixel(data.at(i).value);
21378  result[i * 2 + 1].setX(lastValue);
21379  result[i * 2 + 1].setY(key);
21380  }
21381  } else // key axis is horizontal
21382  {
21383  double lastValue = valueAxis->coordToPixel(data.first().value);
21384  for (int i = 0; i < data.size(); ++i) {
21385  const double key = keyAxis->coordToPixel(data.at(i).key);
21386  result[i * 2 + 0].setX(key);
21387  result[i * 2 + 0].setY(lastValue);
21388  lastValue = valueAxis->coordToPixel(data.at(i).value);
21389  result[i * 2 + 1].setX(key);
21390  result[i * 2 + 1].setY(lastValue);
21391  }
21392  }
21393  return result;
21394 }
21395 
21409  const QVector<QCPGraphData> &data) const {
21410  QVector<QPointF> result;
21411  QCPAxis *keyAxis = mKeyAxis.data();
21412  QCPAxis *valueAxis = mValueAxis.data();
21413  if (!keyAxis || !valueAxis) {
21414  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21415  return result;
21416  }
21417 
21418  result.resize(data.size() * 2);
21419 
21420  // calculate steps from data and transform to pixel coordinates:
21421  if (keyAxis->orientation() == Qt::Vertical) {
21422  double lastKey = keyAxis->coordToPixel(data.first().key);
21423  for (int i = 0; i < data.size(); ++i) {
21424  const double value = valueAxis->coordToPixel(data.at(i).value);
21425  result[i * 2 + 0].setX(value);
21426  result[i * 2 + 0].setY(lastKey);
21427  lastKey = keyAxis->coordToPixel(data.at(i).key);
21428  result[i * 2 + 1].setX(value);
21429  result[i * 2 + 1].setY(lastKey);
21430  }
21431  } else // key axis is horizontal
21432  {
21433  double lastKey = keyAxis->coordToPixel(data.first().key);
21434  for (int i = 0; i < data.size(); ++i) {
21435  const double value = valueAxis->coordToPixel(data.at(i).value);
21436  result[i * 2 + 0].setX(lastKey);
21437  result[i * 2 + 0].setY(value);
21438  lastKey = keyAxis->coordToPixel(data.at(i).key);
21439  result[i * 2 + 1].setX(lastKey);
21440  result[i * 2 + 1].setY(value);
21441  }
21442  }
21443  return result;
21444 }
21445 
21459  const QVector<QCPGraphData> &data) const {
21460  QVector<QPointF> result;
21461  QCPAxis *keyAxis = mKeyAxis.data();
21462  QCPAxis *valueAxis = mValueAxis.data();
21463  if (!keyAxis || !valueAxis) {
21464  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21465  return result;
21466  }
21467 
21468  result.resize(data.size() * 2);
21469 
21470  // calculate steps from data and transform to pixel coordinates:
21471  if (keyAxis->orientation() == Qt::Vertical) {
21472  double lastKey = keyAxis->coordToPixel(data.first().key);
21473  double lastValue = valueAxis->coordToPixel(data.first().value);
21474  result[0].setX(lastValue);
21475  result[0].setY(lastKey);
21476  for (int i = 1; i < data.size(); ++i) {
21477  const double key =
21478  (keyAxis->coordToPixel(data.at(i).key) + lastKey) * 0.5;
21479  result[i * 2 - 1].setX(lastValue);
21480  result[i * 2 - 1].setY(key);
21481  lastValue = valueAxis->coordToPixel(data.at(i).value);
21482  lastKey = keyAxis->coordToPixel(data.at(i).key);
21483  result[i * 2 + 0].setX(lastValue);
21484  result[i * 2 + 0].setY(key);
21485  }
21486  result[data.size() * 2 - 1].setX(lastValue);
21487  result[data.size() * 2 - 1].setY(lastKey);
21488  } else // key axis is horizontal
21489  {
21490  double lastKey = keyAxis->coordToPixel(data.first().key);
21491  double lastValue = valueAxis->coordToPixel(data.first().value);
21492  result[0].setX(lastKey);
21493  result[0].setY(lastValue);
21494  for (int i = 1; i < data.size(); ++i) {
21495  const double key =
21496  (keyAxis->coordToPixel(data.at(i).key) + lastKey) * 0.5;
21497  result[i * 2 - 1].setX(key);
21498  result[i * 2 - 1].setY(lastValue);
21499  lastValue = valueAxis->coordToPixel(data.at(i).value);
21500  lastKey = keyAxis->coordToPixel(data.at(i).key);
21501  result[i * 2 + 0].setX(key);
21502  result[i * 2 + 0].setY(lastValue);
21503  }
21504  result[data.size() * 2 - 1].setX(lastKey);
21505  result[data.size() * 2 - 1].setY(lastValue);
21506  }
21507  return result;
21508 }
21509 
21523  const QVector<QCPGraphData> &data) const {
21524  QVector<QPointF> result;
21525  QCPAxis *keyAxis = mKeyAxis.data();
21526  QCPAxis *valueAxis = mValueAxis.data();
21527  if (!keyAxis || !valueAxis) {
21528  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21529  return result;
21530  }
21531 
21532  result.resize(data.size() * 2);
21533 
21534  // transform data points to pixels:
21535  if (keyAxis->orientation() == Qt::Vertical) {
21536  for (int i = 0; i < data.size(); ++i) {
21537  const double key = keyAxis->coordToPixel(data.at(i).key);
21538  result[i * 2 + 0].setX(valueAxis->coordToPixel(0));
21539  result[i * 2 + 0].setY(key);
21540  result[i * 2 + 1].setX(valueAxis->coordToPixel(data.at(i).value));
21541  result[i * 2 + 1].setY(key);
21542  }
21543  } else // key axis is horizontal
21544  {
21545  for (int i = 0; i < data.size(); ++i) {
21546  const double key = keyAxis->coordToPixel(data.at(i).key);
21547  result[i * 2 + 0].setX(key);
21548  result[i * 2 + 0].setY(valueAxis->coordToPixel(0));
21549  result[i * 2 + 1].setX(key);
21550  result[i * 2 + 1].setY(valueAxis->coordToPixel(data.at(i).value));
21551  }
21552  }
21553  return result;
21554 }
21555 
21576 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const {
21577  if (mLineStyle == lsImpulse)
21578  return; // fill doesn't make sense for impulse plot
21579  if (painter->brush().style() == Qt::NoBrush ||
21580  painter->brush().color().alpha() == 0)
21581  return;
21582 
21583  applyFillAntialiasingHint(painter);
21584  QVector<QCPDataRange> segments =
21585  getNonNanSegments(lines, keyAxis()->orientation());
21586  if (!mChannelFillGraph) {
21587  // draw base fill under graph, fill goes all the way to the
21588  // zero-value-line:
21589  for (int i = 0; i < segments.size(); ++i)
21590  painter->drawPolygon(getFillPolygon(lines, segments.at(i)));
21591  } else {
21592  // draw fill between this graph and mChannelFillGraph:
21593  QVector<QPointF> otherLines;
21594  mChannelFillGraph->getLines(
21595  &otherLines, QCPDataRange(0, mChannelFillGraph->dataCount()));
21596  if (!otherLines.isEmpty()) {
21597  QVector<QCPDataRange> otherSegments = getNonNanSegments(
21598  &otherLines, mChannelFillGraph->keyAxis()->orientation());
21599  QVector<QPair<QCPDataRange, QCPDataRange>> segmentPairs =
21600  getOverlappingSegments(segments, lines, otherSegments,
21601  &otherLines);
21602  for (int i = 0; i < segmentPairs.size(); ++i)
21603  painter->drawPolygon(getChannelFillPolygon(
21604  lines, segmentPairs.at(i).first, &otherLines,
21605  segmentPairs.at(i).second));
21606  }
21607  }
21608 }
21609 
21619  const QVector<QPointF> &scatters,
21620  const QCPScatterStyle &style) const {
21622  style.applyTo(painter, mPen);
21623  for (int i = 0; i < scatters.size(); ++i)
21624  style.drawShape(painter, scatters.at(i).x(), scatters.at(i).y());
21625 }
21626 
21634  const QVector<QPointF> &lines) const {
21635  if (painter->pen().style() != Qt::NoPen &&
21636  painter->pen().color().alpha() != 0) {
21638  drawPolyline(painter, lines);
21639  }
21640 }
21641 
21651  const QVector<QPointF> &lines) const {
21652  if (painter->pen().style() != Qt::NoPen &&
21653  painter->pen().color().alpha() != 0) {
21655  QPen oldPen = painter->pen();
21656  QPen newPen = painter->pen();
21657  newPen.setCapStyle(
21658  Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
21659  painter->setPen(newPen);
21660  painter->drawLines(lines);
21661  painter->setPen(oldPen);
21662  }
21663 }
21664 
21680  QVector<QCPGraphData> *lineData,
21682  const QCPGraphDataContainer::const_iterator &end) const {
21683  if (!lineData) return;
21684  QCPAxis *keyAxis = mKeyAxis.data();
21685  QCPAxis *valueAxis = mValueAxis.data();
21686  if (!keyAxis || !valueAxis) {
21687  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21688  return;
21689  }
21690  if (begin == end) return;
21691 
21692  int dataCount = end - begin;
21693  int maxCount = (std::numeric_limits<int>::max)();
21694  if (mAdaptiveSampling) {
21695  double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key) -
21696  keyAxis->coordToPixel((end - 1)->key));
21697  if (2 * keyPixelSpan + 2 <
21698  static_cast<double>((std::numeric_limits<int>::max)()))
21699  maxCount = 2 * keyPixelSpan + 2;
21700  }
21701 
21702  if (mAdaptiveSampling &&
21703  dataCount >= maxCount) // use adaptive sampling only if there are at
21704  // least two points per pixel on average
21705  {
21707  double minValue = it->value;
21708  double maxValue = it->value;
21709  QCPGraphDataContainer::const_iterator currentIntervalFirstPoint = it;
21710  int reversedFactor =
21711  keyAxis->pixelOrientation(); // is used to calculate keyEpsilon
21712  // pixel into the correct
21713  // direction
21714  int reversedRound = reversedFactor == -1
21715  ? 1
21716  : 0; // is used to switch between floor
21717  // (normal) and ceil (reversed)
21718  // rounding of currentIntervalStartKey
21719  double currentIntervalStartKey = keyAxis->pixelToCoord(
21720  (int)(keyAxis->coordToPixel(begin->key) + reversedRound));
21721  double lastIntervalEndKey = currentIntervalStartKey;
21722  double keyEpsilon =
21723  qAbs(currentIntervalStartKey -
21725  keyAxis->coordToPixel(currentIntervalStartKey) +
21726  1.0 * reversedFactor)); // interval of one pixel
21727  // on screen when mapped
21728  // to plot key coordinates
21729  bool keyEpsilonVariable =
21730  keyAxis->scaleType() ==
21731  QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs
21732  // to be updated after every interval
21733  // (for log axes)
21734  int intervalDataCount = 1;
21735  ++it; // advance iterator to second data point because adaptive
21736  // sampling works in 1 point retrospect
21737  while (it != end) {
21738  if (it->key <
21739  currentIntervalStartKey +
21740  keyEpsilon) // data point is still within same pixel,
21741  // so skip it and expand value span of this
21742  // cluster if necessary
21743  {
21744  if (it->value < minValue)
21745  minValue = it->value;
21746  else if (it->value > maxValue)
21747  maxValue = it->value;
21748  ++intervalDataCount;
21749  } else // new pixel interval started
21750  {
21751  if (intervalDataCount >=
21752  2) // last pixel had multiple data points, consolidate them
21753  // to a cluster
21754  {
21755  if (lastIntervalEndKey <
21756  currentIntervalStartKey -
21757  keyEpsilon) // last point is further away, so
21758  // first point of this cluster must
21759  // be at a real data point
21760  lineData->append(QCPGraphData(
21761  currentIntervalStartKey + keyEpsilon * 0.2,
21762  currentIntervalFirstPoint->value));
21763  lineData->append(QCPGraphData(
21764  currentIntervalStartKey + keyEpsilon * 0.25,
21765  minValue));
21766  lineData->append(QCPGraphData(
21767  currentIntervalStartKey + keyEpsilon * 0.75,
21768  maxValue));
21769  if (it->key >
21770  currentIntervalStartKey +
21771  keyEpsilon *
21772  2) // new pixel started further away
21773  // from previous cluster, so make
21774  // sure the last point of the
21775  // cluster is at a real data point
21776  lineData->append(QCPGraphData(
21777  currentIntervalStartKey + keyEpsilon * 0.8,
21778  (it - 1)->value));
21779  } else
21780  lineData->append(
21781  QCPGraphData(currentIntervalFirstPoint->key,
21782  currentIntervalFirstPoint->value));
21783  lastIntervalEndKey = (it - 1)->key;
21784  minValue = it->value;
21785  maxValue = it->value;
21786  currentIntervalFirstPoint = it;
21787  currentIntervalStartKey = keyAxis->pixelToCoord(
21788  (int)(keyAxis->coordToPixel(it->key) + reversedRound));
21789  if (keyEpsilonVariable)
21790  keyEpsilon = qAbs(currentIntervalStartKey -
21793  currentIntervalStartKey) +
21794  1.0 * reversedFactor));
21795  intervalDataCount = 1;
21796  }
21797  ++it;
21798  }
21799  // handle last interval:
21800  if (intervalDataCount >= 2) // last pixel had multiple data points,
21801  // consolidate them to a cluster
21802  {
21803  if (lastIntervalEndKey <
21804  currentIntervalStartKey -
21805  keyEpsilon) // last point wasn't a cluster, so first
21806  // point of this cluster must be at a real
21807  // data point
21808  lineData->append(
21809  QCPGraphData(currentIntervalStartKey + keyEpsilon * 0.2,
21810  currentIntervalFirstPoint->value));
21811  lineData->append(QCPGraphData(
21812  currentIntervalStartKey + keyEpsilon * 0.25, minValue));
21813  lineData->append(QCPGraphData(
21814  currentIntervalStartKey + keyEpsilon * 0.75, maxValue));
21815  } else
21816  lineData->append(QCPGraphData(currentIntervalFirstPoint->key,
21817  currentIntervalFirstPoint->value));
21818 
21819  } else // don't use adaptive sampling algorithm, transfer points one-to-one
21820  // from the data container into the output
21821  {
21822  lineData->resize(dataCount);
21823  std::copy(begin, end, lineData->begin());
21824  }
21825 }
21826 
21842  QVector<QCPGraphData> *scatterData,
21845  if (!scatterData) return;
21846  QCPAxis *keyAxis = mKeyAxis.data();
21847  QCPAxis *valueAxis = mValueAxis.data();
21848  if (!keyAxis || !valueAxis) {
21849  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
21850  return;
21851  }
21852 
21853  const int scatterModulo = mScatterSkip + 1;
21854  const bool doScatterSkip = mScatterSkip > 0;
21855  int beginIndex = begin - mDataContainer->constBegin();
21856  int endIndex = end - mDataContainer->constBegin();
21857  while (doScatterSkip && begin != end &&
21858  beginIndex % scatterModulo !=
21859  0) // advance begin iterator to first non-skipped scatter
21860  {
21861  ++beginIndex;
21862  ++begin;
21863  }
21864  if (begin == end) return;
21865  int dataCount = end - begin;
21866  int maxCount = (std::numeric_limits<int>::max)();
21867  if (mAdaptiveSampling) {
21868  int keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key) -
21869  keyAxis->coordToPixel((end - 1)->key));
21870  maxCount = 2 * keyPixelSpan + 2;
21871  }
21872 
21873  if (mAdaptiveSampling &&
21874  dataCount >= maxCount) // use adaptive sampling only if there are at
21875  // least two points per pixel on average
21876  {
21877  double valueMaxRange = valueAxis->range().upper;
21878  double valueMinRange = valueAxis->range().lower;
21880  int itIndex = beginIndex;
21881  double minValue = it->value;
21882  double maxValue = it->value;
21883  QCPGraphDataContainer::const_iterator minValueIt = it;
21884  QCPGraphDataContainer::const_iterator maxValueIt = it;
21885  QCPGraphDataContainer::const_iterator currentIntervalStart = it;
21886  int reversedFactor =
21887  keyAxis->pixelOrientation(); // is used to calculate keyEpsilon
21888  // pixel into the correct
21889  // direction
21890  int reversedRound = reversedFactor == -1
21891  ? 1
21892  : 0; // is used to switch between floor
21893  // (normal) and ceil (reversed)
21894  // rounding of currentIntervalStartKey
21895  double currentIntervalStartKey = keyAxis->pixelToCoord(
21896  (int)(keyAxis->coordToPixel(begin->key) + reversedRound));
21897  double keyEpsilon =
21898  qAbs(currentIntervalStartKey -
21900  keyAxis->coordToPixel(currentIntervalStartKey) +
21901  1.0 * reversedFactor)); // interval of one pixel
21902  // on screen when mapped
21903  // to plot key coordinates
21904  bool keyEpsilonVariable =
21905  keyAxis->scaleType() ==
21906  QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs
21907  // to be updated after every interval
21908  // (for log axes)
21909  int intervalDataCount = 1;
21910  // advance iterator to second (non-skipped) data point because adaptive
21911  // sampling works in 1 point retrospect:
21912  if (!doScatterSkip)
21913  ++it;
21914  else {
21915  itIndex += scatterModulo;
21916  if (itIndex < endIndex) // make sure we didn't jump over end
21917  it += scatterModulo;
21918  else {
21919  it = end;
21920  itIndex = endIndex;
21921  }
21922  }
21923  // main loop over data points:
21924  while (it != end) {
21925  if (it->key <
21926  currentIntervalStartKey +
21927  keyEpsilon) // data point is still within same pixel,
21928  // so skip it and expand value span of this
21929  // pixel if necessary
21930  {
21931  if (it->value < minValue && it->value > valueMinRange &&
21932  it->value < valueMaxRange) {
21933  minValue = it->value;
21934  minValueIt = it;
21935  } else if (it->value > maxValue && it->value > valueMinRange &&
21936  it->value < valueMaxRange) {
21937  maxValue = it->value;
21938  maxValueIt = it;
21939  }
21940  ++intervalDataCount;
21941  } else // new pixel started
21942  {
21943  if (intervalDataCount >=
21944  2) // last pixel had multiple data points, consolidate them
21945  {
21946  // determine value pixel span and add as many points in
21947  // interval to maintain certain vertical data density (this
21948  // is specific to scatter plot):
21949  double valuePixelSpan =
21950  qAbs(valueAxis->coordToPixel(minValue) -
21951  valueAxis->coordToPixel(maxValue));
21952  int dataModulo = qMax(
21953  1,
21954  qRound(intervalDataCount /
21955  (valuePixelSpan /
21956  4.0))); // approximately every 4 value
21957  // pixels one data point on average
21959  currentIntervalStart;
21960  int c = 0;
21961  while (intervalIt != it) {
21962  if ((c % dataModulo == 0 || intervalIt == minValueIt ||
21963  intervalIt == maxValueIt) &&
21964  intervalIt->value > valueMinRange &&
21965  intervalIt->value < valueMaxRange)
21966  scatterData->append(*intervalIt);
21967  ++c;
21968  if (!doScatterSkip)
21969  ++intervalIt;
21970  else
21971  intervalIt +=
21972  scatterModulo; // since we know indices of
21973  // "currentIntervalStart",
21974  // "intervalIt" and "it" are
21975  // multiples of
21976  // scatterModulo, we can't
21977  // accidentally jump over
21978  // "it" here
21979  }
21980  } else if (currentIntervalStart->value > valueMinRange &&
21981  currentIntervalStart->value < valueMaxRange)
21982  scatterData->append(*currentIntervalStart);
21983  minValue = it->value;
21984  maxValue = it->value;
21985  currentIntervalStart = it;
21986  currentIntervalStartKey = keyAxis->pixelToCoord(
21987  (int)(keyAxis->coordToPixel(it->key) + reversedRound));
21988  if (keyEpsilonVariable)
21989  keyEpsilon = qAbs(currentIntervalStartKey -
21992  currentIntervalStartKey) +
21993  1.0 * reversedFactor));
21994  intervalDataCount = 1;
21995  }
21996  // advance to next data point:
21997  if (!doScatterSkip)
21998  ++it;
21999  else {
22000  itIndex += scatterModulo;
22001  if (itIndex < endIndex) // make sure we didn't jump over end
22002  it += scatterModulo;
22003  else {
22004  it = end;
22005  itIndex = endIndex;
22006  }
22007  }
22008  }
22009  // handle last interval:
22010  if (intervalDataCount >=
22011  2) // last pixel had multiple data points, consolidate them
22012  {
22013  // determine value pixel span and add as many points in interval to
22014  // maintain certain vertical data density (this is specific to
22015  // scatter plot):
22016  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue) -
22017  valueAxis->coordToPixel(maxValue));
22018  int dataModulo =
22019  qMax(1, qRound(intervalDataCount /
22020  (valuePixelSpan /
22021  4.0))); // approximately every 4 value
22022  // pixels one data point on average
22024  currentIntervalStart;
22025  int intervalItIndex = intervalIt - mDataContainer->constBegin();
22026  int c = 0;
22027  while (intervalIt != it) {
22028  if ((c % dataModulo == 0 || intervalIt == minValueIt ||
22029  intervalIt == maxValueIt) &&
22030  intervalIt->value > valueMinRange &&
22031  intervalIt->value < valueMaxRange)
22032  scatterData->append(*intervalIt);
22033  ++c;
22034  if (!doScatterSkip)
22035  ++intervalIt;
22036  else // here we can't guarantee that adding scatterModulo
22037  // doesn't exceed "it" (because "it" is equal to "end"
22038  // here, and "end" isn't scatterModulo-aligned), so check
22039  // via index comparison:
22040  {
22041  intervalItIndex += scatterModulo;
22042  if (intervalItIndex < itIndex)
22043  intervalIt += scatterModulo;
22044  else {
22045  intervalIt = it;
22046  intervalItIndex = itIndex;
22047  }
22048  }
22049  }
22050  } else if (currentIntervalStart->value > valueMinRange &&
22051  currentIntervalStart->value < valueMaxRange)
22052  scatterData->append(*currentIntervalStart);
22053 
22054  } else // don't use adaptive sampling algorithm, transfer points one-to-one
22055  // from the data container into the output
22056  {
22058  int itIndex = beginIndex;
22059  scatterData->reserve(dataCount);
22060  while (it != end) {
22061  scatterData->append(*it);
22062  // advance to next data point:
22063  if (!doScatterSkip)
22064  ++it;
22065  else {
22066  itIndex += scatterModulo;
22067  if (itIndex < endIndex)
22068  it += scatterModulo;
22069  else {
22070  it = end;
22071  itIndex = endIndex;
22072  }
22073  }
22074  }
22075  }
22076 }
22077 
22090  const QCPDataRange &rangeRestriction) const {
22091  if (rangeRestriction.isEmpty()) {
22092  end = mDataContainer->constEnd();
22093  begin = end;
22094  } else {
22095  QCPAxis *keyAxis = mKeyAxis.data();
22096  QCPAxis *valueAxis = mValueAxis.data();
22097  if (!keyAxis || !valueAxis) {
22098  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22099  return;
22100  }
22101  // get visible data range:
22102  begin = mDataContainer->findBegin(keyAxis->range().lower);
22103  end = mDataContainer->findEnd(keyAxis->range().upper);
22104  // limit lower/upperEnd to rangeRestriction:
22105  mDataContainer->limitIteratorsToDataRange(
22106  begin, end, rangeRestriction); // this also ensures
22107  // rangeRestriction outside data
22108  // bounds doesn't break anything
22109  }
22110 }
22111 
22124 QVector<QCPDataRange> QCPGraph::getNonNanSegments(
22125  const QVector<QPointF> *lineData,
22126  Qt::Orientation keyOrientation) const {
22127  QVector<QCPDataRange> result;
22128  const int n = lineData->size();
22129 
22130  QCPDataRange currentSegment(-1, -1);
22131  int i = 0;
22132 
22133  if (keyOrientation == Qt::Horizontal) {
22134  while (i < n) {
22135  while (i < n &&
22136  qIsNaN(lineData->at(i).y())) // seek next non-NaN data point
22137  ++i;
22138  if (i == n) break;
22139  currentSegment.setBegin(i++);
22140  while (i < n &&
22141  !qIsNaN(lineData->at(i).y())) // seek next NaN data point or
22142  // end of data
22143  ++i;
22144  currentSegment.setEnd(i++);
22145  result.append(currentSegment);
22146  }
22147  } else // keyOrientation == Qt::Vertical
22148  {
22149  while (i < n) {
22150  while (i < n &&
22151  qIsNaN(lineData->at(i).x())) // seek next non-NaN data point
22152  ++i;
22153  if (i == n) break;
22154  currentSegment.setBegin(i++);
22155  while (i < n &&
22156  !qIsNaN(lineData->at(i).x())) // seek next NaN data point or
22157  // end of data
22158  ++i;
22159  currentSegment.setEnd(i++);
22160  result.append(currentSegment);
22161  }
22162  }
22163  return result;
22164 }
22165 
22186 QVector<QPair<QCPDataRange, QCPDataRange>> QCPGraph::getOverlappingSegments(
22187  QVector<QCPDataRange> thisSegments,
22188  const QVector<QPointF> *thisData,
22189  QVector<QCPDataRange> otherSegments,
22190  const QVector<QPointF> *otherData) const {
22191  QVector<QPair<QCPDataRange, QCPDataRange>> result;
22192  if (thisData->isEmpty() || otherData->isEmpty() || thisSegments.isEmpty() ||
22193  otherSegments.isEmpty())
22194  return result;
22195 
22196  int thisIndex = 0;
22197  int otherIndex = 0;
22198  const bool verticalKey = mKeyAxis->orientation() == Qt::Vertical;
22199  while (thisIndex < thisSegments.size() &&
22200  otherIndex < otherSegments.size()) {
22201  if (thisSegments.at(thisIndex).size() <
22202  2) // segments with fewer than two points won't have a fill anyhow
22203  {
22204  ++thisIndex;
22205  continue;
22206  }
22207  if (otherSegments.at(otherIndex).size() <
22208  2) // segments with fewer than two points won't have a fill anyhow
22209  {
22210  ++otherIndex;
22211  continue;
22212  }
22213  double thisLower, thisUpper, otherLower, otherUpper;
22214  if (!verticalKey) {
22215  thisLower = thisData->at(thisSegments.at(thisIndex).begin()).x();
22216  thisUpper = thisData->at(thisSegments.at(thisIndex).end() - 1).x();
22217  otherLower =
22218  otherData->at(otherSegments.at(otherIndex).begin()).x();
22219  otherUpper =
22220  otherData->at(otherSegments.at(otherIndex).end() - 1).x();
22221  } else {
22222  thisLower = thisData->at(thisSegments.at(thisIndex).begin()).y();
22223  thisUpper = thisData->at(thisSegments.at(thisIndex).end() - 1).y();
22224  otherLower =
22225  otherData->at(otherSegments.at(otherIndex).begin()).y();
22226  otherUpper =
22227  otherData->at(otherSegments.at(otherIndex).end() - 1).y();
22228  }
22229 
22230  int bPrecedence;
22231  if (segmentsIntersect(thisLower, thisUpper, otherLower, otherUpper,
22232  bPrecedence))
22233  result.append(QPair<QCPDataRange, QCPDataRange>(
22234  thisSegments.at(thisIndex), otherSegments.at(otherIndex)));
22235 
22236  if (bPrecedence <=
22237  0) // otherSegment doesn't reach as far as thisSegment, so continue
22238  // with next otherSegment, keeping current thisSegment
22239  ++otherIndex;
22240  else // otherSegment reaches further than thisSegment, so continue with
22241  // next thisSegment, keeping current otherSegment
22242  ++thisIndex;
22243  }
22244 
22245  return result;
22246 }
22247 
22264 bool QCPGraph::segmentsIntersect(double aLower,
22265  double aUpper,
22266  double bLower,
22267  double bUpper,
22268  int &bPrecedence) const {
22269  bPrecedence = 0;
22270  if (aLower > bUpper) {
22271  bPrecedence = -1;
22272  return false;
22273  } else if (bLower > aUpper) {
22274  bPrecedence = 1;
22275  return false;
22276  } else {
22277  if (aUpper > bUpper)
22278  bPrecedence = -1;
22279  else if (aUpper < bUpper)
22280  bPrecedence = 1;
22281 
22282  return true;
22283  }
22284 }
22285 
22299 QPointF QCPGraph::getFillBasePoint(QPointF matchingDataPoint) const {
22300  QCPAxis *keyAxis = mKeyAxis.data();
22301  QCPAxis *valueAxis = mValueAxis.data();
22302  if (!keyAxis || !valueAxis) {
22303  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22304  return QPointF();
22305  }
22306 
22307  QPointF result;
22309  if (keyAxis->orientation() == Qt::Horizontal) {
22310  result.setX(matchingDataPoint.x());
22311  result.setY(valueAxis->coordToPixel(0));
22312  } else // keyAxis->orientation() == Qt::Vertical
22313  {
22314  result.setX(valueAxis->coordToPixel(0));
22315  result.setY(matchingDataPoint.y());
22316  }
22317  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
22318  {
22319  // In logarithmic scaling we can't just draw to value 0 so we just fill
22320  // all the way to the axis which is in the direction towards 0
22321  if (keyAxis->orientation() == Qt::Vertical) {
22322  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
22323  (valueAxis->range().upper > 0 &&
22324  valueAxis->rangeReversed())) // if range is negative, zero is
22325  // on opposite side of key axis
22326  result.setX(keyAxis->axisRect()->right());
22327  else
22328  result.setX(keyAxis->axisRect()->left());
22329  result.setY(matchingDataPoint.y());
22330  } else if (keyAxis->axisType() == QCPAxis::atTop ||
22332  result.setX(matchingDataPoint.x());
22333  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
22334  (valueAxis->range().upper > 0 &&
22335  valueAxis->rangeReversed())) // if range is negative, zero is
22336  // on opposite side of key axis
22337  result.setY(keyAxis->axisRect()->top());
22338  else
22339  result.setY(keyAxis->axisRect()->bottom());
22340  }
22341  }
22342  return result;
22343 }
22344 
22364 const QPolygonF QCPGraph::getFillPolygon(const QVector<QPointF> *lineData,
22365  QCPDataRange segment) const {
22366  if (segment.size() < 2) return QPolygonF();
22367  QPolygonF result(segment.size() + 2);
22368 
22369  result[0] = getFillBasePoint(lineData->at(segment.begin()));
22370  std::copy(lineData->constBegin() + segment.begin(),
22371  lineData->constBegin() + segment.end(), result.begin() + 1);
22372  result[result.size() - 1] =
22373  getFillBasePoint(lineData->at(segment.end() - 1));
22374 
22375  return result;
22376 }
22377 
22398  const QVector<QPointF> *thisData,
22399  QCPDataRange thisSegment,
22400  const QVector<QPointF> *otherData,
22401  QCPDataRange otherSegment) const {
22402  if (!mChannelFillGraph) return QPolygonF();
22403 
22404  QCPAxis *keyAxis = mKeyAxis.data();
22405  QCPAxis *valueAxis = mValueAxis.data();
22406  if (!keyAxis || !valueAxis) {
22407  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
22408  return QPolygonF();
22409  }
22410  if (!mChannelFillGraph.data()->mKeyAxis) {
22411  qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid";
22412  return QPolygonF();
22413  }
22414 
22415  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() !=
22416  keyAxis->orientation())
22417  return QPolygonF(); // don't have same axis orientation, can't fill
22418  // that (Note: if keyAxis fits, valueAxis will fit
22419  // too, because it's always orthogonal to keyAxis)
22420 
22421  if (thisData->isEmpty()) return QPolygonF();
22422  QVector<QPointF> thisSegmentData(thisSegment.size());
22423  QVector<QPointF> otherSegmentData(otherSegment.size());
22424  std::copy(thisData->constBegin() + thisSegment.begin(),
22425  thisData->constBegin() + thisSegment.end(),
22426  thisSegmentData.begin());
22427  std::copy(otherData->constBegin() + otherSegment.begin(),
22428  otherData->constBegin() + otherSegment.end(),
22429  otherSegmentData.begin());
22430  // pointers to be able to swap them, depending which data range needs
22431  // cropping:
22432  QVector<QPointF> *staticData = &thisSegmentData;
22433  QVector<QPointF> *croppedData = &otherSegmentData;
22434 
22435  // crop both vectors to ranges in which the keys overlap (which coord is
22436  // key, depends on axisType):
22437  if (keyAxis->orientation() == Qt::Horizontal) {
22438  // x is key
22439  // crop lower bound:
22440  if (staticData->first().x() <
22441  croppedData->first().x()) // other one must be cropped
22442  qSwap(staticData, croppedData);
22443  const int lowBound =
22444  findIndexBelowX(croppedData, staticData->first().x());
22445  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
22446  croppedData->remove(0, lowBound);
22447  // set lowest point of cropped data to fit exactly key position of first
22448  // static data point via linear interpolation:
22449  if (croppedData->size() < 2)
22450  return QPolygonF(); // need at least two points for interpolation
22451  double slope;
22452  if (!qFuzzyCompare(croppedData->at(1).x(), croppedData->at(0).x()))
22453  slope = (croppedData->at(1).y() - croppedData->at(0).y()) /
22454  (croppedData->at(1).x() - croppedData->at(0).x());
22455  else
22456  slope = 0;
22457  (*croppedData)[0].setY(
22458  croppedData->at(0).y() +
22459  slope * (staticData->first().x() - croppedData->at(0).x()));
22460  (*croppedData)[0].setX(staticData->first().x());
22461 
22462  // crop upper bound:
22463  if (staticData->last().x() >
22464  croppedData->last().x()) // other one must be cropped
22465  qSwap(staticData, croppedData);
22466  int highBound = findIndexAboveX(croppedData, staticData->last().x());
22467  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
22468  croppedData->remove(highBound + 1,
22469  croppedData->size() - (highBound + 1));
22470  // set highest point of cropped data to fit exactly key position of last
22471  // static data point via linear interpolation:
22472  if (croppedData->size() < 2)
22473  return QPolygonF(); // need at least two points for interpolation
22474  const int li = croppedData->size() - 1; // last index
22475  if (!qFuzzyCompare(croppedData->at(li).x(),
22476  croppedData->at(li - 1).x()))
22477  slope = (croppedData->at(li).y() - croppedData->at(li - 1).y()) /
22478  (croppedData->at(li).x() - croppedData->at(li - 1).x());
22479  else
22480  slope = 0;
22481  (*croppedData)[li].setY(
22482  croppedData->at(li - 1).y() +
22483  slope * (staticData->last().x() - croppedData->at(li - 1).x()));
22484  (*croppedData)[li].setX(staticData->last().x());
22485  } else // mKeyAxis->orientation() == Qt::Vertical
22486  {
22487  // y is key
22488  // crop lower bound:
22489  if (staticData->first().y() <
22490  croppedData->first().y()) // other one must be cropped
22491  qSwap(staticData, croppedData);
22492  int lowBound = findIndexBelowY(croppedData, staticData->first().y());
22493  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
22494  croppedData->remove(0, lowBound);
22495  // set lowest point of cropped data to fit exactly key position of first
22496  // static data point via linear interpolation:
22497  if (croppedData->size() < 2)
22498  return QPolygonF(); // need at least two points for interpolation
22499  double slope;
22500  if (!qFuzzyCompare(croppedData->at(1).y(),
22501  croppedData->at(0).y())) // avoid division by zero
22502  // in step plots
22503  slope = (croppedData->at(1).x() - croppedData->at(0).x()) /
22504  (croppedData->at(1).y() - croppedData->at(0).y());
22505  else
22506  slope = 0;
22507  (*croppedData)[0].setX(
22508  croppedData->at(0).x() +
22509  slope * (staticData->first().y() - croppedData->at(0).y()));
22510  (*croppedData)[0].setY(staticData->first().y());
22511 
22512  // crop upper bound:
22513  if (staticData->last().y() >
22514  croppedData->last().y()) // other one must be cropped
22515  qSwap(staticData, croppedData);
22516  int highBound = findIndexAboveY(croppedData, staticData->last().y());
22517  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
22518  croppedData->remove(highBound + 1,
22519  croppedData->size() - (highBound + 1));
22520  // set highest point of cropped data to fit exactly key position of last
22521  // static data point via linear interpolation:
22522  if (croppedData->size() < 2)
22523  return QPolygonF(); // need at least two points for interpolation
22524  int li = croppedData->size() - 1; // last index
22525  if (!qFuzzyCompare(croppedData->at(li).y(),
22526  croppedData->at(li - 1).y())) // avoid division by
22527  // zero in step plots
22528  slope = (croppedData->at(li).x() - croppedData->at(li - 1).x()) /
22529  (croppedData->at(li).y() - croppedData->at(li - 1).y());
22530  else
22531  slope = 0;
22532  (*croppedData)[li].setX(
22533  croppedData->at(li - 1).x() +
22534  slope * (staticData->last().y() - croppedData->at(li - 1).y()));
22535  (*croppedData)[li].setY(staticData->last().y());
22536  }
22537 
22538  // return joined:
22539  for (int i = otherSegmentData.size() - 1; i >= 0;
22540  --i) // insert reversed, otherwise the polygon will be twisted
22541  thisSegmentData << otherSegmentData.at(i);
22542  return QPolygonF(thisSegmentData);
22543 }
22544 
22553 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const {
22554  for (int i = data->size() - 1; i >= 0; --i) {
22555  if (data->at(i).x() < x) {
22556  if (i < data->size() - 1)
22557  return i + 1;
22558  else
22559  return data->size() - 1;
22560  }
22561  }
22562  return -1;
22563 }
22564 
22573 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const {
22574  for (int i = 0; i < data->size(); ++i) {
22575  if (data->at(i).x() > x) {
22576  if (i > 0)
22577  return i - 1;
22578  else
22579  return 0;
22580  }
22581  }
22582  return -1;
22583 }
22584 
22593 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const {
22594  for (int i = data->size() - 1; i >= 0; --i) {
22595  if (data->at(i).y() < y) {
22596  if (i < data->size() - 1)
22597  return i + 1;
22598  else
22599  return data->size() - 1;
22600  }
22601  }
22602  return -1;
22603 }
22604 
22620  const QPointF &pixelPoint,
22621  QCPGraphDataContainer::const_iterator &closestData) const {
22622  closestData = mDataContainer->constEnd();
22623  if (mDataContainer->isEmpty()) return -1.0;
22624  if (mLineStyle == lsNone && mScatterStyle.isNone()) return -1.0;
22625 
22626  // calculate minimum distances to graph data points and find closestData
22627  // iterator:
22628  double minDistSqr = (std::numeric_limits<double>::max)();
22629  // determine which key range comes into question, taking selection tolerance
22630  // around pos into account:
22631  double posKeyMin, posKeyMax, dummy;
22632  pixelsToCoords(pixelPoint - QPointF(mParentPlot->selectionTolerance(),
22634  posKeyMin, dummy);
22635  pixelsToCoords(pixelPoint + QPointF(mParentPlot->selectionTolerance(),
22637  posKeyMax, dummy);
22638  if (posKeyMin > posKeyMax) qSwap(posKeyMin, posKeyMax);
22639  // iterate over found data points and then choose the one with the shortest
22640  // distance to pos:
22642  mDataContainer->findBegin(posKeyMin, true);
22644  mDataContainer->findEnd(posKeyMax, true);
22645  for (QCPGraphDataContainer::const_iterator it = begin; it != end; ++it) {
22646  const double currentDistSqr =
22647  QCPVector2D(coordsToPixels(it->key, it->value) - pixelPoint)
22648  .lengthSquared();
22649  if (currentDistSqr < minDistSqr) {
22650  minDistSqr = currentDistSqr;
22651  closestData = it;
22652  }
22653  }
22654 
22655  // calculate distance to graph line if there is one (if so, will probably be
22656  // smaller than distance to closest data point):
22657  if (mLineStyle != lsNone) {
22658  // line displayed, calculate distance to line segments:
22659  QVector<QPointF> lineData;
22660  getLines(&lineData, QCPDataRange(0, dataCount()));
22661  QCPVector2D p(pixelPoint);
22662  const int step = mLineStyle == lsImpulse
22663  ? 2
22664  : 1; // impulse plot differs from other line
22665  // styles in that the lineData points are
22666  // only pairwise connected
22667  for (int i = 0; i < lineData.size() - 1; i += step) {
22668  const double currentDistSqr =
22669  p.distanceSquaredToLine(lineData.at(i), lineData.at(i + 1));
22670  if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
22671  }
22672  }
22673 
22674  return qSqrt(minDistSqr);
22675 }
22676 
22685 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const {
22686  for (int i = 0; i < data->size(); ++i) {
22687  if (data->at(i).y() > y) {
22688  if (i > 0)
22689  return i - 1;
22690  else
22691  return 0;
22692  }
22693  }
22694  return -1;
22695 }
22696 /* end of 'src/plottables/plottable-graph.cpp' */
22697 
22698 /* including file 'src/plottables/plottable-curve.cpp', size 63742 */
22699 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
22700 
22704 
22723 /* start documentation of inline functions */
22724 
22776 /* end documentation of inline functions */
22777 
22781 QCPCurveData::QCPCurveData() : t(0), key(0), value(0) {}
22782 
22786 QCPCurveData::QCPCurveData(double t, double key, double value)
22787  : t(t), key(key), value(value) {}
22788 
22792 
22833 /* start of documentation of inline functions */
22834 
22843 /* end of documentation of inline functions */
22844 
22857 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
22858  : QCPAbstractPlottable1D<QCPCurveData>(keyAxis, valueAxis) {
22859  // modify inherited properties from abstract plottable:
22860  setPen(QPen(Qt::blue, 0));
22861  setBrush(Qt::NoBrush);
22862 
22865  setScatterSkip(0);
22866 }
22867 
22869 
22887 void QCPCurve::setData(QSharedPointer<QCPCurveDataContainer> data) {
22888  mDataContainer = data;
22889 }
22890 
22903 void QCPCurve::setData(const QVector<double> &t,
22904  const QVector<double> &keys,
22905  const QVector<double> &values,
22906  bool alreadySorted) {
22907  mDataContainer->clear();
22908  addData(t, keys, values, alreadySorted);
22909 }
22910 
22922 void QCPCurve::setData(const QVector<double> &keys,
22923  const QVector<double> &values) {
22924  mDataContainer->clear();
22925  addData(keys, values);
22926 }
22927 
22936  mScatterStyle = style;
22937 }
22938 
22951 void QCPCurve::setScatterSkip(int skip) { mScatterSkip = qMax(0, skip); }
22952 
22962 
22976 void QCPCurve::addData(const QVector<double> &t,
22977  const QVector<double> &keys,
22978  const QVector<double> &values,
22979  bool alreadySorted) {
22980  if (t.size() != keys.size() || t.size() != values.size())
22981  qDebug() << Q_FUNC_INFO
22982  << "ts, keys and values have different sizes:" << t.size()
22983  << keys.size() << values.size();
22984  const int n = qMin(qMin(t.size(), keys.size()), values.size());
22985  QVector<QCPCurveData> tempData(n);
22986  QVector<QCPCurveData>::iterator it = tempData.begin();
22987  const QVector<QCPCurveData>::iterator itEnd = tempData.end();
22988  int i = 0;
22989  while (it != itEnd) {
22990  it->t = t[i];
22991  it->key = keys[i];
22992  it->value = values[i];
22993  ++it;
22994  ++i;
22995  }
22996  mDataContainer->add(tempData,
22997  alreadySorted); // don't modify tempData beyond this to
22998  // prevent copy on write
22999 }
23000 
23013 void QCPCurve::addData(const QVector<double> &keys,
23014  const QVector<double> &values) {
23015  if (keys.size() != values.size())
23016  qDebug() << Q_FUNC_INFO
23017  << "keys and values have different sizes:" << keys.size()
23018  << values.size();
23019  const int n = qMin(keys.size(), values.size());
23020  double tStart;
23021  if (!mDataContainer->isEmpty())
23022  tStart = (mDataContainer->constEnd() - 1)->t + 1.0;
23023  else
23024  tStart = 0;
23025  QVector<QCPCurveData> tempData(n);
23026  QVector<QCPCurveData>::iterator it = tempData.begin();
23027  const QVector<QCPCurveData>::iterator itEnd = tempData.end();
23028  int i = 0;
23029  while (it != itEnd) {
23030  it->t = tStart + i;
23031  it->key = keys[i];
23032  it->value = values[i];
23033  ++it;
23034  ++i;
23035  }
23036  mDataContainer->add(tempData, true); // don't modify tempData beyond this
23037  // to prevent copy on write
23038 }
23039 
23046 void QCPCurve::addData(double t, double key, double value) {
23047  mDataContainer->add(QCPCurveData(t, key, value));
23048 }
23049 
23061 void QCPCurve::addData(double key, double value) {
23062  if (!mDataContainer->isEmpty())
23064  (mDataContainer->constEnd() - 1)->t + 1.0, key, value));
23065  else
23066  mDataContainer->add(QCPCurveData(0.0, key, value));
23067 }
23068 
23077 double QCPCurve::selectTest(const QPointF &pos,
23078  bool onlySelectable,
23079  QVariant *details) const {
23080  if ((onlySelectable && mSelectable == QCP::stNone) ||
23081  mDataContainer->isEmpty())
23082  return -1;
23083  if (!mKeyAxis || !mValueAxis) return -1;
23084 
23085  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
23086  QCPCurveDataContainer::const_iterator closestDataPoint =
23087  mDataContainer->constEnd();
23088  double result = pointDistance(pos, closestDataPoint);
23089  if (details) {
23090  int pointIndex = closestDataPoint - mDataContainer->constBegin();
23091  details->setValue(
23092  QCPDataSelection(QCPDataRange(pointIndex, pointIndex + 1)));
23093  }
23094  return result;
23095  } else
23096  return -1;
23097 }
23098 
23099 /* inherits documentation from base class */
23101  QCP::SignDomain inSignDomain) const {
23102  return mDataContainer->keyRange(foundRange, inSignDomain);
23103 }
23104 
23105 /* inherits documentation from base class */
23107  QCP::SignDomain inSignDomain,
23108  const QCPRange &inKeyRange) const {
23109  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
23110 }
23111 
23112 /* inherits documentation from base class */
23113 void QCPCurve::draw(QCPPainter *painter) {
23114  if (mDataContainer->isEmpty()) return;
23115 
23116  // allocate line vector:
23117  QVector<QPointF> lines, scatters;
23118 
23119  // loop over and draw segments of unselected/selected data:
23120  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
23121  getDataSegments(selectedSegments, unselectedSegments);
23122  allSegments << unselectedSegments << selectedSegments;
23123  for (int i = 0; i < allSegments.size(); ++i) {
23124  bool isSelectedSegment = i >= unselectedSegments.size();
23125 
23126  // fill with curve data:
23127  QPen finalCurvePen =
23128  mPen; // determine the final pen already here, because the line
23129  // optimization depends on its stroke width
23130  if (isSelectedSegment && mSelectionDecorator)
23131  finalCurvePen = mSelectionDecorator->pen();
23132 
23133  QCPDataRange lineDataRange =
23134  isSelectedSegment
23135  ? allSegments.at(i)
23136  : allSegments.at(i).adjusted(
23137  -1,
23138  1); // unselected segments extend lines to
23139  // bordering selected data point (safe to
23140  // exceed total data bounds in first/last
23141  // segment, getCurveLines takes care)
23142  getCurveLines(&lines, lineDataRange, finalCurvePen.widthF());
23143 
23144  // check data validity if flag set:
23145 #ifdef QCUSTOMPLOT_CHECK_DATA
23147  mDataContainer->constBegin();
23148  it != mDataContainer->constEnd(); ++it) {
23149  if (QCP::isInvalidData(it->t) ||
23150  QCP::isInvalidData(it->key, it->value))
23151  qDebug() << Q_FUNC_INFO << "Data point at" << it->key
23152  << "invalid."
23153  << "Plottable name:" << name();
23154  }
23155 #endif
23156 
23157  // draw curve fill:
23158  applyFillAntialiasingHint(painter);
23159  if (isSelectedSegment && mSelectionDecorator)
23160  mSelectionDecorator->applyBrush(painter);
23161  else
23162  painter->setBrush(mBrush);
23163  painter->setPen(Qt::NoPen);
23164  if (painter->brush().style() != Qt::NoBrush &&
23165  painter->brush().color().alpha() != 0)
23166  painter->drawPolygon(QPolygonF(lines));
23167 
23168  // draw curve line:
23169  if (mLineStyle != lsNone) {
23170  painter->setPen(finalCurvePen);
23171  painter->setBrush(Qt::NoBrush);
23172  drawCurveLine(painter, lines);
23173  }
23174 
23175  // draw scatters:
23176  QCPScatterStyle finalScatterStyle = mScatterStyle;
23177  if (isSelectedSegment && mSelectionDecorator)
23178  finalScatterStyle =
23180  if (!finalScatterStyle.isNone()) {
23181  getScatters(&scatters, allSegments.at(i), finalScatterStyle.size());
23182  drawScatterPlot(painter, scatters, finalScatterStyle);
23183  }
23184  }
23185 
23186  // draw other selection decoration that isn't just line/scatter pens and
23187  // brushes:
23188  if (mSelectionDecorator)
23190 }
23191 
23192 /* inherits documentation from base class */
23193 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const {
23194  // draw fill:
23195  if (mBrush.style() != Qt::NoBrush) {
23196  applyFillAntialiasingHint(painter);
23197  painter->fillRect(QRectF(rect.left(), rect.top() + rect.height() / 2.0,
23198  rect.width(), rect.height() / 3.0),
23199  mBrush);
23200  }
23201  // draw line vertically centered:
23202  if (mLineStyle != lsNone) {
23204  painter->setPen(mPen);
23205  painter->drawLine(QLineF(
23206  rect.left(), rect.top() + rect.height() / 2.0, rect.right() + 5,
23207  rect.top() + rect.height() /
23208  2.0)); // +5 on x2 else last segment is
23209  // missing from dashed/dotted pens
23210  }
23211  // draw scatter symbol:
23212  if (!mScatterStyle.isNone()) {
23214  // scale scatter pixmap if it's too large to fit in legend icon rect:
23216  (mScatterStyle.pixmap().size().width() > rect.width() ||
23217  mScatterStyle.pixmap().size().height() > rect.height())) {
23218  QCPScatterStyle scaledStyle(mScatterStyle);
23219  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(
23220  rect.size().toSize(), Qt::KeepAspectRatio,
23221  Qt::SmoothTransformation));
23222  scaledStyle.applyTo(painter, mPen);
23223  scaledStyle.drawShape(painter, QRectF(rect).center());
23224  } else {
23225  mScatterStyle.applyTo(painter, mPen);
23226  mScatterStyle.drawShape(painter, QRectF(rect).center());
23227  }
23228  }
23229 }
23230 
23238  const QVector<QPointF> &lines) const {
23239  if (painter->pen().style() != Qt::NoPen &&
23240  painter->pen().color().alpha() != 0) {
23242  drawPolyline(painter, lines);
23243  }
23244 }
23245 
23255  const QVector<QPointF> &points,
23256  const QCPScatterStyle &style) const {
23257  // draw scatter point symbols:
23259  style.applyTo(painter, mPen);
23260  for (int i = 0; i < points.size(); ++i)
23261  if (!qIsNaN(points.at(i).x()) && !qIsNaN(points.at(i).y()))
23262  style.drawShape(painter, points.at(i));
23263 }
23264 
23296 void QCPCurve::getCurveLines(QVector<QPointF> *lines,
23297  const QCPDataRange &dataRange,
23298  double penWidth) const {
23299  if (!lines) return;
23300  lines->clear();
23301  QCPAxis *keyAxis = mKeyAxis.data();
23302  QCPAxis *valueAxis = mValueAxis.data();
23303  if (!keyAxis || !valueAxis) {
23304  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
23305  return;
23306  }
23307 
23308  // add margins to rect to compensate for stroke width
23309  const double strokeMargin = qMax(
23310  qreal(1.0), qreal(penWidth * 0.75)); // stroke radius + 50% safety
23311  const double keyMin = keyAxis->pixelToCoord(
23313  strokeMargin * keyAxis->pixelOrientation());
23314  const double keyMax = keyAxis->pixelToCoord(
23316  strokeMargin * keyAxis->pixelOrientation());
23317  const double valueMin = valueAxis->pixelToCoord(
23319  strokeMargin * valueAxis->pixelOrientation());
23320  const double valueMax = valueAxis->pixelToCoord(
23322  strokeMargin * valueAxis->pixelOrientation());
23324  mDataContainer->constBegin();
23326  mDataContainer->limitIteratorsToDataRange(itBegin, itEnd, dataRange);
23327  if (itBegin == itEnd) return;
23329  QCPCurveDataContainer::const_iterator prevIt = itEnd - 1;
23330  int prevRegion = getRegion(prevIt->key, prevIt->value, keyMin, valueMax,
23331  keyMax, valueMin);
23332  QVector<QPointF>
23333  trailingPoints; // points that must be applied after all other
23334  // points (are generated only when handling first
23335  // point to get virtual segment between last and
23336  // first point right)
23337  while (it != itEnd) {
23338  const int currentRegion = getRegion(it->key, it->value, keyMin,
23339  valueMax, keyMax, valueMin);
23340  if (currentRegion !=
23341  prevRegion) // changed region, possibly need to add some optimized
23342  // edge points or original points if entering R
23343  {
23344  if (currentRegion !=
23345  5) // segment doesn't end in R, so it's a candidate for removal
23346  {
23347  QPointF crossA, crossB;
23348  if (prevRegion ==
23349  5) // we're coming from R, so add this point optimized
23350  {
23351  lines->append(getOptimizedPoint(
23352  currentRegion, it->key, it->value, prevIt->key,
23353  prevIt->value, keyMin, valueMax, keyMax, valueMin));
23354  // in the situations 5->1/7/9/3 the segment may leave R and
23355  // directly cross through two outer regions. In these cases
23356  // we need to add an additional corner point
23357  *lines << getOptimizedCornerPoints(
23358  prevRegion, currentRegion, prevIt->key,
23359  prevIt->value, it->key, it->value, keyMin, valueMax,
23360  keyMax, valueMin);
23361  } else if (mayTraverse(prevRegion, currentRegion) &&
23362  getTraverse(prevIt->key, prevIt->value, it->key,
23363  it->value, keyMin, valueMax, keyMax,
23364  valueMin, crossA, crossB)) {
23365  // add the two cross points optimized if segment crosses R
23366  // and if segment isn't virtual zeroth segment between last
23367  // and first curve point:
23368  QVector<QPointF> beforeTraverseCornerPoints,
23369  afterTraverseCornerPoints;
23370  getTraverseCornerPoints(prevRegion, currentRegion, keyMin,
23371  valueMax, keyMax, valueMin,
23372  beforeTraverseCornerPoints,
23373  afterTraverseCornerPoints);
23374  if (it != itBegin) {
23375  *lines << beforeTraverseCornerPoints;
23376  lines->append(crossA);
23377  lines->append(crossB);
23378  *lines << afterTraverseCornerPoints;
23379  } else {
23380  lines->append(crossB);
23381  *lines << afterTraverseCornerPoints;
23382  trailingPoints << beforeTraverseCornerPoints << crossA;
23383  }
23384  } else // doesn't cross R, line is just moving around in
23385  // outside regions, so only need to add optimized
23386  // point(s) at the boundary corner(s)
23387  {
23388  *lines << getOptimizedCornerPoints(
23389  prevRegion, currentRegion, prevIt->key,
23390  prevIt->value, it->key, it->value, keyMin, valueMax,
23391  keyMax, valueMin);
23392  }
23393  } else // segment does end in R, so we add previous point optimized
23394  // and this point at original position
23395  {
23396  if (it == itBegin) // it is first point in curve and prevIt is
23397  // last one. So save optimized point for
23398  // adding it to the lineData in the end
23399  trailingPoints << getOptimizedPoint(
23400  prevRegion, prevIt->key, prevIt->value, it->key,
23401  it->value, keyMin, valueMax, keyMax, valueMin);
23402  else
23403  lines->append(getOptimizedPoint(
23404  prevRegion, prevIt->key, prevIt->value, it->key,
23405  it->value, keyMin, valueMax, keyMax, valueMin));
23406  lines->append(coordsToPixels(it->key, it->value));
23407  }
23408  } else // region didn't change
23409  {
23410  if (currentRegion == 5) // still in R, keep adding original points
23411  {
23412  lines->append(coordsToPixels(it->key, it->value));
23413  } else // still outside R, no need to add anything
23414  {
23415  // see how this is not doing anything? That's the main
23416  // optimization...
23417  }
23418  }
23419  prevIt = it;
23420  prevRegion = currentRegion;
23421  ++it;
23422  }
23423  *lines << trailingPoints;
23424 }
23425 
23447 void QCPCurve::getScatters(QVector<QPointF> *scatters,
23448  const QCPDataRange &dataRange,
23449  double scatterWidth) const {
23450  if (!scatters) return;
23451  scatters->clear();
23452  QCPAxis *keyAxis = mKeyAxis.data();
23453  QCPAxis *valueAxis = mValueAxis.data();
23454  if (!keyAxis || !valueAxis) {
23455  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
23456  return;
23457  }
23458 
23461  mDataContainer->limitIteratorsToDataRange(begin, end, dataRange);
23462  if (begin == end) return;
23463  const int scatterModulo = mScatterSkip + 1;
23464  const bool doScatterSkip = mScatterSkip > 0;
23465  int endIndex = end - mDataContainer->constBegin();
23466 
23467  QCPRange keyRange = keyAxis->range();
23468  QCPRange valueRange = valueAxis->range();
23469  // extend range to include width of scatter symbols:
23470  keyRange.lower =
23472  scatterWidth * keyAxis->pixelOrientation());
23473  keyRange.upper =
23475  scatterWidth * keyAxis->pixelOrientation());
23476  valueRange.lower = valueAxis->pixelToCoord(
23477  valueAxis->coordToPixel(valueRange.lower) -
23478  scatterWidth * valueAxis->pixelOrientation());
23479  valueRange.upper = valueAxis->pixelToCoord(
23480  valueAxis->coordToPixel(valueRange.upper) +
23481  scatterWidth * valueAxis->pixelOrientation());
23482 
23484  int itIndex = begin - mDataContainer->constBegin();
23485  while (doScatterSkip && it != end &&
23486  itIndex % scatterModulo !=
23487  0) // advance begin iterator to first non-skipped scatter
23488  {
23489  ++itIndex;
23490  ++it;
23491  }
23492  if (keyAxis->orientation() == Qt::Vertical) {
23493  while (it != end) {
23494  if (!qIsNaN(it->value) && keyRange.contains(it->key) &&
23495  valueRange.contains(it->value))
23496  scatters->append(QPointF(valueAxis->coordToPixel(it->value),
23497  keyAxis->coordToPixel(it->key)));
23498 
23499  // advance iterator to next (non-skipped) data point:
23500  if (!doScatterSkip)
23501  ++it;
23502  else {
23503  itIndex += scatterModulo;
23504  if (itIndex < endIndex) // make sure we didn't jump over end
23505  it += scatterModulo;
23506  else {
23507  it = end;
23508  itIndex = endIndex;
23509  }
23510  }
23511  }
23512  } else {
23513  while (it != end) {
23514  if (!qIsNaN(it->value) && keyRange.contains(it->key) &&
23515  valueRange.contains(it->value))
23516  scatters->append(QPointF(keyAxis->coordToPixel(it->key),
23517  valueAxis->coordToPixel(it->value)));
23518 
23519  // advance iterator to next (non-skipped) data point:
23520  if (!doScatterSkip)
23521  ++it;
23522  else {
23523  itIndex += scatterModulo;
23524  if (itIndex < endIndex) // make sure we didn't jump over end
23525  it += scatterModulo;
23526  else {
23527  it = end;
23528  itIndex = endIndex;
23529  }
23530  }
23531  }
23532  }
23533 }
23534 
23556 int QCPCurve::getRegion(double key,
23557  double value,
23558  double keyMin,
23559  double valueMax,
23560  double keyMax,
23561  double valueMin) const {
23562  if (key < keyMin) // region 123
23563  {
23564  if (value > valueMax)
23565  return 1;
23566  else if (value < valueMin)
23567  return 3;
23568  else
23569  return 2;
23570  } else if (key > keyMax) // region 789
23571  {
23572  if (value > valueMax)
23573  return 7;
23574  else if (value < valueMin)
23575  return 9;
23576  else
23577  return 8;
23578  } else // region 456
23579  {
23580  if (value > valueMax)
23581  return 4;
23582  else if (value < valueMin)
23583  return 6;
23584  else
23585  return 5;
23586  }
23587 }
23588 
23606 QPointF QCPCurve::getOptimizedPoint(int otherRegion,
23607  double otherKey,
23608  double otherValue,
23609  double key,
23610  double value,
23611  double keyMin,
23612  double valueMax,
23613  double keyMax,
23614  double valueMin) const {
23615  // The intersection point interpolation here is done in pixel coordinates,
23616  // so we don't need to differentiate between different axis scale types.
23617  // Note that the nomenclature top/left/bottom/right/min/max is with respect
23618  // to the rect in plot coordinates, wich may be different in pixel
23619  // coordinates (horz/vert key axes, reversed ranges)
23620 
23621  const double keyMinPx = mKeyAxis->coordToPixel(keyMin);
23622  const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);
23623  const double valueMinPx = mValueAxis->coordToPixel(valueMin);
23624  const double valueMaxPx = mValueAxis->coordToPixel(valueMax);
23625  const double otherValuePx = mValueAxis->coordToPixel(otherValue);
23626  const double valuePx = mValueAxis->coordToPixel(value);
23627  const double otherKeyPx = mKeyAxis->coordToPixel(otherKey);
23628  const double keyPx = mKeyAxis->coordToPixel(key);
23629  double intersectKeyPx = keyMinPx; // initial key just a fail-safe
23630  double intersectValuePx = valueMinPx; // initial value just a fail-safe
23631  switch (otherRegion) {
23632  case 1: // top and left edge
23633  {
23634  intersectValuePx = valueMaxPx;
23635  intersectKeyPx = otherKeyPx +
23636  (keyPx - otherKeyPx) / (valuePx - otherValuePx) *
23637  (intersectValuePx - otherValuePx);
23638  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) ||
23639  intersectKeyPx >
23640  qMax(keyMinPx,
23641  keyMaxPx)) // check whether top edge is not
23642  // intersected, then it must be left
23643  // edge (qMin/qMax necessary since axes
23644  // may be reversed)
23645  {
23646  intersectKeyPx = keyMinPx;
23647  intersectValuePx =
23648  otherValuePx + (valuePx - otherValuePx) /
23649  (keyPx - otherKeyPx) *
23650  (intersectKeyPx - otherKeyPx);
23651  }
23652  break;
23653  }
23654  case 2: // left edge
23655  {
23656  intersectKeyPx = keyMinPx;
23657  intersectValuePx = otherValuePx +
23658  (valuePx - otherValuePx) / (keyPx - otherKeyPx) *
23659  (intersectKeyPx - otherKeyPx);
23660  break;
23661  }
23662  case 3: // bottom and left edge
23663  {
23664  intersectValuePx = valueMinPx;
23665  intersectKeyPx = otherKeyPx +
23666  (keyPx - otherKeyPx) / (valuePx - otherValuePx) *
23667  (intersectValuePx - otherValuePx);
23668  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) ||
23669  intersectKeyPx >
23670  qMax(keyMinPx,
23671  keyMaxPx)) // check whether bottom edge is not
23672  // intersected, then it must be left
23673  // edge (qMin/qMax necessary since axes
23674  // may be reversed)
23675  {
23676  intersectKeyPx = keyMinPx;
23677  intersectValuePx =
23678  otherValuePx + (valuePx - otherValuePx) /
23679  (keyPx - otherKeyPx) *
23680  (intersectKeyPx - otherKeyPx);
23681  }
23682  break;
23683  }
23684  case 4: // top edge
23685  {
23686  intersectValuePx = valueMaxPx;
23687  intersectKeyPx = otherKeyPx +
23688  (keyPx - otherKeyPx) / (valuePx - otherValuePx) *
23689  (intersectValuePx - otherValuePx);
23690  break;
23691  }
23692  case 5: {
23693  break; // case 5 shouldn't happen for this function but we add it
23694  // anyway to prevent potential discontinuity in branch table
23695  }
23696  case 6: // bottom edge
23697  {
23698  intersectValuePx = valueMinPx;
23699  intersectKeyPx = otherKeyPx +
23700  (keyPx - otherKeyPx) / (valuePx - otherValuePx) *
23701  (intersectValuePx - otherValuePx);
23702  break;
23703  }
23704  case 7: // top and right edge
23705  {
23706  intersectValuePx = valueMaxPx;
23707  intersectKeyPx = otherKeyPx +
23708  (keyPx - otherKeyPx) / (valuePx - otherValuePx) *
23709  (intersectValuePx - otherValuePx);
23710  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) ||
23711  intersectKeyPx >
23712  qMax(keyMinPx,
23713  keyMaxPx)) // check whether top edge is not
23714  // intersected, then it must be right
23715  // edge (qMin/qMax necessary since axes
23716  // may be reversed)
23717  {
23718  intersectKeyPx = keyMaxPx;
23719  intersectValuePx =
23720  otherValuePx + (valuePx - otherValuePx) /
23721  (keyPx - otherKeyPx) *
23722  (intersectKeyPx - otherKeyPx);
23723  }
23724  break;
23725  }
23726  case 8: // right edge
23727  {
23728  intersectKeyPx = keyMaxPx;
23729  intersectValuePx = otherValuePx +
23730  (valuePx - otherValuePx) / (keyPx - otherKeyPx) *
23731  (intersectKeyPx - otherKeyPx);
23732  break;
23733  }
23734  case 9: // bottom and right edge
23735  {
23736  intersectValuePx = valueMinPx;
23737  intersectKeyPx = otherKeyPx +
23738  (keyPx - otherKeyPx) / (valuePx - otherValuePx) *
23739  (intersectValuePx - otherValuePx);
23740  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) ||
23741  intersectKeyPx >
23742  qMax(keyMinPx,
23743  keyMaxPx)) // check whether bottom edge is not
23744  // intersected, then it must be right
23745  // edge (qMin/qMax necessary since axes
23746  // may be reversed)
23747  {
23748  intersectKeyPx = keyMaxPx;
23749  intersectValuePx =
23750  otherValuePx + (valuePx - otherValuePx) /
23751  (keyPx - otherKeyPx) *
23752  (intersectKeyPx - otherKeyPx);
23753  }
23754  break;
23755  }
23756  }
23757  if (mKeyAxis->orientation() == Qt::Horizontal)
23758  return QPointF(intersectKeyPx, intersectValuePx);
23759  else
23760  return QPointF(intersectValuePx, intersectKeyPx);
23761 }
23762 
23784 QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion,
23785  int currentRegion,
23786  double prevKey,
23787  double prevValue,
23788  double key,
23789  double value,
23790  double keyMin,
23791  double valueMax,
23792  double keyMax,
23793  double valueMin) const {
23794  QVector<QPointF> result;
23795  switch (prevRegion) {
23796  case 1: {
23797  switch (currentRegion) {
23798  case 2: {
23799  result << coordsToPixels(keyMin, valueMax);
23800  break;
23801  }
23802  case 4: {
23803  result << coordsToPixels(keyMin, valueMax);
23804  break;
23805  }
23806  case 3: {
23807  result << coordsToPixels(keyMin, valueMax)
23808  << coordsToPixels(keyMin, valueMin);
23809  break;
23810  }
23811  case 7: {
23812  result << coordsToPixels(keyMin, valueMax)
23813  << coordsToPixels(keyMax, valueMax);
23814  break;
23815  }
23816  case 6: {
23817  result << coordsToPixels(keyMin, valueMax)
23818  << coordsToPixels(keyMin, valueMin);
23819  result.append(result.last());
23820  break;
23821  }
23822  case 8: {
23823  result << coordsToPixels(keyMin, valueMax)
23824  << coordsToPixels(keyMax, valueMax);
23825  result.append(result.last());
23826  break;
23827  }
23828  case 9: { // in this case we need another distinction of cases:
23829  // segment may pass below or above rect, requiring
23830  // either bottom right or top left corner points
23831  if ((value - prevValue) / (key - prevKey) * (keyMin - key) +
23832  value <
23833  valueMin) // segment passes below R
23834  {
23835  result << coordsToPixels(keyMin, valueMax)
23836  << coordsToPixels(keyMin, valueMin);
23837  result.append(result.last());
23838  result << coordsToPixels(keyMax, valueMin);
23839  } else {
23840  result << coordsToPixels(keyMin, valueMax)
23841  << coordsToPixels(keyMax, valueMax);
23842  result.append(result.last());
23843  result << coordsToPixels(keyMax, valueMin);
23844  }
23845  break;
23846  }
23847  }
23848  break;
23849  }
23850  case 2: {
23851  switch (currentRegion) {
23852  case 1: {
23853  result << coordsToPixels(keyMin, valueMax);
23854  break;
23855  }
23856  case 3: {
23857  result << coordsToPixels(keyMin, valueMin);
23858  break;
23859  }
23860  case 4: {
23861  result << coordsToPixels(keyMin, valueMax);
23862  result.append(result.last());
23863  break;
23864  }
23865  case 6: {
23866  result << coordsToPixels(keyMin, valueMin);
23867  result.append(result.last());
23868  break;
23869  }
23870  case 7: {
23871  result << coordsToPixels(keyMin, valueMax);
23872  result.append(result.last());
23873  result << coordsToPixels(keyMax, valueMax);
23874  break;
23875  }
23876  case 9: {
23877  result << coordsToPixels(keyMin, valueMin);
23878  result.append(result.last());
23879  result << coordsToPixels(keyMax, valueMin);
23880  break;
23881  }
23882  }
23883  break;
23884  }
23885  case 3: {
23886  switch (currentRegion) {
23887  case 2: {
23888  result << coordsToPixels(keyMin, valueMin);
23889  break;
23890  }
23891  case 6: {
23892  result << coordsToPixels(keyMin, valueMin);
23893  break;
23894  }
23895  case 1: {
23896  result << coordsToPixels(keyMin, valueMin)
23897  << coordsToPixels(keyMin, valueMax);
23898  break;
23899  }
23900  case 9: {
23901  result << coordsToPixels(keyMin, valueMin)
23902  << coordsToPixels(keyMax, valueMin);
23903  break;
23904  }
23905  case 4: {
23906  result << coordsToPixels(keyMin, valueMin)
23907  << coordsToPixels(keyMin, valueMax);
23908  result.append(result.last());
23909  break;
23910  }
23911  case 8: {
23912  result << coordsToPixels(keyMin, valueMin)
23913  << coordsToPixels(keyMax, valueMin);
23914  result.append(result.last());
23915  break;
23916  }
23917  case 7: { // in this case we need another distinction of cases:
23918  // segment may pass below or above rect, requiring
23919  // either bottom right or top left corner points
23920  if ((value - prevValue) / (key - prevKey) * (keyMax - key) +
23921  value <
23922  valueMin) // segment passes below R
23923  {
23924  result << coordsToPixels(keyMin, valueMin)
23925  << coordsToPixels(keyMax, valueMin);
23926  result.append(result.last());
23927  result << coordsToPixels(keyMax, valueMax);
23928  } else {
23929  result << coordsToPixels(keyMin, valueMin)
23930  << coordsToPixels(keyMin, valueMax);
23931  result.append(result.last());
23932  result << coordsToPixels(keyMax, valueMax);
23933  }
23934  break;
23935  }
23936  }
23937  break;
23938  }
23939  case 4: {
23940  switch (currentRegion) {
23941  case 1: {
23942  result << coordsToPixels(keyMin, valueMax);
23943  break;
23944  }
23945  case 7: {
23946  result << coordsToPixels(keyMax, valueMax);
23947  break;
23948  }
23949  case 2: {
23950  result << coordsToPixels(keyMin, valueMax);
23951  result.append(result.last());
23952  break;
23953  }
23954  case 8: {
23955  result << coordsToPixels(keyMax, valueMax);
23956  result.append(result.last());
23957  break;
23958  }
23959  case 3: {
23960  result << coordsToPixels(keyMin, valueMax);
23961  result.append(result.last());
23962  result << coordsToPixels(keyMin, valueMin);
23963  break;
23964  }
23965  case 9: {
23966  result << coordsToPixels(keyMax, valueMax);
23967  result.append(result.last());
23968  result << coordsToPixels(keyMax, valueMin);
23969  break;
23970  }
23971  }
23972  break;
23973  }
23974  case 5: {
23975  switch (currentRegion) {
23976  case 1: {
23977  result << coordsToPixels(keyMin, valueMax);
23978  break;
23979  }
23980  case 7: {
23981  result << coordsToPixels(keyMax, valueMax);
23982  break;
23983  }
23984  case 9: {
23985  result << coordsToPixels(keyMax, valueMin);
23986  break;
23987  }
23988  case 3: {
23989  result << coordsToPixels(keyMin, valueMin);
23990  break;
23991  }
23992  }
23993  break;
23994  }
23995  case 6: {
23996  switch (currentRegion) {
23997  case 3: {
23998  result << coordsToPixels(keyMin, valueMin);
23999  break;
24000  }
24001  case 9: {
24002  result << coordsToPixels(keyMax, valueMin);
24003  break;
24004  }
24005  case 2: {
24006  result << coordsToPixels(keyMin, valueMin);
24007  result.append(result.last());
24008  break;
24009  }
24010  case 8: {
24011  result << coordsToPixels(keyMax, valueMin);
24012  result.append(result.last());
24013  break;
24014  }
24015  case 1: {
24016  result << coordsToPixels(keyMin, valueMin);
24017  result.append(result.last());
24018  result << coordsToPixels(keyMin, valueMax);
24019  break;
24020  }
24021  case 7: {
24022  result << coordsToPixels(keyMax, valueMin);
24023  result.append(result.last());
24024  result << coordsToPixels(keyMax, valueMax);
24025  break;
24026  }
24027  }
24028  break;
24029  }
24030  case 7: {
24031  switch (currentRegion) {
24032  case 4: {
24033  result << coordsToPixels(keyMax, valueMax);
24034  break;
24035  }
24036  case 8: {
24037  result << coordsToPixels(keyMax, valueMax);
24038  break;
24039  }
24040  case 1: {
24041  result << coordsToPixels(keyMax, valueMax)
24042  << coordsToPixels(keyMin, valueMax);
24043  break;
24044  }
24045  case 9: {
24046  result << coordsToPixels(keyMax, valueMax)
24047  << coordsToPixels(keyMax, valueMin);
24048  break;
24049  }
24050  case 2: {
24051  result << coordsToPixels(keyMax, valueMax)
24052  << coordsToPixels(keyMin, valueMax);
24053  result.append(result.last());
24054  break;
24055  }
24056  case 6: {
24057  result << coordsToPixels(keyMax, valueMax)
24058  << coordsToPixels(keyMax, valueMin);
24059  result.append(result.last());
24060  break;
24061  }
24062  case 3: { // in this case we need another distinction of cases:
24063  // segment may pass below or above rect, requiring
24064  // either bottom right or top left corner points
24065  if ((value - prevValue) / (key - prevKey) * (keyMax - key) +
24066  value <
24067  valueMin) // segment passes below R
24068  {
24069  result << coordsToPixels(keyMax, valueMax)
24070  << coordsToPixels(keyMax, valueMin);
24071  result.append(result.last());
24072  result << coordsToPixels(keyMin, valueMin);
24073  } else {
24074  result << coordsToPixels(keyMax, valueMax)
24075  << coordsToPixels(keyMin, valueMax);
24076  result.append(result.last());
24077  result << coordsToPixels(keyMin, valueMin);
24078  }
24079  break;
24080  }
24081  }
24082  break;
24083  }
24084  case 8: {
24085  switch (currentRegion) {
24086  case 7: {
24087  result << coordsToPixels(keyMax, valueMax);
24088  break;
24089  }
24090  case 9: {
24091  result << coordsToPixels(keyMax, valueMin);
24092  break;
24093  }
24094  case 4: {
24095  result << coordsToPixels(keyMax, valueMax);
24096  result.append(result.last());
24097  break;
24098  }
24099  case 6: {
24100  result << coordsToPixels(keyMax, valueMin);
24101  result.append(result.last());
24102  break;
24103  }
24104  case 1: {
24105  result << coordsToPixels(keyMax, valueMax);
24106  result.append(result.last());
24107  result << coordsToPixels(keyMin, valueMax);
24108  break;
24109  }
24110  case 3: {
24111  result << coordsToPixels(keyMax, valueMin);
24112  result.append(result.last());
24113  result << coordsToPixels(keyMin, valueMin);
24114  break;
24115  }
24116  }
24117  break;
24118  }
24119  case 9: {
24120  switch (currentRegion) {
24121  case 6: {
24122  result << coordsToPixels(keyMax, valueMin);
24123  break;
24124  }
24125  case 8: {
24126  result << coordsToPixels(keyMax, valueMin);
24127  break;
24128  }
24129  case 3: {
24130  result << coordsToPixels(keyMax, valueMin)
24131  << coordsToPixels(keyMin, valueMin);
24132  break;
24133  }
24134  case 7: {
24135  result << coordsToPixels(keyMax, valueMin)
24136  << coordsToPixels(keyMax, valueMax);
24137  break;
24138  }
24139  case 2: {
24140  result << coordsToPixels(keyMax, valueMin)
24141  << coordsToPixels(keyMin, valueMin);
24142  result.append(result.last());
24143  break;
24144  }
24145  case 4: {
24146  result << coordsToPixels(keyMax, valueMin)
24147  << coordsToPixels(keyMax, valueMax);
24148  result.append(result.last());
24149  break;
24150  }
24151  case 1: { // in this case we need another distinction of cases:
24152  // segment may pass below or above rect, requiring
24153  // either bottom right or top left corner points
24154  if ((value - prevValue) / (key - prevKey) * (keyMin - key) +
24155  value <
24156  valueMin) // segment passes below R
24157  {
24158  result << coordsToPixels(keyMax, valueMin)
24159  << coordsToPixels(keyMin, valueMin);
24160  result.append(result.last());
24161  result << coordsToPixels(keyMin, valueMax);
24162  } else {
24163  result << coordsToPixels(keyMax, valueMin)
24164  << coordsToPixels(keyMax, valueMax);
24165  result.append(result.last());
24166  result << coordsToPixels(keyMin, valueMax);
24167  }
24168  break;
24169  }
24170  }
24171  break;
24172  }
24173  }
24174  return result;
24175 }
24176 
24190 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const {
24191  switch (prevRegion) {
24192  case 1: {
24193  switch (currentRegion) {
24194  case 4:
24195  case 7:
24196  case 2:
24197  case 3:
24198  return false;
24199  default:
24200  return true;
24201  }
24202  }
24203  case 2: {
24204  switch (currentRegion) {
24205  case 1:
24206  case 3:
24207  return false;
24208  default:
24209  return true;
24210  }
24211  }
24212  case 3: {
24213  switch (currentRegion) {
24214  case 1:
24215  case 2:
24216  case 6:
24217  case 9:
24218  return false;
24219  default:
24220  return true;
24221  }
24222  }
24223  case 4: {
24224  switch (currentRegion) {
24225  case 1:
24226  case 7:
24227  return false;
24228  default:
24229  return true;
24230  }
24231  }
24232  case 5:
24233  return false; // should never occur
24234  case 6: {
24235  switch (currentRegion) {
24236  case 3:
24237  case 9:
24238  return false;
24239  default:
24240  return true;
24241  }
24242  }
24243  case 7: {
24244  switch (currentRegion) {
24245  case 1:
24246  case 4:
24247  case 8:
24248  case 9:
24249  return false;
24250  default:
24251  return true;
24252  }
24253  }
24254  case 8: {
24255  switch (currentRegion) {
24256  case 7:
24257  case 9:
24258  return false;
24259  default:
24260  return true;
24261  }
24262  }
24263  case 9: {
24264  switch (currentRegion) {
24265  case 3:
24266  case 6:
24267  case 8:
24268  case 7:
24269  return false;
24270  default:
24271  return true;
24272  }
24273  }
24274  default:
24275  return true;
24276  }
24277 }
24278 
24295 bool QCPCurve::getTraverse(double prevKey,
24296  double prevValue,
24297  double key,
24298  double value,
24299  double keyMin,
24300  double valueMax,
24301  double keyMax,
24302  double valueMin,
24303  QPointF &crossA,
24304  QPointF &crossB) const {
24305  // The intersection point interpolation here is done in pixel coordinates,
24306  // so we don't need to differentiate between different axis scale types.
24307  // Note that the nomenclature top/left/bottom/right/min/max is with respect
24308  // to the rect in plot coordinates, wich may be different in pixel
24309  // coordinates (horz/vert key axes, reversed ranges)
24310 
24311  QList<QPointF> intersections;
24312  const double valueMinPx = mValueAxis->coordToPixel(valueMin);
24313  const double valueMaxPx = mValueAxis->coordToPixel(valueMax);
24314  const double keyMinPx = mKeyAxis->coordToPixel(keyMin);
24315  const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);
24316  const double keyPx = mKeyAxis->coordToPixel(key);
24317  const double valuePx = mValueAxis->coordToPixel(value);
24318  const double prevKeyPx = mKeyAxis->coordToPixel(prevKey);
24319  const double prevValuePx = mValueAxis->coordToPixel(prevValue);
24320  if (qFuzzyIsNull(key - prevKey)) // line is parallel to value axis
24321  {
24322  // due to region filter in mayTraverse(), if line is parallel to value
24323  // or key axis, region 5 is traversed here
24324  intersections.append(
24325  mKeyAxis->orientation() == Qt::Horizontal
24326  ? QPointF(keyPx, valueMinPx)
24327  : QPointF(valueMinPx,
24328  keyPx)); // direction will be taken care of
24329  // at end of method
24330  intersections.append(mKeyAxis->orientation() == Qt::Horizontal
24331  ? QPointF(keyPx, valueMaxPx)
24332  : QPointF(valueMaxPx, keyPx));
24333  } else if (qFuzzyIsNull(value - prevValue)) // line is parallel to key axis
24334  {
24335  // due to region filter in mayTraverse(), if line is parallel to value
24336  // or key axis, region 5 is traversed here
24337  intersections.append(
24338  mKeyAxis->orientation() == Qt::Horizontal
24339  ? QPointF(keyMinPx, valuePx)
24340  : QPointF(valuePx,
24341  keyMinPx)); // direction will be taken care
24342  // of at end of method
24343  intersections.append(mKeyAxis->orientation() == Qt::Horizontal
24344  ? QPointF(keyMaxPx, valuePx)
24345  : QPointF(valuePx, keyMaxPx));
24346  } else // line is skewed
24347  {
24348  double gamma;
24349  double keyPerValuePx = (keyPx - prevKeyPx) / (valuePx - prevValuePx);
24350  // check top of rect:
24351  gamma = prevKeyPx + (valueMaxPx - prevValuePx) * keyPerValuePx;
24352  if (gamma >= qMin(keyMinPx, keyMaxPx) &&
24353  gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since
24354  // axes may be reversed
24355  intersections.append(mKeyAxis->orientation() == Qt::Horizontal
24356  ? QPointF(gamma, valueMaxPx)
24357  : QPointF(valueMaxPx, gamma));
24358  // check bottom of rect:
24359  gamma = prevKeyPx + (valueMinPx - prevValuePx) * keyPerValuePx;
24360  if (gamma >= qMin(keyMinPx, keyMaxPx) &&
24361  gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since
24362  // axes may be reversed
24363  intersections.append(mKeyAxis->orientation() == Qt::Horizontal
24364  ? QPointF(gamma, valueMinPx)
24365  : QPointF(valueMinPx, gamma));
24366  const double valuePerKeyPx = 1.0 / keyPerValuePx;
24367  // check left of rect:
24368  gamma = prevValuePx + (keyMinPx - prevKeyPx) * valuePerKeyPx;
24369  if (gamma >= qMin(valueMinPx, valueMaxPx) &&
24370  gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since
24371  // axes may be reversed
24372  intersections.append(mKeyAxis->orientation() == Qt::Horizontal
24373  ? QPointF(keyMinPx, gamma)
24374  : QPointF(gamma, keyMinPx));
24375  // check right of rect:
24376  gamma = prevValuePx + (keyMaxPx - prevKeyPx) * valuePerKeyPx;
24377  if (gamma >= qMin(valueMinPx, valueMaxPx) &&
24378  gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since
24379  // axes may be reversed
24380  intersections.append(mKeyAxis->orientation() == Qt::Horizontal
24381  ? QPointF(keyMaxPx, gamma)
24382  : QPointF(gamma, keyMaxPx));
24383  }
24384 
24385  // handle cases where found points isn't exactly 2:
24386  if (intersections.size() > 2) {
24387  // line probably goes through corner of rect, and we got duplicate
24388  // points there. single out the point pair with greatest distance in
24389  // between:
24390  double distSqrMax = 0;
24391  QPointF pv1, pv2;
24392  for (int i = 0; i < intersections.size() - 1; ++i) {
24393  for (int k = i + 1; k < intersections.size(); ++k) {
24394  QPointF distPoint = intersections.at(i) - intersections.at(k);
24395  double distSqr = distPoint.x() * distPoint.x() + distPoint.y() +
24396  distPoint.y();
24397  if (distSqr > distSqrMax) {
24398  pv1 = intersections.at(i);
24399  pv2 = intersections.at(k);
24400  distSqrMax = distSqr;
24401  }
24402  }
24403  }
24404  intersections = QList<QPointF>() << pv1 << pv2;
24405  } else if (intersections.size() != 2) {
24406  // one or even zero points found (shouldn't happen unless line perfectly
24407  // tangent to corner), no need to draw segment
24408  return false;
24409  }
24410 
24411  // possibly re-sort points so optimized point segment has same direction as
24412  // original segment:
24413  double xDelta = keyPx - prevKeyPx;
24414  double yDelta = valuePx - prevValuePx;
24415  if (mKeyAxis->orientation() != Qt::Horizontal) qSwap(xDelta, yDelta);
24416  if (xDelta * (intersections.at(1).x() - intersections.at(0).x()) +
24417  yDelta * (intersections.at(1).y() - intersections.at(0).y()) <
24418  0) // scalar product of both segments < 0 -> opposite direction
24419  intersections.move(0, 1);
24420  crossA = intersections.at(0);
24421  crossB = intersections.at(1);
24422  return true;
24423 }
24424 
24455  int currentRegion,
24456  double keyMin,
24457  double valueMax,
24458  double keyMax,
24459  double valueMin,
24460  QVector<QPointF> &beforeTraverse,
24461  QVector<QPointF> &afterTraverse) const {
24462  switch (prevRegion) {
24463  case 1: {
24464  switch (currentRegion) {
24465  case 6: {
24466  beforeTraverse << coordsToPixels(keyMin, valueMax);
24467  break;
24468  }
24469  case 9: {
24470  beforeTraverse << coordsToPixels(keyMin, valueMax);
24471  afterTraverse << coordsToPixels(keyMax, valueMin);
24472  break;
24473  }
24474  case 8: {
24475  beforeTraverse << coordsToPixels(keyMin, valueMax);
24476  break;
24477  }
24478  }
24479  break;
24480  }
24481  case 2: {
24482  switch (currentRegion) {
24483  case 7: {
24484  afterTraverse << coordsToPixels(keyMax, valueMax);
24485  break;
24486  }
24487  case 9: {
24488  afterTraverse << coordsToPixels(keyMax, valueMin);
24489  break;
24490  }
24491  }
24492  break;
24493  }
24494  case 3: {
24495  switch (currentRegion) {
24496  case 4: {
24497  beforeTraverse << coordsToPixels(keyMin, valueMin);
24498  break;
24499  }
24500  case 7: {
24501  beforeTraverse << coordsToPixels(keyMin, valueMin);
24502  afterTraverse << coordsToPixels(keyMax, valueMax);
24503  break;
24504  }
24505  case 8: {
24506  beforeTraverse << coordsToPixels(keyMin, valueMin);
24507  break;
24508  }
24509  }
24510  break;
24511  }
24512  case 4: {
24513  switch (currentRegion) {
24514  case 3: {
24515  afterTraverse << coordsToPixels(keyMin, valueMin);
24516  break;
24517  }
24518  case 9: {
24519  afterTraverse << coordsToPixels(keyMax, valueMin);
24520  break;
24521  }
24522  }
24523  break;
24524  }
24525  case 5: {
24526  break;
24527  } // shouldn't happen because this method only handles full traverses
24528  case 6: {
24529  switch (currentRegion) {
24530  case 1: {
24531  afterTraverse << coordsToPixels(keyMin, valueMax);
24532  break;
24533  }
24534  case 7: {
24535  afterTraverse << coordsToPixels(keyMax, valueMax);
24536  break;
24537  }
24538  }
24539  break;
24540  }
24541  case 7: {
24542  switch (currentRegion) {
24543  case 2: {
24544  beforeTraverse << coordsToPixels(keyMax, valueMax);
24545  break;
24546  }
24547  case 3: {
24548  beforeTraverse << coordsToPixels(keyMax, valueMax);
24549  afterTraverse << coordsToPixels(keyMin, valueMin);
24550  break;
24551  }
24552  case 6: {
24553  beforeTraverse << coordsToPixels(keyMax, valueMax);
24554  break;
24555  }
24556  }
24557  break;
24558  }
24559  case 8: {
24560  switch (currentRegion) {
24561  case 1: {
24562  afterTraverse << coordsToPixels(keyMin, valueMax);
24563  break;
24564  }
24565  case 3: {
24566  afterTraverse << coordsToPixels(keyMin, valueMin);
24567  break;
24568  }
24569  }
24570  break;
24571  }
24572  case 9: {
24573  switch (currentRegion) {
24574  case 2: {
24575  beforeTraverse << coordsToPixels(keyMax, valueMin);
24576  break;
24577  }
24578  case 1: {
24579  beforeTraverse << coordsToPixels(keyMax, valueMin);
24580  afterTraverse << coordsToPixels(keyMin, valueMax);
24581  break;
24582  }
24583  case 4: {
24584  beforeTraverse << coordsToPixels(keyMax, valueMin);
24585  break;
24586  }
24587  }
24588  break;
24589  }
24590  }
24591 }
24592 
24608  const QPointF &pixelPoint,
24609  QCPCurveDataContainer::const_iterator &closestData) const {
24610  closestData = mDataContainer->constEnd();
24611  if (mDataContainer->isEmpty()) return -1.0;
24612  if (mLineStyle == lsNone && mScatterStyle.isNone()) return -1.0;
24613 
24614  if (mDataContainer->size() == 1) {
24615  QPointF dataPoint = coordsToPixels(mDataContainer->constBegin()->key,
24616  mDataContainer->constBegin()->value);
24617  closestData = mDataContainer->constBegin();
24618  return QCPVector2D(dataPoint - pixelPoint).length();
24619  }
24620 
24621  // calculate minimum distances to curve data points and find closestData
24622  // iterator:
24623  double minDistSqr = (std::numeric_limits<double>::max)();
24624  // iterate over found data points and then choose the one with the shortest
24625  // distance to pos:
24628  for (QCPCurveDataContainer::const_iterator it = begin; it != end; ++it) {
24629  const double currentDistSqr =
24630  QCPVector2D(coordsToPixels(it->key, it->value) - pixelPoint)
24631  .lengthSquared();
24632  if (currentDistSqr < minDistSqr) {
24633  minDistSqr = currentDistSqr;
24634  closestData = it;
24635  }
24636  }
24637 
24638  // calculate distance to line if there is one (if so, will probably be
24639  // smaller than distance to closest data point):
24640  if (mLineStyle != lsNone) {
24641  QVector<QPointF> lines;
24642  getCurveLines(&lines, QCPDataRange(0, dataCount()),
24644  1.2); // optimized lines outside axis rect
24645  // shouldn't respond to clicks at the edge,
24646  // so use 1.2*tolerance as pen width
24647  for (int i = 0; i < lines.size() - 1; ++i) {
24648  double currentDistSqr =
24649  QCPVector2D(pixelPoint)
24650  .distanceSquaredToLine(lines.at(i),
24651  lines.at(i + 1));
24652  if (currentDistSqr < minDistSqr) minDistSqr = currentDistSqr;
24653  }
24654  }
24655 
24656  return qSqrt(minDistSqr);
24657 }
24658 /* end of 'src/plottables/plottable-curve.cpp' */
24659 
24660 /* including file 'src/plottables/plottable-bars.cpp', size 43725 */
24661 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
24662 
24666 
24703 /* start of documentation of inline functions */
24704 
24731 /* end of documentation of inline functions */
24732 
24737  : QObject(parentPlot),
24738  mParentPlot(parentPlot),
24739  mSpacingType(stAbsolute),
24740  mSpacing(4) {}
24741 
24743 
24754 }
24755 
24763 void QCPBarsGroup::setSpacing(double spacing) { mSpacing = spacing; }
24764 
24771 QCPBars *QCPBarsGroup::bars(int index) const {
24772  if (index >= 0 && index < mBars.size()) {
24773  return mBars.at(index);
24774  } else {
24775  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
24776  return 0;
24777  }
24778 }
24779 
24786  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars
24787  // in the loop is okay
24788  bars->setBarsGroup(0); // removes itself via removeBars
24789 }
24790 
24798  if (!bars) {
24799  qDebug() << Q_FUNC_INFO << "bars is 0";
24800  return;
24801  }
24802 
24803  if (!mBars.contains(bars))
24804  bars->setBarsGroup(this);
24805  else
24806  qDebug() << Q_FUNC_INFO
24807  << "bars plottable is already in this bars group:"
24808  << reinterpret_cast<quintptr>(bars);
24809 }
24810 
24820 void QCPBarsGroup::insert(int i, QCPBars *bars) {
24821  if (!bars) {
24822  qDebug() << Q_FUNC_INFO << "bars is 0";
24823  return;
24824  }
24825 
24826  // first append to bars list normally:
24827  if (!mBars.contains(bars)) bars->setBarsGroup(this);
24828  // then move to according position:
24829  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size() - 1));
24830 }
24831 
24838  if (!bars) {
24839  qDebug() << Q_FUNC_INFO << "bars is 0";
24840  return;
24841  }
24842 
24843  if (mBars.contains(bars))
24844  bars->setBarsGroup(0);
24845  else
24846  qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:"
24847  << reinterpret_cast<quintptr>(bars);
24848 }
24849 
24858  if (!mBars.contains(bars)) mBars.append(bars);
24859 }
24860 
24868 void QCPBarsGroup::unregisterBars(QCPBars *bars) { mBars.removeOne(bars); }
24869 
24876 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord) {
24877  // find list of all base bars in case some mBars are stacked:
24878  QList<const QCPBars *> baseBars;
24879  foreach (const QCPBars *b, mBars) {
24880  while (b->barBelow()) b = b->barBelow();
24881  if (!baseBars.contains(b)) baseBars.append(b);
24882  }
24883  // find base bar this "bars" is stacked on:
24884  const QCPBars *thisBase = bars;
24885  while (thisBase->barBelow()) thisBase = thisBase->barBelow();
24886 
24887  // determine key pixel offset of this base bars considering all other base
24888  // bars in this barsgroup:
24889  double result = 0;
24890  int index = baseBars.indexOf(thisBase);
24891  if (index >= 0) {
24892  if (baseBars.size() % 2 == 1 &&
24893  index == (baseBars.size() - 1) /
24894  2) // is center bar (int division on purpose)
24895  {
24896  return result;
24897  } else {
24898  double lowerPixelWidth, upperPixelWidth;
24899  int startIndex;
24900  int dir = (index <= (baseBars.size() - 1) / 2)
24901  ? -1
24902  : 1; // if bar is to lower keys of center, dir is
24903  // negative
24904  if (baseBars.size() % 2 == 0) // even number of bars
24905  {
24906  startIndex = baseBars.size() / 2 + (dir < 0 ? -1 : 0);
24907  result += getPixelSpacing(baseBars.at(startIndex), keyCoord) *
24908  0.5; // half of middle spacing
24909  } else // uneven number of bars
24910  {
24911  startIndex = (baseBars.size() - 1) / 2 + dir;
24912  baseBars.at((baseBars.size() - 1) / 2)
24913  ->getPixelWidth(keyCoord, lowerPixelWidth,
24914  upperPixelWidth);
24915  result += qAbs(upperPixelWidth - lowerPixelWidth) *
24916  0.5; // half of center bar
24917  result +=
24918  getPixelSpacing(baseBars.at((baseBars.size() - 1) / 2),
24919  keyCoord); // center bar spacing
24920  }
24921  for (int i = startIndex; i != index;
24922  i += dir) // add widths and spacings of bars in between center
24923  // and our bars
24924  {
24925  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth,
24926  upperPixelWidth);
24927  result += qAbs(upperPixelWidth - lowerPixelWidth);
24928  result += getPixelSpacing(baseBars.at(i), keyCoord);
24929  }
24930  // finally half of our bars width:
24931  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth,
24932  upperPixelWidth);
24933  result += qAbs(upperPixelWidth - lowerPixelWidth) * 0.5;
24934  // correct sign of result depending on orientation and direction of
24935  // key axis:
24936  result *= dir * thisBase->keyAxis()->pixelOrientation();
24937  }
24938  }
24939  return result;
24940 }
24941 
24953 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord) {
24954  switch (mSpacingType) {
24955  case stAbsolute: {
24956  return mSpacing;
24957  }
24958  case stAxisRectRatio: {
24959  if (bars->keyAxis()->orientation() == Qt::Horizontal)
24960  return bars->keyAxis()->axisRect()->width() * mSpacing;
24961  else
24962  return bars->keyAxis()->axisRect()->height() * mSpacing;
24963  }
24964  case stPlotCoords: {
24965  double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
24966  return qAbs(bars->keyAxis()->coordToPixel(keyCoord + mSpacing) -
24967  keyPixel);
24968  }
24969  }
24970  return 0;
24971 }
24972 
24976 
24993 /* start documentation of inline functions */
24994 
25046 /* end documentation of inline functions */
25047 
25051 QCPBarsData::QCPBarsData() : key(0), value(0) {}
25052 
25056 QCPBarsData::QCPBarsData(double key, double value) : key(key), value(value) {}
25057 
25061 
25099 /* start of documentation of inline functions */
25100 
25123 /* end of documentation of inline functions */
25124 
25137 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
25138  : QCPAbstractPlottable1D<QCPBarsData>(keyAxis, valueAxis),
25139  mWidth(0.75),
25140  mWidthType(wtPlotCoords),
25141  mBarsGroup(0),
25142  mBaseValue(0),
25143  mStackingGap(0) {
25144  // modify inherited properties from abstract plottable:
25145  mPen.setColor(Qt::blue);
25146  mPen.setStyle(Qt::SolidLine);
25147  mBrush.setColor(QColor(40, 50, 255, 30));
25148  mBrush.setStyle(Qt::SolidPattern);
25149  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
25150 }
25151 
25153  setBarsGroup(0);
25154  if (mBarBelow || mBarAbove)
25155  connectBars(mBarBelow.data(),
25156  mBarAbove.data()); // take this bar out of any stacking
25157 }
25158 
25176 void QCPBars::setData(QSharedPointer<QCPBarsDataContainer> data) {
25177  mDataContainer = data;
25178 }
25179 
25192 void QCPBars::setData(const QVector<double> &keys,
25193  const QVector<double> &values,
25194  bool alreadySorted) {
25195  mDataContainer->clear();
25196  addData(keys, values, alreadySorted);
25197 }
25198 
25207 
25218 }
25219 
25227  // deregister at old group:
25228  if (mBarsGroup) mBarsGroup->unregisterBars(this);
25230  // register at new group:
25231  if (mBarsGroup) mBarsGroup->registerBars(this);
25232 }
25233 
25246 void QCPBars::setBaseValue(double baseValue) { mBaseValue = baseValue; }
25247 
25253 void QCPBars::setStackingGap(double pixels) { mStackingGap = pixels; }
25254 
25268 void QCPBars::addData(const QVector<double> &keys,
25269  const QVector<double> &values,
25270  bool alreadySorted) {
25271  if (keys.size() != values.size())
25272  qDebug() << Q_FUNC_INFO
25273  << "keys and values have different sizes:" << keys.size()
25274  << values.size();
25275  const int n = qMin(keys.size(), values.size());
25276  QVector<QCPBarsData> tempData(n);
25277  QVector<QCPBarsData>::iterator it = tempData.begin();
25278  const QVector<QCPBarsData>::iterator itEnd = tempData.end();
25279  int i = 0;
25280  while (it != itEnd) {
25281  it->key = keys[i];
25282  it->value = values[i];
25283  ++it;
25284  ++i;
25285  }
25286  mDataContainer->add(tempData,
25287  alreadySorted); // don't modify tempData beyond this to
25288  // prevent copy on write
25289 }
25290 
25297 void QCPBars::addData(double key, double value) {
25298  mDataContainer->add(QCPBarsData(key, value));
25299 }
25300 
25317  if (bars == this) return;
25318  if (bars && (bars->keyAxis() != mKeyAxis.data() ||
25319  bars->valueAxis() != mValueAxis.data())) {
25320  qDebug() << Q_FUNC_INFO
25321  << "passed QCPBars* doesn't have same key and value axis as "
25322  "this QCPBars";
25323  return;
25324  }
25325  // remove from stacking:
25326  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one
25327  // (or both) of them is 0
25328  // if new bar given, insert this bar below it:
25329  if (bars) {
25330  if (bars->mBarBelow) connectBars(bars->mBarBelow.data(), this);
25331  connectBars(this, bars);
25332  }
25333 }
25334 
25351  if (bars == this) return;
25352  if (bars && (bars->keyAxis() != mKeyAxis.data() ||
25353  bars->valueAxis() != mValueAxis.data())) {
25354  qDebug() << Q_FUNC_INFO
25355  << "passed QCPBars* doesn't have same key and value axis as "
25356  "this QCPBars";
25357  return;
25358  }
25359  // remove from stacking:
25360  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one
25361  // (or both) of them is 0
25362  // if new bar given, insert this bar above it:
25363  if (bars) {
25364  if (bars->mBarAbove) connectBars(this, bars->mBarAbove.data());
25365  connectBars(bars, this);
25366  }
25367 }
25368 
25373  bool onlySelectable) const {
25375  if ((onlySelectable && mSelectable == QCP::stNone) ||
25376  mDataContainer->isEmpty())
25377  return result;
25378  if (!mKeyAxis || !mValueAxis) return result;
25379 
25380  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
25381  getVisibleDataBounds(visibleBegin, visibleEnd);
25382 
25383  for (QCPBarsDataContainer::const_iterator it = visibleBegin;
25384  it != visibleEnd; ++it) {
25385  if (rect.intersects(getBarRect(it->key, it->value)))
25386  result.addDataRange(
25387  QCPDataRange(it - mDataContainer->constBegin(),
25388  it - mDataContainer->constBegin() + 1),
25389  false);
25390  }
25391  result.simplify();
25392  return result;
25393 }
25394 
25403 double QCPBars::selectTest(const QPointF &pos,
25404  bool onlySelectable,
25405  QVariant *details) const {
25406  Q_UNUSED(details)
25407  if ((onlySelectable && mSelectable == QCP::stNone) ||
25408  mDataContainer->isEmpty())
25409  return -1;
25410  if (!mKeyAxis || !mValueAxis) return -1;
25411 
25412  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
25413  // get visible data range:
25414  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
25415  getVisibleDataBounds(visibleBegin, visibleEnd);
25416  for (QCPBarsDataContainer::const_iterator it = visibleBegin;
25417  it != visibleEnd; ++it) {
25418  if (getBarRect(it->key, it->value).contains(pos)) {
25419  if (details) {
25420  int pointIndex = it - mDataContainer->constBegin();
25421  details->setValue(QCPDataSelection(
25422  QCPDataRange(pointIndex, pointIndex + 1)));
25423  }
25424  return mParentPlot->selectionTolerance() * 0.99;
25425  }
25426  }
25427  }
25428  return -1;
25429 }
25430 
25431 /* inherits documentation from base class */
25433  QCP::SignDomain inSignDomain) const {
25434  /* Note: If this QCPBars uses absolute pixels as width (or is in a
25435  QCPBarsGroup with spacing in absolute pixels), using this method to adapt
25436  the key axis range to fit the bars into the currently visible axis range
25437  will not work perfectly. Because in the moment the axis range is changed to
25438  the new range, the fixed pixel widths/spacings will represent different
25439  coordinate spans than before, which in turn would require a different key
25440  range to perfectly fit, and so on. The only solution would be to iteratively
25441  approach the perfect fitting axis range, but the mismatch isn't large enough
25442  in most applications, to warrant this here. If a user does need a better
25443  fit, he should call the corresponding axis rescale multiple times in a row.
25444  */
25445  QCPRange range;
25446  range = mDataContainer->keyRange(foundRange, inSignDomain);
25447 
25448  // determine exact range of bars by including bar width and barsgroup
25449  // offset:
25450  if (foundRange && mKeyAxis) {
25451  double lowerPixelWidth, upperPixelWidth, keyPixel;
25452  // lower range bound:
25453  getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
25454  keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
25455  if (mBarsGroup)
25456  keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
25457  const double lowerCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
25458  if (!qIsNaN(lowerCorrected) && qIsFinite(lowerCorrected) &&
25459  range.lower > lowerCorrected)
25460  range.lower = lowerCorrected;
25461  // upper range bound:
25462  getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
25463  keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
25464  if (mBarsGroup)
25465  keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
25466  const double upperCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
25467  if (!qIsNaN(upperCorrected) && qIsFinite(upperCorrected) &&
25468  range.upper < upperCorrected)
25469  range.upper = upperCorrected;
25470  }
25471  return range;
25472 }
25473 
25474 /* inherits documentation from base class */
25476  QCP::SignDomain inSignDomain,
25477  const QCPRange &inKeyRange) const {
25478  // Note: can't simply use mDataContainer->valueRange here because we need to
25479  // take into account bar base value and possible stacking of multiple bars
25480  QCPRange range;
25481  range.lower = mBaseValue;
25482  range.upper = mBaseValue;
25483  bool haveLower = true; // set to true, because baseValue should always be
25484  // visible in bar charts
25485  bool haveUpper = true; // set to true, because baseValue should always be
25486  // visible in bar charts
25487  QCPBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();
25489  if (inKeyRange != QCPRange()) {
25490  itBegin = mDataContainer->findBegin(inKeyRange.lower);
25491  itEnd = mDataContainer->findEnd(inKeyRange.upper);
25492  }
25493  for (QCPBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it) {
25494  const double current =
25495  it->value + getStackedBaseValue(it->key, it->value >= 0);
25496  if (qIsNaN(current)) continue;
25497  if (inSignDomain == QCP::sdBoth ||
25498  (inSignDomain == QCP::sdNegative && current < 0) ||
25499  (inSignDomain == QCP::sdPositive && current > 0)) {
25500  if (current < range.lower || !haveLower) {
25501  range.lower = current;
25502  haveLower = true;
25503  }
25504  if (current > range.upper || !haveUpper) {
25505  range.upper = current;
25506  haveUpper = true;
25507  }
25508  }
25509  }
25510 
25511  foundRange = true; // return true because bar charts always have the 0-line
25512  // visible
25513  return range;
25514 }
25515 
25516 /* inherits documentation from base class */
25517 QPointF QCPBars::dataPixelPosition(int index) const {
25518  if (index >= 0 && index < mDataContainer->size()) {
25519  QCPAxis *keyAxis = mKeyAxis.data();
25520  QCPAxis *valueAxis = mValueAxis.data();
25521  if (!keyAxis || !valueAxis) {
25522  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
25523  return QPointF();
25524  }
25525 
25527  mDataContainer->constBegin() + index;
25528  const double valuePixel = valueAxis->coordToPixel(
25529  getStackedBaseValue(it->key, it->value >= 0) + it->value);
25530  const double keyPixel =
25531  keyAxis->coordToPixel(it->key) +
25532  (mBarsGroup ? mBarsGroup->keyPixelOffset(this, it->key) : 0);
25533  if (keyAxis->orientation() == Qt::Horizontal)
25534  return QPointF(keyPixel, valuePixel);
25535  else
25536  return QPointF(valuePixel, keyPixel);
25537  } else {
25538  qDebug() << Q_FUNC_INFO << "Index out of bounds" << index;
25539  return QPointF();
25540  }
25541 }
25542 
25543 /* inherits documentation from base class */
25544 void QCPBars::draw(QCPPainter *painter) {
25545  if (!mKeyAxis || !mValueAxis) {
25546  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
25547  return;
25548  }
25549  if (mDataContainer->isEmpty()) return;
25550 
25551  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
25552  getVisibleDataBounds(visibleBegin, visibleEnd);
25553 
25554  // loop over and draw segments of unselected/selected data:
25555  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
25556  getDataSegments(selectedSegments, unselectedSegments);
25557  allSegments << unselectedSegments << selectedSegments;
25558  for (int i = 0; i < allSegments.size(); ++i) {
25559  bool isSelectedSegment = i >= unselectedSegments.size();
25560  QCPBarsDataContainer::const_iterator begin = visibleBegin;
25561  QCPBarsDataContainer::const_iterator end = visibleEnd;
25562  mDataContainer->limitIteratorsToDataRange(begin, end,
25563  allSegments.at(i));
25564  if (begin == end) continue;
25565 
25566  for (QCPBarsDataContainer::const_iterator it = begin; it != end; ++it) {
25567  // check data validity if flag set:
25568 #ifdef QCUSTOMPLOT_CHECK_DATA
25569  if (QCP::isInvalidData(it->key, it->value))
25570  qDebug() << Q_FUNC_INFO << "Data point at" << it->key
25571  << "of drawn range invalid."
25572  << "Plottable name:" << name();
25573 #endif
25574  // draw bar:
25575  if (isSelectedSegment && mSelectionDecorator) {
25576  mSelectionDecorator->applyBrush(painter);
25577  mSelectionDecorator->applyPen(painter);
25578  } else {
25579  painter->setBrush(mBrush);
25580  painter->setPen(mPen);
25581  }
25583  painter->drawPolygon(getBarRect(it->key, it->value));
25584  }
25585  }
25586 
25587  // draw other selection decoration that isn't just line/scatter pens and
25588  // brushes:
25589  if (mSelectionDecorator)
25591 }
25592 
25593 /* inherits documentation from base class */
25594 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const {
25595  // draw filled rect:
25597  painter->setBrush(mBrush);
25598  painter->setPen(mPen);
25599  QRectF r = QRectF(0, 0, rect.width() * 0.67, rect.height() * 0.67);
25600  r.moveCenter(rect.center());
25601  painter->drawRect(r);
25602 }
25603 
25623  if (!mKeyAxis) {
25624  qDebug() << Q_FUNC_INFO << "invalid key axis";
25625  begin = mDataContainer->constEnd();
25626  end = mDataContainer->constEnd();
25627  return;
25628  }
25629  if (mDataContainer->isEmpty()) {
25630  begin = mDataContainer->constEnd();
25631  end = mDataContainer->constEnd();
25632  return;
25633  }
25634 
25635  // get visible data range as QMap iterators
25636  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower);
25637  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper);
25638  double lowerPixelBound =
25639  mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
25640  double upperPixelBound =
25641  mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
25642  bool isVisible = false;
25643  // walk left from begin to find lower bar that actually is completely
25644  // outside visible pixel range:
25646  while (it != mDataContainer->constBegin()) {
25647  --it;
25648  const QRectF barRect = getBarRect(it->key, it->value);
25649  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
25650  isVisible = ((!mKeyAxis.data()->rangeReversed() &&
25651  barRect.right() >= lowerPixelBound) ||
25652  (mKeyAxis.data()->rangeReversed() &&
25653  barRect.left() <= lowerPixelBound));
25654  else // keyaxis is vertical
25655  isVisible = ((!mKeyAxis.data()->rangeReversed() &&
25656  barRect.top() <= lowerPixelBound) ||
25657  (mKeyAxis.data()->rangeReversed() &&
25658  barRect.bottom() >= lowerPixelBound));
25659  if (isVisible)
25660  begin = it;
25661  else
25662  break;
25663  }
25664  // walk right from ubound to find upper bar that actually is completely
25665  // outside visible pixel range:
25666  it = end;
25667  while (it != mDataContainer->constEnd()) {
25668  const QRectF barRect = getBarRect(it->key, it->value);
25669  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
25670  isVisible = ((!mKeyAxis.data()->rangeReversed() &&
25671  barRect.left() <= upperPixelBound) ||
25672  (mKeyAxis.data()->rangeReversed() &&
25673  barRect.right() >= upperPixelBound));
25674  else // keyaxis is vertical
25675  isVisible = ((!mKeyAxis.data()->rangeReversed() &&
25676  barRect.bottom() >= upperPixelBound) ||
25677  (mKeyAxis.data()->rangeReversed() &&
25678  barRect.top() <= upperPixelBound));
25679  if (isVisible)
25680  end = it + 1;
25681  else
25682  break;
25683  ++it;
25684  }
25685 }
25686 
25694 QRectF QCPBars::getBarRect(double key, double value) const {
25695  QCPAxis *keyAxis = mKeyAxis.data();
25696  QCPAxis *valueAxis = mValueAxis.data();
25697  if (!keyAxis || !valueAxis) {
25698  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
25699  return QRectF();
25700  }
25701 
25702  double lowerPixelWidth, upperPixelWidth;
25703  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
25704  double base = getStackedBaseValue(key, value >= 0);
25705  double basePixel = valueAxis->coordToPixel(base);
25706  double valuePixel = valueAxis->coordToPixel(base + value);
25707  double keyPixel = keyAxis->coordToPixel(key);
25708  if (mBarsGroup) keyPixel += mBarsGroup->keyPixelOffset(this, key);
25709  double bottomOffset = (mBarBelow && mPen != Qt::NoPen ? 1 : 0) *
25710  (mPen.isCosmetic() ? 1 : mPen.widthF());
25711  bottomOffset += mBarBelow ? mStackingGap : 0;
25712  bottomOffset *= (value < 0 ? -1 : 1) * valueAxis->pixelOrientation();
25713  if (qAbs(valuePixel - basePixel) <= qAbs(bottomOffset))
25714  bottomOffset = valuePixel - basePixel;
25715  if (keyAxis->orientation() == Qt::Horizontal) {
25716  return QRectF(QPointF(keyPixel + lowerPixelWidth, valuePixel),
25717  QPointF(keyPixel + upperPixelWidth,
25718  basePixel + bottomOffset))
25719  .normalized();
25720  } else {
25721  return QRectF(QPointF(basePixel + bottomOffset,
25722  keyPixel + lowerPixelWidth),
25723  QPointF(valuePixel, keyPixel + upperPixelWidth))
25724  .normalized();
25725  }
25726 }
25727 
25739 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const {
25740  lower = 0;
25741  upper = 0;
25742  switch (mWidthType) {
25743  case wtAbsolute: {
25744  upper = mWidth * 0.5 * mKeyAxis.data()->pixelOrientation();
25745  lower = -upper;
25746  break;
25747  }
25748  case wtAxisRectRatio: {
25749  if (mKeyAxis && mKeyAxis.data()->axisRect()) {
25750  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
25751  upper = mKeyAxis.data()->axisRect()->width() * mWidth *
25752  0.5 * mKeyAxis.data()->pixelOrientation();
25753  else
25754  upper = mKeyAxis.data()->axisRect()->height() * mWidth *
25755  0.5 * mKeyAxis.data()->pixelOrientation();
25756  lower = -upper;
25757  } else
25758  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
25759  break;
25760  }
25761  case wtPlotCoords: {
25762  if (mKeyAxis) {
25763  double keyPixel = mKeyAxis.data()->coordToPixel(key);
25764  upper = mKeyAxis.data()->coordToPixel(key + mWidth * 0.5) -
25765  keyPixel;
25766  lower = mKeyAxis.data()->coordToPixel(key - mWidth * 0.5) -
25767  keyPixel;
25768  // no need to qSwap(lower, higher) when range reversed, because
25769  // higher/lower are gained by coordinate transform which
25770  // includes range direction
25771  } else
25772  qDebug() << Q_FUNC_INFO << "No key axis defined";
25773  break;
25774  }
25775  }
25776 }
25777 
25789 double QCPBars::getStackedBaseValue(double key, bool positive) const {
25790  if (mBarBelow) {
25791  double max =
25792  0; // don't initialize with mBaseValue here because only base
25793  // value of bottom-most bar has meaning in a bar stack
25794  // find bars of mBarBelow that are approximately at key and find largest
25795  // one:
25796  double epsilon =
25797  qAbs(key) *
25798  (sizeof(key) == 4 ? 1e-6
25799  : 1e-14); // should be safe even when changed
25800  // to use float at some point
25801  if (key == 0) epsilon = (sizeof(key) == 4 ? 1e-6 : 1e-14);
25803  mBarBelow.data()->mDataContainer->findBegin(key - epsilon);
25805  mBarBelow.data()->mDataContainer->findEnd(key + epsilon);
25806  while (it != itEnd) {
25807  if (it->key > key - epsilon && it->key < key + epsilon) {
25808  if ((positive && it->value > max) ||
25809  (!positive && it->value < max))
25810  max = it->value;
25811  }
25812  ++it;
25813  }
25814  // recurse down the bar-stack to find the total height:
25815  return max + mBarBelow.data()->getStackedBaseValue(key, positive);
25816  } else
25817  return mBaseValue;
25818 }
25819 
25829 void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) {
25830  if (!lower && !upper) return;
25831 
25832  if (!lower) // disconnect upper at bottom
25833  {
25834  // disconnect old bar below upper:
25835  if (upper->mBarBelow &&
25836  upper->mBarBelow.data()->mBarAbove.data() == upper)
25837  upper->mBarBelow.data()->mBarAbove = 0;
25838  upper->mBarBelow = 0;
25839  } else if (!upper) // disconnect lower at top
25840  {
25841  // disconnect old bar above lower:
25842  if (lower->mBarAbove &&
25843  lower->mBarAbove.data()->mBarBelow.data() == lower)
25844  lower->mBarAbove.data()->mBarBelow = 0;
25845  lower->mBarAbove = 0;
25846  } else // connect lower and upper
25847  {
25848  // disconnect old bar above lower:
25849  if (lower->mBarAbove &&
25850  lower->mBarAbove.data()->mBarBelow.data() == lower)
25851  lower->mBarAbove.data()->mBarBelow = 0;
25852  // disconnect old bar below upper:
25853  if (upper->mBarBelow &&
25854  upper->mBarBelow.data()->mBarAbove.data() == upper)
25855  upper->mBarBelow.data()->mBarAbove = 0;
25856  lower->mBarAbove = upper;
25857  upper->mBarBelow = lower;
25858  }
25859 }
25860 /* end of 'src/plottables/plottable-bars.cpp' */
25861 
25862 /* including file 'src/plottables/plottable-statisticalbox.cpp', size 28837 */
25863 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
25864 
25868 
25908 /* start documentation of inline functions */
25909 
25962 /* end documentation of inline functions */
25963 
25968  : key(0),
25969  minimum(0),
25970  lowerQuartile(0),
25971  median(0),
25972  upperQuartile(0),
25973  maximum(0) {}
25974 
25981  double minimum,
25982  double lowerQuartile,
25983  double median,
25984  double upperQuartile,
25985  double maximum,
25986  const QVector<double> &outliers)
25987  : key(key),
25988  minimum(minimum),
25989  lowerQuartile(lowerQuartile),
25990  median(median),
25991  upperQuartile(upperQuartile),
25992  maximum(maximum),
25993  outliers(outliers) {}
25994 
25998 
26052 /* start documentation of inline functions */
26053 
26063 /* end documentation of inline functions */
26064 
26078  : QCPAbstractPlottable1D<QCPStatisticalBoxData>(keyAxis, valueAxis),
26079  mWidth(0.5),
26080  mWhiskerWidth(0.2),
26081  mWhiskerPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap),
26082  mWhiskerBarPen(Qt::black),
26083  mWhiskerAntialiased(false),
26084  mMedianPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap),
26085  mOutlierStyle(QCPScatterStyle::ssCircle, Qt::blue, 6) {
26086  setPen(QPen(Qt::black));
26087  setBrush(Qt::NoBrush);
26088 }
26089 
26108  QSharedPointer<QCPStatisticalBoxDataContainer> data) {
26109  mDataContainer = data;
26110 }
26124 void QCPStatisticalBox::setData(const QVector<double> &keys,
26125  const QVector<double> &minimum,
26126  const QVector<double> &lowerQuartile,
26127  const QVector<double> &median,
26128  const QVector<double> &upperQuartile,
26129  const QVector<double> &maximum,
26130  bool alreadySorted) {
26131  mDataContainer->clear();
26133  alreadySorted);
26134 }
26135 
26142 
26152 
26166 
26177  mWhiskerBarPen = pen;
26178 }
26179 
26189  mWhiskerAntialiased = enabled;
26190 }
26191 
26196 void QCPStatisticalBox::setMedianPen(const QPen &pen) { mMedianPen = pen; }
26197 
26206  mOutlierStyle = style;
26207 }
26208 
26223 void QCPStatisticalBox::addData(const QVector<double> &keys,
26224  const QVector<double> &minimum,
26225  const QVector<double> &lowerQuartile,
26226  const QVector<double> &median,
26227  const QVector<double> &upperQuartile,
26228  const QVector<double> &maximum,
26229  bool alreadySorted) {
26230  if (keys.size() != minimum.size() ||
26231  minimum.size() != lowerQuartile.size() ||
26232  lowerQuartile.size() != median.size() ||
26233  median.size() != upperQuartile.size() ||
26234  upperQuartile.size() != maximum.size() || maximum.size() != keys.size())
26235  qDebug() << Q_FUNC_INFO
26236  << "keys, minimum, lowerQuartile, median, upperQuartile, "
26237  "maximum have different sizes:"
26238  << keys.size() << minimum.size() << lowerQuartile.size()
26239  << median.size() << upperQuartile.size() << maximum.size();
26240  const int n = qMin(keys.size(),
26241  qMin(minimum.size(),
26242  qMin(lowerQuartile.size(),
26243  qMin(median.size(), qMin(upperQuartile.size(),
26244  maximum.size())))));
26245  QVector<QCPStatisticalBoxData> tempData(n);
26246  QVector<QCPStatisticalBoxData>::iterator it = tempData.begin();
26247  const QVector<QCPStatisticalBoxData>::iterator itEnd = tempData.end();
26248  int i = 0;
26249  while (it != itEnd) {
26250  it->key = keys[i];
26251  it->minimum = minimum[i];
26252  it->lowerQuartile = lowerQuartile[i];
26253  it->median = median[i];
26254  it->upperQuartile = upperQuartile[i];
26255  it->maximum = maximum[i];
26256  ++it;
26257  ++i;
26258  }
26259  mDataContainer->add(tempData,
26260  alreadySorted); // don't modify tempData beyond this to
26261  // prevent copy on write
26262 }
26263 
26273  double minimum,
26274  double lowerQuartile,
26275  double median,
26276  double upperQuartile,
26277  double maximum,
26278  const QVector<double> &outliers) {
26281  outliers));
26282 }
26283 
26288  bool onlySelectable) const {
26290  if ((onlySelectable && mSelectable == QCP::stNone) ||
26291  mDataContainer->isEmpty())
26292  return result;
26293  if (!mKeyAxis || !mValueAxis) return result;
26294 
26295  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
26296  getVisibleDataBounds(visibleBegin, visibleEnd);
26297 
26298  for (QCPStatisticalBoxDataContainer::const_iterator it = visibleBegin;
26299  it != visibleEnd; ++it) {
26300  if (rect.intersects(getQuartileBox(it)))
26301  result.addDataRange(
26302  QCPDataRange(it - mDataContainer->constBegin(),
26303  it - mDataContainer->constBegin() + 1),
26304  false);
26305  }
26306  result.simplify();
26307  return result;
26308 }
26309 
26318 double QCPStatisticalBox::selectTest(const QPointF &pos,
26319  bool onlySelectable,
26320  QVariant *details) const {
26321  Q_UNUSED(details)
26322  if ((onlySelectable && mSelectable == QCP::stNone) ||
26323  mDataContainer->isEmpty())
26324  return -1;
26325  if (!mKeyAxis || !mValueAxis) return -1;
26326 
26327  if (mKeyAxis->axisRect()->rect().contains(pos.toPoint())) {
26328  // get visible data range:
26329  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
26331  mDataContainer->constEnd();
26332  getVisibleDataBounds(visibleBegin, visibleEnd);
26333  double minDistSqr = (std::numeric_limits<double>::max)();
26334  for (QCPStatisticalBoxDataContainer::const_iterator it = visibleBegin;
26335  it != visibleEnd; ++it) {
26336  if (getQuartileBox(it).contains(pos)) // quartile box
26337  {
26338  double currentDistSqr =
26339  mParentPlot->selectionTolerance() * 0.99 *
26340  mParentPlot->selectionTolerance() * 0.99;
26341  if (currentDistSqr < minDistSqr) {
26342  minDistSqr = currentDistSqr;
26343  closestDataPoint = it;
26344  }
26345  } else // whiskers
26346  {
26347  const QVector<QLineF> whiskerBackbones(
26349  for (int i = 0; i < whiskerBackbones.size(); ++i) {
26350  double currentDistSqr =
26352  whiskerBackbones.at(i));
26353  if (currentDistSqr < minDistSqr) {
26354  minDistSqr = currentDistSqr;
26355  closestDataPoint = it;
26356  }
26357  }
26358  }
26359  }
26360  if (details) {
26361  int pointIndex = closestDataPoint - mDataContainer->constBegin();
26362  details->setValue(
26363  QCPDataSelection(QCPDataRange(pointIndex, pointIndex + 1)));
26364  }
26365  return qSqrt(minDistSqr);
26366  }
26367  return -1;
26368 }
26369 
26370 /* inherits documentation from base class */
26372  QCP::SignDomain inSignDomain) const {
26373  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
26374  // determine exact range by including width of bars/flags:
26375  if (foundRange) {
26376  if (inSignDomain != QCP::sdPositive || range.lower - mWidth * 0.5 > 0)
26377  range.lower -= mWidth * 0.5;
26378  if (inSignDomain != QCP::sdNegative || range.upper + mWidth * 0.5 < 0)
26379  range.upper += mWidth * 0.5;
26380  }
26381  return range;
26382 }
26383 
26384 /* inherits documentation from base class */
26386  QCP::SignDomain inSignDomain,
26387  const QCPRange &inKeyRange) const {
26388  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
26389 }
26390 
26391 /* inherits documentation from base class */
26393  if (mDataContainer->isEmpty()) return;
26394  QCPAxis *keyAxis = mKeyAxis.data();
26395  QCPAxis *valueAxis = mValueAxis.data();
26396  if (!keyAxis || !valueAxis) {
26397  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
26398  return;
26399  }
26400 
26401  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
26402  getVisibleDataBounds(visibleBegin, visibleEnd);
26403 
26404  // loop over and draw segments of unselected/selected data:
26405  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
26406  getDataSegments(selectedSegments, unselectedSegments);
26407  allSegments << unselectedSegments << selectedSegments;
26408  for (int i = 0; i < allSegments.size(); ++i) {
26409  bool isSelectedSegment = i >= unselectedSegments.size();
26412  mDataContainer->limitIteratorsToDataRange(begin, end,
26413  allSegments.at(i));
26414  if (begin == end) continue;
26415 
26417  it != end; ++it) {
26418  // check data validity if flag set:
26419 #ifdef QCUSTOMPLOT_CHECK_DATA
26420  if (QCP::isInvalidData(it->key, it->minimum) ||
26421  QCP::isInvalidData(it->lowerQuartile, it->median) ||
26422  QCP::isInvalidData(it->upperQuartile, it->maximum))
26423  qDebug() << Q_FUNC_INFO << "Data point at" << it->key
26424  << "of drawn range has invalid data."
26425  << "Plottable name:" << name();
26426  for (int i = 0; i < it->outliers.size(); ++i)
26427  if (QCP::isInvalidData(it->outliers.at(i)))
26428  qDebug() << Q_FUNC_INFO << "Data point outlier at"
26429  << it->key << "of drawn range invalid."
26430  << "Plottable name:" << name();
26431 #endif
26432 
26433  if (isSelectedSegment && mSelectionDecorator) {
26434  mSelectionDecorator->applyPen(painter);
26435  mSelectionDecorator->applyBrush(painter);
26436  } else {
26437  painter->setPen(mPen);
26438  painter->setBrush(mBrush);
26439  }
26440  QCPScatterStyle finalOutlierStyle = mOutlierStyle;
26441  if (isSelectedSegment && mSelectionDecorator)
26442  finalOutlierStyle = mSelectionDecorator->getFinalScatterStyle(
26443  mOutlierStyle);
26444  drawStatisticalBox(painter, it, finalOutlierStyle);
26445  }
26446  }
26447 
26448  // draw other selection decoration that isn't just line/scatter pens and
26449  // brushes:
26450  if (mSelectionDecorator)
26452 }
26453 
26454 /* inherits documentation from base class */
26456  const QRectF &rect) const {
26457  // draw filled rect:
26459  painter->setPen(mPen);
26460  painter->setBrush(mBrush);
26461  QRectF r = QRectF(0, 0, rect.width() * 0.67, rect.height() * 0.67);
26462  r.moveCenter(rect.center());
26463  painter->drawRect(r);
26464 }
26465 
26476  QCPPainter *painter,
26478  const QCPScatterStyle &outlierStyle) const {
26479  // draw quartile box:
26481  const QRectF quartileBox = getQuartileBox(it);
26482  painter->drawRect(quartileBox);
26483  // draw median line with cliprect set to quartile box:
26484  painter->save();
26485  painter->setClipRect(quartileBox, Qt::IntersectClip);
26486  painter->setPen(mMedianPen);
26487  painter->drawLine(
26488  QLineF(coordsToPixels(it->key - mWidth * 0.5, it->median),
26489  coordsToPixels(it->key + mWidth * 0.5, it->median)));
26490  painter->restore();
26491  // draw whisker lines:
26493  painter->setPen(mWhiskerPen);
26494  painter->drawLines(getWhiskerBackboneLines(it));
26495  painter->setPen(mWhiskerBarPen);
26496  painter->drawLines(getWhiskerBarLines(it));
26497  // draw outliers:
26499  outlierStyle.applyTo(painter, mPen);
26500  for (int i = 0; i < it->outliers.size(); ++i)
26501  outlierStyle.drawShape(painter,
26502  coordsToPixels(it->key, it->outliers.at(i)));
26503 }
26504 
26524  if (!mKeyAxis) {
26525  qDebug() << Q_FUNC_INFO << "invalid key axis";
26526  begin = mDataContainer->constEnd();
26527  end = mDataContainer->constEnd();
26528  return;
26529  }
26530  begin = mDataContainer->findBegin(
26531  mKeyAxis.data()->range().lower -
26532  mWidth * 0.5); // subtract half width of box to include partially
26533  // visible data points
26534  end = mDataContainer->findEnd(
26535  mKeyAxis.data()->range().upper +
26536  mWidth * 0.5); // add half width of box to include partially
26537  // visible data points
26538 }
26539 
26550  QRectF result;
26551  result.setTopLeft(
26552  coordsToPixels(it->key - mWidth * 0.5, it->upperQuartile));
26553  result.setBottomRight(
26554  coordsToPixels(it->key + mWidth * 0.5, it->lowerQuartile));
26555  return result;
26556 }
26557 
26568  QVector<QLineF> result(2);
26569  result[0].setPoints(coordsToPixels(it->key, it->lowerQuartile),
26570  coordsToPixels(it->key, it->minimum)); // min backbone
26571  result[1].setPoints(coordsToPixels(it->key, it->upperQuartile),
26572  coordsToPixels(it->key, it->maximum)); // max backbone
26573  return result;
26574 }
26575 
26586  QVector<QLineF> result(2);
26587  result[0].setPoints(
26588  coordsToPixels(it->key - mWhiskerWidth * 0.5, it->minimum),
26589  coordsToPixels(it->key + mWhiskerWidth * 0.5,
26590  it->minimum)); // min bar
26591  result[1].setPoints(
26592  coordsToPixels(it->key - mWhiskerWidth * 0.5, it->maximum),
26593  coordsToPixels(it->key + mWhiskerWidth * 0.5,
26594  it->maximum)); // max bar
26595  return result;
26596 }
26597 /* end of 'src/plottables/plottable-statisticalbox.cpp' */
26598 
26599 /* including file 'src/plottables/plottable-colormap.cpp', size 47881 */
26600 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
26601 
26605 
26643 /* start of documentation of inline functions */
26644 
26651 /* end of documentation of inline functions */
26652 
26662  int valueSize,
26663  const QCPRange &keyRange,
26664  const QCPRange &valueRange)
26665  : mKeySize(0),
26666  mValueSize(0),
26667  mKeyRange(keyRange),
26668  mValueRange(valueRange),
26669  mIsEmpty(true),
26670  mData(0),
26671  mAlpha(0),
26672  mDataModified(true) {
26674  fill(0);
26675 }
26676 
26678  if (mData) delete[] mData;
26679  if (mAlpha) delete[] mAlpha;
26680 }
26681 
26687  : mKeySize(0),
26688  mValueSize(0),
26689  mIsEmpty(true),
26690  mData(0),
26691  mAlpha(0),
26692  mDataModified(true) {
26693  *this = other;
26694 }
26695 
26701  if (&other != this) {
26702  const int keySize = other.keySize();
26703  const int valueSize = other.valueSize();
26704  if (!other.mAlpha && mAlpha) clearAlpha();
26706  if (other.mAlpha && !mAlpha) createAlpha(false);
26707  setRange(other.keyRange(), other.valueRange());
26708  if (!isEmpty()) {
26709  memcpy(mData, other.mData, sizeof(mData[0]) * keySize * valueSize);
26710  if (mAlpha)
26711  memcpy(mAlpha, other.mAlpha,
26712  sizeof(mAlpha[0]) * keySize * valueSize);
26713  }
26714  mDataBounds = other.mDataBounds;
26715  mDataModified = true;
26716  }
26717  return *this;
26718 }
26719 
26720 /* undocumented getter */
26721 double QCPColorMapData::data(double key, double value) {
26722  int keyCell = (key - mKeyRange.lower) /
26723  (mKeyRange.upper - mKeyRange.lower) * (mKeySize - 1) +
26724  0.5;
26725  int valueCell = (value - mValueRange.lower) /
26727  (mValueSize - 1) +
26728  0.5;
26729  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 &&
26730  valueCell < mValueSize)
26731  return mData[valueCell * mKeySize + keyCell];
26732  else
26733  return 0;
26734 }
26735 
26736 /* undocumented getter */
26737 double QCPColorMapData::cell(int keyIndex, int valueIndex) {
26738  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 &&
26739  valueIndex < mValueSize)
26740  return mData[valueIndex * mKeySize + keyIndex];
26741  else
26742  return 0;
26743 }
26744 
26755 unsigned char QCPColorMapData::alpha(int keyIndex, int valueIndex) {
26756  if (mAlpha && keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 &&
26757  valueIndex < mValueSize)
26758  return mAlpha[valueIndex * mKeySize + keyIndex];
26759  else
26760  return 255;
26761 }
26762 
26775 void QCPColorMapData::setSize(int keySize, int valueSize) {
26776  if (keySize != mKeySize || valueSize != mValueSize) {
26777  mKeySize = keySize;
26779  if (mData) delete[] mData;
26780  mIsEmpty = mKeySize == 0 || mValueSize == 0;
26781  if (!mIsEmpty) {
26782 #ifdef __EXCEPTIONS
26783  try { // 2D arrays get memory intensive fast. So if the allocation
26784  // fails, at least output debug message
26785 #endif
26786  mData = new double[mKeySize * mValueSize];
26787 #ifdef __EXCEPTIONS
26788  } catch (...) {
26789  mData = 0;
26790  }
26791 #endif
26792  if (mData)
26793  fill(0);
26794  else
26795  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "
26796  << mKeySize << "*" << mValueSize;
26797  } else
26798  mData = 0;
26799 
26800  if (mAlpha) // if we had an alpha map, recreate it with new size
26801  createAlpha();
26802 
26803  mDataModified = true;
26804  }
26805 }
26806 
26819 
26831 void QCPColorMapData::setValueSize(int valueSize) {
26833 }
26834 
26847  const QCPRange &valueRange) {
26850 }
26851 
26865  mKeyRange = keyRange;
26866 }
26867 
26880 void QCPColorMapData::setValueRange(const QCPRange &valueRange) {
26882 }
26883 
26897 void QCPColorMapData::setData(double key, double value, double z) {
26898  int keyCell = (key - mKeyRange.lower) /
26899  (mKeyRange.upper - mKeyRange.lower) * (mKeySize - 1) +
26900  0.5;
26901  int valueCell = (value - mValueRange.lower) /
26903  (mValueSize - 1) +
26904  0.5;
26905  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 &&
26906  valueCell < mValueSize) {
26907  mData[valueCell * mKeySize + keyCell] = z;
26908  if (z < mDataBounds.lower) mDataBounds.lower = z;
26909  if (z > mDataBounds.upper) mDataBounds.upper = z;
26910  mDataModified = true;
26911  }
26912 }
26913 
26926 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z) {
26927  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 &&
26928  valueIndex < mValueSize) {
26929  mData[valueIndex * mKeySize + keyIndex] = z;
26930  if (z < mDataBounds.lower) mDataBounds.lower = z;
26931  if (z > mDataBounds.upper) mDataBounds.upper = z;
26932  mDataModified = true;
26933  } else
26934  qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex
26935  << valueIndex;
26936 }
26937 
26954 void QCPColorMapData::setAlpha(int keyIndex,
26955  int valueIndex,
26956  unsigned char alpha) {
26957  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 &&
26958  valueIndex < mValueSize) {
26959  if (mAlpha || createAlpha()) {
26960  mAlpha[valueIndex * mKeySize + keyIndex] = alpha;
26961  mDataModified = true;
26962  }
26963  } else
26964  qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex
26965  << valueIndex;
26966 }
26967 
26984  if (mKeySize > 0 && mValueSize > 0) {
26985  double minHeight = mData[0];
26986  double maxHeight = mData[0];
26987  const int dataCount = mValueSize * mKeySize;
26988  for (int i = 0; i < dataCount; ++i) {
26989  if (mData[i] > maxHeight) maxHeight = mData[i];
26990  if (mData[i] < minHeight) minHeight = mData[i];
26991  }
26992  mDataBounds.lower = minHeight;
26993  mDataBounds.upper = maxHeight;
26994  }
26995 }
26996 
27003 
27008  if (mAlpha) {
27009  delete[] mAlpha;
27010  mAlpha = 0;
27011  mDataModified = true;
27012  }
27013 }
27014 
27018 void QCPColorMapData::fill(double z) {
27019  const int dataCount = mValueSize * mKeySize;
27020  for (int i = 0; i < dataCount; ++i) mData[i] = z;
27021  mDataBounds = QCPRange(z, z);
27022  mDataModified = true;
27023 }
27024 
27035 void QCPColorMapData::fillAlpha(unsigned char alpha) {
27036  if (mAlpha || createAlpha(false)) {
27037  const int dataCount = mValueSize * mKeySize;
27038  for (int i = 0; i < dataCount; ++i) mAlpha[i] = alpha;
27039  mDataModified = true;
27040  }
27041 }
27042 
27063  double value,
27064  int *keyIndex,
27065  int *valueIndex) const {
27066  if (keyIndex)
27067  *keyIndex = (key - mKeyRange.lower) /
27069  (mKeySize - 1) +
27070  0.5;
27071  if (valueIndex)
27072  *valueIndex = (value - mValueRange.lower) /
27074  (mValueSize - 1) +
27075  0.5;
27076 }
27077 
27095  int valueIndex,
27096  double *key,
27097  double *value) const {
27098  if (key)
27099  *key = keyIndex / (double)(mKeySize - 1) *
27101  mKeyRange.lower;
27102  if (value)
27103  *value = valueIndex / (double)(mValueSize - 1) *
27106 }
27107 
27123 bool QCPColorMapData::createAlpha(bool initializeOpaque) {
27124  clearAlpha();
27125  if (isEmpty()) return false;
27126 
27127 #ifdef __EXCEPTIONS
27128  try { // 2D arrays get memory intensive fast. So if the allocation fails,
27129  // at least output debug message
27130 #endif
27131  mAlpha = new unsigned char[mKeySize * mValueSize];
27132 #ifdef __EXCEPTIONS
27133  } catch (...) {
27134  mAlpha = 0;
27135  }
27136 #endif
27137  if (mAlpha) {
27138  if (initializeOpaque) fillAlpha(255);
27139  return true;
27140  } else {
27141  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "
27142  << mKeySize << "*" << mValueSize;
27143  return false;
27144  }
27145 }
27146 
27150 
27233 /* start documentation of inline functions */
27234 
27243 /* end documentation of inline functions */
27244 
27245 /* start documentation of signals */
27246 
27268 /* end documentation of signals */
27269 
27279  : QCPAbstractPlottable(keyAxis, valueAxis),
27280  mDataScaleType(QCPAxis::stLinear),
27281  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
27282  mGradient(QCPColorGradient::gpCold),
27283  mInterpolate(true),
27284  mTightBoundary(false),
27285  mMapImageInvalidated(true) {}
27286 
27288 
27298  if (mMapData == data) {
27299  qDebug() << Q_FUNC_INFO
27300  << "The data pointer is already in (and owned by) this "
27301  "plottable"
27302  << reinterpret_cast<quintptr>(data);
27303  return;
27304  }
27305  if (copy) {
27306  *mMapData = *data;
27307  } else {
27308  delete mMapData;
27309  mMapData = data;
27310  }
27311  mMapImageInvalidated = true;
27312 }
27313 
27323 void QCPColorMap::setDataRange(const QCPRange &dataRange) {
27324  if (!QCPRange::validRange(dataRange)) return;
27325  if (mDataRange.lower != dataRange.lower ||
27329  else
27331  mMapImageInvalidated = true;
27333  }
27334 }
27335 
27343  if (mDataScaleType != scaleType) {
27344  mDataScaleType = scaleType;
27345  mMapImageInvalidated = true;
27349  }
27350 }
27351 
27366  if (mGradient != gradient) {
27367  mGradient = gradient;
27368  mMapImageInvalidated = true;
27369  emit gradientChanged(mGradient);
27370  }
27371 }
27372 
27381 void QCPColorMap::setInterpolate(bool enabled) {
27382  mInterpolate = enabled;
27384  true; // because oversampling factors might need to change
27385 }
27386 
27401 void QCPColorMap::setTightBoundary(bool enabled) { mTightBoundary = enabled; }
27402 
27421  if (mColorScale) // unconnect signals from old color scale
27422  {
27423  disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(),
27424  SLOT(setDataRange(QCPRange)));
27425  disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)),
27426  mColorScale.data(),
27428  disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)),
27429  mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
27430  disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this,
27431  SLOT(setDataRange(QCPRange)));
27432  disconnect(mColorScale.data(),
27433  SIGNAL(gradientChanged(QCPColorGradient)), this,
27434  SLOT(setGradient(QCPColorGradient)));
27435  disconnect(mColorScale.data(),
27438  }
27440  if (mColorScale) // connect signals to new color scale
27441  {
27442  setGradient(mColorScale.data()->gradient());
27443  setDataRange(mColorScale.data()->dataRange());
27444  setDataScaleType(mColorScale.data()->dataScaleType());
27445  connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(),
27446  SLOT(setDataRange(QCPRange)));
27447  connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)),
27449  connect(this, SIGNAL(gradientChanged(QCPColorGradient)),
27450  mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
27451  connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this,
27452  SLOT(setDataRange(QCPRange)));
27453  connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)),
27454  this, SLOT(setGradient(QCPColorGradient)));
27455  connect(mColorScale.data(),
27458  }
27459 }
27460 
27484 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds) {
27485  if (recalculateDataBounds) mMapData->recalculateDataBounds();
27487 }
27488 
27504 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode,
27505  const QSize &thumbSize) {
27506  if (mMapImage.isNull() && !data()->isEmpty())
27507  updateMapImage(); // try to update map image if it's null (happens if
27508  // no draw has happened yet)
27509 
27510  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so
27511  // check here again
27512  {
27513  bool mirrorX =
27514  (keyAxis()->orientation() == Qt::Horizontal ? keyAxis()
27515  : valueAxis())
27516  ->rangeReversed();
27517  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis()
27518  : keyAxis())
27519  ->rangeReversed();
27520  mLegendIcon =
27521  QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY))
27522  .scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
27523  }
27524 }
27525 
27526 /* inherits documentation from base class */
27527 double QCPColorMap::selectTest(const QPointF &pos,
27528  bool onlySelectable,
27529  QVariant *details) const {
27530  Q_UNUSED(details)
27531  if ((onlySelectable && mSelectable == QCP::stNone) || mMapData->isEmpty())
27532  return -1;
27533  if (!mKeyAxis || !mValueAxis) return -1;
27534 
27535  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
27536  double posKey, posValue;
27537  pixelsToCoords(pos, posKey, posValue);
27538  if (mMapData->keyRange().contains(posKey) &&
27539  mMapData->valueRange().contains(posValue)) {
27540  if (details)
27541  details->setValue(QCPDataSelection(QCPDataRange(
27542  0, 1))); // temporary solution, to facilitate
27543  // whole-plottable selection. Replace in
27544  // future version with segmented 2D selection.
27545  return mParentPlot->selectionTolerance() * 0.99;
27546  }
27547  }
27548  return -1;
27549 }
27550 
27551 /* inherits documentation from base class */
27553  QCP::SignDomain inSignDomain) const {
27554  foundRange = true;
27556  result.normalize();
27557  if (inSignDomain == QCP::sdPositive) {
27558  if (result.lower <= 0 && result.upper > 0)
27559  result.lower = result.upper * 1e-3;
27560  else if (result.lower <= 0 && result.upper <= 0)
27561  foundRange = false;
27562  } else if (inSignDomain == QCP::sdNegative) {
27563  if (result.upper >= 0 && result.lower < 0)
27564  result.upper = result.lower * 1e-3;
27565  else if (result.upper >= 0 && result.lower >= 0)
27566  foundRange = false;
27567  }
27568  return result;
27569 }
27570 
27571 /* inherits documentation from base class */
27573  QCP::SignDomain inSignDomain,
27574  const QCPRange &inKeyRange) const {
27575  if (inKeyRange != QCPRange()) {
27576  if (mMapData->keyRange().upper < inKeyRange.lower ||
27577  mMapData->keyRange().lower > inKeyRange.upper) {
27578  foundRange = false;
27579  return QCPRange();
27580  }
27581  }
27582 
27583  foundRange = true;
27585  result.normalize();
27586  if (inSignDomain == QCP::sdPositive) {
27587  if (result.lower <= 0 && result.upper > 0)
27588  result.lower = result.upper * 1e-3;
27589  else if (result.lower <= 0 && result.upper <= 0)
27590  foundRange = false;
27591  } else if (inSignDomain == QCP::sdNegative) {
27592  if (result.upper >= 0 && result.lower < 0)
27593  result.upper = result.lower * 1e-3;
27594  else if (result.upper >= 0 && result.lower >= 0)
27595  foundRange = false;
27596  }
27597  return result;
27598 }
27599 
27616  QCPAxis *keyAxis = mKeyAxis.data();
27617  if (!keyAxis) return;
27618  if (mMapData->isEmpty()) return;
27619 
27620  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
27621  const int keySize = mMapData->keySize();
27622  const int valueSize = mMapData->valueSize();
27623  int keyOversamplingFactor =
27624  mInterpolate
27625  ? 1
27626  : (int)(1.0 +
27627  100.0 / (double)keySize); // make mMapImage have at
27628  // least size 100, factor
27629  // becomes 1 if size >
27630  // 200 or interpolation
27631  // is on
27632  int valueOversamplingFactor =
27633  mInterpolate
27634  ? 1
27635  : (int)(1.0 +
27636  100.0 / (double)valueSize); // make mMapImage have
27637  // at least size 100,
27638  // factor becomes 1 if
27639  // size > 200 or
27640  // interpolation is on
27641 
27642  // resize mMapImage to correct dimensions including possible oversampling
27643  // factors, according to key/value axes orientation:
27644  if (keyAxis->orientation() == Qt::Horizontal &&
27645  (mMapImage.width() != keySize * keyOversamplingFactor ||
27646  mMapImage.height() != valueSize * valueOversamplingFactor))
27647  mMapImage = QImage(QSize(keySize * keyOversamplingFactor,
27648  valueSize * valueOversamplingFactor),
27649  format);
27650  else if (keyAxis->orientation() == Qt::Vertical &&
27651  (mMapImage.width() != valueSize * valueOversamplingFactor ||
27652  mMapImage.height() != keySize * keyOversamplingFactor))
27653  mMapImage = QImage(QSize(valueSize * valueOversamplingFactor,
27654  keySize * keyOversamplingFactor),
27655  format);
27656 
27657  if (mMapImage.isNull()) {
27658  qDebug() << Q_FUNC_INFO
27659  << "Couldn't create map image (possibly too large for memory)";
27660  mMapImage = QImage(QSize(10, 10), format);
27661  mMapImage.fill(Qt::black);
27662  } else {
27663  QImage *localMapImage =
27664  &mMapImage; // this is the image on which the colorization
27665  // operates. Either the final mMapImage, or if we
27666  // need oversampling, mUndersampledMapImage
27667  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) {
27668  // resize undersampled map image to actual key/value cell sizes:
27669  if (keyAxis->orientation() == Qt::Horizontal &&
27670  (mUndersampledMapImage.width() != keySize ||
27671  mUndersampledMapImage.height() != valueSize))
27673  QImage(QSize(keySize, valueSize), format);
27674  else if (keyAxis->orientation() == Qt::Vertical &&
27675  (mUndersampledMapImage.width() != valueSize ||
27676  mUndersampledMapImage.height() != keySize))
27678  QImage(QSize(valueSize, keySize), format);
27679  localMapImage =
27680  &mUndersampledMapImage; // make the colorization run on the
27681  // undersampled image
27682  } else if (!mUndersampledMapImage.isNull())
27684  QImage(); // don't need oversampling mechanism anymore (map
27685  // size has changed) but mUndersampledMapImage
27686  // still has nonzero size, free it
27687 
27688  const double *rawData = mMapData->mData;
27689  const unsigned char *rawAlpha = mMapData->mAlpha;
27690  if (keyAxis->orientation() == Qt::Horizontal) {
27691  const int lineCount = valueSize;
27692  const int rowCount = keySize;
27693  for (int line = 0; line < lineCount; ++line) {
27694  QRgb *pixels = reinterpret_cast<QRgb *>(localMapImage->scanLine(
27695  lineCount - 1 -
27696  line)); // invert scanline index because QImage counts
27697  // scanlines from top, but our vertical index
27698  // counts from bottom (mathematical coordinate
27699  // system)
27700  if (rawAlpha)
27702  rawData + line * rowCount,
27703  rawAlpha + line * rowCount, mDataRange, pixels,
27704  rowCount, 1,
27706  else
27708  rawData + line * rowCount, mDataRange, pixels,
27709  rowCount, 1,
27711  }
27712  } else // keyAxis->orientation() == Qt::Vertical
27713  {
27714  const int lineCount = keySize;
27715  const int rowCount = valueSize;
27716  for (int line = 0; line < lineCount; ++line) {
27717  QRgb *pixels = reinterpret_cast<QRgb *>(localMapImage->scanLine(
27718  lineCount - 1 -
27719  line)); // invert scanline index because QImage counts
27720  // scanlines from top, but our vertical index
27721  // counts from bottom (mathematical coordinate
27722  // system)
27723  if (rawAlpha)
27725  rawData + line, rawAlpha + line, mDataRange, pixels,
27726  rowCount, lineCount,
27728  else
27730  rawData + line, mDataRange, pixels, rowCount,
27731  lineCount,
27733  }
27734  }
27735 
27736  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) {
27737  if (keyAxis->orientation() == Qt::Horizontal)
27739  keySize * keyOversamplingFactor,
27740  valueSize * valueOversamplingFactor,
27741  Qt::IgnoreAspectRatio, Qt::FastTransformation);
27742  else
27744  valueSize * valueOversamplingFactor,
27745  keySize * keyOversamplingFactor, Qt::IgnoreAspectRatio,
27746  Qt::FastTransformation);
27747  }
27748  }
27749  mMapData->mDataModified = false;
27750  mMapImageInvalidated = false;
27751 }
27752 
27753 /* inherits documentation from base class */
27755  if (mMapData->isEmpty()) return;
27756  if (!mKeyAxis || !mValueAxis) return;
27758 
27760 
27761  // use buffer if painting vectorized (PDF):
27762  const bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
27763  QCPPainter *localPainter = painter; // will be redirected to paint on
27764  // mapBuffer if painting vectorized
27765  QRectF mapBufferTarget; // the rect in absolute widget coordinates where
27766  // the visible map portion/buffer will end up in
27767  QPixmap mapBuffer;
27768  if (useBuffer) {
27769  const double mapBufferPixelRatio =
27770  3; // factor by which DPI is increased in embedded bitmaps
27771  mapBufferTarget = painter->clipRegion().boundingRect();
27772  mapBuffer = QPixmap(
27773  (mapBufferTarget.size() * mapBufferPixelRatio).toSize());
27774  mapBuffer.fill(Qt::transparent);
27775  localPainter = new QCPPainter(&mapBuffer);
27776  localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
27777  localPainter->translate(-mapBufferTarget.topLeft());
27778  }
27779 
27780  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower,
27784  .normalized();
27785  // extend imageRect to contain outer halves/quarters of bordering/cornering
27786  // pixels (cells are centered on map range boundary):
27787  double halfCellWidth = 0; // in pixels
27788  double halfCellHeight = 0; // in pixels
27789  if (keyAxis()->orientation() == Qt::Horizontal) {
27790  if (mMapData->keySize() > 1)
27791  halfCellWidth =
27792  0.5 * imageRect.width() / (double)(mMapData->keySize() - 1);
27793  if (mMapData->valueSize() > 1)
27794  halfCellHeight = 0.5 * imageRect.height() /
27795  (double)(mMapData->valueSize() - 1);
27796  } else // keyAxis orientation is Qt::Vertical
27797  {
27798  if (mMapData->keySize() > 1)
27799  halfCellHeight = 0.5 * imageRect.height() /
27800  (double)(mMapData->keySize() - 1);
27801  if (mMapData->valueSize() > 1)
27802  halfCellWidth = 0.5 * imageRect.width() /
27803  (double)(mMapData->valueSize() - 1);
27804  }
27805  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth,
27806  halfCellHeight);
27807  const bool mirrorX =
27808  (keyAxis()->orientation() == Qt::Horizontal ? keyAxis()
27809  : valueAxis())
27810  ->rangeReversed();
27811  const bool mirrorY =
27812  (valueAxis()->orientation() == Qt::Vertical ? valueAxis()
27813  : keyAxis())
27814  ->rangeReversed();
27815  const bool smoothBackup = localPainter->renderHints().testFlag(
27816  QPainter::SmoothPixmapTransform);
27817  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
27818  QRegion clipBackup;
27819  if (mTightBoundary) {
27820  clipBackup = localPainter->clipRegion();
27821  QRectF tightClipRect =
27826  .normalized();
27827  localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
27828  }
27829  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
27830  if (mTightBoundary) localPainter->setClipRegion(clipBackup);
27831  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
27832 
27833  if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer
27834  // with original painter
27835  {
27836  delete localPainter;
27837  painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
27838  }
27839 }
27840 
27841 /* inherits documentation from base class */
27843  const QRectF &rect) const {
27845  // draw map thumbnail:
27846  if (!mLegendIcon.isNull()) {
27847  QPixmap scaledIcon =
27848  mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio,
27849  Qt::FastTransformation);
27850  QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
27851  iconRect.moveCenter(rect.center());
27852  painter->drawPixmap(iconRect.topLeft(), scaledIcon);
27853  }
27854  /*
27855  // draw frame:
27856  painter->setBrush(Qt::NoBrush);
27857  painter->setPen(Qt::black);
27858  painter->drawRect(rect.adjusted(1, 1, 0, 0));
27859  */
27860 }
27861 /* end of 'src/plottables/plottable-colormap.cpp' */
27862 
27863 /* including file 'src/plottables/plottable-financial.cpp', size 42827 */
27864 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
27865 
27869 
27888 /* start documentation of inline functions */
27889 
27941 /* end documentation of inline functions */
27942 
27947  : key(0), open(0), high(0), low(0), close(0) {}
27948 
27953  double key, double open, double high, double low, double close)
27954  : key(key), open(open), high(high), low(low), close(close) {}
27955 
27959 
28016 /* start of documentation of inline functions */
28017 
28026 /* end of documentation of inline functions */
28027 
28041  : QCPAbstractPlottable1D<QCPFinancialData>(keyAxis, valueAxis),
28042  mChartStyle(csCandlestick),
28043  mWidth(0.5),
28044  mWidthType(wtPlotCoords),
28045  mTwoColored(true),
28046  mBrushPositive(QBrush(QColor(50, 160, 0))),
28047  mBrushNegative(QBrush(QColor(180, 0, 15))),
28048  mPenPositive(QPen(QColor(40, 150, 0))),
28049  mPenNegative(QPen(QColor(170, 5, 5))) {
28050  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
28051 }
28052 
28054 
28072 void QCPFinancial::setData(QSharedPointer<QCPFinancialDataContainer> data) {
28073  mDataContainer = data;
28074 }
28075 
28088 void QCPFinancial::setData(const QVector<double> &keys,
28089  const QVector<double> &open,
28090  const QVector<double> &high,
28091  const QVector<double> &low,
28092  const QVector<double> &close,
28093  bool alreadySorted) {
28094  mDataContainer->clear();
28095  addData(keys, open, high, low, close, alreadySorted);
28096 }
28097 
28102  mChartStyle = style;
28103 }
28104 
28113 
28124 }
28125 
28136 
28147 void QCPFinancial::setBrushPositive(const QBrush &brush) {
28149 }
28150 
28161 void QCPFinancial::setBrushNegative(const QBrush &brush) {
28163 }
28164 
28175 void QCPFinancial::setPenPositive(const QPen &pen) { mPenPositive = pen; }
28176 
28187 void QCPFinancial::setPenNegative(const QPen &pen) { mPenNegative = pen; }
28188 
28204 void QCPFinancial::addData(const QVector<double> &keys,
28205  const QVector<double> &open,
28206  const QVector<double> &high,
28207  const QVector<double> &low,
28208  const QVector<double> &close,
28209  bool alreadySorted) {
28210  if (keys.size() != open.size() || open.size() != high.size() ||
28211  high.size() != low.size() || low.size() != close.size() ||
28212  close.size() != keys.size())
28213  qDebug() << Q_FUNC_INFO
28214  << "keys, open, high, low, close have different sizes:"
28215  << keys.size() << open.size() << high.size() << low.size()
28216  << close.size();
28217  const int n = qMin(keys.size(),
28218  qMin(open.size(),
28219  qMin(high.size(), qMin(low.size(), close.size()))));
28220  QVector<QCPFinancialData> tempData(n);
28221  QVector<QCPFinancialData>::iterator it = tempData.begin();
28222  const QVector<QCPFinancialData>::iterator itEnd = tempData.end();
28223  int i = 0;
28224  while (it != itEnd) {
28225  it->key = keys[i];
28226  it->open = open[i];
28227  it->high = high[i];
28228  it->low = low[i];
28229  it->close = close[i];
28230  ++it;
28231  ++i;
28232  }
28233  mDataContainer->add(tempData,
28234  alreadySorted); // don't modify tempData beyond this to
28235  // prevent copy on write
28236 }
28237 
28249  double key, double open, double high, double low, double close) {
28250  mDataContainer->add(QCPFinancialData(key, open, high, low, close));
28251 }
28252 
28257  bool onlySelectable) const {
28259  if ((onlySelectable && mSelectable == QCP::stNone) ||
28260  mDataContainer->isEmpty())
28261  return result;
28262  if (!mKeyAxis || !mValueAxis) return result;
28263 
28264  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
28265  getVisibleDataBounds(visibleBegin, visibleEnd);
28266 
28267  for (QCPFinancialDataContainer::const_iterator it = visibleBegin;
28268  it != visibleEnd; ++it) {
28269  if (rect.intersects(selectionHitBox(it)))
28270  result.addDataRange(
28271  QCPDataRange(it - mDataContainer->constBegin(),
28272  it - mDataContainer->constBegin() + 1),
28273  false);
28274  }
28275  result.simplify();
28276  return result;
28277 }
28278 
28287 double QCPFinancial::selectTest(const QPointF &pos,
28288  bool onlySelectable,
28289  QVariant *details) const {
28290  Q_UNUSED(details)
28291  if ((onlySelectable && mSelectable == QCP::stNone) ||
28292  mDataContainer->isEmpty())
28293  return -1;
28294  if (!mKeyAxis || !mValueAxis) return -1;
28295 
28296  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
28297  // get visible data range:
28298  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
28300  mDataContainer->constEnd();
28301  getVisibleDataBounds(visibleBegin, visibleEnd);
28302  // perform select test according to configured style:
28303  double result = -1;
28304  switch (mChartStyle) {
28305  case QCPFinancial::csOhlc:
28306  result = ohlcSelectTest(pos, visibleBegin, visibleEnd,
28307  closestDataPoint);
28308  break;
28310  result = candlestickSelectTest(pos, visibleBegin, visibleEnd,
28311  closestDataPoint);
28312  break;
28313  }
28314  if (details) {
28315  int pointIndex = closestDataPoint - mDataContainer->constBegin();
28316  details->setValue(
28317  QCPDataSelection(QCPDataRange(pointIndex, pointIndex + 1)));
28318  }
28319  return result;
28320  }
28321 
28322  return -1;
28323 }
28324 
28325 /* inherits documentation from base class */
28327  QCP::SignDomain inSignDomain) const {
28328  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
28329  // determine exact range by including width of bars/flags:
28330  if (foundRange) {
28331  if (inSignDomain != QCP::sdPositive || range.lower - mWidth * 0.5 > 0)
28332  range.lower -= mWidth * 0.5;
28333  if (inSignDomain != QCP::sdNegative || range.upper + mWidth * 0.5 < 0)
28334  range.upper += mWidth * 0.5;
28335  }
28336  return range;
28337 }
28338 
28339 /* inherits documentation from base class */
28341  QCP::SignDomain inSignDomain,
28342  const QCPRange &inKeyRange) const {
28343  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
28344 }
28345 
28361  const QVector<double> &time,
28362  const QVector<double> &value,
28363  double timeBinSize,
28364  double timeBinOffset) {
28366  int count = qMin(time.size(), value.size());
28367  if (count == 0) return QCPFinancialDataContainer();
28368 
28369  QCPFinancialData currentBinData(0, value.first(), value.first(),
28370  value.first(), value.first());
28371  int currentBinIndex =
28372  qFloor((time.first() - timeBinOffset) / timeBinSize + 0.5);
28373  for (int i = 0; i < count; ++i) {
28374  int index = qFloor((time.at(i) - timeBinOffset) / timeBinSize + 0.5);
28375  if (currentBinIndex ==
28376  index) // data point still in current bin, extend high/low:
28377  {
28378  if (value.at(i) < currentBinData.low)
28379  currentBinData.low = value.at(i);
28380  if (value.at(i) > currentBinData.high)
28381  currentBinData.high = value.at(i);
28382  if (i ==
28383  count - 1) // last data point is in current bin, finalize bin:
28384  {
28385  currentBinData.close = value.at(i);
28386  currentBinData.key = timeBinOffset + (index)*timeBinSize;
28387  data.add(currentBinData);
28388  }
28389  } else // data point not anymore in current bin, set close of old and
28390  // open of new bin, and add old to map:
28391  {
28392  // finalize current bin:
28393  currentBinData.close = value.at(i - 1);
28394  currentBinData.key = timeBinOffset + (index - 1) * timeBinSize;
28395  data.add(currentBinData);
28396  // start next bin:
28397  currentBinIndex = index;
28398  currentBinData.open = value.at(i);
28399  currentBinData.high = value.at(i);
28400  currentBinData.low = value.at(i);
28401  }
28402  }
28403 
28404  return data;
28405 }
28406 
28407 /* inherits documentation from base class */
28409  // get visible data range:
28410  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
28411  getVisibleDataBounds(visibleBegin, visibleEnd);
28412 
28413  // loop over and draw segments of unselected/selected data:
28414  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
28415  getDataSegments(selectedSegments, unselectedSegments);
28416  allSegments << unselectedSegments << selectedSegments;
28417  for (int i = 0; i < allSegments.size(); ++i) {
28418  bool isSelectedSegment = i >= unselectedSegments.size();
28419  QCPFinancialDataContainer::const_iterator begin = visibleBegin;
28421  mDataContainer->limitIteratorsToDataRange(begin, end,
28422  allSegments.at(i));
28423  if (begin == end) continue;
28424 
28425  // draw data segment according to configured style:
28426  switch (mChartStyle) {
28427  case QCPFinancial::csOhlc:
28428  drawOhlcPlot(painter, begin, end, isSelectedSegment);
28429  break;
28431  drawCandlestickPlot(painter, begin, end, isSelectedSegment);
28432  break;
28433  }
28434  }
28435 
28436  // draw other selection decoration that isn't just line/scatter pens and
28437  // brushes:
28438  if (mSelectionDecorator)
28440 }
28441 
28442 /* inherits documentation from base class */
28444  const QRectF &rect) const {
28445  painter->setAntialiasing(false); // legend icon especially of csCandlestick
28446  // looks better without antialiasing
28447  if (mChartStyle == csOhlc) {
28448  if (mTwoColored) {
28449  // draw upper left half icon with positive color:
28450  painter->setBrush(mBrushPositive);
28451  painter->setPen(mPenPositive);
28452  painter->setClipRegion(QRegion(QPolygon()
28453  << rect.bottomLeft().toPoint()
28454  << rect.topRight().toPoint()
28455  << rect.topLeft().toPoint()));
28456  painter->drawLine(QLineF(0, rect.height() * 0.5, rect.width(),
28457  rect.height() * 0.5)
28458  .translated(rect.topLeft()));
28459  painter->drawLine(QLineF(rect.width() * 0.2, rect.height() * 0.3,
28460  rect.width() * 0.2, rect.height() * 0.5)
28461  .translated(rect.topLeft()));
28462  painter->drawLine(QLineF(rect.width() * 0.8, rect.height() * 0.5,
28463  rect.width() * 0.8, rect.height() * 0.7)
28464  .translated(rect.topLeft()));
28465  // draw bottom right half icon with negative color:
28466  painter->setBrush(mBrushNegative);
28467  painter->setPen(mPenNegative);
28468  painter->setClipRegion(QRegion(QPolygon()
28469  << rect.bottomLeft().toPoint()
28470  << rect.topRight().toPoint()
28471  << rect.bottomRight().toPoint()));
28472  painter->drawLine(QLineF(0, rect.height() * 0.5, rect.width(),
28473  rect.height() * 0.5)
28474  .translated(rect.topLeft()));
28475  painter->drawLine(QLineF(rect.width() * 0.2, rect.height() * 0.3,
28476  rect.width() * 0.2, rect.height() * 0.5)
28477  .translated(rect.topLeft()));
28478  painter->drawLine(QLineF(rect.width() * 0.8, rect.height() * 0.5,
28479  rect.width() * 0.8, rect.height() * 0.7)
28480  .translated(rect.topLeft()));
28481  } else {
28482  painter->setBrush(mBrush);
28483  painter->setPen(mPen);
28484  painter->drawLine(QLineF(0, rect.height() * 0.5, rect.width(),
28485  rect.height() * 0.5)
28486  .translated(rect.topLeft()));
28487  painter->drawLine(QLineF(rect.width() * 0.2, rect.height() * 0.3,
28488  rect.width() * 0.2, rect.height() * 0.5)
28489  .translated(rect.topLeft()));
28490  painter->drawLine(QLineF(rect.width() * 0.8, rect.height() * 0.5,
28491  rect.width() * 0.8, rect.height() * 0.7)
28492  .translated(rect.topLeft()));
28493  }
28494  } else if (mChartStyle == csCandlestick) {
28495  if (mTwoColored) {
28496  // draw upper left half icon with positive color:
28497  painter->setBrush(mBrushPositive);
28498  painter->setPen(mPenPositive);
28499  painter->setClipRegion(QRegion(QPolygon()
28500  << rect.bottomLeft().toPoint()
28501  << rect.topRight().toPoint()
28502  << rect.topLeft().toPoint()));
28503  painter->drawLine(QLineF(0, rect.height() * 0.5,
28504  rect.width() * 0.25, rect.height() * 0.5)
28505  .translated(rect.topLeft()));
28506  painter->drawLine(QLineF(rect.width() * 0.75, rect.height() * 0.5,
28507  rect.width(), rect.height() * 0.5)
28508  .translated(rect.topLeft()));
28509  painter->drawRect(QRectF(rect.width() * 0.25, rect.height() * 0.25,
28510  rect.width() * 0.5, rect.height() * 0.5)
28511  .translated(rect.topLeft()));
28512  // draw bottom right half icon with negative color:
28513  painter->setBrush(mBrushNegative);
28514  painter->setPen(mPenNegative);
28515  painter->setClipRegion(QRegion(QPolygon()
28516  << rect.bottomLeft().toPoint()
28517  << rect.topRight().toPoint()
28518  << rect.bottomRight().toPoint()));
28519  painter->drawLine(QLineF(0, rect.height() * 0.5,
28520  rect.width() * 0.25, rect.height() * 0.5)
28521  .translated(rect.topLeft()));
28522  painter->drawLine(QLineF(rect.width() * 0.75, rect.height() * 0.5,
28523  rect.width(), rect.height() * 0.5)
28524  .translated(rect.topLeft()));
28525  painter->drawRect(QRectF(rect.width() * 0.25, rect.height() * 0.25,
28526  rect.width() * 0.5, rect.height() * 0.5)
28527  .translated(rect.topLeft()));
28528  } else {
28529  painter->setBrush(mBrush);
28530  painter->setPen(mPen);
28531  painter->drawLine(QLineF(0, rect.height() * 0.5,
28532  rect.width() * 0.25, rect.height() * 0.5)
28533  .translated(rect.topLeft()));
28534  painter->drawLine(QLineF(rect.width() * 0.75, rect.height() * 0.5,
28535  rect.width(), rect.height() * 0.5)
28536  .translated(rect.topLeft()));
28537  painter->drawRect(QRectF(rect.width() * 0.25, rect.height() * 0.25,
28538  rect.width() * 0.5, rect.height() * 0.5)
28539  .translated(rect.topLeft()));
28540  }
28541  }
28542 }
28543 
28553  QCPPainter *painter,
28556  bool isSelected) {
28557  QCPAxis *keyAxis = mKeyAxis.data();
28558  QCPAxis *valueAxis = mValueAxis.data();
28559  if (!keyAxis || !valueAxis) {
28560  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
28561  return;
28562  }
28563 
28564  if (keyAxis->orientation() == Qt::Horizontal) {
28565  for (QCPFinancialDataContainer::const_iterator it = begin; it != end;
28566  ++it) {
28567  if (isSelected && mSelectionDecorator)
28568  mSelectionDecorator->applyPen(painter);
28569  else if (mTwoColored)
28570  painter->setPen(it->close >= it->open ? mPenPositive
28571  : mPenNegative);
28572  else
28573  painter->setPen(mPen);
28574  double keyPixel = keyAxis->coordToPixel(it->key);
28575  double openPixel = valueAxis->coordToPixel(it->open);
28576  double closePixel = valueAxis->coordToPixel(it->close);
28577  // draw backbone:
28578  painter->drawLine(
28579  QPointF(keyPixel, valueAxis->coordToPixel(it->high)),
28580  QPointF(keyPixel, valueAxis->coordToPixel(it->low)));
28581  // draw open:
28582  double pixelWidth = getPixelWidth(
28583  it->key, keyPixel); // sign of this makes sure open/close
28584  // are on correct sides
28585  painter->drawLine(QPointF(keyPixel - pixelWidth, openPixel),
28586  QPointF(keyPixel, openPixel));
28587  // draw close:
28588  painter->drawLine(QPointF(keyPixel, closePixel),
28589  QPointF(keyPixel + pixelWidth, closePixel));
28590  }
28591  } else {
28592  for (QCPFinancialDataContainer::const_iterator it = begin; it != end;
28593  ++it) {
28594  if (isSelected && mSelectionDecorator)
28595  mSelectionDecorator->applyPen(painter);
28596  else if (mTwoColored)
28597  painter->setPen(it->close >= it->open ? mPenPositive
28598  : mPenNegative);
28599  else
28600  painter->setPen(mPen);
28601  double keyPixel = keyAxis->coordToPixel(it->key);
28602  double openPixel = valueAxis->coordToPixel(it->open);
28603  double closePixel = valueAxis->coordToPixel(it->close);
28604  // draw backbone:
28605  painter->drawLine(
28606  QPointF(valueAxis->coordToPixel(it->high), keyPixel),
28607  QPointF(valueAxis->coordToPixel(it->low), keyPixel));
28608  // draw open:
28609  double pixelWidth = getPixelWidth(
28610  it->key, keyPixel); // sign of this makes sure open/close
28611  // are on correct sides
28612  painter->drawLine(QPointF(openPixel, keyPixel - pixelWidth),
28613  QPointF(openPixel, keyPixel));
28614  // draw close:
28615  painter->drawLine(QPointF(closePixel, keyPixel),
28616  QPointF(closePixel, keyPixel + pixelWidth));
28617  }
28618  }
28619 }
28620 
28630  QCPPainter *painter,
28633  bool isSelected) {
28634  QCPAxis *keyAxis = mKeyAxis.data();
28635  QCPAxis *valueAxis = mValueAxis.data();
28636  if (!keyAxis || !valueAxis) {
28637  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
28638  return;
28639  }
28640 
28641  if (keyAxis->orientation() == Qt::Horizontal) {
28642  for (QCPFinancialDataContainer::const_iterator it = begin; it != end;
28643  ++it) {
28644  if (isSelected && mSelectionDecorator) {
28645  mSelectionDecorator->applyPen(painter);
28646  mSelectionDecorator->applyBrush(painter);
28647  } else if (mTwoColored) {
28648  painter->setPen(it->close >= it->open ? mPenPositive
28649  : mPenNegative);
28650  painter->setBrush(it->close >= it->open ? mBrushPositive
28651  : mBrushNegative);
28652  } else {
28653  painter->setPen(mPen);
28654  painter->setBrush(mBrush);
28655  }
28656  double keyPixel = keyAxis->coordToPixel(it->key);
28657  double openPixel = valueAxis->coordToPixel(it->open);
28658  double closePixel = valueAxis->coordToPixel(it->close);
28659  // draw high:
28660  painter->drawLine(
28661  QPointF(keyPixel, valueAxis->coordToPixel(it->high)),
28662  QPointF(keyPixel, valueAxis->coordToPixel(
28663  qMax(it->open, it->close))));
28664  // draw low:
28665  painter->drawLine(
28666  QPointF(keyPixel, valueAxis->coordToPixel(it->low)),
28667  QPointF(keyPixel, valueAxis->coordToPixel(
28668  qMin(it->open, it->close))));
28669  // draw open-close box:
28670  double pixelWidth = getPixelWidth(it->key, keyPixel);
28671  painter->drawRect(
28672  QRectF(QPointF(keyPixel - pixelWidth, closePixel),
28673  QPointF(keyPixel + pixelWidth, openPixel)));
28674  }
28675  } else // keyAxis->orientation() == Qt::Vertical
28676  {
28677  for (QCPFinancialDataContainer::const_iterator it = begin; it != end;
28678  ++it) {
28679  if (isSelected && mSelectionDecorator) {
28680  mSelectionDecorator->applyPen(painter);
28681  mSelectionDecorator->applyBrush(painter);
28682  } else if (mTwoColored) {
28683  painter->setPen(it->close >= it->open ? mPenPositive
28684  : mPenNegative);
28685  painter->setBrush(it->close >= it->open ? mBrushPositive
28686  : mBrushNegative);
28687  } else {
28688  painter->setPen(mPen);
28689  painter->setBrush(mBrush);
28690  }
28691  double keyPixel = keyAxis->coordToPixel(it->key);
28692  double openPixel = valueAxis->coordToPixel(it->open);
28693  double closePixel = valueAxis->coordToPixel(it->close);
28694  // draw high:
28695  painter->drawLine(
28696  QPointF(valueAxis->coordToPixel(it->high), keyPixel),
28697  QPointF(valueAxis->coordToPixel(qMax(it->open, it->close)),
28698  keyPixel));
28699  // draw low:
28700  painter->drawLine(
28701  QPointF(valueAxis->coordToPixel(it->low), keyPixel),
28702  QPointF(valueAxis->coordToPixel(qMin(it->open, it->close)),
28703  keyPixel));
28704  // draw open-close box:
28705  double pixelWidth = getPixelWidth(it->key, keyPixel);
28706  painter->drawRect(
28707  QRectF(QPointF(closePixel, keyPixel - pixelWidth),
28708  QPointF(openPixel, keyPixel + pixelWidth)));
28709  }
28710  }
28711 }
28712 
28727 double QCPFinancial::getPixelWidth(double key, double keyPixel) const {
28728  double result = 0;
28729  switch (mWidthType) {
28730  case wtAbsolute: {
28731  if (mKeyAxis)
28732  result = mWidth * 0.5 * mKeyAxis.data()->pixelOrientation();
28733  break;
28734  }
28735  case wtAxisRectRatio: {
28736  if (mKeyAxis && mKeyAxis.data()->axisRect()) {
28737  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
28738  result = mKeyAxis.data()->axisRect()->width() * mWidth *
28739  0.5 * mKeyAxis.data()->pixelOrientation();
28740  else
28741  result = mKeyAxis.data()->axisRect()->height() * mWidth *
28742  0.5 * mKeyAxis.data()->pixelOrientation();
28743  } else
28744  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
28745  break;
28746  }
28747  case wtPlotCoords: {
28748  if (mKeyAxis)
28749  result = mKeyAxis.data()->coordToPixel(key + mWidth * 0.5) -
28750  keyPixel;
28751  else
28752  qDebug() << Q_FUNC_INFO << "No key axis defined";
28753  break;
28754  }
28755  }
28756  return result;
28757 }
28758 
28770  const QPointF &pos,
28773  QCPFinancialDataContainer::const_iterator &closestDataPoint) const {
28774  closestDataPoint = mDataContainer->constEnd();
28775  QCPAxis *keyAxis = mKeyAxis.data();
28776  QCPAxis *valueAxis = mValueAxis.data();
28777  if (!keyAxis || !valueAxis) {
28778  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
28779  return -1;
28780  }
28781 
28782  double minDistSqr = (std::numeric_limits<double>::max)();
28783  if (keyAxis->orientation() == Qt::Horizontal) {
28784  for (QCPFinancialDataContainer::const_iterator it = begin; it != end;
28785  ++it) {
28786  double keyPixel = keyAxis->coordToPixel(it->key);
28787  // calculate distance to backbone:
28788  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(
28789  QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)),
28790  QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)));
28791  if (currentDistSqr < minDistSqr) {
28792  minDistSqr = currentDistSqr;
28793  closestDataPoint = it;
28794  }
28795  }
28796  } else // keyAxis->orientation() == Qt::Vertical
28797  {
28798  for (QCPFinancialDataContainer::const_iterator it = begin; it != end;
28799  ++it) {
28800  double keyPixel = keyAxis->coordToPixel(it->key);
28801  // calculate distance to backbone:
28802  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(
28803  QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel),
28804  QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel));
28805  if (currentDistSqr < minDistSqr) {
28806  minDistSqr = currentDistSqr;
28807  closestDataPoint = it;
28808  }
28809  }
28810  }
28811  return qSqrt(minDistSqr);
28812 }
28813 
28825  const QPointF &pos,
28828  QCPFinancialDataContainer::const_iterator &closestDataPoint) const {
28829  closestDataPoint = mDataContainer->constEnd();
28830  QCPAxis *keyAxis = mKeyAxis.data();
28831  QCPAxis *valueAxis = mValueAxis.data();
28832  if (!keyAxis || !valueAxis) {
28833  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
28834  return -1;
28835  }
28836 
28837  double minDistSqr = (std::numeric_limits<double>::max)();
28838  if (keyAxis->orientation() == Qt::Horizontal) {
28839  for (QCPFinancialDataContainer::const_iterator it = begin; it != end;
28840  ++it) {
28841  double currentDistSqr;
28842  // determine whether pos is in open-close-box:
28843  QCPRange boxKeyRange(it->key - mWidth * 0.5,
28844  it->key + mWidth * 0.5);
28845  QCPRange boxValueRange(it->close, it->open);
28846  double posKey, posValue;
28847  pixelsToCoords(pos, posKey, posValue);
28848  if (boxKeyRange.contains(posKey) &&
28849  boxValueRange.contains(posValue)) // is in open-close-box
28850  {
28851  currentDistSqr = mParentPlot->selectionTolerance() * 0.99 *
28852  mParentPlot->selectionTolerance() * 0.99;
28853  } else {
28854  // calculate distance to high/low lines:
28855  double keyPixel = keyAxis->coordToPixel(it->key);
28856  double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(
28857  QCPVector2D(keyPixel,
28858  valueAxis->coordToPixel(it->high)),
28859  QCPVector2D(keyPixel, valueAxis->coordToPixel(qMax(
28860  it->open, it->close))));
28861  double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(
28862  QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)),
28863  QCPVector2D(keyPixel, valueAxis->coordToPixel(qMin(
28864  it->open, it->close))));
28865  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
28866  }
28867  if (currentDistSqr < minDistSqr) {
28868  minDistSqr = currentDistSqr;
28869  closestDataPoint = it;
28870  }
28871  }
28872  } else // keyAxis->orientation() == Qt::Vertical
28873  {
28874  for (QCPFinancialDataContainer::const_iterator it = begin; it != end;
28875  ++it) {
28876  double currentDistSqr;
28877  // determine whether pos is in open-close-box:
28878  QCPRange boxKeyRange(it->key - mWidth * 0.5,
28879  it->key + mWidth * 0.5);
28880  QCPRange boxValueRange(it->close, it->open);
28881  double posKey, posValue;
28882  pixelsToCoords(pos, posKey, posValue);
28883  if (boxKeyRange.contains(posKey) &&
28884  boxValueRange.contains(posValue)) // is in open-close-box
28885  {
28886  currentDistSqr = mParentPlot->selectionTolerance() * 0.99 *
28887  mParentPlot->selectionTolerance() * 0.99;
28888  } else {
28889  // calculate distance to high/low lines:
28890  double keyPixel = keyAxis->coordToPixel(it->key);
28891  double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(
28892  QCPVector2D(valueAxis->coordToPixel(it->high),
28893  keyPixel),
28895  qMax(it->open, it->close)),
28896  keyPixel));
28897  double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(
28898  QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel),
28900  qMin(it->open, it->close)),
28901  keyPixel));
28902  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
28903  }
28904  if (currentDistSqr < minDistSqr) {
28905  minDistSqr = currentDistSqr;
28906  closestDataPoint = it;
28907  }
28908  }
28909  }
28910  return qSqrt(minDistSqr);
28911 }
28912 
28933  if (!mKeyAxis) {
28934  qDebug() << Q_FUNC_INFO << "invalid key axis";
28935  begin = mDataContainer->constEnd();
28936  end = mDataContainer->constEnd();
28937  return;
28938  }
28939  begin = mDataContainer->findBegin(
28940  mKeyAxis.data()->range().lower -
28941  mWidth * 0.5); // subtract half width of ohlc/candlestick to
28942  // include partially visible data points
28943  end = mDataContainer->findEnd(
28944  mKeyAxis.data()->range().upper +
28945  mWidth * 0.5); // add half width of ohlc/candlestick to include
28946  // partially visible data points
28947 }
28948 
28957  QCPAxis *keyAxis = mKeyAxis.data();
28958  QCPAxis *valueAxis = mValueAxis.data();
28959  if (!keyAxis || !valueAxis) {
28960  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
28961  return QRectF();
28962  }
28963 
28964  double keyPixel = keyAxis->coordToPixel(it->key);
28965  double highPixel = valueAxis->coordToPixel(it->high);
28966  double lowPixel = valueAxis->coordToPixel(it->low);
28967  double keyWidthPixels =
28968  keyPixel - keyAxis->coordToPixel(it->key - mWidth * 0.5);
28969  if (keyAxis->orientation() == Qt::Horizontal)
28970  return QRectF(keyPixel - keyWidthPixels, highPixel, keyWidthPixels * 2,
28971  lowPixel - highPixel)
28972  .normalized();
28973  else
28974  return QRectF(highPixel, keyPixel - keyWidthPixels,
28975  lowPixel - highPixel, keyWidthPixels * 2)
28976  .normalized();
28977 }
28978 /* end of 'src/plottables/plottable-financial.cpp' */
28979 
28980 /* including file 'src/plottables/plottable-errorbar.cpp', size 37570 */
28981 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
28982 
28986 
29005 QCPErrorBarsData::QCPErrorBarsData() : errorMinus(0), errorPlus(0) {}
29006 
29012  : errorMinus(error), errorPlus(error) {}
29013 
29018 QCPErrorBarsData::QCPErrorBarsData(double errorMinus, double errorPlus)
29019  : errorMinus(errorMinus), errorPlus(errorPlus) {}
29020 
29024 
29059 /* start of documentation of inline functions */
29060 
29069 /* end of documentation of inline functions */
29070 
29088  : QCPAbstractPlottable(keyAxis, valueAxis),
29089  mDataContainer(new QVector<QCPErrorBarsData>),
29090  mErrorType(etValueError),
29091  mWhiskerWidth(9),
29092  mSymbolGap(10) {
29093  setPen(QPen(Qt::black, 0));
29094  setBrush(Qt::NoBrush);
29095 }
29096 
29098 
29119 void QCPErrorBars::setData(QSharedPointer<QCPErrorBarsDataContainer> data) {
29120  mDataContainer = data;
29121 }
29122 
29133 void QCPErrorBars::setData(const QVector<double> &error) {
29134  mDataContainer->clear();
29135  addData(error);
29136 }
29137 
29148 void QCPErrorBars::setData(const QVector<double> &errorMinus,
29149  const QVector<double> &errorPlus) {
29150  mDataContainer->clear();
29151  addData(errorMinus, errorPlus);
29152 }
29153 
29171  if (plottable && qobject_cast<QCPErrorBars *>(plottable)) {
29172  mDataPlottable = 0;
29173  qDebug() << Q_FUNC_INFO
29174  << "can't set another QCPErrorBars instance as data plottable";
29175  return;
29176  }
29177  if (plottable && !plottable->interface1D()) {
29178  mDataPlottable = 0;
29179  qDebug() << Q_FUNC_INFO
29180  << "passed plottable doesn't implement 1d interface, can't "
29181  "associate with QCPErrorBars";
29182  return;
29183  }
29184 
29185  mDataPlottable = plottable;
29186 }
29187 
29194 
29199 void QCPErrorBars::setWhiskerWidth(double pixels) { mWhiskerWidth = pixels; }
29200 
29206 void QCPErrorBars::setSymbolGap(double pixels) { mSymbolGap = pixels; }
29207 
29218 void QCPErrorBars::addData(const QVector<double> &error) {
29219  addData(error, error);
29220 }
29221 
29232 void QCPErrorBars::addData(const QVector<double> &errorMinus,
29233  const QVector<double> &errorPlus) {
29234  if (errorMinus.size() != errorPlus.size())
29235  qDebug() << Q_FUNC_INFO
29236  << "minus and plus error vectors have different sizes:"
29237  << errorMinus.size() << errorPlus.size();
29238  const int n = qMin(errorMinus.size(), errorPlus.size());
29239  mDataContainer->reserve(n);
29240  for (int i = 0; i < n; ++i)
29241  mDataContainer->append(
29242  QCPErrorBarsData(errorMinus.at(i), errorPlus.at(i)));
29243 }
29244 
29255 void QCPErrorBars::addData(double error) {
29256  mDataContainer->append(QCPErrorBarsData(error));
29257 }
29258 
29269 void QCPErrorBars::addData(double errorMinus, double errorPlus) {
29270  mDataContainer->append(QCPErrorBarsData(errorMinus, errorPlus));
29271 }
29272 
29273 /* inherits documentation from base class */
29274 int QCPErrorBars::dataCount() const { return mDataContainer->size(); }
29275 
29276 /* inherits documentation from base class */
29277 double QCPErrorBars::dataMainKey(int index) const {
29278  if (mDataPlottable)
29279  return mDataPlottable->interface1D()->dataMainKey(index);
29280  else
29281  qDebug() << Q_FUNC_INFO << "no data plottable set";
29282  return 0;
29283 }
29284 
29285 /* inherits documentation from base class */
29286 double QCPErrorBars::dataSortKey(int index) const {
29287  if (mDataPlottable)
29288  return mDataPlottable->interface1D()->dataSortKey(index);
29289  else
29290  qDebug() << Q_FUNC_INFO << "no data plottable set";
29291  return 0;
29292 }
29293 
29294 /* inherits documentation from base class */
29295 double QCPErrorBars::dataMainValue(int index) const {
29296  if (mDataPlottable)
29297  return mDataPlottable->interface1D()->dataMainValue(index);
29298  else
29299  qDebug() << Q_FUNC_INFO << "no data plottable set";
29300  return 0;
29301 }
29302 
29303 /* inherits documentation from base class */
29305  if (mDataPlottable) {
29306  const double value =
29307  mDataPlottable->interface1D()->dataMainValue(index);
29308  if (index >= 0 && index < mDataContainer->size() &&
29310  return QCPRange(value - mDataContainer->at(index).errorMinus,
29311  value + mDataContainer->at(index).errorPlus);
29312  else
29313  return QCPRange(value, value);
29314  } else {
29315  qDebug() << Q_FUNC_INFO << "no data plottable set";
29316  return QCPRange();
29317  }
29318 }
29319 
29320 /* inherits documentation from base class */
29321 QPointF QCPErrorBars::dataPixelPosition(int index) const {
29322  if (mDataPlottable)
29323  return mDataPlottable->interface1D()->dataPixelPosition(index);
29324  else
29325  qDebug() << Q_FUNC_INFO << "no data plottable set";
29326  return QPointF();
29327 }
29328 
29329 /* inherits documentation from base class */
29331  if (mDataPlottable) {
29332  return mDataPlottable->interface1D()->sortKeyIsMainKey();
29333  } else {
29334  qDebug() << Q_FUNC_INFO << "no data plottable set";
29335  return true;
29336  }
29337 }
29338 
29343  bool onlySelectable) const {
29345  if (!mDataPlottable) return result;
29346  if ((onlySelectable && mSelectable == QCP::stNone) ||
29347  mDataContainer->isEmpty())
29348  return result;
29349  if (!mKeyAxis || !mValueAxis) return result;
29350 
29351  QCPErrorBarsDataContainer::const_iterator visibleBegin, visibleEnd;
29352  getVisibleDataBounds(visibleBegin, visibleEnd,
29353  QCPDataRange(0, dataCount()));
29354 
29355  QVector<QLineF> backbones, whiskers;
29356  for (QCPErrorBarsDataContainer::const_iterator it = visibleBegin;
29357  it != visibleEnd; ++it) {
29358  backbones.clear();
29359  whiskers.clear();
29360  getErrorBarLines(it, backbones, whiskers);
29361  for (int i = 0; i < backbones.size(); ++i) {
29362  if (rectIntersectsLine(rect, backbones.at(i))) {
29363  result.addDataRange(
29364  QCPDataRange(it - mDataContainer->constBegin(),
29365  it - mDataContainer->constBegin() + 1),
29366  false);
29367  break;
29368  }
29369  }
29370  }
29371  result.simplify();
29372  return result;
29373 }
29374 
29375 /* inherits documentation from base class */
29376 int QCPErrorBars::findBegin(double sortKey, bool expandedRange) const {
29377  if (mDataPlottable) {
29378  if (mDataContainer->isEmpty()) return 0;
29379  int beginIndex = mDataPlottable->interface1D()->findBegin(
29380  sortKey, expandedRange);
29381  if (beginIndex >= mDataContainer->size())
29382  beginIndex = mDataContainer->size() - 1;
29383  return beginIndex;
29384  } else
29385  qDebug() << Q_FUNC_INFO << "no data plottable set";
29386  return 0;
29387 }
29388 
29389 /* inherits documentation from base class */
29390 int QCPErrorBars::findEnd(double sortKey, bool expandedRange) const {
29391  if (mDataPlottable) {
29392  if (mDataContainer->isEmpty()) return 0;
29393  int endIndex =
29394  mDataPlottable->interface1D()->findEnd(sortKey, expandedRange);
29395  if (endIndex > mDataContainer->size())
29396  endIndex = mDataContainer->size();
29397  return endIndex;
29398  } else
29399  qDebug() << Q_FUNC_INFO << "no data plottable set";
29400  return 0;
29401 }
29402 
29411 double QCPErrorBars::selectTest(const QPointF &pos,
29412  bool onlySelectable,
29413  QVariant *details) const {
29414  if (!mDataPlottable) return -1;
29415 
29416  if ((onlySelectable && mSelectable == QCP::stNone) ||
29417  mDataContainer->isEmpty())
29418  return -1;
29419  if (!mKeyAxis || !mValueAxis) return -1;
29420 
29421  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) {
29422  QCPErrorBarsDataContainer::const_iterator closestDataPoint =
29423  mDataContainer->constEnd();
29424  double result = pointDistance(pos, closestDataPoint);
29425  if (details) {
29426  int pointIndex = closestDataPoint - mDataContainer->constBegin();
29427  details->setValue(
29428  QCPDataSelection(QCPDataRange(pointIndex, pointIndex + 1)));
29429  }
29430  return result;
29431  } else
29432  return -1;
29433 }
29434 
29435 /* inherits documentation from base class */
29437  if (!mDataPlottable) return;
29438  if (!mKeyAxis || !mValueAxis) {
29439  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
29440  return;
29441  }
29442  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty())
29443  return;
29444 
29445  // if the sort key isn't the main key, we must check the visibility for each
29446  // data point/error bar individually (getVisibleDataBounds applies range
29447  // restriction, but otherwise can only return full data range):
29448  bool checkPointVisibility =
29449  !mDataPlottable->interface1D()->sortKeyIsMainKey();
29450 
29451  // check data validity if flag set:
29452 #ifdef QCUSTOMPLOT_CHECK_DATA
29453  QCPErrorBarsDataContainer::const_iterator it;
29454  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd();
29455  ++it) {
29456  if (QCP::isInvalidData(it->errorMinus, it->errorPlus))
29457  qDebug() << Q_FUNC_INFO << "Data point at index"
29458  << it - mDataContainer->constBegin() << "invalid."
29459  << "Plottable name:" << name();
29460  }
29461 #endif
29462 
29464  painter->setBrush(Qt::NoBrush);
29465  // loop over and draw segments of unselected/selected data:
29466  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
29467  getDataSegments(selectedSegments, unselectedSegments);
29468  allSegments << unselectedSegments << selectedSegments;
29469  QVector<QLineF> backbones, whiskers;
29470  for (int i = 0; i < allSegments.size(); ++i) {
29471  QCPErrorBarsDataContainer::const_iterator begin, end;
29472  getVisibleDataBounds(begin, end, allSegments.at(i));
29473  if (begin == end) continue;
29474 
29475  bool isSelectedSegment = i >= unselectedSegments.size();
29476  if (isSelectedSegment && mSelectionDecorator)
29477  mSelectionDecorator->applyPen(painter);
29478  else
29479  painter->setPen(mPen);
29480  if (painter->pen().capStyle() == Qt::SquareCap) {
29481  QPen capFixPen(painter->pen());
29482  capFixPen.setCapStyle(Qt::FlatCap);
29483  painter->setPen(capFixPen);
29484  }
29485  backbones.clear();
29486  whiskers.clear();
29487  for (QCPErrorBarsDataContainer::const_iterator it = begin; it != end;
29488  ++it) {
29489  if (!checkPointVisibility ||
29490  errorBarVisible(it - mDataContainer->constBegin()))
29491  getErrorBarLines(it, backbones, whiskers);
29492  }
29493  painter->drawLines(backbones);
29494  painter->drawLines(whiskers);
29495  }
29496 
29497  // draw other selection decoration that isn't just line/scatter pens and
29498  // brushes:
29499  if (mSelectionDecorator)
29501 }
29502 
29503 /* inherits documentation from base class */
29505  const QRectF &rect) const {
29507  painter->setPen(mPen);
29508  if (mErrorType == etValueError && mValueAxis &&
29509  mValueAxis->orientation() == Qt::Vertical) {
29510  painter->drawLine(QLineF(rect.center().x(), rect.top() + 2,
29511  rect.center().x(), rect.bottom() - 1));
29512  painter->drawLine(QLineF(rect.center().x() - 4, rect.top() + 2,
29513  rect.center().x() + 4, rect.top() + 2));
29514  painter->drawLine(QLineF(rect.center().x() - 4, rect.bottom() - 1,
29515  rect.center().x() + 4, rect.bottom() - 1));
29516  } else {
29517  painter->drawLine(QLineF(rect.left() + 2, rect.center().y(),
29518  rect.right() - 2, rect.center().y()));
29519  painter->drawLine(QLineF(rect.left() + 2, rect.center().y() - 4,
29520  rect.left() + 2, rect.center().y() + 4));
29521  painter->drawLine(QLineF(rect.right() - 2, rect.center().y() - 4,
29522  rect.right() - 2, rect.center().y() + 4));
29523  }
29524 }
29525 
29526 /* inherits documentation from base class */
29528  QCP::SignDomain inSignDomain) const {
29529  if (!mDataPlottable) {
29530  foundRange = false;
29531  return QCPRange();
29532  }
29533 
29534  QCPRange range;
29535  bool haveLower = false;
29536  bool haveUpper = false;
29537  QCPErrorBarsDataContainer::const_iterator it;
29538  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd();
29539  ++it) {
29540  if (mErrorType == etValueError) {
29541  // error bar doesn't extend in key dimension (except whisker but we
29542  // ignore that here), so only use data point center
29543  const double current = mDataPlottable->interface1D()->dataMainKey(
29544  it - mDataContainer->constBegin());
29545  if (qIsNaN(current)) continue;
29546  if (inSignDomain == QCP::sdBoth ||
29547  (inSignDomain == QCP::sdNegative && current < 0) ||
29548  (inSignDomain == QCP::sdPositive && current > 0)) {
29549  if (current < range.lower || !haveLower) {
29550  range.lower = current;
29551  haveLower = true;
29552  }
29553  if (current > range.upper || !haveUpper) {
29554  range.upper = current;
29555  haveUpper = true;
29556  }
29557  }
29558  } else // mErrorType == etKeyError
29559  {
29560  const double dataKey = mDataPlottable->interface1D()->dataMainKey(
29561  it - mDataContainer->constBegin());
29562  if (qIsNaN(dataKey)) continue;
29563  // plus error:
29564  double current =
29565  dataKey + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
29566  if (inSignDomain == QCP::sdBoth ||
29567  (inSignDomain == QCP::sdNegative && current < 0) ||
29568  (inSignDomain == QCP::sdPositive && current > 0)) {
29569  if (current > range.upper || !haveUpper) {
29570  range.upper = current;
29571  haveUpper = true;
29572  }
29573  }
29574  // minus error:
29575  current = dataKey - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
29576  if (inSignDomain == QCP::sdBoth ||
29577  (inSignDomain == QCP::sdNegative && current < 0) ||
29578  (inSignDomain == QCP::sdPositive && current > 0)) {
29579  if (current < range.lower || !haveLower) {
29580  range.lower = current;
29581  haveLower = true;
29582  }
29583  }
29584  }
29585  }
29586 
29587  if (haveUpper && !haveLower) {
29588  range.lower = range.upper;
29589  haveLower = true;
29590  } else if (haveLower && !haveUpper) {
29591  range.upper = range.lower;
29592  haveUpper = true;
29593  }
29594 
29595  foundRange = haveLower && haveUpper;
29596  return range;
29597 }
29598 
29599 /* inherits documentation from base class */
29601  QCP::SignDomain inSignDomain,
29602  const QCPRange &inKeyRange) const {
29603  if (!mDataPlottable) {
29604  foundRange = false;
29605  return QCPRange();
29606  }
29607 
29608  QCPRange range;
29609  const bool restrictKeyRange = inKeyRange != QCPRange();
29610  bool haveLower = false;
29611  bool haveUpper = false;
29612  QCPErrorBarsDataContainer::const_iterator itBegin =
29613  mDataContainer->constBegin();
29614  QCPErrorBarsDataContainer::const_iterator itEnd =
29615  mDataContainer->constEnd();
29616  if (mDataPlottable->interface1D()->sortKeyIsMainKey() && restrictKeyRange) {
29617  itBegin = mDataContainer->constBegin() + findBegin(inKeyRange.lower);
29618  itEnd = mDataContainer->constBegin() + findEnd(inKeyRange.upper);
29619  }
29620  for (QCPErrorBarsDataContainer::const_iterator it = itBegin; it != itEnd;
29621  ++it) {
29622  if (restrictKeyRange) {
29623  const double dataKey = mDataPlottable->interface1D()->dataMainKey(
29624  it - mDataContainer->constBegin());
29625  if (dataKey < inKeyRange.lower || dataKey > inKeyRange.upper)
29626  continue;
29627  }
29628  if (mErrorType == etValueError) {
29629  const double dataValue =
29630  mDataPlottable->interface1D()->dataMainValue(
29631  it - mDataContainer->constBegin());
29632  if (qIsNaN(dataValue)) continue;
29633  // plus error:
29634  double current =
29635  dataValue + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
29636  if (inSignDomain == QCP::sdBoth ||
29637  (inSignDomain == QCP::sdNegative && current < 0) ||
29638  (inSignDomain == QCP::sdPositive && current > 0)) {
29639  if (current > range.upper || !haveUpper) {
29640  range.upper = current;
29641  haveUpper = true;
29642  }
29643  }
29644  // minus error:
29645  current = dataValue - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
29646  if (inSignDomain == QCP::sdBoth ||
29647  (inSignDomain == QCP::sdNegative && current < 0) ||
29648  (inSignDomain == QCP::sdPositive && current > 0)) {
29649  if (current < range.lower || !haveLower) {
29650  range.lower = current;
29651  haveLower = true;
29652  }
29653  }
29654  } else // mErrorType == etKeyError
29655  {
29656  // error bar doesn't extend in value dimension (except whisker but
29657  // we ignore that here), so only use data point center
29658  const double current = mDataPlottable->interface1D()->dataMainValue(
29659  it - mDataContainer->constBegin());
29660  if (qIsNaN(current)) continue;
29661  if (inSignDomain == QCP::sdBoth ||
29662  (inSignDomain == QCP::sdNegative && current < 0) ||
29663  (inSignDomain == QCP::sdPositive && current > 0)) {
29664  if (current < range.lower || !haveLower) {
29665  range.lower = current;
29666  haveLower = true;
29667  }
29668  if (current > range.upper || !haveUpper) {
29669  range.upper = current;
29670  haveUpper = true;
29671  }
29672  }
29673  }
29674  }
29675 
29676  if (haveUpper && !haveLower) {
29677  range.lower = range.upper;
29678  haveLower = true;
29679  } else if (haveLower && !haveUpper) {
29680  range.upper = range.lower;
29681  haveUpper = true;
29682  }
29683 
29684  foundRange = haveLower && haveUpper;
29685  return range;
29686 }
29687 
29702  QCPErrorBarsDataContainer::const_iterator it,
29703  QVector<QLineF> &backbones,
29704  QVector<QLineF> &whiskers) const {
29705  if (!mDataPlottable) return;
29706 
29707  int index = it - mDataContainer->constBegin();
29708  QPointF centerPixel =
29709  mDataPlottable->interface1D()->dataPixelPosition(index);
29710  if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y())) return;
29711  QCPAxis *errorAxis =
29712  mErrorType == etValueError ? mValueAxis.data() : mKeyAxis.data();
29713  QCPAxis *orthoAxis =
29714  mErrorType == etValueError ? mKeyAxis.data() : mValueAxis.data();
29715  const double centerErrorAxisPixel =
29716  errorAxis->orientation() == Qt::Horizontal ? centerPixel.x()
29717  : centerPixel.y();
29718  const double centerOrthoAxisPixel =
29719  orthoAxis->orientation() == Qt::Horizontal ? centerPixel.x()
29720  : centerPixel.y();
29721  const double centerErrorAxisCoord = errorAxis->pixelToCoord(
29722  centerErrorAxisPixel); // depending on plottable, this might be
29723  // different from just
29724  // mDataPlottable->interface1D()->dataMainKey/Value
29725  const double symbolGap = mSymbolGap * 0.5 * errorAxis->pixelOrientation();
29726  // plus error:
29727  double errorStart, errorEnd;
29728  if (!qIsNaN(it->errorPlus)) {
29729  errorStart = centerErrorAxisPixel + symbolGap;
29730  errorEnd =
29731  errorAxis->coordToPixel(centerErrorAxisCoord + it->errorPlus);
29732  if (errorAxis->orientation() == Qt::Vertical) {
29733  if ((errorStart > errorEnd) != errorAxis->rangeReversed())
29734  backbones.append(QLineF(centerOrthoAxisPixel, errorStart,
29735  centerOrthoAxisPixel, errorEnd));
29736  whiskers.append(QLineF(
29737  centerOrthoAxisPixel - mWhiskerWidth * 0.5, errorEnd,
29738  centerOrthoAxisPixel + mWhiskerWidth * 0.5, errorEnd));
29739  } else {
29740  if ((errorStart < errorEnd) != errorAxis->rangeReversed())
29741  backbones.append(QLineF(errorStart, centerOrthoAxisPixel,
29742  errorEnd, centerOrthoAxisPixel));
29743  whiskers.append(QLineF(
29744  errorEnd, centerOrthoAxisPixel - mWhiskerWidth * 0.5,
29745  errorEnd, centerOrthoAxisPixel + mWhiskerWidth * 0.5));
29746  }
29747  }
29748  // minus error:
29749  if (!qIsNaN(it->errorMinus)) {
29750  errorStart = centerErrorAxisPixel - symbolGap;
29751  errorEnd =
29752  errorAxis->coordToPixel(centerErrorAxisCoord - it->errorMinus);
29753  if (errorAxis->orientation() == Qt::Vertical) {
29754  if ((errorStart < errorEnd) != errorAxis->rangeReversed())
29755  backbones.append(QLineF(centerOrthoAxisPixel, errorStart,
29756  centerOrthoAxisPixel, errorEnd));
29757  whiskers.append(QLineF(
29758  centerOrthoAxisPixel - mWhiskerWidth * 0.5, errorEnd,
29759  centerOrthoAxisPixel + mWhiskerWidth * 0.5, errorEnd));
29760  } else {
29761  if ((errorStart > errorEnd) != errorAxis->rangeReversed())
29762  backbones.append(QLineF(errorStart, centerOrthoAxisPixel,
29763  errorEnd, centerOrthoAxisPixel));
29764  whiskers.append(QLineF(
29765  errorEnd, centerOrthoAxisPixel - mWhiskerWidth * 0.5,
29766  errorEnd, centerOrthoAxisPixel + mWhiskerWidth * 0.5));
29767  }
29768  }
29769 }
29770 
29793  QCPErrorBarsDataContainer::const_iterator &begin,
29794  QCPErrorBarsDataContainer::const_iterator &end,
29795  const QCPDataRange &rangeRestriction) const {
29796  QCPAxis *keyAxis = mKeyAxis.data();
29797  QCPAxis *valueAxis = mValueAxis.data();
29798  if (!keyAxis || !valueAxis) {
29799  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
29800  end = mDataContainer->constEnd();
29801  begin = end;
29802  return;
29803  }
29804  if (!mDataPlottable || rangeRestriction.isEmpty()) {
29805  end = mDataContainer->constEnd();
29806  begin = end;
29807  return;
29808  }
29809  if (!mDataPlottable->interface1D()->sortKeyIsMainKey()) {
29810  // if the sort key isn't the main key, it's not possible to find a
29811  // contiguous range of visible data points, so this method then only
29812  // applies the range restriction and otherwise returns the full data
29813  // range. Visibility checks must be done on a per-datapoin-basis during
29814  // drawing
29815  QCPDataRange dataRange(0, mDataContainer->size());
29816  dataRange = dataRange.bounded(rangeRestriction);
29817  begin = mDataContainer->constBegin() + dataRange.begin();
29818  end = mDataContainer->constBegin() + dataRange.end();
29819  return;
29820  }
29821 
29822  // get visible data range via interface from data plottable, and then
29823  // restrict to available error data points:
29824  const int n = qMin(mDataContainer->size(),
29825  mDataPlottable->interface1D()->dataCount());
29826  int beginIndex =
29827  mDataPlottable->interface1D()->findBegin(keyAxis->range().lower);
29828  int endIndex =
29829  mDataPlottable->interface1D()->findEnd(keyAxis->range().upper);
29830  int i = beginIndex;
29831  while (i > 0 && i < n && i > rangeRestriction.begin()) {
29832  if (errorBarVisible(i)) beginIndex = i;
29833  --i;
29834  }
29835  i = endIndex;
29836  while (i >= 0 && i < n && i < rangeRestriction.end()) {
29837  if (errorBarVisible(i)) endIndex = i + 1;
29838  ++i;
29839  }
29840  QCPDataRange dataRange(beginIndex, endIndex);
29841  dataRange = dataRange.bounded(
29842  rangeRestriction.bounded(QCPDataRange(0, mDataContainer->size())));
29843  begin = mDataContainer->constBegin() + dataRange.begin();
29844  end = mDataContainer->constBegin() + dataRange.end();
29845 }
29846 
29855  const QPointF &pixelPoint,
29856  QCPErrorBarsDataContainer::const_iterator &closestData) const {
29857  closestData = mDataContainer->constEnd();
29858  if (!mDataPlottable || mDataContainer->isEmpty()) return -1.0;
29859  if (!mKeyAxis || !mValueAxis) {
29860  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
29861  return -1.0;
29862  }
29863 
29864  QCPErrorBarsDataContainer::const_iterator begin, end;
29865  getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount()));
29866 
29867  // calculate minimum distances to error backbones (whiskers are ignored for
29868  // speed) and find closestData iterator:
29869  double minDistSqr = (std::numeric_limits<double>::max)();
29870  QVector<QLineF> backbones, whiskers;
29871  for (QCPErrorBarsDataContainer::const_iterator it = begin; it != end;
29872  ++it) {
29873  getErrorBarLines(it, backbones, whiskers);
29874  for (int i = 0; i < backbones.size(); ++i) {
29875  const double currentDistSqr =
29876  QCPVector2D(pixelPoint)
29877  .distanceSquaredToLine(backbones.at(i));
29878  if (currentDistSqr < minDistSqr) {
29879  minDistSqr = currentDistSqr;
29880  closestData = it;
29881  }
29882  }
29883  }
29884  return qSqrt(minDistSqr);
29885 }
29886 
29896  QList<QCPDataRange> &selectedSegments,
29897  QList<QCPDataRange> &unselectedSegments) const {
29898  selectedSegments.clear();
29899  unselectedSegments.clear();
29900  if (mSelectable ==
29901  QCP::stWhole) // stWhole selection type draws the entire plottable with
29902  // selected style if mSelection isn't empty
29903  {
29904  if (selected())
29905  selectedSegments << QCPDataRange(0, dataCount());
29906  else
29907  unselectedSegments << QCPDataRange(0, dataCount());
29908  } else {
29909  QCPDataSelection sel(selection());
29910  sel.simplify();
29911  selectedSegments = sel.dataRanges();
29912  unselectedSegments =
29914  }
29915 }
29916 
29927 bool QCPErrorBars::errorBarVisible(int index) const {
29928  QPointF centerPixel =
29929  mDataPlottable->interface1D()->dataPixelPosition(index);
29930  const double centerKeyPixel = mKeyAxis->orientation() == Qt::Horizontal
29931  ? centerPixel.x()
29932  : centerPixel.y();
29933  if (qIsNaN(centerKeyPixel)) return false;
29934 
29935  double keyMin, keyMax;
29936  if (mErrorType == etKeyError) {
29937  const double centerKey = mKeyAxis->pixelToCoord(centerKeyPixel);
29938  const double errorPlus = mDataContainer->at(index).errorPlus;
29939  const double errorMinus = mDataContainer->at(index).errorMinus;
29940  keyMax = centerKey + (qIsNaN(errorPlus) ? 0 : errorPlus);
29941  keyMin = centerKey - (qIsNaN(errorMinus) ? 0 : errorMinus);
29942  } else // mErrorType == etValueError
29943  {
29944  keyMax = mKeyAxis->pixelToCoord(centerKeyPixel +
29945  mWhiskerWidth * 0.5 *
29946  mKeyAxis->pixelOrientation());
29947  keyMin = mKeyAxis->pixelToCoord(centerKeyPixel -
29948  mWhiskerWidth * 0.5 *
29949  mKeyAxis->pixelOrientation());
29950  }
29951  return ((keyMax > mKeyAxis->range().lower) &&
29952  (keyMin < mKeyAxis->range().upper));
29953 }
29954 
29962 bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect,
29963  const QLineF &line) const {
29964  if (pixelRect.left() > line.x1() && pixelRect.left() > line.x2())
29965  return false;
29966  else if (pixelRect.right() < line.x1() && pixelRect.right() < line.x2())
29967  return false;
29968  else if (pixelRect.top() > line.y1() && pixelRect.top() > line.y2())
29969  return false;
29970  else if (pixelRect.bottom() < line.y1() && pixelRect.bottom() < line.y2())
29971  return false;
29972  else
29973  return true;
29974 }
29975 /* end of 'src/plottables/plottable-errorbar.cpp' */
29976 
29977 /* including file 'src/items/item-straightline.cpp', size 7592 */
29978 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
29979 
29983 
30001  : QCPAbstractItem(parentPlot),
30002  point1(createPosition(QLatin1String("point1"))),
30003  point2(createPosition(QLatin1String("point2"))) {
30004  point1->setCoords(0, 0);
30005  point2->setCoords(1, 1);
30006 
30007  setPen(QPen(Qt::black));
30008  setSelectedPen(QPen(Qt::blue, 2));
30009 }
30010 
30012 
30018 void QCPItemStraightLine::setPen(const QPen &pen) { mPen = pen; }
30019 
30026  mSelectedPen = pen;
30027 }
30028 
30029 /* inherits documentation from base class */
30030 double QCPItemStraightLine::selectTest(const QPointF &pos,
30031  bool onlySelectable,
30032  QVariant *details) const {
30033  Q_UNUSED(details)
30034  if (onlySelectable && !mSelectable) return -1;
30035 
30036  return QCPVector2D(pos).distanceToStraightLine(
30037  point1->pixelPosition(),
30039 }
30040 
30041 /* inherits documentation from base class */
30043  QCPVector2D start(point1->pixelPosition());
30045  // get visible segment of straight line inside clipRect:
30046  double clipPad = mainPen().widthF();
30047  QLineF line = getRectClippedStraightLine(
30048  start, end - start,
30049  clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
30050  // paint visible segment, if existent:
30051  if (!line.isNull()) {
30052  painter->setPen(mainPen());
30053  painter->drawLine(line);
30054  }
30055 }
30056 
30065  const QCPVector2D &base,
30066  const QCPVector2D &vec,
30067  const QRect &rect) const {
30068  double bx, by;
30069  double gamma;
30070  QLineF result;
30071  if (vec.x() == 0 && vec.y() == 0) return result;
30072  if (qFuzzyIsNull(vec.x())) // line is vertical
30073  {
30074  // check top of rect:
30075  bx = rect.left();
30076  by = rect.top();
30077  gamma = base.x() - bx + (by - base.y()) * vec.x() / vec.y();
30078  if (gamma >= 0 && gamma <= rect.width())
30079  result.setLine(bx + gamma, rect.top(), bx + gamma,
30080  rect.bottom()); // no need to check bottom because
30081  // we know line is vertical
30082  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
30083  {
30084  // check left of rect:
30085  bx = rect.left();
30086  by = rect.top();
30087  gamma = base.y() - by + (bx - base.x()) * vec.y() / vec.x();
30088  if (gamma >= 0 && gamma <= rect.height())
30089  result.setLine(rect.left(), by + gamma, rect.right(),
30090  by + gamma); // no need to check right because we
30091  // know line is horizontal
30092  } else // line is skewed
30093  {
30094  QList<QCPVector2D> pointVectors;
30095  // check top of rect:
30096  bx = rect.left();
30097  by = rect.top();
30098  gamma = base.x() - bx + (by - base.y()) * vec.x() / vec.y();
30099  if (gamma >= 0 && gamma <= rect.width())
30100  pointVectors.append(QCPVector2D(bx + gamma, by));
30101  // check bottom of rect:
30102  bx = rect.left();
30103  by = rect.bottom();
30104  gamma = base.x() - bx + (by - base.y()) * vec.x() / vec.y();
30105  if (gamma >= 0 && gamma <= rect.width())
30106  pointVectors.append(QCPVector2D(bx + gamma, by));
30107  // check left of rect:
30108  bx = rect.left();
30109  by = rect.top();
30110  gamma = base.y() - by + (bx - base.x()) * vec.y() / vec.x();
30111  if (gamma >= 0 && gamma <= rect.height())
30112  pointVectors.append(QCPVector2D(bx, by + gamma));
30113  // check right of rect:
30114  bx = rect.right();
30115  by = rect.top();
30116  gamma = base.y() - by + (bx - base.x()) * vec.y() / vec.x();
30117  if (gamma >= 0 && gamma <= rect.height())
30118  pointVectors.append(QCPVector2D(bx, by + gamma));
30119 
30120  // evaluate points:
30121  if (pointVectors.size() == 2) {
30122  result.setPoints(pointVectors.at(0).toPointF(),
30123  pointVectors.at(1).toPointF());
30124  } else if (pointVectors.size() > 2) {
30125  // line probably goes through corner of rect, and we got two points
30126  // there. single out the point pair with greatest distance:
30127  double distSqrMax = 0;
30128  QCPVector2D pv1, pv2;
30129  for (int i = 0; i < pointVectors.size() - 1; ++i) {
30130  for (int k = i + 1; k < pointVectors.size(); ++k) {
30131  double distSqr = (pointVectors.at(i) - pointVectors.at(k))
30132  .lengthSquared();
30133  if (distSqr > distSqrMax) {
30134  pv1 = pointVectors.at(i);
30135  pv2 = pointVectors.at(k);
30136  distSqrMax = distSqr;
30137  }
30138  }
30139  }
30140  result.setPoints(pv1.toPointF(), pv2.toPointF());
30141  }
30142  }
30143  return result;
30144 }
30145 
30152  return mSelected ? mSelectedPen : mPen;
30153 }
30154 /* end of 'src/items/item-straightline.cpp' */
30155 
30156 /* including file 'src/items/item-line.cpp', size 8498 */
30157 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
30158 
30162 
30184  : QCPAbstractItem(parentPlot),
30185  start(createPosition(QLatin1String("start"))),
30186  end(createPosition(QLatin1String("end"))) {
30187  start->setCoords(0, 0);
30188  end->setCoords(1, 1);
30189 
30190  setPen(QPen(Qt::black));
30191  setSelectedPen(QPen(Qt::blue, 2));
30192 }
30193 
30195 
30201 void QCPItemLine::setPen(const QPen &pen) { mPen = pen; }
30202 
30208 void QCPItemLine::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
30209 
30221 
30233 
30234 /* inherits documentation from base class */
30235 double QCPItemLine::selectTest(const QPointF &pos,
30236  bool onlySelectable,
30237  QVariant *details) const {
30238  Q_UNUSED(details)
30239  if (onlySelectable && !mSelectable) return -1;
30240 
30241  return qSqrt(QCPVector2D(pos).distanceSquaredToLine(start->pixelPosition(),
30242  end->pixelPosition()));
30243 }
30244 
30245 /* inherits documentation from base class */
30247  QCPVector2D startVec(start->pixelPosition());
30248  QCPVector2D endVec(end->pixelPosition());
30249  if (qFuzzyIsNull((startVec - endVec).lengthSquared())) return;
30250  // get visible segment of straight line inside clipRect:
30251  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
30252  clipPad = qMax(clipPad, (double)mainPen().widthF());
30253  QLineF line = getRectClippedLine(
30254  startVec, endVec,
30255  clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
30256  // paint visible segment, if existent:
30257  if (!line.isNull()) {
30258  painter->setPen(mainPen());
30259  painter->drawLine(line);
30260  painter->setBrush(Qt::SolidPattern);
30262  mTail.draw(painter, startVec, startVec - endVec);
30264  mHead.draw(painter, endVec, endVec - startVec);
30265  }
30266 }
30267 
30276  const QCPVector2D &end,
30277  const QRect &rect) const {
30278  bool containsStart = rect.contains(start.x(), start.y());
30279  bool containsEnd = rect.contains(end.x(), end.y());
30280  if (containsStart && containsEnd)
30281  return QLineF(start.toPointF(), end.toPointF());
30282 
30283  QCPVector2D base = start;
30284  QCPVector2D vec = end - start;
30285  double bx, by;
30286  double gamma, mu;
30287  QLineF result;
30288  QList<QCPVector2D> pointVectors;
30289 
30290  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
30291  {
30292  // check top of rect:
30293  bx = rect.left();
30294  by = rect.top();
30295  mu = (by - base.y()) / vec.y();
30296  if (mu >= 0 && mu <= 1) {
30297  gamma = base.x() - bx + mu * vec.x();
30298  if (gamma >= 0 && gamma <= rect.width())
30299  pointVectors.append(QCPVector2D(bx + gamma, by));
30300  }
30301  // check bottom of rect:
30302  bx = rect.left();
30303  by = rect.bottom();
30304  mu = (by - base.y()) / vec.y();
30305  if (mu >= 0 && mu <= 1) {
30306  gamma = base.x() - bx + mu * vec.x();
30307  if (gamma >= 0 && gamma <= rect.width())
30308  pointVectors.append(QCPVector2D(bx + gamma, by));
30309  }
30310  }
30311  if (!qFuzzyIsNull(vec.x())) // line is not vertical
30312  {
30313  // check left of rect:
30314  bx = rect.left();
30315  by = rect.top();
30316  mu = (bx - base.x()) / vec.x();
30317  if (mu >= 0 && mu <= 1) {
30318  gamma = base.y() - by + mu * vec.y();
30319  if (gamma >= 0 && gamma <= rect.height())
30320  pointVectors.append(QCPVector2D(bx, by + gamma));
30321  }
30322  // check right of rect:
30323  bx = rect.right();
30324  by = rect.top();
30325  mu = (bx - base.x()) / vec.x();
30326  if (mu >= 0 && mu <= 1) {
30327  gamma = base.y() - by + mu * vec.y();
30328  if (gamma >= 0 && gamma <= rect.height())
30329  pointVectors.append(QCPVector2D(bx, by + gamma));
30330  }
30331  }
30332 
30333  if (containsStart) pointVectors.append(start);
30334  if (containsEnd) pointVectors.append(end);
30335 
30336  // evaluate points:
30337  if (pointVectors.size() == 2) {
30338  result.setPoints(pointVectors.at(0).toPointF(),
30339  pointVectors.at(1).toPointF());
30340  } else if (pointVectors.size() > 2) {
30341  // line probably goes through corner of rect, and we got two points
30342  // there. single out the point pair with greatest distance:
30343  double distSqrMax = 0;
30344  QCPVector2D pv1, pv2;
30345  for (int i = 0; i < pointVectors.size() - 1; ++i) {
30346  for (int k = i + 1; k < pointVectors.size(); ++k) {
30347  double distSqr = (pointVectors.at(i) - pointVectors.at(k))
30348  .lengthSquared();
30349  if (distSqr > distSqrMax) {
30350  pv1 = pointVectors.at(i);
30351  pv2 = pointVectors.at(k);
30352  distSqrMax = distSqr;
30353  }
30354  }
30355  }
30356  result.setPoints(pv1.toPointF(), pv2.toPointF());
30357  }
30358  return result;
30359 }
30360 
30366 QPen QCPItemLine::mainPen() const { return mSelected ? mSelectedPen : mPen; }
30367 /* end of 'src/items/item-line.cpp' */
30368 
30369 /* including file 'src/items/item-curve.cpp', size 7248 */
30370 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
30371 
30375 
30404  : QCPAbstractItem(parentPlot),
30405  start(createPosition(QLatin1String("start"))),
30406  startDir(createPosition(QLatin1String("startDir"))),
30407  endDir(createPosition(QLatin1String("endDir"))),
30408  end(createPosition(QLatin1String("end"))) {
30409  start->setCoords(0, 0);
30410  startDir->setCoords(0.5, 0);
30411  endDir->setCoords(0, 0.5);
30412  end->setCoords(1, 1);
30413 
30414  setPen(QPen(Qt::black));
30415  setSelectedPen(QPen(Qt::blue, 2));
30416 }
30417 
30419 
30425 void QCPItemCurve::setPen(const QPen &pen) { mPen = pen; }
30426 
30432 void QCPItemCurve::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
30433 
30445 
30457 
30458 /* inherits documentation from base class */
30459 double QCPItemCurve::selectTest(const QPointF &pos,
30460  bool onlySelectable,
30461  QVariant *details) const {
30462  Q_UNUSED(details)
30463  if (onlySelectable && !mSelectable) return -1;
30464 
30465  QPointF startVec(start->pixelPosition());
30466  QPointF startDirVec(startDir->pixelPosition());
30467  QPointF endDirVec(endDir->pixelPosition());
30468  QPointF endVec(end->pixelPosition());
30469 
30470  QPainterPath cubicPath(startVec);
30471  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
30472 
30473  QList<QPolygonF> polygons = cubicPath.toSubpathPolygons();
30474  if (polygons.isEmpty()) return -1;
30475  const QPolygonF polygon = polygons.first();
30476  QCPVector2D p(pos);
30477  double minDistSqr = (std::numeric_limits<double>::max)();
30478  for (int i = 1; i < polygon.size(); ++i) {
30479  double distSqr =
30480  p.distanceSquaredToLine(polygon.at(i - 1), polygon.at(i));
30481  if (distSqr < minDistSqr) minDistSqr = distSqr;
30482  }
30483  return qSqrt(minDistSqr);
30484 }
30485 
30486 /* inherits documentation from base class */
30488  QCPVector2D startVec(start->pixelPosition());
30489  QCPVector2D startDirVec(startDir->pixelPosition());
30490  QCPVector2D endDirVec(endDir->pixelPosition());
30491  QCPVector2D endVec(end->pixelPosition());
30492  if ((endVec - startVec).length() > 1e10) // too large curves cause crash
30493  return;
30494 
30495  QPainterPath cubicPath(startVec.toPointF());
30496  cubicPath.cubicTo(startDirVec.toPointF(), endDirVec.toPointF(),
30497  endVec.toPointF());
30498 
30499  // paint visible segment, if existent:
30500  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(),
30501  mainPen().widthF(), mainPen().widthF());
30502  QRect cubicRect = cubicPath.controlPointRect().toRect();
30503  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x
30504  // or y position
30505  cubicRect.adjust(0, 0, 1, 1);
30506  if (clip.intersects(cubicRect)) {
30507  painter->setPen(mainPen());
30508  painter->drawPath(cubicPath);
30509  painter->setBrush(Qt::SolidPattern);
30511  mTail.draw(painter, startVec,
30512  M_PI - cubicPath.angleAtPercent(0) / 180.0 * M_PI);
30514  mHead.draw(painter, endVec,
30515  -cubicPath.angleAtPercent(1) / 180.0 * M_PI);
30516  }
30517 }
30518 
30524 QPen QCPItemCurve::mainPen() const { return mSelected ? mSelectedPen : mPen; }
30525 /* end of 'src/items/item-curve.cpp' */
30526 
30527 /* including file 'src/items/item-rect.cpp', size 6479 */
30528 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
30529 
30533 
30552  : QCPAbstractItem(parentPlot),
30553  topLeft(createPosition(QLatin1String("topLeft"))),
30554  bottomRight(createPosition(QLatin1String("bottomRight"))),
30555  top(createAnchor(QLatin1String("top"), aiTop)),
30556  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
30557  right(createAnchor(QLatin1String("right"), aiRight)),
30558  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
30559  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
30560  left(createAnchor(QLatin1String("left"), aiLeft)) {
30561  topLeft->setCoords(0, 1);
30562  bottomRight->setCoords(1, 0);
30563 
30564  setPen(QPen(Qt::black));
30565  setSelectedPen(QPen(Qt::blue, 2));
30566  setBrush(Qt::NoBrush);
30567  setSelectedBrush(Qt::NoBrush);
30568 }
30569 
30571 
30577 void QCPItemRect::setPen(const QPen &pen) { mPen = pen; }
30578 
30584 void QCPItemRect::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
30585 
30592 void QCPItemRect::setBrush(const QBrush &brush) { mBrush = brush; }
30593 
30600 void QCPItemRect::setSelectedBrush(const QBrush &brush) {
30602 }
30603 
30604 /* inherits documentation from base class */
30605 double QCPItemRect::selectTest(const QPointF &pos,
30606  bool onlySelectable,
30607  QVariant *details) const {
30608  Q_UNUSED(details)
30609  if (onlySelectable && !mSelectable) return -1;
30610 
30611  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition())
30612  .normalized();
30613  bool filledRect =
30614  mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
30615  return rectDistance(rect, pos, filledRect);
30616 }
30617 
30618 /* inherits documentation from base class */
30620  QPointF p1 = topLeft->pixelPosition();
30621  QPointF p2 = bottomRight->pixelPosition();
30622  if (p1.toPoint() == p2.toPoint()) return;
30623  QRectF rect = QRectF(p1, p2).normalized();
30624  double clipPad = mainPen().widthF();
30625  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
30626  if (boundingRect.intersects(
30627  clipRect())) // only draw if bounding rect of rect item is
30628  // visible in cliprect
30629  {
30630  painter->setPen(mainPen());
30631  painter->setBrush(mainBrush());
30632  painter->drawRect(rect);
30633  }
30634 }
30635 
30636 /* inherits documentation from base class */
30637 QPointF QCPItemRect::anchorPixelPosition(int anchorId) const {
30638  QRectF rect =
30640  switch (anchorId) {
30641  case aiTop:
30642  return (rect.topLeft() + rect.topRight()) * 0.5;
30643  case aiTopRight:
30644  return rect.topRight();
30645  case aiRight:
30646  return (rect.topRight() + rect.bottomRight()) * 0.5;
30647  case aiBottom:
30648  return (rect.bottomLeft() + rect.bottomRight()) * 0.5;
30649  case aiBottomLeft:
30650  return rect.bottomLeft();
30651  case aiLeft:
30652  return (rect.topLeft() + rect.bottomLeft()) * 0.5;
30653  }
30654 
30655  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
30656  return QPointF();
30657 }
30658 
30664 QPen QCPItemRect::mainPen() const { return mSelected ? mSelectedPen : mPen; }
30665 
30671 QBrush QCPItemRect::mainBrush() const {
30672  return mSelected ? mSelectedBrush : mBrush;
30673 }
30674 /* end of 'src/items/item-rect.cpp' */
30675 
30676 /* including file 'src/items/item-text.cpp', size 13338 */
30677 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
30678 
30682 
30707  : QCPAbstractItem(parentPlot),
30708  position(createPosition(QLatin1String("position"))),
30709  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
30710  top(createAnchor(QLatin1String("top"), aiTop)),
30711  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
30712  right(createAnchor(QLatin1String("right"), aiRight)),
30713  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
30714  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
30715  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
30716  left(createAnchor(QLatin1String("left"), aiLeft)),
30717  mText(QLatin1String("text")),
30718  mPositionAlignment(Qt::AlignCenter),
30719  mTextAlignment(Qt::AlignTop | Qt::AlignHCenter),
30720  mRotation(0) {
30721  position->setCoords(0, 0);
30722 
30723  setPen(Qt::NoPen);
30724  setSelectedPen(Qt::NoPen);
30725  setBrush(Qt::NoBrush);
30726  setSelectedBrush(Qt::NoBrush);
30729 }
30730 
30732 
30736 void QCPItemText::setColor(const QColor &color) { mColor = color; }
30737 
30743 }
30744 
30751 void QCPItemText::setPen(const QPen &pen) { mPen = pen; }
30752 
30759 void QCPItemText::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
30760 
30767 void QCPItemText::setBrush(const QBrush &brush) { mBrush = brush; }
30768 
30775 void QCPItemText::setSelectedBrush(const QBrush &brush) {
30777 }
30778 
30784 void QCPItemText::setFont(const QFont &font) { mFont = font; }
30785 
30791 void QCPItemText::setSelectedFont(const QFont &font) { mSelectedFont = font; }
30792 
30799 void QCPItemText::setText(const QString &text) { mText = text; }
30800 
30815  mPositionAlignment = alignment;
30816 }
30817 
30823  mTextAlignment = alignment;
30824 }
30825 
30830 void QCPItemText::setRotation(double degrees) { mRotation = degrees; }
30831 
30837 void QCPItemText::setPadding(const QMargins &padding) { mPadding = padding; }
30838 
30839 /* inherits documentation from base class */
30840 double QCPItemText::selectTest(const QPointF &pos,
30841  bool onlySelectable,
30842  QVariant *details) const {
30843  Q_UNUSED(details)
30844  if (onlySelectable && !mSelectable) return -1;
30845 
30846  // The rect may be rotated, so we transform the actual clicked pos to the
30847  // rotated coordinate system, so we can use the normal rectDistance function
30848  // for non-rotated rects:
30849  QPointF positionPixels(position->pixelPosition());
30850  QTransform inputTransform;
30851  inputTransform.translate(positionPixels.x(), positionPixels.y());
30852  inputTransform.rotate(-mRotation);
30853  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
30854  QPointF rotatedPos = inputTransform.map(pos);
30855  QFontMetrics fontMetrics(mFont);
30856  QRect textRect = fontMetrics.boundingRect(
30857  0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, mText);
30858  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(),
30859  mPadding.right(), mPadding.bottom());
30860  QPointF textPos =
30861  getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
30862  textBoxRect.moveTopLeft(textPos.toPoint());
30863 
30864  return rectDistance(textBoxRect, rotatedPos, true);
30865 }
30866 
30867 /* inherits documentation from base class */
30869  QPointF pos(position->pixelPosition());
30870  QTransform transform = painter->transform();
30871  transform.translate(pos.x(), pos.y());
30872  if (!qFuzzyIsNull(mRotation)) transform.rotate(mRotation);
30873  painter->setFont(mainFont());
30874  QRect textRect = painter->fontMetrics().boundingRect(
30875  0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, mText);
30876  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(),
30877  mPadding.right(), mPadding.bottom());
30878  QPointF textPos =
30879  getTextDrawPoint(QPointF(0, 0), textBoxRect,
30880  mPositionAlignment); // 0, 0 because the transform
30881  // does the translation
30882  textRect.moveTopLeft(textPos.toPoint() +
30883  QPoint(mPadding.left(), mPadding.top()));
30884  textBoxRect.moveTopLeft(textPos.toPoint());
30885  double clipPad = mainPen().widthF();
30886  QRect boundingRect =
30887  textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
30888  if (transform.mapRect(boundingRect)
30889  .intersects(painter->transform().mapRect(clipRect()))) {
30890  painter->setTransform(transform);
30891  if ((mainBrush().style() != Qt::NoBrush &&
30892  mainBrush().color().alpha() != 0) ||
30893  (mainPen().style() != Qt::NoPen &&
30894  mainPen().color().alpha() != 0)) {
30895  painter->setPen(mainPen());
30896  painter->setBrush(mainBrush());
30897  painter->drawRect(textBoxRect);
30898  }
30899  painter->setBrush(Qt::NoBrush);
30900  painter->setPen(QPen(mainColor()));
30901  painter->drawText(textRect, Qt::TextDontClip | mTextAlignment, mText);
30902  }
30903 }
30904 
30905 /* inherits documentation from base class */
30906 QPointF QCPItemText::anchorPixelPosition(int anchorId) const {
30907  // get actual rect points (pretty much copied from draw function):
30908  QPointF pos(position->pixelPosition());
30909  QTransform transform;
30910  transform.translate(pos.x(), pos.y());
30911  if (!qFuzzyIsNull(mRotation)) transform.rotate(mRotation);
30912  QFontMetrics fontMetrics(mainFont());
30913  QRect textRect = fontMetrics.boundingRect(
30914  0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, mText);
30915  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(),
30916  mPadding.right(), mPadding.bottom());
30917  QPointF textPos =
30918  getTextDrawPoint(QPointF(0, 0), textBoxRect,
30919  mPositionAlignment); // 0, 0 because the transform
30920  // does the translation
30921  textBoxRect.moveTopLeft(textPos.toPoint());
30922  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
30923 
30924  switch (anchorId) {
30925  case aiTopLeft:
30926  return rectPoly.at(0);
30927  case aiTop:
30928  return (rectPoly.at(0) + rectPoly.at(1)) * 0.5;
30929  case aiTopRight:
30930  return rectPoly.at(1);
30931  case aiRight:
30932  return (rectPoly.at(1) + rectPoly.at(2)) * 0.5;
30933  case aiBottomRight:
30934  return rectPoly.at(2);
30935  case aiBottom:
30936  return (rectPoly.at(2) + rectPoly.at(3)) * 0.5;
30937  case aiBottomLeft:
30938  return rectPoly.at(3);
30939  case aiLeft:
30940  return (rectPoly.at(3) + rectPoly.at(0)) * 0.5;
30941  }
30942 
30943  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
30944  return QPointF();
30945 }
30946 
30958 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos,
30959  const QRectF &rect,
30960  Qt::Alignment positionAlignment) const {
30961  if (positionAlignment == 0 ||
30962  positionAlignment == (Qt::AlignLeft | Qt::AlignTop))
30963  return pos;
30964 
30965  QPointF result = pos; // start at top left
30966  if (positionAlignment.testFlag(Qt::AlignHCenter))
30967  result.rx() -= rect.width() / 2.0;
30968  else if (positionAlignment.testFlag(Qt::AlignRight))
30969  result.rx() -= rect.width();
30970  if (positionAlignment.testFlag(Qt::AlignVCenter))
30971  result.ry() -= rect.height() / 2.0;
30972  else if (positionAlignment.testFlag(Qt::AlignBottom))
30973  result.ry() -= rect.height();
30974  return result;
30975 }
30976 
30982 QFont QCPItemText::mainFont() const {
30983  return mSelected ? mSelectedFont : mFont;
30984 }
30985 
30991 QColor QCPItemText::mainColor() const {
30992  return mSelected ? mSelectedColor : mColor;
30993 }
30994 
31000 QPen QCPItemText::mainPen() const { return mSelected ? mSelectedPen : mPen; }
31001 
31007 QBrush QCPItemText::mainBrush() const {
31008  return mSelected ? mSelectedBrush : mBrush;
31009 }
31010 /* end of 'src/items/item-text.cpp' */
31011 
31012 /* including file 'src/items/item-ellipse.cpp', size 7863 */
31013 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
31014 
31018 
31037  : QCPAbstractItem(parentPlot),
31038  topLeft(createPosition(QLatin1String("topLeft"))),
31039  bottomRight(createPosition(QLatin1String("bottomRight"))),
31040  topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
31041  top(createAnchor(QLatin1String("top"), aiTop)),
31042  topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
31043  right(createAnchor(QLatin1String("right"), aiRight)),
31044  bottomRightRim(
31045  createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
31046  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
31047  bottomLeftRim(
31048  createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
31049  left(createAnchor(QLatin1String("left"), aiLeft)),
31050  center(createAnchor(QLatin1String("center"), aiCenter)) {
31051  topLeft->setCoords(0, 1);
31052  bottomRight->setCoords(1, 0);
31053 
31054  setPen(QPen(Qt::black));
31055  setSelectedPen(QPen(Qt::blue, 2));
31056  setBrush(Qt::NoBrush);
31057  setSelectedBrush(Qt::NoBrush);
31058 }
31059 
31061 
31067 void QCPItemEllipse::setPen(const QPen &pen) { mPen = pen; }
31068 
31075 
31082 void QCPItemEllipse::setBrush(const QBrush &brush) { mBrush = brush; }
31083 
31090 void QCPItemEllipse::setSelectedBrush(const QBrush &brush) {
31092 }
31093 
31094 /* inherits documentation from base class */
31095 double QCPItemEllipse::selectTest(const QPointF &pos,
31096  bool onlySelectable,
31097  QVariant *details) const {
31098  Q_UNUSED(details)
31099  if (onlySelectable && !mSelectable) return -1;
31100 
31101  QPointF p1 = topLeft->pixelPosition();
31102  QPointF p2 = bottomRight->pixelPosition();
31103  QPointF center((p1 + p2) / 2.0);
31104  double a = qAbs(p1.x() - p2.x()) / 2.0;
31105  double b = qAbs(p1.y() - p2.y()) / 2.0;
31106  double x = pos.x() - center.x();
31107  double y = pos.y() - center.y();
31108 
31109  // distance to border:
31110  double c = 1.0 / qSqrt(x * x / (a * a) + y * y / (b * b));
31111  double result = qAbs(c - 1) * qSqrt(x * x + y * y);
31112  // filled ellipse, allow click inside to count as hit:
31113  if (result > mParentPlot->selectionTolerance() * 0.99 &&
31114  mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0) {
31115  if (x * x / (a * a) + y * y / (b * b) <= 1)
31117  }
31118  return result;
31119 }
31120 
31121 /* inherits documentation from base class */
31123  QPointF p1 = topLeft->pixelPosition();
31124  QPointF p2 = bottomRight->pixelPosition();
31125  if (p1.toPoint() == p2.toPoint()) return;
31126  QRectF ellipseRect = QRectF(p1, p2).normalized();
31127  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(),
31128  mainPen().widthF(), mainPen().widthF());
31129  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse
31130  // is visible in cliprect
31131  {
31132  painter->setPen(mainPen());
31133  painter->setBrush(mainBrush());
31134 #ifdef __EXCEPTIONS
31135  try // drawEllipse sometimes throws exceptions if ellipse is too big
31136  {
31137 #endif
31138  painter->drawEllipse(ellipseRect);
31139 #ifdef __EXCEPTIONS
31140  } catch (...) {
31141  qDebug() << Q_FUNC_INFO
31142  << "Item too large for memory, setting invisible";
31143  setVisible(false);
31144  }
31145 #endif
31146  }
31147 }
31148 
31149 /* inherits documentation from base class */
31150 QPointF QCPItemEllipse::anchorPixelPosition(int anchorId) const {
31151  QRectF rect =
31153  switch (anchorId) {
31154  case aiTopLeftRim:
31155  return rect.center() +
31156  (rect.topLeft() - rect.center()) * 1 / qSqrt(2);
31157  case aiTop:
31158  return (rect.topLeft() + rect.topRight()) * 0.5;
31159  case aiTopRightRim:
31160  return rect.center() +
31161  (rect.topRight() - rect.center()) * 1 / qSqrt(2);
31162  case aiRight:
31163  return (rect.topRight() + rect.bottomRight()) * 0.5;
31164  case aiBottomRightRim:
31165  return rect.center() +
31166  (rect.bottomRight() - rect.center()) * 1 / qSqrt(2);
31167  case aiBottom:
31168  return (rect.bottomLeft() + rect.bottomRight()) * 0.5;
31169  case aiBottomLeftRim:
31170  return rect.center() +
31171  (rect.bottomLeft() - rect.center()) * 1 / qSqrt(2);
31172  case aiLeft:
31173  return (rect.topLeft() + rect.bottomLeft()) * 0.5;
31174  case aiCenter:
31175  return (rect.topLeft() + rect.bottomRight()) * 0.5;
31176  }
31177 
31178  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
31179  return QPointF();
31180 }
31181 
31188 
31195  return mSelected ? mSelectedBrush : mBrush;
31196 }
31197 /* end of 'src/items/item-ellipse.cpp' */
31198 
31199 /* including file 'src/items/item-pixmap.cpp', size 10615 */
31200 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
31201 
31205 
31230  : QCPAbstractItem(parentPlot),
31231  topLeft(createPosition(QLatin1String("topLeft"))),
31232  bottomRight(createPosition(QLatin1String("bottomRight"))),
31233  top(createAnchor(QLatin1String("top"), aiTop)),
31234  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
31235  right(createAnchor(QLatin1String("right"), aiRight)),
31236  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
31237  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
31238  left(createAnchor(QLatin1String("left"), aiLeft)),
31239  mScaled(false),
31240  mScaledPixmapInvalidated(true),
31241  mAspectRatioMode(Qt::KeepAspectRatio),
31242  mTransformationMode(Qt::SmoothTransformation) {
31243  topLeft->setCoords(0, 1);
31244  bottomRight->setCoords(1, 0);
31245 
31246  setPen(Qt::NoPen);
31247  setSelectedPen(QPen(Qt::blue));
31248 }
31249 
31251 
31255 void QCPItemPixmap::setPixmap(const QPixmap &pixmap) {
31256  mPixmap = pixmap;
31257  mScaledPixmapInvalidated = true;
31258  if (mPixmap.isNull()) qDebug() << Q_FUNC_INFO << "pixmap is null";
31259 }
31260 
31265 void QCPItemPixmap::setScaled(bool scaled,
31266  Qt::AspectRatioMode aspectRatioMode,
31267  Qt::TransformationMode transformationMode) {
31268  mScaled = scaled;
31271  mScaledPixmapInvalidated = true;
31272 }
31273 
31279 void QCPItemPixmap::setPen(const QPen &pen) { mPen = pen; }
31280 
31287 void QCPItemPixmap::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
31288 
31289 /* inherits documentation from base class */
31290 double QCPItemPixmap::selectTest(const QPointF &pos,
31291  bool onlySelectable,
31292  QVariant *details) const {
31293  Q_UNUSED(details)
31294  if (onlySelectable && !mSelectable) return -1;
31295 
31296  return rectDistance(getFinalRect(), pos, true);
31297 }
31298 
31299 /* inherits documentation from base class */
31301  bool flipHorz = false;
31302  bool flipVert = false;
31303  QRect rect = getFinalRect(&flipHorz, &flipVert);
31304  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
31305  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
31306  if (boundingRect.intersects(clipRect())) {
31307  updateScaledPixmap(rect, flipHorz, flipVert);
31308  painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
31309  QPen pen = mainPen();
31310  if (pen.style() != Qt::NoPen) {
31311  painter->setPen(pen);
31312  painter->setBrush(Qt::NoBrush);
31313  painter->drawRect(rect);
31314  }
31315  }
31316 }
31317 
31318 /* inherits documentation from base class */
31319 QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const {
31320  bool flipHorz;
31321  bool flipVert;
31322  QRect rect = getFinalRect(&flipHorz, &flipVert);
31323  // we actually want denormal rects (negative width/height) here, so restore
31324  // the flipped state:
31325  if (flipHorz) rect.adjust(rect.width(), 0, -rect.width(), 0);
31326  if (flipVert) rect.adjust(0, rect.height(), 0, -rect.height());
31327 
31328  switch (anchorId) {
31329  case aiTop:
31330  return (rect.topLeft() + rect.topRight()) * 0.5;
31331  case aiTopRight:
31332  return rect.topRight();
31333  case aiRight:
31334  return (rect.topRight() + rect.bottomRight()) * 0.5;
31335  case aiBottom:
31336  return (rect.bottomLeft() + rect.bottomRight()) * 0.5;
31337  case aiBottomLeft:
31338  return rect.bottomLeft();
31339  case aiLeft:
31340  return (rect.topLeft() + rect.bottomLeft()) * 0.5;
31341  ;
31342  }
31343 
31344  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
31345  return QPointF();
31346 }
31347 
31362  bool flipHorz,
31363  bool flipVert) {
31364  if (mPixmap.isNull()) return;
31365 
31366  if (mScaled) {
31367 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
31368  double devicePixelRatio = mPixmap.devicePixelRatio();
31369 #else
31370  double devicePixelRatio = 1.0;
31371 #endif
31372  if (finalRect.isNull()) finalRect = getFinalRect(&flipHorz, &flipVert);
31374  finalRect.size() != mScaledPixmap.size() / devicePixelRatio) {
31375  mScaledPixmap =
31376  mPixmap.scaled(finalRect.size() * devicePixelRatio,
31378  if (flipHorz || flipVert)
31379  mScaledPixmap = QPixmap::fromImage(
31380  mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
31381 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
31382  mScaledPixmap.setDevicePixelRatio(devicePixelRatio);
31383 #endif
31384  }
31385  } else if (!mScaledPixmap.isNull())
31386  mScaledPixmap = QPixmap();
31387  mScaledPixmapInvalidated = false;
31388 }
31389 
31406 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const {
31407  QRect result;
31408  bool flipHorz = false;
31409  bool flipVert = false;
31410  QPoint p1 = topLeft->pixelPosition().toPoint();
31411  QPoint p2 = bottomRight->pixelPosition().toPoint();
31412  if (p1 == p2) return QRect(p1, QSize(0, 0));
31413  if (mScaled) {
31414  QSize newSize = QSize(p2.x() - p1.x(), p2.y() - p1.y());
31415  QPoint topLeft = p1;
31416  if (newSize.width() < 0) {
31417  flipHorz = true;
31418  newSize.rwidth() *= -1;
31419  topLeft.setX(p2.x());
31420  }
31421  if (newSize.height() < 0) {
31422  flipVert = true;
31423  newSize.rheight() *= -1;
31424  topLeft.setY(p2.y());
31425  }
31426  QSize scaledSize = mPixmap.size();
31427 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
31428  scaledSize /= mPixmap.devicePixelRatio();
31429  scaledSize.scale(newSize * mPixmap.devicePixelRatio(),
31431 #else
31432  scaledSize.scale(newSize, mAspectRatioMode);
31433 #endif
31434  result = QRect(topLeft, scaledSize);
31435  } else {
31436 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
31437  result = QRect(p1, mPixmap.size() / mPixmap.devicePixelRatio());
31438 #else
31439  result = QRect(p1, mPixmap.size());
31440 #endif
31441  }
31442  if (flippedHorz) *flippedHorz = flipHorz;
31443  if (flippedVert) *flippedVert = flipVert;
31444  return result;
31445 }
31446 
31453 /* end of 'src/items/item-pixmap.cpp' */
31454 
31455 /* including file 'src/items/item-tracer.cpp', size 14624 */
31456 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
31457 
31461 
31503  : QCPAbstractItem(parentPlot),
31504  position(createPosition(QLatin1String("position"))),
31505  mSize(6),
31506  mStyle(tsCrosshair),
31507  mGraph(0),
31508  mGraphKey(0),
31509  mInterpolating(false) {
31510  position->setCoords(0, 0);
31511 
31512  setBrush(Qt::NoBrush);
31513  setSelectedBrush(Qt::NoBrush);
31514  setPen(QPen(Qt::black));
31515  setSelectedPen(QPen(Qt::blue, 2));
31516 }
31517 
31519 
31525 void QCPItemTracer::setPen(const QPen &pen) { mPen = pen; }
31526 
31532 void QCPItemTracer::setSelectedPen(const QPen &pen) { mSelectedPen = pen; }
31533 
31539 void QCPItemTracer::setBrush(const QBrush &brush) { mBrush = brush; }
31540 
31547 void QCPItemTracer::setSelectedBrush(const QBrush &brush) {
31549 }
31550 
31556 
31564  mStyle = style;
31565 }
31566 
31579  if (graph) {
31580  if (graph->parentPlot() == mParentPlot) {
31583  mGraph = graph;
31584  updatePosition();
31585  } else
31586  qDebug() << Q_FUNC_INFO
31587  << "graph isn't in same QCustomPlot instance as this item";
31588  } else {
31589  mGraph = 0;
31590  }
31591 }
31592 
31603 void QCPItemTracer::setGraphKey(double key) { mGraphKey = key; }
31604 
31617 void QCPItemTracer::setInterpolating(bool enabled) { mInterpolating = enabled; }
31618 
31619 /* inherits documentation from base class */
31620 double QCPItemTracer::selectTest(const QPointF &pos,
31621  bool onlySelectable,
31622  QVariant *details) const {
31623  Q_UNUSED(details)
31624  if (onlySelectable && !mSelectable) return -1;
31625 
31626  QPointF center(position->pixelPosition());
31627  double w = mSize / 2.0;
31628  QRect clip = clipRect();
31629  switch (mStyle) {
31630  case tsNone:
31631  return -1;
31632  case tsPlus: {
31633  if (clipRect().intersects(
31634  QRectF(center - QPointF(w, w), center + QPointF(w, w))
31635  .toRect()))
31636  return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(
31637  center + QPointF(-w, 0),
31638  center + QPointF(w, 0)),
31639  QCPVector2D(pos).distanceSquaredToLine(
31640  center + QPointF(0, -w),
31641  center + QPointF(0, w))));
31642  break;
31643  }
31644  case tsCrosshair: {
31645  return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(
31646  QCPVector2D(clip.left(), center.y()),
31647  QCPVector2D(clip.right(), center.y())),
31648  QCPVector2D(pos).distanceSquaredToLine(
31649  QCPVector2D(center.x(), clip.top()),
31650  QCPVector2D(center.x(), clip.bottom()))));
31651  }
31652  case tsCircle: {
31653  if (clip.intersects(
31654  QRectF(center - QPointF(w, w), center + QPointF(w, w))
31655  .toRect())) {
31656  // distance to border:
31657  double centerDist = QCPVector2D(center - pos).length();
31658  double circleLine = w;
31659  double result = qAbs(centerDist - circleLine);
31660  // filled ellipse, allow click inside to count as hit:
31661  if (result > mParentPlot->selectionTolerance() * 0.99 &&
31662  mBrush.style() != Qt::NoBrush &&
31663  mBrush.color().alpha() != 0) {
31664  if (centerDist <= circleLine)
31666  }
31667  return result;
31668  }
31669  break;
31670  }
31671  case tsSquare: {
31672  if (clip.intersects(
31673  QRectF(center - QPointF(w, w), center + QPointF(w, w))
31674  .toRect())) {
31675  QRectF rect =
31676  QRectF(center - QPointF(w, w), center + QPointF(w, w));
31677  bool filledRect = mBrush.style() != Qt::NoBrush &&
31678  mBrush.color().alpha() != 0;
31679  return rectDistance(rect, pos, filledRect);
31680  }
31681  break;
31682  }
31683  }
31684  return -1;
31685 }
31686 
31687 /* inherits documentation from base class */
31689  updatePosition();
31690  if (mStyle == tsNone) return;
31691 
31692  painter->setPen(mainPen());
31693  painter->setBrush(mainBrush());
31694  QPointF center(position->pixelPosition());
31695  double w = mSize / 2.0;
31696  QRect clip = clipRect();
31697  switch (mStyle) {
31698  case tsNone:
31699  return;
31700  case tsPlus: {
31701  if (clip.intersects(
31702  QRectF(center - QPointF(w, w), center + QPointF(w, w))
31703  .toRect())) {
31704  painter->drawLine(QLineF(center + QPointF(-w, 0),
31705  center + QPointF(w, 0)));
31706  painter->drawLine(QLineF(center + QPointF(0, -w),
31707  center + QPointF(0, w)));
31708  }
31709  break;
31710  }
31711  case tsCrosshair: {
31712  if (center.y() > clip.top() && center.y() < clip.bottom())
31713  painter->drawLine(QLineF(clip.left(), center.y(), clip.right(),
31714  center.y()));
31715  if (center.x() > clip.left() && center.x() < clip.right())
31716  painter->drawLine(QLineF(center.x(), clip.top(), center.x(),
31717  clip.bottom()));
31718  break;
31719  }
31720  case tsCircle: {
31721  if (clip.intersects(
31722  QRectF(center - QPointF(w, w), center + QPointF(w, w))
31723  .toRect()))
31724  painter->drawEllipse(center, w, w);
31725  break;
31726  }
31727  case tsSquare: {
31728  if (clip.intersects(
31729  QRectF(center - QPointF(w, w), center + QPointF(w, w))
31730  .toRect()))
31731  painter->drawRect(
31732  QRectF(center - QPointF(w, w), center + QPointF(w, w)));
31733  break;
31734  }
31735  }
31736 }
31737 
31753  if (mGraph) {
31755  if (mGraph->data()->size() > 1) {
31757  mGraph->data()->constBegin();
31759  mGraph->data()->constEnd() - 1;
31760  if (mGraphKey <= first->key)
31761  position->setCoords(first->key, first->value);
31762  else if (mGraphKey >= last->key)
31763  position->setCoords(last->key, last->value);
31764  else {
31766  mGraph->data()->findBegin(mGraphKey);
31767  if (it !=
31768  mGraph->data()
31769  ->constEnd()) // mGraphKey is not exactly on
31770  // last iterator, but somewhere
31771  // between iterators
31772  {
31774  ++it; // won't advance to constEnd because we handled
31775  // that case (mGraphKey >= last->key) before
31776  if (mInterpolating) {
31777  // interpolate between iterators around mGraphKey:
31778  double slope = 0;
31779  if (!qFuzzyCompare((double)it->key,
31780  (double)prevIt->key))
31781  slope = (it->value - prevIt->value) /
31782  (it->key - prevIt->key);
31784  mGraphKey,
31785  (mGraphKey - prevIt->key) * slope +
31786  prevIt->value);
31787  } else {
31788  // find iterator with key closest to mGraphKey:
31789  if (mGraphKey < (prevIt->key + it->key) * 0.5)
31790  position->setCoords(prevIt->key, prevIt->value);
31791  else
31792  position->setCoords(it->key, it->value);
31793  }
31794  } else // mGraphKey is exactly on last iterator (should
31795  // actually be caught when comparing first/last
31796  // keys, but this is a failsafe for fp uncertainty)
31797  position->setCoords(it->key, it->value);
31798  }
31799  } else if (mGraph->data()->size() == 1) {
31801  mGraph->data()->constBegin();
31802  position->setCoords(it->key, it->value);
31803  } else
31804  qDebug() << Q_FUNC_INFO << "graph has no data";
31805  } else
31806  qDebug() << Q_FUNC_INFO
31807  << "graph not contained in QCustomPlot instance (anymore)";
31808  }
31809 }
31810 
31817 
31824  return mSelected ? mSelectedBrush : mBrush;
31825 }
31826 /* end of 'src/items/item-tracer.cpp' */
31827 
31828 /* including file 'src/items/item-bracket.cpp', size 10687 */
31829 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
31830 
31834 
31867  : QCPAbstractItem(parentPlot),
31868  left(createPosition(QLatin1String("left"))),
31869  right(createPosition(QLatin1String("right"))),
31870  center(createAnchor(QLatin1String("center"), aiCenter)),
31871  mLength(8),
31872  mStyle(bsCalligraphic) {
31873  left->setCoords(0, 0);
31874  right->setCoords(1, 1);
31875 
31876  setPen(QPen(Qt::black));
31877  setSelectedPen(QPen(Qt::blue, 2));
31878 }
31879 
31881 
31892 void QCPItemBracket::setPen(const QPen &pen) { mPen = pen; }
31893 
31900 
31911 void QCPItemBracket::setLength(double length) { mLength = length; }
31912 
31919  mStyle = style;
31920 }
31921 
31922 /* inherits documentation from base class */
31923 double QCPItemBracket::selectTest(const QPointF &pos,
31924  bool onlySelectable,
31925  QVariant *details) const {
31926  Q_UNUSED(details)
31927  if (onlySelectable && !mSelectable) return -1;
31928 
31929  QCPVector2D p(pos);
31930  QCPVector2D leftVec(left->pixelPosition());
31931  QCPVector2D rightVec(right->pixelPosition());
31932  if (leftVec.toPoint() == rightVec.toPoint()) return -1;
31933 
31934  QCPVector2D widthVec = (rightVec - leftVec) * 0.5;
31935  QCPVector2D lengthVec = widthVec.perpendicular().normalized() * mLength;
31936  QCPVector2D centerVec = (rightVec + leftVec) * 0.5 - lengthVec;
31937 
31938  switch (mStyle) {
31940  case QCPItemBracket::bsRound: {
31941  double a = p.distanceSquaredToLine(centerVec - widthVec,
31942  centerVec + widthVec);
31943  double b = p.distanceSquaredToLine(centerVec - widthVec + lengthVec,
31944  centerVec - widthVec);
31945  double c = p.distanceSquaredToLine(centerVec + widthVec + lengthVec,
31946  centerVec + widthVec);
31947  return qSqrt(qMin(qMin(a, b), c));
31948  }
31951  double a = p.distanceSquaredToLine(
31952  centerVec - widthVec * 0.75 + lengthVec * 0.15,
31953  centerVec + lengthVec * 0.3);
31954  double b = p.distanceSquaredToLine(
31955  centerVec - widthVec + lengthVec * 0.7,
31956  centerVec - widthVec * 0.75 + lengthVec * 0.15);
31957  double c = p.distanceSquaredToLine(
31958  centerVec + widthVec * 0.75 + lengthVec * 0.15,
31959  centerVec + lengthVec * 0.3);
31960  double d = p.distanceSquaredToLine(
31961  centerVec + widthVec + lengthVec * 0.7,
31962  centerVec + widthVec * 0.75 + lengthVec * 0.15);
31963  return qSqrt(qMin(qMin(a, b), qMin(c, d)));
31964  }
31965  }
31966  return -1;
31967 }
31968 
31969 /* inherits documentation from base class */
31971  QCPVector2D leftVec(left->pixelPosition());
31972  QCPVector2D rightVec(right->pixelPosition());
31973  if (leftVec.toPoint() == rightVec.toPoint()) return;
31974 
31975  QCPVector2D widthVec = (rightVec - leftVec) * 0.5;
31976  QCPVector2D lengthVec = widthVec.perpendicular().normalized() * mLength;
31977  QCPVector2D centerVec = (rightVec + leftVec) * 0.5 - lengthVec;
31978 
31979  QPolygon boundingPoly;
31980  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
31981  << (rightVec - lengthVec).toPoint()
31982  << (leftVec - lengthVec).toPoint();
31983  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(),
31984  mainPen().widthF(), mainPen().widthF());
31985  if (clip.intersects(boundingPoly.boundingRect())) {
31986  painter->setPen(mainPen());
31987  switch (mStyle) {
31988  case bsSquare: {
31989  painter->drawLine((centerVec + widthVec).toPointF(),
31990  (centerVec - widthVec).toPointF());
31991  painter->drawLine(
31992  (centerVec + widthVec).toPointF(),
31993  (centerVec + widthVec + lengthVec).toPointF());
31994  painter->drawLine(
31995  (centerVec - widthVec).toPointF(),
31996  (centerVec - widthVec + lengthVec).toPointF());
31997  break;
31998  }
31999  case bsRound: {
32000  painter->setBrush(Qt::NoBrush);
32001  QPainterPath path;
32002  path.moveTo((centerVec + widthVec + lengthVec).toPointF());
32003  path.cubicTo((centerVec + widthVec).toPointF(),
32004  (centerVec + widthVec).toPointF(),
32005  centerVec.toPointF());
32006  path.cubicTo((centerVec - widthVec).toPointF(),
32007  (centerVec - widthVec).toPointF(),
32008  (centerVec - widthVec + lengthVec).toPointF());
32009  painter->drawPath(path);
32010  break;
32011  }
32012  case bsCurly: {
32013  painter->setBrush(Qt::NoBrush);
32014  QPainterPath path;
32015  path.moveTo((centerVec + widthVec + lengthVec).toPointF());
32016  path.cubicTo(
32017  (centerVec + widthVec - lengthVec * 0.8).toPointF(),
32018  (centerVec + 0.4 * widthVec + lengthVec).toPointF(),
32019  centerVec.toPointF());
32020  path.cubicTo(
32021  (centerVec - 0.4 * widthVec + lengthVec).toPointF(),
32022  (centerVec - widthVec - lengthVec * 0.8).toPointF(),
32023  (centerVec - widthVec + lengthVec).toPointF());
32024  painter->drawPath(path);
32025  break;
32026  }
32027  case bsCalligraphic: {
32028  painter->setPen(Qt::NoPen);
32029  painter->setBrush(QBrush(mainPen().color()));
32030  QPainterPath path;
32031  path.moveTo((centerVec + widthVec + lengthVec).toPointF());
32032 
32033  path.cubicTo(
32034  (centerVec + widthVec - lengthVec * 0.8).toPointF(),
32035  (centerVec + 0.4 * widthVec + 0.8 * lengthVec)
32036  .toPointF(),
32037  centerVec.toPointF());
32038  path.cubicTo(
32039  (centerVec - 0.4 * widthVec + 0.8 * lengthVec)
32040  .toPointF(),
32041  (centerVec - widthVec - lengthVec * 0.8).toPointF(),
32042  (centerVec - widthVec + lengthVec).toPointF());
32043 
32044  path.cubicTo(
32045  (centerVec - widthVec - lengthVec * 0.5).toPointF(),
32046  (centerVec - 0.2 * widthVec + 1.2 * lengthVec)
32047  .toPointF(),
32048  (centerVec + lengthVec * 0.2).toPointF());
32049  path.cubicTo(
32050  (centerVec + 0.2 * widthVec + 1.2 * lengthVec)
32051  .toPointF(),
32052  (centerVec + widthVec - lengthVec * 0.5).toPointF(),
32053  (centerVec + widthVec + lengthVec).toPointF());
32054 
32055  painter->drawPath(path);
32056  break;
32057  }
32058  }
32059  }
32060 }
32061 
32062 /* inherits documentation from base class */
32063 QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const {
32064  QCPVector2D leftVec(left->pixelPosition());
32065  QCPVector2D rightVec(right->pixelPosition());
32066  if (leftVec.toPoint() == rightVec.toPoint()) return leftVec.toPointF();
32067 
32068  QCPVector2D widthVec = (rightVec - leftVec) * 0.5;
32069  QCPVector2D lengthVec = widthVec.perpendicular().normalized() * mLength;
32070  QCPVector2D centerVec = (rightVec + leftVec) * 0.5 - lengthVec;
32071 
32072  switch (anchorId) {
32073  case aiCenter:
32074  return centerVec.toPointF();
32075  }
32076  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
32077  return QPointF();
32078 }
32079 
32086 /* end of 'src/items/item-bracket.cpp' */
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
double qtCompatWheelEventDelta(const QWheelEvent *event) noexcept
Definition: QtCompat.h:751
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
QCPDataContainer< QCPFinancialData > QCPFinancialDataContainer
Definition: qcustomplot.h:6909
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
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 QCP::Interaction selectionCategory() const
void selectableChanged(bool selectable)
QCPItemPosition * createPosition(const QString &name)
void setClipAxisRect(QCPAxisRect *rect)
double rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
bool hasAnchor(const QString &name) const
Q_SLOT void setSelectable(bool selectable)
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)
virtual QPointF anchorPixelPosition(int anchorId) const
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 paint buffers, which define the rendering backend.
Definition: qcustomplot.h:710
QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio)
QSize size() const
Definition: qcustomplot.h:716
virtual ~QCPAbstractPaintBuffer()
void setDevicePixelRatio(double ratio)
bool invalidated() const
Definition: qcustomplot.h:717
void setSize(const QSize &size)
void setInvalidated(bool invalidated=true)
virtual void reallocateBuffer()=0
A template base class for plottables with one-dimensional data.
Definition: qcustomplot.h:4551
void drawPolyline(QCPPainter *painter, const QVector< QPointF > &lineData) const
Definition: qcustomplot.h:5106
QSharedPointer< QCPDataContainer< QCPGraphData > > mDataContainer
Definition: qcustomplot.h:4583
void getDataSegments(QList< QCPDataRange > &selectedSegments, QList< QCPDataRange > &unselectedSegments) const
Definition: qcustomplot.h:5073
The abstract base class for all data representing objects in a plot.
Definition: qcustomplot.h:3816
QCP::SelectionType selectable() const
Definition: qcustomplot.h:3847
QCPDataSelection selection() const
Definition: qcustomplot.h:3849
void setAntialiasedFill(bool enabled)
bool selected() const
Definition: qcustomplot.h:3848
QCPSelectionDecorator * mSelectionDecorator
Definition: qcustomplot.h:3912
void rescaleAxes(bool onlyEnlarge=false) const
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPDataSelection mSelection
Definition: qcustomplot.h:3911
void setSelectionDecorator(QCPSelectionDecorator *decorator)
Q_SLOT void setSelection(QCPDataSelection selection)
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)
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 setBrush(const QBrush &brush)
void coordsToPixels(double key, double value, double &x, double &y) const
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:3909
virtual QCPPlottableInterface1D * interface1D()
Definition: qcustomplot.h:3873
void setKeyAxis(QCPAxis *axis)
virtual ~QCPAbstractPlottable()
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
void applyScattersAntialiasingHint(QCPPainter *painter) const
bool removeFromLegend() const
void rescaleKeyAxis(bool onlyEnlarge=false) const
QCPAxis * valueAxis() const
Definition: qcustomplot.h:3846
QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual QCP::Interaction selectionCategory() const
Holds multiple axes and arranges them in a rectangular shape.
Definition: qcustomplot.h:5375
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)
virtual void layoutChanged()
void zoom(const QRectF &pixelRect)
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
QList< QCPAxis * > rangeZoomAxes(Qt::Orientation orientation)
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)
Qt::Orientations rangeZoom() const
Definition: qcustomplot.h:5398
QList< QPointer< QCPAxis > > mRangeZoomHorzAxis
Definition: qcustomplot.h:5473
Qt::Orientations rangeDrag() const
Definition: qcustomplot.h:5397
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< QCPAxis * > rangeDragAxes(Qt::Orientation orientation)
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
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range)
static QDateTime keyToDateTime(double key)
void setTickOrigin(double origin)
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
Qt::TimeSpec mDateTimeSpec
Definition: qcustomplot.h:1928
enum QCPAxisTickerDateTime::DateStrategy mDateStrategy
virtual int getSubTickCount(double tickStep)
static double dateTimeToKey(const QDateTime dateTime)
void setDateTimeFormat(const QString &format)
virtual double getTickStep(const QCPRange &range)
void setDateTimeSpec(Qt::TimeSpec spec)
@ ssPowers
An integer power of the specified tick step is allowed.
Definition: qcustomplot.h:2032
void setTickStep(double step)
ScaleStrategy mScaleStrategy
Definition: qcustomplot.h:2049
virtual double getTickStep(const QCPRange &range)
void setScaleStrategy(ScaleStrategy strategy)
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range)
virtual int getSubTickCount(double tickStep)
virtual double getTickStep(const QCPRange &range)
void setLogBase(double base)
void setSubTickCount(int subTicks)
virtual double getTickStep(const QCPRange &range)
void simplifyFraction(int &numerator, int &denominator) const
QString unicodeSuperscript(int number) const
FractionStyle mFractionStyle
Definition: qcustomplot.h:2143
void setPiValue(double pi)
void setPeriodicity(int multiplesOfPi)
QString unicodeSubscript(int number) const
virtual int getSubTickCount(double tickStep)
void setFractionStyle(FractionStyle style)
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
void setPiSymbol(QString symbol)
QString unicodeFraction(int numerator, int denominator) const
QString fractionToString(int numerator, int denominator) const
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
QMap< double, QString > mTicks
Definition: qcustomplot.h:2084
void addTick(double position, const QString &label)
virtual double getTickStep(const QCPRange &range)
void setTicks(const QMap< double, QString > &ticks)
void setSubTickCount(int subTicks)
virtual int getSubTickCount(double tickStep)
void addTicks(const QMap< double, QString > &ticks)
QMap< double, QString > & ticks()
Definition: qcustomplot.h:2066
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range)
void replaceUnit(QString &text, TimeUnit unit, int value) const
void setTimeFormat(const QString &format)
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
TimeUnit mBiggestUnit
Definition: qcustomplot.h:1992
QHash< TimeUnit, int > mFieldWidth
Definition: qcustomplot.h:1989
@ tuSeconds
Seconds (%s in setTimeFormat)
Definition: qcustomplot.h:1966
@ tuMinutes
Minutes (%m in setTimeFormat)
Definition: qcustomplot.h:1968
@ tuHours
Hours (%h in setTimeFormat)
Definition: qcustomplot.h:1970
@ tuDays
Days (%d in setTimeFormat)
Definition: qcustomplot.h:1972
TimeUnit mSmallestUnit
Definition: qcustomplot.h:1992
virtual int getSubTickCount(double tickStep)
void setFieldWidth(TimeUnit unit, int width)
QHash< TimeUnit, QString > mFormatPattern
Definition: qcustomplot.h:1993
virtual double getTickStep(const QCPRange &range)
The base class tick generator used by QCPAxis to create tick positions and tick labels.
Definition: qcustomplot.h:1821
virtual ~QCPAxisTicker()
double getMantissa(double input, double *magnitude=0) const
void setTickCount(int count)
virtual int getSubTickCount(double tickStep)
double pickClosest(double target, const QVector< double > &candidates) const
double mTickOrigin
Definition: qcustomplot.h:1867
void setTickStepStrategy(TickStepStrategy strategy)
virtual QVector< QString > createLabelVector(const QVector< double > &ticks, const QLocale &locale, QChar formatChar, int precision)
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
virtual double getTickStep(const QCPRange &range)
virtual QVector< double > createSubTickVector(int subTickCount, const QVector< double > &ticks)
void trimTicks(const QCPRange &range, QVector< double > &ticks, bool keepOneOutlier) const
void setTickOrigin(double origin)
TickStepStrategy mTickStepStrategy
Definition: qcustomplot.h:1865
double cleanMantissa(double input) const
virtual void generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector< double > &ticks, QVector< double > *subTicks, QVector< QString > *tickLabels)
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range)
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)
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:2562
QCPLineEnding lowerEnding() const
QCPGrid * mGrid
Definition: qcustomplot.h:2552
void setTickLabelSide(LabelSide side)
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 setSelectedSubTickPen(const QPen &pen)
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
void setTickLabelFont(const QFont &font)
bool mCachedMarginValid
Definition: qcustomplot.h:2558
void scaleRange(double factor)
QPen mSubTickPen
Definition: qcustomplot.h:2545
void setLabel(const QString &str)
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
@ 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
QList< QCPAbstractItem * > items() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setLabelPadding(int padding)
QColor mLabelColor
Definition: qcustomplot.h:2528
int pixelOrientation() const
Definition: qcustomplot.h:2484
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)
QCPRange mDragStartRange
Definition: qcustomplot.h:2561
void setSubTickLengthOut(int outside)
void setTicker(QSharedPointer< QCPAxisTicker > ticker)
QFont mSelectedTickLabelFont
Definition: qcustomplot.h:2533
int mPadding
Definition: qcustomplot.h:2519
virtual void deselectEvent(bool *selectionStateChanged)
double pixelToCoord(double value) const
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)
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
QVector< double > mTickVector
Definition: qcustomplot.h:2555
QCPAxisPainterPrivate * mAxisPainter
Definition: qcustomplot.h:2553
virtual void wheelEvent(QWheelEvent *event)
void setLabelFont(const QFont &font)
void setBasePen(const QPen &pen)
QCPAxisRect * mAxisRect
Definition: qcustomplot.h:2517
QSharedPointer< QCPAxisTicker > ticker() const
Definition: qcustomplot.h:2391
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)
SelectableParts selectedParts() const
Definition: qcustomplot.h:2417
QPen getBasePen() const
QColor getTickLabelColor() const
SelectableParts mSelectedParts
Definition: qcustomplot.h:2521
QColor mSelectedLabelColor
Definition: qcustomplot.h:2528
QPen mSelectedTickPen
Definition: qcustomplot.h:2544
void setSelectedTickLabelColor(const QColor &color)
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:2562
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)
QPen getSubTickPen() const
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
@ 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)
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details)
int subTickLengthOut() const
QVector< QString > mTickVectorLabels
Definition: qcustomplot.h:2556
void setRangeUpper(double upper)
int mNumberPrecision
Definition: qcustomplot.h:2535
int tickLengthIn() const
ScaleType scaleType() const
Definition: qcustomplot.h:2388
int tickLengthOut() const
bool mDragging
Definition: qcustomplot.h:2560
QList< QCPGraph * > graphs() const
QPen mBasePen
Definition: qcustomplot.h:2522
ScaleType mScaleType
Definition: qcustomplot.h:2549
bool subTicks() const
Definition: qcustomplot.h:2405
void setTickPen(const QPen &pen)
QSharedPointer< QCPAxisTicker > mTicker
Definition: qcustomplot.h:2554
QFont mTickLabelFont
Definition: qcustomplot.h:2533
Q_SLOT void setScaleType(QCPAxis::ScaleType type)
bool tickLabels() const
Definition: qcustomplot.h:2393
QFont mSelectedLabelFont
Definition: qcustomplot.h:2527
void setNumberFormat(const QString &formatCode)
AxisType mAxisType
Definition: qcustomplot.h:2516
QString mLabel
Definition: qcustomplot.h:2526
QColor getLabelColor() const
QFont getLabelFont() const
void setSelectedBasePen(const QPen &pen)
Q_SLOT void setRange(const QCPRange &range)
void setSubTickPen(const QPen &pen)
int offset() const
bool mNumberBeautifulPowers
Definition: qcustomplot.h:2537
double coordToPixel(double value) const
bool mSubTicks
Definition: qcustomplot.h:2541
void setTickLabelPadding(int padding)
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
int subTickLengthIn() const
void setSubTicks(bool show)
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:6413
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
QRectF getBarRect(double key, double value) const
double getStackedBaseValue(double key, bool positive) const
QCPBars * barBelow() const
Definition: qcustomplot.h:6487
double mStackingGap
Definition: qcustomplot.h:6531
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
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
double mBaseValue
Definition: qcustomplot.h:6530
QCPBarsGroup * mBarsGroup
Definition: qcustomplot.h:6529
virtual QPointF dataPixelPosition(int index) const
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
double width() const
Definition: qcustomplot.h:6482
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const
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 setStackingGap(double pixels)
void setBarsGroup(QCPBarsGroup *barsGroup)
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)
bool stopsUseAlpha() const
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
unsigned char * mAlpha
Definition: qcustomplot.h:6775
QCPRange mValueRange
Definition: qcustomplot.h:6770
double data(double key, double value)
void fill(double z)
bool createAlpha(bool initializeOpaque=true)
unsigned char alpha(int keyIndex, int valueIndex)
QCPRange valueRange() const
Definition: qcustomplot.h:6734
int valueSize() const
Definition: qcustomplot.h:6732
void setCell(int keyIndex, int valueIndex, double z)
void fillAlpha(unsigned char alpha)
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 setAlpha(int keyIndex, int valueIndex, unsigned char alpha)
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 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
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
@ lsLine
Data points are connected with a straight line.
Definition: qcustomplot.h:6215
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
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)
void getScatters(QVector< QPointF > *scatters, const QCPDataRange &dataRange, double scatterWidth) const
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
void setScatterSkip(int skip)
int mScatterSkip
Definition: qcustomplot.h:6265
virtual ~QCPCurve()
double pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const
virtual void drawCurveLine(QCPPainter *painter, const QVector< QPointF > &lines) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
LineStyle mLineStyle
Definition: qcustomplot.h:6266
void getCurveLines(QVector< QPointF > *lines, const QCPDataRange &dataRange, double penWidth) const
bool mayTraverse(int prevRegion, int currentRegion) const
bool getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const
The generic data container for one-dimensional plottables.
Definition: qcustomplot.h:2863
QVector< DataType >::const_iterator const_iterator
Definition: qcustomplot.h:2865
Describes a data range given by begin and end index.
Definition: qcustomplot.h:1105
bool contains(const QCPDataRange &other) const
void setEnd(int end)
Definition: qcustomplot.h:1125
QCPDataRange adjusted(int changeBegin, int changeEnd) const
Definition: qcustomplot.h:1133
QCPDataRange expanded(const QCPDataRange &other) const
void setBegin(int begin)
Definition: qcustomplot.h:1124
QCPDataRange intersection(const QCPDataRange &other) const
bool intersects(const QCPDataRange &other) const
QCPDataRange bounded(const QCPDataRange &other) const
bool isEmpty() const
Definition: qcustomplot.h:1129
int size() const
Definition: qcustomplot.h:1120
int begin() const
Definition: qcustomplot.h:1118
int end() const
Definition: qcustomplot.h:1119
Describes a data set by holding multiple QCPDataRange instances.
Definition: qcustomplot.h:1145
void enforceType(QCP::SelectionType type)
QCPDataSelection & operator+=(const QCPDataSelection &other)
void addDataRange(const QCPDataRange &dataRange, bool simplify=true)
bool operator==(const QCPDataSelection &other) const
QCPDataSelection & operator-=(const QCPDataSelection &other)
QCPDataRange dataRange(int index=0) const
bool isEmpty() const
Definition: qcustomplot.h:1185
QCPDataRange span() const
bool contains(const QCPDataSelection &other) const
int dataRangeCount() const
Definition: qcustomplot.h:1176
QList< QCPDataRange > dataRanges() const
Definition: qcustomplot.h:1179
int dataPointCount() const
QCPDataSelection inverse(const QCPDataRange &outerRange) const
QCPDataSelection intersection(const QCPDataRange &other) const
Holds the data of one single error bar for QCPErrorBars.
Definition: qcustomplot.h:7067
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const
virtual void draw(QCPPainter *painter)
virtual QCPRange dataValueRange(int index) const
QPointer< QCPAbstractPlottable > mDataPlottable
Definition: qcustomplot.h:7180
virtual double dataSortKey(int index) const
void getDataSegments(QList< QCPDataRange > &selectedSegments, QList< QCPDataRange > &unselectedSegments) const
void setSymbolGap(double pixels)
double symbolGap() const
Definition: qcustomplot.h:7135
double mWhiskerWidth
Definition: qcustomplot.h:7182
bool errorBarVisible(int index) const
virtual int findBegin(double sortKey, bool expandedRange=true) const
double mSymbolGap
Definition: qcustomplot.h:7183
QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual int findEnd(double sortKey, bool expandedRange=true) const
virtual QPointF dataPixelPosition(int index) const
virtual ~QCPErrorBars()
QSharedPointer< QCPErrorBarsDataContainer > mDataContainer
Definition: qcustomplot.h:7179
virtual double dataMainValue(int index) const
double pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const
void setData(QSharedPointer< QCPErrorBarsDataContainer > data)
virtual bool sortKeyIsMainKey() const
bool rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const
void setDataPlottable(QCPAbstractPlottable *plottable)
void getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
void addData(const QVector< double > &error)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector< QLineF > &backbones, QVector< QLineF > &whiskers) const
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const
void setWhiskerWidth(double pixels)
virtual int dataCount() const
virtual double dataMainKey(int index) const
QSharedPointer< QCPErrorBarsDataContainer > data() const
Definition: qcustomplot.h:7127
void setErrorType(ErrorType type)
ErrorType mErrorType
Definition: qcustomplot.h:7181
Holds the data of one single data point for QCPFinancial.
Definition: qcustomplot.h:6874
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
double width() const
Definition: qcustomplot.h:6961
void setTwoColored(bool twoColored)
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const
void setWidthType(WidthType widthType)
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)
double getPixelWidth(double key, double keyPixel) const
QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
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
WidthType mWidthType
Definition: qcustomplot.h:7021
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
WidthType widthType() const
Definition: qcustomplot.h:6962
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)
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)
@ wtAbsolute
width is in absolute pixels
Definition: qcustomplot.h:6932
@ wtAxisRectRatio
width is given by a fraction of the axis rect size
Definition: qcustomplot.h:6934
QRectF selectionHitBox(QCPFinancialDataContainer::const_iterator it) const
bool twoColored() const
Definition: qcustomplot.h:6963
void setPenNegative(const QPen &pen)
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:5963
A plottable representing a graph in a plot.
Definition: qcustomplot.h:5996
QVector< QPointF > dataToLines(const QVector< QCPGraphData > &data) const
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
QVector< QCPDataRange > getNonNanSegments(const QVector< QPointF > *lineData, Qt::Orientation keyOrientation) const
void setScatterStyle(const QCPScatterStyle &style)
QPointF getFillBasePoint(QPointF matchingDataPoint) const
void setScatterSkip(int skip)
void setData(QSharedPointer< QCPGraphDataContainer > data)
QVector< QPointF > dataToStepLeftLines(const QVector< QCPGraphData > &data) const
virtual void getOptimizedLineData(QVector< QCPGraphData > *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
virtual void drawImpulsePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
QVector< QPointF > dataToStepCenterLines(const QVector< QCPGraphData > &data) const
QPointer< QCPGraph > mChannelFillGraph
Definition: qcustomplot.h:6083
QVector< QPointF > dataToImpulseLines(const QVector< QCPGraphData > &data) const
void setChannelFillGraph(QCPGraph *targetGraph)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const
QVector< QPair< QCPDataRange, QCPDataRange > > getOverlappingSegments(QVector< QCPDataRange > thisSegments, const QVector< QPointF > *thisData, QVector< QCPDataRange > otherSegments, const QVector< QPointF > *otherData) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:6081
virtual void drawLinePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
int mScatterSkip
Definition: qcustomplot.h:6082
void setLineStyle(LineStyle ls)
virtual void getOptimizedScatterData(QVector< QCPGraphData > *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const
void getLines(QVector< QPointF > *lines, const QCPDataRange &dataRange) const
int findIndexBelowY(const QVector< QPointF > *data, double y) const
virtual void draw(QCPPainter *painter)
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lines) const
void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
LineStyle mLineStyle
Definition: qcustomplot.h:6080
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
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lineData, QCPDataRange thisSegment, const QVector< QPointF > *otherData, QCPDataRange otherSegment) const
void getScatters(QVector< QPointF > *scatters, const QCPDataRange &dataRange) const
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
QVector< QPointF > dataToStepRightLines(const QVector< QCPGraphData > &data) const
void setAdaptiveSampling(bool enabled)
bool segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > &scatters, const QCPScatterStyle &style) const
QSharedPointer< QCPGraphDataContainer > data() const
Definition: qcustomplot.h:6040
@ 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)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
virtual ~QCPGraph()
const QPolygonF getFillPolygon(const QVector< QPointF > *lineData, QCPDataRange segment) 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 QPointF pixelPosition() const
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
QSet< QCPItemPosition * > mChildrenX
Definition: qcustomplot.h:3969
void addChildX(QCPItemPosition *pos)
void addChildY(QCPItemPosition *pos)
void setSelectedPen(const QPen &pen)
QCPItemBracket(QCustomPlot *parentPlot)
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)
virtual QPointF anchorPixelPosition(int anchorId) const
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
virtual QPointF anchorPixelPosition(int anchorId) const
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
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
virtual QPointF anchorPixelPosition(int anchorId) const
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:7658
QPixmap mPixmap
Definition: qcustomplot.h:7677
bool mScaledPixmapInvalidated
Definition: qcustomplot.h:7680
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
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)
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
virtual QPointF pixelPosition() const
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
double key() const
Definition: qcustomplot.h:4039
void setPixelPosition(const QPointF &pixelPosition)
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)
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
virtual QPointF anchorPixelPosition(int anchorId) const
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
QPen mainPen() const
virtual ~QCPItemRect()
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)
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)
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)
void setColor(const QColor &color)
virtual QPointF anchorPixelPosition(int anchorId) const
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
LayerMode mMode
Definition: qcustomplot.h:868
QList< QCPLayerable * > children() const
Definition: qcustomplot.h:850
bool mVisible
Definition: qcustomplot.h:867
QString name() const
Definition: qcustomplot.h:848
LayerMode mode() const
Definition: qcustomplot.h:852
void drawToPaintBuffer()
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 setMode(LayerMode mode)
QWeakPointer< QCPAbstractPaintBuffer > mPaintBuffer
Definition: qcustomplot.h:871
void draw(QCPPainter *painter)
void setVisible(bool visible)
void removeChild(QCPLayerable *layerable)
bool visible() const
Definition: qcustomplot.h:851
int index() const
Definition: qcustomplot.h:849
void replot()
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 void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
virtual ~QCPLayerable()
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:904
virtual void wheelEvent(QWheelEvent *event)
void setAntialiased(bool enabled)
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
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
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
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details)
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)
@ scrInnerRect
Minimum/Maximum size constraints apply to inner rect.
Definition: qcustomplot.h:1449
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
void setSizeConstraintRect(SizeConstraintRect constraintRect)
void setOuterRect(const QRect &rect)
virtual QSize minimumOuterSizeHint() const
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)
QSize minimumSize() const
Definition: qcustomplot.h:1466
SizeConstraintRect sizeConstraintRect() const
Definition: qcustomplot.h:1468
void setMaximumSize(const QSize &size)
virtual void layoutChanged()
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)
QCPLayout * mParentLayout
Definition: qcustomplot.h:1503
SizeConstraintRect mSizeConstraintRect
Definition: qcustomplot.h:1505
void setAutoMargins(QCP::MarginSides sides)
virtual QSize maximumOuterSizeHint() const
QMargins margins() const
Definition: qcustomplot.h:1463
QCP::MarginSides mAutoMargins
Definition: qcustomplot.h:1508
QSize maximumSize() const
Definition: qcustomplot.h:1467
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
FillOrder mFillOrder
Definition: qcustomplot.h:1664
void setColumnSpacing(int pixels)
void insertRow(int newIndex)
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
void indexToRowCol(int index, int &row, int &column) const
QCPLayoutElement * element(int row, int column) const
virtual bool take(QCPLayoutElement *element)
int rowColToIndex(int row, int column) const
void setColumnStretchFactors(const QList< double > &factors)
FillOrder fillOrder() const
Definition: qcustomplot.h:1620
virtual int elementCount() const
Definition: qcustomplot.h:1634
void setRowStretchFactor(int row, double factor)
virtual QSize minimumOuterSizeHint() const
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
QList< double > mColumnStretchFactors
Definition: qcustomplot.h:1660
void setRowSpacing(int pixels)
bool hasElement(int row, int column)
virtual QSize maximumOuterSizeHint() const
void setWrap(int count)
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
void setFillOrder(FillOrder order, bool rearrange=true)
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)
static QSize getFinalMinimumOuterSize(const QCPLayoutElement *el)
virtual bool take(QCPLayoutElement *element)=0
virtual QList< QCPLayoutElement * > elements(bool recursive) const
static QSize getFinalMaximumOuterSize(const QCPLayoutElement *el)
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
A paint buffer based on QPixmap, using software raster rendering.
Definition: qcustomplot.h:743
virtual QCPPainter * startPainting()
virtual ~QCPPaintBufferPixmap()
virtual void draw(QCPPainter *painter) const
virtual void reallocateBuffer()
void clear(const QColor &color)
QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio)
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)
Defines an abstract interface for one-dimensional plottables.
Definition: qcustomplot.h:4529
virtual int dataCount() const =0
virtual double dataMainKey(int index) const =0
virtual double dataMainValue(int index) const =0
A legend item representing a plottable with an icon and the plottable name.
Definition: qcustomplot.h:5583
QColor getTextColor() const
virtual void draw(QCPPainter *painter)
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:5593
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
virtual QSize minimumOuterSizeHint() const
QPen getIconBorderPen() const
Represents the range an axis is encompassing.
Definition: qcustomplot.h:975
void expand(const QCPRange &otherRange)
QCPRange bounded(double lowerBound, double upperBound) const
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
void normalize()
Definition: qcustomplot.h:1016
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:2695
double size() const
Definition: qcustomplot.h:2803
bool isPenDefined() const
Definition: qcustomplot.h:2822
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)
void setFromOther(const QCPScatterStyle &other, ScatterProperties properties)
QPainterPath mCustomPath
Definition: qcustomplot.h:2835
@ spShape
0x08 The shape property, see setShape
Definition: qcustomplot.h:2716
@ spSize
0x04 The size property, see setSize
Definition: qcustomplot.h:2714
@ spPen
0x01 The pen property, see setPen
Definition: qcustomplot.h:2710
@ spBrush
0x02 The brush property, see setBrush
Definition: qcustomplot.h:2712
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
void setBracketStyle(BracketStyle style)
virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection)
void setBracketBrush(const QBrush &brush)
virtual void drawBracket(QCPPainter *painter, int direction) const
void setTangentToData(bool enabled)
QPointF getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const
@ bsPlus
A plus is drawn.
Definition: qcustomplot.h:5316
@ bsSquareBracket
A square bracket is drawn.
Definition: qcustomplot.h:5308
double getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const
void setBracketPen(const QPen &pen)
void setTangentAverage(int pointCount)
void setBracketHeight(int height)
Controls how a plottable's data selection is drawn.
Definition: qcustomplot.h:3764
QCPScatterStyle getFinalScatterStyle(const QCPScatterStyle &unselectedStyle) const
void applyBrush(QCPPainter *painter) const
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:3805
virtual void copyFrom(const QCPSelectionDecorator *other)
virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection)
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:3802
void applyPen(QCPPainter *painter) const
QBrush brush() const
Definition: qcustomplot.h:3772
void setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties)
QCPScatterStyle scatterStyle() const
Definition: qcustomplot.h:3773
void setBrush(const QBrush &brush)
QCPScatterStyle::ScatterProperties usedScatterProperties() const
Definition: qcustomplot.h:3774
virtual ~QCPSelectionDecorator()
void setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties=QCPScatterStyle::spPen)
void setPen(const QPen &pen)
QCPScatterStyle::ScatterProperties mUsedScatterProperties
Definition: qcustomplot.h:3803
virtual bool registerWithPlottable(QCPAbstractPlottable *plottable)
Provides rect/rubber-band data selection and range zoom interaction.
Definition: qcustomplot.h:1325
void accepted(const QRect &rect, QMouseEvent *event)
virtual void keyPressEvent(QKeyEvent *event)
void changed(const QRect &rect, QMouseEvent *event)
QCPRange range(const QCPAxis *axis) const
virtual void startSelection(QMouseEvent *event)
virtual void endSelection(QMouseEvent *event)
void started(QMouseEvent *event)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
virtual void moveSelection(QMouseEvent *event)
QBrush brush() const
Definition: qcustomplot.h:1335
void setBrush(const QBrush &brush)
QPen pen() const
Definition: qcustomplot.h:1334
bool isActive() const
Definition: qcustomplot.h:1336
void setPen(const QPen &pen)
QCPSelectionRect(QCustomPlot *parentPlot)
virtual ~QCPSelectionRect()
void canceled(const QRect &rect, QInputEvent *event)
Q_SLOT void cancel()
virtual void draw(QCPPainter *painter)
Holds the data of one single data point for QCPStatisticalBox.
Definition: qcustomplot.h:6558
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
virtual void drawStatisticalBox(QCPPainter *painter, QCPStatisticalBoxDataContainer::const_iterator it, const QCPScatterStyle &outlierStyle) 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)
QVector< double > outliers() const
Definition: qcustomplot.h:3543
void setWidth(double width)
void getVisibleDataBounds(QCPStatisticalBoxDataContainer::const_iterator &begin, QCPStatisticalBoxDataContainer::const_iterator &end) const
QVector< QLineF > getWhiskerBackboneLines(QCPStatisticalBoxDataContainer::const_iterator it) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void setWhiskerPen(const QPen &pen)
double maximum() const
Definition: qcustomplot.h:3542
void setWhiskerAntialiased(bool enabled)
void setMedianPen(const QPen &pen)
QSharedPointer< QCPStatisticalBoxDataContainer > data() const
Definition: qcustomplot.h:6622
virtual void draw(QCPPainter *painter)
QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
void addData(const QVector< double > &keys, const QVector< double > &minimum, const QVector< double > &lowerQuartile, const QVector< double > &median, const QVector< double > &upperQuartile, const QVector< double > &maximum, bool alreadySorted=false)
QRectF getQuartileBox(QCPStatisticalBoxDataContainer::const_iterator it) const
void setWhiskerBarPen(const QPen &pen)
double minimum() const
Definition: qcustomplot.h:3538
double median() const
Definition: qcustomplot.h:3540
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
QCPScatterStyle outlierStyle() const
Definition: qcustomplot.h:6631
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const
QVector< QLineF > getWhiskerBarLines(QCPStatisticalBoxDataContainer::const_iterator it) const
double lowerQuartile() const
Definition: qcustomplot.h:3539
double upperQuartile() const
Definition: qcustomplot.h:3541
void setFont(const QFont &font)
void setSelectedFont(const QFont &font)
bool selected() const
Definition: qcustomplot.h:5786
QRect mTextBoundingRect
Definition: qcustomplot.h:5823
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
virtual void deselectEvent(bool *selectionStateChanged)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Q_SLOT void setSelectable(bool selectable)
QString text() const
Definition: qcustomplot.h:5779
virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
void selectionChanged(bool selected)
void setTextColor(const QColor &color)
QColor mainTextColor() const
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
virtual void draw(QCPPainter *painter)
QFont font() const
Definition: qcustomplot.h:5781
bool selectable() const
Definition: qcustomplot.h:5785
virtual QSize minimumOuterSizeHint() const
void doubleClicked(QMouseEvent *event)
void setTextFlags(int flags)
Q_SLOT void setSelected(bool selected)
void setSelectedTextColor(const QColor &color)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
void setText(const QString &text)
void selectableChanged(bool selectable)
void clicked(QMouseEvent *event)
QColor mSelectedTextColor
Definition: qcustomplot.h:5822
QCPTextElement(QCustomPlot *parentPlot)
QFont mainFont() const
virtual QSize maximumOuterSizeHint() const
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details)
Represents two doubles as a mathematical 2D vector.
Definition: qcustomplot.h:543
QCPVector2D perpendicular() const
Definition: qcustomplot.h:569
double length() const
Definition: qcustomplot.h:561
double distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const
double dot(const QCPVector2D &vec) const
Definition: qcustomplot.h:570
QCPVector2D & operator-=(const QCPVector2D &vector)
double x() const
Definition: qcustomplot.h:551
double y() const
Definition: qcustomplot.h:552
QCPVector2D normalized() const
double lengthSquared() const
Definition: qcustomplot.h:562
QCPVector2D & operator+=(const QCPVector2D &vector)
QCPVector2D & operator*=(double factor)
QPointF toPointF() const
Definition: qcustomplot.h:564
bool isNull() const
Definition: qcustomplot.h:566
void normalize()
QPoint toPoint() const
Definition: qcustomplot.h:563
double distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const
QCPVector2D & operator/=(double divisor)
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 setSelectionRect(QCPSelectionRect *selectionRect)
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)
void setBufferDevicePixelRatio(double ratio)
int itemCount() const
QRect viewport() const
Definition: qcustomplot.h:4232
void toPainter(QCPPainter *painter, int width=0, int height=0)
QPointer< QCPLayerable > mMouseEventLayerable
Definition: qcustomplot.h:4459
void setupPaintBuffers()
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
QVariant mMouseSignalLayerableDetails
Definition: qcustomplot.h:4462
QCPAbstractPlottable * plottable(int index)
friend class QCPAxisRect
Definition: qcustomplot.h:4516
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)
QCPSelectionRect * mSelectionRect
Definition: qcustomplot.h:4452
QCPAxisRect * axisRectAt(const QPointF &pos) const
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
virtual Q_SLOT void processRectZoom(QRect rect, QMouseEvent *event)
QList< QSharedPointer< QCPAbstractPaintBuffer > > mPaintBuffers
Definition: qcustomplot.h:4456
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
double mBufferDevicePixelRatio
Definition: qcustomplot.h:4431
friend class QCPLegend
Definition: qcustomplot.h:4513
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
virtual void updateLayout()
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
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)
void setOpenGl(bool enabled, int multisampling=16)
QList< QCPAxis * > selectedAxes() const
void updateLayerIndices() const
void setSelectionRectMode(QCP::SelectionRectMode mode)
void plottableDoubleClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)
bool setupOpenGl()
virtual void axisRemoved(QCPAxis *axis)
int axisRectCount() const
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
bool removeGraph(QCPGraph *graph)
QCPAbstractPaintBuffer * createPaintBuffer()
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)
QPixmap toPixmap(int width=0, int height=0, double scale=1.0)
QCPGraph * graph() const
QList< QCPAbstractPlottable * > mPlottables
Definition: qcustomplot.h:4434
int mOpenGlMultisamples
Definition: qcustomplot.h:4465
bool mAutoAddPlottableToLegend
Definition: qcustomplot.h:4433
bool mOpenGlCacheLabelsBackup
Definition: qcustomplot.h:4467
bool mReplotting
Definition: qcustomplot.h:4463
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:4447
QCPLayoutGrid * mPlotLayout
Definition: qcustomplot.h:4432
int mSelectionTolerance
Definition: qcustomplot.h:4441
virtual void mousePressEvent(QMouseEvent *event)
QCP::SelectionRectMode mSelectionRectMode
Definition: qcustomplot.h:4451
void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
QCPAbstractItem * itemAt(const QPointF &pos, bool onlySelectable=false) const
QRect mViewport
Definition: qcustomplot.h:4430
virtual Q_SLOT void processRectSelection(QRect rect, QMouseEvent *event)
virtual Q_SLOT void processPointSelection(QMouseEvent *event)
QPoint mMousePressPos
Definition: qcustomplot.h:4457
virtual void mouseMoveEvent(QMouseEvent *event)
void mouseWheel(QWheelEvent *event)
void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
bool mNoAntialiasingOnDrag
Definition: qcustomplot.h:4442
QList< QCPLegend * > selectedLegends() const
void mouseRelease(QMouseEvent *event)
QList< QCPLayerable * > layerableListAt(const QPointF &pos, bool onlySelectable, QList< QVariant > *selectionDetails=0) const
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
bool mReplotQueued
Definition: qcustomplot.h:4464
bool registerGraph(QCPGraph *graph)
QList< QCPLayer * > mLayers
Definition: qcustomplot.h:4438
friend class QCPGraph
Definition: qcustomplot.h:4518
bool mMouseHasMoved
Definition: qcustomplot.h:4458
QList< QCPGraph * > selectedGraphs() const
bool hasInvalidatedPaintBuffers()
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())
QVariant mMouseEventLayerableDetails
Definition: qcustomplot.h:4461
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)
QCPSelectionRect * selectionRect() const
Definition: qcustomplot.h:4257
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
void setAutoAddPlottableToLegend(bool on)
QCPAxis * xAxis2
Definition: qcustomplot.h:4393
QCP::AntialiasedElements mOpenGlAntialiasedElementsBackup
Definition: qcustomplot.h:4466
QCPAbstractPlottable * plottable()
QPointer< QCPLayerable > mMouseSignalLayerable
Definition: qcustomplot.h:4460
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
bool registerPlottable(QCPAbstractPlottable *plottable)
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
bool registerItem(QCPAbstractItem *item)
void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true)
QList< QCPAbstractItem * > selectedItems() const
a[190]
const double * e
GraphType data
Definition: graph_cut.cc:138
normal_z y
normal_z rgb
normal_z x
normal_z z
bool isInvalidData(double value)
Definition: qcustomplot.h:425
ExportPen
Definition: qcustomplot.h:193
@ epNoCosmetic
Definition: qcustomplot.h:194
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
@ phImmediateRefresh
Definition: qcustomplot.h:299
@ phCacheLabels
Definition: qcustomplot.h:308
ResolutionUnit
Definition: qcustomplot.h:178
@ ruDotsPerCentimeter
Definition: qcustomplot.h:181
@ ruDotsPerMeter
Resolution is given in dots per meter (dpm)
Definition: qcustomplot.h:179
@ ruDotsPerInch
Resolution is given in dots per inch (DPI/PPI)
Definition: qcustomplot.h:184
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
SelectionType
Definition: qcustomplot.h:402
@ stMultipleDataRanges
Definition: qcustomplot.h:414
@ stDataRange
Definition: qcustomplot.h:411
@ stNone
The plottable is not selectable.
Definition: qcustomplot.h:403
@ stSingleData
One individual data point can be selected at a time.
Definition: qcustomplot.h:409
@ stWhole
Definition: qcustomplot.h:405
SelectionRectMode
Definition: qcustomplot.h:359
@ srmSelect
Definition: qcustomplot.h:369
@ srmZoom
Definition: qcustomplot.h:364
@ srmNone
Definition: qcustomplot.h:360
AntialiasedElement
Definition: qcustomplot.h:249
@ aeAxes
0x0001 Axis base line and tick marks
Definition: qcustomplot.h:250
@ aeOther
Definition: qcustomplot.h:273
@ aeItems
0x0040 Main lines of items
Definition: qcustomplot.h:262
@ aeScatters
Definition: qcustomplot.h:264
@ aeLegendItems
0x0010 Legend items
Definition: qcustomplot.h:258
@ 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
SignDomain
Definition: qcustomplot.h:210
@ sdNegative
The negative sign domain, i.e. numbers smaller than zero.
Definition: qcustomplot.h:211
@ sdPositive
The positive sign domain, i.e. numbers greater than zero.
Definition: qcustomplot.h:215
@ sdBoth
Both sign domains, including zero, i.e. all numbers.
Definition: qcustomplot.h:213
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)