qcustomplot.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 ** **
3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
4 ** Copyright (C) 2011-2018 Emanuel Eichhammer **
5 ** **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
8 ** the Free Software Foundation, either version 3 of the License, or **
9 ** (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, **
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program. If not, see http://www.gnu.org/licenses/. **
18 ** **
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Date: 25.06.18 **
23 ** Version: 2.0.1 **
24 ****************************************************************************/
25 
27 
28 
29 /* including file 'src/vector2d.cpp', size 7340 */
30 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
31 
35 
43 /* start documentation of inline functions */
44 
106 /* end documentation of inline functions */
107 
112  mX(0),
113  mY(0)
114 {
115 }
116 
121 QCPVector2D::QCPVector2D(double x, double y) :
122  mX(x),
123  mY(y)
124 {
125 }
126 
131 QCPVector2D::QCPVector2D(const QPoint &point) :
132  mX(point.x()),
133  mY(point.y())
134 {
135 }
136 
141 QCPVector2D::QCPVector2D(const QPointF &point) :
142  mX(point.x()),
143  mY(point.y())
144 {
145 }
146 
153 {
154  double len = length();
155  mX /= len;
156  mY /= len;
157 }
158 
165 {
166  QCPVector2D result(mX, mY);
167  result.normalize();
168  return result;
169 }
170 
178 double QCPVector2D::distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const
179 {
180  QCPVector2D v(end-start);
181  double vLengthSqr = v.lengthSquared();
182  if (!qFuzzyIsNull(vLengthSqr))
183  {
184  double mu = v.dot(*this-start)/vLengthSqr;
185  if (mu < 0)
186  return (*this-start).lengthSquared();
187  else if (mu > 1)
188  return (*this-end).lengthSquared();
189  else
190  return ((start + mu*v)-*this).lengthSquared();
191  } else
192  return (*this-start).lengthSquared();
193 }
194 
202 double QCPVector2D::distanceSquaredToLine(const QLineF &line) const
203 {
204  return distanceSquaredToLine(QCPVector2D(line.p1()), QCPVector2D(line.p2()));
205 }
206 
213 double QCPVector2D::distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const
214 {
215  return qAbs((*this-base).dot(direction.perpendicular()))/direction.length();
216 }
217 
223 {
224  mX *= factor;
225  mY *= factor;
226  return *this;
227 }
228 
234 {
235  mX /= divisor;
236  mY /= divisor;
237  return *this;
238 }
239 
244 {
245  mX += vector.mX;
246  mY += vector.mY;
247  return *this;
248 }
249 
254 {
255  mX -= vector.mX;
256  mY -= vector.mY;
257  return *this;
258 }
259 /* end of 'src/vector2d.cpp' */
260 
261 
262 /* including file 'src/painter.cpp', size 8670 */
263 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
264 
268 
286  QPainter(),
287  mModes(pmDefault),
288  mIsAntialiasing(false)
289 {
290  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
291  // a call to begin() will follow
292 }
293 
300 QCPPainter::QCPPainter(QPaintDevice *device) :
301  QPainter(device),
302  mModes(pmDefault),
303  mIsAntialiasing(false)
304 {
305 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
306  if (isActive())
307  setRenderHint(QPainter::NonCosmeticDefaultPen);
308 #endif
309 }
310 
317 void QCPPainter::setPen(const QPen &pen)
318 {
319  QPainter::setPen(pen);
320  if (mModes.testFlag(pmNonCosmetic))
321  makeNonCosmetic();
322 }
323 
331 void QCPPainter::setPen(const QColor &color)
332 {
333  QPainter::setPen(color);
334  if (mModes.testFlag(pmNonCosmetic))
335  makeNonCosmetic();
336 }
337 
345 void QCPPainter::setPen(Qt::PenStyle penStyle)
346 {
347  QPainter::setPen(penStyle);
348  if (mModes.testFlag(pmNonCosmetic))
349  makeNonCosmetic();
350 }
351 
360 void QCPPainter::drawLine(const QLineF &line)
361 {
362  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
363  QPainter::drawLine(line);
364  else
365  QPainter::drawLine(line.toLine());
366 }
367 
374 void QCPPainter::setAntialiasing(bool enabled)
375 {
376  setRenderHint(QPainter::Antialiasing, enabled);
377  if (mIsAntialiasing != enabled)
378  {
379  mIsAntialiasing = enabled;
380  if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
381  {
382  if (mIsAntialiasing)
383  translate(0.5, 0.5);
384  else
385  translate(-0.5, -0.5);
386  }
387  }
388 }
389 
394 void QCPPainter::setModes(QCPPainter::PainterModes modes)
395 {
396  mModes = modes;
397 }
398 
410 bool QCPPainter::begin(QPaintDevice *device)
411 {
412  bool result = QPainter::begin(device);
413 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
414  if (result)
415  setRenderHint(QPainter::NonCosmeticDefaultPen);
416 #endif
417  return result;
418 }
419 
426 {
427  if (!enabled && mModes.testFlag(mode))
428  mModes &= ~mode;
429  else if (enabled && !mModes.testFlag(mode))
430  mModes |= mode;
431 }
432 
442 {
444  QPainter::save();
445 }
446 
456 {
457  if (!mAntialiasingStack.isEmpty())
459  else
460  qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
461  QPainter::restore();
462 }
463 
469 {
470  if (qFuzzyIsNull(pen().widthF()))
471  {
472  QPen p = pen();
473  p.setWidth(1);
474  QPainter::setPen(p);
475  }
476 }
477 /* end of 'src/painter.cpp' */
478 
479 
480 /* including file 'src/paintbuffer.cpp', size 18502 */
481 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
482 
486 
507 /* start documentation of pure virtual functions */
508 
550 /* end documentation of pure virtual functions */
551 /* start documentation of inline functions */
552 
563 /* end documentation of inline functions */
564 
570 QCPAbstractPaintBuffer::QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio) :
571  mSize(size),
572  mDevicePixelRatio(devicePixelRatio),
573  mInvalidated(true)
574 {
575 }
576 
578 {
579 }
580 
590 {
591  if (mSize != size)
592  {
593  mSize = size;
595  }
596 }
597 
614 {
616 }
617 
628 {
629  if (!qFuzzyCompare(ratio, mDevicePixelRatio))
630  {
631 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
632  mDevicePixelRatio = ratio;
634 #else
635  qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
636  mDevicePixelRatio = 1.0;
637 #endif
638  }
639 }
640 
644 
657  QCPAbstractPaintBuffer(size, devicePixelRatio)
658 {
660 }
661 
663 {
664 }
665 
666 /* inherits documentation from base class */
668 {
669  QCPPainter *result = new QCPPainter(&mBuffer);
670  result->setRenderHint(QPainter::HighQualityAntialiasing);
671  return result;
672 }
673 
674 /* inherits documentation from base class */
676 {
677  if (painter && painter->isActive())
678  painter->drawPixmap(0, 0, mBuffer);
679  else
680  qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
681 }
682 
683 /* inherits documentation from base class */
684 void QCPPaintBufferPixmap::clear(const QColor &color)
685 {
686  mBuffer.fill(color);
687 }
688 
689 /* inherits documentation from base class */
691 {
692  setInvalidated();
693  if (!qFuzzyCompare(1.0, mDevicePixelRatio))
694  {
695 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
696  mBuffer = QPixmap(mSize*mDevicePixelRatio);
697  mBuffer.setDevicePixelRatio(mDevicePixelRatio);
698 #else
699  qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
700  mDevicePixelRatio = 1.0;
701  mBuffer = QPixmap(mSize);
702 #endif
703  } else
704  {
705  mBuffer = QPixmap(mSize);
706  }
707 }
708 
709 
710 #ifdef QCP_OPENGL_PBUFFER
711 
734 QCPPaintBufferGlPbuffer::QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples) :
736  mGlPBuffer(0),
737  mMultisamples(qMax(0, multisamples))
738 {
739  QCPPaintBufferGlPbuffer::reallocateBuffer();
740 }
741 
742 QCPPaintBufferGlPbuffer::~QCPPaintBufferGlPbuffer()
743 {
744  if (mGlPBuffer)
745  delete mGlPBuffer;
746 }
747 
748 /* inherits documentation from base class */
749 QCPPainter *QCPPaintBufferGlPbuffer::startPainting()
750 {
751  if (!mGlPBuffer->isValid())
752  {
753  qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
754  return 0;
755  }
756 
757  QCPPainter *result = new QCPPainter(mGlPBuffer);
758  result->setRenderHint(QPainter::HighQualityAntialiasing);
759  return result;
760 }
761 
762 /* inherits documentation from base class */
763 void QCPPaintBufferGlPbuffer::draw(QCPPainter *painter) const
764 {
765  if (!painter || !painter->isActive())
766  {
767  qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
768  return;
769  }
770  if (!mGlPBuffer->isValid())
771  {
772  qDebug() << Q_FUNC_INFO << "OpenGL pbuffer isn't valid, reallocateBuffer was not called?";
773  return;
774  }
775  painter->drawImage(0, 0, mGlPBuffer->toImage());
776 }
777 
778 /* inherits documentation from base class */
779 void QCPPaintBufferGlPbuffer::clear(const QColor &color)
780 {
781  if (mGlPBuffer->isValid())
782  {
783  mGlPBuffer->makeCurrent();
784  glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
785  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
786  mGlPBuffer->doneCurrent();
787  } else
788  qDebug() << Q_FUNC_INFO << "OpenGL pbuffer invalid or context not current";
789 }
790 
791 /* inherits documentation from base class */
792 void QCPPaintBufferGlPbuffer::reallocateBuffer()
793 {
794  if (mGlPBuffer)
795  delete mGlPBuffer;
796 
797  QGLFormat format;
798  format.setAlpha(true);
799  format.setSamples(mMultisamples);
800  mGlPBuffer = new QGLPixelBuffer(mSize, format);
801 }
802 #endif // QCP_OPENGL_PBUFFER
803 
804 
805 #ifdef QCP_OPENGL_FBO
806 
830 QCPPaintBufferGlFbo::QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer<QOpenGLContext> glContext, QWeakPointer<QOpenGLPaintDevice> glPaintDevice) :
832  mGlContext(glContext),
833  mGlPaintDevice(glPaintDevice),
834  mGlFrameBuffer(0)
835 {
836  QCPPaintBufferGlFbo::reallocateBuffer();
837 }
838 
839 QCPPaintBufferGlFbo::~QCPPaintBufferGlFbo()
840 {
841  if (mGlFrameBuffer)
842  delete mGlFrameBuffer;
843 }
844 
845 /* inherits documentation from base class */
846 QCPPainter *QCPPaintBufferGlFbo::startPainting()
847 {
848  if (mGlPaintDevice.isNull())
849  {
850  qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
851  return 0;
852  }
853  if (!mGlFrameBuffer)
854  {
855  qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
856  return 0;
857  }
858 
859  if (QOpenGLContext::currentContext() != mGlContext.data())
860  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
861  mGlFrameBuffer->bind();
862  QCPPainter *result = new QCPPainter(mGlPaintDevice.data());
863  result->setRenderHint(QPainter::HighQualityAntialiasing);
864  return result;
865 }
866 
867 /* inherits documentation from base class */
868 void QCPPaintBufferGlFbo::donePainting()
869 {
870  if (mGlFrameBuffer && mGlFrameBuffer->isBound())
871  mGlFrameBuffer->release();
872  else
873  qDebug() << Q_FUNC_INFO << "Either OpenGL frame buffer not valid or was not bound";
874 }
875 
876 /* inherits documentation from base class */
877 void QCPPaintBufferGlFbo::draw(QCPPainter *painter) const
878 {
879  if (!painter || !painter->isActive())
880  {
881  qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
882  return;
883  }
884  if (!mGlFrameBuffer)
885  {
886  qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
887  return;
888  }
889  painter->drawImage(0, 0, mGlFrameBuffer->toImage());
890 }
891 
892 /* inherits documentation from base class */
893 void QCPPaintBufferGlFbo::clear(const QColor &color)
894 {
895  if (mGlContext.isNull())
896  {
897  qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
898  return;
899  }
900  if (!mGlFrameBuffer)
901  {
902  qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
903  return;
904  }
905 
906  if (QOpenGLContext::currentContext() != mGlContext.data())
907  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
908  mGlFrameBuffer->bind();
909  glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
910  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
911  mGlFrameBuffer->release();
912 }
913 
914 /* inherits documentation from base class */
915 void QCPPaintBufferGlFbo::reallocateBuffer()
916 {
917  // release and delete possibly existing framebuffer:
918  if (mGlFrameBuffer)
919  {
920  if (mGlFrameBuffer->isBound())
921  mGlFrameBuffer->release();
922  delete mGlFrameBuffer;
923  mGlFrameBuffer = 0;
924  }
925 
926  if (mGlContext.isNull())
927  {
928  qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
929  return;
930  }
931  if (mGlPaintDevice.isNull())
932  {
933  qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
934  return;
935  }
936 
937  // create new fbo with appropriate size:
938  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
939  QOpenGLFramebufferObjectFormat frameBufferFormat;
940  frameBufferFormat.setSamples(mGlContext.data()->format().samples());
941  frameBufferFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
942  mGlFrameBuffer = new QOpenGLFramebufferObject(mSize*mDevicePixelRatio, frameBufferFormat);
943  if (mGlPaintDevice.data()->size() != mSize*mDevicePixelRatio)
944  mGlPaintDevice.data()->setSize(mSize*mDevicePixelRatio);
945 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
946  mGlPaintDevice.data()->setDevicePixelRatio(mDevicePixelRatio);
947 #endif
948 }
949 #endif // QCP_OPENGL_FBO
950 /* end of 'src/paintbuffer.cpp' */
951 
952 
953 /* including file 'src/layer.cpp', size 37304 */
954 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
955 
959 
1016 /* start documentation of inline functions */
1017 
1032 /* end documentation of inline functions */
1033 
1042 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
1043  QObject(parentPlot),
1044  mParentPlot(parentPlot),
1045  mName(layerName),
1046  mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
1047  mVisible(true),
1048  mMode(lmLogical)
1049 {
1050  // Note: no need to make sure layerName is unique, because layer
1051  // management is done with QCustomPlot functions.
1052 }
1053 
1055 {
1056  // If child layerables are still on this layer, detach them, so they don't try to reach back to this
1057  // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
1058  // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
1059  // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
1060 
1061  while (!mChildren.isEmpty())
1062  mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
1063 
1064  if (mParentPlot->currentLayer() == this)
1065  qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand.";
1066 }
1067 
1077 {
1078  mVisible = visible;
1079 }
1080 
1103 {
1104  if (mMode != mode)
1105  {
1106  mMode = mode;
1107  if (!mPaintBuffer.isNull())
1108  mPaintBuffer.data()->setInvalidated();
1109  }
1110 }
1111 
1119 {
1120  foreach (QCPLayerable *child, mChildren)
1121  {
1122  if (child->realVisibility())
1123  {
1124  painter->save();
1125  painter->setClipRect(child->clipRect().translated(0, -1));
1126  child->applyDefaultAntialiasingHint(painter);
1127  child->draw(painter);
1128  painter->restore();
1129  }
1130  }
1131 }
1132 
1142 {
1143  if (!mPaintBuffer.isNull())
1144  {
1145  if (QCPPainter *painter = mPaintBuffer.data()->startPainting())
1146  {
1147  if (painter->isActive())
1148  draw(painter);
1149  else
1150  qDebug() << Q_FUNC_INFO << "paint buffer returned inactive painter";
1151  delete painter;
1152  mPaintBuffer.data()->donePainting();
1153  } else
1154  qDebug() << Q_FUNC_INFO << "paint buffer returned zero painter";
1155  } else
1156  qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
1157 }
1158 
1173 {
1175  {
1176  if (!mPaintBuffer.isNull())
1177  {
1178  mPaintBuffer.data()->clear(Qt::transparent);
1180  mPaintBuffer.data()->setInvalidated(false);
1181  mParentPlot->update();
1182  } else
1183  qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
1184  } else if (mMode == lmLogical)
1185  mParentPlot->replot();
1186 }
1187 
1198 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
1199 {
1200  if (!mChildren.contains(layerable))
1201  {
1202  if (prepend)
1203  mChildren.prepend(layerable);
1204  else
1205  mChildren.append(layerable);
1206  if (!mPaintBuffer.isNull())
1207  mPaintBuffer.data()->setInvalidated();
1208  } else
1209  qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
1210 }
1211 
1222 {
1223  if (mChildren.removeOne(layerable))
1224  {
1225  if (!mPaintBuffer.isNull())
1226  mPaintBuffer.data()->setInvalidated();
1227  } else
1228  qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
1229 }
1230 
1231 
1235 
1248 /* start documentation of inline functions */
1249 
1263 /* end documentation of inline functions */
1264 /* start documentation of pure virtual functions */
1265 
1306 /* end documentation of pure virtual functions */
1307 /* start documentation of signals */
1308 
1317 /* end documentation of signals */
1318 
1339 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
1340  QObject(plot),
1341  mVisible(true),
1342  mParentPlot(plot),
1343  mParentLayerable(parentLayerable),
1344  mLayer(0),
1345  mAntialiased(true)
1346 {
1347  if (mParentPlot)
1348  {
1349  if (targetLayer.isEmpty())
1351  else if (!setLayer(targetLayer))
1352  qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
1353  }
1354 }
1355 
1357 {
1358  if (mLayer)
1359  {
1360  mLayer->removeChild(this);
1361  mLayer = 0;
1362  }
1363 }
1364 
1371 {
1372  mVisible = on;
1373 }
1374 
1385 {
1386  return moveToLayer(layer, false);
1387 }
1388 
1394 bool QCPLayerable::setLayer(const QString &layerName)
1395 {
1396  if (!mParentPlot)
1397  {
1398  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1399  return false;
1400  }
1401  if (QCPLayer *layer = mParentPlot->layer(layerName))
1402  {
1403  return setLayer(layer);
1404  } else
1405  {
1406  qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
1407  return false;
1408  }
1409 }
1410 
1418 {
1419  mAntialiased = enabled;
1420 }
1421 
1433 {
1434  return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1435 }
1436 
1475 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1476 {
1477  Q_UNUSED(pos)
1478  Q_UNUSED(onlySelectable)
1479  Q_UNUSED(details)
1480  return -1.0;
1481 }
1482 
1501 {
1502  if (mParentPlot)
1503  {
1504  qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1505  return;
1506  }
1507 
1508  if (!parentPlot)
1509  qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1510 
1513 }
1514 
1527 {
1529 }
1530 
1540 {
1541  if (layer && !mParentPlot)
1542  {
1543  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1544  return false;
1545  }
1546  if (layer && layer->parentPlot() != mParentPlot)
1547  {
1548  qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1549  return false;
1550  }
1551 
1552  QCPLayer *oldLayer = mLayer;
1553  if (mLayer)
1554  mLayer->removeChild(this);
1555  mLayer = layer;
1556  if (mLayer)
1557  mLayer->addChild(this, prepend);
1558  if (mLayer != oldLayer)
1559  emit layerChanged(mLayer);
1560  return true;
1561 }
1562 
1570 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1571 {
1572  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1573  painter->setAntialiasing(false);
1574  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1575  painter->setAntialiasing(true);
1576  else
1577  painter->setAntialiasing(localAntialiased);
1578 }
1579 
1597 {
1598  Q_UNUSED(parentPlot)
1599 }
1600 
1613 {
1614  return QCP::iSelectOther;
1615 }
1616 
1627 {
1628  if (mParentPlot)
1629  return mParentPlot->viewport();
1630  else
1631  return QRect();
1632 }
1633 
1662 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1663 {
1664  Q_UNUSED(event)
1665  Q_UNUSED(additive)
1666  Q_UNUSED(details)
1667  Q_UNUSED(selectionStateChanged)
1668 }
1669 
1682 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1683 {
1684  Q_UNUSED(selectionStateChanged)
1685 }
1686 
1712 void QCPLayerable::mousePressEvent(QMouseEvent *event, const QVariant &details)
1713 {
1714  Q_UNUSED(details)
1715  event->ignore();
1716 }
1717 
1730 void QCPLayerable::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
1731 {
1732  Q_UNUSED(startPos)
1733  event->ignore();
1734 }
1735 
1748 void QCPLayerable::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
1749 {
1750  Q_UNUSED(startPos)
1751  event->ignore();
1752 }
1753 
1780 void QCPLayerable::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
1781 {
1782  Q_UNUSED(details)
1783  event->ignore();
1784 }
1785 
1803 void QCPLayerable::wheelEvent(QWheelEvent *event)
1804 {
1805  event->ignore();
1806 }
1807 /* end of 'src/layer.cpp' */
1808 
1809 
1810 /* including file 'src/axis/range.cpp', size 12221 */
1811 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
1812 
1816 
1825 /* start of documentation of inline functions */
1826 
1868 /* end of documentation of inline functions */
1869 
1881 const double QCPRange::minRange = 1e-280;
1882 
1894 const double QCPRange::maxRange = 1e250;
1895 
1900  lower(0),
1901  upper(0)
1902 {
1903 }
1904 
1912 QCPRange::QCPRange(double lower, double upper) :
1913  lower(lower),
1914  upper(upper)
1915 {
1916  normalize();
1917 }
1918 
1931 void QCPRange::expand(const QCPRange &otherRange)
1932 {
1933  if (lower > otherRange.lower || qIsNaN(lower))
1934  lower = otherRange.lower;
1935  if (upper < otherRange.upper || qIsNaN(upper))
1936  upper = otherRange.upper;
1937 }
1938 
1951 void QCPRange::expand(double includeCoord)
1952 {
1953  if (lower > includeCoord || qIsNaN(lower))
1954  lower = includeCoord;
1955  if (upper < includeCoord || qIsNaN(upper))
1956  upper = includeCoord;
1957 }
1958 
1959 
1970 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1971 {
1972  QCPRange result = *this;
1973  result.expand(otherRange);
1974  return result;
1975 }
1976 
1987 QCPRange QCPRange::expanded(double includeCoord) const
1988 {
1989  QCPRange result = *this;
1990  result.expand(includeCoord);
1991  return result;
1992 }
1993 
2002 QCPRange QCPRange::bounded(double lowerBound, double upperBound) const
2003 {
2004  if (lowerBound > upperBound)
2005  qSwap(lowerBound, upperBound);
2006 
2007  QCPRange result(lower, upper);
2008  if (result.lower < lowerBound)
2009  {
2010  result.lower = lowerBound;
2011  result.upper = lowerBound + size();
2012  if (result.upper > upperBound || qFuzzyCompare(size(), upperBound-lowerBound))
2013  result.upper = upperBound;
2014  } else if (result.upper > upperBound)
2015  {
2016  result.upper = upperBound;
2017  result.lower = upperBound - size();
2018  if (result.lower < lowerBound || qFuzzyCompare(size(), upperBound-lowerBound))
2019  result.lower = lowerBound;
2020  }
2021 
2022  return result;
2023 }
2024 
2038 {
2039  double rangeFac = 1e-3;
2040  QCPRange sanitizedRange(lower, upper);
2041  sanitizedRange.normalize();
2042  // can't have range spanning negative and positive values in log plot, so change range to fix it
2043  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
2044  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
2045  {
2046  // case lower is 0
2047  if (rangeFac < sanitizedRange.upper*rangeFac)
2048  sanitizedRange.lower = rangeFac;
2049  else
2050  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2051  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
2052  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
2053  {
2054  // case upper is 0
2055  if (-rangeFac > sanitizedRange.lower*rangeFac)
2056  sanitizedRange.upper = -rangeFac;
2057  else
2058  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2059  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
2060  {
2061  // find out whether negative or positive interval is wider to decide which sign domain will be chosen
2062  if (-sanitizedRange.lower > sanitizedRange.upper)
2063  {
2064  // negative is wider, do same as in case upper is 0
2065  if (-rangeFac > sanitizedRange.lower*rangeFac)
2066  sanitizedRange.upper = -rangeFac;
2067  else
2068  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2069  } else
2070  {
2071  // positive is wider, do same as in case lower is 0
2072  if (rangeFac < sanitizedRange.upper*rangeFac)
2073  sanitizedRange.lower = rangeFac;
2074  else
2075  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2076  }
2077  }
2078  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
2079  return sanitizedRange;
2080 }
2081 
2087 {
2088  QCPRange sanitizedRange(lower, upper);
2089  sanitizedRange.normalize();
2090  return sanitizedRange;
2091 }
2092 
2101 bool QCPRange::validRange(double lower, double upper)
2102 {
2103  return (lower > -maxRange &&
2104  upper < maxRange &&
2105  qAbs(lower-upper) > minRange &&
2106  qAbs(lower-upper) < maxRange &&
2107  !(lower > 0 && qIsInf(upper/lower)) &&
2108  !(upper < 0 && qIsInf(lower/upper)));
2109 }
2110 
2120 bool QCPRange::validRange(const QCPRange &range)
2121 {
2122  return (range.lower > -maxRange &&
2123  range.upper < maxRange &&
2124  qAbs(range.lower-range.upper) > minRange &&
2125  qAbs(range.lower-range.upper) < maxRange &&
2126  !(range.lower > 0 && qIsInf(range.upper/range.lower)) &&
2127  !(range.upper < 0 && qIsInf(range.lower/range.upper)));
2128 }
2129 /* end of 'src/axis/range.cpp' */
2130 
2131 
2132 /* including file 'src/selection.cpp', size 21941 */
2133 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
2134 
2138 
2165 /* start documentation of inline functions */
2166 
2225 /* end documentation of inline functions */
2226 
2231  mBegin(0),
2232  mEnd(0)
2233 {
2234 }
2235 
2242  mBegin(begin),
2243  mEnd(end)
2244 {
2245 }
2246 
2257 {
2258  QCPDataRange result(intersection(other));
2259  if (result.isEmpty()) // no intersection, preserve respective bounding side of otherRange as both begin and end of return value
2260  {
2261  if (mEnd <= other.mBegin)
2262  result = QCPDataRange(other.mBegin, other.mBegin);
2263  else
2264  result = QCPDataRange(other.mEnd, other.mEnd);
2265  }
2266  return result;
2267 }
2268 
2273 {
2274  return QCPDataRange(qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd));
2275 }
2276 
2288 {
2289  QCPDataRange result(qMax(mBegin, other.mBegin), qMin(mEnd, other.mEnd));
2290  if (result.isValid())
2291  return result;
2292  else
2293  return QCPDataRange();
2294 }
2295 
2301 bool QCPDataRange::intersects(const QCPDataRange &other) const
2302 {
2303  return !( (mBegin > other.mBegin && mBegin >= other.mEnd) ||
2304  (mEnd <= other.mBegin && mEnd < other.mEnd) );
2305 }
2306 
2312 bool QCPDataRange::contains(const QCPDataRange &other) const
2313 {
2314  return mBegin <= other.mBegin && mEnd >= other.mEnd;
2315 }
2316 
2317 
2318 
2322 
2356 /* start documentation of inline functions */
2357 
2383 /* end documentation of inline functions */
2384 
2389 {
2390 }
2391 
2396 {
2397  mDataRanges.append(range);
2398 }
2399 
2408 {
2409  if (mDataRanges.size() != other.mDataRanges.size())
2410  return false;
2411  for (int i=0; i<mDataRanges.size(); ++i)
2412  {
2413  if (mDataRanges.at(i) != other.mDataRanges.at(i))
2414  return false;
2415  }
2416  return true;
2417 }
2418 
2424 {
2425  mDataRanges << other.mDataRanges;
2426  simplify();
2427  return *this;
2428 }
2429 
2435 {
2436  addDataRange(other);
2437  return *this;
2438 }
2439 
2444 {
2445  for (int i=0; i<other.dataRangeCount(); ++i)
2446  *this -= other.dataRange(i);
2447 
2448  return *this;
2449 }
2450 
2455 {
2456  if (other.isEmpty() || isEmpty())
2457  return *this;
2458 
2459  simplify();
2460  int i=0;
2461  while (i < mDataRanges.size())
2462  {
2463  const int thisBegin = mDataRanges.at(i).begin();
2464  const int thisEnd = mDataRanges.at(i).end();
2465  if (thisBegin >= other.end())
2466  break; // since data ranges are sorted after the simplify() call, no ranges which contain other will come after this
2467 
2468  if (thisEnd > other.begin()) // ranges which don't fulfill this are entirely before other and can be ignored
2469  {
2470  if (thisBegin >= other.begin()) // range leading segment is encompassed
2471  {
2472  if (thisEnd <= other.end()) // range fully encompassed, remove completely
2473  {
2474  mDataRanges.removeAt(i);
2475  continue;
2476  } else // only leading segment is encompassed, trim accordingly
2477  mDataRanges[i].setBegin(other.end());
2478  } else // leading segment is not encompassed
2479  {
2480  if (thisEnd <= other.end()) // only trailing segment is encompassed, trim accordingly
2481  {
2482  mDataRanges[i].setEnd(other.begin());
2483  } else // other lies inside this range, so split range
2484  {
2485  mDataRanges[i].setEnd(other.begin());
2486  mDataRanges.insert(i+1, QCPDataRange(other.end(), thisEnd));
2487  break; // since data ranges are sorted (and don't overlap) after simplify() call, we're done here
2488  }
2489  }
2490  }
2491  ++i;
2492  }
2493 
2494  return *this;
2495 }
2496 
2502 {
2503  int result = 0;
2504  for (int i=0; i<mDataRanges.size(); ++i)
2505  result += mDataRanges.at(i).length();
2506  return result;
2507 }
2508 
2518 {
2519  if (index >= 0 && index < mDataRanges.size())
2520  {
2521  return mDataRanges.at(index);
2522  } else
2523  {
2524  qDebug() << Q_FUNC_INFO << "index out of range:" << index;
2525  return QCPDataRange();
2526  }
2527 }
2528 
2534 {
2535  if (isEmpty())
2536  return QCPDataRange();
2537  else
2538  return QCPDataRange(mDataRanges.first().begin(), mDataRanges.last().end());
2539 }
2540 
2547 void QCPDataSelection::addDataRange(const QCPDataRange &dataRange, bool simplify)
2548 {
2549  mDataRanges.append(dataRange);
2550  if (simplify)
2551  this->simplify();
2552 }
2553 
2560 {
2561  mDataRanges.clear();
2562 }
2563 
2574 {
2575  // remove any empty ranges:
2576  for (int i=mDataRanges.size()-1; i>=0; --i)
2577  {
2578  if (mDataRanges.at(i).isEmpty())
2579  mDataRanges.removeAt(i);
2580  }
2581  if (mDataRanges.isEmpty())
2582  return;
2583 
2584  // sort ranges by starting value, ascending:
2585  std::sort(mDataRanges.begin(), mDataRanges.end(), lessThanDataRangeBegin);
2586 
2587  // join overlapping/contiguous ranges:
2588  int i = 1;
2589  while (i < mDataRanges.size())
2590  {
2591  if (mDataRanges.at(i-1).end() >= mDataRanges.at(i).begin()) // range i overlaps/joins with i-1, so expand range i-1 appropriately and remove range i from list
2592  {
2593  mDataRanges[i-1].setEnd(qMax(mDataRanges.at(i-1).end(), mDataRanges.at(i).end()));
2594  mDataRanges.removeAt(i);
2595  } else
2596  ++i;
2597  }
2598 }
2599 
2611 {
2612  simplify();
2613  switch (type)
2614  {
2615  case QCP::stNone:
2616  {
2617  mDataRanges.clear();
2618  break;
2619  }
2620  case QCP::stWhole:
2621  {
2622  // whole selection isn't defined by data range, so don't change anything (is handled in plottable methods)
2623  break;
2624  }
2625  case QCP::stSingleData:
2626  {
2627  // reduce all data ranges to the single first data point:
2628  if (!mDataRanges.isEmpty())
2629  {
2630  if (mDataRanges.size() > 1)
2631  mDataRanges = QList<QCPDataRange>() << mDataRanges.first();
2632  if (mDataRanges.first().length() > 1)
2633  mDataRanges.first().setEnd(mDataRanges.first().begin()+1);
2634  }
2635  break;
2636  }
2637  case QCP::stDataRange:
2638  {
2639  if (!isEmpty())
2640  mDataRanges = QList<QCPDataRange>() << span();
2641  break;
2642  }
2644  {
2645  // this is the selection type that allows all concievable combinations of ranges, so do nothing
2646  break;
2647  }
2648  }
2649 }
2650 
2658 {
2659  if (other.isEmpty()) return false;
2660 
2661  int otherIndex = 0;
2662  int thisIndex = 0;
2663  while (thisIndex < mDataRanges.size() && otherIndex < other.mDataRanges.size())
2664  {
2665  if (mDataRanges.at(thisIndex).contains(other.mDataRanges.at(otherIndex)))
2666  ++otherIndex;
2667  else
2668  ++thisIndex;
2669  }
2670  return thisIndex < mDataRanges.size(); // if thisIndex ran all the way to the end to find a containing range for the current otherIndex, other is not contained in this
2671 }
2672 
2682 {
2683  QCPDataSelection result;
2684  for (int i=0; i<mDataRanges.size(); ++i)
2685  result.addDataRange(mDataRanges.at(i).intersection(other), false);
2686  result.simplify();
2687  return result;
2688 }
2689 
2695 {
2696  QCPDataSelection result;
2697  for (int i=0; i<other.dataRangeCount(); ++i)
2698  result += intersection(other.dataRange(i));
2699  result.simplify();
2700  return result;
2701 }
2702 
2713 {
2714  if (isEmpty())
2715  return QCPDataSelection(outerRange);
2716  QCPDataRange fullRange = outerRange.expanded(span());
2717 
2718  QCPDataSelection result;
2719  // first unselected segment:
2720  if (mDataRanges.first().begin() != fullRange.begin())
2721  result.addDataRange(QCPDataRange(fullRange.begin(), mDataRanges.first().begin()), false);
2722  // intermediate unselected segments:
2723  for (int i=1; i<mDataRanges.size(); ++i)
2724  result.addDataRange(QCPDataRange(mDataRanges.at(i-1).end(), mDataRanges.at(i).begin()), false);
2725  // last unselected segment:
2726  if (mDataRanges.last().end() != fullRange.end())
2727  result.addDataRange(QCPDataRange(mDataRanges.last().end(), fullRange.end()), false);
2728  result.simplify();
2729  return result;
2730 }
2731 /* end of 'src/selection.cpp' */
2732 
2733 
2734 /* including file 'src/selectionrect.cpp', size 9224 */
2735 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
2736 
2740 
2763 /* start of documentation of inline functions */
2764 
2773 /* end of documentation of inline functions */
2774 /* start documentation of signals */
2775 
2812 /* end documentation of signals */
2813 
2820  QCPLayerable(parentPlot),
2821  mPen(QBrush(Qt::gray), 0, Qt::DashLine),
2822  mBrush(Qt::NoBrush),
2823  mActive(false)
2824 {
2825 }
2826 
2828 {
2829  cancel();
2830 }
2831 
2837 {
2838  if (axis)
2839  {
2840  if (axis->orientation() == Qt::Horizontal)
2841  return QCPRange(axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width()));
2842  else
2843  return QCPRange(axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top()));
2844  } else
2845  {
2846  qDebug() << Q_FUNC_INFO << "called with axis zero";
2847  return QCPRange();
2848  }
2849 }
2850 
2857 {
2858  mPen = pen;
2859 }
2860 
2868 {
2869  mBrush = brush;
2870 }
2871 
2877 {
2878  if (mActive)
2879  {
2880  mActive = false;
2881  emit canceled(mRect, 0);
2882  }
2883 }
2884 
2891 void QCPSelectionRect::startSelection(QMouseEvent *event)
2892 {
2893  mActive = true;
2894  mRect = QRect(event->pos(), event->pos());
2895  emit started(event);
2896 }
2897 
2904 void QCPSelectionRect::moveSelection(QMouseEvent *event)
2905 {
2906  mRect.setBottomRight(event->pos());
2907  emit changed(mRect, event);
2908  layer()->replot();
2909 }
2910 
2917 void QCPSelectionRect::endSelection(QMouseEvent *event)
2918 {
2919  mRect.setBottomRight(event->pos());
2920  mActive = false;
2921  emit accepted(mRect, event);
2922 }
2923 
2930 void QCPSelectionRect::keyPressEvent(QKeyEvent *event)
2931 {
2932  if (event->key() == Qt::Key_Escape && mActive)
2933  {
2934  mActive = false;
2935  emit canceled(mRect, event);
2936  }
2937 }
2938 
2939 /* inherits documentation from base class */
2941 {
2943 }
2944 
2952 {
2953  if (mActive)
2954  {
2955  painter->setPen(mPen);
2956  painter->setBrush(mBrush);
2957  painter->drawRect(mRect);
2958  }
2959 }
2960 /* end of 'src/selectionrect.cpp' */
2961 
2962 
2963 /* including file 'src/layout.cpp', size 79139 */
2964 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
2965 
2969 
3001 /* start documentation of inline functions */
3002 
3009 /* end documentation of inline functions */
3010 
3015  QObject(parentPlot),
3016  mParentPlot(parentPlot)
3017 {
3018  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
3019  mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
3020  mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
3021  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
3022 }
3023 
3025 {
3026  clear();
3027 }
3028 
3034 {
3035  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
3036  while (it.hasNext())
3037  {
3038  it.next();
3039  if (!it.value().isEmpty())
3040  return false;
3041  }
3042  return true;
3043 }
3044 
3050 {
3051  // make all children remove themselves from this margin group:
3052  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
3053  while (it.hasNext())
3054  {
3055  it.next();
3056  const QList<QCPLayoutElement*> elements = it.value();
3057  for (int i=elements.size()-1; i>=0; --i)
3058  elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
3059  }
3060 }
3061 
3073 {
3074  // query all automatic margins of the layout elements in this margin group side and find maximum:
3075  int result = 0;
3076  const QList<QCPLayoutElement*> elements = mChildren.value(side);
3077  for (int i=0; i<elements.size(); ++i)
3078  {
3079  if (!elements.at(i)->autoMargins().testFlag(side))
3080  continue;
3081  int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
3082  if (m > result)
3083  result = m;
3084  }
3085  return result;
3086 }
3087 
3095 {
3096  if (!mChildren[side].contains(element))
3097  mChildren[side].append(element);
3098  else
3099  qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
3100 }
3101 
3109 {
3110  if (!mChildren[side].removeOne(element))
3111  qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
3112 }
3113 
3114 
3118 
3145 /* start documentation of inline functions */
3146 
3176 /* end documentation of inline functions */
3177 
3182  QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
3183  mParentLayout(0),
3184  mMinimumSize(),
3185  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
3186  mSizeConstraintRect(scrInnerRect),
3187  mRect(0, 0, 0, 0),
3188  mOuterRect(0, 0, 0, 0),
3189  mMargins(0, 0, 0, 0),
3190  mMinimumMargins(0, 0, 0, 0),
3191  mAutoMargins(QCP::msAll)
3192 {
3193 }
3194 
3196 {
3197  setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
3198  // unregister at layout:
3199  if (qobject_cast<QCPLayout*>(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor
3200  mParentLayout->take(this);
3201 }
3202 
3215 {
3216  if (mOuterRect != rect)
3217  {
3218  mOuterRect = rect;
3219  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
3220  }
3221 }
3222 
3235 {
3236  if (mMargins != margins)
3237  {
3238  mMargins = margins;
3239  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
3240  }
3241 }
3242 
3253 {
3254  if (mMinimumMargins != margins)
3255  {
3257  }
3258 }
3259 
3270 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
3271 {
3272  mAutoMargins = sides;
3273 }
3274 
3287 void QCPLayoutElement::setMinimumSize(const QSize &size)
3288 {
3289  if (mMinimumSize != size)
3290  {
3291  mMinimumSize = size;
3292  if (mParentLayout)
3294  }
3295 }
3296 
3304 void QCPLayoutElement::setMinimumSize(int width, int height)
3305 {
3306  setMinimumSize(QSize(width, height));
3307 }
3308 
3316 void QCPLayoutElement::setMaximumSize(const QSize &size)
3317 {
3318  if (mMaximumSize != size)
3319  {
3320  mMaximumSize = size;
3321  if (mParentLayout)
3323  }
3324 }
3325 
3333 void QCPLayoutElement::setMaximumSize(int width, int height)
3334 {
3335  setMaximumSize(QSize(width, height));
3336 }
3337 
3348 {
3349  if (mSizeConstraintRect != constraintRect)
3350  {
3351  mSizeConstraintRect = constraintRect;
3352  if (mParentLayout)
3354  }
3355 }
3356 
3370 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
3371 {
3372  QVector<QCP::MarginSide> sideVector;
3373  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
3374  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
3375  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
3376  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
3377 
3378  for (int i=0; i<sideVector.size(); ++i)
3379  {
3380  QCP::MarginSide side = sideVector.at(i);
3381  if (marginGroup(side) != group)
3382  {
3383  QCPMarginGroup *oldGroup = marginGroup(side);
3384  if (oldGroup) // unregister at old group
3385  oldGroup->removeChild(side, this);
3386 
3387  if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
3388  {
3389  mMarginGroups.remove(side);
3390  } else // setting to a new group
3391  {
3392  mMarginGroups[side] = group;
3393  group->addChild(side, this);
3394  }
3395  }
3396  }
3397 }
3398 
3412 {
3413  if (phase == upMargins)
3414  {
3415  if (mAutoMargins != QCP::msNone)
3416  {
3417  // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
3418  QMargins newMargins = mMargins;
3419  QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
3420  foreach (QCP::MarginSide side, allMarginSides)
3421  {
3422  if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
3423  {
3424  if (mMarginGroups.contains(side))
3425  QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
3426  else
3427  QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
3428  // apply minimum margin restrictions:
3429  if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
3430  QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
3431  }
3432  }
3433  setMargins(newMargins);
3434  }
3435  }
3436 }
3437 
3453 {
3454  return QSize(mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom());
3455 }
3456 
3472 {
3473  return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
3474 }
3475 
3483 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
3484 {
3485  Q_UNUSED(recursive)
3486  return QList<QCPLayoutElement*>();
3487 }
3488 
3500 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3501 {
3502  Q_UNUSED(details)
3503 
3504  if (onlySelectable)
3505  return -1;
3506 
3507  if (QRectF(mOuterRect).contains(pos))
3508  {
3509  if (mParentPlot)
3510  return mParentPlot->selectionTolerance()*0.99;
3511  else
3512  {
3513  qDebug() << Q_FUNC_INFO << "parent plot not defined";
3514  return -1;
3515  }
3516  } else
3517  return -1;
3518 }
3519 
3526 {
3527  foreach (QCPLayoutElement* el, elements(false))
3528  {
3529  if (!el->parentPlot())
3530  el->initializeParentPlot(parentPlot);
3531  }
3532 }
3533 
3544 {
3546 }
3547 
3560 {
3561 }
3562 
3566 
3590 /* start documentation of pure virtual functions */
3591 
3634 /* end documentation of pure virtual functions */
3635 
3641 {
3642 }
3643 
3654 {
3655  QCPLayoutElement::update(phase);
3656 
3657  // set child element rects according to layout:
3658  if (phase == upLayout)
3659  updateLayout();
3660 
3661  // propagate update call to child elements:
3662  const int elCount = elementCount();
3663  for (int i=0; i<elCount; ++i)
3664  {
3665  if (QCPLayoutElement *el = elementAt(i))
3666  el->update(phase);
3667  }
3668 }
3669 
3670 /* inherits documentation from base class */
3671 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
3672 {
3673  const int c = elementCount();
3674  QList<QCPLayoutElement*> result;
3675 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
3676  result.reserve(c);
3677 #endif
3678  for (int i=0; i<c; ++i)
3679  result.append(elementAt(i));
3680  if (recursive)
3681  {
3682  for (int i=0; i<c; ++i)
3683  {
3684  if (result.at(i))
3685  result << result.at(i)->elements(recursive);
3686  }
3687  }
3688  return result;
3689 }
3690 
3699 {
3700 }
3701 
3713 bool QCPLayout::removeAt(int index)
3714 {
3715  if (QCPLayoutElement *el = takeAt(index))
3716  {
3717  delete el;
3718  return true;
3719  } else
3720  return false;
3721 }
3722 
3735 {
3736  if (take(element))
3737  {
3738  delete element;
3739  return true;
3740  } else
3741  return false;
3742 }
3743 
3751 {
3752  for (int i=elementCount()-1; i>=0; --i)
3753  {
3754  if (elementAt(i))
3755  removeAt(i);
3756  }
3757  simplify();
3758 }
3759 
3769 {
3770  if (QWidget *w = qobject_cast<QWidget*>(parent()))
3771  w->updateGeometry();
3772  else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
3773  l->sizeConstraintsChanged();
3774 }
3775 
3789 {
3790 }
3791 
3792 
3806 {
3807  if (el)
3808  {
3809  el->mParentLayout = this;
3810  el->setParentLayerable(this);
3811  el->setParent(this);
3812  if (!el->parentPlot())
3814  el->layoutChanged();
3815  } else
3816  qDebug() << Q_FUNC_INFO << "Null element passed";
3817 }
3818 
3830 {
3831  if (el)
3832  {
3833  el->mParentLayout = 0;
3834  el->setParentLayerable(0);
3835  el->setParent(mParentPlot);
3836  // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
3837  } else
3838  qDebug() << Q_FUNC_INFO << "Null element passed";
3839 }
3840 
3870 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
3871 {
3872  if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
3873  {
3874  qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
3875  return QVector<int>();
3876  }
3877  if (stretchFactors.isEmpty())
3878  return QVector<int>();
3879  int sectionCount = stretchFactors.size();
3880  QVector<double> sectionSizes(sectionCount);
3881  // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
3882  int minSizeSum = 0;
3883  for (int i=0; i<sectionCount; ++i)
3884  minSizeSum += minSizes.at(i);
3885  if (totalSize < minSizeSum)
3886  {
3887  // new stretch factors are minimum sizes and minimum sizes are set to zero:
3888  for (int i=0; i<sectionCount; ++i)
3889  {
3890  stretchFactors[i] = minSizes.at(i);
3891  minSizes[i] = 0;
3892  }
3893  }
3894 
3895  QList<int> minimumLockedSections;
3896  QList<int> unfinishedSections;
3897  for (int i=0; i<sectionCount; ++i)
3898  unfinishedSections.append(i);
3899  double freeSize = totalSize;
3900 
3901  int outerIterations = 0;
3902  while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
3903  {
3904  ++outerIterations;
3905  int innerIterations = 0;
3906  while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
3907  {
3908  ++innerIterations;
3909  // find section that hits its maximum next:
3910  int nextId = -1;
3911  double nextMax = 1e12;
3912  for (int i=0; i<unfinishedSections.size(); ++i)
3913  {
3914  int secId = unfinishedSections.at(i);
3915  double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
3916  if (hitsMaxAt < nextMax)
3917  {
3918  nextMax = hitsMaxAt;
3919  nextId = secId;
3920  }
3921  }
3922  // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section
3923  // actually hits its maximum, without exceeding the total size when we add up all sections)
3924  double stretchFactorSum = 0;
3925  for (int i=0; i<unfinishedSections.size(); ++i)
3926  stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
3927  double nextMaxLimit = freeSize/stretchFactorSum;
3928  if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
3929  {
3930  for (int i=0; i<unfinishedSections.size(); ++i)
3931  {
3932  sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
3933  freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
3934  }
3935  unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
3936  } else // next maximum isn't hit, just distribute rest of free space on remaining sections
3937  {
3938  for (int i=0; i<unfinishedSections.size(); ++i)
3939  sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
3940  unfinishedSections.clear();
3941  }
3942  }
3943  if (innerIterations == sectionCount*2)
3944  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
3945 
3946  // now check whether the resulting section sizes violate minimum restrictions:
3947  bool foundMinimumViolation = false;
3948  for (int i=0; i<sectionSizes.size(); ++i)
3949  {
3950  if (minimumLockedSections.contains(i))
3951  continue;
3952  if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
3953  {
3954  sectionSizes[i] = minSizes.at(i); // set it to minimum
3955  foundMinimumViolation = true; // make sure we repeat the whole optimization process
3956  minimumLockedSections.append(i);
3957  }
3958  }
3959  if (foundMinimumViolation)
3960  {
3961  freeSize = totalSize;
3962  for (int i=0; i<sectionCount; ++i)
3963  {
3964  if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
3965  unfinishedSections.append(i);
3966  else
3967  freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
3968  }
3969  // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
3970  for (int i=0; i<unfinishedSections.size(); ++i)
3971  sectionSizes[unfinishedSections.at(i)] = 0;
3972  }
3973  }
3974  if (outerIterations == sectionCount*2)
3975  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
3976 
3977  QVector<int> result(sectionCount);
3978  for (int i=0; i<sectionCount; ++i)
3979  result[i] = qRound(sectionSizes.at(i));
3980  return result;
3981 }
3982 
3996 {
3997  QSize minOuterHint = el->minimumOuterSizeHint();
3998  QSize minOuter = el->minimumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset minimum of 0)
3999  if (minOuter.width() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4000  minOuter.rwidth() += el->margins().left() + el->margins().right();
4001  if (minOuter.height() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4002  minOuter.rheight() += el->margins().top() + el->margins().bottom();
4003 
4004  return QSize(minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(),
4005  minOuter.height() > 0 ? minOuter.height() : minOuterHint.height());;
4006 }
4007 
4021 {
4022  QSize maxOuterHint = el->maximumOuterSizeHint();
4023  QSize maxOuter = el->maximumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset maximum of QWIDGETSIZE_MAX)
4024  if (maxOuter.width() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4025  maxOuter.rwidth() += el->margins().left() + el->margins().right();
4026  if (maxOuter.height() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4027  maxOuter.rheight() += el->margins().top() + el->margins().bottom();
4028 
4029  return QSize(maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(),
4030  maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height());
4031 }
4032 
4033 
4037 
4058 /* start documentation of inline functions */
4059 
4074 /* end documentation of inline functions */
4075 
4080  mColumnSpacing(5),
4081  mRowSpacing(5),
4082  mWrap(0),
4083  mFillOrder(foColumnsFirst)
4084 {
4085 }
4086 
4088 {
4089  // clear all child layout elements. This is important because only the specific layouts know how
4090  // to handle removing elements (clear calls virtual removeAt method to do that).
4091  clear();
4092 }
4093 
4102 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
4103 {
4104  if (row >= 0 && row < mElements.size())
4105  {
4106  if (column >= 0 && column < mElements.first().size())
4107  {
4108  if (QCPLayoutElement *result = mElements.at(row).at(column))
4109  return result;
4110  else
4111  qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
4112  } else
4113  qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
4114  } else
4115  qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
4116  return 0;
4117 }
4118 
4119 
4135 {
4136  if (!hasElement(row, column))
4137  {
4138  if (element && element->layout()) // remove from old layout first
4139  element->layout()->take(element);
4140  expandTo(row+1, column+1);
4141  mElements[row][column] = element;
4142  if (element)
4143  adoptElement(element);
4144  return true;
4145  } else
4146  qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
4147  return false;
4148 }
4149 
4161 {
4162  int rowIndex = 0;
4163  int colIndex = 0;
4164  if (mFillOrder == foColumnsFirst)
4165  {
4166  while (hasElement(rowIndex, colIndex))
4167  {
4168  ++colIndex;
4169  if (colIndex >= mWrap && mWrap > 0)
4170  {
4171  colIndex = 0;
4172  ++rowIndex;
4173  }
4174  }
4175  } else
4176  {
4177  while (hasElement(rowIndex, colIndex))
4178  {
4179  ++rowIndex;
4180  if (rowIndex >= mWrap && mWrap > 0)
4181  {
4182  rowIndex = 0;
4183  ++colIndex;
4184  }
4185  }
4186  }
4187  return addElement(rowIndex, colIndex, element);
4188 }
4189 
4196 bool QCPLayoutGrid::hasElement(int row, int column)
4197 {
4198  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
4199  return mElements.at(row).at(column);
4200  else
4201  return false;
4202 }
4203 
4216 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
4217 {
4218  if (column >= 0 && column < columnCount())
4219  {
4220  if (factor > 0)
4221  mColumnStretchFactors[column] = factor;
4222  else
4223  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
4224  } else
4225  qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
4226 }
4227 
4240 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
4241 {
4242  if (factors.size() == mColumnStretchFactors.size())
4243  {
4244  mColumnStretchFactors = factors;
4245  for (int i=0; i<mColumnStretchFactors.size(); ++i)
4246  {
4247  if (mColumnStretchFactors.at(i) <= 0)
4248  {
4249  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
4250  mColumnStretchFactors[i] = 1;
4251  }
4252  }
4253  } else
4254  qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
4255 }
4256 
4269 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
4270 {
4271  if (row >= 0 && row < rowCount())
4272  {
4273  if (factor > 0)
4274  mRowStretchFactors[row] = factor;
4275  else
4276  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
4277  } else
4278  qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
4279 }
4280 
4293 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
4294 {
4295  if (factors.size() == mRowStretchFactors.size())
4296  {
4297  mRowStretchFactors = factors;
4298  for (int i=0; i<mRowStretchFactors.size(); ++i)
4299  {
4300  if (mRowStretchFactors.at(i) <= 0)
4301  {
4302  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
4303  mRowStretchFactors[i] = 1;
4304  }
4305  }
4306  } else
4307  qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
4308 }
4309 
4316 {
4317  mColumnSpacing = pixels;
4318 }
4319 
4326 {
4327  mRowSpacing = pixels;
4328 }
4329 
4347 void QCPLayoutGrid::setWrap(int count)
4348 {
4349  mWrap = qMax(0, count);
4350 }
4351 
4377 void QCPLayoutGrid::setFillOrder(FillOrder order, bool rearrange)
4378 {
4379  // if rearranging, take all elements via linear index of old fill order:
4380  const int elCount = elementCount();
4381  QVector<QCPLayoutElement*> tempElements;
4382  if (rearrange)
4383  {
4384  tempElements.reserve(elCount);
4385  for (int i=0; i<elCount; ++i)
4386  {
4387  if (elementAt(i))
4388  tempElements.append(takeAt(i));
4389  }
4390  simplify();
4391  }
4392  // change fill order as requested:
4393  mFillOrder = order;
4394  // if rearranging, re-insert via linear index according to new fill order:
4395  if (rearrange)
4396  {
4397  for (int i=0; i<tempElements.size(); ++i)
4398  addElement(tempElements.at(i));
4399  }
4400 }
4401 
4416 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
4417 {
4418  // add rows as necessary:
4419  while (rowCount() < newRowCount)
4420  {
4421  mElements.append(QList<QCPLayoutElement*>());
4422  mRowStretchFactors.append(1);
4423  }
4424  // go through rows and expand columns as necessary:
4425  int newColCount = qMax(columnCount(), newColumnCount);
4426  for (int i=0; i<rowCount(); ++i)
4427  {
4428  while (mElements.at(i).size() < newColCount)
4429  mElements[i].append(0);
4430  }
4431  while (mColumnStretchFactors.size() < newColCount)
4432  mColumnStretchFactors.append(1);
4433 }
4434 
4441 void QCPLayoutGrid::insertRow(int newIndex)
4442 {
4443  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
4444  {
4445  expandTo(1, 1);
4446  return;
4447  }
4448 
4449  if (newIndex < 0)
4450  newIndex = 0;
4451  if (newIndex > rowCount())
4452  newIndex = rowCount();
4453 
4454  mRowStretchFactors.insert(newIndex, 1);
4455  QList<QCPLayoutElement*> newRow;
4456  for (int col=0; col<columnCount(); ++col)
4457  newRow.append((QCPLayoutElement*)0);
4458  mElements.insert(newIndex, newRow);
4459 }
4460 
4469 {
4470  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
4471  {
4472  expandTo(1, 1);
4473  return;
4474  }
4475 
4476  if (newIndex < 0)
4477  newIndex = 0;
4478  if (newIndex > columnCount())
4479  newIndex = columnCount();
4480 
4481  mColumnStretchFactors.insert(newIndex, 1);
4482  for (int row=0; row<rowCount(); ++row)
4483  mElements[row].insert(newIndex, (QCPLayoutElement*)0);
4484 }
4485 
4499 int QCPLayoutGrid::rowColToIndex(int row, int column) const
4500 {
4501  if (row >= 0 && row < rowCount())
4502  {
4503  if (column >= 0 && column < columnCount())
4504  {
4505  switch (mFillOrder)
4506  {
4507  case foRowsFirst: return column*rowCount() + row;
4508  case foColumnsFirst: return row*columnCount() + column;
4509  }
4510  } else
4511  qDebug() << Q_FUNC_INFO << "row index out of bounds:" << row;
4512  } else
4513  qDebug() << Q_FUNC_INFO << "column index out of bounds:" << column;
4514  return 0;
4515 }
4516 
4532 void QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const
4533 {
4534  row = -1;
4535  column = -1;
4536  const int nCols = columnCount();
4537  const int nRows = rowCount();
4538  if (nCols == 0 || nRows == 0)
4539  return;
4540  if (index < 0 || index >= elementCount())
4541  {
4542  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
4543  return;
4544  }
4545 
4546  switch (mFillOrder)
4547  {
4548  case foRowsFirst:
4549  {
4550  column = index / nRows;
4551  row = index % nRows;
4552  break;
4553  }
4554  case foColumnsFirst:
4555  {
4556  row = index / nCols;
4557  column = index % nCols;
4558  break;
4559  }
4560  }
4561 }
4562 
4563 /* inherits documentation from base class */
4565 {
4566  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
4567  getMinimumRowColSizes(&minColWidths, &minRowHeights);
4568  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4569 
4570  int totalRowSpacing = (rowCount()-1) * mRowSpacing;
4571  int totalColSpacing = (columnCount()-1) * mColumnSpacing;
4572  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
4573  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
4574 
4575  // go through cells and set rects accordingly:
4576  int yOffset = mRect.top();
4577  for (int row=0; row<rowCount(); ++row)
4578  {
4579  if (row > 0)
4580  yOffset += rowHeights.at(row-1)+mRowSpacing;
4581  int xOffset = mRect.left();
4582  for (int col=0; col<columnCount(); ++col)
4583  {
4584  if (col > 0)
4585  xOffset += colWidths.at(col-1)+mColumnSpacing;
4586  if (mElements.at(row).at(col))
4587  mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
4588  }
4589  }
4590 }
4591 
4601 {
4602  if (index >= 0 && index < elementCount())
4603  {
4604  int row, col;
4605  indexToRowCol(index, row, col);
4606  return mElements.at(row).at(col);
4607  } else
4608  return 0;
4609 }
4610 
4620 {
4621  if (QCPLayoutElement *el = elementAt(index))
4622  {
4623  releaseElement(el);
4624  int row, col;
4625  indexToRowCol(index, row, col);
4626  mElements[row][col] = 0;
4627  return el;
4628  } else
4629  {
4630  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
4631  return 0;
4632  }
4633 }
4634 
4635 /* inherits documentation from base class */
4637 {
4638  if (element)
4639  {
4640  for (int i=0; i<elementCount(); ++i)
4641  {
4642  if (elementAt(i) == element)
4643  {
4644  takeAt(i);
4645  return true;
4646  }
4647  }
4648  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
4649  } else
4650  qDebug() << Q_FUNC_INFO << "Can't take null element";
4651  return false;
4652 }
4653 
4654 /* inherits documentation from base class */
4655 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
4656 {
4657  QList<QCPLayoutElement*> result;
4658  const int elCount = elementCount();
4659 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
4660  result.reserve(elCount);
4661 #endif
4662  for (int i=0; i<elCount; ++i)
4663  result.append(elementAt(i));
4664  if (recursive)
4665  {
4666  for (int i=0; i<elCount; ++i)
4667  {
4668  if (result.at(i))
4669  result << result.at(i)->elements(recursive);
4670  }
4671  }
4672  return result;
4673 }
4674 
4679 {
4680  // remove rows with only empty cells:
4681  for (int row=rowCount()-1; row>=0; --row)
4682  {
4683  bool hasElements = false;
4684  for (int col=0; col<columnCount(); ++col)
4685  {
4686  if (mElements.at(row).at(col))
4687  {
4688  hasElements = true;
4689  break;
4690  }
4691  }
4692  if (!hasElements)
4693  {
4694  mRowStretchFactors.removeAt(row);
4695  mElements.removeAt(row);
4696  if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
4697  mColumnStretchFactors.clear();
4698  }
4699  }
4700 
4701  // remove columns with only empty cells:
4702  for (int col=columnCount()-1; col>=0; --col)
4703  {
4704  bool hasElements = false;
4705  for (int row=0; row<rowCount(); ++row)
4706  {
4707  if (mElements.at(row).at(col))
4708  {
4709  hasElements = true;
4710  break;
4711  }
4712  }
4713  if (!hasElements)
4714  {
4715  mColumnStretchFactors.removeAt(col);
4716  for (int row=0; row<rowCount(); ++row)
4717  mElements[row].removeAt(col);
4718  }
4719  }
4720 }
4721 
4722 /* inherits documentation from base class */
4724 {
4725  QVector<int> minColWidths, minRowHeights;
4726  getMinimumRowColSizes(&minColWidths, &minRowHeights);
4727  QSize result(0, 0);
4728  for (int i=0; i<minColWidths.size(); ++i)
4729  result.rwidth() += minColWidths.at(i);
4730  for (int i=0; i<minRowHeights.size(); ++i)
4731  result.rheight() += minRowHeights.at(i);
4732  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
4733  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
4734  result.rwidth() += mMargins.left()+mMargins.right();
4735  result.rheight() += mMargins.top()+mMargins.bottom();
4736  return result;
4737 }
4738 
4739 /* inherits documentation from base class */
4741 {
4742  QVector<int> maxColWidths, maxRowHeights;
4743  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4744 
4745  QSize result(0, 0);
4746  for (int i=0; i<maxColWidths.size(); ++i)
4747  result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
4748  for (int i=0; i<maxRowHeights.size(); ++i)
4749  result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
4750  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
4751  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
4752  result.rwidth() += mMargins.left()+mMargins.right();
4753  result.rheight() += mMargins.top()+mMargins.bottom();
4754  if (result.height() > QWIDGETSIZE_MAX)
4755  result.setHeight(QWIDGETSIZE_MAX);
4756  if (result.width() > QWIDGETSIZE_MAX)
4757  result.setWidth(QWIDGETSIZE_MAX);
4758  return result;
4759 }
4760 
4774 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
4775 {
4776  *minColWidths = QVector<int>(columnCount(), 0);
4777  *minRowHeights = QVector<int>(rowCount(), 0);
4778  for (int row=0; row<rowCount(); ++row)
4779  {
4780  for (int col=0; col<columnCount(); ++col)
4781  {
4782  if (QCPLayoutElement *el = mElements.at(row).at(col))
4783  {
4784  QSize minSize = getFinalMinimumOuterSize(el);
4785  if (minColWidths->at(col) < minSize.width())
4786  (*minColWidths)[col] = minSize.width();
4787  if (minRowHeights->at(row) < minSize.height())
4788  (*minRowHeights)[row] = minSize.height();
4789  }
4790  }
4791  }
4792 }
4793 
4807 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
4808 {
4809  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
4810  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
4811  for (int row=0; row<rowCount(); ++row)
4812  {
4813  for (int col=0; col<columnCount(); ++col)
4814  {
4815  if (QCPLayoutElement *el = mElements.at(row).at(col))
4816  {
4817  QSize maxSize = getFinalMaximumOuterSize(el);
4818  if (maxColWidths->at(col) > maxSize.width())
4819  (*maxColWidths)[col] = maxSize.width();
4820  if (maxRowHeights->at(row) > maxSize.height())
4821  (*maxRowHeights)[row] = maxSize.height();
4822  }
4823  }
4824  }
4825 }
4826 
4827 
4831 
4849 /* start documentation of inline functions */
4850 
4857 /* end documentation of inline functions */
4858 
4863 {
4864 }
4865 
4867 {
4868  // clear all child layout elements. This is important because only the specific layouts know how
4869  // to handle removing elements (clear calls virtual removeAt method to do that).
4870  clear();
4871 }
4872 
4877 {
4878  if (elementAt(index))
4879  return mInsetPlacement.at(index);
4880  else
4881  {
4882  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4883  return ipFree;
4884  }
4885 }
4886 
4891 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
4892 {
4893  if (elementAt(index))
4894  return mInsetAlignment.at(index);
4895  else
4896  {
4897  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4898  return 0;
4899  }
4900 }
4901 
4906 QRectF QCPLayoutInset::insetRect(int index) const
4907 {
4908  if (elementAt(index))
4909  return mInsetRect.at(index);
4910  else
4911  {
4912  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4913  return QRectF();
4914  }
4915 }
4916 
4923 {
4924  if (elementAt(index))
4925  mInsetPlacement[index] = placement;
4926  else
4927  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4928 }
4929 
4938 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
4939 {
4940  if (elementAt(index))
4941  mInsetAlignment[index] = alignment;
4942  else
4943  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4944 }
4945 
4957 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
4958 {
4959  if (elementAt(index))
4960  mInsetRect[index] = rect;
4961  else
4962  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4963 }
4964 
4965 /* inherits documentation from base class */
4967 {
4968  for (int i=0; i<mElements.size(); ++i)
4969  {
4970  QCPLayoutElement *el = mElements.at(i);
4971  QRect insetRect;
4972  QSize finalMinSize = getFinalMinimumOuterSize(el);
4973  QSize finalMaxSize = getFinalMaximumOuterSize(el);
4974  if (mInsetPlacement.at(i) == ipFree)
4975  {
4976  insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
4977  rect().y()+rect().height()*mInsetRect.at(i).y(),
4978  rect().width()*mInsetRect.at(i).width(),
4979  rect().height()*mInsetRect.at(i).height());
4980  if (insetRect.size().width() < finalMinSize.width())
4981  insetRect.setWidth(finalMinSize.width());
4982  if (insetRect.size().height() < finalMinSize.height())
4983  insetRect.setHeight(finalMinSize.height());
4984  if (insetRect.size().width() > finalMaxSize.width())
4985  insetRect.setWidth(finalMaxSize.width());
4986  if (insetRect.size().height() > finalMaxSize.height())
4987  insetRect.setHeight(finalMaxSize.height());
4988  } else if (mInsetPlacement.at(i) == ipBorderAligned)
4989  {
4990  insetRect.setSize(finalMinSize);
4991  Qt::Alignment al = mInsetAlignment.at(i);
4992  if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
4993  else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
4994  else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
4995  if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
4996  else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
4997  else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
4998  }
4999  mElements.at(i)->setOuterRect(insetRect);
5000  }
5001 }
5002 
5003 /* inherits documentation from base class */
5005 {
5006  return mElements.size();
5007 }
5008 
5009 /* inherits documentation from base class */
5011 {
5012  if (index >= 0 && index < mElements.size())
5013  return mElements.at(index);
5014  else
5015  return 0;
5016 }
5017 
5018 /* inherits documentation from base class */
5020 {
5021  if (QCPLayoutElement *el = elementAt(index))
5022  {
5023  releaseElement(el);
5024  mElements.removeAt(index);
5025  mInsetPlacement.removeAt(index);
5026  mInsetAlignment.removeAt(index);
5027  mInsetRect.removeAt(index);
5028  return el;
5029  } else
5030  {
5031  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
5032  return 0;
5033  }
5034 }
5035 
5036 /* inherits documentation from base class */
5038 {
5039  if (element)
5040  {
5041  for (int i=0; i<elementCount(); ++i)
5042  {
5043  if (elementAt(i) == element)
5044  {
5045  takeAt(i);
5046  return true;
5047  }
5048  }
5049  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
5050  } else
5051  qDebug() << Q_FUNC_INFO << "Can't take null element";
5052  return false;
5053 }
5054 
5064 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5065 {
5066  Q_UNUSED(details)
5067  if (onlySelectable)
5068  return -1;
5069 
5070  for (int i=0; i<mElements.size(); ++i)
5071  {
5072  // inset layout shall only return positive selectTest, if actually an inset object is at pos
5073  // else it would block the entire underlying QCPAxisRect with its surface.
5074  if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
5075  return mParentPlot->selectionTolerance()*0.99;
5076  }
5077  return -1;
5078 }
5079 
5091 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
5092 {
5093  if (element)
5094  {
5095  if (element->layout()) // remove from old layout first
5096  element->layout()->take(element);
5097  mElements.append(element);
5098  mInsetPlacement.append(ipBorderAligned);
5099  mInsetAlignment.append(alignment);
5100  mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
5101  adoptElement(element);
5102  } else
5103  qDebug() << Q_FUNC_INFO << "Can't add null element";
5104 }
5105 
5118 {
5119  if (element)
5120  {
5121  if (element->layout()) // remove from old layout first
5122  element->layout()->take(element);
5123  mElements.append(element);
5124  mInsetPlacement.append(ipFree);
5125  mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
5126  mInsetRect.append(rect);
5127  adoptElement(element);
5128  } else
5129  qDebug() << Q_FUNC_INFO << "Can't add null element";
5130 }
5131 /* end of 'src/layout.cpp' */
5132 
5133 
5134 /* including file 'src/lineending.cpp', size 11536 */
5135 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
5136 
5140 
5165  mStyle(esNone),
5166  mWidth(8),
5167  mLength(10),
5168  mInverted(false)
5169 {
5170 }
5171 
5176  mStyle(style),
5177  mWidth(width),
5178  mLength(length),
5179  mInverted(inverted)
5180 {
5181 }
5182 
5187 {
5188  mStyle = style;
5189 }
5190 
5198 {
5199  mWidth = width;
5200 }
5201 
5209 {
5210  mLength = length;
5211 }
5212 
5222 {
5223  mInverted = inverted;
5224 }
5225 
5236 {
5237  switch (mStyle)
5238  {
5239  case esNone:
5240  return 0;
5241 
5242  case esFlatArrow:
5243  case esSpikeArrow:
5244  case esLineArrow:
5245  case esSkewedBar:
5246  return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
5247 
5248  case esDisc:
5249  case esSquare:
5250  case esDiamond:
5251  case esBar:
5252  case esHalfBar:
5253  return mWidth*1.42; // items that only have a width -> width*sqrt(2)
5254 
5255  }
5256  return 0;
5257 }
5258 
5271 {
5272  switch (mStyle)
5273  {
5274  case esNone:
5275  case esLineArrow:
5276  case esSkewedBar:
5277  case esBar:
5278  case esHalfBar:
5279  return 0;
5280 
5281  case esFlatArrow:
5282  return mLength;
5283 
5284  case esDisc:
5285  case esSquare:
5286  case esDiamond:
5287  return mWidth*0.5;
5288 
5289  case esSpikeArrow:
5290  return mLength*0.8;
5291  }
5292  return 0;
5293 }
5294 
5300 void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const
5301 {
5302  if (mStyle == esNone)
5303  return;
5304 
5305  QCPVector2D lengthVec = dir.normalized() * mLength*(mInverted ? -1 : 1);
5306  if (lengthVec.isNull())
5307  lengthVec = QCPVector2D(1, 0);
5308  QCPVector2D widthVec = dir.normalized().perpendicular() * mWidth*0.5*(mInverted ? -1 : 1);
5309 
5310  QPen penBackup = painter->pen();
5311  QBrush brushBackup = painter->brush();
5312  QPen miterPen = penBackup;
5313  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
5314  QBrush brush(painter->pen().color(), Qt::SolidPattern);
5315  switch (mStyle)
5316  {
5317  case esNone: break;
5318  case esFlatArrow:
5319  {
5320  QPointF points[3] = {pos.toPointF(),
5321  (pos-lengthVec+widthVec).toPointF(),
5322  (pos-lengthVec-widthVec).toPointF()
5323  };
5324  painter->setPen(miterPen);
5325  painter->setBrush(brush);
5326  painter->drawConvexPolygon(points, 3);
5327  painter->setBrush(brushBackup);
5328  painter->setPen(penBackup);
5329  break;
5330  }
5331  case esSpikeArrow:
5332  {
5333  QPointF points[4] = {pos.toPointF(),
5334  (pos-lengthVec+widthVec).toPointF(),
5335  (pos-lengthVec*0.8).toPointF(),
5336  (pos-lengthVec-widthVec).toPointF()
5337  };
5338  painter->setPen(miterPen);
5339  painter->setBrush(brush);
5340  painter->drawConvexPolygon(points, 4);
5341  painter->setBrush(brushBackup);
5342  painter->setPen(penBackup);
5343  break;
5344  }
5345  case esLineArrow:
5346  {
5347  QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
5348  pos.toPointF(),
5349  (pos-lengthVec-widthVec).toPointF()
5350  };
5351  painter->setPen(miterPen);
5352  painter->drawPolyline(points, 3);
5353  painter->setPen(penBackup);
5354  break;
5355  }
5356  case esDisc:
5357  {
5358  painter->setBrush(brush);
5359  painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
5360  painter->setBrush(brushBackup);
5361  break;
5362  }
5363  case esSquare:
5364  {
5365  QCPVector2D widthVecPerp = widthVec.perpendicular();
5366  QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
5367  (pos-widthVecPerp-widthVec).toPointF(),
5368  (pos+widthVecPerp-widthVec).toPointF(),
5369  (pos+widthVecPerp+widthVec).toPointF()
5370  };
5371  painter->setPen(miterPen);
5372  painter->setBrush(brush);
5373  painter->drawConvexPolygon(points, 4);
5374  painter->setBrush(brushBackup);
5375  painter->setPen(penBackup);
5376  break;
5377  }
5378  case esDiamond:
5379  {
5380  QCPVector2D widthVecPerp = widthVec.perpendicular();
5381  QPointF points[4] = {(pos-widthVecPerp).toPointF(),
5382  (pos-widthVec).toPointF(),
5383  (pos+widthVecPerp).toPointF(),
5384  (pos+widthVec).toPointF()
5385  };
5386  painter->setPen(miterPen);
5387  painter->setBrush(brush);
5388  painter->drawConvexPolygon(points, 4);
5389  painter->setBrush(brushBackup);
5390  painter->setPen(penBackup);
5391  break;
5392  }
5393  case esBar:
5394  {
5395  painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
5396  break;
5397  }
5398  case esHalfBar:
5399  {
5400  painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
5401  break;
5402  }
5403  case esSkewedBar:
5404  {
5405  if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
5406  {
5407  // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
5408  painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)).toPointF(),
5409  (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)).toPointF());
5410  } else
5411  {
5412  // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
5413  painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
5414  (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
5415  }
5416  break;
5417  }
5418  }
5419 }
5420 
5426 void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double angle) const
5427 {
5428  draw(painter, pos, QCPVector2D(qCos(angle), qSin(angle)));
5429 }
5430 /* end of 'src/lineending.cpp' */
5431 
5432 
5433 /* including file 'src/axis/axisticker.cpp', size 18664 */
5434 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
5435 
5439 
5497  mTickStepStrategy(tssReadability),
5498  mTickCount(5),
5499  mTickOrigin(0)
5500 {
5501 }
5502 
5504 {
5505 
5506 }
5507 
5513 {
5514  mTickStepStrategy = strategy;
5515 }
5516 
5526 {
5527  if (count > 0)
5528  mTickCount = count;
5529  else
5530  qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count;
5531 }
5532 
5541 void QCPAxisTicker::setTickOrigin(double origin)
5542 {
5543  mTickOrigin = origin;
5544 }
5545 
5559 void QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector<double> &ticks, QVector<double> *subTicks, QVector<QString> *tickLabels)
5560 {
5561  // generate (major) ticks:
5562  double tickStep = getTickStep(range);
5563  ticks = createTickVector(tickStep, range);
5564  trimTicks(range, ticks, true); // trim ticks to visible range plus one outer tick on each side (incase a subclass createTickVector creates more)
5565 
5566  // generate sub ticks between major ticks:
5567  if (subTicks)
5568  {
5569  if (ticks.size() > 0)
5570  {
5571  *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks);
5572  trimTicks(range, *subTicks, false);
5573  } else
5574  *subTicks = QVector<double>();
5575  }
5576 
5577  // finally trim also outliers (no further clipping happens in axis drawing):
5578  trimTicks(range, ticks, false);
5579  // generate labels for visible ticks if requested:
5580  if (tickLabels)
5581  *tickLabels = createLabelVector(ticks, locale, formatChar, precision);
5582 }
5583 
5595 {
5596  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
5597  return cleanMantissa(exactStep);
5598 }
5599 
5608 {
5609  int result = 1; // default to 1, if no proper value can be found
5610 
5611  // separate integer and fractional part of mantissa:
5612  double epsilon = 0.01;
5613  double intPartf;
5614  int intPart;
5615  double fracPart = modf(getMantissa(tickStep), &intPartf);
5616  intPart = intPartf;
5617 
5618  // handle cases with (almost) integer mantissa:
5619  if (fracPart < epsilon || 1.0-fracPart < epsilon)
5620  {
5621  if (1.0-fracPart < epsilon)
5622  ++intPart;
5623  switch (intPart)
5624  {
5625  case 1: result = 4; break; // 1.0 -> 0.2 substep
5626  case 2: result = 3; break; // 2.0 -> 0.5 substep
5627  case 3: result = 2; break; // 3.0 -> 1.0 substep
5628  case 4: result = 3; break; // 4.0 -> 1.0 substep
5629  case 5: result = 4; break; // 5.0 -> 1.0 substep
5630  case 6: result = 2; break; // 6.0 -> 2.0 substep
5631  case 7: result = 6; break; // 7.0 -> 1.0 substep
5632  case 8: result = 3; break; // 8.0 -> 2.0 substep
5633  case 9: result = 2; break; // 9.0 -> 3.0 substep
5634  }
5635  } else
5636  {
5637  // handle cases with significantly fractional mantissa:
5638  if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5639  {
5640  switch (intPart)
5641  {
5642  case 1: result = 2; break; // 1.5 -> 0.5 substep
5643  case 2: result = 4; break; // 2.5 -> 0.5 substep
5644  case 3: result = 4; break; // 3.5 -> 0.7 substep
5645  case 4: result = 2; break; // 4.5 -> 1.5 substep
5646  case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on)
5647  case 6: result = 4; break; // 6.5 -> 1.3 substep
5648  case 7: result = 2; break; // 7.5 -> 2.5 substep
5649  case 8: result = 4; break; // 8.5 -> 1.7 substep
5650  case 9: result = 4; break; // 9.5 -> 1.9 substep
5651  }
5652  }
5653  // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5654  }
5655 
5656  return result;
5657 }
5658 
5670 QString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
5671 {
5672  return locale.toString(tick, formatChar.toLatin1(), precision);
5673 }
5674 
5684 QVector<double> QCPAxisTicker::createSubTickVector(int subTickCount, const QVector<double> &ticks)
5685 {
5686  QVector<double> result;
5687  if (subTickCount <= 0 || ticks.size() < 2)
5688  return result;
5689 
5690  result.reserve((ticks.size()-1)*subTickCount);
5691  for (int i=1; i<ticks.size(); ++i)
5692  {
5693  double subTickStep = (ticks.at(i)-ticks.at(i-1))/(double)(subTickCount+1);
5694  for (int k=1; k<=subTickCount; ++k)
5695  result.append(ticks.at(i-1) + k*subTickStep);
5696  }
5697  return result;
5698 }
5699 
5715 QVector<double> QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range)
5716 {
5717  QVector<double> result;
5718  // Generate tick positions according to tickStep:
5719  qint64 firstStep = floor((range.lower-mTickOrigin)/tickStep); // do not use qFloor here, or we'll lose 64 bit precision
5720  qint64 lastStep = ceil((range.upper-mTickOrigin)/tickStep); // do not use qCeil here, or we'll lose 64 bit precision
5721  int tickcount = lastStep-firstStep+1;
5722  if (tickcount < 0) tickcount = 0;
5723  result.resize(tickcount);
5724  for (int i=0; i<tickcount; ++i)
5725  result[i] = mTickOrigin + (firstStep+i)*tickStep;
5726  return result;
5727 }
5728 
5738 QVector<QString> QCPAxisTicker::createLabelVector(const QVector<double> &ticks, const QLocale &locale, QChar formatChar, int precision)
5739 {
5740  QVector<QString> result;
5741  result.reserve(ticks.size());
5742  for (int i=0; i<ticks.size(); ++i)
5743  result.append(getTickLabel(ticks.at(i), locale, formatChar, precision));
5744  return result;
5745 }
5746 
5754 void QCPAxisTicker::trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const
5755 {
5756  bool lowFound = false;
5757  bool highFound = false;
5758  int lowIndex = 0;
5759  int highIndex = -1;
5760 
5761  for (int i=0; i < ticks.size(); ++i)
5762  {
5763  if (ticks.at(i) >= range.lower)
5764  {
5765  lowFound = true;
5766  lowIndex = i;
5767  break;
5768  }
5769  }
5770  for (int i=ticks.size()-1; i >= 0; --i)
5771  {
5772  if (ticks.at(i) <= range.upper)
5773  {
5774  highFound = true;
5775  highIndex = i;
5776  break;
5777  }
5778  }
5779 
5780  if (highFound && lowFound)
5781  {
5782  int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0));
5783  int trimBack = qMax(0, ticks.size()-(keepOneOutlier ? 2 : 1)-highIndex);
5784  if (trimFront > 0 || trimBack > 0)
5785  ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack);
5786  } else // all ticks are either all below or all above the range
5787  ticks.clear();
5788 }
5789 
5796 double QCPAxisTicker::pickClosest(double target, const QVector<double> &candidates) const
5797 {
5798  if (candidates.size() == 1)
5799  return candidates.first();
5800  QVector<double>::const_iterator it = std::lower_bound(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 
5816 double QCPAxisTicker::getMantissa(double input, double *magnitude) const
5817 {
5818  const double mag = qPow(10.0, qFloor(qLn(input)/qLn(10.0)));
5819  if (magnitude) *magnitude = mag;
5820  return input/mag;
5821 }
5822 
5829 double QCPAxisTicker::cleanMantissa(double input) const
5830 {
5831  double magnitude;
5832  const double mantissa = getMantissa(input, &magnitude);
5833  switch (mTickStepStrategy)
5834  {
5835  case tssReadability:
5836  {
5837  return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude;
5838  }
5839  case tssMeetTickCount:
5840  {
5841  // this gives effectively a mantissa 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*magnitude; // round digit after decimal point to 0.5
5844  else
5845  return (int)(mantissa/2.0)*2.0*magnitude; // round to first digit in multiples of 2
5846  }
5847  }
5848  return input;
5849 }
5850 /* end of 'src/axis/axisticker.cpp' */
5851 
5852 
5853 /* including file 'src/axis/axistickerdatetime.cpp', size 14443 */
5854 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
5855 
5859 
5900  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
5901  mDateTimeSpec(Qt::LocalTime),
5902  mDateStrategy(dsNone)
5903 {
5904  setTickCount(4);
5905 }
5906 
5915 void QCPAxisTickerDateTime::setDateTimeFormat(const QString &format)
5916 {
5917  mDateTimeFormat = format;
5918 }
5919 
5931 {
5932  mDateTimeSpec = spec;
5933 }
5934 
5945 {
5947 }
5948 
5956 void QCPAxisTickerDateTime::setTickOrigin(const QDateTime &origin)
5957 {
5958  setTickOrigin(dateTimeToKey(origin));
5959 }
5960 
5975 {
5976  double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
5977 
5979  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds
5980  {
5981  result = cleanMantissa(result);
5982  } else if (result < 86400*30.4375*12) // below a year
5983  {
5984  result = pickClosest(result, QVector<double>()
5985  << 1 << 2.5 << 5 << 10 << 15 << 30 << 60 << 2.5*60 << 5*60 << 10*60 << 15*60 << 30*60 << 60*60 // second, minute, hour range
5986  << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24 // hour to day range
5987  << 86400*2 << 86400*5 << 86400*7 << 86400*14 << 86400*30.4375 << 86400*30.4375*2 << 86400*30.4375*3 << 86400*30.4375*6 << 86400*30.4375*12); // day, week, month range (avg. days per month includes leap years)
5988  if (result > 86400*30.4375-1) // month tick intervals or larger
5990  else if (result > 3600*24-1) // day tick intervals or larger
5992  } else // more than a year, go back to normal clean mantissa algorithm but in units of years
5993  {
5994  const double secondsPerYear = 86400*30.4375*12; // average including leap years
5995  result = cleanMantissa(result/secondsPerYear)*secondsPerYear;
5997  }
5998  return result;
5999 }
6000 
6009 {
6010  int result = QCPAxisTicker::getSubTickCount(tickStep);
6011  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day/week/month range (as specified in getTickStep)
6012  {
6013  case 5*60: result = 4; break;
6014  case 10*60: result = 1; break;
6015  case 15*60: result = 2; break;
6016  case 30*60: result = 1; break;
6017  case 60*60: result = 3; break;
6018  case 3600*2: result = 3; break;
6019  case 3600*3: result = 2; break;
6020  case 3600*6: result = 1; break;
6021  case 3600*12: result = 3; break;
6022  case 3600*24: result = 3; break;
6023  case 86400*2: result = 1; break;
6024  case 86400*5: result = 4; break;
6025  case 86400*7: result = 6; break;
6026  case 86400*14: result = 1; break;
6027  case (int)(86400*30.4375+0.5): result = 3; break;
6028  case (int)(86400*30.4375*2+0.5): result = 1; break;
6029  case (int)(86400*30.4375*3+0.5): result = 2; break;
6030  case (int)(86400*30.4375*6+0.5): result = 5; break;
6031  case (int)(86400*30.4375*12+0.5): result = 3; break;
6032  }
6033  return result;
6034 }
6035 
6043 QString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6044 {
6045  Q_UNUSED(precision)
6046  Q_UNUSED(formatChar)
6047  return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
6048 }
6049 
6057 QVector<double> QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range)
6058 {
6059  QVector<double> result = QCPAxisTicker::createTickVector(tickStep, range);
6060  if (!result.isEmpty())
6061  {
6063  {
6064  QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // the time of this datetime will be set for all other ticks, if possible
6065  QDateTime tickDateTime;
6066  for (int i=0; i<result.size(); ++i)
6067  {
6068  tickDateTime = keyToDateTime(result.at(i));
6069  tickDateTime.setTime(uniformDateTime.time());
6070  result[i] = dateTimeToKey(tickDateTime);
6071  }
6072  } else if (mDateStrategy == dsUniformDayInMonth)
6073  {
6074  QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // this day (in month) and time will be set for all other ticks, if possible
6075  QDateTime tickDateTime;
6076  for (int i=0; i<result.size(); ++i)
6077  {
6078  tickDateTime = keyToDateTime(result.at(i));
6079  tickDateTime.setTime(uniformDateTime.time());
6080  int thisUniformDay = uniformDateTime.date().day() <= tickDateTime.date().daysInMonth() ? uniformDateTime.date().day() : tickDateTime.date().daysInMonth(); // don't exceed month (e.g. try to set day 31 in February)
6081  if (thisUniformDay-tickDateTime.date().day() < -15) // with leap years involved, date month may jump backwards or forwards, and needs to be corrected before setting day
6082  tickDateTime = tickDateTime.addMonths(1);
6083  else if (thisUniformDay-tickDateTime.date().day() > 15) // with leap years involved, date month may jump backwards or forwards, and needs to be corrected before setting day
6084  tickDateTime = tickDateTime.addMonths(-1);
6085  tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay));
6086  result[i] = dateTimeToKey(tickDateTime);
6087  }
6088  }
6089  }
6090  return result;
6091 }
6092 
6103 {
6104 # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6105  return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000);
6106 # else
6107  return QDateTime::fromMSecsSinceEpoch(key*1000.0);
6108 # endif
6109 }
6110 
6122 double QCPAxisTickerDateTime::dateTimeToKey(const QDateTime dateTime)
6123 {
6124 # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6125  return dateTime.toTime_t()+dateTime.time().msec()/1000.0;
6126 # else
6127  return dateTime.toMSecsSinceEpoch()/1000.0;
6128 # endif
6129 }
6130 
6140 {
6141 # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6142  return QDateTime(date).toTime_t();
6143 # else
6144  return QDateTime(date).toMSecsSinceEpoch()/1000.0;
6145 # endif
6146 }
6147 /* end of 'src/axis/axistickerdatetime.cpp' */
6148 
6149 
6150 /* including file 'src/axis/axistickertime.cpp', size 11747 */
6151 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
6152 
6156 
6197  mTimeFormat(QLatin1String("%h:%m:%s")),
6198  mSmallestUnit(tuSeconds),
6199  mBiggestUnit(tuHours)
6200 {
6201  setTickCount(4);
6203  mFieldWidth[tuSeconds] = 2;
6204  mFieldWidth[tuMinutes] = 2;
6205  mFieldWidth[tuHours] = 2;
6206  mFieldWidth[tuDays] = 1;
6207 
6208  mFormatPattern[tuMilliseconds] = QLatin1String("%z");
6209  mFormatPattern[tuSeconds] = QLatin1String("%s");
6210  mFormatPattern[tuMinutes] = QLatin1String("%m");
6211  mFormatPattern[tuHours] = QLatin1String("%h");
6212  mFormatPattern[tuDays] = QLatin1String("%d");
6213 }
6214 
6233 void QCPAxisTickerTime::setTimeFormat(const QString &format)
6234 {
6235  mTimeFormat = format;
6236 
6237  // determine smallest and biggest unit in format, to optimize unit replacement and allow biggest
6238  // unit to consume remaining time of a tick value and grow beyond its modulo (e.g. min > 59)
6241  bool hasSmallest = false;
6242  for (int i = tuMilliseconds; i <= tuDays; ++i)
6243  {
6244  TimeUnit unit = static_cast<TimeUnit>(i);
6245  if (mTimeFormat.contains(mFormatPattern.value(unit)))
6246  {
6247  if (!hasSmallest)
6248  {
6249  mSmallestUnit = unit;
6250  hasSmallest = true;
6251  }
6252  mBiggestUnit = unit;
6253  }
6254  }
6255 }
6256 
6265 {
6266  mFieldWidth[unit] = qMax(width, 1);
6267 }
6268 
6279 {
6280  double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6281 
6282  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds
6283  {
6285  result = qMax(cleanMantissa(result), 0.001); // smallest tick step is 1 millisecond
6286  else // have no milliseconds available in format, so stick with 1 second tickstep
6287  result = 1.0;
6288  } else if (result < 3600*24) // below a day
6289  {
6290  // the filling of availableSteps seems a bit contorted but it fills in a sorted fashion and thus saves a post-fill sorting run
6291  QVector<double> availableSteps;
6292  // seconds range:
6293  if (mSmallestUnit <= tuSeconds)
6294  availableSteps << 1;
6296  availableSteps << 2.5; // only allow half second steps if milliseconds are there to display it
6297  else if (mSmallestUnit == tuSeconds)
6298  availableSteps << 2;
6299  if (mSmallestUnit <= tuSeconds)
6300  availableSteps << 5 << 10 << 15 << 30;
6301  // minutes range:
6302  if (mSmallestUnit <= tuMinutes)
6303  availableSteps << 1*60;
6304  if (mSmallestUnit <= tuSeconds)
6305  availableSteps << 2.5*60; // only allow half minute steps if seconds are there to display it
6306  else if (mSmallestUnit == tuMinutes)
6307  availableSteps << 2*60;
6308  if (mSmallestUnit <= tuMinutes)
6309  availableSteps << 5*60 << 10*60 << 15*60 << 30*60;
6310  // hours range:
6311  if (mSmallestUnit <= tuHours)
6312  availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600;
6313  // pick available step that is most appropriate to approximate ideal step:
6314  result = pickClosest(result, availableSteps);
6315  } else // more than a day, go back to normal clean mantissa algorithm but in units of days
6316  {
6317  const double secondsPerDay = 3600*24;
6318  result = cleanMantissa(result/secondsPerDay)*secondsPerDay;
6319  }
6320  return result;
6321 }
6322 
6330 {
6331  int result = QCPAxisTicker::getSubTickCount(tickStep);
6332  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day range (as specified in getTickStep)
6333  {
6334  case 5*60: result = 4; break;
6335  case 10*60: result = 1; break;
6336  case 15*60: result = 2; break;
6337  case 30*60: result = 1; break;
6338  case 60*60: result = 3; break;
6339  case 3600*2: result = 3; break;
6340  case 3600*3: result = 2; break;
6341  case 3600*6: result = 1; break;
6342  case 3600*12: result = 3; break;
6343  case 3600*24: result = 3; break;
6344  }
6345  return result;
6346 }
6347 
6355 QString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6356 {
6357  Q_UNUSED(precision)
6358  Q_UNUSED(formatChar)
6359  Q_UNUSED(locale)
6360  bool negative = tick < 0;
6361  if (negative) tick *= -1;
6362  double values[tuDays+1]; // contains the msec/sec/min/... value with its respective modulo (e.g. minute 0..59)
6363  double restValues[tuDays+1]; // contains the msec/sec/min/... value as if it's the largest available unit and thus consumes the remaining time
6364 
6365  restValues[tuMilliseconds] = tick*1000;
6366  values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000;
6367  values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60;
6368  values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60;
6369  values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24;
6370  // no need to set values[tuDays] because days are always a rest value (there is no higher unit so it consumes all remaining time)
6371 
6372  QString result = mTimeFormat;
6373  for (int i = mSmallestUnit; i <= mBiggestUnit; ++i)
6374  {
6375  TimeUnit iUnit = static_cast<TimeUnit>(i);
6376  replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit]));
6377  }
6378  if (negative)
6379  result.prepend(QLatin1Char('-'));
6380  return result;
6381 }
6382 
6388 void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const
6389 {
6390  QString valueStr = QString::number(value);
6391  while (valueStr.size() < mFieldWidth.value(unit))
6392  valueStr.prepend(QLatin1Char('0'));
6393 
6394  text.replace(mFormatPattern.value(unit), valueStr);
6395 }
6396 /* end of 'src/axis/axistickertime.cpp' */
6397 
6398 
6399 /* including file 'src/axis/axistickerfixed.cpp', size 5583 */
6400 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
6401 
6405 
6430  mTickStep(1.0),
6431  mScaleStrategy(ssNone)
6432 {
6433 }
6434 
6445 {
6446  if (step > 0)
6447  mTickStep = step;
6448  else
6449  qDebug() << Q_FUNC_INFO << "tick step must be greater than zero:" << step;
6450 }
6451 
6460 {
6461  mScaleStrategy = strategy;
6462 }
6463 
6475 {
6476  switch (mScaleStrategy)
6477  {
6478  case ssNone:
6479  {
6480  return mTickStep;
6481  }
6482  case ssMultiples:
6483  {
6484  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6485  if (exactStep < mTickStep)
6486  return mTickStep;
6487  else
6488  return (qint64)(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep;
6489  }
6490  case ssPowers:
6491  {
6492  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6493  return qPow(mTickStep, (int)(qLn(exactStep)/qLn(mTickStep)+0.5));
6494  }
6495  }
6496  return mTickStep;
6497 }
6498 /* end of 'src/axis/axistickerfixed.cpp' */
6499 
6500 
6501 /* including file 'src/axis/axistickertext.cpp', size 8661 */
6502 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
6503 
6507 
6527 /* start of documentation of inline functions */
6528 
6538 /* end of documentation of inline functions */
6539 
6545  mSubTickCount(0)
6546 {
6547 }
6548 
6559 void QCPAxisTickerText::setTicks(const QMap<double, QString> &ticks)
6560 {
6561  mTicks = ticks;
6562 }
6563 
6572 void QCPAxisTickerText::setTicks(const QVector<double> &positions, const QVector<QString> &labels)
6573 {
6574  clear();
6575  addTicks(positions, labels);
6576 }
6577 
6584 {
6585  if (subTicks >= 0)
6586  mSubTickCount = subTicks;
6587  else
6588  qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
6589 }
6590 
6600 {
6601  mTicks.clear();
6602 }
6603 
6610 void QCPAxisTickerText::addTick(double position, const QString &label)
6611 {
6612  mTicks.insert(position, label);
6613 }
6614 
6625 void QCPAxisTickerText::addTicks(const QMap<double, QString> &ticks)
6626 {
6627  mTicks.unite(ticks);
6628 }
6629 
6641 void QCPAxisTickerText::addTicks(const QVector<double> &positions, const QVector<QString> &labels)
6642 {
6643  if (positions.size() != labels.size())
6644  qDebug() << Q_FUNC_INFO << "passed unequal length vectors for positions and labels:" << positions.size() << labels.size();
6645  int n = qMin(positions.size(), labels.size());
6646  for (int i=0; i<n; ++i)
6647  mTicks.insert(positions.at(i), labels.at(i));
6648 }
6649 
6656 {
6657  // text axis ticker has manual tick positions, so doesn't need this method
6658  Q_UNUSED(range)
6659  return 1.0;
6660 }
6661 
6668 {
6669  Q_UNUSED(tickStep)
6670  return mSubTickCount;
6671 }
6672 
6679 QString QCPAxisTickerText::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6680 {
6681  Q_UNUSED(locale)
6682  Q_UNUSED(formatChar)
6683  Q_UNUSED(precision)
6684  return mTicks.value(tick);
6685 }
6686 
6694 QVector<double> QCPAxisTickerText::createTickVector(double tickStep, const QCPRange &range)
6695 {
6696  Q_UNUSED(tickStep)
6697  QVector<double> result;
6698  if (mTicks.isEmpty())
6699  return result;
6700 
6701  QMap<double, QString>::const_iterator start = mTicks.lowerBound(range.lower);
6702  QMap<double, QString>::const_iterator end = mTicks.upperBound(range.upper);
6703  // this method should try to give one tick outside of range so proper subticks can be generated:
6704  if (start != mTicks.constBegin()) --start;
6705  if (end != mTicks.constEnd()) ++end;
6706  for (QMap<double, QString>::const_iterator it = start; it != end; ++it)
6707  result.append(it.key());
6708 
6709  return result;
6710 }
6711 /* end of 'src/axis/axistickertext.cpp' */
6712 
6713 
6714 /* including file 'src/axis/axistickerpi.cpp', size 11170 */
6715 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
6716 
6720 
6741  mPiSymbol(QLatin1String(" ")+QChar(0x03C0)),
6742  mPiValue(M_PI),
6743  mPeriodicity(0),
6744  mFractionStyle(fsUnicodeFractions),
6745  mPiTickStep(0)
6746 {
6747  setTickCount(4);
6748 }
6749 
6757 void QCPAxisTickerPi::setPiSymbol(QString symbol)
6758 {
6759  mPiSymbol = symbol;
6760 }
6761 
6769 {
6770  mPiValue = pi;
6771 }
6772 
6781 void QCPAxisTickerPi::setPeriodicity(int multiplesOfPi)
6782 {
6783  mPeriodicity = qAbs(multiplesOfPi);
6784 }
6785 
6791 {
6792  mFractionStyle = style;
6793 }
6794 
6804 {
6805  mPiTickStep = range.size()/mPiValue/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6807  return mPiTickStep*mPiValue;
6808 }
6809 
6819 {
6820  return QCPAxisTicker::getSubTickCount(tickStep/mPiValue);
6821 }
6822 
6831 QString QCPAxisTickerPi::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6832 {
6833  double tickInPis = tick/mPiValue;
6834  if (mPeriodicity > 0)
6835  tickInPis = fmod(tickInPis, mPeriodicity);
6836 
6837  if (mFractionStyle != fsFloatingPoint && mPiTickStep > 0.09 && mPiTickStep < 50)
6838  {
6839  // simply construct fraction from decimal like 1.234 -> 1234/1000 and then simplify fraction, smaller digits are irrelevant due to mPiTickStep conditional above
6840  int denominator = 1000;
6841  int numerator = qRound(tickInPis*denominator);
6842  simplifyFraction(numerator, denominator);
6843  if (qAbs(numerator) == 1 && denominator == 1)
6844  return (numerator < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed();
6845  else if (numerator == 0)
6846  return QLatin1String("0");
6847  else
6848  return fractionToString(numerator, denominator) + mPiSymbol;
6849  } else
6850  {
6851  if (qFuzzyIsNull(tickInPis))
6852  return QLatin1String("0");
6853  else if (qFuzzyCompare(qAbs(tickInPis), 1.0))
6854  return (tickInPis < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed();
6855  else
6856  return QCPAxisTicker::getTickLabel(tickInPis, locale, formatChar, precision) + mPiSymbol;
6857  }
6858 }
6859 
6866 void QCPAxisTickerPi::simplifyFraction(int &numerator, int &denominator) const
6867 {
6868  if (numerator == 0 || denominator == 0)
6869  return;
6870 
6871  int num = numerator;
6872  int denom = denominator;
6873  while (denom != 0) // euclidean gcd algorithm
6874  {
6875  int oldDenom = denom;
6876  denom = num % denom;
6877  num = oldDenom;
6878  }
6879  // num is now gcd of numerator and denominator
6880  numerator /= num;
6881  denominator /= num;
6882 }
6883 
6893 QString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const
6894 {
6895  if (denominator == 0)
6896  {
6897  qDebug() << Q_FUNC_INFO << "called with zero denominator";
6898  return QString();
6899  }
6900  if (mFractionStyle == fsFloatingPoint) // should never be the case when calling this function
6901  {
6902  qDebug() << Q_FUNC_INFO << "shouldn't be called with fraction style fsDecimal";
6903  return QString::number(numerator/(double)denominator); // failsafe
6904  }
6905  int sign = numerator*denominator < 0 ? -1 : 1;
6906  numerator = qAbs(numerator);
6907  denominator = qAbs(denominator);
6908 
6909  if (denominator == 1)
6910  {
6911  return QString::number(sign*numerator);
6912  } else
6913  {
6914  int integerPart = numerator/denominator;
6915  int remainder = numerator%denominator;
6916  if (remainder == 0)
6917  {
6918  return QString::number(sign*integerPart);
6919  } else
6920  {
6922  {
6923  return QString(QLatin1String("%1%2%3/%4"))
6924  .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
6925  .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(" ") : QLatin1String(""))
6926  .arg(remainder)
6927  .arg(denominator);
6928  } else if (mFractionStyle == fsUnicodeFractions)
6929  {
6930  return QString(QLatin1String("%1%2%3"))
6931  .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
6932  .arg(integerPart > 0 ? QString::number(integerPart) : QLatin1String(""))
6933  .arg(unicodeFraction(remainder, denominator));
6934  }
6935  }
6936  }
6937  return QString();
6938 }
6939 
6949 QString QCPAxisTickerPi::unicodeFraction(int numerator, int denominator) const
6950 {
6951  return unicodeSuperscript(numerator)+QChar(0x2044)+unicodeSubscript(denominator);
6952 }
6953 
6959 QString QCPAxisTickerPi::unicodeSuperscript(int number) const
6960 {
6961  if (number == 0)
6962  return QString(QChar(0x2070));
6963 
6964  QString result;
6965  while (number > 0)
6966  {
6967  const int digit = number%10;
6968  switch (digit)
6969  {
6970  case 1: { result.prepend(QChar(0x00B9)); break; }
6971  case 2: { result.prepend(QChar(0x00B2)); break; }
6972  case 3: { result.prepend(QChar(0x00B3)); break; }
6973  default: { result.prepend(QChar(0x2070+digit)); break; }
6974  }
6975  number /= 10;
6976  }
6977  return result;
6978 }
6979 
6985 QString QCPAxisTickerPi::unicodeSubscript(int number) const
6986 {
6987  if (number == 0)
6988  return QString(QChar(0x2080));
6989 
6990  QString result;
6991  while (number > 0)
6992  {
6993  result.prepend(QChar(0x2080+number%10));
6994  number /= 10;
6995  }
6996  return result;
6997 }
6998 /* end of 'src/axis/axistickerpi.cpp' */
6999 
7000 
7001 /* including file 'src/axis/axistickerlog.cpp', size 7106 */
7002 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
7003 
7007 
7032  mLogBase(10.0),
7033  mSubTickCount(8), // generates 10 intervals
7034  mLogBaseLnInv(1.0/qLn(mLogBase))
7035 {
7036 }
7037 
7043 {
7044  if (base > 0)
7045  {
7046  mLogBase = base;
7047  mLogBaseLnInv = 1.0/qLn(mLogBase);
7048  } else
7049  qDebug() << Q_FUNC_INFO << "log base has to be greater than zero:" << base;
7050 }
7051 
7063 {
7064  if (subTicks >= 0)
7065  mSubTickCount = subTicks;
7066  else
7067  qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
7068 }
7069 
7078 {
7079  // Logarithmic axis ticker has unequal tick spacing, so doesn't need this method
7080  Q_UNUSED(range)
7081  return 1.0;
7082 }
7083 
7092 {
7093  Q_UNUSED(tickStep)
7094  return mSubTickCount;
7095 }
7096 
7106 QVector<double> QCPAxisTickerLog::createTickVector(double tickStep, const QCPRange &range)
7107 {
7108  Q_UNUSED(tickStep)
7109  QVector<double> result;
7110  if (range.lower > 0 && range.upper > 0) // positive range
7111  {
7112  double exactPowerStep = qLn(range.upper/range.lower)*mLogBaseLnInv/(double)(mTickCount+1e-10);
7113  double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
7114  double currentTick = qPow(newLogBase, qFloor(qLn(range.lower)/qLn(newLogBase)));
7115  result.append(currentTick);
7116  while (currentTick < range.upper && currentTick > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
7117  {
7118  currentTick *= newLogBase;
7119  result.append(currentTick);
7120  }
7121  } else if (range.lower < 0 && range.upper < 0) // negative range
7122  {
7123  double exactPowerStep = qLn(range.lower/range.upper)*mLogBaseLnInv/(double)(mTickCount+1e-10);
7124  double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
7125  double currentTick = -qPow(newLogBase, qCeil(qLn(-range.lower)/qLn(newLogBase)));
7126  result.append(currentTick);
7127  while (currentTick < range.upper && currentTick < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
7128  {
7129  currentTick /= newLogBase;
7130  result.append(currentTick);
7131  }
7132  } else // invalid range for logarithmic scale, because lower and upper have different sign
7133  {
7134  qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << range.lower << ".." << range.upper;
7135  }
7136 
7137  return result;
7138 }
7139 /* end of 'src/axis/axistickerlog.cpp' */
7140 
7141 
7142 /* including file 'src/axis/axis.cpp', size 99515 */
7143 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
7144 
7145 
7149 
7169  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
7170  mParentAxis(parentAxis)
7171 {
7172  // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
7173  setParent(parentAxis);
7174  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
7175  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
7176  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
7177  setSubGridVisible(false);
7178  setAntialiased(false);
7179  setAntialiasedSubGrid(false);
7180  setAntialiasedZeroLine(false);
7181 }
7182 
7189 {
7191 }
7192 
7197 {
7198  mAntialiasedSubGrid = enabled;
7199 }
7200 
7205 {
7206  mAntialiasedZeroLine = enabled;
7207 }
7208 
7212 void QCPGrid::setPen(const QPen &pen)
7213 {
7214  mPen = pen;
7215 }
7216 
7220 void QCPGrid::setSubGridPen(const QPen &pen)
7221 {
7222  mSubGridPen = pen;
7223 }
7224 
7231 void QCPGrid::setZeroLinePen(const QPen &pen)
7232 {
7233  mZeroLinePen = pen;
7234 }
7235 
7250 {
7252 }
7253 
7260 {
7261  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
7262 
7264  drawSubGridLines(painter);
7265  drawGridLines(painter);
7266 }
7267 
7275 {
7276  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
7277 
7278  const int tickCount = mParentAxis->mTickVector.size();
7279  double t; // helper variable, result of coordinate-to-pixel transforms
7280  if (mParentAxis->orientation() == Qt::Horizontal)
7281  {
7282  // draw zeroline:
7283  int zeroLineIndex = -1;
7284  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
7285  {
7287  painter->setPen(mZeroLinePen);
7288  double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
7289  for (int i=0; i<tickCount; ++i)
7290  {
7291  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
7292  {
7293  zeroLineIndex = i;
7294  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
7295  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
7296  break;
7297  }
7298  }
7299  }
7300  // draw grid lines:
7302  painter->setPen(mPen);
7303  for (int i=0; i<tickCount; ++i)
7304  {
7305  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
7306  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
7307  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
7308  }
7309  } else
7310  {
7311  // draw zeroline:
7312  int zeroLineIndex = -1;
7313  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
7314  {
7316  painter->setPen(mZeroLinePen);
7317  double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
7318  for (int i=0; i<tickCount; ++i)
7319  {
7320  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
7321  {
7322  zeroLineIndex = i;
7323  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
7324  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
7325  break;
7326  }
7327  }
7328  }
7329  // draw grid lines:
7331  painter->setPen(mPen);
7332  for (int i=0; i<tickCount; ++i)
7333  {
7334  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
7335  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
7336  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
7337  }
7338  }
7339 }
7340 
7348 {
7349  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
7350 
7352  double t; // helper variable, result of coordinate-to-pixel transforms
7353  painter->setPen(mSubGridPen);
7354  if (mParentAxis->orientation() == Qt::Horizontal)
7355  {
7356  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
7357  {
7359  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
7360  }
7361  } else
7362  {
7363  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
7364  {
7366  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
7367  }
7368  }
7369 }
7370 
7371 
7375 
7398 /* start of documentation of inline functions */
7399 
7456 /* end of documentation of inline functions */
7457 /* start of documentation of signals */
7458 
7497 /* end of documentation of signals */
7498 
7507  QCPLayerable(parent->parentPlot(), QString(), parent),
7508  // axis base:
7509  mAxisType(type),
7510  mAxisRect(parent),
7511  mPadding(5),
7512  mOrientation(orientation(type)),
7513  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
7514  mSelectedParts(spNone),
7515  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7516  mSelectedBasePen(QPen(Qt::blue, 2)),
7517  // axis label:
7518  mLabel(),
7519  mLabelFont(mParentPlot->font()),
7520  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
7521  mLabelColor(Qt::black),
7522  mSelectedLabelColor(Qt::blue),
7523  // tick labels:
7524  mTickLabels(true),
7525  mTickLabelFont(mParentPlot->font()),
7526  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
7527  mTickLabelColor(Qt::black),
7528  mSelectedTickLabelColor(Qt::blue),
7529  mNumberPrecision(6),
7530  mNumberFormatChar('g'),
7531  mNumberBeautifulPowers(true),
7532  // ticks and subticks:
7533  mTicks(true),
7534  mSubTicks(true),
7535  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7536  mSelectedTickPen(QPen(Qt::blue, 2)),
7537  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7538  mSelectedSubTickPen(QPen(Qt::blue, 2)),
7539  // scale and range:
7540  mRange(0, 5),
7541  mRangeReversed(false),
7542  mScaleType(stLinear),
7543  // internal members:
7544  mGrid(new QCPGrid(this)),
7545  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
7546  mTicker(new QCPAxisTicker),
7547  mCachedMarginValid(false),
7548  mCachedMargin(0)
7549 {
7550  setParent(parent);
7551  mGrid->setVisible(false);
7552  setAntialiased(false);
7553  setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again
7554 
7555  if (type == atTop)
7556  {
7558  setLabelPadding(6);
7559  } else if (type == atRight)
7560  {
7562  setLabelPadding(12);
7563  } else if (type == atBottom)
7564  {
7566  setLabelPadding(3);
7567  } else if (type == atLeft)
7568  {
7570  setLabelPadding(10);
7571  }
7572 }
7573 
7575 {
7576  delete mAxisPainter;
7577  delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
7578 }
7579 
7580 /* No documentation as it is a property getter */
7582 {
7584 }
7585 
7586 /* No documentation as it is a property getter */
7588 {
7590 }
7591 
7592 /* No documentation as it is a property getter */
7594 {
7595  return mAxisPainter->tickLabelSide;
7596 }
7597 
7598 /* No documentation as it is a property getter */
7599 QString QCPAxis::numberFormat() const
7600 {
7601  QString result;
7602  result.append(mNumberFormatChar);
7604  {
7605  result.append(QLatin1Char('b'));
7607  result.append(QLatin1Char('c'));
7608  }
7609  return result;
7610 }
7611 
7612 /* No documentation as it is a property getter */
7614 {
7615  return mAxisPainter->tickLengthIn;
7616 }
7617 
7618 /* No documentation as it is a property getter */
7620 {
7621  return mAxisPainter->tickLengthOut;
7622 }
7623 
7624 /* No documentation as it is a property getter */
7626 {
7627  return mAxisPainter->subTickLengthIn;
7628 }
7629 
7630 /* No documentation as it is a property getter */
7632 {
7634 }
7635 
7636 /* No documentation as it is a property getter */
7638 {
7639  return mAxisPainter->labelPadding;
7640 }
7641 
7642 /* No documentation as it is a property getter */
7643 int QCPAxis::offset() const
7644 {
7645  return mAxisPainter->offset;
7646 }
7647 
7648 /* No documentation as it is a property getter */
7650 {
7651  return mAxisPainter->lowerEnding;
7652 }
7653 
7654 /* No documentation as it is a property getter */
7656 {
7657  return mAxisPainter->upperEnding;
7658 }
7659 
7675 {
7676  if (mScaleType != type)
7677  {
7678  mScaleType = type;
7679  if (mScaleType == stLogarithmic)
7681  mCachedMarginValid = false;
7683  }
7684 }
7685 
7695 {
7696  if (range.lower == mRange.lower && range.upper == mRange.upper)
7697  return;
7698 
7699  if (!QCPRange::validRange(range)) return;
7700  QCPRange oldRange = mRange;
7701  if (mScaleType == stLogarithmic)
7702  {
7703  mRange = range.sanitizedForLogScale();
7704  } else
7705  {
7706  mRange = range.sanitizedForLinScale();
7707  }
7708  emit rangeChanged(mRange);
7709  emit rangeChanged(mRange, oldRange);
7710 }
7711 
7722 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
7723 {
7724  if (mSelectableParts != selectable)
7725  {
7726  mSelectableParts = selectable;
7728  }
7729 }
7730 
7746 void QCPAxis::setSelectedParts(const SelectableParts &selected)
7747 {
7748  if (mSelectedParts != selected)
7749  {
7750  mSelectedParts = selected;
7752  }
7753 }
7754 
7764 void QCPAxis::setRange(double lower, double upper)
7765 {
7766  if (lower == mRange.lower && upper == mRange.upper)
7767  return;
7768 
7769  if (!QCPRange::validRange(lower, upper)) return;
7770  QCPRange oldRange = mRange;
7771  mRange.lower = lower;
7772  mRange.upper = upper;
7773  if (mScaleType == stLogarithmic)
7774  {
7776  } else
7777  {
7779  }
7780  emit rangeChanged(mRange);
7781  emit rangeChanged(mRange, oldRange);
7782 }
7783 
7795 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
7796 {
7797  if (alignment == Qt::AlignLeft)
7798  setRange(position, position+size);
7799  else if (alignment == Qt::AlignRight)
7800  setRange(position-size, position);
7801  else // alignment == Qt::AlignCenter
7802  setRange(position-size/2.0, position+size/2.0);
7803 }
7804 
7809 void QCPAxis::setRangeLower(double lower)
7810 {
7811  if (mRange.lower == lower)
7812  return;
7813 
7814  QCPRange oldRange = mRange;
7815  mRange.lower = lower;
7816  if (mScaleType == stLogarithmic)
7817  {
7819  } else
7820  {
7822  }
7823  emit rangeChanged(mRange);
7824  emit rangeChanged(mRange, oldRange);
7825 }
7826 
7831 void QCPAxis::setRangeUpper(double upper)
7832 {
7833  if (mRange.upper == upper)
7834  return;
7835 
7836  QCPRange oldRange = mRange;
7837  mRange.upper = upper;
7838  if (mScaleType == stLogarithmic)
7839  {
7841  } else
7842  {
7844  }
7845  emit rangeChanged(mRange);
7846  emit rangeChanged(mRange, oldRange);
7847 }
7848 
7858 void QCPAxis::setRangeReversed(bool reversed)
7859 {
7860  mRangeReversed = reversed;
7861 }
7862 
7876 void QCPAxis::setTicker(QSharedPointer<QCPAxisTicker> ticker)
7877 {
7878  if (ticker)
7879  mTicker = ticker;
7880  else
7881  qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker";
7882  // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector
7883 }
7884 
7893 void QCPAxis::setTicks(bool show)
7894 {
7895  if (mTicks != show)
7896  {
7897  mTicks = show;
7898  mCachedMarginValid = false;
7899  }
7900 }
7901 
7905 void QCPAxis::setTickLabels(bool show)
7906 {
7907  if (mTickLabels != show)
7908  {
7909  mTickLabels = show;
7910  mCachedMarginValid = false;
7911  if (!mTickLabels)
7912  mTickVectorLabels.clear();
7913  }
7914 }
7915 
7921 {
7922  if (mAxisPainter->tickLabelPadding != padding)
7923  {
7925  mCachedMarginValid = false;
7926  }
7927 }
7928 
7934 void QCPAxis::setTickLabelFont(const QFont &font)
7935 {
7936  if (font != mTickLabelFont)
7937  {
7938  mTickLabelFont = font;
7939  mCachedMarginValid = false;
7940  }
7941 }
7942 
7948 void QCPAxis::setTickLabelColor(const QColor &color)
7949 {
7950  mTickLabelColor = color;
7951 }
7952 
7962 void QCPAxis::setTickLabelRotation(double degrees)
7963 {
7964  if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
7965  {
7966  mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
7967  mCachedMarginValid = false;
7968  }
7969 }
7970 
7979 {
7980  mAxisPainter->tickLabelSide = side;
7981  mCachedMarginValid = false;
7982 }
7983 
8014 void QCPAxis::setNumberFormat(const QString &formatCode)
8015 {
8016  if (formatCode.isEmpty())
8017  {
8018  qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
8019  return;
8020  }
8021  mCachedMarginValid = false;
8022 
8023  // interpret first char as number format char:
8024  QString allowedFormatChars(QLatin1String("eEfgG"));
8025  if (allowedFormatChars.contains(formatCode.at(0)))
8026  {
8027  mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
8028  } else
8029  {
8030  qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
8031  return;
8032  }
8033  if (formatCode.length() < 2)
8034  {
8035  mNumberBeautifulPowers = false;
8037  return;
8038  }
8039 
8040  // interpret second char as indicator for beautiful decimal powers:
8041  if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
8042  {
8043  mNumberBeautifulPowers = true;
8044  } else
8045  {
8046  qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
8047  return;
8048  }
8049  if (formatCode.length() < 3)
8050  {
8052  return;
8053  }
8054 
8055  // interpret third char as indicator for dot or cross multiplication symbol:
8056  if (formatCode.at(2) == QLatin1Char('c'))
8057  {
8059  } else if (formatCode.at(2) == QLatin1Char('d'))
8060  {
8062  } else
8063  {
8064  qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
8065  return;
8066  }
8067 }
8068 
8074 void QCPAxis::setNumberPrecision(int precision)
8075 {
8076  if (mNumberPrecision != precision)
8077  {
8078  mNumberPrecision = precision;
8079  mCachedMarginValid = false;
8080  }
8081 }
8082 
8091 void QCPAxis::setTickLength(int inside, int outside)
8092 {
8093  setTickLengthIn(inside);
8094  setTickLengthOut(outside);
8095 }
8096 
8104 {
8105  if (mAxisPainter->tickLengthIn != inside)
8106  {
8107  mAxisPainter->tickLengthIn = inside;
8108  }
8109 }
8110 
8118 void QCPAxis::setTickLengthOut(int outside)
8119 {
8120  if (mAxisPainter->tickLengthOut != outside)
8121  {
8122  mAxisPainter->tickLengthOut = outside;
8123  mCachedMarginValid = false; // only outside tick length can change margin
8124  }
8125 }
8126 
8134 void QCPAxis::setSubTicks(bool show)
8135 {
8136  if (mSubTicks != show)
8137  {
8138  mSubTicks = show;
8139  mCachedMarginValid = false;
8140  }
8141 }
8142 
8151 void QCPAxis::setSubTickLength(int inside, int outside)
8152 {
8153  setSubTickLengthIn(inside);
8154  setSubTickLengthOut(outside);
8155 }
8156 
8164 {
8165  if (mAxisPainter->subTickLengthIn != inside)
8166  {
8167  mAxisPainter->subTickLengthIn = inside;
8168  }
8169 }
8170 
8179 {
8180  if (mAxisPainter->subTickLengthOut != outside)
8181  {
8182  mAxisPainter->subTickLengthOut = outside;
8183  mCachedMarginValid = false; // only outside tick length can change margin
8184  }
8185 }
8186 
8192 void QCPAxis::setBasePen(const QPen &pen)
8193 {
8194  mBasePen = pen;
8195 }
8196 
8202 void QCPAxis::setTickPen(const QPen &pen)
8203 {
8204  mTickPen = pen;
8205 }
8206 
8212 void QCPAxis::setSubTickPen(const QPen &pen)
8213 {
8214  mSubTickPen = pen;
8215 }
8216 
8222 void QCPAxis::setLabelFont(const QFont &font)
8223 {
8224  if (mLabelFont != font)
8225  {
8226  mLabelFont = font;
8227  mCachedMarginValid = false;
8228  }
8229 }
8230 
8236 void QCPAxis::setLabelColor(const QColor &color)
8237 {
8238  mLabelColor = color;
8239 }
8240 
8245 void QCPAxis::setLabel(const QString &str)
8246 {
8247  if (mLabel != str)
8248  {
8249  mLabel = str;
8250  mCachedMarginValid = false;
8251  }
8252 }
8253 
8260 {
8261  if (mAxisPainter->labelPadding != padding)
8262  {
8264  mCachedMarginValid = false;
8265  }
8266 }
8267 
8279 {
8280  if (mPadding != padding)
8281  {
8282  mPadding = padding;
8283  mCachedMarginValid = false;
8284  }
8285 }
8286 
8296 {
8298 }
8299 
8305 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
8306 {
8307  if (font != mSelectedTickLabelFont)
8308  {
8309  mSelectedTickLabelFont = font;
8310  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
8311  }
8312 }
8313 
8319 void QCPAxis::setSelectedLabelFont(const QFont &font)
8320 {
8321  mSelectedLabelFont = font;
8322  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
8323 }
8324 
8330 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
8331 {
8332  if (color != mSelectedTickLabelColor)
8333  {
8334  mSelectedTickLabelColor = color;
8335  }
8336 }
8337 
8343 void QCPAxis::setSelectedLabelColor(const QColor &color)
8344 {
8345  mSelectedLabelColor = color;
8346 }
8347 
8353 void QCPAxis::setSelectedBasePen(const QPen &pen)
8354 {
8355  mSelectedBasePen = pen;
8356 }
8357 
8363 void QCPAxis::setSelectedTickPen(const QPen &pen)
8364 {
8365  mSelectedTickPen = pen;
8366 }
8367 
8373 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
8374 {
8375  mSelectedSubTickPen = pen;
8376 }
8377 
8389 {
8390  mAxisPainter->lowerEnding = ending;
8391 }
8392 
8404 {
8405  mAxisPainter->upperEnding = ending;
8406 }
8407 
8415 void QCPAxis::moveRange(double diff)
8416 {
8417  QCPRange oldRange = mRange;
8418  if (mScaleType == stLinear)
8419  {
8420  mRange.lower += diff;
8421  mRange.upper += diff;
8422  } else // mScaleType == stLogarithmic
8423  {
8424  mRange.lower *= diff;
8425  mRange.upper *= diff;
8426  }
8427  emit rangeChanged(mRange);
8428  emit rangeChanged(mRange, oldRange);
8429 }
8430 
8440 void QCPAxis::scaleRange(double factor)
8441 {
8442  scaleRange(factor, range().center());
8443 }
8444 
8454 void QCPAxis::scaleRange(double factor, double center)
8455 {
8456  QCPRange oldRange = mRange;
8457  if (mScaleType == stLinear)
8458  {
8459  QCPRange newRange;
8460  newRange.lower = (mRange.lower-center)*factor + center;
8461  newRange.upper = (mRange.upper-center)*factor + center;
8462  if (QCPRange::validRange(newRange))
8463  mRange = newRange.sanitizedForLinScale();
8464  } else // mScaleType == stLogarithmic
8465  {
8466  if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
8467  {
8468  QCPRange newRange;
8469  newRange.lower = qPow(mRange.lower/center, factor)*center;
8470  newRange.upper = qPow(mRange.upper/center, factor)*center;
8471  if (QCPRange::validRange(newRange))
8472  mRange = newRange.sanitizedForLogScale();
8473  } else
8474  qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
8475  }
8476  emit rangeChanged(mRange);
8477  emit rangeChanged(mRange, oldRange);
8478 }
8479 
8493 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
8494 {
8495  int otherPixelSize, ownPixelSize;
8496 
8497  if (otherAxis->orientation() == Qt::Horizontal)
8498  otherPixelSize = otherAxis->axisRect()->width();
8499  else
8500  otherPixelSize = otherAxis->axisRect()->height();
8501 
8502  if (orientation() == Qt::Horizontal)
8503  ownPixelSize = axisRect()->width();
8504  else
8505  ownPixelSize = axisRect()->height();
8506 
8507  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
8508  setRange(range().center(), newRangeSize, Qt::AlignCenter);
8509 }
8510 
8517 void QCPAxis::rescale(bool onlyVisiblePlottables)
8518 {
8519  QList<QCPAbstractPlottable*> p = plottables();
8520  QCPRange newRange;
8521  bool haveRange = false;
8522  for (int i=0; i<p.size(); ++i)
8523  {
8524  if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
8525  continue;
8526  QCPRange plottableRange;
8527  bool currentFoundRange;
8528  QCP::SignDomain signDomain = QCP::sdBoth;
8529  if (mScaleType == stLogarithmic)
8530  signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);
8531  if (p.at(i)->keyAxis() == this)
8532  plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
8533  else
8534  plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
8535  if (currentFoundRange)
8536  {
8537  if (!haveRange)
8538  newRange = plottableRange;
8539  else
8540  newRange.expand(plottableRange);
8541  haveRange = true;
8542  }
8543  }
8544  if (haveRange)
8545  {
8546  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
8547  {
8548  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
8549  if (mScaleType == stLinear)
8550  {
8551  newRange.lower = center-mRange.size()/2.0;
8552  newRange.upper = center+mRange.size()/2.0;
8553  } else // mScaleType == stLogarithmic
8554  {
8555  newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
8556  newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
8557  }
8558  }
8559  setRange(newRange);
8560  }
8561 }
8562 
8566 double QCPAxis::pixelToCoord(double value) const
8567 {
8568  if (orientation() == Qt::Horizontal)
8569  {
8570  if (mScaleType == stLinear)
8571  {
8572  if (!mRangeReversed)
8573  return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
8574  else
8575  return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
8576  } else // mScaleType == stLogarithmic
8577  {
8578  if (!mRangeReversed)
8579  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
8580  else
8581  return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
8582  }
8583  } else // orientation() == Qt::Vertical
8584  {
8585  if (mScaleType == stLinear)
8586  {
8587  if (!mRangeReversed)
8588  return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
8589  else
8590  return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
8591  } else // mScaleType == stLogarithmic
8592  {
8593  if (!mRangeReversed)
8594  return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
8595  else
8596  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
8597  }
8598  }
8599 }
8600 
8604 double QCPAxis::coordToPixel(double value) const
8605 {
8606  if (orientation() == Qt::Horizontal)
8607  {
8608  if (mScaleType == stLinear)
8609  {
8610  if (!mRangeReversed)
8611  return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
8612  else
8613  return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
8614  } else // mScaleType == stLogarithmic
8615  {
8616  if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range
8617  return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
8618  else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range
8619  return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
8620  else
8621  {
8622  if (!mRangeReversed)
8623  return qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
8624  else
8625  return qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
8626  }
8627  }
8628  } else // orientation() == Qt::Vertical
8629  {
8630  if (mScaleType == stLinear)
8631  {
8632  if (!mRangeReversed)
8633  return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
8634  else
8635  return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
8636  } else // mScaleType == stLogarithmic
8637  {
8638  if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range
8639  return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
8640  else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range
8641  return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
8642  else
8643  {
8644  if (!mRangeReversed)
8645  return mAxisRect->bottom()-qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->height();
8646  else
8647  return mAxisRect->bottom()-qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->height();
8648  }
8649  }
8650  }
8651 }
8652 
8663 {
8664  if (!mVisible)
8665  return spNone;
8666 
8667  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
8668  return spAxis;
8669  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
8670  return spTickLabels;
8671  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
8672  return spAxisLabel;
8673  else
8674  return spNone;
8675 }
8676 
8677 /* inherits documentation from base class */
8678 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
8679 {
8680  if (!mParentPlot) return -1;
8681  SelectablePart part = getPartAt(pos);
8682  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
8683  return -1;
8684 
8685  if (details)
8686  details->setValue(part);
8687  return mParentPlot->selectionTolerance()*0.99;
8688 }
8689 
8697 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
8698 {
8699  QList<QCPAbstractPlottable*> result;
8700  if (!mParentPlot) return result;
8701 
8702  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
8703  {
8704  if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
8705  result.append(mParentPlot->mPlottables.at(i));
8706  }
8707  return result;
8708 }
8709 
8715 QList<QCPGraph*> QCPAxis::graphs() const
8716 {
8717  QList<QCPGraph*> result;
8718  if (!mParentPlot) return result;
8719 
8720  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
8721  {
8722  if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
8723  result.append(mParentPlot->mGraphs.at(i));
8724  }
8725  return result;
8726 }
8727 
8734 QList<QCPAbstractItem*> QCPAxis::items() const
8735 {
8736  QList<QCPAbstractItem*> result;
8737  if (!mParentPlot) return result;
8738 
8739  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
8740  {
8741  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
8742  for (int posId=0; posId<positions.size(); ++posId)
8743  {
8744  if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
8745  {
8746  result.append(mParentPlot->mItems.at(itemId));
8747  break;
8748  }
8749  }
8750  }
8751  return result;
8752 }
8753 
8759 {
8760  switch (side)
8761  {
8762  case QCP::msLeft: return atLeft;
8763  case QCP::msRight: return atRight;
8764  case QCP::msTop: return atTop;
8765  case QCP::msBottom: return atBottom;
8766  default: break;
8767  }
8768  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
8769  return atLeft;
8770 }
8771 
8776 {
8777  switch (type)
8778  {
8779  case atLeft: return atRight; break;
8780  case atRight: return atLeft; break;
8781  case atBottom: return atTop; break;
8782  case atTop: return atBottom; break;
8783  default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
8784  }
8785 }
8786 
8787 /* inherits documentation from base class */
8788 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8789 {
8790  Q_UNUSED(event)
8791  SelectablePart part = details.value<SelectablePart>();
8792  if (mSelectableParts.testFlag(part))
8793  {
8794  SelectableParts selBefore = mSelectedParts;
8795  setSelectedParts(additive ? mSelectedParts^part : part);
8796  if (selectionStateChanged)
8797  *selectionStateChanged = mSelectedParts != selBefore;
8798  }
8799 }
8800 
8801 /* inherits documentation from base class */
8802 void QCPAxis::deselectEvent(bool *selectionStateChanged)
8803 {
8804  SelectableParts selBefore = mSelectedParts;
8806  if (selectionStateChanged)
8807  *selectionStateChanged = mSelectedParts != selBefore;
8808 }
8809 
8825 void QCPAxis::mousePressEvent(QMouseEvent *event, const QVariant &details)
8826 {
8827  Q_UNUSED(details)
8828  if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag) ||
8829  !mAxisRect->rangeDrag().testFlag(orientation()) ||
8830  !mAxisRect->rangeDragAxes(orientation()).contains(this))
8831  {
8832  event->ignore();
8833  return;
8834  }
8835 
8836  if (event->buttons() & Qt::LeftButton)
8837  {
8838  mDragging = true;
8839  // initialize antialiasing backup in case we start dragging:
8841  {
8844  }
8845  // Mouse range dragging interaction:
8846  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
8848  }
8849 }
8850 
8863 void QCPAxis::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
8864 {
8865  if (mDragging)
8866  {
8867  const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y();
8868  const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y();
8870  {
8871  const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel);
8873  } else if (mScaleType == QCPAxis::stLogarithmic)
8874  {
8875  const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel);
8877  }
8878 
8882  }
8883 }
8884 
8897 void QCPAxis::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
8898 {
8899  Q_UNUSED(event)
8900  Q_UNUSED(startPos)
8901  mDragging = false;
8903  {
8906  }
8907 }
8908 
8924 void QCPAxis::wheelEvent(QWheelEvent *event)
8925 {
8926  // Mouse range zooming interaction:
8927  if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom) ||
8928  !mAxisRect->rangeZoom().testFlag(orientation()) ||
8929  !mAxisRect->rangeZoomAxes(orientation()).contains(this))
8930  {
8931  event->ignore();
8932  return;
8933  }
8934 
8935  const double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
8936  const double factor = qPow(mAxisRect->rangeZoomFactor(orientation()), wheelSteps);
8937  scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y()));
8938  mParentPlot->replot();
8939 }
8940 
8957 {
8959 }
8960 
8968 {
8969  QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
8970  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
8971  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
8972  tickPositions.reserve(mTickVector.size());
8973  tickLabels.reserve(mTickVector.size());
8974  subTickPositions.reserve(mSubTickVector.size());
8975 
8976  if (mTicks)
8977  {
8978  for (int i=0; i<mTickVector.size(); ++i)
8979  {
8980  tickPositions.append(coordToPixel(mTickVector.at(i)));
8981  if (mTickLabels)
8982  tickLabels.append(mTickVectorLabels.at(i));
8983  }
8984 
8985  if (mSubTicks)
8986  {
8987  const int subTickCount = mSubTickVector.size();
8988  for (int i=0; i<subTickCount; ++i)
8989  subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
8990  }
8991  }
8992 
8993  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
8994  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
9009  mAxisPainter->tickPositions = tickPositions;
9011  mAxisPainter->subTickPositions = subTickPositions;
9012  mAxisPainter->draw(painter);
9013 }
9014 
9024 {
9025  if (!mParentPlot) return;
9026  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
9027 
9028  QVector<QString> oldLabels = mTickVectorLabels;
9030  mCachedMarginValid &= mTickVectorLabels == oldLabels; // if labels have changed, margin might have changed, too
9031 }
9032 
9039 {
9040  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
9041 }
9042 
9049 {
9050  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
9051 }
9052 
9059 {
9061 }
9062 
9069 {
9071 }
9072 
9079 {
9081 }
9082 
9089 {
9091 }
9092 
9099 {
9101 }
9102 
9118 {
9119  if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
9120  return 0;
9121 
9122  if (mCachedMarginValid)
9123  return mCachedMargin;
9124 
9125  // run through similar steps as QCPAxis::draw, and calculate margin needed to fit axis and its labels
9126  int margin = 0;
9127 
9128  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
9129  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
9130  tickPositions.reserve(mTickVector.size());
9131  tickLabels.reserve(mTickVector.size());
9132 
9133  if (mTicks)
9134  {
9135  for (int i=0; i<mTickVector.size(); ++i)
9136  {
9137  tickPositions.append(coordToPixel(mTickVector.at(i)));
9138  if (mTickLabels)
9139  tickLabels.append(mTickVectorLabels.at(i));
9140  }
9141  }
9142  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
9143  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
9150  mAxisPainter->tickPositions = tickPositions;
9152  margin += mAxisPainter->size();
9153  margin += mPadding;
9154 
9155  mCachedMargin = margin;
9156  mCachedMarginValid = true;
9157  return margin;
9158 }
9159 
9160 /* inherits documentation from base class */
9162 {
9163  return QCP::iSelectAxes;
9164 }
9165 
9166 
9170 
9188  type(QCPAxis::atLeft),
9189  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
9190  lowerEnding(QCPLineEnding::esNone),
9191  upperEnding(QCPLineEnding::esNone),
9192  labelPadding(0),
9193  tickLabelPadding(0),
9194  tickLabelRotation(0),
9196  substituteExponent(true),
9197  numberMultiplyCross(false),
9198  tickLengthIn(5),
9199  tickLengthOut(0),
9200  subTickLengthIn(2),
9201  subTickLengthOut(0),
9202  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
9203  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
9204  offset(0),
9205  abbreviateDecimalPowers(false),
9206  reversedEndings(false),
9207  mParentPlot(parentPlot),
9208  mLabelCache(16) // cache at most 16 (tick) labels
9209 {
9210 }
9211 
9213 {
9214 }
9215 
9224 {
9225  QByteArray newHash = generateLabelParameterHash();
9226  if (newHash != mLabelParameterHash)
9227  {
9228  mLabelCache.clear();
9229  mLabelParameterHash = newHash;
9230  }
9231 
9232  QPoint origin;
9233  switch (type)
9234  {
9235  case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
9236  case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
9237  case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break;
9238  case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
9239  }
9240 
9241  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
9242  switch (type)
9243  {
9244  case QCPAxis::atTop: yCor = -1; break;
9245  case QCPAxis::atRight: xCor = 1; break;
9246  default: break;
9247  }
9248  int margin = 0;
9249  // draw baseline:
9250  QLineF baseLine;
9251  painter->setPen(basePen);
9252  if (QCPAxis::orientation(type) == Qt::Horizontal)
9253  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
9254  else
9255  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
9256  if (reversedEndings)
9257  baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
9258  painter->drawLine(baseLine);
9259 
9260  // draw ticks:
9261  if (!tickPositions.isEmpty())
9262  {
9263  painter->setPen(tickPen);
9264  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
9265  if (QCPAxis::orientation(type) == Qt::Horizontal)
9266  {
9267  for (int i=0; i<tickPositions.size(); ++i)
9268  painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
9269  } else
9270  {
9271  for (int i=0; i<tickPositions.size(); ++i)
9272  painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
9273  }
9274  }
9275 
9276  // draw subticks:
9277  if (!subTickPositions.isEmpty())
9278  {
9279  painter->setPen(subTickPen);
9280  // direction of ticks ("inward" is right for left axis and left for right axis)
9281  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
9282  if (QCPAxis::orientation(type) == Qt::Horizontal)
9283  {
9284  for (int i=0; i<subTickPositions.size(); ++i)
9285  painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
9286  } else
9287  {
9288  for (int i=0; i<subTickPositions.size(); ++i)
9289  painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
9290  }
9291  }
9292  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
9293 
9294  // draw axis base endings:
9295  bool antialiasingBackup = painter->antialiasing();
9296  painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
9297  painter->setBrush(QBrush(basePen.color()));
9298  QCPVector2D baseLineVector(baseLine.dx(), baseLine.dy());
9300  lowerEnding.draw(painter, QCPVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
9302  upperEnding.draw(painter, QCPVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
9303  painter->setAntialiasing(antialiasingBackup);
9304 
9305  // tick labels:
9306  QRect oldClipRect;
9307  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
9308  {
9309  oldClipRect = painter->clipRegion().boundingRect();
9310  painter->setClipRect(axisRect);
9311  }
9312  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
9313  if (!tickLabels.isEmpty())
9314  {
9316  margin += tickLabelPadding;
9317  painter->setFont(tickLabelFont);
9318  painter->setPen(QPen(tickLabelColor));
9319  const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
9320  int distanceToAxis = margin;
9322  distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
9323  for (int i=0; i<maxLabelIndex; ++i)
9324  placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
9326  margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
9327  }
9329  painter->setClipRect(oldClipRect);
9330 
9331  // axis label:
9332  QRect labelBounds;
9333  if (!label.isEmpty())
9334  {
9335  margin += labelPadding;
9336  painter->setFont(labelFont);
9337  painter->setPen(QPen(labelColor));
9338  labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
9339  if (type == QCPAxis::atLeft)
9340  {
9341  QTransform oldTransform = painter->transform();
9342  painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
9343  painter->rotate(-90);
9344  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9345  painter->setTransform(oldTransform);
9346  }
9347  else if (type == QCPAxis::atRight)
9348  {
9349  QTransform oldTransform = painter->transform();
9350  painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
9351  painter->rotate(90);
9352  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9353  painter->setTransform(oldTransform);
9354  }
9355  else if (type == QCPAxis::atTop)
9356  painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9357  else if (type == QCPAxis::atBottom)
9358  painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9359  }
9360 
9361  // set selection boxes:
9362  int selectionTolerance = 0;
9363  if (mParentPlot)
9364  selectionTolerance = mParentPlot->selectionTolerance();
9365  else
9366  qDebug() << Q_FUNC_INFO << "mParentPlot is null";
9367  int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
9368  int selAxisInSize = selectionTolerance;
9369  int selTickLabelSize;
9370  int selTickLabelOffset;
9372  {
9373  selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
9374  selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
9375  } else
9376  {
9377  selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
9378  selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
9379  }
9380  int selLabelSize = labelBounds.height();
9381  int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
9382  if (type == QCPAxis::atLeft)
9383  {
9384  mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
9385  mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
9386  mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
9387  } else if (type == QCPAxis::atRight)
9388  {
9389  mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
9390  mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
9391  mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
9392  } else if (type == QCPAxis::atTop)
9393  {
9394  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
9395  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
9396  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
9397  } else if (type == QCPAxis::atBottom)
9398  {
9399  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
9400  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
9401  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
9402  }
9403  mAxisSelectionBox = mAxisSelectionBox.normalized();
9405  mLabelSelectionBox = mLabelSelectionBox.normalized();
9406  // draw hitboxes for debug purposes:
9407  //painter->setBrush(Qt::NoBrush);
9408  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
9409 }
9410 
9417 {
9418  int result = 0;
9419 
9420  // get length of tick marks pointing outwards:
9421  if (!tickPositions.isEmpty())
9422  result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
9423 
9424  // calculate size of tick labels:
9426  {
9427  QSize tickLabelsSize(0, 0);
9428  if (!tickLabels.isEmpty())
9429  {
9430  for (int i=0; i<tickLabels.size(); ++i)
9431  getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
9432  result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
9433  result += tickLabelPadding;
9434  }
9435  }
9436 
9437  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
9438  if (!label.isEmpty())
9439  {
9440  QFontMetrics fontMetrics(labelFont);
9441  QRect bounds;
9442  bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
9443  result += bounds.height() + labelPadding;
9444  }
9445 
9446  return result;
9447 }
9448 
9456 {
9457  mLabelCache.clear();
9458 }
9459 
9468 {
9469  QByteArray result;
9470  result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));
9471  result.append(QByteArray::number(tickLabelRotation));
9472  result.append(QByteArray::number((int)tickLabelSide));
9473  result.append(QByteArray::number((int)substituteExponent));
9474  result.append(QByteArray::number((int)numberMultiplyCross));
9475  result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
9476  result.append(tickLabelFont.toString().toLatin1());
9477  return result;
9478 }
9479 
9499 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
9500 {
9501  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
9502  if (text.isEmpty()) return;
9503  QSize finalSize;
9504  QPointF labelAnchor;
9505  switch (type)
9506  {
9507  case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
9508  case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
9509  case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
9510  case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
9511  }
9512  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
9513  {
9514  CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache
9515  if (!cachedLabel) // no cached label existed, create it
9516  {
9517  cachedLabel = new CachedLabel;
9518  TickLabelData labelData = getTickLabelData(painter->font(), text);
9519  cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
9520  if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio()))
9521  {
9522  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio());
9523 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
9524 # ifdef QCP_DEVICEPIXELRATIO_FLOAT
9525  cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF());
9526 # else
9527  cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio());
9528 # endif
9529 #endif
9530  } else
9531  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
9532  cachedLabel->pixmap.fill(Qt::transparent);
9533  QCPPainter cachePainter(&cachedLabel->pixmap);
9534  cachePainter.setPen(painter->pen());
9535  drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
9536  }
9537  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
9538  bool labelClippedByBorder = false;
9540  {
9541  if (QCPAxis::orientation(type) == Qt::Horizontal)
9542  labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
9543  else
9544  labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
9545  }
9546  if (!labelClippedByBorder)
9547  {
9548  painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
9549  finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
9550  }
9551  mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created
9552  } else // label caching disabled, draw text directly on surface:
9553  {
9554  TickLabelData labelData = getTickLabelData(painter->font(), text);
9555  QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
9556  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
9557  bool labelClippedByBorder = false;
9559  {
9560  if (QCPAxis::orientation(type) == Qt::Horizontal)
9561  labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
9562  else
9563  labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
9564  }
9565  if (!labelClippedByBorder)
9566  {
9567  drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
9568  finalSize = labelData.rotatedTotalBounds.size();
9569  }
9570  }
9571 
9572  // expand passed tickLabelsSize if current tick label is larger:
9573  if (finalSize.width() > tickLabelsSize->width())
9574  tickLabelsSize->setWidth(finalSize.width());
9575  if (finalSize.height() > tickLabelsSize->height())
9576  tickLabelsSize->setHeight(finalSize.height());
9577 }
9578 
9588 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
9589 {
9590  // backup painter settings that we're about to change:
9591  QTransform oldTransform = painter->transform();
9592  QFont oldFont = painter->font();
9593 
9594  // transform painter to position/rotation:
9595  painter->translate(x, y);
9596  if (!qFuzzyIsNull(tickLabelRotation))
9597  painter->rotate(tickLabelRotation);
9598 
9599  // draw text:
9600  if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
9601  {
9602  painter->setFont(labelData.baseFont);
9603  painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
9604  if (!labelData.suffixPart.isEmpty())
9605  painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart);
9606  painter->setFont(labelData.expFont);
9607  painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
9608  } else
9609  {
9610  painter->setFont(labelData.baseFont);
9611  painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
9612  }
9613 
9614  // reset painter settings to what it was before:
9615  painter->setTransform(oldTransform);
9616  painter->setFont(oldFont);
9617 }
9618 
9628 {
9629  TickLabelData result;
9630 
9631  // determine whether beautiful decimal powers should be used
9632  bool useBeautifulPowers = false;
9633  int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart
9634  int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart
9635  if (substituteExponent)
9636  {
9637  ePos = text.indexOf(QLatin1Char('e'));
9638  if (ePos > 0 && text.at(ePos-1).isDigit())
9639  {
9640  eLast = ePos;
9641  while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))
9642  ++eLast;
9643  if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power
9644  useBeautifulPowers = true;
9645  }
9646  }
9647 
9648  // calculate text bounding rects and do string preparation for beautiful decimal powers:
9649  result.baseFont = font;
9650  if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
9651  result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
9652  if (useBeautifulPowers)
9653  {
9654  // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
9655  result.basePart = text.left(ePos);
9656  result.suffixPart = text.mid(eLast+1); // also drawn normally but after exponent
9657  // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
9658  if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
9659  result.basePart = QLatin1String("10");
9660  else
9661  result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
9662  result.expPart = text.mid(ePos+1, eLast-ePos);
9663  // clip "+" and leading zeros off expPart:
9664  while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
9665  result.expPart.remove(1, 1);
9666  if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
9667  result.expPart.remove(0, 1);
9668  // prepare smaller font for exponent:
9669  result.expFont = font;
9670  if (result.expFont.pointSize() > 0)
9671  result.expFont.setPointSize(result.expFont.pointSize()*0.75);
9672  else
9673  result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
9674  // calculate bounding rects of base part(s), exponent part and total one:
9675  result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
9676  result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
9677  if (!result.suffixPart.isEmpty())
9678  result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart);
9679  result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+result.suffixBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA
9680  } else // useBeautifulPowers == false
9681  {
9682  result.basePart = text;
9683  result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
9684  }
9685  result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler
9686 
9687  // calculate possibly different bounding rect after rotation:
9688  result.rotatedTotalBounds = result.totalBounds;
9689  if (!qFuzzyIsNull(tickLabelRotation))
9690  {
9691  QTransform transform;
9692  transform.rotate(tickLabelRotation);
9693  result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
9694  }
9695 
9696  return result;
9697 }
9698 
9710 {
9711  /*
9712  calculate label offset from base point at tick (non-trivial, for best visual appearance): short
9713  explanation for bottom axis: The anchor, i.e. the point in the label that is placed
9714  horizontally under the corresponding tick is always on the label side that is closer to the
9715  axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
9716  is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
9717  will be centered under the tick (i.e. displaced horizontally by half its height). At the same
9718  time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
9719  labels.
9720  */
9721  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
9722  bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
9723  double radians = tickLabelRotation/180.0*M_PI;
9724  int x=0, y=0;
9725  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
9726  {
9727  if (doRotation)
9728  {
9729  if (tickLabelRotation > 0)
9730  {
9731  x = -qCos(radians)*labelData.totalBounds.width();
9732  y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
9733  } else
9734  {
9735  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
9736  y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
9737  }
9738  } else
9739  {
9740  x = -labelData.totalBounds.width();
9741  y = -labelData.totalBounds.height()/2.0;
9742  }
9743  } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
9744  {
9745  if (doRotation)
9746  {
9747  if (tickLabelRotation > 0)
9748  {
9749  x = +qSin(radians)*labelData.totalBounds.height();
9750  y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
9751  } else
9752  {
9753  x = 0;
9754  y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
9755  }
9756  } else
9757  {
9758  x = 0;
9759  y = -labelData.totalBounds.height()/2.0;
9760  }
9761  } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
9762  {
9763  if (doRotation)
9764  {
9765  if (tickLabelRotation > 0)
9766  {
9767  x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
9768  y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
9769  } else
9770  {
9771  x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
9772  y = -qCos(-radians)*labelData.totalBounds.height();
9773  }
9774  } else
9775  {
9776  x = -labelData.totalBounds.width()/2.0;
9777  y = -labelData.totalBounds.height();
9778  }
9779  } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
9780  {
9781  if (doRotation)
9782  {
9783  if (tickLabelRotation > 0)
9784  {
9785  x = +qSin(radians)*labelData.totalBounds.height()/2.0;
9786  y = 0;
9787  } else
9788  {
9789  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
9790  y = +qSin(-radians)*labelData.totalBounds.width();
9791  }
9792  } else
9793  {
9794  x = -labelData.totalBounds.width()/2.0;
9795  y = 0;
9796  }
9797  }
9798 
9799  return QPointF(x, y);
9800 }
9801 
9809 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
9810 {
9811  // note: this function must return the same tick label sizes as the placeTickLabel function.
9812  QSize finalSize;
9813  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
9814  {
9815  const CachedLabel *cachedLabel = mLabelCache.object(text);
9816  finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
9817  } else // label caching disabled or no label with this text cached:
9818  {
9819  TickLabelData labelData = getTickLabelData(font, text);
9820  finalSize = labelData.rotatedTotalBounds.size();
9821  }
9822 
9823  // expand passed tickLabelsSize if current tick label is larger:
9824  if (finalSize.width() > tickLabelsSize->width())
9825  tickLabelsSize->setWidth(finalSize.width());
9826  if (finalSize.height() > tickLabelsSize->height())
9827  tickLabelsSize->setHeight(finalSize.height());
9828 }
9829 /* end of 'src/axis/axis.cpp' */
9830 
9831 
9832 /* including file 'src/scatterstyle.cpp', size 17450 */
9833 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
9834 
9838 
9889 /* start documentation of inline functions */
9890 
9912 /* end documentation of inline functions */
9913 
9921  mSize(6),
9922  mShape(ssNone),
9923  mPen(Qt::NoPen),
9924  mBrush(Qt::NoBrush),
9925  mPenDefined(false)
9926 {
9927 }
9928 
9937  mSize(size),
9938  mShape(shape),
9939  mPen(Qt::NoPen),
9940  mBrush(Qt::NoBrush),
9941  mPenDefined(false)
9942 {
9943 }
9944 
9950  mSize(size),
9951  mShape(shape),
9952  mPen(QPen(color)),
9953  mBrush(Qt::NoBrush),
9954  mPenDefined(true)
9955 {
9956 }
9957 
9962 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
9963  mSize(size),
9964  mShape(shape),
9965  mPen(QPen(color)),
9966  mBrush(QBrush(fill)),
9967  mPenDefined(true)
9968 {
9969 }
9970 
9986 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
9987  mSize(size),
9988  mShape(shape),
9989  mPen(pen),
9990  mBrush(brush),
9991  mPenDefined(pen.style() != Qt::NoPen)
9992 {
9993 }
9994 
10000  mSize(5),
10001  mShape(ssPixmap),
10002  mPen(Qt::NoPen),
10003  mBrush(Qt::NoBrush),
10004  mPixmap(pixmap),
10005  mPenDefined(false)
10006 {
10007 }
10008 
10018 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
10019  mSize(size),
10020  mShape(ssCustom),
10021  mPen(pen),
10022  mBrush(brush),
10023  mCustomPath(customPath),
10024  mPenDefined(pen.style() != Qt::NoPen)
10025 {
10026 }
10027 
10031 void QCPScatterStyle::setFromOther(const QCPScatterStyle &other, ScatterProperties properties)
10032 {
10033  if (properties.testFlag(spPen))
10034  {
10035  setPen(other.pen());
10036  if (!other.isPenDefined())
10037  undefinePen();
10038  }
10039  if (properties.testFlag(spBrush))
10040  setBrush(other.brush());
10041  if (properties.testFlag(spSize))
10042  setSize(other.size());
10043  if (properties.testFlag(spShape))
10044  {
10045  setShape(other.shape());
10046  if (other.shape() == ssPixmap)
10047  setPixmap(other.pixmap());
10048  else if (other.shape() == ssCustom)
10049  setCustomPath(other.customPath());
10050  }
10051 }
10052 
10059 {
10060  mSize = size;
10061 }
10062 
10072 {
10073  mShape = shape;
10074 }
10075 
10085 void QCPScatterStyle::setPen(const QPen &pen)
10086 {
10087  mPenDefined = true;
10088  mPen = pen;
10089 }
10090 
10098 {
10099  mBrush = brush;
10100 }
10101 
10110 {
10111  setShape(ssPixmap);
10112  mPixmap = pixmap;
10113 }
10114 
10121 {
10122  setShape(ssCustom);
10124 }
10125 
10133 {
10134  mPenDefined = false;
10135 }
10136 
10146 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
10147 {
10148  painter->setPen(mPenDefined ? mPen : defaultPen);
10149  painter->setBrush(mBrush);
10150 }
10151 
10160 void QCPScatterStyle::drawShape(QCPPainter *painter, const QPointF &pos) const
10161 {
10162  drawShape(painter, pos.x(), pos.y());
10163 }
10164 
10168 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
10169 {
10170  double w = mSize/2.0;
10171  switch (mShape)
10172  {
10173  case ssNone: break;
10174  case ssDot:
10175  {
10176  painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
10177  break;
10178  }
10179  case ssCross:
10180  {
10181  painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
10182  painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
10183  break;
10184  }
10185  case ssPlus:
10186  {
10187  painter->drawLine(QLineF(x-w, y, x+w, y));
10188  painter->drawLine(QLineF( x, y+w, x, y-w));
10189  break;
10190  }
10191  case ssCircle:
10192  {
10193  painter->drawEllipse(QPointF(x , y), w, w);
10194  break;
10195  }
10196  case ssDisc:
10197  {
10198  QBrush b = painter->brush();
10199  painter->setBrush(painter->pen().color());
10200  painter->drawEllipse(QPointF(x , y), w, w);
10201  painter->setBrush(b);
10202  break;
10203  }
10204  case ssSquare:
10205  {
10206  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
10207  break;
10208  }
10209  case ssDiamond:
10210  {
10211  QPointF lineArray[4] = {QPointF(x-w, y),
10212  QPointF( x, y-w),
10213  QPointF(x+w, y),
10214  QPointF( x, y+w)};
10215  painter->drawPolygon(lineArray, 4);
10216  break;
10217  }
10218  case ssStar:
10219  {
10220  painter->drawLine(QLineF(x-w, y, x+w, y));
10221  painter->drawLine(QLineF( x, y+w, x, y-w));
10222  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
10223  painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
10224  break;
10225  }
10226  case ssTriangle:
10227  {
10228  QPointF lineArray[3] = {QPointF(x-w, y+0.755*w),
10229  QPointF(x+w, y+0.755*w),
10230  QPointF( x, y-0.977*w)};
10231  painter->drawPolygon(lineArray, 3);
10232  break;
10233  }
10234  case ssTriangleInverted:
10235  {
10236  QPointF lineArray[3] = {QPointF(x-w, y-0.755*w),
10237  QPointF(x+w, y-0.755*w),
10238  QPointF( x, y+0.977*w)};
10239  painter->drawPolygon(lineArray, 3);
10240  break;
10241  }
10242  case ssCrossSquare:
10243  {
10244  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
10245  painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
10246  painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
10247  break;
10248  }
10249  case ssPlusSquare:
10250  {
10251  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
10252  painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
10253  painter->drawLine(QLineF( x, y+w, x, y-w));
10254  break;
10255  }
10256  case ssCrossCircle:
10257  {
10258  painter->drawEllipse(QPointF(x, y), w, w);
10259  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
10260  painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
10261  break;
10262  }
10263  case ssPlusCircle:
10264  {
10265  painter->drawEllipse(QPointF(x, y), w, w);
10266  painter->drawLine(QLineF(x-w, y, x+w, y));
10267  painter->drawLine(QLineF( x, y+w, x, y-w));
10268  break;
10269  }
10270  case ssPeace:
10271  {
10272  painter->drawEllipse(QPointF(x, y), w, w);
10273  painter->drawLine(QLineF(x, y-w, x, y+w));
10274  painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
10275  painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
10276  break;
10277  }
10278  case ssPixmap:
10279  {
10280  const double widthHalf = mPixmap.width()*0.5;
10281  const double heightHalf = mPixmap.height()*0.5;
10282 #if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
10283  const QRectF clipRect = painter->clipRegion().boundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);
10284 #else
10285  const QRectF clipRect = painter->clipBoundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);
10286 #endif
10287  if (clipRect.contains(x, y))
10288  painter->drawPixmap(x-widthHalf, y-heightHalf, mPixmap);
10289  break;
10290  }
10291  case ssCustom:
10292  {
10293  QTransform oldTransform = painter->transform();
10294  painter->translate(x, y);
10295  painter->scale(mSize/6.0, mSize/6.0);
10296  painter->drawPath(mCustomPath);
10297  painter->setTransform(oldTransform);
10298  break;
10299  }
10300  }
10301 }
10302 /* end of 'src/scatterstyle.cpp' */
10303 
10304 //amalgamation: add datacontainer.cpp
10305 
10306 /* including file 'src/plottable.cpp', size 38845 */
10307 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
10308 
10312 
10343  mPen(QColor(80, 80, 255), 2.5),
10344  mBrush(Qt::NoBrush),
10345  mScatterStyle(),
10346  mUsedScatterProperties(QCPScatterStyle::spNone),
10347  mPlottable(0)
10348 {
10349 }
10350 
10352 {
10353 }
10354 
10359 {
10360  mPen = pen;
10361 }
10362 
10367 {
10368  mBrush = brush;
10369 }
10370 
10378 void QCPSelectionDecorator::setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties)
10379 {
10381  setUsedScatterProperties(usedProperties);
10382 }
10383 
10391 void QCPSelectionDecorator::setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties)
10392 {
10393  mUsedScatterProperties = properties;
10394 }
10395 
10402 {
10403  painter->setPen(mPen);
10404 }
10405 
10412 {
10413  painter->setBrush(mBrush);
10414 }
10415 
10425 {
10426  QCPScatterStyle result(unselectedStyle);
10428 
10429  // if style shall inherit pen from plottable (has no own pen defined), give it the selected
10430  // plottable pen explicitly, so it doesn't use the unselected plottable pen when used in the
10431  // plottable:
10432  if (!result.isPenDefined())
10433  result.setPen(mPen);
10434 
10435  return result;
10436 }
10437 
10443 {
10444  setPen(other->pen());
10445  setBrush(other->brush());
10447 }
10448 
10458 {
10459  Q_UNUSED(painter)
10460  Q_UNUSED(selection)
10461 }
10462 
10473 {
10474  if (!mPlottable)
10475  {
10476  mPlottable = plottable;
10477  return true;
10478  } else
10479  {
10480  qDebug() << Q_FUNC_INFO << "This selection decorator is already registered with plottable:" << reinterpret_cast<quintptr>(mPlottable);
10481  return false;
10482  }
10483 }
10484 
10485 
10489 
10571 /* start of documentation of inline functions */
10572 
10607 /* end of documentation of inline functions */
10608 /* start of documentation of pure virtual functions */
10609 
10660 /* end of documentation of pure virtual functions */
10661 /* start of documentation of signals */
10662 
10688 /* end of documentation of signals */
10689 
10702  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
10703  mName(),
10704  mAntialiasedFill(true),
10705  mAntialiasedScatters(true),
10706  mPen(Qt::black),
10707  mBrush(Qt::NoBrush),
10708  mKeyAxis(keyAxis),
10709  mValueAxis(valueAxis),
10710  mSelectable(QCP::stWhole),
10711  mSelectionDecorator(0)
10712 {
10713  if (keyAxis->parentPlot() != valueAxis->parentPlot())
10714  qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
10715  if (keyAxis->orientation() == valueAxis->orientation())
10716  qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
10717 
10720 }
10721 
10723 {
10724  if (mSelectionDecorator)
10725  {
10726  delete mSelectionDecorator;
10727  mSelectionDecorator = 0;
10728  }
10729 }
10730 
10736 {
10737  mName = name;
10738 }
10739 
10747 {
10748  mAntialiasedFill = enabled;
10749 }
10750 
10758 {
10759  mAntialiasedScatters = enabled;
10760 }
10761 
10771 {
10772  mPen = pen;
10773 }
10774 
10785 {
10786  mBrush = brush;
10787 }
10788 
10801 {
10802  mKeyAxis = axis;
10803 }
10804 
10817 {
10818  mValueAxis = axis;
10819 }
10820 
10821 
10841 {
10842  selection.enforceType(mSelectable);
10843  if (mSelection != selection)
10844  {
10846  emit selectionChanged(selected());
10848  }
10849 }
10850 
10861 {
10862  if (decorator)
10863  {
10864  if (decorator->registerWithPlottable(this))
10865  {
10866  if (mSelectionDecorator) // delete old decorator if necessary
10867  delete mSelectionDecorator;
10868  mSelectionDecorator = decorator;
10869  }
10870  } else if (mSelectionDecorator) // just clear decorator
10871  {
10872  delete mSelectionDecorator;
10873  mSelectionDecorator = 0;
10874  }
10875 }
10876 
10888 {
10889  if (mSelectable != selectable)
10890  {
10892  QCPDataSelection oldSelection = mSelection;
10895  if (mSelection != oldSelection)
10896  {
10897  emit selectionChanged(selected());
10899  }
10900  }
10901 }
10902 
10903 
10913 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
10914 {
10915  QCPAxis *keyAxis = mKeyAxis.data();
10916  QCPAxis *valueAxis = mValueAxis.data();
10917  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
10918 
10919  if (keyAxis->orientation() == Qt::Horizontal)
10920  {
10921  x = keyAxis->coordToPixel(key);
10922  y = valueAxis->coordToPixel(value);
10923  } else
10924  {
10925  y = keyAxis->coordToPixel(key);
10926  x = valueAxis->coordToPixel(value);
10927  }
10928 }
10929 
10934 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
10935 {
10936  QCPAxis *keyAxis = mKeyAxis.data();
10937  QCPAxis *valueAxis = mValueAxis.data();
10938  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
10939 
10940  if (keyAxis->orientation() == Qt::Horizontal)
10941  return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
10942  else
10943  return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
10944 }
10945 
10955 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
10956 {
10957  QCPAxis *keyAxis = mKeyAxis.data();
10958  QCPAxis *valueAxis = mValueAxis.data();
10959  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
10960 
10961  if (keyAxis->orientation() == Qt::Horizontal)
10962  {
10963  key = keyAxis->pixelToCoord(x);
10964  value = valueAxis->pixelToCoord(y);
10965  } else
10966  {
10967  key = keyAxis->pixelToCoord(y);
10968  value = valueAxis->pixelToCoord(x);
10969  }
10970 }
10971 
10976 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
10977 {
10978  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
10979 }
10980 
10994 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
10995 {
10996  rescaleKeyAxis(onlyEnlarge);
10997  rescaleValueAxis(onlyEnlarge);
10998 }
10999 
11005 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
11006 {
11007  QCPAxis *keyAxis = mKeyAxis.data();
11008  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
11009 
11010  QCP::SignDomain signDomain = QCP::sdBoth;
11011  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
11012  signDomain = (keyAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
11013 
11014  bool foundRange;
11015  QCPRange newRange = getKeyRange(foundRange, signDomain);
11016  if (foundRange)
11017  {
11018  if (onlyEnlarge)
11019  newRange.expand(keyAxis->range());
11020  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
11021  {
11022  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
11023  if (keyAxis->scaleType() == QCPAxis::stLinear)
11024  {
11025  newRange.lower = center-keyAxis->range().size()/2.0;
11026  newRange.upper = center+keyAxis->range().size()/2.0;
11027  } else // scaleType() == stLogarithmic
11028  {
11029  newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
11030  newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
11031  }
11032  }
11033  keyAxis->setRange(newRange);
11034  }
11035 }
11036 
11047 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const
11048 {
11049  QCPAxis *keyAxis = mKeyAxis.data();
11050  QCPAxis *valueAxis = mValueAxis.data();
11051  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
11052 
11053  QCP::SignDomain signDomain = QCP::sdBoth;
11054  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
11055  signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
11056 
11057  bool foundRange;
11058  QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange());
11059  if (foundRange)
11060  {
11061  if (onlyEnlarge)
11062  newRange.expand(valueAxis->range());
11063  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
11064  {
11065  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
11066  if (valueAxis->scaleType() == QCPAxis::stLinear)
11067  {
11068  newRange.lower = center-valueAxis->range().size()/2.0;
11069  newRange.upper = center+valueAxis->range().size()/2.0;
11070  } else // scaleType() == stLogarithmic
11071  {
11072  newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
11073  newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
11074  }
11075  }
11076  valueAxis->setRange(newRange);
11077  }
11078 }
11079 
11095 {
11096  if (!legend)
11097  {
11098  qDebug() << Q_FUNC_INFO << "passed legend is null";
11099  return false;
11100  }
11101  if (legend->parentPlot() != mParentPlot)
11102  {
11103  qDebug() << Q_FUNC_INFO << "passed legend isn't in the same QCustomPlot as this plottable";
11104  return false;
11105  }
11106 
11107  if (!legend->hasItemWithPlottable(this))
11108  {
11109  legend->addItem(new QCPPlottableLegendItem(legend, this));
11110  return true;
11111  } else
11112  return false;
11113 }
11114 
11122 {
11123  if (!mParentPlot || !mParentPlot->legend)
11124  return false;
11125  else
11126  return addToLegend(mParentPlot->legend);
11127 }
11128 
11140 {
11141  if (!legend)
11142  {
11143  qDebug() << Q_FUNC_INFO << "passed legend is null";
11144  return false;
11145  }
11146 
11147  if (QCPPlottableLegendItem *lip = legend->itemWithPlottable(this))
11148  return legend->removeItem(lip);
11149  else
11150  return false;
11151 }
11152 
11160 {
11161  if (!mParentPlot || !mParentPlot->legend)
11162  return false;
11163  else
11165 }
11166 
11167 /* inherits documentation from base class */
11169 {
11170  if (mKeyAxis && mValueAxis)
11171  return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
11172  else
11173  return QRect();
11174 }
11175 
11176 /* inherits documentation from base class */
11178 {
11179  return QCP::iSelectPlottables;
11180 }
11181 
11198 {
11200 }
11201 
11214 {
11216 }
11217 
11230 {
11232 }
11233 
11234 /* inherits documentation from base class */
11235 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
11236 {
11237  Q_UNUSED(event)
11238 
11239  if (mSelectable != QCP::stNone)
11240  {
11241  QCPDataSelection newSelection = details.value<QCPDataSelection>();
11242  QCPDataSelection selectionBefore = mSelection;
11243  if (additive)
11244  {
11245  if (mSelectable == QCP::stWhole) // in whole selection mode, we toggle to no selection even if currently unselected point was hit
11246  {
11247  if (selected())
11249  else
11250  setSelection(newSelection);
11251  } else // in all other selection modes we toggle selections of homogeneously selected/unselected segments
11252  {
11253  if (mSelection.contains(newSelection)) // if entire newSelection is already selected, toggle selection
11254  setSelection(mSelection-newSelection);
11255  else
11256  setSelection(mSelection+newSelection);
11257  }
11258  } else
11259  setSelection(newSelection);
11260  if (selectionStateChanged)
11261  *selectionStateChanged = mSelection != selectionBefore;
11262  }
11263 }
11264 
11265 /* inherits documentation from base class */
11266 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
11267 {
11268  if (mSelectable != QCP::stNone)
11269  {
11270  QCPDataSelection selectionBefore = mSelection;
11272  if (selectionStateChanged)
11273  *selectionStateChanged = mSelection != selectionBefore;
11274  }
11275 }
11276 /* end of 'src/plottable.cpp' */
11277 
11278 
11279 /* including file 'src/item.cpp', size 49271 */
11280 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
11281 
11285 
11307 /* start documentation of inline functions */
11308 
11319 /* end documentation of inline functions */
11320 
11326 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId) :
11327  mName(name),
11328  mParentPlot(parentPlot),
11329  mParentItem(parentItem),
11330  mAnchorId(anchorId)
11331 {
11332 }
11333 
11335 {
11336  // unregister as parent at children:
11337  foreach (QCPItemPosition *child, mChildrenX.toList())
11338  {
11339  if (child->parentAnchorX() == this)
11340  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
11341  }
11342  foreach (QCPItemPosition *child, mChildrenY.toList())
11343  {
11344  if (child->parentAnchorY() == this)
11345  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
11346  }
11347 }
11348 
11356 {
11357  if (mParentItem)
11358  {
11359  if (mAnchorId > -1)
11360  {
11362  } else
11363  {
11364  qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
11365  return QPointF();
11366  }
11367  } else
11368  {
11369  qDebug() << Q_FUNC_INFO << "no parent item set";
11370  return QPointF();
11371  }
11372 }
11373 
11383 {
11384  if (!mChildrenX.contains(pos))
11385  mChildrenX.insert(pos);
11386  else
11387  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
11388 }
11389 
11397 {
11398  if (!mChildrenX.remove(pos))
11399  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
11400 }
11401 
11411 {
11412  if (!mChildrenY.contains(pos))
11413  mChildrenY.insert(pos);
11414  else
11415  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
11416 }
11417 
11425 {
11426  if (!mChildrenY.remove(pos))
11427  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
11428 }
11429 
11430 
11434 
11469 /* start documentation of inline functions */
11470 
11492 /* end documentation of inline functions */
11493 
11499 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name) :
11500  QCPItemAnchor(parentPlot, parentItem, name),
11501  mPositionTypeX(ptAbsolute),
11502  mPositionTypeY(ptAbsolute),
11503  mKey(0),
11504  mValue(0),
11505  mParentAnchorX(0),
11506  mParentAnchorY(0)
11507 {
11508 }
11509 
11511 {
11512  // unregister as parent at children:
11513  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
11514  // the setParentAnchor(0) call the correct QCPItemPosition::pixelPosition function instead of QCPItemAnchor::pixelPosition
11515  foreach (QCPItemPosition *child, mChildrenX.toList())
11516  {
11517  if (child->parentAnchorX() == this)
11518  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
11519  }
11520  foreach (QCPItemPosition *child, mChildrenY.toList())
11521  {
11522  if (child->parentAnchorY() == this)
11523  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
11524  }
11525  // unregister as child in parent:
11526  if (mParentAnchorX)
11528  if (mParentAnchorY)
11530 }
11531 
11532 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
11534 {
11535  return mAxisRect.data();
11536 }
11537 
11564 {
11565  setTypeX(type);
11566  setTypeY(type);
11567 }
11568 
11577 {
11578  if (mPositionTypeX != type)
11579  {
11580  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
11581  // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning.
11582  bool retainPixelPosition = true;
11583  if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
11584  retainPixelPosition = false;
11585  if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
11586  retainPixelPosition = false;
11587 
11588  QPointF pixel;
11589  if (retainPixelPosition)
11590  pixel = pixelPosition();
11591 
11592  mPositionTypeX = type;
11593 
11594  if (retainPixelPosition)
11595  setPixelPosition(pixel);
11596  }
11597 }
11598 
11607 {
11608  if (mPositionTypeY != type)
11609  {
11610  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
11611  // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning.
11612  bool retainPixelPosition = true;
11613  if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
11614  retainPixelPosition = false;
11615  if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
11616  retainPixelPosition = false;
11617 
11618  QPointF pixel;
11619  if (retainPixelPosition)
11620  pixel = pixelPosition();
11621 
11622  mPositionTypeY = type;
11623 
11624  if (retainPixelPosition)
11625  setPixelPosition(pixel);
11626  }
11627 }
11628 
11648 {
11649  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
11650  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
11651  return successX && successY;
11652 }
11653 
11662 {
11663  // make sure self is not assigned as parent:
11664  if (parentAnchor == this)
11665  {
11666  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
11667  return false;
11668  }
11669  // make sure no recursive parent-child-relationships are created:
11670  QCPItemAnchor *currentParent = parentAnchor;
11671  while (currentParent)
11672  {
11673  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
11674  {
11675  // is a QCPItemPosition, might have further parent, so keep iterating
11676  if (currentParentPos == this)
11677  {
11678  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
11679  return false;
11680  }
11681  currentParent = currentParentPos->parentAnchorX();
11682  } else
11683  {
11684  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
11685  // same, to prevent a position being child of an anchor which itself depends on the position,
11686  // because they're both on the same item:
11687  if (currentParent->mParentItem == mParentItem)
11688  {
11689  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
11690  return false;
11691  }
11692  break;
11693  }
11694  }
11695 
11696  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
11699 
11700  // save pixel position:
11701  QPointF pixelP;
11702  if (keepPixelPosition)
11703  pixelP = pixelPosition();
11704  // unregister at current parent anchor:
11705  if (mParentAnchorX)
11707  // register at new parent anchor:
11708  if (parentAnchor)
11709  parentAnchor->addChildX(this);
11711  // restore pixel position under new parent:
11712  if (keepPixelPosition)
11713  setPixelPosition(pixelP);
11714  else
11715  setCoords(0, coords().y());
11716  return true;
11717 }
11718 
11727 {
11728  // make sure self is not assigned as parent:
11729  if (parentAnchor == this)
11730  {
11731  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
11732  return false;
11733  }
11734  // make sure no recursive parent-child-relationships are created:
11735  QCPItemAnchor *currentParent = parentAnchor;
11736  while (currentParent)
11737  {
11738  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
11739  {
11740  // is a QCPItemPosition, might have further parent, so keep iterating
11741  if (currentParentPos == this)
11742  {
11743  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
11744  return false;
11745  }
11746  currentParent = currentParentPos->parentAnchorY();
11747  } else
11748  {
11749  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
11750  // same, to prevent a position being child of an anchor which itself depends on the position,
11751  // because they're both on the same item:
11752  if (currentParent->mParentItem == mParentItem)
11753  {
11754  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
11755  return false;
11756  }
11757  break;
11758  }
11759  }
11760 
11761  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
11764 
11765  // save pixel position:
11766  QPointF pixelP;
11767  if (keepPixelPosition)
11768  pixelP = pixelPosition();
11769  // unregister at current parent anchor:
11770  if (mParentAnchorY)
11772  // register at new parent anchor:
11773  if (parentAnchor)
11774  parentAnchor->addChildY(this);
11776  // restore pixel position under new parent:
11777  if (keepPixelPosition)
11778  setPixelPosition(pixelP);
11779  else
11780  setCoords(coords().x(), 0);
11781  return true;
11782 }
11783 
11802 {
11803  mKey = key;
11804  mValue = value;
11805 }
11806 
11812 void QCPItemPosition::setCoords(const QPointF &pos)
11813 {
11814  setCoords(pos.x(), pos.y());
11815 }
11816 
11824 {
11825  QPointF result;
11826 
11827  // determine X:
11828  switch (mPositionTypeX)
11829  {
11830  case ptAbsolute:
11831  {
11832  result.rx() = mKey;
11833  if (mParentAnchorX)
11834  result.rx() += mParentAnchorX->pixelPosition().x();
11835  break;
11836  }
11837  case ptViewportRatio:
11838  {
11839  result.rx() = mKey*mParentPlot->viewport().width();
11840  if (mParentAnchorX)
11841  result.rx() += mParentAnchorX->pixelPosition().x();
11842  else
11843  result.rx() += mParentPlot->viewport().left();
11844  break;
11845  }
11846  case ptAxisRectRatio:
11847  {
11848  if (mAxisRect)
11849  {
11850  result.rx() = mKey*mAxisRect.data()->width();
11851  if (mParentAnchorX)
11852  result.rx() += mParentAnchorX->pixelPosition().x();
11853  else
11854  result.rx() += mAxisRect.data()->left();
11855  } else
11856  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
11857  break;
11858  }
11859  case ptPlotCoords:
11860  {
11861  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
11862  result.rx() = mKeyAxis.data()->coordToPixel(mKey);
11863  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
11864  result.rx() = mValueAxis.data()->coordToPixel(mValue);
11865  else
11866  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
11867  break;
11868  }
11869  }
11870 
11871  // determine Y:
11872  switch (mPositionTypeY)
11873  {
11874  case ptAbsolute:
11875  {
11876  result.ry() = mValue;
11877  if (mParentAnchorY)
11878  result.ry() += mParentAnchorY->pixelPosition().y();
11879  break;
11880  }
11881  case ptViewportRatio:
11882  {
11883  result.ry() = mValue*mParentPlot->viewport().height();
11884  if (mParentAnchorY)
11885  result.ry() += mParentAnchorY->pixelPosition().y();
11886  else
11887  result.ry() += mParentPlot->viewport().top();
11888  break;
11889  }
11890  case ptAxisRectRatio:
11891  {
11892  if (mAxisRect)
11893  {
11894  result.ry() = mValue*mAxisRect.data()->height();
11895  if (mParentAnchorY)
11896  result.ry() += mParentAnchorY->pixelPosition().y();
11897  else
11898  result.ry() += mAxisRect.data()->top();
11899  } else
11900  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
11901  break;
11902  }
11903  case ptPlotCoords:
11904  {
11905  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
11906  result.ry() = mKeyAxis.data()->coordToPixel(mKey);
11907  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
11908  result.ry() = mValueAxis.data()->coordToPixel(mValue);
11909  else
11910  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
11911  break;
11912  }
11913  }
11914 
11915  return result;
11916 }
11917 
11924 {
11925  mKeyAxis = keyAxis;
11927 }
11928 
11935 {
11936  mAxisRect = axisRect;
11937 }
11938 
11950 {
11951  double x = pixelPosition.x();
11952  double y = pixelPosition.y();
11953 
11954  switch (mPositionTypeX)
11955  {
11956  case ptAbsolute:
11957  {
11958  if (mParentAnchorX)
11959  x -= mParentAnchorX->pixelPosition().x();
11960  break;
11961  }
11962  case ptViewportRatio:
11963  {
11964  if (mParentAnchorX)
11965  x -= mParentAnchorX->pixelPosition().x();
11966  else
11967  x -= mParentPlot->viewport().left();
11968  x /= (double)mParentPlot->viewport().width();
11969  break;
11970  }
11971  case ptAxisRectRatio:
11972  {
11973  if (mAxisRect)
11974  {
11975  if (mParentAnchorX)
11976  x -= mParentAnchorX->pixelPosition().x();
11977  else
11978  x -= mAxisRect.data()->left();
11979  x /= (double)mAxisRect.data()->width();
11980  } else
11981  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
11982  break;
11983  }
11984  case ptPlotCoords:
11985  {
11986  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
11987  x = mKeyAxis.data()->pixelToCoord(x);
11988  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
11989  y = mValueAxis.data()->pixelToCoord(x);
11990  else
11991  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
11992  break;
11993  }
11994  }
11995 
11996  switch (mPositionTypeY)
11997  {
11998  case ptAbsolute:
11999  {
12000  if (mParentAnchorY)
12001  y -= mParentAnchorY->pixelPosition().y();
12002  break;
12003  }
12004  case ptViewportRatio:
12005  {
12006  if (mParentAnchorY)
12007  y -= mParentAnchorY->pixelPosition().y();
12008  else
12009  y -= mParentPlot->viewport().top();
12010  y /= (double)mParentPlot->viewport().height();
12011  break;
12012  }
12013  case ptAxisRectRatio:
12014  {
12015  if (mAxisRect)
12016  {
12017  if (mParentAnchorY)
12018  y -= mParentAnchorY->pixelPosition().y();
12019  else
12020  y -= mAxisRect.data()->top();
12021  y /= (double)mAxisRect.data()->height();
12022  } else
12023  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
12024  break;
12025  }
12026  case ptPlotCoords:
12027  {
12028  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
12029  x = mKeyAxis.data()->pixelToCoord(y);
12030  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
12031  y = mValueAxis.data()->pixelToCoord(y);
12032  else
12033  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
12034  break;
12035  }
12036  }
12037 
12038  setCoords(x, y);
12039 }
12040 
12041 
12045 
12171 /* start of documentation of inline functions */
12172 
12188 /* end of documentation of inline functions */
12189 /* start documentation of pure virtual functions */
12190 
12201 /* end documentation of pure virtual functions */
12202 /* start documentation of signals */
12203 
12209 /* end documentation of signals */
12210 
12215  QCPLayerable(parentPlot),
12216  mClipToAxisRect(false),
12217  mSelectable(true),
12218  mSelected(false)
12219 {
12220  parentPlot->registerItem(this);
12221 
12222  QList<QCPAxisRect*> rects = parentPlot->axisRects();
12223  if (rects.size() > 0)
12224  {
12225  setClipToAxisRect(true);
12226  setClipAxisRect(rects.first());
12227  }
12228 }
12229 
12231 {
12232  // don't delete mPositions because every position is also an anchor and thus in mAnchors
12233  qDeleteAll(mAnchors);
12234 }
12235 
12236 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
12238 {
12239  return mClipAxisRect.data();
12240 }
12241 
12249 {
12250  mClipToAxisRect = clip;
12251  if (mClipToAxisRect)
12253 }
12254 
12262 {
12263  mClipAxisRect = rect;
12264  if (mClipToAxisRect)
12266 }
12267 
12278 {
12279  if (mSelectable != selectable)
12280  {
12283  }
12284 }
12285 
12301 {
12302  if (mSelected != selected)
12303  {
12304  mSelected = selected;
12306  }
12307 }
12308 
12319 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
12320 {
12321  for (int i=0; i<mPositions.size(); ++i)
12322  {
12323  if (mPositions.at(i)->name() == name)
12324  return mPositions.at(i);
12325  }
12326  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
12327  return 0;
12328 }
12329 
12340 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
12341 {
12342  for (int i=0; i<mAnchors.size(); ++i)
12343  {
12344  if (mAnchors.at(i)->name() == name)
12345  return mAnchors.at(i);
12346  }
12347  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
12348  return 0;
12349 }
12350 
12359 bool QCPAbstractItem::hasAnchor(const QString &name) const
12360 {
12361  for (int i=0; i<mAnchors.size(); ++i)
12362  {
12363  if (mAnchors.at(i)->name() == name)
12364  return true;
12365  }
12366  return false;
12367 }
12368 
12379 {
12381  return mClipAxisRect.data()->rect();
12382  else
12383  return mParentPlot->viewport();
12384 }
12385 
12400 {
12402 }
12403 
12417 double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const
12418 {
12419  double result = -1;
12420 
12421  // distance to border:
12422  QList<QLineF> lines;
12423  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
12424  << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
12425  double minDistSqr = (std::numeric_limits<double>::max)();
12426  for (int i=0; i<lines.size(); ++i)
12427  {
12428  double distSqr = QCPVector2D(pos).distanceSquaredToLine(lines.at(i).p1(), lines.at(i).p2());
12429  if (distSqr < minDistSqr)
12430  minDistSqr = distSqr;
12431  }
12432  result = qSqrt(minDistSqr);
12433 
12434  // filled rect, allow click inside to count as hit:
12435  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
12436  {
12437  if (rect.contains(pos))
12438  result = mParentPlot->selectionTolerance()*0.99;
12439  }
12440  return result;
12441 }
12442 
12453 QPointF QCPAbstractItem::anchorPixelPosition(int anchorId) const
12454 {
12455  qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
12456  return QPointF();
12457 }
12458 
12474 {
12475  if (hasAnchor(name))
12476  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
12477  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
12478  mPositions.append(newPosition);
12479  mAnchors.append(newPosition); // every position is also an anchor
12480  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
12481  newPosition->setType(QCPItemPosition::ptPlotCoords);
12482  if (mParentPlot->axisRect())
12483  newPosition->setAxisRect(mParentPlot->axisRect());
12484  newPosition->setCoords(0, 0);
12485  return newPosition;
12486 }
12487 
12507 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
12508 {
12509  if (hasAnchor(name))
12510  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
12511  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
12512  mAnchors.append(newAnchor);
12513  return newAnchor;
12514 }
12515 
12516 /* inherits documentation from base class */
12517 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12518 {
12519  Q_UNUSED(event)
12520  Q_UNUSED(details)
12521  if (mSelectable)
12522  {
12523  bool selBefore = mSelected;
12524  setSelected(additive ? !mSelected : true);
12525  if (selectionStateChanged)
12526  *selectionStateChanged = mSelected != selBefore;
12527  }
12528 }
12529 
12530 /* inherits documentation from base class */
12531 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
12532 {
12533  if (mSelectable)
12534  {
12535  bool selBefore = mSelected;
12536  setSelected(false);
12537  if (selectionStateChanged)
12538  *selectionStateChanged = mSelected != selBefore;
12539  }
12540 }
12541 
12542 /* inherits documentation from base class */
12544 {
12545  return QCP::iSelectItems;
12546 }
12547 /* end of 'src/item.cpp' */
12548 
12549 
12550 /* including file 'src/core.cpp', size 126207 */
12551 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
12552 
12556 
12566 /* start of documentation of inline functions */
12567 
12582 /* end of documentation of inline functions */
12583 /* start of documentation of signals */
12584 
12755 /* end of documentation of signals */
12756 /* start of documentation of public members */
12757 
12854 /* end of documentation of public members */
12855 
12859 QCustomPlot::QCustomPlot(QWidget *parent) :
12860  QWidget(parent),
12861  xAxis(0),
12862  yAxis(0),
12863  xAxis2(0),
12864  yAxis2(0),
12865  legend(0),
12866  mBufferDevicePixelRatio(1.0), // will be adapted to primary screen below
12867  mPlotLayout(0),
12868  mAutoAddPlottableToLegend(true),
12869  mAntialiasedElements(QCP::aeNone),
12870  mNotAntialiasedElements(QCP::aeNone),
12871  mInteractions(0),
12872  mSelectionTolerance(8),
12873  mNoAntialiasingOnDrag(false),
12874  mBackgroundBrush(Qt::white, Qt::SolidPattern),
12875  mBackgroundScaled(true),
12876  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
12877  mCurrentLayer(0),
12878  mPlottingHints(QCP::phCacheLabels|QCP::phImmediateRefresh),
12879  mMultiSelectModifier(Qt::ControlModifier),
12880  mSelectionRectMode(QCP::srmNone),
12881  mSelectionRect(0),
12882  mOpenGl(false),
12883  mMouseHasMoved(false),
12884  mMouseEventLayerable(0),
12885  mMouseSignalLayerable(0),
12886  mReplotting(false),
12887  mReplotQueued(false),
12888  mOpenGlMultisamples(16),
12889  mOpenGlAntialiasedElementsBackup(QCP::aeNone),
12890  mOpenGlCacheLabelsBackup(true)
12891 {
12892  setAttribute(Qt::WA_NoMousePropagation);
12893  setAttribute(Qt::WA_OpaquePaintEvent);
12894  setFocusPolicy(Qt::ClickFocus);
12895  setMouseTracking(true);
12896  QLocale currentLocale = locale();
12897  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
12898  setLocale(currentLocale);
12899 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
12900 # ifdef QCP_DEVICEPIXELRATIO_FLOAT
12901  setBufferDevicePixelRatio(QWidget::devicePixelRatioF());
12902 # else
12903  setBufferDevicePixelRatio(QWidget::devicePixelRatio());
12904 # endif
12905 #endif
12906 
12909  // create initial layers:
12910  mLayers.append(new QCPLayer(this, QLatin1String("background")));
12911  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
12912  mLayers.append(new QCPLayer(this, QLatin1String("main")));
12913  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
12914  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
12915  mLayers.append(new QCPLayer(this, QLatin1String("overlay")));
12917  setCurrentLayer(QLatin1String("main"));
12918  layer(QLatin1String("overlay"))->setMode(QCPLayer::lmBuffered);
12919 
12920  // create initial layout, axis rect and legend:
12921  mPlotLayout = new QCPLayoutGrid;
12923  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
12924  mPlotLayout->setLayer(QLatin1String("main"));
12925  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
12926  mPlotLayout->addElement(0, 0, defaultAxisRect);
12927  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
12928  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
12929  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
12930  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
12931  legend = new QCPLegend;
12932  legend->setVisible(false);
12933  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
12934  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
12935 
12936  defaultAxisRect->setLayer(QLatin1String("background"));
12937  xAxis->setLayer(QLatin1String("axes"));
12938  yAxis->setLayer(QLatin1String("axes"));
12939  xAxis2->setLayer(QLatin1String("axes"));
12940  yAxis2->setLayer(QLatin1String("axes"));
12941  xAxis->grid()->setLayer(QLatin1String("grid"));
12942  yAxis->grid()->setLayer(QLatin1String("grid"));
12943  xAxis2->grid()->setLayer(QLatin1String("grid"));
12944  yAxis2->grid()->setLayer(QLatin1String("grid"));
12945  legend->setLayer(QLatin1String("legend"));
12946 
12947  // create selection rect instance:
12948  mSelectionRect = new QCPSelectionRect(this);
12949  mSelectionRect->setLayer(QLatin1String("overlay"));
12950 
12951  setViewport(rect()); // needs to be called after mPlotLayout has been created
12952 
12954 }
12955 
12957 {
12958  clearPlottables();
12959  clearItems();
12960 
12961  if (mPlotLayout)
12962  {
12963  delete mPlotLayout;
12964  mPlotLayout = 0;
12965  }
12966 
12967  mCurrentLayer = 0;
12968  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
12969  mLayers.clear();
12970 }
12971 
12989 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
12990 {
12992 
12993  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
12995  mNotAntialiasedElements |= ~mAntialiasedElements;
12996 }
12997 
13005 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
13006 {
13007  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
13008  mAntialiasedElements &= ~antialiasedElement;
13009  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
13010  mAntialiasedElements |= antialiasedElement;
13011 
13012  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
13014  mNotAntialiasedElements |= ~mAntialiasedElements;
13015 }
13016 
13036 {
13038 
13039  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
13041  mAntialiasedElements |= ~mNotAntialiasedElements;
13042 }
13043 
13051 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
13052 {
13053  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
13054  mNotAntialiasedElements &= ~notAntialiasedElement;
13055  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
13056  mNotAntialiasedElements |= notAntialiasedElement;
13057 
13058  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
13060  mAntialiasedElements |= ~mNotAntialiasedElements;
13061 }
13062 
13070 {
13072 }
13073 
13128 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
13129 {
13131 }
13132 
13140 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
13141 {
13142  if (!enabled && mInteractions.testFlag(interaction))
13143  mInteractions &= ~interaction;
13144  else if (enabled && !mInteractions.testFlag(interaction))
13145  mInteractions |= interaction;
13146 }
13147 
13162 {
13163  mSelectionTolerance = pixels;
13164 }
13165 
13176 {
13177  mNoAntialiasingOnDrag = enabled;
13178 }
13179 
13185 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
13186 {
13187  mPlottingHints = hints;
13188 }
13189 
13196 {
13197  QCP::PlottingHints newHints = mPlottingHints;
13198  if (!enabled)
13199  newHints &= ~hint;
13200  else
13201  newHints |= hint;
13202 
13203  if (newHints != mPlottingHints)
13204  setPlottingHints(newHints);
13205 }
13206 
13217 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
13218 {
13219  mMultiSelectModifier = modifier;
13220 }
13221 
13243 {
13244  if (mSelectionRect)
13245  {
13246  if (mode == QCP::srmNone)
13247  mSelectionRect->cancel(); // when switching to none, we immediately want to abort a potentially active selection rect
13248 
13249  // disconnect old connections:
13251  disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
13252  else if (mSelectionRectMode == QCP::srmZoom)
13253  disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
13254 
13255  // establish new ones:
13256  if (mode == QCP::srmSelect)
13257  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
13258  else if (mode == QCP::srmZoom)
13259  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
13260  }
13261 
13262  mSelectionRectMode = mode;
13263 }
13264 
13276 {
13277  if (mSelectionRect)
13278  delete mSelectionRect;
13279 
13281 
13282  if (mSelectionRect)
13283  {
13284  // establish connections with new selection rect:
13286  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
13287  else if (mSelectionRectMode == QCP::srmZoom)
13288  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
13289  }
13290 }
13291 
13328 void QCustomPlot::setOpenGl(bool enabled, int multisampling)
13329 {
13330  mOpenGlMultisamples = qMax(0, multisampling);
13331 #ifdef QCUSTOMPLOT_USE_OPENGL
13332  mOpenGl = enabled;
13333  if (mOpenGl)
13334  {
13335  if (setupOpenGl())
13336  {
13337  // backup antialiasing override and labelcaching setting so we can restore upon disabling OpenGL
13340  // set antialiasing override to antialias all (aligns gl pixel grid properly), and disable label caching (would use software rasterizer for pixmap caches):
13343  } else
13344  {
13345  qDebug() << Q_FUNC_INFO << "Failed to enable OpenGL, continuing plotting without hardware acceleration.";
13346  mOpenGl = false;
13347  }
13348  } else
13349  {
13350  // restore antialiasing override and labelcaching to what it was before enabling OpenGL, if nobody changed it in the meantime:
13353  if (!mPlottingHints.testFlag(QCP::phCacheLabels))
13355  freeOpenGl();
13356  }
13357  // recreate all paint buffers:
13358  mPaintBuffers.clear();
13360 #else
13361  Q_UNUSED(enabled)
13362  qDebug() << Q_FUNC_INFO << "QCustomPlot can't use OpenGL because QCUSTOMPLOT_USE_OPENGL was not defined during compilation (add 'DEFINES += QCUSTOMPLOT_USE_OPENGL' to your qmake .pro file)";
13363 #endif
13364 }
13365 
13382 void QCustomPlot::setViewport(const QRect &rect)
13383 {
13384  mViewport = rect;
13385  if (mPlotLayout)
13387 }
13388 
13401 {
13402  if (!qFuzzyCompare(ratio, mBufferDevicePixelRatio))
13403  {
13404 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
13405  mBufferDevicePixelRatio = ratio;
13406  for (int i=0; i<mPaintBuffers.size(); ++i)
13407  mPaintBuffers.at(i)->setDevicePixelRatio(mBufferDevicePixelRatio);
13408  // Note: axis label cache has devicePixelRatio as part of cache hash, so no need to manually clear cache here
13409 #else
13410  qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
13412 #endif
13413  }
13414 }
13415 
13431 void QCustomPlot::setBackground(const QPixmap &pm)
13432 {
13433  mBackgroundPixmap = pm;
13434  mScaledBackgroundPixmap = QPixmap();
13435 }
13436 
13450 void QCustomPlot::setBackground(const QBrush &brush)
13451 {
13452  mBackgroundBrush = brush;
13453 }
13454 
13462 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
13463 {
13464  mBackgroundPixmap = pm;
13465  mScaledBackgroundPixmap = QPixmap();
13466  mBackgroundScaled = scaled;
13467  mBackgroundScaledMode = mode;
13468 }
13469 
13481 {
13482  mBackgroundScaled = scaled;
13483 }
13484 
13491 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
13492 {
13493  mBackgroundScaledMode = mode;
13494 }
13495 
13505 {
13506  if (index >= 0 && index < mPlottables.size())
13507  {
13508  return mPlottables.at(index);
13509  } else
13510  {
13511  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13512  return 0;
13513  }
13514 }
13515 
13524 {
13525  if (!mPlottables.isEmpty())
13526  {
13527  return mPlottables.last();
13528  } else
13529  return 0;
13530 }
13531 
13541 {
13542  if (!mPlottables.contains(plottable))
13543  {
13544  qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
13545  return false;
13546  }
13547 
13548  // remove plottable from legend:
13549  plottable->removeFromLegend();
13550  // special handling for QCPGraphs to maintain the simple graph interface:
13551  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
13552  mGraphs.removeOne(graph);
13553  // remove plottable:
13554  delete plottable;
13555  mPlottables.removeOne(plottable);
13556  return true;
13557 }
13558 
13564 {
13565  if (index >= 0 && index < mPlottables.size())
13566  return removePlottable(mPlottables[index]);
13567  else
13568  {
13569  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13570  return false;
13571  }
13572 }
13573 
13583 {
13584  int c = mPlottables.size();
13585  for (int i=c-1; i >= 0; --i)
13587  return c;
13588 }
13589 
13596 {
13597  return mPlottables.size();
13598 }
13599 
13607 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
13608 {
13609  QList<QCPAbstractPlottable*> result;
13611  {
13612  if (plottable->selected())
13613  result.append(plottable);
13614  }
13615  return result;
13616 }
13617 
13630 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
13631 {
13632  QCPAbstractPlottable *resultPlottable = 0;
13633  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
13634 
13636  {
13637  if (onlySelectable && !plottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable
13638  continue;
13639  if ((plottable->keyAxis()->axisRect()->rect() & plottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
13640  {
13641  double currentDistance = plottable->selectTest(pos, false);
13642  if (currentDistance >= 0 && currentDistance < resultDistance)
13643  {
13644  resultPlottable = plottable;
13645  resultDistance = currentDistance;
13646  }
13647  }
13648  }
13649 
13650  return resultPlottable;
13651 }
13652 
13657 {
13658  return mPlottables.contains(plottable);
13659 }
13660 
13669 QCPGraph *QCustomPlot::graph(int index) const
13670 {
13671  if (index >= 0 && index < mGraphs.size())
13672  {
13673  return mGraphs.at(index);
13674  } else
13675  {
13676  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13677  return 0;
13678  }
13679 }
13680 
13689 {
13690  if (!mGraphs.isEmpty())
13691  {
13692  return mGraphs.last();
13693  } else
13694  return 0;
13695 }
13696 
13710 {
13711  if (!keyAxis) keyAxis = xAxis;
13712  if (!valueAxis) valueAxis = yAxis;
13713  if (!keyAxis || !valueAxis)
13714  {
13715  qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
13716  return 0;
13717  }
13718  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
13719  {
13720  qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
13721  return 0;
13722  }
13723 
13724  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
13725  newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
13726  return newGraph;
13727 }
13728 
13740 {
13741  return removePlottable(graph);
13742 }
13743 
13749 {
13750  if (index >= 0 && index < mGraphs.size())
13751  return removeGraph(mGraphs[index]);
13752  else
13753  return false;
13754 }
13755 
13765 {
13766  int c = mGraphs.size();
13767  for (int i=c-1; i >= 0; --i)
13768  removeGraph(mGraphs[i]);
13769  return c;
13770 }
13771 
13778 {
13779  return mGraphs.size();
13780 }
13781 
13790 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
13791 {
13792  QList<QCPGraph*> result;
13793  foreach (QCPGraph *graph, mGraphs)
13794  {
13795  if (graph->selected())
13796  result.append(graph);
13797  }
13798  return result;
13799 }
13800 
13810 {
13811  if (index >= 0 && index < mItems.size())
13812  {
13813  return mItems.at(index);
13814  } else
13815  {
13816  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13817  return 0;
13818  }
13819 }
13820 
13829 {
13830  if (!mItems.isEmpty())
13831  {
13832  return mItems.last();
13833  } else
13834  return 0;
13835 }
13836 
13845 {
13846  if (mItems.contains(item))
13847  {
13848  delete item;
13849  mItems.removeOne(item);
13850  return true;
13851  } else
13852  {
13853  qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
13854  return false;
13855  }
13856 }
13857 
13863 {
13864  if (index >= 0 && index < mItems.size())
13865  return removeItem(mItems[index]);
13866  else
13867  {
13868  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13869  return false;
13870  }
13871 }
13872 
13881 {
13882  int c = mItems.size();
13883  for (int i=c-1; i >= 0; --i)
13884  removeItem(mItems[i]);
13885  return c;
13886 }
13887 
13894 {
13895  return mItems.size();
13896 }
13897 
13903 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
13904 {
13905  QList<QCPAbstractItem*> result;
13906  foreach (QCPAbstractItem *item, mItems)
13907  {
13908  if (item->selected())
13909  result.append(item);
13910  }
13911  return result;
13912 }
13913 
13927 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
13928 {
13929  QCPAbstractItem *resultItem = 0;
13930  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
13931 
13932  foreach (QCPAbstractItem *item, mItems)
13933  {
13934  if (onlySelectable && !item->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
13935  continue;
13936  if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
13937  {
13938  double currentDistance = item->selectTest(pos, false);
13939  if (currentDistance >= 0 && currentDistance < resultDistance)
13940  {
13941  resultItem = item;
13942  resultDistance = currentDistance;
13943  }
13944  }
13945  }
13946 
13947  return resultItem;
13948 }
13949 
13956 {
13957  return mItems.contains(item);
13958 }
13959 
13968 QCPLayer *QCustomPlot::layer(const QString &name) const
13969 {
13970  foreach (QCPLayer *layer, mLayers)
13971  {
13972  if (layer->name() == name)
13973  return layer;
13974  }
13975  return 0;
13976 }
13977 
13984 QCPLayer *QCustomPlot::layer(int index) const
13985 {
13986  if (index >= 0 && index < mLayers.size())
13987  {
13988  return mLayers.at(index);
13989  } else
13990  {
13991  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13992  return 0;
13993  }
13994 }
13995 
14000 {
14001  return mCurrentLayer;
14002 }
14003 
14014 bool QCustomPlot::setCurrentLayer(const QString &name)
14015 {
14016  if (QCPLayer *newCurrentLayer = layer(name))
14017  {
14018  return setCurrentLayer(newCurrentLayer);
14019  } else
14020  {
14021  qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
14022  return false;
14023  }
14024 }
14025 
14035 {
14036  if (!mLayers.contains(layer))
14037  {
14038  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
14039  return false;
14040  }
14041 
14042  mCurrentLayer = layer;
14043  return true;
14044 }
14045 
14052 {
14053  return mLayers.size();
14054 }
14055 
14069 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
14070 {
14071  if (!otherLayer)
14072  otherLayer = mLayers.last();
14073  if (!mLayers.contains(otherLayer))
14074  {
14075  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
14076  return false;
14077  }
14078  if (layer(name))
14079  {
14080  qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
14081  return false;
14082  }
14083 
14084  QCPLayer *newLayer = new QCPLayer(this, name);
14085  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
14087  setupPaintBuffers(); // associates new layer with the appropriate paint buffer
14088  return true;
14089 }
14090 
14106 {
14107  if (!mLayers.contains(layer))
14108  {
14109  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
14110  return false;
14111  }
14112  if (mLayers.size() < 2)
14113  {
14114  qDebug() << Q_FUNC_INFO << "can't remove last layer";
14115  return false;
14116  }
14117 
14118  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
14119  int removedIndex = layer->index();
14120  bool isFirstLayer = removedIndex==0;
14121  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
14122  QList<QCPLayerable*> children = layer->children();
14123  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
14124  {
14125  for (int i=children.size()-1; i>=0; --i)
14126  children.at(i)->moveToLayer(targetLayer, true);
14127  } else // append normally
14128  {
14129  for (int i=0; i<children.size(); ++i)
14130  children.at(i)->moveToLayer(targetLayer, false);
14131  }
14132  // if removed layer is current layer, change current layer to layer below/above:
14133  if (layer == mCurrentLayer)
14134  setCurrentLayer(targetLayer);
14135  // invalidate the paint buffer that was responsible for this layer:
14136  if (!layer->mPaintBuffer.isNull())
14137  layer->mPaintBuffer.data()->setInvalidated();
14138  // remove layer:
14139  delete layer;
14140  mLayers.removeOne(layer);
14142  return true;
14143 }
14144 
14155 {
14156  if (!mLayers.contains(layer))
14157  {
14158  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
14159  return false;
14160  }
14161  if (!mLayers.contains(otherLayer))
14162  {
14163  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
14164  return false;
14165  }
14166 
14167  if (layer->index() > otherLayer->index())
14168  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
14169  else if (layer->index() < otherLayer->index())
14170  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1));
14171 
14172  // invalidate the paint buffers that are responsible for the layers:
14173  if (!layer->mPaintBuffer.isNull())
14174  layer->mPaintBuffer.data()->setInvalidated();
14175  if (!otherLayer->mPaintBuffer.isNull())
14176  otherLayer->mPaintBuffer.data()->setInvalidated();
14177 
14179  return true;
14180 }
14181 
14192 {
14193  return axisRects().size();
14194 }
14195 
14216 {
14217  const QList<QCPAxisRect*> rectList = axisRects();
14218  if (index >= 0 && index < rectList.size())
14219  {
14220  return rectList.at(index);
14221  } else
14222  {
14223  qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
14224  return 0;
14225  }
14226 }
14227 
14239 QList<QCPAxisRect*> QCustomPlot::axisRects() const
14240 {
14241  QList<QCPAxisRect*> result;
14242  QStack<QCPLayoutElement*> elementStack;
14243  if (mPlotLayout)
14244  elementStack.push(mPlotLayout);
14245 
14246  while (!elementStack.isEmpty())
14247  {
14248  foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
14249  {
14250  if (element)
14251  {
14252  elementStack.push(element);
14253  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
14254  result.append(ar);
14255  }
14256  }
14257  }
14258 
14259  return result;
14260 }
14261 
14272 {
14273  QCPLayoutElement *currentElement = mPlotLayout;
14274  bool searchSubElements = true;
14275  while (searchSubElements && currentElement)
14276  {
14277  searchSubElements = false;
14278  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
14279  {
14280  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
14281  {
14282  currentElement = subElement;
14283  searchSubElements = true;
14284  break;
14285  }
14286  }
14287  }
14288  return currentElement;
14289 }
14290 
14301 QCPAxisRect *QCustomPlot::axisRectAt(const QPointF &pos) const
14302 {
14303  QCPAxisRect *result = 0;
14304  QCPLayoutElement *currentElement = mPlotLayout;
14305  bool searchSubElements = true;
14306  while (searchSubElements && currentElement)
14307  {
14308  searchSubElements = false;
14309  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
14310  {
14311  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
14312  {
14313  currentElement = subElement;
14314  searchSubElements = true;
14315  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(currentElement))
14316  result = ar;
14317  break;
14318  }
14319  }
14320  }
14321  return result;
14322 }
14323 
14331 QList<QCPAxis*> QCustomPlot::selectedAxes() const
14332 {
14333  QList<QCPAxis*> result, allAxes;
14334  foreach (QCPAxisRect *rect, axisRects())
14335  allAxes << rect->axes();
14336 
14337  foreach (QCPAxis *axis, allAxes)
14338  {
14339  if (axis->selectedParts() != QCPAxis::spNone)
14340  result.append(axis);
14341  }
14342 
14343  return result;
14344 }
14345 
14353 QList<QCPLegend*> QCustomPlot::selectedLegends() const
14354 {
14355  QList<QCPLegend*> result;
14356 
14357  QStack<QCPLayoutElement*> elementStack;
14358  if (mPlotLayout)
14359  elementStack.push(mPlotLayout);
14360 
14361  while (!elementStack.isEmpty())
14362  {
14363  foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
14364  {
14365  if (subElement)
14366  {
14367  elementStack.push(subElement);
14368  if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
14369  {
14370  if (leg->selectedParts() != QCPLegend::spNone)
14371  result.append(leg);
14372  }
14373  }
14374  }
14375  }
14376 
14377  return result;
14378 }
14379 
14390 {
14391  foreach (QCPLayer *layer, mLayers)
14392  {
14393  foreach (QCPLayerable *layerable, layer->children())
14394  layerable->deselectEvent(0);
14395  }
14396 }
14397 
14424 {
14425  if (refreshPriority == QCustomPlot::rpQueuedReplot)
14426  {
14427  if (!mReplotQueued)
14428  {
14429  mReplotQueued = true;
14430  QTimer::singleShot(0, this, SLOT(replot()));
14431  }
14432  return;
14433  }
14434 
14435  if (mReplotting) // incase signals loop back to replot slot
14436  return;
14437  mReplotting = true;
14438  mReplotQueued = false;
14439  emit beforeReplot();
14440 
14441  updateLayout();
14442  // draw all layered objects (grid, axes, plottables, items, legend,...) into their buffers:
14444  foreach (QCPLayer *layer, mLayers)
14445  layer->drawToPaintBuffer();
14446  for (int i=0; i<mPaintBuffers.size(); ++i)
14447  mPaintBuffers.at(i)->setInvalidated(false);
14448 
14449  if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority==rpImmediateRefresh)
14450  repaint();
14451  else
14452  update();
14453 
14454  emit afterReplot();
14455  mReplotting = false;
14456 }
14457 
14466 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
14467 {
14468  QList<QCPAxis*> allAxes;
14469  foreach (QCPAxisRect *rect, axisRects())
14470  allAxes << rect->axes();
14471 
14472  foreach (QCPAxis *axis, allAxes)
14473  axis->rescale(onlyVisiblePlottables);
14474 }
14475 
14513 bool QCustomPlot::savePdf(const QString &fileName, int width, int height, QCP::ExportPen exportPen, const QString &pdfCreator, const QString &pdfTitle)
14514 {
14515  bool success = false;
14516 #ifdef QT_NO_PRINTER
14517  Q_UNUSED(fileName)
14518  Q_UNUSED(exportPen)
14519  Q_UNUSED(width)
14520  Q_UNUSED(height)
14521  Q_UNUSED(pdfCreator)
14522  Q_UNUSED(pdfTitle)
14523  qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
14524 #else
14525  int newWidth, newHeight;
14526  if (width == 0 || height == 0)
14527  {
14528  newWidth = this->width();
14529  newHeight = this->height();
14530  } else
14531  {
14532  newWidth = width;
14533  newHeight = height;
14534  }
14535 
14536  QPrinter printer(QPrinter::ScreenResolution);
14537  printer.setOutputFileName(fileName);
14538  printer.setOutputFormat(QPrinter::PdfFormat);
14539  printer.setColorMode(QPrinter::Color);
14540  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
14541  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
14542  QRect oldViewport = viewport();
14543  setViewport(QRect(0, 0, newWidth, newHeight));
14544 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
14545  printer.setFullPage(true);
14546  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
14547 #else
14548  QPageLayout pageLayout;
14549  pageLayout.setMode(QPageLayout::FullPageMode);
14550  pageLayout.setOrientation(QPageLayout::Portrait);
14551  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
14552  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
14553  printer.setPageLayout(pageLayout);
14554 #endif
14555  QCPPainter printpainter;
14556  if (printpainter.begin(&printer))
14557  {
14558  printpainter.setMode(QCPPainter::pmVectorized);
14559  printpainter.setMode(QCPPainter::pmNoCaching);
14560  printpainter.setMode(QCPPainter::pmNonCosmetic, exportPen==QCP::epNoCosmetic);
14561  printpainter.setWindow(mViewport);
14562  if (mBackgroundBrush.style() != Qt::NoBrush &&
14563  mBackgroundBrush.color() != Qt::white &&
14564  mBackgroundBrush.color() != Qt::transparent &&
14565  mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
14566  printpainter.fillRect(viewport(), mBackgroundBrush);
14567  draw(&printpainter);
14568  printpainter.end();
14569  success = true;
14570  }
14571  setViewport(oldViewport);
14572 #endif // QT_NO_PRINTER
14573  return success;
14574 }
14575 
14621 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
14622 {
14623  return saveRastered(fileName, width, height, scale, "PNG", quality, resolution, resolutionUnit);
14624 }
14625 
14668 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
14669 {
14670  return saveRastered(fileName, width, height, scale, "JPG", quality, resolution, resolutionUnit);
14671 }
14672 
14712 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale, int resolution, QCP::ResolutionUnit resolutionUnit)
14713 {
14714  return saveRastered(fileName, width, height, scale, "BMP", -1, resolution, resolutionUnit);
14715 }
14716 
14726 {
14728 }
14729 
14736 {
14738 }
14739 
14745 void QCustomPlot::paintEvent(QPaintEvent *event)
14746 {
14747  Q_UNUSED(event);
14748  QCPPainter painter(this);
14749  if (painter.isActive())
14750  {
14751  painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
14752  if (mBackgroundBrush.style() != Qt::NoBrush)
14753  painter.fillRect(mViewport, mBackgroundBrush);
14754  drawBackground(&painter);
14755  for (int bufferIndex = 0; bufferIndex < mPaintBuffers.size(); ++bufferIndex)
14756  mPaintBuffers.at(bufferIndex)->draw(&painter);
14757  }
14758 }
14759 
14765 void QCustomPlot::resizeEvent(QResizeEvent *event)
14766 {
14767  Q_UNUSED(event)
14768  // resize and repaint the buffer:
14769  setViewport(rect());
14770  replot(rpQueuedRefresh); // queued refresh is important here, to prevent painting issues in some contexts (e.g. MDI subwindow)
14771 }
14772 
14782 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
14783 {
14784  emit mouseDoubleClick(event);
14785  mMouseHasMoved = false;
14786  mMousePressPos = event->pos();
14787 
14788  // determine layerable under the cursor (this event is called instead of the second press event in a double-click):
14789  QList<QVariant> details;
14790  QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);
14791  for (int i=0; i<candidates.size(); ++i)
14792  {
14793  event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
14794  candidates.at(i)->mouseDoubleClickEvent(event, details.at(i));
14795  if (event->isAccepted())
14796  {
14797  mMouseEventLayerable = candidates.at(i);
14798  mMouseEventLayerableDetails = details.at(i);
14799  break;
14800  }
14801  }
14802 
14803  // emit specialized object double click signals:
14804  if (!candidates.isEmpty())
14805  {
14806  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(candidates.first()))
14807  {
14808  int dataIndex = 0;
14809  if (!details.first().value<QCPDataSelection>().isEmpty())
14810  dataIndex = details.first().value<QCPDataSelection>().dataRange().begin();
14811  emit plottableDoubleClick(ap, dataIndex, event);
14812  } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(candidates.first()))
14813  emit axisDoubleClick(ax, details.first().value<QCPAxis::SelectablePart>(), event);
14814  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(candidates.first()))
14815  emit itemDoubleClick(ai, event);
14816  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(candidates.first()))
14817  emit legendDoubleClick(lg, 0, event);
14818  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(candidates.first()))
14819  emit legendDoubleClick(li->parentLegend(), li, event);
14820  }
14821 
14822  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14823 }
14824 
14834 void QCustomPlot::mousePressEvent(QMouseEvent *event)
14835 {
14836  emit mousePress(event);
14837  // save some state to tell in releaseEvent whether it was a click:
14838  mMouseHasMoved = false;
14839  mMousePressPos = event->pos();
14840 
14842  {
14843  if (mSelectionRectMode != QCP::srmZoom || qobject_cast<QCPAxisRect*>(axisRectAt(mMousePressPos))) // in zoom mode only activate selection rect if on an axis rect
14845  } else
14846  {
14847  // no selection rect interaction, prepare for click signal emission and forward event to layerable under the cursor:
14848  QList<QVariant> details;
14849  QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);
14850  if (!candidates.isEmpty())
14851  {
14852  mMouseSignalLayerable = candidates.first(); // candidate for signal emission is always topmost hit layerable (signal emitted in release event)
14853  mMouseSignalLayerableDetails = details.first();
14854  }
14855  // forward event to topmost candidate which accepts the event:
14856  for (int i=0; i<candidates.size(); ++i)
14857  {
14858  event->accept(); // default impl of QCPLayerable's mouse events call ignore() on the event, in that case propagate to next candidate in list
14859  candidates.at(i)->mousePressEvent(event, details.at(i));
14860  if (event->isAccepted())
14861  {
14862  mMouseEventLayerable = candidates.at(i);
14863  mMouseEventLayerableDetails = details.at(i);
14864  break;
14865  }
14866  }
14867  }
14868 
14869  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14870 }
14871 
14884 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
14885 {
14886  emit mouseMove(event);
14887 
14888  if (!mMouseHasMoved && (mMousePressPos-event->pos()).manhattanLength() > 3)
14889  mMouseHasMoved = true; // moved too far from mouse press position, don't handle as click on mouse release
14890 
14892  mSelectionRect->moveSelection(event);
14893  else if (mMouseEventLayerable) // call event of affected layerable:
14894  mMouseEventLayerable->mouseMoveEvent(event, mMousePressPos);
14895 
14896  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14897 }
14898 
14913 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
14914 {
14915  emit mouseRelease(event);
14916 
14917  if (!mMouseHasMoved) // mouse hasn't moved (much) between press and release, so handle as click
14918  {
14919  if (mSelectionRect && mSelectionRect->isActive()) // a simple click shouldn't successfully finish a selection rect, so cancel it here
14921  if (event->button() == Qt::LeftButton)
14922  processPointSelection(event);
14923 
14924  // emit specialized click signals of QCustomPlot instance:
14925  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(mMouseSignalLayerable))
14926  {
14927  int dataIndex = 0;
14930  emit plottableClick(ap, dataIndex, event);
14931  } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(mMouseSignalLayerable))
14933  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(mMouseSignalLayerable))
14934  emit itemClick(ai, event);
14935  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(mMouseSignalLayerable))
14936  emit legendClick(lg, 0, event);
14937  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(mMouseSignalLayerable))
14938  emit legendClick(li->parentLegend(), li, event);
14940  }
14941 
14942  if (mSelectionRect && mSelectionRect->isActive()) // Note: if a click was detected above, the selection rect is canceled there
14943  {
14944  // finish selection rect, the appropriate action will be taken via signal-slot connection:
14945  mSelectionRect->endSelection(event);
14946  } else
14947  {
14948  // call event of affected layerable:
14950  {
14951  mMouseEventLayerable->mouseReleaseEvent(event, mMousePressPos);
14953  }
14954  }
14955 
14956  if (noAntialiasingOnDrag())
14958 
14959  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14960 }
14961 
14967 void QCustomPlot::wheelEvent(QWheelEvent *event)
14968 {
14969  emit mouseWheel(event);
14970  // forward event to layerable under cursor:
14971  QList<QCPLayerable*> candidates = layerableListAt(event->pos(), false);
14972  for (int i=0; i<candidates.size(); ++i)
14973  {
14974  event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
14975  candidates.at(i)->wheelEvent(event);
14976  if (event->isAccepted())
14977  break;
14978  }
14979  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14980 }
14981 
14993 {
14994  updateLayout();
14995 
14996  // draw viewport background pixmap:
14997  drawBackground(painter);
14998 
14999  // draw all layered objects (grid, axes, plottables, items, legend,...):
15000  foreach (QCPLayer *layer, mLayers)
15001  layer->draw(painter);
15002 
15003  /* Debug code to draw all layout element rects
15004  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
15005  {
15006  painter->setBrush(Qt::NoBrush);
15007  painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
15008  painter->drawRect(el->rect());
15009  painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
15010  painter->drawRect(el->outerRect());
15011  }
15012  */
15013 }
15014 
15024 {
15025  // run through layout phases:
15029 }
15030 
15049 {
15050  // Note: background color is handled in individual replot/save functions
15051 
15052  // draw background pixmap (on top of fill, if brush specified):
15053  if (!mBackgroundPixmap.isNull())
15054  {
15055  if (mBackgroundScaled)
15056  {
15057  // check whether mScaledBackground needs to be updated:
15058  QSize scaledSize(mBackgroundPixmap.size());
15059  scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
15060  if (mScaledBackgroundPixmap.size() != scaledSize)
15061  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
15062  painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
15063  } else
15064  {
15065  painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
15066  }
15067  }
15068 }
15069 
15090 {
15091  int bufferIndex = 0;
15092  if (mPaintBuffers.isEmpty())
15093  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
15094 
15095  for (int layerIndex = 0; layerIndex < mLayers.size(); ++layerIndex)
15096  {
15097  QCPLayer *layer = mLayers.at(layerIndex);
15098  if (layer->mode() == QCPLayer::lmLogical)
15099  {
15100  layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
15101  } else if (layer->mode() == QCPLayer::lmBuffered)
15102  {
15103  ++bufferIndex;
15104  if (bufferIndex >= mPaintBuffers.size())
15105  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
15106  layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
15107  if (layerIndex < mLayers.size()-1 && mLayers.at(layerIndex+1)->mode() == QCPLayer::lmLogical) // not last layer, and next one is logical, so prepare another buffer for next layerables
15108  {
15109  ++bufferIndex;
15110  if (bufferIndex >= mPaintBuffers.size())
15111  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
15112  }
15113  }
15114  }
15115  // remove unneeded buffers:
15116  while (mPaintBuffers.size()-1 > bufferIndex)
15117  mPaintBuffers.removeLast();
15118  // resize buffers to viewport size and clear contents:
15119  for (int i=0; i<mPaintBuffers.size(); ++i)
15120  {
15121  mPaintBuffers.at(i)->setSize(viewport().size()); // won't do anything if already correct size
15122  mPaintBuffers.at(i)->clear(Qt::transparent);
15123  mPaintBuffers.at(i)->setInvalidated();
15124  }
15125 }
15126 
15136 {
15137  if (mOpenGl)
15138  {
15139 #if defined(QCP_OPENGL_FBO)
15140  return new QCPPaintBufferGlFbo(viewport().size(), mBufferDevicePixelRatio, mGlContext, mGlPaintDevice);
15141 #elif defined(QCP_OPENGL_PBUFFER)
15142  return new QCPPaintBufferGlPbuffer(viewport().size(), mBufferDevicePixelRatio, mOpenGlMultisamples);
15143 #else
15144  qDebug() << Q_FUNC_INFO << "OpenGL enabled even though no support for it compiled in, this shouldn't have happened. Falling back to pixmap paint buffer.";
15146 #endif
15147  } else
15149 }
15150 
15163 {
15164  for (int i=0; i<mPaintBuffers.size(); ++i)
15165  {
15166  if (mPaintBuffers.at(i)->invalidated())
15167  return true;
15168  }
15169  return false;
15170 }
15171 
15186 {
15187 #ifdef QCP_OPENGL_FBO
15188  freeOpenGl();
15189  QSurfaceFormat proposedSurfaceFormat;
15190  proposedSurfaceFormat.setSamples(mOpenGlMultisamples);
15191 #ifdef QCP_OPENGL_OFFSCREENSURFACE
15192  QOffscreenSurface *surface = new QOffscreenSurface;
15193 #else
15194  QWindow *surface = new QWindow;
15195  surface->setSurfaceType(QSurface::OpenGLSurface);
15196 #endif
15197  surface->setFormat(proposedSurfaceFormat);
15198  surface->create();
15199  mGlSurface = QSharedPointer<QSurface>(surface);
15200  mGlContext = QSharedPointer<QOpenGLContext>(new QOpenGLContext);
15201  mGlContext->setFormat(mGlSurface->format());
15202  if (!mGlContext->create())
15203  {
15204  qDebug() << Q_FUNC_INFO << "Failed to create OpenGL context";
15205  mGlContext.clear();
15206  mGlSurface.clear();
15207  return false;
15208  }
15209  if (!mGlContext->makeCurrent(mGlSurface.data())) // context needs to be current to create paint device
15210  {
15211  qDebug() << Q_FUNC_INFO << "Failed to make opengl context current";
15212  mGlContext.clear();
15213  mGlSurface.clear();
15214  return false;
15215  }
15216  if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
15217  {
15218  qDebug() << Q_FUNC_INFO << "OpenGL of this system doesn't support frame buffer objects";
15219  mGlContext.clear();
15220  mGlSurface.clear();
15221  return false;
15222  }
15223  mGlPaintDevice = QSharedPointer<QOpenGLPaintDevice>(new QOpenGLPaintDevice);
15224  return true;
15225 #elif defined(QCP_OPENGL_PBUFFER)
15226  return QGLFormat::hasOpenGL();
15227 #else
15228  return false;
15229 #endif
15230 }
15231 
15244 {
15245 #ifdef QCP_OPENGL_FBO
15246  mGlPaintDevice.clear();
15247  mGlContext.clear();
15248  mGlSurface.clear();
15249 #endif
15250 }
15251 
15258 {
15259  if (xAxis == axis)
15260  xAxis = 0;
15261  if (xAxis2 == axis)
15262  xAxis2 = 0;
15263  if (yAxis == axis)
15264  yAxis = 0;
15265  if (yAxis2 == axis)
15266  yAxis2 = 0;
15267 
15268  // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
15269 }
15270 
15277 {
15278  if (this->legend == legend)
15279  this->legend = 0;
15280 }
15281 
15299 void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event)
15300 {
15301  bool selectionStateChanged = false;
15302 
15303  if (mInteractions.testFlag(QCP::iSelectPlottables))
15304  {
15305  QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> > potentialSelections; // map key is number of selected data points, so we have selections sorted by size
15306  QRectF rectF(rect.normalized());
15307  if (QCPAxisRect *affectedAxisRect = axisRectAt(rectF.topLeft()))
15308  {
15309  // determine plottables that were hit by the rect and thus are candidates for selection:
15310  foreach (QCPAbstractPlottable *plottable, affectedAxisRect->plottables())
15311  {
15312  if (QCPPlottableInterface1D *plottableInterface = plottable->interface1D())
15313  {
15314  QCPDataSelection dataSel = plottableInterface->selectTestRect(rectF, true);
15315  if (!dataSel.isEmpty())
15316  potentialSelections.insertMulti(dataSel.dataPointCount(), QPair<QCPAbstractPlottable*, QCPDataSelection>(plottable, dataSel));
15317  }
15318  }
15319 
15320  if (!mInteractions.testFlag(QCP::iMultiSelect))
15321  {
15322  // only leave plottable with most selected points in map, since we will only select a single plottable:
15323  if (!potentialSelections.isEmpty())
15324  {
15325  QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> >::iterator it = potentialSelections.begin();
15326  while (it != potentialSelections.end()-1) // erase all except last element
15327  it = potentialSelections.erase(it);
15328  }
15329  }
15330 
15331  bool additive = event->modifiers().testFlag(mMultiSelectModifier);
15332  // deselect all other layerables if not additive selection:
15333  if (!additive)
15334  {
15335  // emit deselection except to those plottables who will be selected afterwards:
15336  foreach (QCPLayer *layer, mLayers)
15337  {
15338  foreach (QCPLayerable *layerable, layer->children())
15339  {
15340  if ((potentialSelections.isEmpty() || potentialSelections.constBegin()->first != layerable) && mInteractions.testFlag(layerable->selectionCategory()))
15341  {
15342  bool selChanged = false;
15343  layerable->deselectEvent(&selChanged);
15344  selectionStateChanged |= selChanged;
15345  }
15346  }
15347  }
15348  }
15349 
15350  // go through selections in reverse (largest selection first) and emit select events:
15351  QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> >::const_iterator it = potentialSelections.constEnd();
15352  while (it != potentialSelections.constBegin())
15353  {
15354  --it;
15355  if (mInteractions.testFlag(it.value().first->selectionCategory()))
15356  {
15357  bool selChanged = false;
15358  it.value().first->selectEvent(event, additive, QVariant::fromValue(it.value().second), &selChanged);
15359  selectionStateChanged |= selChanged;
15360  }
15361  }
15362  }
15363  }
15364 
15365  if (selectionStateChanged)
15366  {
15367  emit selectionChangedByUser();
15369  } else if (mSelectionRect)
15370  mSelectionRect->layer()->replot();
15371 }
15372 
15384 void QCustomPlot::processRectZoom(QRect rect, QMouseEvent *event)
15385 {
15386  Q_UNUSED(event)
15387  if (QCPAxisRect *axisRect = axisRectAt(rect.topLeft()))
15388  {
15389  QList<QCPAxis*> affectedAxes = QList<QCPAxis*>() << axisRect->rangeZoomAxes(Qt::Horizontal) << axisRect->rangeZoomAxes(Qt::Vertical);
15390  affectedAxes.removeAll(static_cast<QCPAxis*>(0));
15391  axisRect->zoom(QRectF(rect), affectedAxes);
15392  }
15393  replot(rpQueuedReplot); // always replot to make selection rect disappear
15394 }
15395 
15413 void QCustomPlot::processPointSelection(QMouseEvent *event)
15414 {
15415  QVariant details;
15416  QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
15417  bool selectionStateChanged = false;
15418  bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
15419  // deselect all other layerables if not additive selection:
15420  if (!additive)
15421  {
15422  foreach (QCPLayer *layer, mLayers)
15423  {
15424  foreach (QCPLayerable *layerable, layer->children())
15425  {
15426  if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
15427  {
15428  bool selChanged = false;
15429  layerable->deselectEvent(&selChanged);
15430  selectionStateChanged |= selChanged;
15431  }
15432  }
15433  }
15434  }
15435  if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
15436  {
15437  // a layerable was actually clicked, call its selectEvent:
15438  bool selChanged = false;
15439  clickedLayerable->selectEvent(event, additive, details, &selChanged);
15440  selectionStateChanged |= selChanged;
15441  }
15442  if (selectionStateChanged)
15443  {
15444  emit selectionChangedByUser();
15446  }
15447 }
15448 
15461 {
15462  if (mPlottables.contains(plottable))
15463  {
15464  qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
15465  return false;
15466  }
15467  if (plottable->parentPlot() != this)
15468  {
15469  qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
15470  return false;
15471  }
15472 
15473  mPlottables.append(plottable);
15474  // possibly add plottable to legend:
15476  plottable->addToLegend();
15477  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
15478  plottable->setLayer(currentLayer());
15479  return true;
15480 }
15481 
15492 {
15493  if (!graph)
15494  {
15495  qDebug() << Q_FUNC_INFO << "passed graph is zero";
15496  return false;
15497  }
15498  if (mGraphs.contains(graph))
15499  {
15500  qDebug() << Q_FUNC_INFO << "graph already registered with this QCustomPlot";
15501  return false;
15502  }
15503 
15504  mGraphs.append(graph);
15505  return true;
15506 }
15507 
15508 
15519 {
15520  if (mItems.contains(item))
15521  {
15522  qDebug() << Q_FUNC_INFO << "item already added to this QCustomPlot:" << reinterpret_cast<quintptr>(item);
15523  return false;
15524  }
15525  if (item->parentPlot() != this)
15526  {
15527  qDebug() << Q_FUNC_INFO << "item not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
15528  return false;
15529  }
15530 
15531  mItems.append(item);
15532  if (!item->layer()) // usually the layer is already set in the constructor of the item (via QCPLayerable constructor)
15533  item->setLayer(currentLayer());
15534  return true;
15535 }
15536 
15544 {
15545  for (int i=0; i<mLayers.size(); ++i)
15546  mLayers.at(i)->mIndex = i;
15547 }
15548 
15564 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
15565 {
15566  QList<QVariant> details;
15567  QList<QCPLayerable*> candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : 0);
15568  if (selectionDetails && !details.isEmpty())
15569  *selectionDetails = details.first();
15570  if (!candidates.isEmpty())
15571  return candidates.first();
15572  else
15573  return 0;
15574 }
15575 
15594 QList<QCPLayerable*> QCustomPlot::layerableListAt(const QPointF &pos, bool onlySelectable, QList<QVariant> *selectionDetails) const
15595 {
15596  QList<QCPLayerable*> result;
15597  for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
15598  {
15599  const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
15600  for (int i=layerables.size()-1; i>=0; --i)
15601  {
15602  if (!layerables.at(i)->realVisibility())
15603  continue;
15604  QVariant details;
15605  double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : 0);
15606  if (dist >= 0 && dist < selectionTolerance())
15607  {
15608  result.append(layerables.at(i));
15609  if (selectionDetails)
15610  selectionDetails->append(details);
15611  }
15612  }
15613  }
15614  return result;
15615 }
15616 
15635 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
15636 {
15637  QImage buffer = toPixmap(width, height, scale).toImage();
15638 
15639  int dotsPerMeter = 0;
15640  switch (resolutionUnit)
15641  {
15642  case QCP::ruDotsPerMeter: dotsPerMeter = resolution; break;
15643  case QCP::ruDotsPerCentimeter: dotsPerMeter = resolution*100; break;
15644  case QCP::ruDotsPerInch: dotsPerMeter = resolution/0.0254; break;
15645  }
15646  buffer.setDotsPerMeterX(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools
15647  buffer.setDotsPerMeterY(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools
15648  if (!buffer.isNull())
15649  return buffer.save(fileName, format, quality);
15650  else
15651  return false;
15652 }
15653 
15662 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
15663 {
15664  // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
15665  int newWidth, newHeight;
15666  if (width == 0 || height == 0)
15667  {
15668  newWidth = this->width();
15669  newHeight = this->height();
15670  } else
15671  {
15672  newWidth = width;
15673  newHeight = height;
15674  }
15675  int scaledWidth = qRound(scale*newWidth);
15676  int scaledHeight = qRound(scale*newHeight);
15677 
15678  QPixmap result(scaledWidth, scaledHeight);
15679  result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
15680  QCPPainter painter;
15681  painter.begin(&result);
15682  if (painter.isActive())
15683  {
15684  QRect oldViewport = viewport();
15685  setViewport(QRect(0, 0, newWidth, newHeight));
15687  if (!qFuzzyCompare(scale, 1.0))
15688  {
15689  if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
15691  painter.scale(scale, scale);
15692  }
15693  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill
15694  painter.fillRect(mViewport, mBackgroundBrush);
15695  draw(&painter);
15696  setViewport(oldViewport);
15697  painter.end();
15698  } else // might happen if pixmap has width or height zero
15699  {
15700  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
15701  return QPixmap();
15702  }
15703  return result;
15704 }
15705 
15718 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
15719 {
15720  // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
15721  int newWidth, newHeight;
15722  if (width == 0 || height == 0)
15723  {
15724  newWidth = this->width();
15725  newHeight = this->height();
15726  } else
15727  {
15728  newWidth = width;
15729  newHeight = height;
15730  }
15731 
15732  if (painter->isActive())
15733  {
15734  QRect oldViewport = viewport();
15735  setViewport(QRect(0, 0, newWidth, newHeight));
15736  painter->setMode(QCPPainter::pmNoCaching);
15737  if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here
15738  painter->fillRect(mViewport, mBackgroundBrush);
15739  draw(painter);
15740  setViewport(oldViewport);
15741  } else
15742  qDebug() << Q_FUNC_INFO << "Passed painter is not active";
15743 }
15744 /* end of 'src/core.cpp' */
15745 
15746 //amalgamation: add plottable1d.cpp
15747 
15748 /* including file 'src/colorgradient.cpp', size 25342 */
15749 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
15750 
15751 
15755 
15791  mLevelCount(350),
15792  mColorInterpolation(ciRGB),
15793  mPeriodic(false),
15794  mColorBufferInvalidated(true)
15795 {
15796  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
15797 }
15798 
15806  mLevelCount(350),
15808  mPeriodic(false),
15810 {
15811  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
15812  loadPreset(preset);
15813 }
15814 
15815 /* undocumented operator */
15817 {
15818  return ((other.mLevelCount == this->mLevelCount) &&
15819  (other.mColorInterpolation == this->mColorInterpolation) &&
15820  (other.mPeriodic == this->mPeriodic) &&
15821  (other.mColorStops == this->mColorStops));
15822 }
15823 
15831 {
15832  if (n < 2)
15833  {
15834  qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
15835  n = 2;
15836  }
15837  if (n != mLevelCount)
15838  {
15839  mLevelCount = n;
15840  mColorBufferInvalidated = true;
15841  }
15842 }
15843 
15855 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
15856 {
15858  mColorBufferInvalidated = true;
15859 }
15860 
15867 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
15868 {
15869  mColorStops.insert(position, color);
15870  mColorBufferInvalidated = true;
15871 }
15872 
15881 {
15882  if (interpolation != mColorInterpolation)
15883  {
15884  mColorInterpolation = interpolation;
15885  mColorBufferInvalidated = true;
15886  }
15887 }
15888 
15905 {
15906  mPeriodic = enabled;
15907 }
15908 
15927 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
15928 {
15929  // If you change something here, make sure to also adapt color() and the other colorize() overload
15930  if (!data)
15931  {
15932  qDebug() << Q_FUNC_INFO << "null pointer given as data";
15933  return;
15934  }
15935  if (!scanLine)
15936  {
15937  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
15938  return;
15939  }
15942 
15943  if (!logarithmic)
15944  {
15945  const double posToIndexFactor = (mLevelCount-1)/range.size();
15946  if (mPeriodic)
15947  {
15948  for (int i=0; i<n; ++i)
15949  {
15950  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
15951  if (index < 0)
15952  index += mLevelCount;
15953  scanLine[i] = mColorBuffer.at(index);
15954  }
15955  } else
15956  {
15957  for (int i=0; i<n; ++i)
15958  {
15959  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
15960  if (index < 0)
15961  index = 0;
15962  else if (index >= mLevelCount)
15963  index = mLevelCount-1;
15964  scanLine[i] = mColorBuffer.at(index);
15965  }
15966  }
15967  } else // logarithmic == true
15968  {
15969  if (mPeriodic)
15970  {
15971  for (int i=0; i<n; ++i)
15972  {
15973  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
15974  if (index < 0)
15975  index += mLevelCount;
15976  scanLine[i] = mColorBuffer.at(index);
15977  }
15978  } else
15979  {
15980  for (int i=0; i<n; ++i)
15981  {
15982  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
15983  if (index < 0)
15984  index = 0;
15985  else if (index >= mLevelCount)
15986  index = mLevelCount-1;
15987  scanLine[i] = mColorBuffer.at(index);
15988  }
15989  }
15990  }
15991 }
15992 
16001 void QCPColorGradient::colorize(const double *data, const unsigned char *alpha, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
16002 {
16003  // If you change something here, make sure to also adapt color() and the other colorize() overload
16004  if (!data)
16005  {
16006  qDebug() << Q_FUNC_INFO << "null pointer given as data";
16007  return;
16008  }
16009  if (!alpha)
16010  {
16011  qDebug() << Q_FUNC_INFO << "null pointer given as alpha";
16012  return;
16013  }
16014  if (!scanLine)
16015  {
16016  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
16017  return;
16018  }
16021 
16022  if (!logarithmic)
16023  {
16024  const double posToIndexFactor = (mLevelCount-1)/range.size();
16025  if (mPeriodic)
16026  {
16027  for (int i=0; i<n; ++i)
16028  {
16029  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
16030  if (index < 0)
16031  index += mLevelCount;
16032  if (alpha[dataIndexFactor*i] == 255)
16033  {
16034  scanLine[i] = mColorBuffer.at(index);
16035  } else
16036  {
16037  const QRgb rgb = mColorBuffer.at(index);
16038  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
16039  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
16040  }
16041  }
16042  } else
16043  {
16044  for (int i=0; i<n; ++i)
16045  {
16046  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
16047  if (index < 0)
16048  index = 0;
16049  else if (index >= mLevelCount)
16050  index = mLevelCount-1;
16051  if (alpha[dataIndexFactor*i] == 255)
16052  {
16053  scanLine[i] = mColorBuffer.at(index);
16054  } else
16055  {
16056  const QRgb rgb = mColorBuffer.at(index);
16057  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
16058  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
16059  }
16060  }
16061  }
16062  } else // logarithmic == true
16063  {
16064  if (mPeriodic)
16065  {
16066  for (int i=0; i<n; ++i)
16067  {
16068  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
16069  if (index < 0)
16070  index += mLevelCount;
16071  if (alpha[dataIndexFactor*i] == 255)
16072  {
16073  scanLine[i] = mColorBuffer.at(index);
16074  } else
16075  {
16076  const QRgb rgb = mColorBuffer.at(index);
16077  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
16078  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
16079  }
16080  }
16081  } else
16082  {
16083  for (int i=0; i<n; ++i)
16084  {
16085  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
16086  if (index < 0)
16087  index = 0;
16088  else if (index >= mLevelCount)
16089  index = mLevelCount-1;
16090  if (alpha[dataIndexFactor*i] == 255)
16091  {
16092  scanLine[i] = mColorBuffer.at(index);
16093  } else
16094  {
16095  const QRgb rgb = mColorBuffer.at(index);
16096  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
16097  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
16098  }
16099  }
16100  }
16101  }
16102 }
16103 
16116 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
16117 {
16118  // If you change something here, make sure to also adapt ::colorize()
16121  int index = 0;
16122  if (!logarithmic)
16123  index = (position-range.lower)*(mLevelCount-1)/range.size();
16124  else
16125  index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
16126  if (mPeriodic)
16127  {
16128  index = index % mLevelCount;
16129  if (index < 0)
16130  index += mLevelCount;
16131  } else
16132  {
16133  if (index < 0)
16134  index = 0;
16135  else if (index >= mLevelCount)
16136  index = mLevelCount-1;
16137  }
16138  return mColorBuffer.at(index);
16139 }
16140 
16149 {
16150  clearColorStops();
16151  switch (preset)
16152  {
16153  case gpGrayscale:
16155  setColorStopAt(0, Qt::black);
16156  setColorStopAt(1, Qt::white);
16157  break;
16158  case gpHot:
16160  setColorStopAt(0, QColor(50, 0, 0));
16161  setColorStopAt(0.2, QColor(180, 10, 0));
16162  setColorStopAt(0.4, QColor(245, 50, 0));
16163  setColorStopAt(0.6, QColor(255, 150, 10));
16164  setColorStopAt(0.8, QColor(255, 255, 50));
16165  setColorStopAt(1, QColor(255, 255, 255));
16166  break;
16167  case gpCold:
16169  setColorStopAt(0, QColor(0, 0, 50));
16170  setColorStopAt(0.2, QColor(0, 10, 180));
16171  setColorStopAt(0.4, QColor(0, 50, 245));
16172  setColorStopAt(0.6, QColor(10, 150, 255));
16173  setColorStopAt(0.8, QColor(50, 255, 255));
16174  setColorStopAt(1, QColor(255, 255, 255));
16175  break;
16176  case gpNight:
16178  setColorStopAt(0, QColor(10, 20, 30));
16179  setColorStopAt(1, QColor(250, 255, 250));
16180  break;
16181  case gpCandy:
16183  setColorStopAt(0, QColor(0, 0, 255));
16184  setColorStopAt(1, QColor(255, 250, 250));
16185  break;
16186  case gpGeography:
16188  setColorStopAt(0, QColor(70, 170, 210));
16189  setColorStopAt(0.20, QColor(90, 160, 180));
16190  setColorStopAt(0.25, QColor(45, 130, 175));
16191  setColorStopAt(0.30, QColor(100, 140, 125));
16192  setColorStopAt(0.5, QColor(100, 140, 100));
16193  setColorStopAt(0.6, QColor(130, 145, 120));
16194  setColorStopAt(0.7, QColor(140, 130, 120));
16195  setColorStopAt(0.9, QColor(180, 190, 190));
16196  setColorStopAt(1, QColor(210, 210, 230));
16197  break;
16198  case gpIon:
16200  setColorStopAt(0, QColor(50, 10, 10));
16201  setColorStopAt(0.45, QColor(0, 0, 255));
16202  setColorStopAt(0.8, QColor(0, 255, 255));
16203  setColorStopAt(1, QColor(0, 255, 0));
16204  break;
16205  case gpThermal:
16207  setColorStopAt(0, QColor(0, 0, 50));
16208  setColorStopAt(0.15, QColor(20, 0, 120));
16209  setColorStopAt(0.33, QColor(200, 30, 140));
16210  setColorStopAt(0.6, QColor(255, 100, 0));
16211  setColorStopAt(0.85, QColor(255, 255, 40));
16212  setColorStopAt(1, QColor(255, 255, 255));
16213  break;
16214  case gpPolar:
16216  setColorStopAt(0, QColor(50, 255, 255));
16217  setColorStopAt(0.18, QColor(10, 70, 255));
16218  setColorStopAt(0.28, QColor(10, 10, 190));
16219  setColorStopAt(0.5, QColor(0, 0, 0));
16220  setColorStopAt(0.72, QColor(190, 10, 10));
16221  setColorStopAt(0.82, QColor(255, 70, 10));
16222  setColorStopAt(1, QColor(255, 255, 50));
16223  break;
16224  case gpSpectrum:
16226  setColorStopAt(0, QColor(50, 0, 50));
16227  setColorStopAt(0.15, QColor(0, 0, 255));
16228  setColorStopAt(0.35, QColor(0, 255, 255));
16229  setColorStopAt(0.6, QColor(255, 255, 0));
16230  setColorStopAt(0.75, QColor(255, 30, 0));
16231  setColorStopAt(1, QColor(50, 0, 0));
16232  break;
16233  case gpJet:
16235  setColorStopAt(0, QColor(0, 0, 100));
16236  setColorStopAt(0.15, QColor(0, 50, 255));
16237  setColorStopAt(0.35, QColor(0, 255, 255));
16238  setColorStopAt(0.65, QColor(255, 255, 0));
16239  setColorStopAt(0.85, QColor(255, 30, 0));
16240  setColorStopAt(1, QColor(100, 0, 0));
16241  break;
16242  case gpHues:
16244  setColorStopAt(0, QColor(255, 0, 0));
16245  setColorStopAt(1.0/3.0, QColor(0, 0, 255));
16246  setColorStopAt(2.0/3.0, QColor(0, 255, 0));
16247  setColorStopAt(1, QColor(255, 0, 0));
16248  break;
16249  }
16250 }
16251 
16258 {
16259  mColorStops.clear();
16260  mColorBufferInvalidated = true;
16261 }
16262 
16270 {
16271  QCPColorGradient result(*this);
16272  result.clearColorStops();
16273  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
16274  result.setColorStopAt(1.0-it.key(), it.value());
16275  return result;
16276 }
16277 
16284 {
16285  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
16286  {
16287  if (it.value().alpha() < 255)
16288  return true;
16289  }
16290  return false;
16291 }
16292 
16299 {
16300  if (mColorBuffer.size() != mLevelCount)
16301  mColorBuffer.resize(mLevelCount);
16302  if (mColorStops.size() > 1)
16303  {
16304  double indexToPosFactor = 1.0/(double)(mLevelCount-1);
16305  const bool useAlpha = stopsUseAlpha();
16306  for (int i=0; i<mLevelCount; ++i)
16307  {
16308  double position = i*indexToPosFactor;
16309  QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
16310  if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
16311  {
16312  if (useAlpha)
16313  {
16314  const QColor col = (it-1).value();
16315  const float alphaPremultiplier = col.alpha()/255.0f; // since we use QImage::Format_ARGB32_Premultiplied
16316  mColorBuffer[i] = qRgba(col.red()*alphaPremultiplier, col.green()*alphaPremultiplier, col.blue()*alphaPremultiplier, col.alpha());
16317  } else
16318  mColorBuffer[i] = (it-1).value().rgba();
16319  } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
16320  {
16321  if (useAlpha)
16322  {
16323  const QColor col = it.value();
16324  const float alphaPremultiplier = col.alpha()/255.0f; // since we use QImage::Format_ARGB32_Premultiplied
16325  mColorBuffer[i] = qRgba(col.red()*alphaPremultiplier, col.green()*alphaPremultiplier, col.blue()*alphaPremultiplier, col.alpha());
16326  } else
16327  mColorBuffer[i] = it.value().rgba();
16328  } else // position is in between stops (or on an intermediate stop), interpolate color
16329  {
16330  QMap<double, QColor>::const_iterator high = it;
16331  QMap<double, QColor>::const_iterator low = it-1;
16332  double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
16333  switch (mColorInterpolation)
16334  {
16335  case ciRGB:
16336  {
16337  if (useAlpha)
16338  {
16339  const int alpha = (1-t)*low.value().alpha() + t*high.value().alpha();
16340  const float alphaPremultiplier = alpha/255.0f; // since we use QImage::Format_ARGB32_Premultiplied
16341  mColorBuffer[i] = qRgba(((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier,
16342  ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier,
16343  ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier,
16344  alpha);
16345  } else
16346  {
16347  mColorBuffer[i] = qRgb(((1-t)*low.value().red() + t*high.value().red()),
16348  ((1-t)*low.value().green() + t*high.value().green()),
16349  ((1-t)*low.value().blue() + t*high.value().blue()));
16350  }
16351  break;
16352  }
16353  case ciHSV:
16354  {
16355  QColor lowHsv = low.value().toHsv();
16356  QColor highHsv = high.value().toHsv();
16357  double hue = 0;
16358  double hueDiff = highHsv.hueF()-lowHsv.hueF();
16359  if (hueDiff > 0.5)
16360  hue = lowHsv.hueF() - t*(1.0-hueDiff);
16361  else if (hueDiff < -0.5)
16362  hue = lowHsv.hueF() + t*(1.0+hueDiff);
16363  else
16364  hue = lowHsv.hueF() + t*hueDiff;
16365  if (hue < 0) hue += 1.0;
16366  else if (hue >= 1.0) hue -= 1.0;
16367  if (useAlpha)
16368  {
16369  const QRgb rgb = QColor::fromHsvF(hue,
16370  (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),
16371  (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
16372  const float alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF();
16373  mColorBuffer[i] = qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha);
16374  }
16375  else
16376  {
16377  mColorBuffer[i] = QColor::fromHsvF(hue,
16378  (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),
16379  (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
16380  }
16381  break;
16382  }
16383  }
16384  }
16385  }
16386  } else if (mColorStops.size() == 1)
16387  {
16388  const QRgb rgb = mColorStops.constBegin().value().rgb();
16389  const float alpha = mColorStops.constBegin().value().alphaF();
16390  mColorBuffer.fill(qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha));
16391  } else // mColorStops is empty, fill color buffer with black
16392  {
16393  mColorBuffer.fill(qRgb(0, 0, 0));
16394  }
16395  mColorBufferInvalidated = false;
16396 }
16397 /* end of 'src/colorgradient.cpp' */
16398 
16399 
16400 /* including file 'src/selectiondecorator-bracket.cpp', size 12313 */
16401 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
16402 
16406 
16427  mBracketPen(QPen(Qt::black)),
16428  mBracketBrush(Qt::NoBrush),
16429  mBracketWidth(5),
16430  mBracketHeight(50),
16431  mBracketStyle(bsSquareBracket),
16432  mTangentToData(false),
16433  mTangentAverage(2)
16434 {
16435 
16436 }
16437 
16439 {
16440 }
16441 
16447 {
16448  mBracketPen = pen;
16449 }
16450 
16456 {
16457  mBracketBrush = brush;
16458 }
16459 
16466 {
16467  mBracketWidth = width;
16468 }
16469 
16476 {
16477  mBracketHeight = height;
16478 }
16479 
16486 {
16487  mBracketStyle = style;
16488 }
16489 
16498 {
16499  mTangentToData = enabled;
16500 }
16501 
16511 {
16512  mTangentAverage = pointCount;
16513  if (mTangentAverage < 1)
16514  mTangentAverage = 1;
16515 }
16516 
16530 void QCPSelectionDecoratorBracket::drawBracket(QCPPainter *painter, int direction) const
16531 {
16532  switch (mBracketStyle)
16533  {
16534  case bsSquareBracket:
16535  {
16536  painter->drawLine(QLineF(mBracketWidth*direction, -mBracketHeight*0.5, 0, -mBracketHeight*0.5));
16537  painter->drawLine(QLineF(mBracketWidth*direction, mBracketHeight*0.5, 0, mBracketHeight*0.5));
16538  painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));
16539  break;
16540  }
16541  case bsHalfEllipse:
16542  {
16543  painter->drawArc(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight, -90*16, -180*16*direction);
16544  break;
16545  }
16546  case bsEllipse:
16547  {
16548  painter->drawEllipse(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight);
16549  break;
16550  }
16551  case bsPlus:
16552  {
16553  painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));
16554  painter->drawLine(QLineF(-mBracketWidth*0.5, 0, mBracketWidth*0.5, 0));
16555  break;
16556  }
16557  default:
16558  {
16559  qDebug() << Q_FUNC_INFO << "unknown/custom bracket style can't be handeld by default implementation:" << static_cast<int>(mBracketStyle);
16560  break;
16561  }
16562  }
16563 }
16564 
16574 {
16575  if (!mPlottable || selection.isEmpty()) return;
16576 
16577  if (QCPPlottableInterface1D *interface1d = mPlottable->interface1D())
16578  {
16579  foreach (const QCPDataRange &dataRange, selection.dataRanges())
16580  {
16581  // determine position and (if tangent mode is enabled) angle of brackets:
16582  int openBracketDir = (mPlottable->keyAxis() && !mPlottable->keyAxis()->rangeReversed()) ? 1 : -1;
16583  int closeBracketDir = -openBracketDir;
16584  QPointF openBracketPos = getPixelCoordinates(interface1d, dataRange.begin());
16585  QPointF closeBracketPos = getPixelCoordinates(interface1d, dataRange.end()-1);
16586  double openBracketAngle = 0;
16587  double closeBracketAngle = 0;
16588  if (mTangentToData)
16589  {
16590  openBracketAngle = getTangentAngle(interface1d, dataRange.begin(), openBracketDir);
16591  closeBracketAngle = getTangentAngle(interface1d, dataRange.end()-1, closeBracketDir);
16592  }
16593  // draw opening bracket:
16594  QTransform oldTransform = painter->transform();
16595  painter->setPen(mBracketPen);
16596  painter->setBrush(mBracketBrush);
16597  painter->translate(openBracketPos);
16598  painter->rotate(openBracketAngle/M_PI*180.0);
16599  drawBracket(painter, openBracketDir);
16600  painter->setTransform(oldTransform);
16601  // draw closing bracket:
16602  painter->setPen(mBracketPen);
16603  painter->setBrush(mBracketBrush);
16604  painter->translate(closeBracketPos);
16605  painter->rotate(closeBracketAngle/M_PI*180.0);
16606  drawBracket(painter, closeBracketDir);
16607  painter->setTransform(oldTransform);
16608  }
16609  }
16610 }
16611 
16625 double QCPSelectionDecoratorBracket::getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const
16626 {
16627  if (!interface1d || dataIndex < 0 || dataIndex >= interface1d->dataCount())
16628  return 0;
16629  direction = direction < 0 ? -1 : 1; // enforce direction is either -1 or 1
16630 
16631  // how many steps we can actually go from index in the given direction without exceeding data bounds:
16632  int averageCount;
16633  if (direction < 0)
16634  averageCount = qMin(mTangentAverage, dataIndex);
16635  else
16636  averageCount = qMin(mTangentAverage, interface1d->dataCount()-1-dataIndex);
16637  qDebug() << averageCount;
16638  // calculate point average of averageCount points:
16639  QVector<QPointF> points(averageCount);
16640  QPointF pointsAverage;
16641  int currentIndex = dataIndex;
16642  for (int i=0; i<averageCount; ++i)
16643  {
16644  points[i] = getPixelCoordinates(interface1d, currentIndex);
16645  pointsAverage += points[i];
16646  currentIndex += direction;
16647  }
16648  pointsAverage /= (double)averageCount;
16649 
16650  // calculate slope of linear regression through points:
16651  double numSum = 0;
16652  double denomSum = 0;
16653  for (int i=0; i<averageCount; ++i)
16654  {
16655  const double dx = points.at(i).x()-pointsAverage.x();
16656  const double dy = points.at(i).y()-pointsAverage.y();
16657  numSum += dx*dy;
16658  denomSum += dx*dx;
16659  }
16660  if (!qFuzzyIsNull(denomSum) && !qFuzzyIsNull(numSum))
16661  {
16662  return qAtan2(numSum, denomSum);
16663  } else // undetermined angle, probably mTangentAverage == 1, so using only one data point
16664  return 0;
16665 }
16666 
16672 QPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const
16673 {
16674  QCPAxis *keyAxis = mPlottable->keyAxis();
16675  QCPAxis *valueAxis = mPlottable->valueAxis();
16676  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(0, 0); }
16677 
16678  if (keyAxis->orientation() == Qt::Horizontal)
16679  return QPointF(keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)));
16680  else
16681  return QPointF(valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)));
16682 }
16683 /* end of 'src/selectiondecorator-bracket.cpp' */
16684 
16685 
16686 /* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47584 */
16687 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
16688 
16689 
16693 
16731 /* start documentation of inline functions */
16732 
16813 /* end documentation of inline functions */
16814 
16819 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
16820  QCPLayoutElement(parentPlot),
16821  mBackgroundBrush(Qt::NoBrush),
16822  mBackgroundScaled(true),
16823  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
16824  mInsetLayout(new QCPLayoutInset),
16825  mRangeDrag(Qt::Horizontal|Qt::Vertical),
16826  mRangeZoom(Qt::Horizontal|Qt::Vertical),
16827  mRangeZoomFactorHorz(0.85),
16828  mRangeZoomFactorVert(0.85),
16829  mDragging(false)
16830 {
16833  mInsetLayout->setParent(this);
16834 
16835  setMinimumSize(50, 50);
16836  setMinimumMargins(QMargins(15, 15, 15, 15));
16837  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
16838  mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
16839  mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
16840  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
16841 
16842  if (setupDefaultAxes)
16843  {
16844  QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
16845  QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
16846  QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
16847  QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
16848  setRangeDragAxes(xAxis, yAxis);
16849  setRangeZoomAxes(xAxis, yAxis);
16850  xAxis2->setVisible(false);
16851  yAxis2->setVisible(false);
16852  xAxis->grid()->setVisible(true);
16853  yAxis->grid()->setVisible(true);
16854  xAxis2->grid()->setVisible(false);
16855  yAxis2->grid()->setVisible(false);
16856  xAxis2->grid()->setZeroLinePen(Qt::NoPen);
16857  yAxis2->grid()->setZeroLinePen(Qt::NoPen);
16858  xAxis2->grid()->setVisible(false);
16859  yAxis2->grid()->setVisible(false);
16860  }
16861 }
16862 
16864 {
16865  delete mInsetLayout;
16866  mInsetLayout = 0;
16867 
16868  QList<QCPAxis*> axesList = axes();
16869  for (int i=0; i<axesList.size(); ++i)
16870  removeAxis(axesList.at(i));
16871 }
16872 
16879 {
16880  return mAxes.value(type).size();
16881 }
16882 
16889 {
16890  QList<QCPAxis*> ax(mAxes.value(type));
16891  if (index >= 0 && index < ax.size())
16892  {
16893  return ax.at(index);
16894  } else
16895  {
16896  qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
16897  return 0;
16898  }
16899 }
16900 
16909 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
16910 {
16911  QList<QCPAxis*> result;
16912  if (types.testFlag(QCPAxis::atLeft))
16913  result << mAxes.value(QCPAxis::atLeft);
16914  if (types.testFlag(QCPAxis::atRight))
16915  result << mAxes.value(QCPAxis::atRight);
16916  if (types.testFlag(QCPAxis::atTop))
16917  result << mAxes.value(QCPAxis::atTop);
16918  if (types.testFlag(QCPAxis::atBottom))
16919  result << mAxes.value(QCPAxis::atBottom);
16920  return result;
16921 }
16922 
16927 QList<QCPAxis*> QCPAxisRect::axes() const
16928 {
16929  QList<QCPAxis*> result;
16930  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
16931  while (it.hasNext())
16932  {
16933  it.next();
16934  result << it.value();
16935  }
16936  return result;
16937 }
16938 
16960 {
16961  QCPAxis *newAxis = axis;
16962  if (!newAxis)
16963  {
16964  newAxis = new QCPAxis(this, type);
16965  } else // user provided existing axis instance, do some sanity checks
16966  {
16967  if (newAxis->axisType() != type)
16968  {
16969  qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
16970  return 0;
16971  }
16972  if (newAxis->axisRect() != this)
16973  {
16974  qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
16975  return 0;
16976  }
16977  if (axes().contains(newAxis))
16978  {
16979  qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
16980  return 0;
16981  }
16982  }
16983  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
16984  {
16985  bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
16986  newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
16987  newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
16988  }
16989  mAxes[type].append(newAxis);
16990 
16991  // reset convenience axis pointers on parent QCustomPlot if they are unset:
16992  if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)
16993  {
16994  switch (type)
16995  {
16996  case QCPAxis::atBottom: { if (!mParentPlot->xAxis) mParentPlot->xAxis = newAxis; break; }
16997  case QCPAxis::atLeft: { if (!mParentPlot->yAxis) mParentPlot->yAxis = newAxis; break; }
16998  case QCPAxis::atTop: { if (!mParentPlot->xAxis2) mParentPlot->xAxis2 = newAxis; break; }
16999  case QCPAxis::atRight: { if (!mParentPlot->yAxis2) mParentPlot->yAxis2 = newAxis; break; }
17000  }
17001  }
17002 
17003  return newAxis;
17004 }
17005 
17014 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
17015 {
17016  QList<QCPAxis*> result;
17017  if (types.testFlag(QCPAxis::atLeft))
17018  result << addAxis(QCPAxis::atLeft);
17019  if (types.testFlag(QCPAxis::atRight))
17020  result << addAxis(QCPAxis::atRight);
17021  if (types.testFlag(QCPAxis::atTop))
17022  result << addAxis(QCPAxis::atTop);
17023  if (types.testFlag(QCPAxis::atBottom))
17024  result << addAxis(QCPAxis::atBottom);
17025  return result;
17026 }
17027 
17036 {
17037  // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
17038  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
17039  while (it.hasNext())
17040  {
17041  it.next();
17042  if (it.value().contains(axis))
17043  {
17044  if (it.value().first() == axis && it.value().size() > 1) // if removing first axis, transfer axis offset to the new first axis (which at this point is the second axis, if it exists)
17045  it.value()[1]->setOffset(axis->offset());
17046  mAxes[it.key()].removeOne(axis);
17047  if (qobject_cast<QCustomPlot*>(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot)
17048  parentPlot()->axisRemoved(axis);
17049  delete axis;
17050  return true;
17051  }
17052  }
17053  qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
17054  return false;
17055 }
17056 
17065 void QCPAxisRect::zoom(const QRectF &pixelRect)
17066 {
17067  zoom(pixelRect, axes());
17068 }
17069 
17078 void QCPAxisRect::zoom(const QRectF &pixelRect, const QList<QCPAxis*> &affectedAxes)
17079 {
17080  foreach (QCPAxis *axis, affectedAxes)
17081  {
17082  if (!axis)
17083  {
17084  qDebug() << Q_FUNC_INFO << "a passed axis was zero";
17085  continue;
17086  }
17087  QCPRange pixelRange;
17088  if (axis->orientation() == Qt::Horizontal)
17089  pixelRange = QCPRange(pixelRect.left(), pixelRect.right());
17090  else
17091  pixelRange = QCPRange(pixelRect.top(), pixelRect.bottom());
17092  axis->setRange(axis->pixelToCoord(pixelRange.lower), axis->pixelToCoord(pixelRange.upper));
17093  }
17094 }
17095 
17115 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
17116 {
17117  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
17118  if (axisCount(QCPAxis::atBottom) == 0)
17119  xAxis = addAxis(QCPAxis::atBottom);
17120  else
17121  xAxis = axis(QCPAxis::atBottom);
17122 
17123  if (axisCount(QCPAxis::atLeft) == 0)
17124  yAxis = addAxis(QCPAxis::atLeft);
17125  else
17126  yAxis = axis(QCPAxis::atLeft);
17127 
17128  if (axisCount(QCPAxis::atTop) == 0)
17129  xAxis2 = addAxis(QCPAxis::atTop);
17130  else
17131  xAxis2 = axis(QCPAxis::atTop);
17132 
17133  if (axisCount(QCPAxis::atRight) == 0)
17134  yAxis2 = addAxis(QCPAxis::atRight);
17135  else
17136  yAxis2 = axis(QCPAxis::atRight);
17137 
17138  xAxis->setVisible(true);
17139  yAxis->setVisible(true);
17140  xAxis2->setVisible(true);
17141  yAxis2->setVisible(true);
17142  xAxis2->setTickLabels(false);
17143  yAxis2->setTickLabels(false);
17144 
17145  xAxis2->setRange(xAxis->range());
17146  xAxis2->setRangeReversed(xAxis->rangeReversed());
17147  xAxis2->setScaleType(xAxis->scaleType());
17148  xAxis2->setTicks(xAxis->ticks());
17149  xAxis2->setNumberFormat(xAxis->numberFormat());
17150  xAxis2->setNumberPrecision(xAxis->numberPrecision());
17151  xAxis2->ticker()->setTickCount(xAxis->ticker()->tickCount());
17152  xAxis2->ticker()->setTickOrigin(xAxis->ticker()->tickOrigin());
17153 
17154  yAxis2->setRange(yAxis->range());
17155  yAxis2->setRangeReversed(yAxis->rangeReversed());
17156  yAxis2->setScaleType(yAxis->scaleType());
17157  yAxis2->setTicks(yAxis->ticks());
17158  yAxis2->setNumberFormat(yAxis->numberFormat());
17159  yAxis2->setNumberPrecision(yAxis->numberPrecision());
17160  yAxis2->ticker()->setTickCount(yAxis->ticker()->tickCount());
17161  yAxis2->ticker()->setTickOrigin(yAxis->ticker()->tickOrigin());
17162 
17163  if (connectRanges)
17164  {
17165  connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
17166  connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
17167  }
17168 }
17169 
17178 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
17179 {
17180  // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
17181  QList<QCPAbstractPlottable*> result;
17182  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
17183  {
17184  if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this || mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
17185  result.append(mParentPlot->mPlottables.at(i));
17186  }
17187  return result;
17188 }
17189 
17198 QList<QCPGraph*> QCPAxisRect::graphs() const
17199 {
17200  // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
17201  QList<QCPGraph*> result;
17202  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
17203  {
17204  if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
17205  result.append(mParentPlot->mGraphs.at(i));
17206  }
17207  return result;
17208 }
17209 
17220 QList<QCPAbstractItem *> QCPAxisRect::items() const
17221 {
17222  // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
17223  // and miss those items that have this axis rect as clipAxisRect.
17224  QList<QCPAbstractItem*> result;
17225  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
17226  {
17227  if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
17228  {
17229  result.append(mParentPlot->mItems.at(itemId));
17230  continue;
17231  }
17232  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
17233  for (int posId=0; posId<positions.size(); ++posId)
17234  {
17235  if (positions.at(posId)->axisRect() == this ||
17236  positions.at(posId)->keyAxis()->axisRect() == this ||
17237  positions.at(posId)->valueAxis()->axisRect() == this)
17238  {
17239  result.append(mParentPlot->mItems.at(itemId));
17240  break;
17241  }
17242  }
17243  }
17244  return result;
17245 }
17246 
17258 {
17259  QCPLayoutElement::update(phase);
17260 
17261  switch (phase)
17262  {
17263  case upPreparation:
17264  {
17265  QList<QCPAxis*> allAxes = axes();
17266  for (int i=0; i<allAxes.size(); ++i)
17267  allAxes.at(i)->setupTickVectors();
17268  break;
17269  }
17270  case upLayout:
17271  {
17273  break;
17274  }
17275  default: break;
17276  }
17277 
17278  // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
17279  mInsetLayout->update(phase);
17280 }
17281 
17282 /* inherits documentation from base class */
17283 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
17284 {
17285  QList<QCPLayoutElement*> result;
17286  if (mInsetLayout)
17287  {
17288  result << mInsetLayout;
17289  if (recursive)
17290  result << mInsetLayout->elements(recursive);
17291  }
17292  return result;
17293 }
17294 
17295 /* inherits documentation from base class */
17297 {
17298  painter->setAntialiasing(false);
17299 }
17300 
17301 /* inherits documentation from base class */
17303 {
17304  drawBackground(painter);
17305 }
17306 
17322 void QCPAxisRect::setBackground(const QPixmap &pm)
17323 {
17324  mBackgroundPixmap = pm;
17325  mScaledBackgroundPixmap = QPixmap();
17326 }
17327 
17341 void QCPAxisRect::setBackground(const QBrush &brush)
17342 {
17343  mBackgroundBrush = brush;
17344 }
17345 
17353 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
17354 {
17355  mBackgroundPixmap = pm;
17356  mScaledBackgroundPixmap = QPixmap();
17357  mBackgroundScaled = scaled;
17358  mBackgroundScaledMode = mode;
17359 }
17360 
17372 {
17373  mBackgroundScaled = scaled;
17374 }
17375 
17381 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
17382 {
17383  mBackgroundScaledMode = mode;
17384 }
17385 
17392 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
17393 {
17394  if (orientation == Qt::Horizontal)
17395  return mRangeDragHorzAxis.isEmpty() ? 0 : mRangeDragHorzAxis.first().data();
17396  else
17397  return mRangeDragVertAxis.isEmpty() ? 0 : mRangeDragVertAxis.first().data();
17398 }
17399 
17406 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
17407 {
17408  if (orientation == Qt::Horizontal)
17409  return mRangeZoomHorzAxis.isEmpty() ? 0 : mRangeZoomHorzAxis.first().data();
17410  else
17411  return mRangeZoomVertAxis.isEmpty() ? 0 : mRangeZoomVertAxis.first().data();
17412 }
17413 
17419 QList<QCPAxis*> QCPAxisRect::rangeDragAxes(Qt::Orientation orientation)
17420 {
17421  QList<QCPAxis*> result;
17422  if (orientation == Qt::Horizontal)
17423  {
17424  for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
17425  {
17426  if (!mRangeDragHorzAxis.at(i).isNull())
17427  result.append(mRangeDragHorzAxis.at(i).data());
17428  }
17429  } else
17430  {
17431  for (int i=0; i<mRangeDragVertAxis.size(); ++i)
17432  {
17433  if (!mRangeDragVertAxis.at(i).isNull())
17434  result.append(mRangeDragVertAxis.at(i).data());
17435  }
17436  }
17437  return result;
17438 }
17439 
17445 QList<QCPAxis*> QCPAxisRect::rangeZoomAxes(Qt::Orientation orientation)
17446 {
17447  QList<QCPAxis*> result;
17448  if (orientation == Qt::Horizontal)
17449  {
17450  for (int i=0; i<mRangeZoomHorzAxis.size(); ++i)
17451  {
17452  if (!mRangeZoomHorzAxis.at(i).isNull())
17453  result.append(mRangeZoomHorzAxis.at(i).data());
17454  }
17455  } else
17456  {
17457  for (int i=0; i<mRangeZoomVertAxis.size(); ++i)
17458  {
17459  if (!mRangeZoomVertAxis.at(i).isNull())
17460  result.append(mRangeZoomVertAxis.at(i).data());
17461  }
17462  }
17463  return result;
17464 }
17465 
17471 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
17472 {
17473  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
17474 }
17475 
17492 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
17493 {
17494  mRangeDrag = orientations;
17495 }
17496 
17512 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
17513 {
17514  mRangeZoom = orientations;
17515 }
17516 
17527 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
17528 {
17529  QList<QCPAxis*> horz, vert;
17530  if (horizontal)
17531  horz.append(horizontal);
17532  if (vertical)
17533  vert.append(vertical);
17534  setRangeDragAxes(horz, vert);
17535 }
17536 
17547 {
17548  QList<QCPAxis*> horz, vert;
17549  foreach (QCPAxis *ax, axes)
17550  {
17551  if (ax->orientation() == Qt::Horizontal)
17552  horz.append(ax);
17553  else
17554  vert.append(ax);
17555  }
17556  setRangeDragAxes(horz, vert);
17557 }
17558 
17565 void QCPAxisRect::setRangeDragAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)
17566 {
17567  mRangeDragHorzAxis.clear();
17568  foreach (QCPAxis *ax, horizontal)
17569  {
17570  QPointer<QCPAxis> axPointer(ax);
17571  if (!axPointer.isNull())
17572  mRangeDragHorzAxis.append(axPointer);
17573  else
17574  qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast<quintptr>(ax);
17575  }
17576  mRangeDragVertAxis.clear();
17577  foreach (QCPAxis *ax, vertical)
17578  {
17579  QPointer<QCPAxis> axPointer(ax);
17580  if (!axPointer.isNull())
17581  mRangeDragVertAxis.append(axPointer);
17582  else
17583  qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast<quintptr>(ax);
17584  }
17585 }
17586 
17599 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
17600 {
17601  QList<QCPAxis*> horz, vert;
17602  if (horizontal)
17603  horz.append(horizontal);
17604  if (vertical)
17605  vert.append(vertical);
17606  setRangeZoomAxes(horz, vert);
17607 }
17608 
17619 {
17620  QList<QCPAxis*> horz, vert;
17621  foreach (QCPAxis *ax, axes)
17622  {
17623  if (ax->orientation() == Qt::Horizontal)
17624  horz.append(ax);
17625  else
17626  vert.append(ax);
17627  }
17628  setRangeZoomAxes(horz, vert);
17629 }
17630 
17637 void QCPAxisRect::setRangeZoomAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)
17638 {
17639  mRangeZoomHorzAxis.clear();
17640  foreach (QCPAxis *ax, horizontal)
17641  {
17642  QPointer<QCPAxis> axPointer(ax);
17643  if (!axPointer.isNull())
17644  mRangeZoomHorzAxis.append(axPointer);
17645  else
17646  qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast<quintptr>(ax);
17647  }
17648  mRangeZoomVertAxis.clear();
17649  foreach (QCPAxis *ax, vertical)
17650  {
17651  QPointer<QCPAxis> axPointer(ax);
17652  if (!axPointer.isNull())
17653  mRangeZoomVertAxis.append(axPointer);
17654  else
17655  qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast<quintptr>(ax);
17656  }
17657 }
17658 
17669 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
17670 {
17671  mRangeZoomFactorHorz = horizontalFactor;
17672  mRangeZoomFactorVert = verticalFactor;
17673 }
17674 
17680 {
17681  mRangeZoomFactorHorz = factor;
17682  mRangeZoomFactorVert = factor;
17683 }
17684 
17704 {
17705  // draw background fill:
17706  if (mBackgroundBrush != Qt::NoBrush)
17707  painter->fillRect(mRect, mBackgroundBrush);
17708 
17709  // draw background pixmap (on top of fill, if brush specified):
17710  if (!mBackgroundPixmap.isNull())
17711  {
17712  if (mBackgroundScaled)
17713  {
17714  // check whether mScaledBackground needs to be updated:
17715  QSize scaledSize(mBackgroundPixmap.size());
17716  scaledSize.scale(mRect.size(), mBackgroundScaledMode);
17717  if (mScaledBackgroundPixmap.size() != scaledSize)
17718  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
17719  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
17720  } else
17721  {
17722  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
17723  }
17724  }
17725 }
17726 
17738 {
17739  const QList<QCPAxis*> axesList = mAxes.value(type);
17740  if (axesList.isEmpty())
17741  return;
17742 
17743  bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false
17744  for (int i=1; i<axesList.size(); ++i)
17745  {
17746  int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
17747  if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible)
17748  {
17749  if (!isFirstVisible)
17750  offset += axesList.at(i)->tickLengthIn();
17751  isFirstVisible = false;
17752  }
17753  axesList.at(i)->setOffset(offset);
17754  }
17755 }
17756 
17757 /* inherits documentation from base class */
17759 {
17760  if (!mAutoMargins.testFlag(side))
17761  qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
17762 
17764 
17765  // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
17766  const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
17767  if (axesList.size() > 0)
17768  return axesList.last()->offset() + axesList.last()->calculateMargin();
17769  else
17770  return 0;
17771 }
17772 
17784 {
17785  if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)
17786  {
17795  }
17796 }
17797 
17809 void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)
17810 {
17811  Q_UNUSED(details)
17812  if (event->buttons() & Qt::LeftButton)
17813  {
17814  mDragging = true;
17815  // initialize antialiasing backup in case we start dragging:
17817  {
17820  }
17821  // Mouse range dragging interaction:
17822  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
17823  {
17824  mDragStartHorzRange.clear();
17825  for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
17826  mDragStartHorzRange.append(mRangeDragHorzAxis.at(i).isNull() ? QCPRange() : mRangeDragHorzAxis.at(i)->range());
17827  mDragStartVertRange.clear();
17828  for (int i=0; i<mRangeDragVertAxis.size(); ++i)
17829  mDragStartVertRange.append(mRangeDragVertAxis.at(i).isNull() ? QCPRange() : mRangeDragVertAxis.at(i)->range());
17830  }
17831  }
17832 }
17833 
17841 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
17842 {
17843  Q_UNUSED(startPos)
17844  // Mouse range dragging interaction:
17845  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
17846  {
17847 
17848  if (mRangeDrag.testFlag(Qt::Horizontal))
17849  {
17850  for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
17851  {
17852  QCPAxis *ax = mRangeDragHorzAxis.at(i).data();
17853  if (!ax)
17854  continue;
17855  if (i >= mDragStartHorzRange.size())
17856  break;
17857  if (ax->mScaleType == QCPAxis::stLinear)
17858  {
17859  double diff = ax->pixelToCoord(startPos.x()) - ax->pixelToCoord(event->pos().x());
17860  ax->setRange(mDragStartHorzRange.at(i).lower+diff, mDragStartHorzRange.at(i).upper+diff);
17861  } else if (ax->mScaleType == QCPAxis::stLogarithmic)
17862  {
17863  double diff = ax->pixelToCoord(startPos.x()) / ax->pixelToCoord(event->pos().x());
17864  ax->setRange(mDragStartHorzRange.at(i).lower*diff, mDragStartHorzRange.at(i).upper*diff);
17865  }
17866  }
17867  }
17868 
17869  if (mRangeDrag.testFlag(Qt::Vertical))
17870  {
17871  for (int i=0; i<mRangeDragVertAxis.size(); ++i)
17872  {
17873  QCPAxis *ax = mRangeDragVertAxis.at(i).data();
17874  if (!ax)
17875  continue;
17876  if (i >= mDragStartVertRange.size())
17877  break;
17878  if (ax->mScaleType == QCPAxis::stLinear)
17879  {
17880  double diff = ax->pixelToCoord(startPos.y()) - ax->pixelToCoord(event->pos().y());
17881  ax->setRange(mDragStartVertRange.at(i).lower+diff, mDragStartVertRange.at(i).upper+diff);
17882  } else if (ax->mScaleType == QCPAxis::stLogarithmic)
17883  {
17884  double diff = ax->pixelToCoord(startPos.y()) / ax->pixelToCoord(event->pos().y());
17885  ax->setRange(mDragStartVertRange.at(i).lower*diff, mDragStartVertRange.at(i).upper*diff);
17886  }
17887  }
17888  }
17889 
17890  if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
17891  {
17895  }
17896 
17897  }
17898 }
17899 
17900 /* inherits documentation from base class */
17901 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
17902 {
17903  Q_UNUSED(event)
17904  Q_UNUSED(startPos)
17905  mDragging = false;
17907  {
17910  }
17911 }
17912 
17927 void QCPAxisRect::wheelEvent(QWheelEvent *event)
17928 {
17929  // Mouse range zooming interaction:
17930  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
17931  {
17932  if (mRangeZoom != 0)
17933  {
17934  double factor;
17935  double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
17936  if (mRangeZoom.testFlag(Qt::Horizontal))
17937  {
17938  factor = qPow(mRangeZoomFactorHorz, wheelSteps);
17939  for (int i=0; i<mRangeZoomHorzAxis.size(); ++i)
17940  {
17941  if (!mRangeZoomHorzAxis.at(i).isNull())
17942  mRangeZoomHorzAxis.at(i)->scaleRange(factor, mRangeZoomHorzAxis.at(i)->pixelToCoord(event->pos().x()));
17943  }
17944  }
17945  if (mRangeZoom.testFlag(Qt::Vertical))
17946  {
17947  factor = qPow(mRangeZoomFactorVert, wheelSteps);
17948  for (int i=0; i<mRangeZoomVertAxis.size(); ++i)
17949  {
17950  if (!mRangeZoomVertAxis.at(i).isNull())
17951  mRangeZoomVertAxis.at(i)->scaleRange(factor, mRangeZoomVertAxis.at(i)->pixelToCoord(event->pos().y()));
17952  }
17953  }
17954  mParentPlot->replot();
17955  }
17956  }
17957 }
17958 /* end of 'src/layoutelements/layoutelement-axisrect.cpp' */
17959 
17960 
17961 /* including file 'src/layoutelements/layoutelement-legend.cpp', size 31153 */
17962 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
17963 
17967 
17992 /* start of documentation of signals */
17993 
18000 /* end of documentation of signals */
18001 
18007  QCPLayoutElement(parent->parentPlot()),
18008  mParentLegend(parent),
18009  mFont(parent->font()),
18010  mTextColor(parent->textColor()),
18011  mSelectedFont(parent->selectedFont()),
18012  mSelectedTextColor(parent->selectedTextColor()),
18013  mSelectable(true),
18014  mSelected(false)
18015 {
18016  setLayer(QLatin1String("legend"));
18017  setMargins(QMargins(0, 0, 0, 0));
18018 }
18019 
18026 {
18027  mFont = font;
18028 }
18029 
18035 void QCPAbstractLegendItem::setTextColor(const QColor &color)
18036 {
18037  mTextColor = color;
18038 }
18039 
18047 {
18048  mSelectedFont = font;
18049 }
18050 
18058 {
18059  mSelectedTextColor = color;
18060 }
18061 
18068 {
18069  if (mSelectable != selectable)
18070  {
18073  }
18074 }
18075 
18085 {
18086  if (mSelected != selected)
18087  {
18088  mSelected = selected;
18090  }
18091 }
18092 
18093 /* inherits documentation from base class */
18094 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18095 {
18096  Q_UNUSED(details)
18097  if (!mParentPlot) return -1;
18098  if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
18099  return -1;
18100 
18101  if (mRect.contains(pos.toPoint()))
18102  return mParentPlot->selectionTolerance()*0.99;
18103  else
18104  return -1;
18105 }
18106 
18107 /* inherits documentation from base class */
18109 {
18111 }
18112 
18113 /* inherits documentation from base class */
18115 {
18116  return mOuterRect;
18117 }
18118 
18119 /* inherits documentation from base class */
18120 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
18121 {
18122  Q_UNUSED(event)
18123  Q_UNUSED(details)
18125  {
18126  bool selBefore = mSelected;
18127  setSelected(additive ? !mSelected : true);
18128  if (selectionStateChanged)
18129  *selectionStateChanged = mSelected != selBefore;
18130  }
18131 }
18132 
18133 /* inherits documentation from base class */
18134 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
18135 {
18137  {
18138  bool selBefore = mSelected;
18139  setSelected(false);
18140  if (selectionStateChanged)
18141  *selectionStateChanged = mSelected != selBefore;
18142  }
18143 }
18144 
18148 
18181  QCPAbstractLegendItem(parent),
18182  mPlottable(plottable)
18183 {
18184  setAntialiased(false);
18185 }
18186 
18193 {
18195 }
18196 
18203 {
18205 }
18206 
18213 {
18214  return mSelected ? mSelectedFont : mFont;
18215 }
18216 
18224 {
18225  if (!mPlottable) return;
18226  painter->setFont(getFont());
18227  painter->setPen(QPen(getTextColor()));
18228  QSizeF iconSize = mParentLegend->iconSize();
18229  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
18230  QRectF iconRect(mRect.topLeft(), iconSize);
18231  int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
18232  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
18233  // draw icon:
18234  painter->save();
18235  painter->setClipRect(iconRect, Qt::IntersectClip);
18236  mPlottable->drawLegendIcon(painter, iconRect);
18237  painter->restore();
18238  // draw icon border:
18239  if (getIconBorderPen().style() != Qt::NoPen)
18240  {
18241  painter->setPen(getIconBorderPen());
18242  painter->setBrush(Qt::NoBrush);
18243  int halfPen = qCeil(painter->pen().widthF()*0.5)+1;
18244  painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen)); // extend default clip rect so thicker pens (especially during selection) are not clipped
18245  painter->drawRect(iconRect);
18246  }
18247 }
18248 
18257 {
18258  if (!mPlottable) return QSize();
18259  QSize result(0, 0);
18260  QRect textRect;
18261  QFontMetrics fontMetrics(getFont());
18262  QSize iconSize = mParentLegend->iconSize();
18263  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
18264  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width());
18265  result.setHeight(qMax(textRect.height(), iconSize.height()));
18266  result.rwidth() += mMargins.left()+mMargins.right();
18267  result.rheight() += mMargins.top()+mMargins.bottom();
18268  return result;
18269 }
18270 
18271 
18275 
18310 /* start of documentation of signals */
18311 
18319 /* end of documentation of signals */
18320 
18328 {
18329  setFillOrder(QCPLayoutGrid::foRowsFirst);
18330  setWrap(0);
18331 
18332  setRowSpacing(3);
18333  setColumnSpacing(8);
18334  setMargins(QMargins(7, 5, 7, 4));
18335  setAntialiased(false);
18336  setIconSize(32, 18);
18337 
18338  setIconTextPadding(7);
18339 
18340  setSelectableParts(spLegendBox | spItems);
18341  setSelectedParts(spNone);
18342 
18343  setBorderPen(QPen(Qt::black, 0));
18344  setSelectedBorderPen(QPen(Qt::blue, 2));
18345  setIconBorderPen(Qt::NoPen);
18346  setSelectedIconBorderPen(QPen(Qt::blue, 2));
18347  setBrush(Qt::white);
18348  setSelectedBrush(Qt::white);
18349  setTextColor(Qt::black);
18350  setSelectedTextColor(Qt::blue);
18351 }
18352 
18354 {
18355  clearItems();
18356  if (qobject_cast<QCustomPlot*>(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot)
18357  mParentPlot->legendRemoved(this);
18358 }
18359 
18360 /* no doc for getter, see setSelectedParts */
18361 QCPLegend::SelectableParts QCPLegend::selectedParts() const
18362 {
18363  // check whether any legend elements selected, if yes, add spItems to return value
18364  bool hasSelectedItems = false;
18365  for (int i=0; i<itemCount(); ++i)
18366  {
18367  if (item(i) && item(i)->selected())
18368  {
18369  hasSelectedItems = true;
18370  break;
18371  }
18372  }
18373  if (hasSelectedItems)
18374  return mSelectedParts | spItems;
18375  else
18376  return mSelectedParts & ~spItems;
18377 }
18378 
18382 void QCPLegend::setBorderPen(const QPen &pen)
18383 {
18384  mBorderPen = pen;
18385 }
18386 
18390 void QCPLegend::setBrush(const QBrush &brush)
18391 {
18392  mBrush = brush;
18393 }
18394 
18404 void QCPLegend::setFont(const QFont &font)
18405 {
18406  mFont = font;
18407  for (int i=0; i<itemCount(); ++i)
18408  {
18409  if (item(i))
18410  item(i)->setFont(mFont);
18411  }
18412 }
18413 
18423 void QCPLegend::setTextColor(const QColor &color)
18424 {
18425  mTextColor = color;
18426  for (int i=0; i<itemCount(); ++i)
18427  {
18428  if (item(i))
18429  item(i)->setTextColor(color);
18430  }
18431 }
18432 
18437 void QCPLegend::setIconSize(const QSize &size)
18438 {
18439  mIconSize = size;
18440 }
18441 
18444 void QCPLegend::setIconSize(int width, int height)
18445 {
18446  mIconSize.setWidth(width);
18447  mIconSize.setHeight(height);
18448 }
18449 
18456 {
18457  mIconTextPadding = padding;
18458 }
18459 
18466 void QCPLegend::setIconBorderPen(const QPen &pen)
18467 {
18468  mIconBorderPen = pen;
18469 }
18470 
18481 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
18482 {
18483  if (mSelectableParts != selectable)
18484  {
18485  mSelectableParts = selectable;
18486  emit selectableChanged(mSelectableParts);
18487  }
18488 }
18489 
18511 void QCPLegend::setSelectedParts(const SelectableParts &selected)
18512 {
18513  SelectableParts newSelected = selected;
18514  mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
18515 
18516  if (mSelectedParts != newSelected)
18517  {
18518  if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
18519  {
18520  qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
18521  newSelected &= ~spItems;
18522  }
18523  if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
18524  {
18525  for (int i=0; i<itemCount(); ++i)
18526  {
18527  if (item(i))
18528  item(i)->setSelected(false);
18529  }
18530  }
18531  mSelectedParts = newSelected;
18532  emit selectionChanged(mSelectedParts);
18533  }
18534 }
18535 
18542 void QCPLegend::setSelectedBorderPen(const QPen &pen)
18543 {
18544  mSelectedBorderPen = pen;
18545 }
18546 
18553 {
18554  mSelectedIconBorderPen = pen;
18555 }
18556 
18563 void QCPLegend::setSelectedBrush(const QBrush &brush)
18564 {
18565  mSelectedBrush = brush;
18566 }
18567 
18576 {
18577  mSelectedFont = font;
18578  for (int i=0; i<itemCount(); ++i)
18579  {
18580  if (item(i))
18581  item(i)->setSelectedFont(font);
18582  }
18583 }
18584 
18592 void QCPLegend::setSelectedTextColor(const QColor &color)
18593 {
18594  mSelectedTextColor = color;
18595  for (int i=0; i<itemCount(); ++i)
18596  {
18597  if (item(i))
18598  item(i)->setSelectedTextColor(color);
18599  }
18600 }
18601 
18610 {
18611  return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
18612 }
18613 
18621 {
18622  for (int i=0; i<itemCount(); ++i)
18623  {
18624  if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
18625  {
18626  if (pli->plottable() == plottable)
18627  return pli;
18628  }
18629  }
18630  return 0;
18631 }
18632 
18642 {
18643  return elementCount();
18644 }
18645 
18652 {
18653  for (int i=0; i<itemCount(); ++i)
18654  {
18655  if (item == this->item(i))
18656  return true;
18657  }
18658  return false;
18659 }
18660 
18668 {
18669  return itemWithPlottable(plottable);
18670 }
18671 
18683 {
18684  return addElement(item);
18685 }
18686 
18700 bool QCPLegend::removeItem(int index)
18701 {
18702  if (QCPAbstractLegendItem *ali = item(index))
18703  {
18704  bool success = remove(ali);
18705  if (success)
18706  setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering
18707  return success;
18708  } else
18709  return false;
18710 }
18711 
18725 {
18726  bool success = remove(item);
18727  if (success)
18728  setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering
18729  return success;
18730 }
18731 
18736 {
18737  for (int i=itemCount()-1; i>=0; --i)
18738  removeItem(i);
18739 }
18740 
18747 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
18748 {
18749  QList<QCPAbstractLegendItem*> result;
18750  for (int i=0; i<itemCount(); ++i)
18751  {
18752  if (QCPAbstractLegendItem *ali = item(i))
18753  {
18754  if (ali->selected())
18755  result.append(ali);
18756  }
18757  }
18758  return result;
18759 }
18760 
18777 {
18779 }
18780 
18787 {
18788  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
18789 }
18790 
18796 QBrush QCPLegend::getBrush() const
18797 {
18798  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
18799 }
18800 
18807 {
18808  // draw background rect:
18809  painter->setBrush(getBrush());
18810  painter->setPen(getBorderPen());
18811  painter->drawRect(mOuterRect);
18812 }
18813 
18814 /* inherits documentation from base class */
18815 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18816 {
18817  if (!mParentPlot) return -1;
18818  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
18819  return -1;
18820 
18821  if (mOuterRect.contains(pos.toPoint()))
18822  {
18823  if (details) details->setValue(spLegendBox);
18824  return mParentPlot->selectionTolerance()*0.99;
18825  }
18826  return -1;
18827 }
18828 
18829 /* inherits documentation from base class */
18830 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
18831 {
18832  Q_UNUSED(event)
18833  mSelectedParts = selectedParts(); // in case item selection has changed
18834  if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
18835  {
18836  SelectableParts selBefore = mSelectedParts;
18837  setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent)
18838  if (selectionStateChanged)
18839  *selectionStateChanged = mSelectedParts != selBefore;
18840  }
18841 }
18842 
18843 /* inherits documentation from base class */
18844 void QCPLegend::deselectEvent(bool *selectionStateChanged)
18845 {
18846  mSelectedParts = selectedParts(); // in case item selection has changed
18847  if (mSelectableParts.testFlag(spLegendBox))
18848  {
18849  SelectableParts selBefore = mSelectedParts;
18850  setSelectedParts(selectedParts() & ~spLegendBox);
18851  if (selectionStateChanged)
18852  *selectionStateChanged = mSelectedParts != selBefore;
18853  }
18854 }
18855 
18856 /* inherits documentation from base class */
18858 {
18859  return QCP::iSelectLegend;
18860 }
18861 
18862 /* inherits documentation from base class */
18864 {
18865  return QCP::iSelectLegend;
18866 }
18867 
18868 /* inherits documentation from base class */
18870 {
18871  if (parentPlot && !parentPlot->legend)
18872  parentPlot->legend = this;
18873 }
18874 /* end of 'src/layoutelements/layoutelement-legend.cpp' */
18875 
18876 
18877 /* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12761 */
18878 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
18879 
18883 
18894 /* start documentation of signals */
18895 
18918 /* end documentation of signals */
18919 
18926  QCPLayoutElement(parentPlot),
18927  mText(),
18928  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18929  mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18930  mTextColor(Qt::black),
18931  mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18932  mSelectedTextColor(Qt::blue),
18933  mSelectable(false),
18934  mSelected(false)
18935 {
18936  if (parentPlot)
18937  {
18938  mFont = parentPlot->font();
18939  mSelectedFont = parentPlot->font();
18940  }
18941  setMargins(QMargins(2, 2, 2, 2));
18942 }
18943 
18951  QCPLayoutElement(parentPlot),
18952  mText(text),
18953  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18954  mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18955  mTextColor(Qt::black),
18956  mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18957  mSelectedTextColor(Qt::blue),
18958  mSelectable(false),
18959  mSelected(false)
18960 {
18961  if (parentPlot)
18962  {
18963  mFont = parentPlot->font();
18964  mSelectedFont = parentPlot->font();
18965  }
18966  setMargins(QMargins(2, 2, 2, 2));
18967 }
18968 
18975 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize) :
18976  QCPLayoutElement(parentPlot),
18977  mText(text),
18978  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18979  mFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below
18980  mTextColor(Qt::black),
18981  mSelectedFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below
18982  mSelectedTextColor(Qt::blue),
18983  mSelectable(false),
18984  mSelected(false)
18985 {
18986  if (parentPlot)
18987  {
18988  mFont = parentPlot->font();
18989  mFont.setPointSizeF(pointSize);
18990  mSelectedFont = parentPlot->font();
18991  mSelectedFont.setPointSizeF(pointSize);
18992  }
18993  setMargins(QMargins(2, 2, 2, 2));
18994 }
18995 
19002 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize) :
19003  QCPLayoutElement(parentPlot),
19004  mText(text),
19005  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
19006  mFont(QFont(fontFamily, pointSize)),
19007  mTextColor(Qt::black),
19008  mSelectedFont(QFont(fontFamily, pointSize)),
19009  mSelectedTextColor(Qt::blue),
19010  mSelectable(false),
19011  mSelected(false)
19012 {
19013  setMargins(QMargins(2, 2, 2, 2));
19014 }
19015 
19023  QCPLayoutElement(parentPlot),
19024  mText(text),
19025  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
19026  mFont(font),
19027  mTextColor(Qt::black),
19028  mSelectedFont(font),
19029  mSelectedTextColor(Qt::blue),
19030  mSelectable(false),
19031  mSelected(false)
19032 {
19033  setMargins(QMargins(2, 2, 2, 2));
19034 }
19035 
19041 void QCPTextElement::setText(const QString &text)
19042 {
19043  mText = text;
19044 }
19045 
19067 {
19068  mTextFlags = flags;
19069 }
19070 
19076 void QCPTextElement::setFont(const QFont &font)
19077 {
19078  mFont = font;
19079 }
19080 
19086 void QCPTextElement::setTextColor(const QColor &color)
19087 {
19088  mTextColor = color;
19089 }
19090 
19097 {
19098  mSelectedFont = font;
19099 }
19100 
19106 void QCPTextElement::setSelectedTextColor(const QColor &color)
19107 {
19108  mSelectedTextColor = color;
19109 }
19110 
19118 {
19119  if (mSelectable != selectable)
19120  {
19123  }
19124 }
19125 
19134 {
19135  if (mSelected != selected)
19136  {
19137  mSelected = selected;
19139  }
19140 }
19141 
19142 /* inherits documentation from base class */
19144 {
19146 }
19147 
19148 /* inherits documentation from base class */
19150 {
19151  painter->setFont(mainFont());
19152  painter->setPen(QPen(mainTextColor()));
19153  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
19154 }
19155 
19156 /* inherits documentation from base class */
19158 {
19159  QFontMetrics metrics(mFont);
19160  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size());
19161  result.rwidth() += mMargins.left()+mMargins.right();
19162  result.rheight() += mMargins.top()+mMargins.bottom();
19163  return result;
19164 }
19165 
19166 /* inherits documentation from base class */
19168 {
19169  QFontMetrics metrics(mFont);
19170  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size());
19171  result.setWidth(QWIDGETSIZE_MAX);
19172  result.rheight() += mMargins.top()+mMargins.bottom();
19173  return result;
19174 }
19175 
19176 /* inherits documentation from base class */
19177 void QCPTextElement::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
19178 {
19179  Q_UNUSED(event)
19180  Q_UNUSED(details)
19181  if (mSelectable)
19182  {
19183  bool selBefore = mSelected;
19184  setSelected(additive ? !mSelected : true);
19185  if (selectionStateChanged)
19186  *selectionStateChanged = mSelected != selBefore;
19187  }
19188 }
19189 
19190 /* inherits documentation from base class */
19191 void QCPTextElement::deselectEvent(bool *selectionStateChanged)
19192 {
19193  if (mSelectable)
19194  {
19195  bool selBefore = mSelected;
19196  setSelected(false);
19197  if (selectionStateChanged)
19198  *selectionStateChanged = mSelected != selBefore;
19199  }
19200 }
19201 
19212 double QCPTextElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19213 {
19214  Q_UNUSED(details)
19215  if (onlySelectable && !mSelectable)
19216  return -1;
19217 
19218  if (mTextBoundingRect.contains(pos.toPoint()))
19219  return mParentPlot->selectionTolerance()*0.99;
19220  else
19221  return -1;
19222 }
19223 
19230 void QCPTextElement::mousePressEvent(QMouseEvent *event, const QVariant &details)
19231 {
19232  Q_UNUSED(details)
19233  event->accept();
19234 }
19235 
19242 void QCPTextElement::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
19243 {
19244  if ((QPointF(event->pos())-startPos).manhattanLength() <= 3)
19245  emit clicked(event);
19246 }
19247 
19253 void QCPTextElement::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
19254 {
19255  Q_UNUSED(details)
19256  emit doubleClicked(event);
19257 }
19258 
19265 {
19266  return mSelected ? mSelectedFont : mFont;
19267 }
19268 
19275 {
19277 }
19278 /* end of 'src/layoutelements/layoutelement-textelement.cpp' */
19279 
19280 
19281 /* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 26246 */
19282 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
19283 
19284 
19288 
19327 /* start documentation of inline functions */
19328 
19342 /* end documentation of signals */
19343 /* start documentation of signals */
19344 
19366 /* end documentation of signals */
19367 
19372  QCPLayoutElement(parentPlot),
19373  mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
19374  mDataScaleType(QCPAxis::stLinear),
19375  mBarWidth(20),
19376  mAxisRect(new QCPColorScaleAxisRectPrivate(this))
19377 {
19378  setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used)
19380  setDataRange(QCPRange(0, 6));
19381 }
19382 
19384 {
19385  delete mAxisRect;
19386 }
19387 
19388 /* undocumented getter */
19389 QString QCPColorScale::label() const
19390 {
19391  if (!mColorAxis)
19392  {
19393  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
19394  return QString();
19395  }
19396 
19397  return mColorAxis.data()->label();
19398 }
19399 
19400 /* undocumented getter */
19402 {
19403  if (!mAxisRect)
19404  {
19405  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19406  return false;
19407  }
19408 
19409  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
19410  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
19411  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
19412 }
19413 
19414 /* undocumented getter */
19416 {
19417  if (!mAxisRect)
19418  {
19419  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19420  return false;
19421  }
19422 
19423  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
19424  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
19425  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
19426 }
19427 
19436 {
19437  if (!mAxisRect)
19438  {
19439  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19440  return;
19441  }
19442  if (mType != type)
19443  {
19444  mType = type;
19445  QCPRange rangeTransfer(0, 6);
19446  QString labelTransfer;
19447  QSharedPointer<QCPAxisTicker> tickerTransfer;
19448  // transfer/revert some settings on old axis if it exists:
19449  bool doTransfer = (bool)mColorAxis;
19450  if (doTransfer)
19451  {
19452  rangeTransfer = mColorAxis.data()->range();
19453  labelTransfer = mColorAxis.data()->label();
19454  tickerTransfer = mColorAxis.data()->ticker();
19455  mColorAxis.data()->setLabel(QString());
19456  disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19457  disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
19458  }
19459  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
19460  foreach (QCPAxis::AxisType atype, allAxisTypes)
19461  {
19462  mAxisRect.data()->axis(atype)->setTicks(atype == mType);
19463  mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
19464  }
19465  // set new mColorAxis pointer:
19466  mColorAxis = mAxisRect.data()->axis(mType);
19467  // transfer settings to new axis:
19468  if (doTransfer)
19469  {
19470  mColorAxis.data()->setRange(rangeTransfer); // range transfer necessary if axis changes from vertical to horizontal or vice versa (axes with same orientation are synchronized via signals)
19471  mColorAxis.data()->setLabel(labelTransfer);
19472  mColorAxis.data()->setTicker(tickerTransfer);
19473  }
19474  connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19475  connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
19476  mAxisRect.data()->setRangeDragAxes(QList<QCPAxis*>() << mColorAxis.data());
19477  }
19478 }
19479 
19490 {
19491  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
19492  {
19494  if (mColorAxis)
19495  mColorAxis.data()->setRange(mDataRange);
19497  }
19498 }
19499 
19520 {
19521  if (mDataScaleType != scaleType)
19522  {
19523  mDataScaleType = scaleType;
19524  if (mColorAxis)
19525  mColorAxis.data()->setScaleType(mDataScaleType);
19529  }
19530 }
19531 
19540 {
19541  if (mGradient != gradient)
19542  {
19543  mGradient = gradient;
19544  if (mAxisRect)
19545  mAxisRect.data()->mGradientImageInvalidated = true;
19546  emit gradientChanged(mGradient);
19547  }
19548 }
19549 
19554 void QCPColorScale::setLabel(const QString &str)
19555 {
19556  if (!mColorAxis)
19557  {
19558  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
19559  return;
19560  }
19561 
19562  mColorAxis.data()->setLabel(str);
19563 }
19564 
19570 {
19571  mBarWidth = width;
19572 }
19573 
19581 {
19582  if (!mAxisRect)
19583  {
19584  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19585  return;
19586  }
19587 
19588  if (enabled)
19589  mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
19590  else
19591  mAxisRect.data()->setRangeDrag(0);
19592 }
19593 
19601 {
19602  if (!mAxisRect)
19603  {
19604  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19605  return;
19606  }
19607 
19608  if (enabled)
19609  mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
19610  else
19611  mAxisRect.data()->setRangeZoom(0);
19612 }
19613 
19617 QList<QCPColorMap*> QCPColorScale::colorMaps() const
19618 {
19619  QList<QCPColorMap*> result;
19620  for (int i=0; i<mParentPlot->plottableCount(); ++i)
19621  {
19622  if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
19623  if (cm->colorScale() == this)
19624  result.append(cm);
19625  }
19626  return result;
19627 }
19628 
19635 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
19636 {
19637  QList<QCPColorMap*> maps = colorMaps();
19638  QCPRange newRange;
19639  bool haveRange = false;
19643  for (int i=0; i<maps.size(); ++i)
19644  {
19645  if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
19646  continue;
19647  QCPRange mapRange;
19648  if (maps.at(i)->colorScale() == this)
19649  {
19650  bool currentFoundRange = true;
19651  mapRange = maps.at(i)->data()->dataBounds();
19652  if (sign == QCP::sdPositive)
19653  {
19654  if (mapRange.lower <= 0 && mapRange.upper > 0)
19655  mapRange.lower = mapRange.upper*1e-3;
19656  else if (mapRange.lower <= 0 && mapRange.upper <= 0)
19657  currentFoundRange = false;
19658  } else if (sign == QCP::sdNegative)
19659  {
19660  if (mapRange.upper >= 0 && mapRange.lower < 0)
19661  mapRange.upper = mapRange.lower*1e-3;
19662  else if (mapRange.upper >= 0 && mapRange.lower >= 0)
19663  currentFoundRange = false;
19664  }
19665  if (currentFoundRange)
19666  {
19667  if (!haveRange)
19668  newRange = mapRange;
19669  else
19670  newRange.expand(mapRange);
19671  haveRange = true;
19672  }
19673  }
19674  }
19675  if (haveRange)
19676  {
19677  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data
19678  {
19679  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
19681  {
19682  newRange.lower = center-mDataRange.size()/2.0;
19683  newRange.upper = center+mDataRange.size()/2.0;
19684  } else // mScaleType == stLogarithmic
19685  {
19686  newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
19687  newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
19688  }
19689  }
19690  setDataRange(newRange);
19691  }
19692 }
19693 
19694 /* inherits documentation from base class */
19696 {
19697  QCPLayoutElement::update(phase);
19698  if (!mAxisRect)
19699  {
19700  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19701  return;
19702  }
19703 
19704  mAxisRect.data()->update(phase);
19705 
19706  switch (phase)
19707  {
19708  case upMargins:
19709  {
19711  {
19712  setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom());
19713  setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom());
19714  } else
19715  {
19716  setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), QWIDGETSIZE_MAX);
19717  setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), 0);
19718  }
19719  break;
19720  }
19721  case upLayout:
19722  {
19723  mAxisRect.data()->setOuterRect(rect());
19724  break;
19725  }
19726  default: break;
19727  }
19728 }
19729 
19730 /* inherits documentation from base class */
19732 {
19733  painter->setAntialiasing(false);
19734 }
19735 
19736 /* inherits documentation from base class */
19737 void QCPColorScale::mousePressEvent(QMouseEvent *event, const QVariant &details)
19738 {
19739  if (!mAxisRect)
19740  {
19741  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19742  return;
19743  }
19744  mAxisRect.data()->mousePressEvent(event, details);
19745 }
19746 
19747 /* inherits documentation from base class */
19748 void QCPColorScale::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
19749 {
19750  if (!mAxisRect)
19751  {
19752  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19753  return;
19754  }
19755  mAxisRect.data()->mouseMoveEvent(event, startPos);
19756 }
19757 
19758 /* inherits documentation from base class */
19759 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
19760 {
19761  if (!mAxisRect)
19762  {
19763  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19764  return;
19765  }
19766  mAxisRect.data()->mouseReleaseEvent(event, startPos);
19767 }
19768 
19769 /* inherits documentation from base class */
19770 void QCPColorScale::wheelEvent(QWheelEvent *event)
19771 {
19772  if (!mAxisRect)
19773  {
19774  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19775  return;
19776  }
19777  mAxisRect.data()->wheelEvent(event);
19778 }
19779 
19783 
19799  QCPAxisRect(parentColorScale->parentPlot(), true),
19800  mParentColorScale(parentColorScale),
19801  mGradientImageInvalidated(true)
19802 {
19803  setParentLayerable(parentColorScale);
19804  setMinimumMargins(QMargins(0, 0, 0, 0));
19805  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
19806  foreach (QCPAxis::AxisType type, allAxisTypes)
19807  {
19808  axis(type)->setVisible(true);
19809  axis(type)->grid()->setVisible(false);
19810  axis(type)->setPadding(0);
19811  connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
19812  connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
19813  }
19814 
19815  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
19816  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
19817  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
19818  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
19819  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
19820  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
19821  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
19822  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
19823 
19824  // make layer transfers of color scale transfer to axis rect and axes
19825  // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
19826  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
19827  foreach (QCPAxis::AxisType type, allAxisTypes)
19828  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
19829 }
19830 
19839 {
19842 
19843  bool mirrorHorz = false;
19844  bool mirrorVert = false;
19846  {
19847  mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
19848  mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
19849  }
19850 
19851  painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
19852  QCPAxisRect::draw(painter);
19853 }
19854 
19861 {
19862  if (rect().isEmpty())
19863  return;
19864 
19865  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
19867  int w, h;
19868  QVector<double> data(n);
19869  for (int i=0; i<n; ++i)
19870  data[i] = i;
19872  {
19873  w = n;
19874  h = rect().height();
19875  mGradientImage = QImage(w, h, format);
19876  QVector<QRgb*> pixels;
19877  for (int y=0; y<h; ++y)
19878  pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
19879  mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
19880  for (int y=1; y<h; ++y)
19881  memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
19882  } else
19883  {
19884  w = rect().width();
19885  h = n;
19886  mGradientImage = QImage(w, h, format);
19887  for (int y=0; y<h; ++y)
19888  {
19889  QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
19890  const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
19891  for (int x=0; x<w; ++x)
19892  pixels[x] = lineColor;
19893  }
19894  }
19895  mGradientImageInvalidated = false;
19896 }
19897 
19903 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
19904 {
19905  // axis bases of four axes shall always (de-)selected synchronously:
19906  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
19907  foreach (QCPAxis::AxisType type, allAxisTypes)
19908  {
19909  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
19910  if (senderAxis->axisType() == type)
19911  continue;
19912 
19913  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
19914  {
19915  if (selectedParts.testFlag(QCPAxis::spAxis))
19916  axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
19917  else
19918  axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
19919  }
19920  }
19921 }
19922 
19928 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
19929 {
19930  // synchronize axis base selectability:
19931  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
19932  foreach (QCPAxis::AxisType type, allAxisTypes)
19933  {
19934  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
19935  if (senderAxis->axisType() == type)
19936  continue;
19937 
19938  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
19939  {
19940  if (selectableParts.testFlag(QCPAxis::spAxis))
19941  axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
19942  else
19943  axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
19944  }
19945  }
19946 }
19947 /* end of 'src/layoutelements/layoutelement-colorscale.cpp' */
19948 
19949 
19950 /* including file 'src/plottables/plottable-graph.cpp', size 74194 */
19951 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
19952 
19956 
19971 /* start documentation of inline functions */
19972 
20022 /* end documentation of inline functions */
20023 
20028  key(0),
20029  value(0)
20030 {
20031 }
20032 
20037  key(key),
20038  value(value)
20039 {
20040 }
20041 
20042 
20046 
20085 /* start of documentation of inline functions */
20086 
20094 /* end of documentation of inline functions */
20095 
20108 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20109  QCPAbstractPlottable1D<QCPGraphData>(keyAxis, valueAxis)
20110 {
20111  // special handling for QCPGraphs to maintain the simple graph interface:
20112  mParentPlot->registerGraph(this);
20113 
20114  setPen(QPen(Qt::blue, 0));
20115  setBrush(Qt::NoBrush);
20116 
20118  setScatterSkip(0);
20120  setAdaptiveSampling(true);
20121 }
20122 
20124 {
20125 }
20126 
20142 void QCPGraph::setData(QSharedPointer<QCPGraphDataContainer> data)
20143 {
20144  mDataContainer = data;
20145 }
20146 
20158 void QCPGraph::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
20159 {
20160  mDataContainer->clear();
20161  addData(keys, values, alreadySorted);
20162 }
20163 
20171 {
20172  mLineStyle = ls;
20173 }
20174 
20182 {
20183  mScatterStyle = style;
20184 }
20185 
20198 {
20199  mScatterSkip = qMax(0, skip);
20200 }
20201 
20212 {
20213  // prevent setting channel target to this graph itself:
20214  if (targetGraph == this)
20215  {
20216  qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
20217  mChannelFillGraph = 0;
20218  return;
20219  }
20220  // prevent setting channel target to a graph not in the plot:
20221  if (targetGraph && targetGraph->mParentPlot != mParentPlot)
20222  {
20223  qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
20224  mChannelFillGraph = 0;
20225  return;
20226  }
20227 
20228  mChannelFillGraph = targetGraph;
20229 }
20230 
20263 {
20264  mAdaptiveSampling = enabled;
20265 }
20266 
20279 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
20280 {
20281  if (keys.size() != values.size())
20282  qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
20283  const int n = qMin(keys.size(), values.size());
20284  QVector<QCPGraphData> tempData(n);
20285  QVector<QCPGraphData>::iterator it = tempData.begin();
20286  const QVector<QCPGraphData>::iterator itEnd = tempData.end();
20287  int i = 0;
20288  while (it != itEnd)
20289  {
20290  it->key = keys[i];
20291  it->value = values[i];
20292  ++it;
20293  ++i;
20294  }
20295  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
20296 }
20297 
20305 void QCPGraph::addData(double key, double value)
20306 {
20307  mDataContainer->add(QCPGraphData(key, value));
20308 }
20309 
20318 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20319 {
20320  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
20321  return -1;
20322  if (!mKeyAxis || !mValueAxis)
20323  return -1;
20324 
20325  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20326  {
20327  QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
20328  double result = pointDistance(pos, closestDataPoint);
20329  if (details)
20330  {
20331  int pointIndex = closestDataPoint-mDataContainer->constBegin();
20332  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
20333  }
20334  return result;
20335  } else
20336  return -1;
20337 }
20338 
20339 /* inherits documentation from base class */
20340 QCPRange QCPGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
20341 {
20342  return mDataContainer->keyRange(foundRange, inSignDomain);
20343 }
20344 
20345 /* inherits documentation from base class */
20346 QCPRange QCPGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
20347 {
20348  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
20349 }
20350 
20351 /* inherits documentation from base class */
20353 {
20354  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
20355  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
20356  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
20357 
20358  QVector<QPointF> lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while iterating over segments
20359 
20360  // loop over and draw segments of unselected/selected data:
20361  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
20362  getDataSegments(selectedSegments, unselectedSegments);
20363  allSegments << unselectedSegments << selectedSegments;
20364  for (int i=0; i<allSegments.size(); ++i)
20365  {
20366  bool isSelectedSegment = i >= unselectedSegments.size();
20367  // get line pixel points appropriate to line style:
20368  QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getLines takes care)
20369  getLines(&lines, lineDataRange);
20370 
20371  // check data validity if flag set:
20372 #ifdef QCUSTOMPLOT_CHECK_DATA
20374  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
20375  {
20376  if (QCP::isInvalidData(it->key, it->value))
20377  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
20378  }
20379 #endif
20380 
20381  // draw fill of graph:
20382  if (isSelectedSegment && mSelectionDecorator)
20383  mSelectionDecorator->applyBrush(painter);
20384  else
20385  painter->setBrush(mBrush);
20386  painter->setPen(Qt::NoPen);
20387  drawFill(painter, &lines);
20388 
20389  // draw line:
20390  if (mLineStyle != lsNone)
20391  {
20392  if (isSelectedSegment && mSelectionDecorator)
20393  mSelectionDecorator->applyPen(painter);
20394  else
20395  painter->setPen(mPen);
20396  painter->setBrush(Qt::NoBrush);
20397  if (mLineStyle == lsImpulse)
20398  drawImpulsePlot(painter, lines);
20399  else
20400  drawLinePlot(painter, lines); // also step plots can be drawn as a line plot
20401  }
20402 
20403  // draw scatters:
20404  QCPScatterStyle finalScatterStyle = mScatterStyle;
20405  if (isSelectedSegment && mSelectionDecorator)
20407  if (!finalScatterStyle.isNone())
20408  {
20409  getScatters(&scatters, allSegments.at(i));
20410  drawScatterPlot(painter, scatters, finalScatterStyle);
20411  }
20412  }
20413 
20414  // draw other selection decoration that isn't just line/scatter pens and brushes:
20415  if (mSelectionDecorator)
20417 }
20418 
20419 /* inherits documentation from base class */
20420 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20421 {
20422  // draw fill:
20423  if (mBrush.style() != Qt::NoBrush)
20424  {
20425  applyFillAntialiasingHint(painter);
20426  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
20427  }
20428  // draw line vertically centered:
20429  if (mLineStyle != lsNone)
20430  {
20432  painter->setPen(mPen);
20433  painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
20434  }
20435  // draw scatter symbol:
20436  if (!mScatterStyle.isNone())
20437  {
20439  // scale scatter pixmap if it's too large to fit in legend icon rect:
20440  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
20441  {
20442  QCPScatterStyle scaledStyle(mScatterStyle);
20443  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
20444  scaledStyle.applyTo(painter, mPen);
20445  scaledStyle.drawShape(painter, QRectF(rect).center());
20446  } else
20447  {
20448  mScatterStyle.applyTo(painter, mPen);
20449  mScatterStyle.drawShape(painter, QRectF(rect).center());
20450  }
20451  }
20452 }
20453 
20474 void QCPGraph::getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const
20475 {
20476  if (!lines) return;
20478  getVisibleDataBounds(begin, end, dataRange);
20479  if (begin == end)
20480  {
20481  lines->clear();
20482  return;
20483  }
20484 
20485  QVector<QCPGraphData> lineData;
20486  if (mLineStyle != lsNone)
20487  getOptimizedLineData(&lineData, begin, end);
20488 
20489  if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in lineData (significantly simplifies following processing)
20490  std::reverse(lineData.begin(), lineData.end());
20491 
20492  switch (mLineStyle)
20493  {
20494  case lsNone: lines->clear(); break;
20495  case lsLine: *lines = dataToLines(lineData); break;
20496  case lsStepLeft: *lines = dataToStepLeftLines(lineData); break;
20497  case lsStepRight: *lines = dataToStepRightLines(lineData); break;
20498  case lsStepCenter: *lines = dataToStepCenterLines(lineData); break;
20499  case lsImpulse: *lines = dataToImpulseLines(lineData); break;
20500  }
20501 }
20502 
20515 void QCPGraph::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const
20516 {
20517  if (!scatters) return;
20518  QCPAxis *keyAxis = mKeyAxis.data();
20519  QCPAxis *valueAxis = mValueAxis.data();
20520  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; scatters->clear(); return; }
20521 
20523  getVisibleDataBounds(begin, end, dataRange);
20524  if (begin == end)
20525  {
20526  scatters->clear();
20527  return;
20528  }
20529 
20530  QVector<QCPGraphData> data;
20531  getOptimizedScatterData(&data, begin, end);
20532 
20533  if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in data (significantly simplifies following processing)
20534  std::reverse(data.begin(), data.end());
20535 
20536  scatters->resize(data.size());
20537  if (keyAxis->orientation() == Qt::Vertical)
20538  {
20539  for (int i=0; i<data.size(); ++i)
20540  {
20541  if (!qIsNaN(data.at(i).value))
20542  {
20543  (*scatters)[i].setX(valueAxis->coordToPixel(data.at(i).value));
20544  (*scatters)[i].setY(keyAxis->coordToPixel(data.at(i).key));
20545  }
20546  }
20547  } else
20548  {
20549  for (int i=0; i<data.size(); ++i)
20550  {
20551  if (!qIsNaN(data.at(i).value))
20552  {
20553  (*scatters)[i].setX(keyAxis->coordToPixel(data.at(i).key));
20554  (*scatters)[i].setY(valueAxis->coordToPixel(data.at(i).value));
20555  }
20556  }
20557  }
20558 }
20559 
20570 QVector<QPointF> QCPGraph::dataToLines(const QVector<QCPGraphData> &data) const
20571 {
20572  QVector<QPointF> result;
20573  QCPAxis *keyAxis = mKeyAxis.data();
20574  QCPAxis *valueAxis = mValueAxis.data();
20575  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20576 
20577  result.resize(data.size());
20578 
20579  // transform data points to pixels:
20580  if (keyAxis->orientation() == Qt::Vertical)
20581  {
20582  for (int i=0; i<data.size(); ++i)
20583  {
20584  result[i].setX(valueAxis->coordToPixel(data.at(i).value));
20585  result[i].setY(keyAxis->coordToPixel(data.at(i).key));
20586  }
20587  } else // key axis is horizontal
20588  {
20589  for (int i=0; i<data.size(); ++i)
20590  {
20591  result[i].setX(keyAxis->coordToPixel(data.at(i).key));
20592  result[i].setY(valueAxis->coordToPixel(data.at(i).value));
20593  }
20594  }
20595  return result;
20596 }
20597 
20608 QVector<QPointF> QCPGraph::dataToStepLeftLines(const QVector<QCPGraphData> &data) const
20609 {
20610  QVector<QPointF> result;
20611  QCPAxis *keyAxis = mKeyAxis.data();
20612  QCPAxis *valueAxis = mValueAxis.data();
20613  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20614 
20615  result.resize(data.size()*2);
20616 
20617  // calculate steps from data and transform to pixel coordinates:
20618  if (keyAxis->orientation() == Qt::Vertical)
20619  {
20620  double lastValue = valueAxis->coordToPixel(data.first().value);
20621  for (int i=0; i<data.size(); ++i)
20622  {
20623  const double key = keyAxis->coordToPixel(data.at(i).key);
20624  result[i*2+0].setX(lastValue);
20625  result[i*2+0].setY(key);
20626  lastValue = valueAxis->coordToPixel(data.at(i).value);
20627  result[i*2+1].setX(lastValue);
20628  result[i*2+1].setY(key);
20629  }
20630  } else // key axis is horizontal
20631  {
20632  double lastValue = valueAxis->coordToPixel(data.first().value);
20633  for (int i=0; i<data.size(); ++i)
20634  {
20635  const double key = keyAxis->coordToPixel(data.at(i).key);
20636  result[i*2+0].setX(key);
20637  result[i*2+0].setY(lastValue);
20638  lastValue = valueAxis->coordToPixel(data.at(i).value);
20639  result[i*2+1].setX(key);
20640  result[i*2+1].setY(lastValue);
20641  }
20642  }
20643  return result;
20644 }
20645 
20656 QVector<QPointF> QCPGraph::dataToStepRightLines(const QVector<QCPGraphData> &data) const
20657 {
20658  QVector<QPointF> result;
20659  QCPAxis *keyAxis = mKeyAxis.data();
20660  QCPAxis *valueAxis = mValueAxis.data();
20661  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20662 
20663  result.resize(data.size()*2);
20664 
20665  // calculate steps from data and transform to pixel coordinates:
20666  if (keyAxis->orientation() == Qt::Vertical)
20667  {
20668  double lastKey = keyAxis->coordToPixel(data.first().key);
20669  for (int i=0; i<data.size(); ++i)
20670  {
20671  const double value = valueAxis->coordToPixel(data.at(i).value);
20672  result[i*2+0].setX(value);
20673  result[i*2+0].setY(lastKey);
20674  lastKey = keyAxis->coordToPixel(data.at(i).key);
20675  result[i*2+1].setX(value);
20676  result[i*2+1].setY(lastKey);
20677  }
20678  } else // key axis is horizontal
20679  {
20680  double lastKey = keyAxis->coordToPixel(data.first().key);
20681  for (int i=0; i<data.size(); ++i)
20682  {
20683  const double value = valueAxis->coordToPixel(data.at(i).value);
20684  result[i*2+0].setX(lastKey);
20685  result[i*2+0].setY(value);
20686  lastKey = keyAxis->coordToPixel(data.at(i).key);
20687  result[i*2+1].setX(lastKey);
20688  result[i*2+1].setY(value);
20689  }
20690  }
20691  return result;
20692 }
20693 
20704 QVector<QPointF> QCPGraph::dataToStepCenterLines(const QVector<QCPGraphData> &data) const
20705 {
20706  QVector<QPointF> result;
20707  QCPAxis *keyAxis = mKeyAxis.data();
20708  QCPAxis *valueAxis = mValueAxis.data();
20709  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20710 
20711  result.resize(data.size()*2);
20712 
20713  // calculate steps from data and transform to pixel coordinates:
20714  if (keyAxis->orientation() == Qt::Vertical)
20715  {
20716  double lastKey = keyAxis->coordToPixel(data.first().key);
20717  double lastValue = valueAxis->coordToPixel(data.first().value);
20718  result[0].setX(lastValue);
20719  result[0].setY(lastKey);
20720  for (int i=1; i<data.size(); ++i)
20721  {
20722  const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;
20723  result[i*2-1].setX(lastValue);
20724  result[i*2-1].setY(key);
20725  lastValue = valueAxis->coordToPixel(data.at(i).value);
20726  lastKey = keyAxis->coordToPixel(data.at(i).key);
20727  result[i*2+0].setX(lastValue);
20728  result[i*2+0].setY(key);
20729  }
20730  result[data.size()*2-1].setX(lastValue);
20731  result[data.size()*2-1].setY(lastKey);
20732  } else // key axis is horizontal
20733  {
20734  double lastKey = keyAxis->coordToPixel(data.first().key);
20735  double lastValue = valueAxis->coordToPixel(data.first().value);
20736  result[0].setX(lastKey);
20737  result[0].setY(lastValue);
20738  for (int i=1; i<data.size(); ++i)
20739  {
20740  const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;
20741  result[i*2-1].setX(key);
20742  result[i*2-1].setY(lastValue);
20743  lastValue = valueAxis->coordToPixel(data.at(i).value);
20744  lastKey = keyAxis->coordToPixel(data.at(i).key);
20745  result[i*2+0].setX(key);
20746  result[i*2+0].setY(lastValue);
20747  }
20748  result[data.size()*2-1].setX(lastKey);
20749  result[data.size()*2-1].setY(lastValue);
20750  }
20751  return result;
20752 }
20753 
20764 QVector<QPointF> QCPGraph::dataToImpulseLines(const QVector<QCPGraphData> &data) const
20765 {
20766  QVector<QPointF> result;
20767  QCPAxis *keyAxis = mKeyAxis.data();
20768  QCPAxis *valueAxis = mValueAxis.data();
20769  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20770 
20771  result.resize(data.size()*2);
20772 
20773  // transform data points to pixels:
20774  if (keyAxis->orientation() == Qt::Vertical)
20775  {
20776  for (int i=0; i<data.size(); ++i)
20777  {
20778  const double key = keyAxis->coordToPixel(data.at(i).key);
20779  result[i*2+0].setX(valueAxis->coordToPixel(0));
20780  result[i*2+0].setY(key);
20781  result[i*2+1].setX(valueAxis->coordToPixel(data.at(i).value));
20782  result[i*2+1].setY(key);
20783  }
20784  } else // key axis is horizontal
20785  {
20786  for (int i=0; i<data.size(); ++i)
20787  {
20788  const double key = keyAxis->coordToPixel(data.at(i).key);
20789  result[i*2+0].setX(key);
20790  result[i*2+0].setY(valueAxis->coordToPixel(0));
20791  result[i*2+1].setX(key);
20792  result[i*2+1].setY(valueAxis->coordToPixel(data.at(i).value));
20793  }
20794  }
20795  return result;
20796 }
20797 
20815 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const
20816 {
20817  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
20818  if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0) return;
20819 
20820  applyFillAntialiasingHint(painter);
20821  QVector<QCPDataRange> segments = getNonNanSegments(lines, keyAxis()->orientation());
20822  if (!mChannelFillGraph)
20823  {
20824  // draw base fill under graph, fill goes all the way to the zero-value-line:
20825  for (int i=0; i<segments.size(); ++i)
20826  painter->drawPolygon(getFillPolygon(lines, segments.at(i)));
20827  } else
20828  {
20829  // draw fill between this graph and mChannelFillGraph:
20830  QVector<QPointF> otherLines;
20831  mChannelFillGraph->getLines(&otherLines, QCPDataRange(0, mChannelFillGraph->dataCount()));
20832  if (!otherLines.isEmpty())
20833  {
20834  QVector<QCPDataRange> otherSegments = getNonNanSegments(&otherLines, mChannelFillGraph->keyAxis()->orientation());
20835  QVector<QPair<QCPDataRange, QCPDataRange> > segmentPairs = getOverlappingSegments(segments, lines, otherSegments, &otherLines);
20836  for (int i=0; i<segmentPairs.size(); ++i)
20837  painter->drawPolygon(getChannelFillPolygon(lines, segmentPairs.at(i).first, &otherLines, segmentPairs.at(i).second));
20838  }
20839  }
20840 }
20841 
20849 void QCPGraph::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const
20850 {
20852  style.applyTo(painter, mPen);
20853  for (int i=0; i<scatters.size(); ++i)
20854  style.drawShape(painter, scatters.at(i).x(), scatters.at(i).y());
20855 }
20856 
20863 void QCPGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
20864 {
20865  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
20866  {
20868  drawPolyline(painter, lines);
20869  }
20870 }
20871 
20880 void QCPGraph::drawImpulsePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
20881 {
20882  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
20883  {
20885  QPen oldPen = painter->pen();
20886  QPen newPen = painter->pen();
20887  newPen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
20888  painter->setPen(newPen);
20889  painter->drawLines(lines);
20890  painter->setPen(oldPen);
20891  }
20892 }
20893 
20906 void QCPGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
20907 {
20908  if (!lineData) return;
20909  QCPAxis *keyAxis = mKeyAxis.data();
20910  QCPAxis *valueAxis = mValueAxis.data();
20911  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
20912  if (begin == end) return;
20913 
20914  int dataCount = end-begin;
20915  int maxCount = (std::numeric_limits<int>::max)();
20916  if (mAdaptiveSampling)
20917  {
20918  double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));
20919  if (2*keyPixelSpan+2 < static_cast<double>((std::numeric_limits<int>::max)()))
20920  maxCount = 2*keyPixelSpan+2;
20921  }
20922 
20923  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
20924  {
20926  double minValue = it->value;
20927  double maxValue = it->value;
20928  QCPGraphDataContainer::const_iterator currentIntervalFirstPoint = it;
20929  int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction
20930  int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
20931  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound));
20932  double lastIntervalEndKey = currentIntervalStartKey;
20933  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
20934  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
20935  int intervalDataCount = 1;
20936  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
20937  while (it != end)
20938  {
20939  if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
20940  {
20941  if (it->value < minValue)
20942  minValue = it->value;
20943  else if (it->value > maxValue)
20944  maxValue = it->value;
20945  ++intervalDataCount;
20946  } else // new pixel interval started
20947  {
20948  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
20949  {
20950  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
20951  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));
20952  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
20953  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
20954  if (it->key > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point
20955  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.8, (it-1)->value));
20956  } else
20957  lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
20958  lastIntervalEndKey = (it-1)->key;
20959  minValue = it->value;
20960  maxValue = it->value;
20961  currentIntervalFirstPoint = it;
20962  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound));
20963  if (keyEpsilonVariable)
20964  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
20965  intervalDataCount = 1;
20966  }
20967  ++it;
20968  }
20969  // handle last interval:
20970  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
20971  {
20972  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
20973  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));
20974  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
20975  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
20976  } else
20977  lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
20978 
20979  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output
20980  {
20981  lineData->resize(dataCount);
20982  std::copy(begin, end, lineData->begin());
20983  }
20984 }
20985 
20999 {
21000  if (!scatterData) return;
21001  QCPAxis *keyAxis = mKeyAxis.data();
21002  QCPAxis *valueAxis = mValueAxis.data();
21003  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21004 
21005  const int scatterModulo = mScatterSkip+1;
21006  const bool doScatterSkip = mScatterSkip > 0;
21007  int beginIndex = begin-mDataContainer->constBegin();
21008  int endIndex = end-mDataContainer->constBegin();
21009  while (doScatterSkip && begin != end && beginIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter
21010  {
21011  ++beginIndex;
21012  ++begin;
21013  }
21014  if (begin == end) return;
21015  int dataCount = end-begin;
21016  int maxCount = (std::numeric_limits<int>::max)();
21017  if (mAdaptiveSampling)
21018  {
21019  int keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));
21020  maxCount = 2*keyPixelSpan+2;
21021  }
21022 
21023  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
21024  {
21025  double valueMaxRange = valueAxis->range().upper;
21026  double valueMinRange = valueAxis->range().lower;
21028  int itIndex = beginIndex;
21029  double minValue = it->value;
21030  double maxValue = it->value;
21031  QCPGraphDataContainer::const_iterator minValueIt = it;
21032  QCPGraphDataContainer::const_iterator maxValueIt = it;
21033  QCPGraphDataContainer::const_iterator currentIntervalStart = it;
21034  int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction
21035  int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
21036  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound));
21037  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
21038  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
21039  int intervalDataCount = 1;
21040  // advance iterator to second (non-skipped) data point because adaptive sampling works in 1 point retrospect:
21041  if (!doScatterSkip)
21042  ++it;
21043  else
21044  {
21045  itIndex += scatterModulo;
21046  if (itIndex < endIndex) // make sure we didn't jump over end
21047  it += scatterModulo;
21048  else
21049  {
21050  it = end;
21051  itIndex = endIndex;
21052  }
21053  }
21054  // main loop over data points:
21055  while (it != end)
21056  {
21057  if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
21058  {
21059  if (it->value < minValue && it->value > valueMinRange && it->value < valueMaxRange)
21060  {
21061  minValue = it->value;
21062  minValueIt = it;
21063  } else if (it->value > maxValue && it->value > valueMinRange && it->value < valueMaxRange)
21064  {
21065  maxValue = it->value;
21066  maxValueIt = it;
21067  }
21068  ++intervalDataCount;
21069  } else // new pixel started
21070  {
21071  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
21072  {
21073  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
21074  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
21075  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
21076  QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;
21077  int c = 0;
21078  while (intervalIt != it)
21079  {
21080  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)
21081  scatterData->append(*intervalIt);
21082  ++c;
21083  if (!doScatterSkip)
21084  ++intervalIt;
21085  else
21086  intervalIt += scatterModulo; // since we know indices of "currentIntervalStart", "intervalIt" and "it" are multiples of scatterModulo, we can't accidentally jump over "it" here
21087  }
21088  } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)
21089  scatterData->append(*currentIntervalStart);
21090  minValue = it->value;
21091  maxValue = it->value;
21092  currentIntervalStart = it;
21093  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound));
21094  if (keyEpsilonVariable)
21095  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
21096  intervalDataCount = 1;
21097  }
21098  // advance to next data point:
21099  if (!doScatterSkip)
21100  ++it;
21101  else
21102  {
21103  itIndex += scatterModulo;
21104  if (itIndex < endIndex) // make sure we didn't jump over end
21105  it += scatterModulo;
21106  else
21107  {
21108  it = end;
21109  itIndex = endIndex;
21110  }
21111  }
21112  }
21113  // handle last interval:
21114  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
21115  {
21116  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
21117  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
21118  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
21119  QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;
21120  int intervalItIndex = intervalIt-mDataContainer->constBegin();
21121  int c = 0;
21122  while (intervalIt != it)
21123  {
21124  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)
21125  scatterData->append(*intervalIt);
21126  ++c;
21127  if (!doScatterSkip)
21128  ++intervalIt;
21129  else // here we can't guarantee that adding scatterModulo doesn't exceed "it" (because "it" is equal to "end" here, and "end" isn't scatterModulo-aligned), so check via index comparison:
21130  {
21131  intervalItIndex += scatterModulo;
21132  if (intervalItIndex < itIndex)
21133  intervalIt += scatterModulo;
21134  else
21135  {
21136  intervalIt = it;
21137  intervalItIndex = itIndex;
21138  }
21139  }
21140  }
21141  } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)
21142  scatterData->append(*currentIntervalStart);
21143 
21144  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output
21145  {
21147  int itIndex = beginIndex;
21148  scatterData->reserve(dataCount);
21149  while (it != end)
21150  {
21151  scatterData->append(*it);
21152  // advance to next data point:
21153  if (!doScatterSkip)
21154  ++it;
21155  else
21156  {
21157  itIndex += scatterModulo;
21158  if (itIndex < endIndex)
21159  it += scatterModulo;
21160  else
21161  {
21162  it = end;
21163  itIndex = endIndex;
21164  }
21165  }
21166  }
21167  }
21168 }
21169 
21180 {
21181  if (rangeRestriction.isEmpty())
21182  {
21183  end = mDataContainer->constEnd();
21184  begin = end;
21185  } else
21186  {
21187  QCPAxis *keyAxis = mKeyAxis.data();
21188  QCPAxis *valueAxis = mValueAxis.data();
21189  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21190  // get visible data range:
21191  begin = mDataContainer->findBegin(keyAxis->range().lower);
21192  end = mDataContainer->findEnd(keyAxis->range().upper);
21193  // limit lower/upperEnd to rangeRestriction:
21194  mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction); // this also ensures rangeRestriction outside data bounds doesn't break anything
21195  }
21196 }
21197 
21209 QVector<QCPDataRange> QCPGraph::getNonNanSegments(const QVector<QPointF> *lineData, Qt::Orientation keyOrientation) const
21210 {
21211  QVector<QCPDataRange> result;
21212  const int n = lineData->size();
21213 
21214  QCPDataRange currentSegment(-1, -1);
21215  int i = 0;
21216 
21217  if (keyOrientation == Qt::Horizontal)
21218  {
21219  while (i < n)
21220  {
21221  while (i < n && qIsNaN(lineData->at(i).y())) // seek next non-NaN data point
21222  ++i;
21223  if (i == n)
21224  break;
21225  currentSegment.setBegin(i++);
21226  while (i < n && !qIsNaN(lineData->at(i).y())) // seek next NaN data point or end of data
21227  ++i;
21228  currentSegment.setEnd(i++);
21229  result.append(currentSegment);
21230  }
21231  } else // keyOrientation == Qt::Vertical
21232  {
21233  while (i < n)
21234  {
21235  while (i < n && qIsNaN(lineData->at(i).x())) // seek next non-NaN data point
21236  ++i;
21237  if (i == n)
21238  break;
21239  currentSegment.setBegin(i++);
21240  while (i < n && !qIsNaN(lineData->at(i).x())) // seek next NaN data point or end of data
21241  ++i;
21242  currentSegment.setEnd(i++);
21243  result.append(currentSegment);
21244  }
21245  }
21246  return result;
21247 }
21248 
21267 QVector<QPair<QCPDataRange, QCPDataRange> > QCPGraph::getOverlappingSegments(QVector<QCPDataRange> thisSegments, const QVector<QPointF> *thisData, QVector<QCPDataRange> otherSegments, const QVector<QPointF> *otherData) const
21268 {
21269  QVector<QPair<QCPDataRange, QCPDataRange> > result;
21270  if (thisData->isEmpty() || otherData->isEmpty() || thisSegments.isEmpty() || otherSegments.isEmpty())
21271  return result;
21272 
21273  int thisIndex = 0;
21274  int otherIndex = 0;
21275  const bool verticalKey = mKeyAxis->orientation() == Qt::Vertical;
21276  while (thisIndex < thisSegments.size() && otherIndex < otherSegments.size())
21277  {
21278  if (thisSegments.at(thisIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow
21279  {
21280  ++thisIndex;
21281  continue;
21282  }
21283  if (otherSegments.at(otherIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow
21284  {
21285  ++otherIndex;
21286  continue;
21287  }
21288  double thisLower, thisUpper, otherLower, otherUpper;
21289  if (!verticalKey)
21290  {
21291  thisLower = thisData->at(thisSegments.at(thisIndex).begin()).x();
21292  thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).x();
21293  otherLower = otherData->at(otherSegments.at(otherIndex).begin()).x();
21294  otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).x();
21295  } else
21296  {
21297  thisLower = thisData->at(thisSegments.at(thisIndex).begin()).y();
21298  thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).y();
21299  otherLower = otherData->at(otherSegments.at(otherIndex).begin()).y();
21300  otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).y();
21301  }
21302 
21303  int bPrecedence;
21304  if (segmentsIntersect(thisLower, thisUpper, otherLower, otherUpper, bPrecedence))
21305  result.append(QPair<QCPDataRange, QCPDataRange>(thisSegments.at(thisIndex), otherSegments.at(otherIndex)));
21306 
21307  if (bPrecedence <= 0) // otherSegment doesn't reach as far as thisSegment, so continue with next otherSegment, keeping current thisSegment
21308  ++otherIndex;
21309  else // otherSegment reaches further than thisSegment, so continue with next thisSegment, keeping current otherSegment
21310  ++thisIndex;
21311  }
21312 
21313  return result;
21314 }
21315 
21330 bool QCPGraph::segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const
21331 {
21332  bPrecedence = 0;
21333  if (aLower > bUpper)
21334  {
21335  bPrecedence = -1;
21336  return false;
21337  } else if (bLower > aUpper)
21338  {
21339  bPrecedence = 1;
21340  return false;
21341  } else
21342  {
21343  if (aUpper > bUpper)
21344  bPrecedence = -1;
21345  else if (aUpper < bUpper)
21346  bPrecedence = 1;
21347 
21348  return true;
21349  }
21350 }
21351 
21363 QPointF QCPGraph::getFillBasePoint(QPointF matchingDataPoint) const
21364 {
21365  QCPAxis *keyAxis = mKeyAxis.data();
21366  QCPAxis *valueAxis = mValueAxis.data();
21367  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
21368 
21369  QPointF result;
21370  if (valueAxis->scaleType() == QCPAxis::stLinear)
21371  {
21372  if (keyAxis->orientation() == Qt::Horizontal)
21373  {
21374  result.setX(matchingDataPoint.x());
21375  result.setY(valueAxis->coordToPixel(0));
21376  } else // keyAxis->orientation() == Qt::Vertical
21377  {
21378  result.setX(valueAxis->coordToPixel(0));
21379  result.setY(matchingDataPoint.y());
21380  }
21381  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
21382  {
21383  // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
21384  // to the axis which is in the direction towards 0
21385  if (keyAxis->orientation() == Qt::Vertical)
21386  {
21387  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
21388  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
21389  result.setX(keyAxis->axisRect()->right());
21390  else
21391  result.setX(keyAxis->axisRect()->left());
21392  result.setY(matchingDataPoint.y());
21393  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
21394  {
21395  result.setX(matchingDataPoint.x());
21396  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
21397  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
21398  result.setY(keyAxis->axisRect()->top());
21399  else
21400  result.setY(keyAxis->axisRect()->bottom());
21401  }
21402  }
21403  return result;
21404 }
21405 
21422 const QPolygonF QCPGraph::getFillPolygon(const QVector<QPointF> *lineData, QCPDataRange segment) const
21423 {
21424  if (segment.size() < 2)
21425  return QPolygonF();
21426  QPolygonF result(segment.size()+2);
21427 
21428  result[0] = getFillBasePoint(lineData->at(segment.begin()));
21429  std::copy(lineData->constBegin()+segment.begin(), lineData->constBegin()+segment.end(), result.begin()+1);
21430  result[result.size()-1] = getFillBasePoint(lineData->at(segment.end()-1));
21431 
21432  return result;
21433 }
21434 
21453 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *thisData, QCPDataRange thisSegment, const QVector<QPointF> *otherData, QCPDataRange otherSegment) const
21454 {
21455  if (!mChannelFillGraph)
21456  return QPolygonF();
21457 
21458  QCPAxis *keyAxis = mKeyAxis.data();
21459  QCPAxis *valueAxis = mValueAxis.data();
21460  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
21461  if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
21462 
21463  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
21464  return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)
21465 
21466  if (thisData->isEmpty()) return QPolygonF();
21467  QVector<QPointF> thisSegmentData(thisSegment.size());
21468  QVector<QPointF> otherSegmentData(otherSegment.size());
21469  std::copy(thisData->constBegin()+thisSegment.begin(), thisData->constBegin()+thisSegment.end(), thisSegmentData.begin());
21470  std::copy(otherData->constBegin()+otherSegment.begin(), otherData->constBegin()+otherSegment.end(), otherSegmentData.begin());
21471  // pointers to be able to swap them, depending which data range needs cropping:
21472  QVector<QPointF> *staticData = &thisSegmentData;
21473  QVector<QPointF> *croppedData = &otherSegmentData;
21474 
21475  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
21476  if (keyAxis->orientation() == Qt::Horizontal)
21477  {
21478  // x is key
21479  // crop lower bound:
21480  if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
21481  qSwap(staticData, croppedData);
21482  const int lowBound = findIndexBelowX(croppedData, staticData->first().x());
21483  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
21484  croppedData->remove(0, lowBound);
21485  // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation:
21486  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21487  double slope;
21488  if (!qFuzzyCompare(croppedData->at(1).x(), croppedData->at(0).x()))
21489  slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
21490  else
21491  slope = 0;
21492  (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
21493  (*croppedData)[0].setX(staticData->first().x());
21494 
21495  // crop upper bound:
21496  if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
21497  qSwap(staticData, croppedData);
21498  int highBound = findIndexAboveX(croppedData, staticData->last().x());
21499  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
21500  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
21501  // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation:
21502  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21503  const int li = croppedData->size()-1; // last index
21504  if (!qFuzzyCompare(croppedData->at(li).x(), croppedData->at(li-1).x()))
21505  slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
21506  else
21507  slope = 0;
21508  (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
21509  (*croppedData)[li].setX(staticData->last().x());
21510  } else // mKeyAxis->orientation() == Qt::Vertical
21511  {
21512  // y is key
21513  // crop lower bound:
21514  if (staticData->first().y() < croppedData->first().y()) // other one must be cropped
21515  qSwap(staticData, croppedData);
21516  int lowBound = findIndexBelowY(croppedData, staticData->first().y());
21517  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
21518  croppedData->remove(0, lowBound);
21519  // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation:
21520  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21521  double slope;
21522  if (!qFuzzyCompare(croppedData->at(1).y(), croppedData->at(0).y())) // avoid division by zero in step plots
21523  slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
21524  else
21525  slope = 0;
21526  (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
21527  (*croppedData)[0].setY(staticData->first().y());
21528 
21529  // crop upper bound:
21530  if (staticData->last().y() > croppedData->last().y()) // other one must be cropped
21531  qSwap(staticData, croppedData);
21532  int highBound = findIndexAboveY(croppedData, staticData->last().y());
21533  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
21534  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
21535  // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation:
21536  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21537  int li = croppedData->size()-1; // last index
21538  if (!qFuzzyCompare(croppedData->at(li).y(), croppedData->at(li-1).y())) // avoid division by zero in step plots
21539  slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
21540  else
21541  slope = 0;
21542  (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
21543  (*croppedData)[li].setY(staticData->last().y());
21544  }
21545 
21546  // return joined:
21547  for (int i=otherSegmentData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
21548  thisSegmentData << otherSegmentData.at(i);
21549  return QPolygonF(thisSegmentData);
21550 }
21551 
21560 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
21561 {
21562  for (int i=data->size()-1; i>=0; --i)
21563  {
21564  if (data->at(i).x() < x)
21565  {
21566  if (i<data->size()-1)
21567  return i+1;
21568  else
21569  return data->size()-1;
21570  }
21571  }
21572  return -1;
21573 }
21574 
21583 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
21584 {
21585  for (int i=0; i<data->size(); ++i)
21586  {
21587  if (data->at(i).x() > x)
21588  {
21589  if (i>0)
21590  return i-1;
21591  else
21592  return 0;
21593  }
21594  }
21595  return -1;
21596 }
21597 
21606 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
21607 {
21608  for (int i=data->size()-1; i>=0; --i)
21609  {
21610  if (data->at(i).y() < y)
21611  {
21612  if (i<data->size()-1)
21613  return i+1;
21614  else
21615  return data->size()-1;
21616  }
21617  }
21618  return -1;
21619 }
21620 
21632 double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
21633 {
21634  closestData = mDataContainer->constEnd();
21635  if (mDataContainer->isEmpty())
21636  return -1.0;
21637  if (mLineStyle == lsNone && mScatterStyle.isNone())
21638  return -1.0;
21639 
21640  // calculate minimum distances to graph data points and find closestData iterator:
21641  double minDistSqr = (std::numeric_limits<double>::max)();
21642  // determine which key range comes into question, taking selection tolerance around pos into account:
21643  double posKeyMin, posKeyMax, dummy;
21644  pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);
21645  pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy);
21646  if (posKeyMin > posKeyMax)
21647  qSwap(posKeyMin, posKeyMax);
21648  // iterate over found data points and then choose the one with the shortest distance to pos:
21649  QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true);
21650  QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true);
21651  for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it)
21652  {
21653  const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
21654  if (currentDistSqr < minDistSqr)
21655  {
21656  minDistSqr = currentDistSqr;
21657  closestData = it;
21658  }
21659  }
21660 
21661  // calculate distance to graph line if there is one (if so, will probably be smaller than distance to closest data point):
21662  if (mLineStyle != lsNone)
21663  {
21664  // line displayed, calculate distance to line segments:
21665  QVector<QPointF> lineData;
21666  getLines(&lineData, QCPDataRange(0, dataCount()));
21667  QCPVector2D p(pixelPoint);
21668  const int step = mLineStyle==lsImpulse ? 2 : 1; // impulse plot differs from other line styles in that the lineData points are only pairwise connected
21669  for (int i=0; i<lineData.size()-1; i+=step)
21670  {
21671  const double currentDistSqr = p.distanceSquaredToLine(lineData.at(i), lineData.at(i+1));
21672  if (currentDistSqr < minDistSqr)
21673  minDistSqr = currentDistSqr;
21674  }
21675  }
21676 
21677  return qSqrt(minDistSqr);
21678 }
21679 
21688 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
21689 {
21690  for (int i=0; i<data->size(); ++i)
21691  {
21692  if (data->at(i).y() > y)
21693  {
21694  if (i>0)
21695  return i-1;
21696  else
21697  return 0;
21698  }
21699  }
21700  return -1;
21701 }
21702 /* end of 'src/plottables/plottable-graph.cpp' */
21703 
21704 
21705 /* including file 'src/plottables/plottable-curve.cpp', size 63742 */
21706 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
21707 
21711 
21727 /* start documentation of inline functions */
21728 
21779 /* end documentation of inline functions */
21780 
21785  t(0),
21786  key(0),
21787  value(0)
21788 {
21789 }
21790 
21794 QCPCurveData::QCPCurveData(double t, double key, double value) :
21795  t(t),
21796  key(key),
21797  value(value)
21798 {
21799 }
21800 
21801 
21805 
21842 /* start of documentation of inline functions */
21843 
21851 /* end of documentation of inline functions */
21852 
21863 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
21864  QCPAbstractPlottable1D<QCPCurveData>(keyAxis, valueAxis)
21865 {
21866  // modify inherited properties from abstract plottable:
21867  setPen(QPen(Qt::blue, 0));
21868  setBrush(Qt::NoBrush);
21869 
21872  setScatterSkip(0);
21873 }
21874 
21876 {
21877 }
21878 
21894 void QCPCurve::setData(QSharedPointer<QCPCurveDataContainer> data)
21895 {
21896  mDataContainer = data;
21897 }
21898 
21910 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
21911 {
21912  mDataContainer->clear();
21913  addData(t, keys, values, alreadySorted);
21914 }
21915 
21916 
21928 void QCPCurve::setData(const QVector<double> &keys, const QVector<double> &values)
21929 {
21930  mDataContainer->clear();
21931  addData(keys, values);
21932 }
21933 
21942 {
21943  mScatterStyle = style;
21944 }
21945 
21958 {
21959  mScatterSkip = qMax(0, skip);
21960 }
21961 
21970 {
21971  mLineStyle = style;
21972 }
21973 
21986 void QCPCurve::addData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
21987 {
21988  if (t.size() != keys.size() || t.size() != values.size())
21989  qDebug() << Q_FUNC_INFO << "ts, keys and values have different sizes:" << t.size() << keys.size() << values.size();
21990  const int n = qMin(qMin(t.size(), keys.size()), values.size());
21991  QVector<QCPCurveData> tempData(n);
21992  QVector<QCPCurveData>::iterator it = tempData.begin();
21993  const QVector<QCPCurveData>::iterator itEnd = tempData.end();
21994  int i = 0;
21995  while (it != itEnd)
21996  {
21997  it->t = t[i];
21998  it->key = keys[i];
21999  it->value = values[i];
22000  ++it;
22001  ++i;
22002  }
22003  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
22004 }
22005 
22018 void QCPCurve::addData(const QVector<double> &keys, const QVector<double> &values)
22019 {
22020  if (keys.size() != values.size())
22021  qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
22022  const int n = qMin(keys.size(), values.size());
22023  double tStart;
22024  if (!mDataContainer->isEmpty())
22025  tStart = (mDataContainer->constEnd()-1)->t + 1.0;
22026  else
22027  tStart = 0;
22028  QVector<QCPCurveData> tempData(n);
22029  QVector<QCPCurveData>::iterator it = tempData.begin();
22030  const QVector<QCPCurveData>::iterator itEnd = tempData.end();
22031  int i = 0;
22032  while (it != itEnd)
22033  {
22034  it->t = tStart + i;
22035  it->key = keys[i];
22036  it->value = values[i];
22037  ++it;
22038  ++i;
22039  }
22040  mDataContainer->add(tempData, true); // don't modify tempData beyond this to prevent copy on write
22041 }
22042 
22049 void QCPCurve::addData(double t, double key, double value)
22050 {
22051  mDataContainer->add(QCPCurveData(t, key, value));
22052 }
22053 
22064 void QCPCurve::addData(double key, double value)
22065 {
22066  if (!mDataContainer->isEmpty())
22067  mDataContainer->add(QCPCurveData((mDataContainer->constEnd()-1)->t + 1.0, key, value));
22068  else
22069  mDataContainer->add(QCPCurveData(0.0, key, value));
22070 }
22071 
22080 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22081 {
22082  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
22083  return -1;
22084  if (!mKeyAxis || !mValueAxis)
22085  return -1;
22086 
22087  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
22088  {
22089  QCPCurveDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
22090  double result = pointDistance(pos, closestDataPoint);
22091  if (details)
22092  {
22093  int pointIndex = closestDataPoint-mDataContainer->constBegin();
22094  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
22095  }
22096  return result;
22097  } else
22098  return -1;
22099 }
22100 
22101 /* inherits documentation from base class */
22102 QCPRange QCPCurve::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
22103 {
22104  return mDataContainer->keyRange(foundRange, inSignDomain);
22105 }
22106 
22107 /* inherits documentation from base class */
22108 QCPRange QCPCurve::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
22109 {
22110  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
22111 }
22112 
22113 /* inherits documentation from base class */
22115 {
22116  if (mDataContainer->isEmpty()) return;
22117 
22118  // allocate line vector:
22119  QVector<QPointF> lines, scatters;
22120 
22121  // loop over and draw segments of unselected/selected data:
22122  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
22123  getDataSegments(selectedSegments, unselectedSegments);
22124  allSegments << unselectedSegments << selectedSegments;
22125  for (int i=0; i<allSegments.size(); ++i)
22126  {
22127  bool isSelectedSegment = i >= unselectedSegments.size();
22128 
22129  // fill with curve data:
22130  QPen finalCurvePen = mPen; // determine the final pen already here, because the line optimization depends on its stroke width
22131  if (isSelectedSegment && mSelectionDecorator)
22132  finalCurvePen = mSelectionDecorator->pen();
22133 
22134  QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getCurveLines takes care)
22135  getCurveLines(&lines, lineDataRange, finalCurvePen.widthF());
22136 
22137  // check data validity if flag set:
22138  #ifdef QCUSTOMPLOT_CHECK_DATA
22139  for (QCPCurveDataContainer::const_iterator it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
22140  {
22141  if (QCP::isInvalidData(it->t) ||
22142  QCP::isInvalidData(it->key, it->value))
22143  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
22144  }
22145  #endif
22146 
22147  // draw curve fill:
22148  applyFillAntialiasingHint(painter);
22149  if (isSelectedSegment && mSelectionDecorator)
22150  mSelectionDecorator->applyBrush(painter);
22151  else
22152  painter->setBrush(mBrush);
22153  painter->setPen(Qt::NoPen);
22154  if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0)
22155  painter->drawPolygon(QPolygonF(lines));
22156 
22157  // draw curve line:
22158  if (mLineStyle != lsNone)
22159  {
22160  painter->setPen(finalCurvePen);
22161  painter->setBrush(Qt::NoBrush);
22162  drawCurveLine(painter, lines);
22163  }
22164 
22165  // draw scatters:
22166  QCPScatterStyle finalScatterStyle = mScatterStyle;
22167  if (isSelectedSegment && mSelectionDecorator)
22169  if (!finalScatterStyle.isNone())
22170  {
22171  getScatters(&scatters, allSegments.at(i), finalScatterStyle.size());
22172  drawScatterPlot(painter, scatters, finalScatterStyle);
22173  }
22174  }
22175 
22176  // draw other selection decoration that isn't just line/scatter pens and brushes:
22177  if (mSelectionDecorator)
22179 }
22180 
22181 /* inherits documentation from base class */
22182 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
22183 {
22184  // draw fill:
22185  if (mBrush.style() != Qt::NoBrush)
22186  {
22187  applyFillAntialiasingHint(painter);
22188  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
22189  }
22190  // draw line vertically centered:
22191  if (mLineStyle != lsNone)
22192  {
22194  painter->setPen(mPen);
22195  painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
22196  }
22197  // draw scatter symbol:
22198  if (!mScatterStyle.isNone())
22199  {
22201  // scale scatter pixmap if it's too large to fit in legend icon rect:
22202  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
22203  {
22204  QCPScatterStyle scaledStyle(mScatterStyle);
22205  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
22206  scaledStyle.applyTo(painter, mPen);
22207  scaledStyle.drawShape(painter, QRectF(rect).center());
22208  } else
22209  {
22210  mScatterStyle.applyTo(painter, mPen);
22211  mScatterStyle.drawShape(painter, QRectF(rect).center());
22212  }
22213  }
22214 }
22215 
22222 void QCPCurve::drawCurveLine(QCPPainter *painter, const QVector<QPointF> &lines) const
22223 {
22224  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
22225  {
22227  drawPolyline(painter, lines);
22228  }
22229 }
22230 
22238 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &points, const QCPScatterStyle &style) const
22239 {
22240  // draw scatter point symbols:
22242  style.applyTo(painter, mPen);
22243  for (int i=0; i<points.size(); ++i)
22244  if (!qIsNaN(points.at(i).x()) && !qIsNaN(points.at(i).y()))
22245  style.drawShape(painter, points.at(i));
22246 }
22247 
22276 void QCPCurve::getCurveLines(QVector<QPointF> *lines, const QCPDataRange &dataRange, double penWidth) const
22277 {
22278  if (!lines) return;
22279  lines->clear();
22280  QCPAxis *keyAxis = mKeyAxis.data();
22281  QCPAxis *valueAxis = mValueAxis.data();
22282  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
22283 
22284  // add margins to rect to compensate for stroke width
22285  const double strokeMargin = qMax(qreal(1.0), qreal(penWidth*0.75)); // stroke radius + 50% safety
22286  const double keyMin = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*keyAxis->pixelOrientation());
22287  const double keyMax = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*keyAxis->pixelOrientation());
22288  const double valueMin = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)-strokeMargin*valueAxis->pixelOrientation());
22289  const double valueMax = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)+strokeMargin*valueAxis->pixelOrientation());
22290  QCPCurveDataContainer::const_iterator itBegin = mDataContainer->constBegin();
22292  mDataContainer->limitIteratorsToDataRange(itBegin, itEnd, dataRange);
22293  if (itBegin == itEnd)
22294  return;
22296  QCPCurveDataContainer::const_iterator prevIt = itEnd-1;
22297  int prevRegion = getRegion(prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin);
22298  QVector<QPointF> trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right)
22299  while (it != itEnd)
22300  {
22301  const int currentRegion = getRegion(it->key, it->value, keyMin, valueMax, keyMax, valueMin);
22302  if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
22303  {
22304  if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
22305  {
22306  QPointF crossA, crossB;
22307  if (prevRegion == 5) // we're coming from R, so add this point optimized
22308  {
22309  lines->append(getOptimizedPoint(currentRegion, it->key, it->value, prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin));
22310  // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point
22311  *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
22312  } else if (mayTraverse(prevRegion, currentRegion) &&
22313  getTraverse(prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin, crossA, crossB))
22314  {
22315  // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
22316  QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
22317  getTraverseCornerPoints(prevRegion, currentRegion, keyMin, valueMax, keyMax, valueMin, beforeTraverseCornerPoints, afterTraverseCornerPoints);
22318  if (it != itBegin)
22319  {
22320  *lines << beforeTraverseCornerPoints;
22321  lines->append(crossA);
22322  lines->append(crossB);
22323  *lines << afterTraverseCornerPoints;
22324  } else
22325  {
22326  lines->append(crossB);
22327  *lines << afterTraverseCornerPoints;
22328  trailingPoints << beforeTraverseCornerPoints << crossA ;
22329  }
22330  } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s)
22331  {
22332  *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
22333  }
22334  } else // segment does end in R, so we add previous point optimized and this point at original position
22335  {
22336  if (it == itBegin) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end
22337  trailingPoints << getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
22338  else
22339  lines->append(getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin));
22340  lines->append(coordsToPixels(it->key, it->value));
22341  }
22342  } else // region didn't change
22343  {
22344  if (currentRegion == 5) // still in R, keep adding original points
22345  {
22346  lines->append(coordsToPixels(it->key, it->value));
22347  } else // still outside R, no need to add anything
22348  {
22349  // see how this is not doing anything? That's the main optimization...
22350  }
22351  }
22352  prevIt = it;
22353  prevRegion = currentRegion;
22354  ++it;
22355  }
22356  *lines << trailingPoints;
22357 }
22358 
22379 void QCPCurve::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange, double scatterWidth) const
22380 {
22381  if (!scatters) return;
22382  scatters->clear();
22383  QCPAxis *keyAxis = mKeyAxis.data();
22384  QCPAxis *valueAxis = mValueAxis.data();
22385  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
22386 
22389  mDataContainer->limitIteratorsToDataRange(begin, end, dataRange);
22390  if (begin == end)
22391  return;
22392  const int scatterModulo = mScatterSkip+1;
22393  const bool doScatterSkip = mScatterSkip > 0;
22394  int endIndex = end-mDataContainer->constBegin();
22395 
22396  QCPRange keyRange = keyAxis->range();
22397  QCPRange valueRange = valueAxis->range();
22398  // extend range to include width of scatter symbols:
22399  keyRange.lower = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.lower)-scatterWidth*keyAxis->pixelOrientation());
22400  keyRange.upper = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.upper)+scatterWidth*keyAxis->pixelOrientation());
22401  valueRange.lower = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.lower)-scatterWidth*valueAxis->pixelOrientation());
22402  valueRange.upper = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.upper)+scatterWidth*valueAxis->pixelOrientation());
22403 
22405  int itIndex = begin-mDataContainer->constBegin();
22406  while (doScatterSkip && it != end && itIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter
22407  {
22408  ++itIndex;
22409  ++it;
22410  }
22411  if (keyAxis->orientation() == Qt::Vertical)
22412  {
22413  while (it != end)
22414  {
22415  if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))
22416  scatters->append(QPointF(valueAxis->coordToPixel(it->value), keyAxis->coordToPixel(it->key)));
22417 
22418  // advance iterator to next (non-skipped) data point:
22419  if (!doScatterSkip)
22420  ++it;
22421  else
22422  {
22423  itIndex += scatterModulo;
22424  if (itIndex < endIndex) // make sure we didn't jump over end
22425  it += scatterModulo;
22426  else
22427  {
22428  it = end;
22429  itIndex = endIndex;
22430  }
22431  }
22432  }
22433  } else
22434  {
22435  while (it != end)
22436  {
22437  if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))
22438  scatters->append(QPointF(keyAxis->coordToPixel(it->key), valueAxis->coordToPixel(it->value)));
22439 
22440  // advance iterator to next (non-skipped) data point:
22441  if (!doScatterSkip)
22442  ++it;
22443  else
22444  {
22445  itIndex += scatterModulo;
22446  if (itIndex < endIndex) // make sure we didn't jump over end
22447  it += scatterModulo;
22448  else
22449  {
22450  it = end;
22451  itIndex = endIndex;
22452  }
22453  }
22454  }
22455  }
22456 }
22457 
22477 int QCPCurve::getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
22478 {
22479  if (key < keyMin) // region 123
22480  {
22481  if (value > valueMax)
22482  return 1;
22483  else if (value < valueMin)
22484  return 3;
22485  else
22486  return 2;
22487  } else if (key > keyMax) // region 789
22488  {
22489  if (value > valueMax)
22490  return 7;
22491  else if (value < valueMin)
22492  return 9;
22493  else
22494  return 8;
22495  } else // region 456
22496  {
22497  if (value > valueMax)
22498  return 4;
22499  else if (value < valueMin)
22500  return 6;
22501  else
22502  return 5;
22503  }
22504 }
22505 
22521 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
22522 {
22523  // The intersection point interpolation here is done in pixel coordinates, so we don't need to
22524  // differentiate between different axis scale types. Note that the nomenclature
22525  // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be
22526  // different in pixel coordinates (horz/vert key axes, reversed ranges)
22527 
22528  const double keyMinPx = mKeyAxis->coordToPixel(keyMin);
22529  const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);
22530  const double valueMinPx = mValueAxis->coordToPixel(valueMin);
22531  const double valueMaxPx = mValueAxis->coordToPixel(valueMax);
22532  const double otherValuePx = mValueAxis->coordToPixel(otherValue);
22533  const double valuePx = mValueAxis->coordToPixel(value);
22534  const double otherKeyPx = mKeyAxis->coordToPixel(otherKey);
22535  const double keyPx = mKeyAxis->coordToPixel(key);
22536  double intersectKeyPx = keyMinPx; // initial key just a fail-safe
22537  double intersectValuePx = valueMinPx; // initial value just a fail-safe
22538  switch (otherRegion)
22539  {
22540  case 1: // top and left edge
22541  {
22542  intersectValuePx = valueMaxPx;
22543  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22544  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed)
22545  {
22546  intersectKeyPx = keyMinPx;
22547  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22548  }
22549  break;
22550  }
22551  case 2: // left edge
22552  {
22553  intersectKeyPx = keyMinPx;
22554  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22555  break;
22556  }
22557  case 3: // bottom and left edge
22558  {
22559  intersectValuePx = valueMinPx;
22560  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22561  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed)
22562  {
22563  intersectKeyPx = keyMinPx;
22564  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22565  }
22566  break;
22567  }
22568  case 4: // top edge
22569  {
22570  intersectValuePx = valueMaxPx;
22571  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22572  break;
22573  }
22574  case 5:
22575  {
22576  break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
22577  }
22578  case 6: // bottom edge
22579  {
22580  intersectValuePx = valueMinPx;
22581  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22582  break;
22583  }
22584  case 7: // top and right edge
22585  {
22586  intersectValuePx = valueMaxPx;
22587  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22588  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed)
22589  {
22590  intersectKeyPx = keyMaxPx;
22591  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22592  }
22593  break;
22594  }
22595  case 8: // right edge
22596  {
22597  intersectKeyPx = keyMaxPx;
22598  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22599  break;
22600  }
22601  case 9: // bottom and right edge
22602  {
22603  intersectValuePx = valueMinPx;
22604  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22605  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed)
22606  {
22607  intersectKeyPx = keyMaxPx;
22608  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22609  }
22610  break;
22611  }
22612  }
22613  if (mKeyAxis->orientation() == Qt::Horizontal)
22614  return QPointF(intersectKeyPx, intersectValuePx);
22615  else
22616  return QPointF(intersectValuePx, intersectKeyPx);
22617 }
22618 
22637 QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
22638 {
22639  QVector<QPointF> result;
22640  switch (prevRegion)
22641  {
22642  case 1:
22643  {
22644  switch (currentRegion)
22645  {
22646  case 2: { result << coordsToPixels(keyMin, valueMax); break; }
22647  case 4: { result << coordsToPixels(keyMin, valueMax); break; }
22648  case 3: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); break; }
22649  case 7: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); break; }
22650  case 6: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22651  case 8: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22652  case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
22653  if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R
22654  { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }
22655  else
22656  { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }
22657  break;
22658  }
22659  }
22660  break;
22661  }
22662  case 2:
22663  {
22664  switch (currentRegion)
22665  {
22666  case 1: { result << coordsToPixels(keyMin, valueMax); break; }
22667  case 3: { result << coordsToPixels(keyMin, valueMin); break; }
22668  case 4: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22669  case 6: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22670  case 7: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }
22671  case 9: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }
22672  }
22673  break;
22674  }
22675  case 3:
22676  {
22677  switch (currentRegion)
22678  {
22679  case 2: { result << coordsToPixels(keyMin, valueMin); break; }
22680  case 6: { result << coordsToPixels(keyMin, valueMin); break; }
22681  case 1: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); break; }
22682  case 9: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); break; }
22683  case 4: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22684  case 8: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22685  case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
22686  if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R
22687  { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }
22688  else
22689  { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }
22690  break;
22691  }
22692  }
22693  break;
22694  }
22695  case 4:
22696  {
22697  switch (currentRegion)
22698  {
22699  case 1: { result << coordsToPixels(keyMin, valueMax); break; }
22700  case 7: { result << coordsToPixels(keyMax, valueMax); break; }
22701  case 2: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22702  case 8: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22703  case 3: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }
22704  case 9: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }
22705  }
22706  break;
22707  }
22708  case 5:
22709  {
22710  switch (currentRegion)
22711  {
22712  case 1: { result << coordsToPixels(keyMin, valueMax); break; }
22713  case 7: { result << coordsToPixels(keyMax, valueMax); break; }
22714  case 9: { result << coordsToPixels(keyMax, valueMin); break; }
22715  case 3: { result << coordsToPixels(keyMin, valueMin); break; }
22716  }
22717  break;
22718  }
22719  case 6:
22720  {
22721  switch (currentRegion)
22722  {
22723  case 3: { result << coordsToPixels(keyMin, valueMin); break; }
22724  case 9: { result << coordsToPixels(keyMax, valueMin); break; }
22725  case 2: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22726  case 8: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22727  case 1: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }
22728  case 7: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }
22729  }
22730  break;
22731  }
22732  case 7:
22733  {
22734  switch (currentRegion)
22735  {
22736  case 4: { result << coordsToPixels(keyMax, valueMax); break; }
22737  case 8: { result << coordsToPixels(keyMax, valueMax); break; }
22738  case 1: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); break; }
22739  case 9: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); break; }
22740  case 2: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22741  case 6: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22742  case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
22743  if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R
22744  { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }
22745  else
22746  { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }
22747  break;
22748  }
22749  }
22750  break;
22751  }
22752  case 8:
22753  {
22754  switch (currentRegion)
22755  {
22756  case 7: { result << coordsToPixels(keyMax, valueMax); break; }
22757  case 9: { result << coordsToPixels(keyMax, valueMin); break; }
22758  case 4: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22759  case 6: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22760  case 1: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }
22761  case 3: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }
22762  }
22763  break;
22764  }
22765  case 9:
22766  {
22767  switch (currentRegion)
22768  {
22769  case 6: { result << coordsToPixels(keyMax, valueMin); break; }
22770  case 8: { result << coordsToPixels(keyMax, valueMin); break; }
22771  case 3: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); break; }
22772  case 7: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); break; }
22773  case 2: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22774  case 4: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22775  case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
22776  if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R
22777  { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }
22778  else
22779  { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }
22780  break;
22781  }
22782  }
22783  break;
22784  }
22785  }
22786  return result;
22787 }
22788 
22801 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
22802 {
22803  switch (prevRegion)
22804  {
22805  case 1:
22806  {
22807  switch (currentRegion)
22808  {
22809  case 4:
22810  case 7:
22811  case 2:
22812  case 3: return false;
22813  default: return true;
22814  }
22815  }
22816  case 2:
22817  {
22818  switch (currentRegion)
22819  {
22820  case 1:
22821  case 3: return false;
22822  default: return true;
22823  }
22824  }
22825  case 3:
22826  {
22827  switch (currentRegion)
22828  {
22829  case 1:
22830  case 2:
22831  case 6:
22832  case 9: return false;
22833  default: return true;
22834  }
22835  }
22836  case 4:
22837  {
22838  switch (currentRegion)
22839  {
22840  case 1:
22841  case 7: return false;
22842  default: return true;
22843  }
22844  }
22845  case 5: return false; // should never occur
22846  case 6:
22847  {
22848  switch (currentRegion)
22849  {
22850  case 3:
22851  case 9: return false;
22852  default: return true;
22853  }
22854  }
22855  case 7:
22856  {
22857  switch (currentRegion)
22858  {
22859  case 1:
22860  case 4:
22861  case 8:
22862  case 9: return false;
22863  default: return true;
22864  }
22865  }
22866  case 8:
22867  {
22868  switch (currentRegion)
22869  {
22870  case 7:
22871  case 9: return false;
22872  default: return true;
22873  }
22874  }
22875  case 9:
22876  {
22877  switch (currentRegion)
22878  {
22879  case 3:
22880  case 6:
22881  case 8:
22882  case 7: return false;
22883  default: return true;
22884  }
22885  }
22886  default: return true;
22887  }
22888 }
22889 
22890 
22904 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const
22905 {
22906  // The intersection point interpolation here is done in pixel coordinates, so we don't need to
22907  // differentiate between different axis scale types. Note that the nomenclature
22908  // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be
22909  // different in pixel coordinates (horz/vert key axes, reversed ranges)
22910 
22911  QList<QPointF> intersections;
22912  const double valueMinPx = mValueAxis->coordToPixel(valueMin);
22913  const double valueMaxPx = mValueAxis->coordToPixel(valueMax);
22914  const double keyMinPx = mKeyAxis->coordToPixel(keyMin);
22915  const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);
22916  const double keyPx = mKeyAxis->coordToPixel(key);
22917  const double valuePx = mValueAxis->coordToPixel(value);
22918  const double prevKeyPx = mKeyAxis->coordToPixel(prevKey);
22919  const double prevValuePx = mValueAxis->coordToPixel(prevValue);
22920  if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
22921  {
22922  // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here
22923  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMinPx) : QPointF(valueMinPx, keyPx)); // direction will be taken care of at end of method
22924  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMaxPx) : QPointF(valueMaxPx, keyPx));
22925  } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
22926  {
22927  // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here
22928  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, valuePx) : QPointF(valuePx, keyMinPx)); // direction will be taken care of at end of method
22929  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, valuePx) : QPointF(valuePx, keyMaxPx));
22930  } else // line is skewed
22931  {
22932  double gamma;
22933  double keyPerValuePx = (keyPx-prevKeyPx)/(valuePx-prevValuePx);
22934  // check top of rect:
22935  gamma = prevKeyPx + (valueMaxPx-prevValuePx)*keyPerValuePx;
22936  if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed
22937  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMaxPx) : QPointF(valueMaxPx, gamma));
22938  // check bottom of rect:
22939  gamma = prevKeyPx + (valueMinPx-prevValuePx)*keyPerValuePx;
22940  if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed
22941  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMinPx) : QPointF(valueMinPx, gamma));
22942  const double valuePerKeyPx = 1.0/keyPerValuePx;
22943  // check left of rect:
22944  gamma = prevValuePx + (keyMinPx-prevKeyPx)*valuePerKeyPx;
22945  if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed
22946  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, gamma) : QPointF(gamma, keyMinPx));
22947  // check right of rect:
22948  gamma = prevValuePx + (keyMaxPx-prevKeyPx)*valuePerKeyPx;
22949  if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed
22950  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, gamma) : QPointF(gamma, keyMaxPx));
22951  }
22952 
22953  // handle cases where found points isn't exactly 2:
22954  if (intersections.size() > 2)
22955  {
22956  // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
22957  double distSqrMax = 0;
22958  QPointF pv1, pv2;
22959  for (int i=0; i<intersections.size()-1; ++i)
22960  {
22961  for (int k=i+1; k<intersections.size(); ++k)
22962  {
22963  QPointF distPoint = intersections.at(i)-intersections.at(k);
22964  double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
22965  if (distSqr > distSqrMax)
22966  {
22967  pv1 = intersections.at(i);
22968  pv2 = intersections.at(k);
22969  distSqrMax = distSqr;
22970  }
22971  }
22972  }
22973  intersections = QList<QPointF>() << pv1 << pv2;
22974  } else if (intersections.size() != 2)
22975  {
22976  // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
22977  return false;
22978  }
22979 
22980  // possibly re-sort points so optimized point segment has same direction as original segment:
22981  double xDelta = keyPx-prevKeyPx;
22982  double yDelta = valuePx-prevValuePx;
22983  if (mKeyAxis->orientation() != Qt::Horizontal)
22984  qSwap(xDelta, yDelta);
22985  if (xDelta*(intersections.at(1).x()-intersections.at(0).x()) + yDelta*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction
22986  intersections.move(0, 1);
22987  crossA = intersections.at(0);
22988  crossB = intersections.at(1);
22989  return true;
22990 }
22991 
23017 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
23018 {
23019  switch (prevRegion)
23020  {
23021  case 1:
23022  {
23023  switch (currentRegion)
23024  {
23025  case 6: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }
23026  case 9: { beforeTraverse << coordsToPixels(keyMin, valueMax); afterTraverse << coordsToPixels(keyMax, valueMin); break; }
23027  case 8: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }
23028  }
23029  break;
23030  }
23031  case 2:
23032  {
23033  switch (currentRegion)
23034  {
23035  case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }
23036  case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }
23037  }
23038  break;
23039  }
23040  case 3:
23041  {
23042  switch (currentRegion)
23043  {
23044  case 4: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }
23045  case 7: { beforeTraverse << coordsToPixels(keyMin, valueMin); afterTraverse << coordsToPixels(keyMax, valueMax); break; }
23046  case 8: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }
23047  }
23048  break;
23049  }
23050  case 4:
23051  {
23052  switch (currentRegion)
23053  {
23054  case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }
23055  case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }
23056  }
23057  break;
23058  }
23059  case 5: { break; } // shouldn't happen because this method only handles full traverses
23060  case 6:
23061  {
23062  switch (currentRegion)
23063  {
23064  case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }
23065  case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }
23066  }
23067  break;
23068  }
23069  case 7:
23070  {
23071  switch (currentRegion)
23072  {
23073  case 2: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }
23074  case 3: { beforeTraverse << coordsToPixels(keyMax, valueMax); afterTraverse << coordsToPixels(keyMin, valueMin); break; }
23075  case 6: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }
23076  }
23077  break;
23078  }
23079  case 8:
23080  {
23081  switch (currentRegion)
23082  {
23083  case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }
23084  case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }
23085  }
23086  break;
23087  }
23088  case 9:
23089  {
23090  switch (currentRegion)
23091  {
23092  case 2: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }
23093  case 1: { beforeTraverse << coordsToPixels(keyMax, valueMin); afterTraverse << coordsToPixels(keyMin, valueMax); break; }
23094  case 4: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }
23095  }
23096  break;
23097  }
23098  }
23099 }
23100 
23113 double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const
23114 {
23115  closestData = mDataContainer->constEnd();
23116  if (mDataContainer->isEmpty())
23117  return -1.0;
23118  if (mLineStyle == lsNone && mScatterStyle.isNone())
23119  return -1.0;
23120 
23121  if (mDataContainer->size() == 1)
23122  {
23123  QPointF dataPoint = coordsToPixels(mDataContainer->constBegin()->key, mDataContainer->constBegin()->value);
23124  closestData = mDataContainer->constBegin();
23125  return QCPVector2D(dataPoint-pixelPoint).length();
23126  }
23127 
23128  // calculate minimum distances to curve data points and find closestData iterator:
23129  double minDistSqr = (std::numeric_limits<double>::max)();
23130  // iterate over found data points and then choose the one with the shortest distance to pos:
23133  for (QCPCurveDataContainer::const_iterator it=begin; it!=end; ++it)
23134  {
23135  const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
23136  if (currentDistSqr < minDistSqr)
23137  {
23138  minDistSqr = currentDistSqr;
23139  closestData = it;
23140  }
23141  }
23142 
23143  // calculate distance to line if there is one (if so, will probably be smaller than distance to closest data point):
23144  if (mLineStyle != lsNone)
23145  {
23146  QVector<QPointF> lines;
23147  getCurveLines(&lines, QCPDataRange(0, dataCount()), mParentPlot->selectionTolerance()*1.2); // optimized lines outside axis rect shouldn't respond to clicks at the edge, so use 1.2*tolerance as pen width
23148  for (int i=0; i<lines.size()-1; ++i)
23149  {
23150  double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(lines.at(i), lines.at(i+1));
23151  if (currentDistSqr < minDistSqr)
23152  minDistSqr = currentDistSqr;
23153  }
23154  }
23155 
23156  return qSqrt(minDistSqr);
23157 }
23158 /* end of 'src/plottables/plottable-curve.cpp' */
23159 
23160 
23161 /* including file 'src/plottables/plottable-bars.cpp', size 43725 */
23162 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
23163 
23164 
23168 
23203 /* start of documentation of inline functions */
23204 
23231 /* end of documentation of inline functions */
23232 
23237  QObject(parentPlot),
23238  mParentPlot(parentPlot),
23239  mSpacingType(stAbsolute),
23240  mSpacing(4)
23241 {
23242 }
23243 
23245 {
23246  clear();
23247 }
23248 
23257 {
23259 }
23260 
23268 {
23269  mSpacing = spacing;
23270 }
23271 
23278 QCPBars *QCPBarsGroup::bars(int index) const
23279 {
23280  if (index >= 0 && index < mBars.size())
23281  {
23282  return mBars.at(index);
23283  } else
23284  {
23285  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
23286  return 0;
23287  }
23288 }
23289 
23296 {
23297  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
23298  bars->setBarsGroup(0); // removes itself via removeBars
23299 }
23300 
23308 {
23309  if (!bars)
23310  {
23311  qDebug() << Q_FUNC_INFO << "bars is 0";
23312  return;
23313  }
23314 
23315  if (!mBars.contains(bars))
23316  bars->setBarsGroup(this);
23317  else
23318  qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
23319 }
23320 
23331 {
23332  if (!bars)
23333  {
23334  qDebug() << Q_FUNC_INFO << "bars is 0";
23335  return;
23336  }
23337 
23338  // first append to bars list normally:
23339  if (!mBars.contains(bars))
23340  bars->setBarsGroup(this);
23341  // then move to according position:
23342  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
23343 }
23344 
23351 {
23352  if (!bars)
23353  {
23354  qDebug() << Q_FUNC_INFO << "bars is 0";
23355  return;
23356  }
23357 
23358  if (mBars.contains(bars))
23359  bars->setBarsGroup(0);
23360  else
23361  qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
23362 }
23363 
23372 {
23373  if (!mBars.contains(bars))
23374  mBars.append(bars);
23375 }
23376 
23385 {
23386  mBars.removeOne(bars);
23387 }
23388 
23395 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
23396 {
23397  // find list of all base bars in case some mBars are stacked:
23398  QList<const QCPBars*> baseBars;
23399  foreach (const QCPBars *b, mBars)
23400  {
23401  while (b->barBelow())
23402  b = b->barBelow();
23403  if (!baseBars.contains(b))
23404  baseBars.append(b);
23405  }
23406  // find base bar this "bars" is stacked on:
23407  const QCPBars *thisBase = bars;
23408  while (thisBase->barBelow())
23409  thisBase = thisBase->barBelow();
23410 
23411  // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
23412  double result = 0;
23413  int index = baseBars.indexOf(thisBase);
23414  if (index >= 0)
23415  {
23416  if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
23417  {
23418  return result;
23419  } else
23420  {
23421  double lowerPixelWidth, upperPixelWidth;
23422  int startIndex;
23423  int dir = (index <= (baseBars.size()-1)/2) ? -1 : 1; // if bar is to lower keys of center, dir is negative
23424  if (baseBars.size() % 2 == 0) // even number of bars
23425  {
23426  startIndex = baseBars.size()/2 + (dir < 0 ? -1 : 0);
23427  result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
23428  } else // uneven number of bars
23429  {
23430  startIndex = (baseBars.size()-1)/2+dir;
23431  baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
23432  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
23433  result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
23434  }
23435  for (int i = startIndex; i != index; i += dir) // add widths and spacings of bars in between center and our bars
23436  {
23437  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
23438  result += qAbs(upperPixelWidth-lowerPixelWidth);
23439  result += getPixelSpacing(baseBars.at(i), keyCoord);
23440  }
23441  // finally half of our bars width:
23442  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
23443  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
23444  // correct sign of result depending on orientation and direction of key axis:
23445  result *= dir*thisBase->keyAxis()->pixelOrientation();
23446  }
23447  }
23448  return result;
23449 }
23450 
23461 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
23462 {
23463  switch (mSpacingType)
23464  {
23465  case stAbsolute:
23466  {
23467  return mSpacing;
23468  }
23469  case stAxisRectRatio:
23470  {
23471  if (bars->keyAxis()->orientation() == Qt::Horizontal)
23472  return bars->keyAxis()->axisRect()->width()*mSpacing;
23473  else
23474  return bars->keyAxis()->axisRect()->height()*mSpacing;
23475  }
23476  case stPlotCoords:
23477  {
23478  double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
23479  return qAbs(bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel);
23480  }
23481  }
23482  return 0;
23483 }
23484 
23485 
23489 
23504 /* start documentation of inline functions */
23505 
23555 /* end documentation of inline functions */
23556 
23561  key(0),
23562  value(0)
23563 {
23564 }
23565 
23570  key(key),
23571  value(value)
23572 {
23573 }
23574 
23575 
23579 
23615 /* start of documentation of inline functions */
23616 
23638 /* end of documentation of inline functions */
23639 
23650 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
23651  QCPAbstractPlottable1D<QCPBarsData>(keyAxis, valueAxis),
23652  mWidth(0.75),
23653  mWidthType(wtPlotCoords),
23654  mBarsGroup(0),
23655  mBaseValue(0),
23656  mStackingGap(0)
23657 {
23658  // modify inherited properties from abstract plottable:
23659  mPen.setColor(Qt::blue);
23660  mPen.setStyle(Qt::SolidLine);
23661  mBrush.setColor(QColor(40, 50, 255, 30));
23662  mBrush.setStyle(Qt::SolidPattern);
23663  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
23664 }
23665 
23667 {
23668  setBarsGroup(0);
23669  if (mBarBelow || mBarAbove)
23670  connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
23671 }
23672 
23688 void QCPBars::setData(QSharedPointer<QCPBarsDataContainer> data)
23689 {
23690  mDataContainer = data;
23691 }
23692 
23704 void QCPBars::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
23705 {
23706  mDataContainer->clear();
23707  addData(keys, values, alreadySorted);
23708 }
23709 
23717 {
23718  mWidth = width;
23719 }
23720 
23730 {
23732 }
23733 
23741 {
23742  // deregister at old group:
23743  if (mBarsGroup)
23744  mBarsGroup->unregisterBars(this);
23746  // register at new group:
23747  if (mBarsGroup)
23748  mBarsGroup->registerBars(this);
23749 }
23750 
23764 {
23766 }
23767 
23773 void QCPBars::setStackingGap(double pixels)
23774 {
23775  mStackingGap = pixels;
23776 }
23777 
23790 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
23791 {
23792  if (keys.size() != values.size())
23793  qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
23794  const int n = qMin(keys.size(), values.size());
23795  QVector<QCPBarsData> tempData(n);
23796  QVector<QCPBarsData>::iterator it = tempData.begin();
23797  const QVector<QCPBarsData>::iterator itEnd = tempData.end();
23798  int i = 0;
23799  while (it != itEnd)
23800  {
23801  it->key = keys[i];
23802  it->value = values[i];
23803  ++it;
23804  ++i;
23805  }
23806  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
23807 }
23808 
23815 void QCPBars::addData(double key, double value)
23816 {
23817  mDataContainer->add(QCPBarsData(key, value));
23818 }
23819 
23835 {
23836  if (bars == this) return;
23837  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
23838  {
23839  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
23840  return;
23841  }
23842  // remove from stacking:
23843  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
23844  // if new bar given, insert this bar below it:
23845  if (bars)
23846  {
23847  if (bars->mBarBelow)
23848  connectBars(bars->mBarBelow.data(), this);
23849  connectBars(this, bars);
23850  }
23851 }
23852 
23868 {
23869  if (bars == this) return;
23870  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
23871  {
23872  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
23873  return;
23874  }
23875  // remove from stacking:
23876  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
23877  // if new bar given, insert this bar above it:
23878  if (bars)
23879  {
23880  if (bars->mBarAbove)
23881  connectBars(this, bars->mBarAbove.data());
23882  connectBars(bars, this);
23883  }
23884 }
23885 
23889 QCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable) const
23890 {
23891  QCPDataSelection result;
23892  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
23893  return result;
23894  if (!mKeyAxis || !mValueAxis)
23895  return result;
23896 
23897  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
23898  getVisibleDataBounds(visibleBegin, visibleEnd);
23899 
23900  for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
23901  {
23902  if (rect.intersects(getBarRect(it->key, it->value)))
23903  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
23904  }
23905  result.simplify();
23906  return result;
23907 }
23908 
23917 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23918 {
23919  Q_UNUSED(details)
23920  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
23921  return -1;
23922  if (!mKeyAxis || !mValueAxis)
23923  return -1;
23924 
23925  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
23926  {
23927  // get visible data range:
23928  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
23929  getVisibleDataBounds(visibleBegin, visibleEnd);
23930  for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
23931  {
23932  if (getBarRect(it->key, it->value).contains(pos))
23933  {
23934  if (details)
23935  {
23936  int pointIndex = it-mDataContainer->constBegin();
23937  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
23938  }
23939  return mParentPlot->selectionTolerance()*0.99;
23940  }
23941  }
23942  }
23943  return -1;
23944 }
23945 
23946 /* inherits documentation from base class */
23947 QCPRange QCPBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
23948 {
23949  /* Note: If this QCPBars uses absolute pixels as width (or is in a QCPBarsGroup with spacing in
23950  absolute pixels), using this method to adapt the key axis range to fit the bars into the
23951  currently visible axis range will not work perfectly. Because in the moment the axis range is
23952  changed to the new range, the fixed pixel widths/spacings will represent different coordinate
23953  spans than before, which in turn would require a different key range to perfectly fit, and so on.
23954  The only solution would be to iteratively approach the perfect fitting axis range, but the
23955  mismatch isn't large enough in most applications, to warrant this here. If a user does need a
23956  better fit, he should call the corresponding axis rescale multiple times in a row.
23957  */
23958  QCPRange range;
23959  range = mDataContainer->keyRange(foundRange, inSignDomain);
23960 
23961  // determine exact range of bars by including bar width and barsgroup offset:
23962  if (foundRange && mKeyAxis)
23963  {
23964  double lowerPixelWidth, upperPixelWidth, keyPixel;
23965  // lower range bound:
23966  getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
23967  keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
23968  if (mBarsGroup)
23969  keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
23970  const double lowerCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
23971  if (!qIsNaN(lowerCorrected) && qIsFinite(lowerCorrected) && range.lower > lowerCorrected)
23972  range.lower = lowerCorrected;
23973  // upper range bound:
23974  getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
23975  keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
23976  if (mBarsGroup)
23977  keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
23978  const double upperCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
23979  if (!qIsNaN(upperCorrected) && qIsFinite(upperCorrected) && range.upper < upperCorrected)
23980  range.upper = upperCorrected;
23981  }
23982  return range;
23983 }
23984 
23985 /* inherits documentation from base class */
23986 QCPRange QCPBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
23987 {
23988  // Note: can't simply use mDataContainer->valueRange here because we need to
23989  // take into account bar base value and possible stacking of multiple bars
23990  QCPRange range;
23991  range.lower = mBaseValue;
23992  range.upper = mBaseValue;
23993  bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
23994  bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
23995  QCPBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();
23997  if (inKeyRange != QCPRange())
23998  {
23999  itBegin = mDataContainer->findBegin(inKeyRange.lower);
24000  itEnd = mDataContainer->findEnd(inKeyRange.upper);
24001  }
24002  for (QCPBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
24003  {
24004  const double current = it->value + getStackedBaseValue(it->key, it->value >= 0);
24005  if (qIsNaN(current)) continue;
24006  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
24007  {
24008  if (current < range.lower || !haveLower)
24009  {
24010  range.lower = current;
24011  haveLower = true;
24012  }
24013  if (current > range.upper || !haveUpper)
24014  {
24015  range.upper = current;
24016  haveUpper = true;
24017  }
24018  }
24019  }
24020 
24021  foundRange = true; // return true because bar charts always have the 0-line visible
24022  return range;
24023 }
24024 
24025 /* inherits documentation from base class */
24026 QPointF QCPBars::dataPixelPosition(int index) const
24027 {
24028  if (index >= 0 && index < mDataContainer->size())
24029  {
24030  QCPAxis *keyAxis = mKeyAxis.data();
24031  QCPAxis *valueAxis = mValueAxis.data();
24032  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
24033 
24034  const QCPDataContainer<QCPBarsData>::const_iterator it = mDataContainer->constBegin()+index;
24035  const double valuePixel = valueAxis->coordToPixel(getStackedBaseValue(it->key, it->value >= 0) + it->value);
24036  const double keyPixel = keyAxis->coordToPixel(it->key) + (mBarsGroup ? mBarsGroup->keyPixelOffset(this, it->key) : 0);
24037  if (keyAxis->orientation() == Qt::Horizontal)
24038  return QPointF(keyPixel, valuePixel);
24039  else
24040  return QPointF(valuePixel, keyPixel);
24041  } else
24042  {
24043  qDebug() << Q_FUNC_INFO << "Index out of bounds" << index;
24044  return QPointF();
24045  }
24046 }
24047 
24048 /* inherits documentation from base class */
24050 {
24051  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
24052  if (mDataContainer->isEmpty()) return;
24053 
24054  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
24055  getVisibleDataBounds(visibleBegin, visibleEnd);
24056 
24057  // loop over and draw segments of unselected/selected data:
24058  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
24059  getDataSegments(selectedSegments, unselectedSegments);
24060  allSegments << unselectedSegments << selectedSegments;
24061  for (int i=0; i<allSegments.size(); ++i)
24062  {
24063  bool isSelectedSegment = i >= unselectedSegments.size();
24064  QCPBarsDataContainer::const_iterator begin = visibleBegin;
24065  QCPBarsDataContainer::const_iterator end = visibleEnd;
24066  mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
24067  if (begin == end)
24068  continue;
24069 
24070  for (QCPBarsDataContainer::const_iterator it=begin; it!=end; ++it)
24071  {
24072  // check data validity if flag set:
24073 #ifdef QCUSTOMPLOT_CHECK_DATA
24074  if (QCP::isInvalidData(it->key, it->value))
24075  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
24076 #endif
24077  // draw bar:
24078  if (isSelectedSegment && mSelectionDecorator)
24079  {
24080  mSelectionDecorator->applyBrush(painter);
24081  mSelectionDecorator->applyPen(painter);
24082  } else
24083  {
24084  painter->setBrush(mBrush);
24085  painter->setPen(mPen);
24086  }
24088  painter->drawPolygon(getBarRect(it->key, it->value));
24089  }
24090  }
24091 
24092  // draw other selection decoration that isn't just line/scatter pens and brushes:
24093  if (mSelectionDecorator)
24095 }
24096 
24097 /* inherits documentation from base class */
24098 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
24099 {
24100  // draw filled rect:
24102  painter->setBrush(mBrush);
24103  painter->setPen(mPen);
24104  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
24105  r.moveCenter(rect.center());
24106  painter->drawRect(r);
24107 }
24108 
24124 {
24125  if (!mKeyAxis)
24126  {
24127  qDebug() << Q_FUNC_INFO << "invalid key axis";
24128  begin = mDataContainer->constEnd();
24129  end = mDataContainer->constEnd();
24130  return;
24131  }
24132  if (mDataContainer->isEmpty())
24133  {
24134  begin = mDataContainer->constEnd();
24135  end = mDataContainer->constEnd();
24136  return;
24137  }
24138 
24139  // get visible data range as QMap iterators
24140  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower);
24141  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper);
24142  double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
24143  double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
24144  bool isVisible = false;
24145  // walk left from begin to find lower bar that actually is completely outside visible pixel range:
24147  while (it != mDataContainer->constBegin())
24148  {
24149  --it;
24150  const QRectF barRect = getBarRect(it->key, it->value);
24151  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
24152  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.left() <= lowerPixelBound));
24153  else // keyaxis is vertical
24154  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.bottom() >= lowerPixelBound));
24155  if (isVisible)
24156  begin = it;
24157  else
24158  break;
24159  }
24160  // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
24161  it = end;
24162  while (it != mDataContainer->constEnd())
24163  {
24164  const QRectF barRect = getBarRect(it->key, it->value);
24165  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
24166  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.right() >= upperPixelBound));
24167  else // keyaxis is vertical
24168  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.top() <= upperPixelBound));
24169  if (isVisible)
24170  end = it+1;
24171  else
24172  break;
24173  ++it;
24174  }
24175 }
24176 
24183 QRectF QCPBars::getBarRect(double key, double value) const
24184 {
24185  QCPAxis *keyAxis = mKeyAxis.data();
24186  QCPAxis *valueAxis = mValueAxis.data();
24187  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); }
24188 
24189  double lowerPixelWidth, upperPixelWidth;
24190  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
24191  double base = getStackedBaseValue(key, value >= 0);
24192  double basePixel = valueAxis->coordToPixel(base);
24193  double valuePixel = valueAxis->coordToPixel(base+value);
24194  double keyPixel = keyAxis->coordToPixel(key);
24195  if (mBarsGroup)
24196  keyPixel += mBarsGroup->keyPixelOffset(this, key);
24197  double bottomOffset = (mBarBelow && mPen != Qt::NoPen ? 1 : 0)*(mPen.isCosmetic() ? 1 : mPen.widthF());
24198  bottomOffset += mBarBelow ? mStackingGap : 0;
24199  bottomOffset *= (value<0 ? -1 : 1)*valueAxis->pixelOrientation();
24200  if (qAbs(valuePixel-basePixel) <= qAbs(bottomOffset))
24201  bottomOffset = valuePixel-basePixel;
24202  if (keyAxis->orientation() == Qt::Horizontal)
24203  {
24204  return QRectF(QPointF(keyPixel+lowerPixelWidth, valuePixel), QPointF(keyPixel+upperPixelWidth, basePixel+bottomOffset)).normalized();
24205  } else
24206  {
24207  return QRectF(QPointF(basePixel+bottomOffset, keyPixel+lowerPixelWidth), QPointF(valuePixel, keyPixel+upperPixelWidth)).normalized();
24208  }
24209 }
24210 
24220 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
24221 {
24222  lower = 0;
24223  upper = 0;
24224  switch (mWidthType)
24225  {
24226  case wtAbsolute:
24227  {
24228  upper = mWidth*0.5*mKeyAxis.data()->pixelOrientation();
24229  lower = -upper;
24230  break;
24231  }
24232  case wtAxisRectRatio:
24233  {
24234  if (mKeyAxis && mKeyAxis.data()->axisRect())
24235  {
24236  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
24237  upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
24238  else
24239  upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
24240  lower = -upper;
24241  } else
24242  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
24243  break;
24244  }
24245  case wtPlotCoords:
24246  {
24247  if (mKeyAxis)
24248  {
24249  double keyPixel = mKeyAxis.data()->coordToPixel(key);
24250  upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
24251  lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
24252  // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
24253  // coordinate transform which includes range direction
24254  } else
24255  qDebug() << Q_FUNC_INFO << "No key axis defined";
24256  break;
24257  }
24258  }
24259 }
24260 
24270 double QCPBars::getStackedBaseValue(double key, bool positive) const
24271 {
24272  if (mBarBelow)
24273  {
24274  double max = 0; // don't initialize with mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
24275  // find bars of mBarBelow that are approximately at key and find largest one:
24276  double epsilon = qAbs(key)*(sizeof(key)==4 ? 1e-6 : 1e-14); // should be safe even when changed to use float at some point
24277  if (key == 0)
24278  epsilon = (sizeof(key)==4 ? 1e-6 : 1e-14);
24279  QCPBarsDataContainer::const_iterator it = mBarBelow.data()->mDataContainer->findBegin(key-epsilon);
24280  QCPBarsDataContainer::const_iterator itEnd = mBarBelow.data()->mDataContainer->findEnd(key+epsilon);
24281  while (it != itEnd)
24282  {
24283  if (it->key > key-epsilon && it->key < key+epsilon)
24284  {
24285  if ((positive && it->value > max) ||
24286  (!positive && it->value < max))
24287  max = it->value;
24288  }
24289  ++it;
24290  }
24291  // recurse down the bar-stack to find the total height:
24292  return max + mBarBelow.data()->getStackedBaseValue(key, positive);
24293  } else
24294  return mBaseValue;
24295 }
24296 
24306 {
24307  if (!lower && !upper) return;
24308 
24309  if (!lower) // disconnect upper at bottom
24310  {
24311  // disconnect old bar below upper:
24312  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
24313  upper->mBarBelow.data()->mBarAbove = 0;
24314  upper->mBarBelow = 0;
24315  } else if (!upper) // disconnect lower at top
24316  {
24317  // disconnect old bar above lower:
24318  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
24319  lower->mBarAbove.data()->mBarBelow = 0;
24320  lower->mBarAbove = 0;
24321  } else // connect lower and upper
24322  {
24323  // disconnect old bar above lower:
24324  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
24325  lower->mBarAbove.data()->mBarBelow = 0;
24326  // disconnect old bar below upper:
24327  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
24328  upper->mBarBelow.data()->mBarAbove = 0;
24329  lower->mBarAbove = upper;
24330  upper->mBarBelow = lower;
24331  }
24332 }
24333 /* end of 'src/plottables/plottable-bars.cpp' */
24334 
24335 
24336 /* including file 'src/plottables/plottable-statisticalbox.cpp', size 28837 */
24337 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
24338 
24342 
24378 /* start documentation of inline functions */
24379 
24430 /* end documentation of inline functions */
24431 
24436  key(0),
24437  minimum(0),
24438  lowerQuartile(0),
24439  median(0),
24440  upperQuartile(0),
24441  maximum(0)
24442 {
24443 }
24444 
24449 QCPStatisticalBoxData::QCPStatisticalBoxData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers) :
24450  key(key),
24451  minimum(minimum),
24452  lowerQuartile(lowerQuartile),
24453  median(median),
24454  upperQuartile(upperQuartile),
24455  maximum(maximum),
24456  outliers(outliers)
24457 {
24458 }
24459 
24460 
24464 
24513 /* start documentation of inline functions */
24514 
24522 /* end documentation of inline functions */
24523 
24535  QCPAbstractPlottable1D<QCPStatisticalBoxData>(keyAxis, valueAxis),
24536  mWidth(0.5),
24537  mWhiskerWidth(0.2),
24538  mWhiskerPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap),
24539  mWhiskerBarPen(Qt::black),
24540  mWhiskerAntialiased(false),
24541  mMedianPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap),
24542  mOutlierStyle(QCPScatterStyle::ssCircle, Qt::blue, 6)
24543 {
24544  setPen(QPen(Qt::black));
24545  setBrush(Qt::NoBrush);
24546 }
24547 
24564 void QCPStatisticalBox::setData(QSharedPointer<QCPStatisticalBoxDataContainer> data)
24565 {
24566  mDataContainer = data;
24567 }
24579 void QCPStatisticalBox::setData(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)
24580 {
24581  mDataContainer->clear();
24582  addData(keys, minimum, lowerQuartile, median, upperQuartile, maximum, alreadySorted);
24583 }
24584 
24591 {
24592  mWidth = width;
24593 }
24594 
24604 {
24605  mWhiskerWidth = width;
24606 }
24607 
24620 {
24621  mWhiskerPen = pen;
24622 }
24623 
24634 {
24635  mWhiskerBarPen = pen;
24636 }
24637 
24645 {
24646  mWhiskerAntialiased = enabled;
24647 }
24648 
24653 {
24654  mMedianPen = pen;
24655 }
24656 
24664 {
24665  mOutlierStyle = style;
24666 }
24667 
24680 void QCPStatisticalBox::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)
24681 {
24682  if (keys.size() != minimum.size() || minimum.size() != lowerQuartile.size() || lowerQuartile.size() != median.size() ||
24683  median.size() != upperQuartile.size() || upperQuartile.size() != maximum.size() || maximum.size() != keys.size())
24684  qDebug() << Q_FUNC_INFO << "keys, minimum, lowerQuartile, median, upperQuartile, maximum have different sizes:"
24685  << keys.size() << minimum.size() << lowerQuartile.size() << median.size() << upperQuartile.size() << maximum.size();
24686  const int n = qMin(keys.size(), qMin(minimum.size(), qMin(lowerQuartile.size(), qMin(median.size(), qMin(upperQuartile.size(), maximum.size())))));
24687  QVector<QCPStatisticalBoxData> tempData(n);
24688  QVector<QCPStatisticalBoxData>::iterator it = tempData.begin();
24689  const QVector<QCPStatisticalBoxData>::iterator itEnd = tempData.end();
24690  int i = 0;
24691  while (it != itEnd)
24692  {
24693  it->key = keys[i];
24694  it->minimum = minimum[i];
24695  it->lowerQuartile = lowerQuartile[i];
24696  it->median = median[i];
24697  it->upperQuartile = upperQuartile[i];
24698  it->maximum = maximum[i];
24699  ++it;
24700  ++i;
24701  }
24702  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
24703 }
24704 
24713 void QCPStatisticalBox::addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers)
24714 {
24715  mDataContainer->add(QCPStatisticalBoxData(key, minimum, lowerQuartile, median, upperQuartile, maximum, outliers));
24716 }
24717 
24721 QCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool onlySelectable) const
24722 {
24723  QCPDataSelection result;
24724  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
24725  return result;
24726  if (!mKeyAxis || !mValueAxis)
24727  return result;
24728 
24729  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
24730  getVisibleDataBounds(visibleBegin, visibleEnd);
24731 
24732  for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
24733  {
24734  if (rect.intersects(getQuartileBox(it)))
24735  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
24736  }
24737  result.simplify();
24738  return result;
24739 }
24740 
24749 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
24750 {
24751  Q_UNUSED(details)
24752  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
24753  return -1;
24754  if (!mKeyAxis || !mValueAxis)
24755  return -1;
24756 
24757  if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
24758  {
24759  // get visible data range:
24760  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
24761  QCPStatisticalBoxDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
24762  getVisibleDataBounds(visibleBegin, visibleEnd);
24763  double minDistSqr = (std::numeric_limits<double>::max)();
24764  for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
24765  {
24766  if (getQuartileBox(it).contains(pos)) // quartile box
24767  {
24768  double currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
24769  if (currentDistSqr < minDistSqr)
24770  {
24771  minDistSqr = currentDistSqr;
24772  closestDataPoint = it;
24773  }
24774  } else // whiskers
24775  {
24776  const QVector<QLineF> whiskerBackbones(getWhiskerBackboneLines(it));
24777  for (int i=0; i<whiskerBackbones.size(); ++i)
24778  {
24779  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(whiskerBackbones.at(i));
24780  if (currentDistSqr < minDistSqr)
24781  {
24782  minDistSqr = currentDistSqr;
24783  closestDataPoint = it;
24784  }
24785  }
24786  }
24787  }
24788  if (details)
24789  {
24790  int pointIndex = closestDataPoint-mDataContainer->constBegin();
24791  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
24792  }
24793  return qSqrt(minDistSqr);
24794  }
24795  return -1;
24796 }
24797 
24798 /* inherits documentation from base class */
24799 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
24800 {
24801  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
24802  // determine exact range by including width of bars/flags:
24803  if (foundRange)
24804  {
24805  if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)
24806  range.lower -= mWidth*0.5;
24807  if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)
24808  range.upper += mWidth*0.5;
24809  }
24810  return range;
24811 }
24812 
24813 /* inherits documentation from base class */
24814 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
24815 {
24816  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
24817 }
24818 
24819 /* inherits documentation from base class */
24821 {
24822  if (mDataContainer->isEmpty()) return;
24823  QCPAxis *keyAxis = mKeyAxis.data();
24824  QCPAxis *valueAxis = mValueAxis.data();
24825  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
24826 
24827  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
24828  getVisibleDataBounds(visibleBegin, visibleEnd);
24829 
24830  // loop over and draw segments of unselected/selected data:
24831  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
24832  getDataSegments(selectedSegments, unselectedSegments);
24833  allSegments << unselectedSegments << selectedSegments;
24834  for (int i=0; i<allSegments.size(); ++i)
24835  {
24836  bool isSelectedSegment = i >= unselectedSegments.size();
24839  mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
24840  if (begin == end)
24841  continue;
24842 
24843  for (QCPStatisticalBoxDataContainer::const_iterator it=begin; it!=end; ++it)
24844  {
24845  // check data validity if flag set:
24846 # ifdef QCUSTOMPLOT_CHECK_DATA
24847  if (QCP::isInvalidData(it->key, it->minimum) ||
24848  QCP::isInvalidData(it->lowerQuartile, it->median) ||
24849  QCP::isInvalidData(it->upperQuartile, it->maximum))
24850  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range has invalid data." << "Plottable name:" << name();
24851  for (int i=0; i<it->outliers.size(); ++i)
24852  if (QCP::isInvalidData(it->outliers.at(i)))
24853  qDebug() << Q_FUNC_INFO << "Data point outlier at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
24854 # endif
24855 
24856  if (isSelectedSegment && mSelectionDecorator)
24857  {
24858  mSelectionDecorator->applyPen(painter);
24859  mSelectionDecorator->applyBrush(painter);
24860  } else
24861  {
24862  painter->setPen(mPen);
24863  painter->setBrush(mBrush);
24864  }
24865  QCPScatterStyle finalOutlierStyle = mOutlierStyle;
24866  if (isSelectedSegment && mSelectionDecorator)
24868  drawStatisticalBox(painter, it, finalOutlierStyle);
24869  }
24870  }
24871 
24872  // draw other selection decoration that isn't just line/scatter pens and brushes:
24873  if (mSelectionDecorator)
24875 }
24876 
24877 /* inherits documentation from base class */
24878 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
24879 {
24880  // draw filled rect:
24882  painter->setPen(mPen);
24883  painter->setBrush(mBrush);
24884  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
24885  r.moveCenter(rect.center());
24886  painter->drawRect(r);
24887 }
24888 
24898 {
24899  // draw quartile box:
24901  const QRectF quartileBox = getQuartileBox(it);
24902  painter->drawRect(quartileBox);
24903  // draw median line with cliprect set to quartile box:
24904  painter->save();
24905  painter->setClipRect(quartileBox, Qt::IntersectClip);
24906  painter->setPen(mMedianPen);
24907  painter->drawLine(QLineF(coordsToPixels(it->key-mWidth*0.5, it->median), coordsToPixels(it->key+mWidth*0.5, it->median)));
24908  painter->restore();
24909  // draw whisker lines:
24911  painter->setPen(mWhiskerPen);
24912  painter->drawLines(getWhiskerBackboneLines(it));
24913  painter->setPen(mWhiskerBarPen);
24914  painter->drawLines(getWhiskerBarLines(it));
24915  // draw outliers:
24917  outlierStyle.applyTo(painter, mPen);
24918  for (int i=0; i<it->outliers.size(); ++i)
24919  outlierStyle.drawShape(painter, coordsToPixels(it->key, it->outliers.at(i)));
24920 }
24921 
24937 {
24938  if (!mKeyAxis)
24939  {
24940  qDebug() << Q_FUNC_INFO << "invalid key axis";
24941  begin = mDataContainer->constEnd();
24942  end = mDataContainer->constEnd();
24943  return;
24944  }
24945  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of box to include partially visible data points
24946  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of box to include partially visible data points
24947 }
24948 
24957 {
24958  QRectF result;
24959  result.setTopLeft(coordsToPixels(it->key-mWidth*0.5, it->upperQuartile));
24960  result.setBottomRight(coordsToPixels(it->key+mWidth*0.5, it->lowerQuartile));
24961  return result;
24962 }
24963 
24973 {
24974  QVector<QLineF> result(2);
24975  result[0].setPoints(coordsToPixels(it->key, it->lowerQuartile), coordsToPixels(it->key, it->minimum)); // min backbone
24976  result[1].setPoints(coordsToPixels(it->key, it->upperQuartile), coordsToPixels(it->key, it->maximum)); // max backbone
24977  return result;
24978 }
24979 
24988 {
24989  QVector<QLineF> result(2);
24990  result[0].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->minimum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->minimum)); // min bar
24991  result[1].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->maximum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->maximum)); // max bar
24992  return result;
24993 }
24994 /* end of 'src/plottables/plottable-statisticalbox.cpp' */
24995 
24996 
24997 /* including file 'src/plottables/plottable-colormap.cpp', size 47881 */
24998 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
24999 
25003 
25037 /* start of documentation of inline functions */
25038 
25045 /* end of documentation of inline functions */
25046 
25054 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
25055  mKeySize(0),
25056  mValueSize(0),
25057  mKeyRange(keyRange),
25058  mValueRange(valueRange),
25059  mIsEmpty(true),
25060  mData(0),
25061  mAlpha(0),
25062  mDataModified(true)
25063 {
25064  setSize(keySize, valueSize);
25065  fill(0);
25066 }
25067 
25069 {
25070  if (mData)
25071  delete[] mData;
25072  if (mAlpha)
25073  delete[] mAlpha;
25074 }
25075 
25080  mKeySize(0),
25081  mValueSize(0),
25082  mIsEmpty(true),
25083  mData(0),
25084  mAlpha(0),
25085  mDataModified(true)
25086 {
25087  *this = other;
25088 }
25089 
25095 {
25096  if (&other != this)
25097  {
25098  const int keySize = other.keySize();
25099  const int valueSize = other.valueSize();
25100  if (!other.mAlpha && mAlpha)
25101  clearAlpha();
25102  setSize(keySize, valueSize);
25103  if (other.mAlpha && !mAlpha)
25104  createAlpha(false);
25105  setRange(other.keyRange(), other.valueRange());
25106  if (!isEmpty())
25107  {
25108  memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
25109  if (mAlpha)
25110  memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*keySize*valueSize);
25111  }
25112  mDataBounds = other.mDataBounds;
25113  mDataModified = true;
25114  }
25115  return *this;
25116 }
25117 
25118 /* undocumented getter */
25119 double QCPColorMapData::data(double key, double value)
25120 {
25121  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
25122  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
25123  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
25124  return mData[valueCell*mKeySize + keyCell];
25125  else
25126  return 0;
25127 }
25128 
25129 /* undocumented getter */
25130 double QCPColorMapData::cell(int keyIndex, int valueIndex)
25131 {
25132  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
25133  return mData[valueIndex*mKeySize + keyIndex];
25134  else
25135  return 0;
25136 }
25137 
25146 unsigned char QCPColorMapData::alpha(int keyIndex, int valueIndex)
25147 {
25148  if (mAlpha && keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
25149  return mAlpha[valueIndex*mKeySize + keyIndex];
25150  else
25151  return 255;
25152 }
25153 
25167 {
25168  if (keySize != mKeySize || valueSize != mValueSize)
25169  {
25170  mKeySize = keySize;
25172  if (mData)
25173  delete[] mData;
25174  mIsEmpty = mKeySize == 0 || mValueSize == 0;
25175  if (!mIsEmpty)
25176  {
25177 #ifdef __EXCEPTIONS
25178  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
25179 #endif
25180  mData = new double[mKeySize*mValueSize];
25181 #ifdef __EXCEPTIONS
25182  } catch (...) { mData = 0; }
25183 #endif
25184  if (mData)
25185  fill(0);
25186  else
25187  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
25188  } else
25189  mData = 0;
25190 
25191  if (mAlpha) // if we had an alpha map, recreate it with new size
25192  createAlpha();
25193 
25194  mDataModified = true;
25195  }
25196 }
25197 
25209 {
25210  setSize(keySize, mValueSize);
25211 }
25212 
25224 {
25225  setSize(mKeySize, valueSize);
25226 }
25227 
25239 {
25240  setKeyRange(keyRange);
25241  setValueRange(valueRange);
25242 }
25243 
25255 {
25256  mKeyRange = keyRange;
25257 }
25258 
25270 {
25272 }
25273 
25286 void QCPColorMapData::setData(double key, double value, double z)
25287 {
25288  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
25289  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
25290  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
25291  {
25292  mData[valueCell*mKeySize + keyCell] = z;
25293  if (z < mDataBounds.lower)
25294  mDataBounds.lower = z;
25295  if (z > mDataBounds.upper)
25296  mDataBounds.upper = z;
25297  mDataModified = true;
25298  }
25299 }
25300 
25312 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
25313 {
25314  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
25315  {
25316  mData[valueIndex*mKeySize + keyIndex] = z;
25317  if (z < mDataBounds.lower)
25318  mDataBounds.lower = z;
25319  if (z > mDataBounds.upper)
25320  mDataBounds.upper = z;
25321  mDataModified = true;
25322  } else
25323  qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex;
25324 }
25325 
25341 void QCPColorMapData::setAlpha(int keyIndex, int valueIndex, unsigned char alpha)
25342 {
25343  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
25344  {
25345  if (mAlpha || createAlpha())
25346  {
25347  mAlpha[valueIndex*mKeySize + keyIndex] = alpha;
25348  mDataModified = true;
25349  }
25350  } else
25351  qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex;
25352 }
25353 
25368 {
25369  if (mKeySize > 0 && mValueSize > 0)
25370  {
25371  double minHeight = mData[0];
25372  double maxHeight = mData[0];
25373  const int dataCount = mValueSize*mKeySize;
25374  for (int i=0; i<dataCount; ++i)
25375  {
25376  if (mData[i] > maxHeight)
25377  maxHeight = mData[i];
25378  if (mData[i] < minHeight)
25379  minHeight = mData[i];
25380  }
25381  mDataBounds.lower = minHeight;
25382  mDataBounds.upper = maxHeight;
25383  }
25384 }
25385 
25392 {
25393  setSize(0, 0);
25394 }
25395 
25400 {
25401  if (mAlpha)
25402  {
25403  delete[] mAlpha;
25404  mAlpha = 0;
25405  mDataModified = true;
25406  }
25407 }
25408 
25413 {
25414  const int dataCount = mValueSize*mKeySize;
25415  for (int i=0; i<dataCount; ++i)
25416  mData[i] = z;
25417  mDataBounds = QCPRange(z, z);
25418  mDataModified = true;
25419 }
25420 
25431 {
25432  if (mAlpha || createAlpha(false))
25433  {
25434  const int dataCount = mValueSize*mKeySize;
25435  for (int i=0; i<dataCount; ++i)
25436  mAlpha[i] = alpha;
25437  mDataModified = true;
25438  }
25439 }
25440 
25458 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
25459 {
25460  if (keyIndex)
25461  *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
25462  if (valueIndex)
25463  *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
25464 }
25465 
25481 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
25482 {
25483  if (key)
25484  *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
25485  if (value)
25486  *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
25487 }
25488 
25502 bool QCPColorMapData::createAlpha(bool initializeOpaque)
25503 {
25504  clearAlpha();
25505  if (isEmpty())
25506  return false;
25507 
25508 #ifdef __EXCEPTIONS
25509  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
25510 #endif
25511  mAlpha = new unsigned char[mKeySize*mValueSize];
25512 #ifdef __EXCEPTIONS
25513  } catch (...) { mAlpha = 0; }
25514 #endif
25515  if (mAlpha)
25516  {
25517  if (initializeOpaque)
25518  fillAlpha(255);
25519  return true;
25520  } else
25521  {
25522  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
25523  return false;
25524  }
25525 }
25526 
25527 
25531 
25607 /* start documentation of inline functions */
25608 
25617 /* end documentation of inline functions */
25618 
25619 /* start documentation of signals */
25620 
25642 /* end documentation of signals */
25643 
25652  QCPAbstractPlottable(keyAxis, valueAxis),
25653  mDataScaleType(QCPAxis::stLinear),
25654  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
25655  mGradient(QCPColorGradient::gpCold),
25656  mInterpolate(true),
25657  mTightBoundary(false),
25658  mMapImageInvalidated(true)
25659 {
25660 }
25661 
25663 {
25664  delete mMapData;
25665 }
25666 
25675 {
25676  if (mMapData == data)
25677  {
25678  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
25679  return;
25680  }
25681  if (copy)
25682  {
25683  *mMapData = *data;
25684  } else
25685  {
25686  delete mMapData;
25687  mMapData = data;
25688  }
25689  mMapImageInvalidated = true;
25690 }
25691 
25701 {
25702  if (!QCPRange::validRange(dataRange)) return;
25703  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
25704  {
25706  mDataRange = dataRange.sanitizedForLogScale();
25707  else
25708  mDataRange = dataRange.sanitizedForLinScale();
25709  mMapImageInvalidated = true;
25711  }
25712 }
25713 
25720 {
25721  if (mDataScaleType != scaleType)
25722  {
25723  mDataScaleType = scaleType;
25724  mMapImageInvalidated = true;
25728  }
25729 }
25730 
25743 {
25744  if (mGradient != gradient)
25745  {
25746  mGradient = gradient;
25747  mMapImageInvalidated = true;
25748  emit gradientChanged(mGradient);
25749  }
25750 }
25751 
25759 {
25760  mInterpolate = enabled;
25761  mMapImageInvalidated = true; // because oversampling factors might need to change
25762 }
25763 
25776 {
25777  mTightBoundary = enabled;
25778 }
25779 
25795 {
25796  if (mColorScale) // unconnect signals from old color scale
25797  {
25798  disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
25799  disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
25800  disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
25801  disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
25802  disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
25803  disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
25804  }
25806  if (mColorScale) // connect signals to new color scale
25807  {
25808  setGradient(mColorScale.data()->gradient());
25809  setDataRange(mColorScale.data()->dataRange());
25810  setDataScaleType(mColorScale.data()->dataScaleType());
25811  connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
25812  connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
25813  connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
25814  connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
25815  connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
25816  connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
25817  }
25818 }
25819 
25840 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
25841 {
25842  if (recalculateDataBounds)
25845 }
25846 
25861 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
25862 {
25863  if (mMapImage.isNull() && !data()->isEmpty())
25864  updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
25865 
25866  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
25867  {
25868  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
25869  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
25870  mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
25871  }
25872 }
25873 
25874 /* inherits documentation from base class */
25875 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
25876 {
25877  Q_UNUSED(details)
25878  if ((onlySelectable && mSelectable == QCP::stNone) || mMapData->isEmpty())
25879  return -1;
25880  if (!mKeyAxis || !mValueAxis)
25881  return -1;
25882 
25883  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
25884  {
25885  double posKey, posValue;
25886  pixelsToCoords(pos, posKey, posValue);
25887  if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
25888  {
25889  if (details)
25890  details->setValue(QCPDataSelection(QCPDataRange(0, 1))); // temporary solution, to facilitate whole-plottable selection. Replace in future version with segmented 2D selection.
25891  return mParentPlot->selectionTolerance()*0.99;
25892  }
25893  }
25894  return -1;
25895 }
25896 
25897 /* inherits documentation from base class */
25898 QCPRange QCPColorMap::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
25899 {
25900  foundRange = true;
25901  QCPRange result = mMapData->keyRange();
25902  result.normalize();
25903  if (inSignDomain == QCP::sdPositive)
25904  {
25905  if (result.lower <= 0 && result.upper > 0)
25906  result.lower = result.upper*1e-3;
25907  else if (result.lower <= 0 && result.upper <= 0)
25908  foundRange = false;
25909  } else if (inSignDomain == QCP::sdNegative)
25910  {
25911  if (result.upper >= 0 && result.lower < 0)
25912  result.upper = result.lower*1e-3;
25913  else if (result.upper >= 0 && result.lower >= 0)
25914  foundRange = false;
25915  }
25916  return result;
25917 }
25918 
25919 /* inherits documentation from base class */
25920 QCPRange QCPColorMap::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
25921 {
25922  if (inKeyRange != QCPRange())
25923  {
25924  if (mMapData->keyRange().upper < inKeyRange.lower || mMapData->keyRange().lower > inKeyRange.upper)
25925  {
25926  foundRange = false;
25927  return QCPRange();
25928  }
25929  }
25930 
25931  foundRange = true;
25932  QCPRange result = mMapData->valueRange();
25933  result.normalize();
25934  if (inSignDomain == QCP::sdPositive)
25935  {
25936  if (result.lower <= 0 && result.upper > 0)
25937  result.lower = result.upper*1e-3;
25938  else if (result.lower <= 0 && result.upper <= 0)
25939  foundRange = false;
25940  } else if (inSignDomain == QCP::sdNegative)
25941  {
25942  if (result.upper >= 0 && result.lower < 0)
25943  result.upper = result.lower*1e-3;
25944  else if (result.upper >= 0 && result.lower >= 0)
25945  foundRange = false;
25946  }
25947  return result;
25948 }
25949 
25965 {
25966  QCPAxis *keyAxis = mKeyAxis.data();
25967  if (!keyAxis) return;
25968  if (mMapData->isEmpty()) return;
25969 
25970  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
25971  const int keySize = mMapData->keySize();
25972  const int valueSize = mMapData->valueSize();
25973  int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
25974  int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
25975 
25976  // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
25977  if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
25978  mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), format);
25979  else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
25980  mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), format);
25981 
25982  if (mMapImage.isNull())
25983  {
25984  qDebug() << Q_FUNC_INFO << "Couldn't create map image (possibly too large for memory)";
25985  mMapImage = QImage(QSize(10, 10), format);
25986  mMapImage.fill(Qt::black);
25987  } else
25988  {
25989  QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage
25990  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
25991  {
25992  // resize undersampled map image to actual key/value cell sizes:
25993  if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
25994  mUndersampledMapImage = QImage(QSize(keySize, valueSize), format);
25995  else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
25996  mUndersampledMapImage = QImage(QSize(valueSize, keySize), format);
25997  localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image
25998  } else if (!mUndersampledMapImage.isNull())
25999  mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it
26000 
26001  const double *rawData = mMapData->mData;
26002  const unsigned char *rawAlpha = mMapData->mAlpha;
26003  if (keyAxis->orientation() == Qt::Horizontal)
26004  {
26005  const int lineCount = valueSize;
26006  const int rowCount = keySize;
26007  for (int line=0; line<lineCount; ++line)
26008  {
26009  QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
26010  if (rawAlpha)
26011  mGradient.colorize(rawData+line*rowCount, rawAlpha+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
26012  else
26013  mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
26014  }
26015  } else // keyAxis->orientation() == Qt::Vertical
26016  {
26017  const int lineCount = keySize;
26018  const int rowCount = valueSize;
26019  for (int line=0; line<lineCount; ++line)
26020  {
26021  QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
26022  if (rawAlpha)
26023  mGradient.colorize(rawData+line, rawAlpha+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
26024  else
26025  mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
26026  }
26027  }
26028 
26029  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
26030  {
26031  if (keyAxis->orientation() == Qt::Horizontal)
26032  mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
26033  else
26034  mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
26035  }
26036  }
26037  mMapData->mDataModified = false;
26038  mMapImageInvalidated = false;
26039 }
26040 
26041 /* inherits documentation from base class */
26043 {
26044  if (mMapData->isEmpty()) return;
26045  if (!mKeyAxis || !mValueAxis) return;
26047 
26049  updateMapImage();
26050 
26051  // use buffer if painting vectorized (PDF):
26052  const bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
26053  QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized
26054  QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in
26055  QPixmap mapBuffer;
26056  if (useBuffer)
26057  {
26058  const double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps
26059  mapBufferTarget = painter->clipRegion().boundingRect();
26060  mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
26061  mapBuffer.fill(Qt::transparent);
26062  localPainter = new QCPPainter(&mapBuffer);
26063  localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
26064  localPainter->translate(-mapBufferTarget.topLeft());
26065  }
26066 
26067  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
26069  // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):
26070  double halfCellWidth = 0; // in pixels
26071  double halfCellHeight = 0; // in pixels
26072  if (keyAxis()->orientation() == Qt::Horizontal)
26073  {
26074  if (mMapData->keySize() > 1)
26075  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
26076  if (mMapData->valueSize() > 1)
26077  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
26078  } else // keyAxis orientation is Qt::Vertical
26079  {
26080  if (mMapData->keySize() > 1)
26081  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
26082  if (mMapData->valueSize() > 1)
26083  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
26084  }
26085  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
26086  const bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
26087  const bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
26088  const bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
26089  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
26090  QRegion clipBackup;
26091  if (mTightBoundary)
26092  {
26093  clipBackup = localPainter->clipRegion();
26094  QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
26096  localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
26097  }
26098  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
26099  if (mTightBoundary)
26100  localPainter->setClipRegion(clipBackup);
26101  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
26102 
26103  if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter
26104  {
26105  delete localPainter;
26106  painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
26107  }
26108 }
26109 
26110 /* inherits documentation from base class */
26111 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
26112 {
26114  // draw map thumbnail:
26115  if (!mLegendIcon.isNull())
26116  {
26117  QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
26118  QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
26119  iconRect.moveCenter(rect.center());
26120  painter->drawPixmap(iconRect.topLeft(), scaledIcon);
26121  }
26122  /*
26123  // draw frame:
26124  painter->setBrush(Qt::NoBrush);
26125  painter->setPen(Qt::black);
26126  painter->drawRect(rect.adjusted(1, 1, 0, 0));
26127  */
26128 }
26129 /* end of 'src/plottables/plottable-colormap.cpp' */
26130 
26131 
26132 /* including file 'src/plottables/plottable-financial.cpp', size 42827 */
26133 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
26134 
26138 
26156 /* start documentation of inline functions */
26157 
26207 /* end documentation of inline functions */
26208 
26213  key(0),
26214  open(0),
26215  high(0),
26216  low(0),
26217  close(0)
26218 {
26219 }
26220 
26224 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
26225  key(key),
26226  open(open),
26227  high(high),
26228  low(low),
26229  close(close)
26230 {
26231 }
26232 
26233 
26237 
26287 /* start of documentation of inline functions */
26288 
26296 /* end of documentation of inline functions */
26297 
26309  QCPAbstractPlottable1D<QCPFinancialData>(keyAxis, valueAxis),
26310  mChartStyle(csCandlestick),
26311  mWidth(0.5),
26312  mWidthType(wtPlotCoords),
26313  mTwoColored(true),
26314  mBrushPositive(QBrush(QColor(50, 160, 0))),
26315  mBrushNegative(QBrush(QColor(180, 0, 15))),
26316  mPenPositive(QPen(QColor(40, 150, 0))),
26317  mPenNegative(QPen(QColor(170, 5, 5)))
26318 {
26319  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
26320 }
26321 
26323 {
26324 }
26325 
26341 void QCPFinancial::setData(QSharedPointer<QCPFinancialDataContainer> data)
26342 {
26343  mDataContainer = data;
26344 }
26345 
26357 void QCPFinancial::setData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted)
26358 {
26359  mDataContainer->clear();
26360  addData(keys, open, high, low, close, alreadySorted);
26361 }
26362 
26367 {
26368  mChartStyle = style;
26369 }
26370 
26377 {
26378  mWidth = width;
26379 }
26380 
26390 {
26392 }
26393 
26404 {
26406 }
26407 
26418 {
26420 }
26421 
26432 {
26434 }
26435 
26446 {
26447  mPenPositive = pen;
26448 }
26449 
26460 {
26461  mPenNegative = pen;
26462 }
26463 
26478 void QCPFinancial::addData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted)
26479 {
26480  if (keys.size() != open.size() || open.size() != high.size() || high.size() != low.size() || low.size() != close.size() || close.size() != keys.size())
26481  qDebug() << Q_FUNC_INFO << "keys, open, high, low, close have different sizes:" << keys.size() << open.size() << high.size() << low.size() << close.size();
26482  const int n = qMin(keys.size(), qMin(open.size(), qMin(high.size(), qMin(low.size(), close.size()))));
26483  QVector<QCPFinancialData> tempData(n);
26484  QVector<QCPFinancialData>::iterator it = tempData.begin();
26485  const QVector<QCPFinancialData>::iterator itEnd = tempData.end();
26486  int i = 0;
26487  while (it != itEnd)
26488  {
26489  it->key = keys[i];
26490  it->open = open[i];
26491  it->high = high[i];
26492  it->low = low[i];
26493  it->close = close[i];
26494  ++it;
26495  ++i;
26496  }
26497  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
26498 }
26499 
26510 void QCPFinancial::addData(double key, double open, double high, double low, double close)
26511 {
26512  mDataContainer->add(QCPFinancialData(key, open, high, low, close));
26513 }
26514 
26518 QCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelectable) const
26519 {
26520  QCPDataSelection result;
26521  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
26522  return result;
26523  if (!mKeyAxis || !mValueAxis)
26524  return result;
26525 
26526  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
26527  getVisibleDataBounds(visibleBegin, visibleEnd);
26528 
26529  for (QCPFinancialDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
26530  {
26531  if (rect.intersects(selectionHitBox(it)))
26532  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
26533  }
26534  result.simplify();
26535  return result;
26536 }
26537 
26546 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
26547 {
26548  Q_UNUSED(details)
26549  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
26550  return -1;
26551  if (!mKeyAxis || !mValueAxis)
26552  return -1;
26553 
26554  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
26555  {
26556  // get visible data range:
26557  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
26558  QCPFinancialDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
26559  getVisibleDataBounds(visibleBegin, visibleEnd);
26560  // perform select test according to configured style:
26561  double result = -1;
26562  switch (mChartStyle)
26563  {
26564  case QCPFinancial::csOhlc:
26565  result = ohlcSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;
26567  result = candlestickSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;
26568  }
26569  if (details)
26570  {
26571  int pointIndex = closestDataPoint-mDataContainer->constBegin();
26572  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
26573  }
26574  return result;
26575  }
26576 
26577  return -1;
26578 }
26579 
26580 /* inherits documentation from base class */
26581 QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
26582 {
26583  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
26584  // determine exact range by including width of bars/flags:
26585  if (foundRange)
26586  {
26587  if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)
26588  range.lower -= mWidth*0.5;
26589  if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)
26590  range.upper += mWidth*0.5;
26591  }
26592  return range;
26593 }
26594 
26595 /* inherits documentation from base class */
26596 QCPRange QCPFinancial::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
26597 {
26598  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
26599 }
26600 
26615 QCPFinancialDataContainer QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
26616 {
26618  int count = qMin(time.size(), value.size());
26619  if (count == 0)
26620  return QCPFinancialDataContainer();
26621 
26622  QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
26623  int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
26624  for (int i=0; i<count; ++i)
26625  {
26626  int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
26627  if (currentBinIndex == index) // data point still in current bin, extend high/low:
26628  {
26629  if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
26630  if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
26631  if (i == count-1) // last data point is in current bin, finalize bin:
26632  {
26633  currentBinData.close = value.at(i);
26634  currentBinData.key = timeBinOffset+(index)*timeBinSize;
26635  data.add(currentBinData);
26636  }
26637  } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
26638  {
26639  // finalize current bin:
26640  currentBinData.close = value.at(i-1);
26641  currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
26642  data.add(currentBinData);
26643  // start next bin:
26644  currentBinIndex = index;
26645  currentBinData.open = value.at(i);
26646  currentBinData.high = value.at(i);
26647  currentBinData.low = value.at(i);
26648  }
26649  }
26650 
26651  return data;
26652 }
26653 
26654 /* inherits documentation from base class */
26656 {
26657  // get visible data range:
26658  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
26659  getVisibleDataBounds(visibleBegin, visibleEnd);
26660 
26661  // loop over and draw segments of unselected/selected data:
26662  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
26663  getDataSegments(selectedSegments, unselectedSegments);
26664  allSegments << unselectedSegments << selectedSegments;
26665  for (int i=0; i<allSegments.size(); ++i)
26666  {
26667  bool isSelectedSegment = i >= unselectedSegments.size();
26668  QCPFinancialDataContainer::const_iterator begin = visibleBegin;
26670  mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
26671  if (begin == end)
26672  continue;
26673 
26674  // draw data segment according to configured style:
26675  switch (mChartStyle)
26676  {
26677  case QCPFinancial::csOhlc:
26678  drawOhlcPlot(painter, begin, end, isSelectedSegment); break;
26680  drawCandlestickPlot(painter, begin, end, isSelectedSegment); break;
26681  }
26682  }
26683 
26684  // draw other selection decoration that isn't just line/scatter pens and brushes:
26685  if (mSelectionDecorator)
26687 }
26688 
26689 /* inherits documentation from base class */
26690 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
26691 {
26692  painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
26693  if (mChartStyle == csOhlc)
26694  {
26695  if (mTwoColored)
26696  {
26697  // draw upper left half icon with positive color:
26698  painter->setBrush(mBrushPositive);
26699  painter->setPen(mPenPositive);
26700  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
26701  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26702  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
26703  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
26704  // draw bottom right half icon with negative color:
26705  painter->setBrush(mBrushNegative);
26706  painter->setPen(mPenNegative);
26707  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
26708  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26709  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
26710  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
26711  } else
26712  {
26713  painter->setBrush(mBrush);
26714  painter->setPen(mPen);
26715  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26716  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
26717  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
26718  }
26719  } else if (mChartStyle == csCandlestick)
26720  {
26721  if (mTwoColored)
26722  {
26723  // draw upper left half icon with positive color:
26724  painter->setBrush(mBrushPositive);
26725  painter->setPen(mPenPositive);
26726  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
26727  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
26728  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26729  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
26730  // draw bottom right half icon with negative color:
26731  painter->setBrush(mBrushNegative);
26732  painter->setPen(mPenNegative);
26733  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
26734  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
26735  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26736  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
26737  } else
26738  {
26739  painter->setBrush(mBrush);
26740  painter->setPen(mPen);
26741  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
26742  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26743  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
26744  }
26745  }
26746 }
26747 
26755 {
26756  QCPAxis *keyAxis = mKeyAxis.data();
26757  QCPAxis *valueAxis = mValueAxis.data();
26758  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
26759 
26760  if (keyAxis->orientation() == Qt::Horizontal)
26761  {
26762  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26763  {
26764  if (isSelected && mSelectionDecorator)
26765  mSelectionDecorator->applyPen(painter);
26766  else if (mTwoColored)
26767  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26768  else
26769  painter->setPen(mPen);
26770  double keyPixel = keyAxis->coordToPixel(it->key);
26771  double openPixel = valueAxis->coordToPixel(it->open);
26772  double closePixel = valueAxis->coordToPixel(it->close);
26773  // draw backbone:
26774  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(it->low)));
26775  // draw open:
26776  double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides
26777  painter->drawLine(QPointF(keyPixel-pixelWidth, openPixel), QPointF(keyPixel, openPixel));
26778  // draw close:
26779  painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+pixelWidth, closePixel));
26780  }
26781  } else
26782  {
26783  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26784  {
26785  if (isSelected && mSelectionDecorator)
26786  mSelectionDecorator->applyPen(painter);
26787  else if (mTwoColored)
26788  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26789  else
26790  painter->setPen(mPen);
26791  double keyPixel = keyAxis->coordToPixel(it->key);
26792  double openPixel = valueAxis->coordToPixel(it->open);
26793  double closePixel = valueAxis->coordToPixel(it->close);
26794  // draw backbone:
26795  painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(it->low), keyPixel));
26796  // draw open:
26797  double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides
26798  painter->drawLine(QPointF(openPixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel));
26799  // draw close:
26800  painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+pixelWidth));
26801  }
26802  }
26803 }
26804 
26812 {
26813  QCPAxis *keyAxis = mKeyAxis.data();
26814  QCPAxis *valueAxis = mValueAxis.data();
26815  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
26816 
26817  if (keyAxis->orientation() == Qt::Horizontal)
26818  {
26819  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26820  {
26821  if (isSelected && mSelectionDecorator)
26822  {
26823  mSelectionDecorator->applyPen(painter);
26824  mSelectionDecorator->applyBrush(painter);
26825  } else if (mTwoColored)
26826  {
26827  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26828  painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);
26829  } else
26830  {
26831  painter->setPen(mPen);
26832  painter->setBrush(mBrush);
26833  }
26834  double keyPixel = keyAxis->coordToPixel(it->key);
26835  double openPixel = valueAxis->coordToPixel(it->open);
26836  double closePixel = valueAxis->coordToPixel(it->close);
26837  // draw high:
26838  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));
26839  // draw low:
26840  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));
26841  // draw open-close box:
26842  double pixelWidth = getPixelWidth(it->key, keyPixel);
26843  painter->drawRect(QRectF(QPointF(keyPixel-pixelWidth, closePixel), QPointF(keyPixel+pixelWidth, openPixel)));
26844  }
26845  } else // keyAxis->orientation() == Qt::Vertical
26846  {
26847  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26848  {
26849  if (isSelected && mSelectionDecorator)
26850  {
26851  mSelectionDecorator->applyPen(painter);
26852  mSelectionDecorator->applyBrush(painter);
26853  } else if (mTwoColored)
26854  {
26855  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26856  painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);
26857  } else
26858  {
26859  painter->setPen(mPen);
26860  painter->setBrush(mBrush);
26861  }
26862  double keyPixel = keyAxis->coordToPixel(it->key);
26863  double openPixel = valueAxis->coordToPixel(it->open);
26864  double closePixel = valueAxis->coordToPixel(it->close);
26865  // draw high:
26866  painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));
26867  // draw low:
26868  painter->drawLine(QPointF(valueAxis->coordToPixel(it->low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));
26869  // draw open-close box:
26870  double pixelWidth = getPixelWidth(it->key, keyPixel);
26871  painter->drawRect(QRectF(QPointF(closePixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel+pixelWidth)));
26872  }
26873  }
26874 }
26875 
26888 double QCPFinancial::getPixelWidth(double key, double keyPixel) const
26889 {
26890  double result = 0;
26891  switch (mWidthType)
26892  {
26893  case wtAbsolute:
26894  {
26895  if (mKeyAxis)
26896  result = mWidth*0.5*mKeyAxis.data()->pixelOrientation();
26897  break;
26898  }
26899  case wtAxisRectRatio:
26900  {
26901  if (mKeyAxis && mKeyAxis.data()->axisRect())
26902  {
26903  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
26904  result = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
26905  else
26906  result = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
26907  } else
26908  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
26909  break;
26910  }
26911  case wtPlotCoords:
26912  {
26913  if (mKeyAxis)
26914  result = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
26915  else
26916  qDebug() << Q_FUNC_INFO << "No key axis defined";
26917  break;
26918  }
26919  }
26920  return result;
26921 }
26922 
26932 {
26933  closestDataPoint = mDataContainer->constEnd();
26934  QCPAxis *keyAxis = mKeyAxis.data();
26935  QCPAxis *valueAxis = mValueAxis.data();
26936  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
26937 
26938  double minDistSqr = (std::numeric_limits<double>::max)();
26939  if (keyAxis->orientation() == Qt::Horizontal)
26940  {
26941  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26942  {
26943  double keyPixel = keyAxis->coordToPixel(it->key);
26944  // calculate distance to backbone:
26945  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)));
26946  if (currentDistSqr < minDistSqr)
26947  {
26948  minDistSqr = currentDistSqr;
26949  closestDataPoint = it;
26950  }
26951  }
26952  } else // keyAxis->orientation() == Qt::Vertical
26953  {
26954  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26955  {
26956  double keyPixel = keyAxis->coordToPixel(it->key);
26957  // calculate distance to backbone:
26958  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel));
26959  if (currentDistSqr < minDistSqr)
26960  {
26961  minDistSqr = currentDistSqr;
26962  closestDataPoint = it;
26963  }
26964  }
26965  }
26966  return qSqrt(minDistSqr);
26967 }
26968 
26979 {
26980  closestDataPoint = mDataContainer->constEnd();
26981  QCPAxis *keyAxis = mKeyAxis.data();
26982  QCPAxis *valueAxis = mValueAxis.data();
26983  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
26984 
26985  double minDistSqr = (std::numeric_limits<double>::max)();
26986  if (keyAxis->orientation() == Qt::Horizontal)
26987  {
26988  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26989  {
26990  double currentDistSqr;
26991  // determine whether pos is in open-close-box:
26992  QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);
26993  QCPRange boxValueRange(it->close, it->open);
26994  double posKey, posValue;
26995  pixelsToCoords(pos, posKey, posValue);
26996  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
26997  {
26998  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
26999  } else
27000  {
27001  // calculate distance to high/low lines:
27002  double keyPixel = keyAxis->coordToPixel(it->key);
27003  double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));
27004  double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));
27005  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
27006  }
27007  if (currentDistSqr < minDistSqr)
27008  {
27009  minDistSqr = currentDistSqr;
27010  closestDataPoint = it;
27011  }
27012  }
27013  } else // keyAxis->orientation() == Qt::Vertical
27014  {
27015  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
27016  {
27017  double currentDistSqr;
27018  // determine whether pos is in open-close-box:
27019  QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);
27020  QCPRange boxValueRange(it->close, it->open);
27021  double posKey, posValue;
27022  pixelsToCoords(pos, posKey, posValue);
27023  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
27024  {
27025  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
27026  } else
27027  {
27028  // calculate distance to high/low lines:
27029  double keyPixel = keyAxis->coordToPixel(it->key);
27030  double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));
27031  double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));
27032  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
27033  }
27034  if (currentDistSqr < minDistSqr)
27035  {
27036  minDistSqr = currentDistSqr;
27037  closestDataPoint = it;
27038  }
27039  }
27040  }
27041  return qSqrt(minDistSqr);
27042 }
27043 
27059 {
27060  if (!mKeyAxis)
27061  {
27062  qDebug() << Q_FUNC_INFO << "invalid key axis";
27063  begin = mDataContainer->constEnd();
27064  end = mDataContainer->constEnd();
27065  return;
27066  }
27067  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of ohlc/candlestick to include partially visible data points
27068  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of ohlc/candlestick to include partially visible data points
27069 }
27070 
27077 {
27078  QCPAxis *keyAxis = mKeyAxis.data();
27079  QCPAxis *valueAxis = mValueAxis.data();
27080  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); }
27081 
27082  double keyPixel = keyAxis->coordToPixel(it->key);
27083  double highPixel = valueAxis->coordToPixel(it->high);
27084  double lowPixel = valueAxis->coordToPixel(it->low);
27085  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it->key-mWidth*0.5);
27086  if (keyAxis->orientation() == Qt::Horizontal)
27087  return QRectF(keyPixel-keyWidthPixels, highPixel, keyWidthPixels*2, lowPixel-highPixel).normalized();
27088  else
27089  return QRectF(highPixel, keyPixel-keyWidthPixels, lowPixel-highPixel, keyWidthPixels*2).normalized();
27090 }
27091 /* end of 'src/plottables/plottable-financial.cpp' */
27092 
27093 
27094 /* including file 'src/plottables/plottable-errorbar.cpp', size 37570 */
27095 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
27096 
27100 
27120  errorMinus(0),
27121  errorPlus(0)
27122 {
27123 }
27124 
27129  errorMinus(error),
27130  errorPlus(error)
27131 {
27132 }
27133 
27139  errorMinus(errorMinus),
27140  errorPlus(errorPlus)
27141 {
27142 }
27143 
27144 
27148 
27178 /* start of documentation of inline functions */
27179 
27187 /* end of documentation of inline functions */
27188 
27203  QCPAbstractPlottable(keyAxis, valueAxis),
27204  mDataContainer(new QVector<QCPErrorBarsData>),
27205  mErrorType(etValueError),
27206  mWhiskerWidth(9),
27207  mSymbolGap(10)
27208 {
27209  setPen(QPen(Qt::black, 0));
27210  setBrush(Qt::NoBrush);
27211 }
27212 
27214 {
27215 }
27216 
27235 void QCPErrorBars::setData(QSharedPointer<QCPErrorBarsDataContainer> data)
27236 {
27237  mDataContainer = data;
27238 }
27239 
27249 void QCPErrorBars::setData(const QVector<double> &error)
27250 {
27251  mDataContainer->clear();
27252  addData(error);
27253 }
27254 
27265 void QCPErrorBars::setData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)
27266 {
27267  mDataContainer->clear();
27268  addData(errorMinus, errorPlus);
27269 }
27270 
27286 {
27287  if (plottable && qobject_cast<QCPErrorBars*>(plottable))
27288  {
27289  mDataPlottable = 0;
27290  qDebug() << Q_FUNC_INFO << "can't set another QCPErrorBars instance as data plottable";
27291  return;
27292  }
27293  if (plottable && !plottable->interface1D())
27294  {
27295  mDataPlottable = 0;
27296  qDebug() << Q_FUNC_INFO << "passed plottable doesn't implement 1d interface, can't associate with QCPErrorBars";
27297  return;
27298  }
27299 
27300  mDataPlottable = plottable;
27301 }
27302 
27308 {
27309  mErrorType = type;
27310 }
27311 
27317 {
27318  mWhiskerWidth = pixels;
27319 }
27320 
27326 void QCPErrorBars::setSymbolGap(double pixels)
27327 {
27328  mSymbolGap = pixels;
27329 }
27330 
27340 void QCPErrorBars::addData(const QVector<double> &error)
27341 {
27342  addData(error, error);
27343 }
27344 
27355 void QCPErrorBars::addData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)
27356 {
27357  if (errorMinus.size() != errorPlus.size())
27358  qDebug() << Q_FUNC_INFO << "minus and plus error vectors have different sizes:" << errorMinus.size() << errorPlus.size();
27359  const int n = qMin(errorMinus.size(), errorPlus.size());
27360  mDataContainer->reserve(n);
27361  for (int i=0; i<n; ++i)
27362  mDataContainer->append(QCPErrorBarsData(errorMinus.at(i), errorPlus.at(i)));
27363 }
27364 
27374 void QCPErrorBars::addData(double error)
27375 {
27376  mDataContainer->append(QCPErrorBarsData(error));
27377 }
27378 
27389 void QCPErrorBars::addData(double errorMinus, double errorPlus)
27390 {
27391  mDataContainer->append(QCPErrorBarsData(errorMinus, errorPlus));
27392 }
27393 
27394 /* inherits documentation from base class */
27396 {
27397  return mDataContainer->size();
27398 }
27399 
27400 /* inherits documentation from base class */
27401 double QCPErrorBars::dataMainKey(int index) const
27402 {
27403  if (mDataPlottable)
27404  return mDataPlottable->interface1D()->dataMainKey(index);
27405  else
27406  qDebug() << Q_FUNC_INFO << "no data plottable set";
27407  return 0;
27408 }
27409 
27410 /* inherits documentation from base class */
27411 double QCPErrorBars::dataSortKey(int index) const
27412 {
27413  if (mDataPlottable)
27414  return mDataPlottable->interface1D()->dataSortKey(index);
27415  else
27416  qDebug() << Q_FUNC_INFO << "no data plottable set";
27417  return 0;
27418 }
27419 
27420 /* inherits documentation from base class */
27421 double QCPErrorBars::dataMainValue(int index) const
27422 {
27423  if (mDataPlottable)
27424  return mDataPlottable->interface1D()->dataMainValue(index);
27425  else
27426  qDebug() << Q_FUNC_INFO << "no data plottable set";
27427  return 0;
27428 }
27429 
27430 /* inherits documentation from base class */
27432 {
27433  if (mDataPlottable)
27434  {
27435  const double value = mDataPlottable->interface1D()->dataMainValue(index);
27436  if (index >= 0 && index < mDataContainer->size() && mErrorType == etValueError)
27437  return QCPRange(value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus);
27438  else
27439  return QCPRange(value, value);
27440  } else
27441  {
27442  qDebug() << Q_FUNC_INFO << "no data plottable set";
27443  return QCPRange();
27444  }
27445 }
27446 
27447 /* inherits documentation from base class */
27448 QPointF QCPErrorBars::dataPixelPosition(int index) const
27449 {
27450  if (mDataPlottable)
27451  return mDataPlottable->interface1D()->dataPixelPosition(index);
27452  else
27453  qDebug() << Q_FUNC_INFO << "no data plottable set";
27454  return QPointF();
27455 }
27456 
27457 /* inherits documentation from base class */
27459 {
27460  if (mDataPlottable)
27461  {
27462  return mDataPlottable->interface1D()->sortKeyIsMainKey();
27463  } else
27464  {
27465  qDebug() << Q_FUNC_INFO << "no data plottable set";
27466  return true;
27467  }
27468 }
27469 
27473 QCPDataSelection QCPErrorBars::selectTestRect(const QRectF &rect, bool onlySelectable) const
27474 {
27475  QCPDataSelection result;
27476  if (!mDataPlottable)
27477  return result;
27478  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
27479  return result;
27480  if (!mKeyAxis || !mValueAxis)
27481  return result;
27482 
27483  QCPErrorBarsDataContainer::const_iterator visibleBegin, visibleEnd;
27484  getVisibleDataBounds(visibleBegin, visibleEnd, QCPDataRange(0, dataCount()));
27485 
27486  QVector<QLineF> backbones, whiskers;
27487  for (QCPErrorBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
27488  {
27489  backbones.clear();
27490  whiskers.clear();
27491  getErrorBarLines(it, backbones, whiskers);
27492  for (int i=0; i<backbones.size(); ++i)
27493  {
27494  if (rectIntersectsLine(rect, backbones.at(i)))
27495  {
27496  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
27497  break;
27498  }
27499  }
27500  }
27501  result.simplify();
27502  return result;
27503 }
27504 
27505 /* inherits documentation from base class */
27506 int QCPErrorBars::findBegin(double sortKey, bool expandedRange) const
27507 {
27508  if (mDataPlottable)
27509  {
27510  if (mDataContainer->isEmpty())
27511  return 0;
27512  int beginIndex = mDataPlottable->interface1D()->findBegin(sortKey, expandedRange);
27513  if (beginIndex >= mDataContainer->size())
27514  beginIndex = mDataContainer->size()-1;
27515  return beginIndex;
27516  } else
27517  qDebug() << Q_FUNC_INFO << "no data plottable set";
27518  return 0;
27519 }
27520 
27521 /* inherits documentation from base class */
27522 int QCPErrorBars::findEnd(double sortKey, bool expandedRange) const
27523 {
27524  if (mDataPlottable)
27525  {
27526  if (mDataContainer->isEmpty())
27527  return 0;
27528  int endIndex = mDataPlottable->interface1D()->findEnd(sortKey, expandedRange);
27529  if (endIndex > mDataContainer->size())
27530  endIndex = mDataContainer->size();
27531  return endIndex;
27532  } else
27533  qDebug() << Q_FUNC_INFO << "no data plottable set";
27534  return 0;
27535 }
27536 
27545 double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
27546 {
27547  if (!mDataPlottable) return -1;
27548 
27549  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
27550  return -1;
27551  if (!mKeyAxis || !mValueAxis)
27552  return -1;
27553 
27554  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
27555  {
27556  QCPErrorBarsDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
27557  double result = pointDistance(pos, closestDataPoint);
27558  if (details)
27559  {
27560  int pointIndex = closestDataPoint-mDataContainer->constBegin();
27561  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
27562  }
27563  return result;
27564  } else
27565  return -1;
27566 }
27567 
27568 /* inherits documentation from base class */
27570 {
27571  if (!mDataPlottable) return;
27572  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
27573  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
27574 
27575  // if the sort key isn't the main key, we must check the visibility for each data point/error bar individually
27576  // (getVisibleDataBounds applies range restriction, but otherwise can only return full data range):
27577  bool checkPointVisibility = !mDataPlottable->interface1D()->sortKeyIsMainKey();
27578 
27579  // check data validity if flag set:
27580 #ifdef QCUSTOMPLOT_CHECK_DATA
27581  QCPErrorBarsDataContainer::const_iterator it;
27582  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
27583  {
27584  if (QCP::isInvalidData(it->errorMinus, it->errorPlus))
27585  qDebug() << Q_FUNC_INFO << "Data point at index" << it-mDataContainer->constBegin() << "invalid." << "Plottable name:" << name();
27586  }
27587 #endif
27588 
27590  painter->setBrush(Qt::NoBrush);
27591  // loop over and draw segments of unselected/selected data:
27592  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
27593  getDataSegments(selectedSegments, unselectedSegments);
27594  allSegments << unselectedSegments << selectedSegments;
27595  QVector<QLineF> backbones, whiskers;
27596  for (int i=0; i<allSegments.size(); ++i)
27597  {
27598  QCPErrorBarsDataContainer::const_iterator begin, end;
27599  getVisibleDataBounds(begin, end, allSegments.at(i));
27600  if (begin == end)
27601  continue;
27602 
27603  bool isSelectedSegment = i >= unselectedSegments.size();
27604  if (isSelectedSegment && mSelectionDecorator)
27605  mSelectionDecorator->applyPen(painter);
27606  else
27607  painter->setPen(mPen);
27608  if (painter->pen().capStyle() == Qt::SquareCap)
27609  {
27610  QPen capFixPen(painter->pen());
27611  capFixPen.setCapStyle(Qt::FlatCap);
27612  painter->setPen(capFixPen);
27613  }
27614  backbones.clear();
27615  whiskers.clear();
27616  for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
27617  {
27618  if (!checkPointVisibility || errorBarVisible(it-mDataContainer->constBegin()))
27619  getErrorBarLines(it, backbones, whiskers);
27620  }
27621  painter->drawLines(backbones);
27622  painter->drawLines(whiskers);
27623  }
27624 
27625  // draw other selection decoration that isn't just line/scatter pens and brushes:
27626  if (mSelectionDecorator)
27628 }
27629 
27630 /* inherits documentation from base class */
27631 void QCPErrorBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
27632 {
27634  painter->setPen(mPen);
27635  if (mErrorType == etValueError && mValueAxis && mValueAxis->orientation() == Qt::Vertical)
27636  {
27637  painter->drawLine(QLineF(rect.center().x(), rect.top()+2, rect.center().x(), rect.bottom()-1));
27638  painter->drawLine(QLineF(rect.center().x()-4, rect.top()+2, rect.center().x()+4, rect.top()+2));
27639  painter->drawLine(QLineF(rect.center().x()-4, rect.bottom()-1, rect.center().x()+4, rect.bottom()-1));
27640  } else
27641  {
27642  painter->drawLine(QLineF(rect.left()+2, rect.center().y(), rect.right()-2, rect.center().y()));
27643  painter->drawLine(QLineF(rect.left()+2, rect.center().y()-4, rect.left()+2, rect.center().y()+4));
27644  painter->drawLine(QLineF(rect.right()-2, rect.center().y()-4, rect.right()-2, rect.center().y()+4));
27645  }
27646 }
27647 
27648 /* inherits documentation from base class */
27649 QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
27650 {
27651  if (!mDataPlottable)
27652  {
27653  foundRange = false;
27654  return QCPRange();
27655  }
27656 
27657  QCPRange range;
27658  bool haveLower = false;
27659  bool haveUpper = false;
27660  QCPErrorBarsDataContainer::const_iterator it;
27661  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
27662  {
27663  if (mErrorType == etValueError)
27664  {
27665  // error bar doesn't extend in key dimension (except whisker but we ignore that here), so only use data point center
27666  const double current = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
27667  if (qIsNaN(current)) continue;
27668  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27669  {
27670  if (current < range.lower || !haveLower)
27671  {
27672  range.lower = current;
27673  haveLower = true;
27674  }
27675  if (current > range.upper || !haveUpper)
27676  {
27677  range.upper = current;
27678  haveUpper = true;
27679  }
27680  }
27681  } else // mErrorType == etKeyError
27682  {
27683  const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
27684  if (qIsNaN(dataKey)) continue;
27685  // plus error:
27686  double current = dataKey + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
27687  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27688  {
27689  if (current > range.upper || !haveUpper)
27690  {
27691  range.upper = current;
27692  haveUpper = true;
27693  }
27694  }
27695  // minus error:
27696  current = dataKey - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
27697  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27698  {
27699  if (current < range.lower || !haveLower)
27700  {
27701  range.lower = current;
27702  haveLower = true;
27703  }
27704  }
27705  }
27706  }
27707 
27708  if (haveUpper && !haveLower)
27709  {
27710  range.lower = range.upper;
27711  haveLower = true;
27712  } else if (haveLower && !haveUpper)
27713  {
27714  range.upper = range.lower;
27715  haveUpper = true;
27716  }
27717 
27718  foundRange = haveLower && haveUpper;
27719  return range;
27720 }
27721 
27722 /* inherits documentation from base class */
27723 QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
27724 {
27725  if (!mDataPlottable)
27726  {
27727  foundRange = false;
27728  return QCPRange();
27729  }
27730 
27731  QCPRange range;
27732  const bool restrictKeyRange = inKeyRange != QCPRange();
27733  bool haveLower = false;
27734  bool haveUpper = false;
27735  QCPErrorBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();
27736  QCPErrorBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd();
27737  if (mDataPlottable->interface1D()->sortKeyIsMainKey() && restrictKeyRange)
27738  {
27739  itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower);
27740  itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper);
27741  }
27742  for (QCPErrorBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
27743  {
27744  if (restrictKeyRange)
27745  {
27746  const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
27747  if (dataKey < inKeyRange.lower || dataKey > inKeyRange.upper)
27748  continue;
27749  }
27750  if (mErrorType == etValueError)
27751  {
27752  const double dataValue = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin());
27753  if (qIsNaN(dataValue)) continue;
27754  // plus error:
27755  double current = dataValue + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
27756  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27757  {
27758  if (current > range.upper || !haveUpper)
27759  {
27760  range.upper = current;
27761  haveUpper = true;
27762  }
27763  }
27764  // minus error:
27765  current = dataValue - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
27766  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27767  {
27768  if (current < range.lower || !haveLower)
27769  {
27770  range.lower = current;
27771  haveLower = true;
27772  }
27773  }
27774  } else // mErrorType == etKeyError
27775  {
27776  // error bar doesn't extend in value dimension (except whisker but we ignore that here), so only use data point center
27777  const double current = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin());
27778  if (qIsNaN(current)) continue;
27779  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27780  {
27781  if (current < range.lower || !haveLower)
27782  {
27783  range.lower = current;
27784  haveLower = true;
27785  }
27786  if (current > range.upper || !haveUpper)
27787  {
27788  range.upper = current;
27789  haveUpper = true;
27790  }
27791  }
27792  }
27793  }
27794 
27795  if (haveUpper && !haveLower)
27796  {
27797  range.lower = range.upper;
27798  haveLower = true;
27799  } else if (haveLower && !haveUpper)
27800  {
27801  range.upper = range.lower;
27802  haveUpper = true;
27803  }
27804 
27805  foundRange = haveLower && haveUpper;
27806  return range;
27807 }
27808 
27820 void QCPErrorBars::getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector<QLineF> &backbones, QVector<QLineF> &whiskers) const
27821 {
27822  if (!mDataPlottable) return;
27823 
27824  int index = it-mDataContainer->constBegin();
27825  QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);
27826  if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y()))
27827  return;
27828  QCPAxis *errorAxis = mErrorType == etValueError ? mValueAxis.data() : mKeyAxis.data();
27829  QCPAxis *orthoAxis = mErrorType == etValueError ? mKeyAxis.data() : mValueAxis.data();
27830  const double centerErrorAxisPixel = errorAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
27831  const double centerOrthoAxisPixel = orthoAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
27832  const double centerErrorAxisCoord = errorAxis->pixelToCoord(centerErrorAxisPixel); // depending on plottable, this might be different from just mDataPlottable->interface1D()->dataMainKey/Value
27833  const double symbolGap = mSymbolGap*0.5*errorAxis->pixelOrientation();
27834  // plus error:
27835  double errorStart, errorEnd;
27836  if (!qIsNaN(it->errorPlus))
27837  {
27838  errorStart = centerErrorAxisPixel+symbolGap;
27839  errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord+it->errorPlus);
27840  if (errorAxis->orientation() == Qt::Vertical)
27841  {
27842  if ((errorStart > errorEnd) != errorAxis->rangeReversed())
27843  backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));
27844  whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));
27845  } else
27846  {
27847  if ((errorStart < errorEnd) != errorAxis->rangeReversed())
27848  backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));
27849  whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));
27850  }
27851  }
27852  // minus error:
27853  if (!qIsNaN(it->errorMinus))
27854  {
27855  errorStart = centerErrorAxisPixel-symbolGap;
27856  errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord-it->errorMinus);
27857  if (errorAxis->orientation() == Qt::Vertical)
27858  {
27859  if ((errorStart < errorEnd) != errorAxis->rangeReversed())
27860  backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));
27861  whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));
27862  } else
27863  {
27864  if ((errorStart > errorEnd) != errorAxis->rangeReversed())
27865  backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));
27866  whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));
27867  }
27868  }
27869 }
27870 
27889 void QCPErrorBars::getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
27890 {
27891  QCPAxis *keyAxis = mKeyAxis.data();
27892  QCPAxis *valueAxis = mValueAxis.data();
27893  if (!keyAxis || !valueAxis)
27894  {
27895  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
27896  end = mDataContainer->constEnd();
27897  begin = end;
27898  return;
27899  }
27900  if (!mDataPlottable || rangeRestriction.isEmpty())
27901  {
27902  end = mDataContainer->constEnd();
27903  begin = end;
27904  return;
27905  }
27906  if (!mDataPlottable->interface1D()->sortKeyIsMainKey())
27907  {
27908  // if the sort key isn't the main key, it's not possible to find a contiguous range of visible
27909  // data points, so this method then only applies the range restriction and otherwise returns
27910  // the full data range. Visibility checks must be done on a per-datapoin-basis during drawing
27911  QCPDataRange dataRange(0, mDataContainer->size());
27912  dataRange = dataRange.bounded(rangeRestriction);
27913  begin = mDataContainer->constBegin()+dataRange.begin();
27914  end = mDataContainer->constBegin()+dataRange.end();
27915  return;
27916  }
27917 
27918  // get visible data range via interface from data plottable, and then restrict to available error data points:
27919  const int n = qMin(mDataContainer->size(), mDataPlottable->interface1D()->dataCount());
27920  int beginIndex = mDataPlottable->interface1D()->findBegin(keyAxis->range().lower);
27921  int endIndex = mDataPlottable->interface1D()->findEnd(keyAxis->range().upper);
27922  int i = beginIndex;
27923  while (i > 0 && i < n && i > rangeRestriction.begin())
27924  {
27925  if (errorBarVisible(i))
27926  beginIndex = i;
27927  --i;
27928  }
27929  i = endIndex;
27930  while (i >= 0 && i < n && i < rangeRestriction.end())
27931  {
27932  if (errorBarVisible(i))
27933  endIndex = i+1;
27934  ++i;
27935  }
27936  QCPDataRange dataRange(beginIndex, endIndex);
27937  dataRange = dataRange.bounded(rangeRestriction.bounded(QCPDataRange(0, mDataContainer->size())));
27938  begin = mDataContainer->constBegin()+dataRange.begin();
27939  end = mDataContainer->constBegin()+dataRange.end();
27940 }
27941 
27948 double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const
27949 {
27950  closestData = mDataContainer->constEnd();
27951  if (!mDataPlottable || mDataContainer->isEmpty())
27952  return -1.0;
27953  if (!mKeyAxis || !mValueAxis)
27954  {
27955  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
27956  return -1.0;
27957  }
27958 
27959  QCPErrorBarsDataContainer::const_iterator begin, end;
27960  getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount()));
27961 
27962  // calculate minimum distances to error backbones (whiskers are ignored for speed) and find closestData iterator:
27963  double minDistSqr = (std::numeric_limits<double>::max)();
27964  QVector<QLineF> backbones, whiskers;
27965  for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
27966  {
27967  getErrorBarLines(it, backbones, whiskers);
27968  for (int i=0; i<backbones.size(); ++i)
27969  {
27970  const double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(backbones.at(i));
27971  if (currentDistSqr < minDistSqr)
27972  {
27973  minDistSqr = currentDistSqr;
27974  closestData = it;
27975  }
27976  }
27977  }
27978  return qSqrt(minDistSqr);
27979 }
27980 
27988 void QCPErrorBars::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const
27989 {
27990  selectedSegments.clear();
27991  unselectedSegments.clear();
27992  if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty
27993  {
27994  if (selected())
27995  selectedSegments << QCPDataRange(0, dataCount());
27996  else
27997  unselectedSegments << QCPDataRange(0, dataCount());
27998  } else
27999  {
28000  QCPDataSelection sel(selection());
28001  sel.simplify();
28002  selectedSegments = sel.dataRanges();
28003  unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges();
28004  }
28005 }
28006 
28016 bool QCPErrorBars::errorBarVisible(int index) const
28017 {
28018  QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);
28019  const double centerKeyPixel = mKeyAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
28020  if (qIsNaN(centerKeyPixel))
28021  return false;
28022 
28023  double keyMin, keyMax;
28024  if (mErrorType == etKeyError)
28025  {
28026  const double centerKey = mKeyAxis->pixelToCoord(centerKeyPixel);
28027  const double errorPlus = mDataContainer->at(index).errorPlus;
28028  const double errorMinus = mDataContainer->at(index).errorMinus;
28029  keyMax = centerKey+(qIsNaN(errorPlus) ? 0 : errorPlus);
28030  keyMin = centerKey-(qIsNaN(errorMinus) ? 0 : errorMinus);
28031  } else // mErrorType == etValueError
28032  {
28033  keyMax = mKeyAxis->pixelToCoord(centerKeyPixel+mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());
28034  keyMin = mKeyAxis->pixelToCoord(centerKeyPixel-mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());
28035  }
28036  return ((keyMax > mKeyAxis->range().lower) && (keyMin < mKeyAxis->range().upper));
28037 }
28038 
28046 bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const
28047 {
28048  if (pixelRect.left() > line.x1() && pixelRect.left() > line.x2())
28049  return false;
28050  else if (pixelRect.right() < line.x1() && pixelRect.right() < line.x2())
28051  return false;
28052  else if (pixelRect.top() > line.y1() && pixelRect.top() > line.y2())
28053  return false;
28054  else if (pixelRect.bottom() < line.y1() && pixelRect.bottom() < line.y2())
28055  return false;
28056  else
28057  return true;
28058 }
28059 /* end of 'src/plottables/plottable-errorbar.cpp' */
28060 
28061 
28062 /* including file 'src/items/item-straightline.cpp', size 7592 */
28063 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
28064 
28068 
28084  QCPAbstractItem(parentPlot),
28085  point1(createPosition(QLatin1String("point1"))),
28086  point2(createPosition(QLatin1String("point2")))
28087 {
28088  point1->setCoords(0, 0);
28089  point2->setCoords(1, 1);
28090 
28091  setPen(QPen(Qt::black));
28092  setSelectedPen(QPen(Qt::blue,2));
28093 }
28094 
28096 {
28097 }
28098 
28105 {
28106  mPen = pen;
28107 }
28108 
28115 {
28116  mSelectedPen = pen;
28117 }
28118 
28119 /* inherits documentation from base class */
28120 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28121 {
28122  Q_UNUSED(details)
28123  if (onlySelectable && !mSelectable)
28124  return -1;
28125 
28127 }
28128 
28129 /* inherits documentation from base class */
28131 {
28134  // get visible segment of straight line inside clipRect:
28135  double clipPad = mainPen().widthF();
28136  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
28137  // paint visible segment, if existent:
28138  if (!line.isNull())
28139  {
28140  painter->setPen(mainPen());
28141  painter->drawLine(line);
28142  }
28143 }
28144 
28152 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRect &rect) const
28153 {
28154  double bx, by;
28155  double gamma;
28156  QLineF result;
28157  if (vec.x() == 0 && vec.y() == 0)
28158  return result;
28159  if (qFuzzyIsNull(vec.x())) // line is vertical
28160  {
28161  // check top of rect:
28162  bx = rect.left();
28163  by = rect.top();
28164  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
28165  if (gamma >= 0 && gamma <= rect.width())
28166  result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
28167  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
28168  {
28169  // check left of rect:
28170  bx = rect.left();
28171  by = rect.top();
28172  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
28173  if (gamma >= 0 && gamma <= rect.height())
28174  result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
28175  } else // line is skewed
28176  {
28177  QList<QCPVector2D> pointVectors;
28178  // check top of rect:
28179  bx = rect.left();
28180  by = rect.top();
28181  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
28182  if (gamma >= 0 && gamma <= rect.width())
28183  pointVectors.append(QCPVector2D(bx+gamma, by));
28184  // check bottom of rect:
28185  bx = rect.left();
28186  by = rect.bottom();
28187  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
28188  if (gamma >= 0 && gamma <= rect.width())
28189  pointVectors.append(QCPVector2D(bx+gamma, by));
28190  // check left of rect:
28191  bx = rect.left();
28192  by = rect.top();
28193  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
28194  if (gamma >= 0 && gamma <= rect.height())
28195  pointVectors.append(QCPVector2D(bx, by+gamma));
28196  // check right of rect:
28197  bx = rect.right();
28198  by = rect.top();
28199  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
28200  if (gamma >= 0 && gamma <= rect.height())
28201  pointVectors.append(QCPVector2D(bx, by+gamma));
28202 
28203  // evaluate points:
28204  if (pointVectors.size() == 2)
28205  {
28206  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
28207  } else if (pointVectors.size() > 2)
28208  {
28209  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
28210  double distSqrMax = 0;
28211  QCPVector2D pv1, pv2;
28212  for (int i=0; i<pointVectors.size()-1; ++i)
28213  {
28214  for (int k=i+1; k<pointVectors.size(); ++k)
28215  {
28216  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
28217  if (distSqr > distSqrMax)
28218  {
28219  pv1 = pointVectors.at(i);
28220  pv2 = pointVectors.at(k);
28221  distSqrMax = distSqr;
28222  }
28223  }
28224  }
28225  result.setPoints(pv1.toPointF(), pv2.toPointF());
28226  }
28227  }
28228  return result;
28229 }
28230 
28237 {
28238  return mSelected ? mSelectedPen : mPen;
28239 }
28240 /* end of 'src/items/item-straightline.cpp' */
28241 
28242 
28243 /* including file 'src/items/item-line.cpp', size 8498 */
28244 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
28245 
28249 
28267  QCPAbstractItem(parentPlot),
28268  start(createPosition(QLatin1String("start"))),
28269  end(createPosition(QLatin1String("end")))
28270 {
28271  start->setCoords(0, 0);
28272  end->setCoords(1, 1);
28273 
28274  setPen(QPen(Qt::black));
28275  setSelectedPen(QPen(Qt::blue,2));
28276 }
28277 
28279 {
28280 }
28281 
28287 void QCPItemLine::setPen(const QPen &pen)
28288 {
28289  mPen = pen;
28290 }
28291 
28298 {
28299  mSelectedPen = pen;
28300 }
28301 
28311 {
28312  mHead = head;
28313 }
28314 
28324 {
28325  mTail = tail;
28326 }
28327 
28328 /* inherits documentation from base class */
28329 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28330 {
28331  Q_UNUSED(details)
28332  if (onlySelectable && !mSelectable)
28333  return -1;
28334 
28335  return qSqrt(QCPVector2D(pos).distanceSquaredToLine(start->pixelPosition(), end->pixelPosition()));
28336 }
28337 
28338 /* inherits documentation from base class */
28340 {
28341  QCPVector2D startVec(start->pixelPosition());
28342  QCPVector2D endVec(end->pixelPosition());
28343  if (qFuzzyIsNull((startVec-endVec).lengthSquared()))
28344  return;
28345  // get visible segment of straight line inside clipRect:
28346  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
28347  clipPad = qMax(clipPad, (double)mainPen().widthF());
28348  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
28349  // paint visible segment, if existent:
28350  if (!line.isNull())
28351  {
28352  painter->setPen(mainPen());
28353  painter->drawLine(line);
28354  painter->setBrush(Qt::SolidPattern);
28356  mTail.draw(painter, startVec, startVec-endVec);
28358  mHead.draw(painter, endVec, endVec-startVec);
28359  }
28360 }
28361 
28369 QLineF QCPItemLine::getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const
28370 {
28371  bool containsStart = rect.contains(start.x(), start.y());
28372  bool containsEnd = rect.contains(end.x(), end.y());
28373  if (containsStart && containsEnd)
28374  return QLineF(start.toPointF(), end.toPointF());
28375 
28376  QCPVector2D base = start;
28377  QCPVector2D vec = end-start;
28378  double bx, by;
28379  double gamma, mu;
28380  QLineF result;
28381  QList<QCPVector2D> pointVectors;
28382 
28383  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
28384  {
28385  // check top of rect:
28386  bx = rect.left();
28387  by = rect.top();
28388  mu = (by-base.y())/vec.y();
28389  if (mu >= 0 && mu <= 1)
28390  {
28391  gamma = base.x()-bx + mu*vec.x();
28392  if (gamma >= 0 && gamma <= rect.width())
28393  pointVectors.append(QCPVector2D(bx+gamma, by));
28394  }
28395  // check bottom of rect:
28396  bx = rect.left();
28397  by = rect.bottom();
28398  mu = (by-base.y())/vec.y();
28399  if (mu >= 0 && mu <= 1)
28400  {
28401  gamma = base.x()-bx + mu*vec.x();
28402  if (gamma >= 0 && gamma <= rect.width())
28403  pointVectors.append(QCPVector2D(bx+gamma, by));
28404  }
28405  }
28406  if (!qFuzzyIsNull(vec.x())) // line is not vertical
28407  {
28408  // check left of rect:
28409  bx = rect.left();
28410  by = rect.top();
28411  mu = (bx-base.x())/vec.x();
28412  if (mu >= 0 && mu <= 1)
28413  {
28414  gamma = base.y()-by + mu*vec.y();
28415  if (gamma >= 0 && gamma <= rect.height())
28416  pointVectors.append(QCPVector2D(bx, by+gamma));
28417  }
28418  // check right of rect:
28419  bx = rect.right();
28420  by = rect.top();
28421  mu = (bx-base.x())/vec.x();
28422  if (mu >= 0 && mu <= 1)
28423  {
28424  gamma = base.y()-by + mu*vec.y();
28425  if (gamma >= 0 && gamma <= rect.height())
28426  pointVectors.append(QCPVector2D(bx, by+gamma));
28427  }
28428  }
28429 
28430  if (containsStart)
28431  pointVectors.append(start);
28432  if (containsEnd)
28433  pointVectors.append(end);
28434 
28435  // evaluate points:
28436  if (pointVectors.size() == 2)
28437  {
28438  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
28439  } else if (pointVectors.size() > 2)
28440  {
28441  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
28442  double distSqrMax = 0;
28443  QCPVector2D pv1, pv2;
28444  for (int i=0; i<pointVectors.size()-1; ++i)
28445  {
28446  for (int k=i+1; k<pointVectors.size(); ++k)
28447  {
28448  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
28449  if (distSqr > distSqrMax)
28450  {
28451  pv1 = pointVectors.at(i);
28452  pv2 = pointVectors.at(k);
28453  distSqrMax = distSqr;
28454  }
28455  }
28456  }
28457  result.setPoints(pv1.toPointF(), pv2.toPointF());
28458  }
28459  return result;
28460 }
28461 
28468 {
28469  return mSelected ? mSelectedPen : mPen;
28470 }
28471 /* end of 'src/items/item-line.cpp' */
28472 
28473 
28474 /* including file 'src/items/item-curve.cpp', size 7248 */
28475 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
28476 
28480 
28505  QCPAbstractItem(parentPlot),
28506  start(createPosition(QLatin1String("start"))),
28507  startDir(createPosition(QLatin1String("startDir"))),
28508  endDir(createPosition(QLatin1String("endDir"))),
28509  end(createPosition(QLatin1String("end")))
28510 {
28511  start->setCoords(0, 0);
28512  startDir->setCoords(0.5, 0);
28513  endDir->setCoords(0, 0.5);
28514  end->setCoords(1, 1);
28515 
28516  setPen(QPen(Qt::black));
28517  setSelectedPen(QPen(Qt::blue,2));
28518 }
28519 
28521 {
28522 }
28523 
28529 void QCPItemCurve::setPen(const QPen &pen)
28530 {
28531  mPen = pen;
28532 }
28533 
28540 {
28541  mSelectedPen = pen;
28542 }
28543 
28553 {
28554  mHead = head;
28555 }
28556 
28566 {
28567  mTail = tail;
28568 }
28569 
28570 /* inherits documentation from base class */
28571 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28572 {
28573  Q_UNUSED(details)
28574  if (onlySelectable && !mSelectable)
28575  return -1;
28576 
28577  QPointF startVec(start->pixelPosition());
28578  QPointF startDirVec(startDir->pixelPosition());
28579  QPointF endDirVec(endDir->pixelPosition());
28580  QPointF endVec(end->pixelPosition());
28581 
28582  QPainterPath cubicPath(startVec);
28583  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
28584 
28585  QList<QPolygonF> polygons = cubicPath.toSubpathPolygons();
28586  if (polygons.isEmpty())
28587  return -1;
28588  const QPolygonF polygon = polygons.first();
28589  QCPVector2D p(pos);
28590  double minDistSqr = (std::numeric_limits<double>::max)();
28591  for (int i=1; i<polygon.size(); ++i)
28592  {
28593  double distSqr = p.distanceSquaredToLine(polygon.at(i-1), polygon.at(i));
28594  if (distSqr < minDistSqr)
28595  minDistSqr = distSqr;
28596  }
28597  return qSqrt(minDistSqr);
28598 }
28599 
28600 /* inherits documentation from base class */
28602 {
28603  QCPVector2D startVec(start->pixelPosition());
28604  QCPVector2D startDirVec(startDir->pixelPosition());
28605  QCPVector2D endDirVec(endDir->pixelPosition());
28606  QCPVector2D endVec(end->pixelPosition());
28607  if ((endVec-startVec).length() > 1e10) // too large curves cause crash
28608  return;
28609 
28610  QPainterPath cubicPath(startVec.toPointF());
28611  cubicPath.cubicTo(startDirVec.toPointF(), endDirVec.toPointF(), endVec.toPointF());
28612 
28613  // paint visible segment, if existent:
28614  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
28615  QRect cubicRect = cubicPath.controlPointRect().toRect();
28616  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
28617  cubicRect.adjust(0, 0, 1, 1);
28618  if (clip.intersects(cubicRect))
28619  {
28620  painter->setPen(mainPen());
28621  painter->drawPath(cubicPath);
28622  painter->setBrush(Qt::SolidPattern);
28624  mTail.draw(painter, startVec, M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
28626  mHead.draw(painter, endVec, -cubicPath.angleAtPercent(1)/180.0*M_PI);
28627  }
28628 }
28629 
28636 {
28637  return mSelected ? mSelectedPen : mPen;
28638 }
28639 /* end of 'src/items/item-curve.cpp' */
28640 
28641 
28642 /* including file 'src/items/item-rect.cpp', size 6479 */
28643 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
28644 
28648 
28664  QCPAbstractItem(parentPlot),
28665  topLeft(createPosition(QLatin1String("topLeft"))),
28666  bottomRight(createPosition(QLatin1String("bottomRight"))),
28667  top(createAnchor(QLatin1String("top"), aiTop)),
28668  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
28669  right(createAnchor(QLatin1String("right"), aiRight)),
28670  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
28671  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
28672  left(createAnchor(QLatin1String("left"), aiLeft))
28673 {
28674  topLeft->setCoords(0, 1);
28675  bottomRight->setCoords(1, 0);
28676 
28677  setPen(QPen(Qt::black));
28678  setSelectedPen(QPen(Qt::blue,2));
28679  setBrush(Qt::NoBrush);
28680  setSelectedBrush(Qt::NoBrush);
28681 }
28682 
28684 {
28685 }
28686 
28692 void QCPItemRect::setPen(const QPen &pen)
28693 {
28694  mPen = pen;
28695 }
28696 
28703 {
28704  mSelectedPen = pen;
28705 }
28706 
28713 void QCPItemRect::setBrush(const QBrush &brush)
28714 {
28715  mBrush = brush;
28716 }
28717 
28725 {
28727 }
28728 
28729 /* inherits documentation from base class */
28730 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28731 {
28732  Q_UNUSED(details)
28733  if (onlySelectable && !mSelectable)
28734  return -1;
28735 
28736  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition()).normalized();
28737  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
28738  return rectDistance(rect, pos, filledRect);
28739 }
28740 
28741 /* inherits documentation from base class */
28743 {
28744  QPointF p1 = topLeft->pixelPosition();
28745  QPointF p2 = bottomRight->pixelPosition();
28746  if (p1.toPoint() == p2.toPoint())
28747  return;
28748  QRectF rect = QRectF(p1, p2).normalized();
28749  double clipPad = mainPen().widthF();
28750  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
28751  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
28752  {
28753  painter->setPen(mainPen());
28754  painter->setBrush(mainBrush());
28755  painter->drawRect(rect);
28756  }
28757 }
28758 
28759 /* inherits documentation from base class */
28760 QPointF QCPItemRect::anchorPixelPosition(int anchorId) const
28761 {
28762  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());
28763  switch (anchorId)
28764  {
28765  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
28766  case aiTopRight: return rect.topRight();
28767  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
28768  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
28769  case aiBottomLeft: return rect.bottomLeft();
28770  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
28771  }
28772 
28773  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
28774  return QPointF();
28775 }
28776 
28783 {
28784  return mSelected ? mSelectedPen : mPen;
28785 }
28786 
28793 {
28794  return mSelected ? mSelectedBrush : mBrush;
28795 }
28796 /* end of 'src/items/item-rect.cpp' */
28797 
28798 
28799 /* including file 'src/items/item-text.cpp', size 13338 */
28800 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
28801 
28805 
28827  QCPAbstractItem(parentPlot),
28828  position(createPosition(QLatin1String("position"))),
28829  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
28830  top(createAnchor(QLatin1String("top"), aiTop)),
28831  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
28832  right(createAnchor(QLatin1String("right"), aiRight)),
28833  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
28834  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
28835  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
28836  left(createAnchor(QLatin1String("left"), aiLeft)),
28837  mText(QLatin1String("text")),
28838  mPositionAlignment(Qt::AlignCenter),
28839  mTextAlignment(Qt::AlignTop|Qt::AlignHCenter),
28840  mRotation(0)
28841 {
28842  position->setCoords(0, 0);
28843 
28844  setPen(Qt::NoPen);
28845  setSelectedPen(Qt::NoPen);
28846  setBrush(Qt::NoBrush);
28847  setSelectedBrush(Qt::NoBrush);
28848  setColor(Qt::black);
28849  setSelectedColor(Qt::blue);
28850 }
28851 
28853 {
28854 }
28855 
28859 void QCPItemText::setColor(const QColor &color)
28860 {
28861  mColor = color;
28862 }
28863 
28868 {
28870 }
28871 
28878 void QCPItemText::setPen(const QPen &pen)
28879 {
28880  mPen = pen;
28881 }
28882 
28890 {
28891  mSelectedPen = pen;
28892 }
28893 
28900 void QCPItemText::setBrush(const QBrush &brush)
28901 {
28902  mBrush = brush;
28903 }
28904 
28912 {
28914 }
28915 
28921 void QCPItemText::setFont(const QFont &font)
28922 {
28923  mFont = font;
28924 }
28925 
28932 {
28933  mSelectedFont = font;
28934 }
28935 
28942 void QCPItemText::setText(const QString &text)
28943 {
28944  mText = text;
28945 }
28946 
28959 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
28960 {
28961  mPositionAlignment = alignment;
28962 }
28963 
28967 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
28968 {
28969  mTextAlignment = alignment;
28970 }
28971 
28976 void QCPItemText::setRotation(double degrees)
28977 {
28978  mRotation = degrees;
28979 }
28980 
28985 void QCPItemText::setPadding(const QMargins &padding)
28986 {
28987  mPadding = padding;
28988 }
28989 
28990 /* inherits documentation from base class */
28991 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28992 {
28993  Q_UNUSED(details)
28994  if (onlySelectable && !mSelectable)
28995  return -1;
28996 
28997  // The rect may be rotated, so we transform the actual clicked pos to the rotated
28998  // coordinate system, so we can use the normal rectDistance function for non-rotated rects:
28999  QPointF positionPixels(position->pixelPosition());
29000  QTransform inputTransform;
29001  inputTransform.translate(positionPixels.x(), positionPixels.y());
29002  inputTransform.rotate(-mRotation);
29003  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
29004  QPointF rotatedPos = inputTransform.map(pos);
29005  QFontMetrics fontMetrics(mFont);
29006  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
29007  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
29008  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
29009  textBoxRect.moveTopLeft(textPos.toPoint());
29010 
29011  return rectDistance(textBoxRect, rotatedPos, true);
29012 }
29013 
29014 /* inherits documentation from base class */
29016 {
29017  QPointF pos(position->pixelPosition());
29018  QTransform transform = painter->transform();
29019  transform.translate(pos.x(), pos.y());
29020  if (!qFuzzyIsNull(mRotation))
29021  transform.rotate(mRotation);
29022  painter->setFont(mainFont());
29023  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
29024  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
29025  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
29026  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
29027  textBoxRect.moveTopLeft(textPos.toPoint());
29028  double clipPad = mainPen().widthF();
29029  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
29030  if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
29031  {
29032  painter->setTransform(transform);
29033  if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
29034  (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
29035  {
29036  painter->setPen(mainPen());
29037  painter->setBrush(mainBrush());
29038  painter->drawRect(textBoxRect);
29039  }
29040  painter->setBrush(Qt::NoBrush);
29041  painter->setPen(QPen(mainColor()));
29042  painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
29043  }
29044 }
29045 
29046 /* inherits documentation from base class */
29047 QPointF QCPItemText::anchorPixelPosition(int anchorId) const
29048 {
29049  // get actual rect points (pretty much copied from draw function):
29050  QPointF pos(position->pixelPosition());
29051  QTransform transform;
29052  transform.translate(pos.x(), pos.y());
29053  if (!qFuzzyIsNull(mRotation))
29054  transform.rotate(mRotation);
29055  QFontMetrics fontMetrics(mainFont());
29056  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
29057  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
29058  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
29059  textBoxRect.moveTopLeft(textPos.toPoint());
29060  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
29061 
29062  switch (anchorId)
29063  {
29064  case aiTopLeft: return rectPoly.at(0);
29065  case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
29066  case aiTopRight: return rectPoly.at(1);
29067  case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
29068  case aiBottomRight: return rectPoly.at(2);
29069  case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
29070  case aiBottomLeft: return rectPoly.at(3);
29071  case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
29072  }
29073 
29074  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
29075  return QPointF();
29076 }
29077 
29088 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
29089 {
29090  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
29091  return pos;
29092 
29093  QPointF result = pos; // start at top left
29094  if (positionAlignment.testFlag(Qt::AlignHCenter))
29095  result.rx() -= rect.width()/2.0;
29096  else if (positionAlignment.testFlag(Qt::AlignRight))
29097  result.rx() -= rect.width();
29098  if (positionAlignment.testFlag(Qt::AlignVCenter))
29099  result.ry() -= rect.height()/2.0;
29100  else if (positionAlignment.testFlag(Qt::AlignBottom))
29101  result.ry() -= rect.height();
29102  return result;
29103 }
29104 
29111 {
29112  return mSelected ? mSelectedFont : mFont;
29113 }
29114 
29121 {
29122  return mSelected ? mSelectedColor : mColor;
29123 }
29124 
29131 {
29132  return mSelected ? mSelectedPen : mPen;
29133 }
29134 
29141 {
29142  return mSelected ? mSelectedBrush : mBrush;
29143 }
29144 /* end of 'src/items/item-text.cpp' */
29145 
29146 
29147 /* including file 'src/items/item-ellipse.cpp', size 7863 */
29148 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
29149 
29153 
29169  QCPAbstractItem(parentPlot),
29170  topLeft(createPosition(QLatin1String("topLeft"))),
29171  bottomRight(createPosition(QLatin1String("bottomRight"))),
29172  topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
29173  top(createAnchor(QLatin1String("top"), aiTop)),
29174  topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
29175  right(createAnchor(QLatin1String("right"), aiRight)),
29176  bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
29177  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
29178  bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
29179  left(createAnchor(QLatin1String("left"), aiLeft)),
29180  center(createAnchor(QLatin1String("center"), aiCenter))
29181 {
29182  topLeft->setCoords(0, 1);
29183  bottomRight->setCoords(1, 0);
29184 
29185  setPen(QPen(Qt::black));
29186  setSelectedPen(QPen(Qt::blue, 2));
29187  setBrush(Qt::NoBrush);
29188  setSelectedBrush(Qt::NoBrush);
29189 }
29190 
29192 {
29193 }
29194 
29200 void QCPItemEllipse::setPen(const QPen &pen)
29201 {
29202  mPen = pen;
29203 }
29204 
29211 {
29212  mSelectedPen = pen;
29213 }
29214 
29221 void QCPItemEllipse::setBrush(const QBrush &brush)
29222 {
29223  mBrush = brush;
29224 }
29225 
29233 {
29235 }
29236 
29237 /* inherits documentation from base class */
29238 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
29239 {
29240  Q_UNUSED(details)
29241  if (onlySelectable && !mSelectable)
29242  return -1;
29243 
29244  QPointF p1 = topLeft->pixelPosition();
29245  QPointF p2 = bottomRight->pixelPosition();
29246  QPointF center((p1+p2)/2.0);
29247  double a = qAbs(p1.x()-p2.x())/2.0;
29248  double b = qAbs(p1.y()-p2.y())/2.0;
29249  double x = pos.x()-center.x();
29250  double y = pos.y()-center.y();
29251 
29252  // distance to border:
29253  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
29254  double result = qAbs(c-1)*qSqrt(x*x+y*y);
29255  // filled ellipse, allow click inside to count as hit:
29256  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
29257  {
29258  if (x*x/(a*a) + y*y/(b*b) <= 1)
29259  result = mParentPlot->selectionTolerance()*0.99;
29260  }
29261  return result;
29262 }
29263 
29264 /* inherits documentation from base class */
29266 {
29267  QPointF p1 = topLeft->pixelPosition();
29268  QPointF p2 = bottomRight->pixelPosition();
29269  if (p1.toPoint() == p2.toPoint())
29270  return;
29271  QRectF ellipseRect = QRectF(p1, p2).normalized();
29272  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
29273  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
29274  {
29275  painter->setPen(mainPen());
29276  painter->setBrush(mainBrush());
29277 #ifdef __EXCEPTIONS
29278  try // drawEllipse sometimes throws exceptions if ellipse is too big
29279  {
29280 #endif
29281  painter->drawEllipse(ellipseRect);
29282 #ifdef __EXCEPTIONS
29283  } catch (...)
29284  {
29285  qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
29286  setVisible(false);
29287  }
29288 #endif
29289  }
29290 }
29291 
29292 /* inherits documentation from base class */
29293 QPointF QCPItemEllipse::anchorPixelPosition(int anchorId) const
29294 {
29295  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());
29296  switch (anchorId)
29297  {
29298  case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
29299  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
29300  case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
29301  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
29302  case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
29303  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
29304  case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
29305  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
29306  case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
29307  }
29308 
29309  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
29310  return QPointF();
29311 }
29312 
29319 {
29320  return mSelected ? mSelectedPen : mPen;
29321 }
29322 
29329 {
29330  return mSelected ? mSelectedBrush : mBrush;
29331 }
29332 /* end of 'src/items/item-ellipse.cpp' */
29333 
29334 
29335 /* including file 'src/items/item-pixmap.cpp', size 10615 */
29336 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
29337 
29341 
29363  QCPAbstractItem(parentPlot),
29364  topLeft(createPosition(QLatin1String("topLeft"))),
29365  bottomRight(createPosition(QLatin1String("bottomRight"))),
29366  top(createAnchor(QLatin1String("top"), aiTop)),
29367  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
29368  right(createAnchor(QLatin1String("right"), aiRight)),
29369  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
29370  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
29371  left(createAnchor(QLatin1String("left"), aiLeft)),
29372  mScaled(false),
29373  mScaledPixmapInvalidated(true),
29374  mAspectRatioMode(Qt::KeepAspectRatio),
29375  mTransformationMode(Qt::SmoothTransformation)
29376 {
29377  topLeft->setCoords(0, 1);
29378  bottomRight->setCoords(1, 0);
29379 
29380  setPen(Qt::NoPen);
29381  setSelectedPen(QPen(Qt::blue));
29382 }
29383 
29385 {
29386 }
29387 
29391 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
29392 {
29393  mPixmap = pixmap;
29394  mScaledPixmapInvalidated = true;
29395  if (mPixmap.isNull())
29396  qDebug() << Q_FUNC_INFO << "pixmap is null";
29397 }
29398 
29403 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
29404 {
29405  mScaled = scaled;
29408  mScaledPixmapInvalidated = true;
29409 }
29410 
29416 void QCPItemPixmap::setPen(const QPen &pen)
29417 {
29418  mPen = pen;
29419 }
29420 
29427 {
29428  mSelectedPen = pen;
29429 }
29430 
29431 /* inherits documentation from base class */
29432 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
29433 {
29434  Q_UNUSED(details)
29435  if (onlySelectable && !mSelectable)
29436  return -1;
29437 
29438  return rectDistance(getFinalRect(), pos, true);
29439 }
29440 
29441 /* inherits documentation from base class */
29443 {
29444  bool flipHorz = false;
29445  bool flipVert = false;
29446  QRect rect = getFinalRect(&flipHorz, &flipVert);
29447  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
29448  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
29449  if (boundingRect.intersects(clipRect()))
29450  {
29451  updateScaledPixmap(rect, flipHorz, flipVert);
29452  painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
29453  QPen pen = mainPen();
29454  if (pen.style() != Qt::NoPen)
29455  {
29456  painter->setPen(pen);
29457  painter->setBrush(Qt::NoBrush);
29458  painter->drawRect(rect);
29459  }
29460  }
29461 }
29462 
29463 /* inherits documentation from base class */
29464 QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const
29465 {
29466  bool flipHorz;
29467  bool flipVert;
29468  QRect rect = getFinalRect(&flipHorz, &flipVert);
29469  // we actually want denormal rects (negative width/height) here, so restore
29470  // the flipped state:
29471  if (flipHorz)
29472  rect.adjust(rect.width(), 0, -rect.width(), 0);
29473  if (flipVert)
29474  rect.adjust(0, rect.height(), 0, -rect.height());
29475 
29476  switch (anchorId)
29477  {
29478  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
29479  case aiTopRight: return rect.topRight();
29480  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
29481  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
29482  case aiBottomLeft: return rect.bottomLeft();
29483  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
29484  }
29485 
29486  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
29487  return QPointF();
29488 }
29489 
29503 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
29504 {
29505  if (mPixmap.isNull())
29506  return;
29507 
29508  if (mScaled)
29509  {
29510 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29511  double devicePixelRatio = mPixmap.devicePixelRatio();
29512 #else
29513  double devicePixelRatio = 1.0;
29514 #endif
29515  if (finalRect.isNull())
29516  finalRect = getFinalRect(&flipHorz, &flipVert);
29517  if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size()/devicePixelRatio)
29518  {
29519  mScaledPixmap = mPixmap.scaled(finalRect.size()*devicePixelRatio, mAspectRatioMode, mTransformationMode);
29520  if (flipHorz || flipVert)
29521  mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
29522 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29523  mScaledPixmap.setDevicePixelRatio(devicePixelRatio);
29524 #endif
29525  }
29526  } else if (!mScaledPixmap.isNull())
29527  mScaledPixmap = QPixmap();
29528  mScaledPixmapInvalidated = false;
29529 }
29530 
29545 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
29546 {
29547  QRect result;
29548  bool flipHorz = false;
29549  bool flipVert = false;
29550  QPoint p1 = topLeft->pixelPosition().toPoint();
29551  QPoint p2 = bottomRight->pixelPosition().toPoint();
29552  if (p1 == p2)
29553  return QRect(p1, QSize(0, 0));
29554  if (mScaled)
29555  {
29556  QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
29557  QPoint topLeft = p1;
29558  if (newSize.width() < 0)
29559  {
29560  flipHorz = true;
29561  newSize.rwidth() *= -1;
29562  topLeft.setX(p2.x());
29563  }
29564  if (newSize.height() < 0)
29565  {
29566  flipVert = true;
29567  newSize.rheight() *= -1;
29568  topLeft.setY(p2.y());
29569  }
29570  QSize scaledSize = mPixmap.size();
29571 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29572  scaledSize /= mPixmap.devicePixelRatio();
29573  scaledSize.scale(newSize*mPixmap.devicePixelRatio(), mAspectRatioMode);
29574 #else
29575  scaledSize.scale(newSize, mAspectRatioMode);
29576 #endif
29577  result = QRect(topLeft, scaledSize);
29578  } else
29579  {
29580 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29581  result = QRect(p1, mPixmap.size()/mPixmap.devicePixelRatio());
29582 #else
29583  result = QRect(p1, mPixmap.size());
29584 #endif
29585  }
29586  if (flippedHorz)
29587  *flippedHorz = flipHorz;
29588  if (flippedVert)
29589  *flippedVert = flipVert;
29590  return result;
29591 }
29592 
29599 {
29600  return mSelected ? mSelectedPen : mPen;
29601 }
29602 /* end of 'src/items/item-pixmap.cpp' */
29603 
29604 
29605 /* including file 'src/items/item-tracer.cpp', size 14624 */
29606 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
29607 
29611 
29649  QCPAbstractItem(parentPlot),
29650  position(createPosition(QLatin1String("position"))),
29651  mSize(6),
29652  mStyle(tsCrosshair),
29653  mGraph(0),
29654  mGraphKey(0),
29655  mInterpolating(false)
29656 {
29657  position->setCoords(0, 0);
29658 
29659  setBrush(Qt::NoBrush);
29660  setSelectedBrush(Qt::NoBrush);
29661  setPen(QPen(Qt::black));
29662  setSelectedPen(QPen(Qt::blue, 2));
29663 }
29664 
29666 {
29667 }
29668 
29674 void QCPItemTracer::setPen(const QPen &pen)
29675 {
29676  mPen = pen;
29677 }
29678 
29685 {
29686  mSelectedPen = pen;
29687 }
29688 
29694 void QCPItemTracer::setBrush(const QBrush &brush)
29695 {
29696  mBrush = brush;
29697 }
29698 
29705 {
29707 }
29708 
29714 {
29715  mSize = size;
29716 }
29717 
29725 {
29726  mStyle = style;
29727 }
29728 
29740 {
29741  if (graph)
29742  {
29743  if (graph->parentPlot() == mParentPlot)
29744  {
29746  position->setAxes(graph->keyAxis(), graph->valueAxis());
29747  mGraph = graph;
29748  updatePosition();
29749  } else
29750  qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
29751  } else
29752  {
29753  mGraph = 0;
29754  }
29755 }
29756 
29767 {
29768  mGraphKey = key;
29769 }
29770 
29783 {
29784  mInterpolating = enabled;
29785 }
29786 
29787 /* inherits documentation from base class */
29788 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
29789 {
29790  Q_UNUSED(details)
29791  if (onlySelectable && !mSelectable)
29792  return -1;
29793 
29794  QPointF center(position->pixelPosition());
29795  double w = mSize/2.0;
29796  QRect clip = clipRect();
29797  switch (mStyle)
29798  {
29799  case tsNone: return -1;
29800  case tsPlus:
29801  {
29802  if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29803  return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(center+QPointF(-w, 0), center+QPointF(w, 0)),
29804  QCPVector2D(pos).distanceSquaredToLine(center+QPointF(0, -w), center+QPointF(0, w))));
29805  break;
29806  }
29807  case tsCrosshair:
29808  {
29809  return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(clip.left(), center.y()), QCPVector2D(clip.right(), center.y())),
29810  QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(center.x(), clip.top()), QCPVector2D(center.x(), clip.bottom()))));
29811  }
29812  case tsCircle:
29813  {
29814  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29815  {
29816  // distance to border:
29817  double centerDist = QCPVector2D(center-pos).length();
29818  double circleLine = w;
29819  double result = qAbs(centerDist-circleLine);
29820  // filled ellipse, allow click inside to count as hit:
29821  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
29822  {
29823  if (centerDist <= circleLine)
29824  result = mParentPlot->selectionTolerance()*0.99;
29825  }
29826  return result;
29827  }
29828  break;
29829  }
29830  case tsSquare:
29831  {
29832  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29833  {
29834  QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
29835  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
29836  return rectDistance(rect, pos, filledRect);
29837  }
29838  break;
29839  }
29840  }
29841  return -1;
29842 }
29843 
29844 /* inherits documentation from base class */
29846 {
29847  updatePosition();
29848  if (mStyle == tsNone)
29849  return;
29850 
29851  painter->setPen(mainPen());
29852  painter->setBrush(mainBrush());
29853  QPointF center(position->pixelPosition());
29854  double w = mSize/2.0;
29855  QRect clip = clipRect();
29856  switch (mStyle)
29857  {
29858  case tsNone: return;
29859  case tsPlus:
29860  {
29861  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29862  {
29863  painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
29864  painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
29865  }
29866  break;
29867  }
29868  case tsCrosshair:
29869  {
29870  if (center.y() > clip.top() && center.y() < clip.bottom())
29871  painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
29872  if (center.x() > clip.left() && center.x() < clip.right())
29873  painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
29874  break;
29875  }
29876  case tsCircle:
29877  {
29878  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29879  painter->drawEllipse(center, w, w);
29880  break;
29881  }
29882  case tsSquare:
29883  {
29884  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29885  painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
29886  break;
29887  }
29888  }
29889 }
29890 
29904 {
29905  if (mGraph)
29906  {
29908  {
29909  if (mGraph->data()->size() > 1)
29910  {
29911  QCPGraphDataContainer::const_iterator first = mGraph->data()->constBegin();
29912  QCPGraphDataContainer::const_iterator last = mGraph->data()->constEnd()-1;
29913  if (mGraphKey <= first->key)
29914  position->setCoords(first->key, first->value);
29915  else if (mGraphKey >= last->key)
29916  position->setCoords(last->key, last->value);
29917  else
29918  {
29920  if (it != mGraph->data()->constEnd()) // mGraphKey is not exactly on last iterator, but somewhere between iterators
29921  {
29923  ++it; // won't advance to constEnd because we handled that case (mGraphKey >= last->key) before
29924  if (mInterpolating)
29925  {
29926  // interpolate between iterators around mGraphKey:
29927  double slope = 0;
29928  if (!qFuzzyCompare((double)it->key, (double)prevIt->key))
29929  slope = (it->value-prevIt->value)/(it->key-prevIt->key);
29930  position->setCoords(mGraphKey, (mGraphKey-prevIt->key)*slope+prevIt->value);
29931  } else
29932  {
29933  // find iterator with key closest to mGraphKey:
29934  if (mGraphKey < (prevIt->key+it->key)*0.5)
29935  position->setCoords(prevIt->key, prevIt->value);
29936  else
29937  position->setCoords(it->key, it->value);
29938  }
29939  } else // mGraphKey is exactly on last iterator (should actually be caught when comparing first/last keys, but this is a failsafe for fp uncertainty)
29940  position->setCoords(it->key, it->value);
29941  }
29942  } else if (mGraph->data()->size() == 1)
29943  {
29944  QCPGraphDataContainer::const_iterator it = mGraph->data()->constBegin();
29945  position->setCoords(it->key, it->value);
29946  } else
29947  qDebug() << Q_FUNC_INFO << "graph has no data";
29948  } else
29949  qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
29950  }
29951 }
29952 
29959 {
29960  return mSelected ? mSelectedPen : mPen;
29961 }
29962 
29969 {
29970  return mSelected ? mSelectedBrush : mBrush;
29971 }
29972 /* end of 'src/items/item-tracer.cpp' */
29973 
29974 
29975 /* including file 'src/items/item-bracket.cpp', size 10687 */
29976 /* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */
29977 
29981 
30009  QCPAbstractItem(parentPlot),
30010  left(createPosition(QLatin1String("left"))),
30011  right(createPosition(QLatin1String("right"))),
30012  center(createAnchor(QLatin1String("center"), aiCenter)),
30013  mLength(8),
30014  mStyle(bsCalligraphic)
30015 {
30016  left->setCoords(0, 0);
30017  right->setCoords(1, 1);
30018 
30019  setPen(QPen(Qt::black));
30020  setSelectedPen(QPen(Qt::blue, 2));
30021 }
30022 
30024 {
30025 }
30026 
30036 void QCPItemBracket::setPen(const QPen &pen)
30037 {
30038  mPen = pen;
30039 }
30040 
30047 {
30048  mSelectedPen = pen;
30049 }
30050 
30060 {
30061  mLength = length;
30062 }
30063 
30070 {
30071  mStyle = style;
30072 }
30073 
30074 /* inherits documentation from base class */
30075 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
30076 {
30077  Q_UNUSED(details)
30078  if (onlySelectable && !mSelectable)
30079  return -1;
30080 
30081  QCPVector2D p(pos);
30082  QCPVector2D leftVec(left->pixelPosition());
30083  QCPVector2D rightVec(right->pixelPosition());
30084  if (leftVec.toPoint() == rightVec.toPoint())
30085  return -1;
30086 
30087  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
30088  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
30089  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
30090 
30091  switch (mStyle)
30092  {
30095  {
30096  double a = p.distanceSquaredToLine(centerVec-widthVec, centerVec+widthVec);
30097  double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec, centerVec-widthVec);
30098  double c = p.distanceSquaredToLine(centerVec+widthVec+lengthVec, centerVec+widthVec);
30099  return qSqrt(qMin(qMin(a, b), c));
30100  }
30103  {
30104  double a = p.distanceSquaredToLine(centerVec-widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
30105  double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec*0.7, centerVec-widthVec*0.75+lengthVec*0.15);
30106  double c = p.distanceSquaredToLine(centerVec+widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
30107  double d = p.distanceSquaredToLine(centerVec+widthVec+lengthVec*0.7, centerVec+widthVec*0.75+lengthVec*0.15);
30108  return qSqrt(qMin(qMin(a, b), qMin(c, d)));
30109  }
30110  }
30111  return -1;
30112 }
30113 
30114 /* inherits documentation from base class */
30116 {
30117  QCPVector2D leftVec(left->pixelPosition());
30118  QCPVector2D rightVec(right->pixelPosition());
30119  if (leftVec.toPoint() == rightVec.toPoint())
30120  return;
30121 
30122  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
30123  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
30124  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
30125 
30126  QPolygon boundingPoly;
30127  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
30128  << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
30129  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
30130  if (clip.intersects(boundingPoly.boundingRect()))
30131  {
30132  painter->setPen(mainPen());
30133  switch (mStyle)
30134  {
30135  case bsSquare:
30136  {
30137  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
30138  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
30139  painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
30140  break;
30141  }
30142  case bsRound:
30143  {
30144  painter->setBrush(Qt::NoBrush);
30145  QPainterPath path;
30146  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
30147  path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
30148  path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
30149  painter->drawPath(path);
30150  break;
30151  }
30152  case bsCurly:
30153  {
30154  painter->setBrush(Qt::NoBrush);
30155  QPainterPath path;
30156  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
30157  path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF());
30158  path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
30159  painter->drawPath(path);
30160  break;
30161  }
30162  case bsCalligraphic:
30163  {
30164  painter->setPen(Qt::NoPen);
30165  painter->setBrush(QBrush(mainPen().color()));
30166  QPainterPath path;
30167  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
30168 
30169  path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
30170  path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
30171 
30172  path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
30173  path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
30174 
30175  painter->drawPath(path);
30176  break;
30177  }
30178  }
30179  }
30180 }
30181 
30182 /* inherits documentation from base class */
30183 QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const
30184 {
30185  QCPVector2D leftVec(left->pixelPosition());
30186  QCPVector2D rightVec(right->pixelPosition());
30187  if (leftVec.toPoint() == rightVec.toPoint())
30188  return leftVec.toPointF();
30189 
30190  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
30191  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
30192  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
30193 
30194  switch (anchorId)
30195  {
30196  case aiCenter:
30197  return centerVec.toPointF();
30198  }
30199  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
30200  return QPointF();
30201 }
30202 
30209 {
30210  return mSelected ? mSelectedPen : mPen;
30211 }
30212 /* end of 'src/items/item-bracket.cpp' */
30213 
30214 
void setTickLabels(bool show)
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
SelectablePart getPartAt(const QPointF &pos) const
double mBaseValue
Definition: qcustomplot.h:5547
QFont mSelectedTickLabelFont
Definition: qcustomplot.h:2153
void setSelectedBrush(const QBrush &brush)
void drawBackground(QCPPainter *painter)
0x02 right margin
Definition: qcustomplot.h:202
bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove)
virtual QSize minimumOuterSizeHint() const
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QList< QCPAbstractPlottable * > selectedPlottables() const
QCPItemAnchor *const right
Definition: qcustomplot.h:6424
int iconTextPadding() const
Definition: qcustomplot.h:4849
virtual ~QCPErrorBars()
d
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)
QCPGrid(QCPAxis *parentAxis)
virtual ~QCPLayoutGrid()
void setBrush(const QBrush &brush)
void setBrush(const QBrush &brush)
double cleanMantissa(double input) const
bool isEmpty() const
Definition: qcustomplot.h:5726
QCPLineEnding tail() const
Definition: qcustomplot.h:6201
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
Qt::TransformationMode transformationMode() const
Definition: qcustomplot.h:6472
bool mReplotting
Definition: qcustomplot.h:3796
void setChannelFillGraph(QCPGraph *targetGraph)
QPen getBasePen() const
double pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const
int mNumberPrecision
Definition: qcustomplot.h:2155
void setWhiskerWidth(double pixels)
void drawSubGridLines(QCPPainter *painter) const
ScatterShape mShape
Definition: qcustomplot.h:2383
virtual int commonMargin(QCP::MarginSide side) const
bool visible() const
Definition: qcustomplot.h:712
virtual bool sortKeyIsMainKey() const Q_DECL_OVERRIDE
The base class tick generator used by QCPAxis to create tick positions and tick labels.
Definition: qcustomplot.h:1538
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:713
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes) ...
Definition: qcustomplot.h:256
QCPItemPosition *const position
Definition: qcustomplot.h:6581
QList< QCPGraph * > graphs() const
void setWidth(double width)
int mOpenGlMultisamples
Definition: qcustomplot.h:3798
virtual QRect clipRect() const Q_DECL_OVERRIDE
void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
virtual ~QCustomPlot()
QCPItemPosition *const start
Definition: qcustomplot.h:6162
QCPLayer * currentLayer() const
QCPLayer * mLayer
Definition: qcustomplot.h:738
QList< QCPAbstractPlottable * > plottables() const
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QFont mainFont() const
Manages the position of an item.
Definition: qcustomplot.h:3443
QColor mSelectedLabelColor
Definition: qcustomplot.h:2148
void enforceType(QCP::SelectionType type)
QCPVector2D & operator/=(double divisor)
QCP::AntialiasedElements antialiasedElements() const
Definition: qcustomplot.h:3638
bool mNumberBeautifulPowers
Definition: qcustomplot.h:2157
QPen pen() const
Definition: qcustomplot.h:1109
void setBrush(const QBrush &brush)
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
void insertColumn(int newIndex)
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
QCPDataSelection & operator-=(const QCPDataSelection &other)
QCPLayoutElement(QCustomPlot *parentPlot=0)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPItemEllipse(QCustomPlot *parentPlot)
bool removeGraph(QCPGraph *graph)
QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
QPen getSubTickPen() const
QCPItemAnchor *const left
Definition: qcustomplot.h:6270
void setTickLabelFont(const QFont &font)
QBrush mainBrush() const
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
QCPAxis * mParentAxis
Definition: qcustomplot.h:1905
void setTimeFormat(const QString &format)
void setSelectedPen(const QPen &pen)
bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
Q_SLOT void setSelected(bool selected)
Fractions are written as rationals using ASCII characters only, e.g. "1/4" or "1/8".
Definition: qcustomplot.h:1786
0x04 Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width...
Definition: qcustomplot.h:472
Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white.
Definition: qcustomplot.h:4466
0x0200 Zero-lines, see QCPGrid::setZeroLinePen
Definition: qcustomplot.h:228
void setNumberPrecision(int precision)
static QSize getFinalMinimumOuterSize(const QCPLayoutElement *el)
QColor mSelectedTextColor
Definition: qcustomplot.h:4995
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:3369
QBrush mainBrush() const
AxisType mAxisType
Definition: qcustomplot.h:2136
QCPLayoutInset * mInsetLayout
Definition: qcustomplot.h:4679
QVector< QPointF > dataToStepCenterLines(const QVector< QCPGraphData > &data) const
QList< QSharedPointer< QCPAbstractPaintBuffer > > mPaintBuffers
Definition: qcustomplot.h:3789
int top() const
Definition: qcustomplot.h:4657
QCPLineEnding lowerEnding
Definition: qcustomplot.h:2241
Milliseconds, one thousandth of a second (%z in setTimeFormat)
Definition: qcustomplot.h:1653
void dataRangeChanged(const QCPRange &newRange)
A filled circle.
Definition: qcustomplot.h:1493
int axisCount(QCPAxis::AxisType type) const
void setName(const QString &name)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setBracketBrush(const QBrush &brush)
void removeChild(QCPLayerable *layerable)
QCPBarsGroup * mBarsGroup
Definition: qcustomplot.h:5546
QCPItemPosition *const topLeft
Definition: qcustomplot.h:6419
void setPlottingHints(const QCP::PlottingHints &hints)
int dataRangeCount() const
Definition: qcustomplot.h:954
QCPRange mDataRange
Definition: qcustomplot.h:5797
Q_SLOT void setDataRange(const QCPRange &dataRange)
void moveRange(double diff)
void setLabel(const QString &str)
virtual ~QCPFinancial()
QSize maximumSize() const
Definition: qcustomplot.h:1232
void releaseElement(QCPLayoutElement *el)
bool isEmpty() const
Definition: qcustomplot.h:963
void setSymbolGap(double pixels)
void removeChildY(QCPItemPosition *pos)
void setPixmap(const QPixmap &pixmap)
Any combination of data points/ranges can be selected.
Definition: qcustomplot.h:300
void setAntialiasedZeroLine(bool enabled)
bool errorBarVisible(int index) const
QCPAxisRect * clipAxisRect() const
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
The abstract base class for layouts.
Definition: qcustomplot.h:1287
QRectF getBarRect(double key, double value) const
Resolution is given in dots per inch (DPI/PPI)
Definition: qcustomplot.h:172
QBrush mBrush
Definition: qcustomplot.h:6277
void setType(QCPAxis::AxisType type)
bool realVisibility() const
void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
Logarithmic scaling with correspondingly transformed axis coordinates (possibly also setTicker to a Q...
Definition: qcustomplot.h:1994
QList< QCPAxis * > rangeDragAxes(Qt::Orientation orientation)
void selectionChanged(bool selected)
QPen mSelectedPen
Definition: qcustomplot.h:6167
QList< QCPRange > mDragStartHorzRange
Definition: qcustomplot.h:4686
Q_SLOT void setSelectableParts(const SelectableParts &selectableParts)
bool segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const
void selectionChanged(bool selected)
bool removeItem(QCPAbstractItem *item)
void setLabelFont(const QFont &font)
QCPColorScale * mParentColorScale
Definition: qcustomplot.h:5031
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void setLabel(const QString &str)
QCPColorGradient gradient() const
Definition: qcustomplot.h:5769
bool inverted() const
Definition: qcustomplot.h:1509
QPixmap pixmap() const
Definition: qcustomplot.h:6469
void setBegin(int begin)
Definition: qcustomplot.h:911
void setKeyAxis(QCPAxis *axis)
QPointer< QCPGraph > mChannelFillGraph
Definition: qcustomplot.h:5223
TimeUnit mBiggestUnit
Definition: qcustomplot.h:1677
void setSelectedColor(const QColor &color)
void setColorStops(const QMap< double, QColor > &colorStops)
void setPen(const QPen &pen)
ScaleType mScaleType
Definition: qcustomplot.h:2168
QCPLayerable * parentLayerable() const
Definition: qcustomplot.h:714
Cosmetic pens are converted to pens with pixel width 1 when exporting.
Definition: qcustomplot.h:180
void setPen(const QPen &pen)
QRectF getQuartileBox(QCPStatisticalBoxDataContainer::const_iterator it) const
void setFont(const QFont &font)
line is drawn as steps where the step is in between two data points
Definition: qcustomplot.h:5184
PainterModes modes() const
Definition: qcustomplot.h:483
void setSelectedTickLabelColor(const QColor &color)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
SizeConstraintRect sizeConstraintRect() const
Definition: qcustomplot.h:1233
ROSCPP_DECL void start()
bool removeItem(int index)
Qt::Alignment positionAlignment() const
Definition: qcustomplot.h:6326
QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true)
void setPeriodicity(int multiplesOfPi)
QColor getTextColor() const
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE
QVector< int > getSectionSizes(QVector< int > maxSizes, QVector< int > minSizes, QVector< double > stretchFactors, int totalSize) const
void setScatterSkip(int skip)
QList< QCPDataRange > dataRanges() const
Definition: qcustomplot.h:957
void setWidthType(WidthType widthType)
void setRangeDrag(Qt::Orientations orientations)
bool clipToAxisRect() const
Definition: qcustomplot.h:3527
virtual QList< QCPLayoutElement * > elements(bool recursive) const
QCPLineEnding upperEnding
Definition: qcustomplot.h:2241
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)
ScaleType scaleType() const
Definition: qcustomplot.h:2016
virtual ~QCPBarsGroup()
QCPLayoutGrid * mPlotLayout
Definition: qcustomplot.h:3766
void setClipToAxisRect(bool clip)
QVector< QPointF > dataToStepLeftLines(const QVector< QCPGraphData > &data) const
void setValueRange(const QCPRange &valueRange)
virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
void setPen(const QPen &pen)
Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18))
bool mTicks
Definition: qcustomplot.h:2160
bool removeAt(int index)
void setSelectedPen(const QPen &pen)
virtual double dataMainValue(int index) const =0
QBrush mBrushNegative
Definition: qcustomplot.h:5939
QList< QPointer< QCPAxis > > mRangeZoomHorzAxis
Definition: qcustomplot.h:4682
QCPMarginGroup(QCustomPlot *parentPlot)
Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
double distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const
Controls how a plottable&#39;s data selection is drawn.
Definition: qcustomplot.h:3248
void setTightBoundary(bool enabled)
QColor mainTextColor() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lineData, QCPDataRange thisSegment, const QVector< QPointF > *otherData, QCPDataRange otherSegment) const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void setBrush(const QBrush &brush)
each data point is represented by a line parallel to the value axis, which reaches from the data poin...
Definition: qcustomplot.h:5185
Rows are filled first, and a new element is wrapped to the next column if the row count would exceed ...
Definition: qcustomplot.h:1349
Represents the range an axis is encompassing.
Definition: qcustomplot.h:777
QString text() const
Definition: qcustomplot.h:6325
virtual QCPItemPosition * toQCPItemPosition()
Definition: qcustomplot.h:3427
virtual void deselectEvent(bool *selectionStateChanged)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
void setRangeUpper(double upper)
QRect axisSelectionBox() const
Definition: qcustomplot.h:2234
Candlestick representation.
Definition: qcustomplot.h:5890
QPen mSubTickPen
Definition: qcustomplot.h:2164
void setTangentToData(bool enabled)
Hours (%h in setTimeFormat)
Definition: qcustomplot.h:1656
QCPLayoutElement * element(int row, int column) const
QLineF getRectClippedStraightLine(const QCPVector2D &point1, const QCPVector2D &vec, const QRect &rect) const
custom painter operations are performed per scatter (As QPainterPath, see setCustomPath) ...
Definition: qcustomplot.h:2343
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
bool antialiasing() const
Definition: qcustomplot.h:482
int mScatterSkip
Definition: qcustomplot.h:5222
void rescale(bool onlyVisiblePlottables=false)
Continuous lightness from black over icey colors to white (suited for non-biased data representation)...
Definition: qcustomplot.h:4461
QCPLayer * mCurrentLayer
Definition: qcustomplot.h:3781
QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const
void setSelectedBorderPen(const QPen &pen)
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE
Queues the entire replot for the next event loop iteration. This way multiple redundant replots can b...
Definition: qcustomplot.h:3624
void recalculateDataBounds()
f
QCPLineEnding mTail
Definition: qcustomplot.h:6220
A plus shaped crosshair which spans the complete axis rect.
Definition: qcustomplot.h:6544
QPen mSelectedTickPen
Definition: qcustomplot.h:2163
void setPen(const QPen &pen)
void mouseDoubleClick(QMouseEvent *event)
void setBracketPen(const QPen &pen)
virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE
void setNoAntialiasingOnDrag(bool enabled)
void setTickOrigin(double origin)
QFont font() const
Definition: qcustomplot.h:6323
std::vector< double > values
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
void setInteractions(const QCP::Interactions &interactions)
void setDateTimeFormat(const QString &format)
SpacingType mSpacingType
Definition: qcustomplot.h:5431
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:5103
double sign(double arg)
bool mSubGridVisible
Definition: qcustomplot.h:1900
void setTickLengthIn(int inside)
double width() const
Definition: qcustomplot.h:5511
QFont mSelectedLabelFont
Definition: qcustomplot.h:2147
QCPAxis * valueAxis() const
Definition: qcustomplot.h:3321
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
void setFromOther(const QCPScatterStyle &other, ScatterProperties properties)
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details)
void setData(QSharedPointer< QCPCurveDataContainer > data)
QCPAxisPainterPrivate(QCustomPlot *parentPlot)
QPointer< QCPAxisRect > mClipAxisRect
Definition: qcustomplot.h:3555
0x08 The shape property, see setShape
Definition: qcustomplot.h:2312
void setMinimumMargins(const QMargins &margins)
virtual double dataMainValue(int index) const Q_DECL_OVERRIDE
QCPDataSelection & operator+=(const QCPDataSelection &other)
bool isActive() const
Definition: qcustomplot.h:1111
QPen basePen() const
Definition: qcustomplot.h:2036
Q_SLOT void setGradient(const QCPColorGradient &gradient)
QFont getLabelFont() const
void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
void afterReplot()
QWeakPointer< QCPAbstractPaintBuffer > mPaintBuffer
Definition: qcustomplot.h:681
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
friend class QCPLegend
Definition: qcustomplot.h:3841
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE=0
QCustomPlot * mParentPlot
Definition: qcustomplot.h:2276
void setRangeDrag(bool enabled)
Holds the data of one single data point for QCPCurve.
Definition: qcustomplot.h:5271
void setSubTickLengthIn(int inside)
QVector< QLineF > getWhiskerBarLines(QCPStatisticalBoxDataContainer::const_iterator it) const
QPen getBorderPen() const
QList< QCPBars * > mBars
Definition: qcustomplot.h:5433
QColor mSelectedColor
Definition: qcustomplot.h:6363
Columns are filled first, and a new element is wrapped to the next row if the column count would exce...
Definition: qcustomplot.h:1350
bool rangeDrag() const
QString unicodeSubscript(int number) const
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
QVector< QPair< QCPDataRange, QCPDataRange > > getOverlappingSegments(QVector< QCPDataRange > thisSegments, const QVector< QPointF > *thisData, QVector< QCPDataRange > otherSegments, const QVector< QPointF > *otherData) const
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:2181
QPointF getFillBasePoint(QPointF matchingDataPoint) const
QCPItemPosition *const end
Definition: qcustomplot.h:6215
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:6420
int index() const
Definition: qcustomplot.h:659
void setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties)
0x004 axis (tick) labels will be cached as pixmaps, increasing replot performance.
Definition: qcustomplot.h:245
QCPScatterStyle outlierStyle() const
Definition: qcustomplot.h:5636
bool mIsAntialiasing
Definition: qcustomplot.h:506
{ssPlusSquare.png} a square with a plus inside
Definition: qcustomplot.h:2338
Qt::Alignment mTextAlignment
Definition: qcustomplot.h:6369
void setBrush(const QBrush &brush)
int findIndexAboveY(const QVector< QPointF > *data, double y) const
Q_SLOT void setSelectable(bool selectable)
ScatterShape shape() const
Definition: qcustomplot.h:2357
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
void setRowSpacing(int pixels)
LineStyle mLineStyle
Definition: qcustomplot.h:5220
QSet< QCPItemPosition * > mChildrenY
Definition: qcustomplot.h:3424
Bar width is given by a fraction of the axis rect size.
Definition: qcustomplot.h:5502
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:3498
QFont mLabelFont
Definition: qcustomplot.h:2147
QCustomPlot * mParentPlot
Definition: qcustomplot.h:736
void setColumnStretchFactor(int column, double factor)
QVector< DataType >::const_iterator const_iterator
Definition: qcustomplot.h:2415
QCPGraph * mGraph
Definition: qcustomplot.h:6589
void setBrush(const QBrush &brush)
QCPItemBracket(QCustomPlot *parentPlot)
void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation)
QCPBarsGroup * barsGroup() const
Definition: qcustomplot.h:5513
Holds the data of one single data point (one bar) for QCPBars.
Definition: qcustomplot.h:5451
QPen mainPen() const
QPen mZeroLinePen
Definition: qcustomplot.h:1902
virtual ~QCPMarginGroup()
Provides rect/rubber-band data selection and range zoom interaction.
Definition: qcustomplot.h:1099
bool removePlottable(QCPAbstractPlottable *plottable)
bool rangeZoom() const
QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
bool removeLayer(QCPLayer *layer)
Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and pha...
Definition: qcustomplot.h:4470
0x0000 No elements
Definition: qcustomplot.h:231
QCPTextElement(QCustomPlot *parentPlot)
virtual void axisRemoved(QCPAxis *axis)
Layer has its own paint buffer and may be replotted individually (see replot).
Definition: qcustomplot.h:649
int size() const
Definition: qcustomplot.h:907
QSharedPointer< QCPErrorBarsDataContainer > data() const
Definition: qcustomplot.h:6021
bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove)
QList< QCPAxis * > rangeZoomAxes(Qt::Orientation orientation)
QSet< QCPItemPosition * > mChildrenX
Definition: qcustomplot.h:3424
SpacingType spacingType() const
Definition: qcustomplot.h:5410
void setFieldWidth(TimeUnit unit, int width)
void setSelectedFont(const QFont &font)
virtual QCPRange dataValueRange(int index) const Q_DECL_OVERRIDE
ScaleStrategy mScaleStrategy
Definition: qcustomplot.h:1725
QList< QCPLegend * > selectedLegends() const
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
QPointer< QCPColorScale > mColorScale
Definition: qcustomplot.h:5803
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:4687
void setScatterSkip(int skip)
0x02 The brush property, see setBrush
Definition: qcustomplot.h:2310
QPen pen() const
Definition: qcustomplot.h:6249
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:4794
void setTextAlignment(Qt::Alignment alignment)
void setSelectedTextColor(const QColor &color)
QCPScatterStyle::ScatterProperties mUsedScatterProperties
Definition: qcustomplot.h:3281
virtual QCP::Interaction selectionCategory() const
bool selected() const
Definition: qcustomplot.h:4741
virtual void updateLayout()
Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allo...
Definition: qcustomplot.h:4465
0x00 Default mode for painting on screen devices
Definition: qcustomplot.h:469
QList< QCPDataRange > mDataRanges
Definition: qcustomplot.h:973
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:2296
int clearPlottables()
virtual ~QCPItemCurve()
The axis backbone and tick marks.
Definition: qcustomplot.h:2002
QPainterPath customPath() const
Definition: qcustomplot.h:2361
int tickLengthIn() const
QCPRange sanitizedForLogScale() const
QList< QCPRange > mDragStartVertRange
Definition: qcustomplot.h:4686
WidthType mWidthType
Definition: qcustomplot.h:5545
An ellipse is drawn. The size of the ellipse is given by the bracket width/height properties...
Definition: qcustomplot.h:4537
no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines) ...
Definition: qcustomplot.h:2326
bool mAntialiasedSubGrid
Definition: qcustomplot.h:1901
void setRangeReversed(bool reversed)
QCPLayoutElement * layoutElementAt(const QPointF &pos) const
virtual void updateLayout() Q_DECL_OVERRIDE
IMETHOD Vector diff(const Vector &p_w_a, const Vector &p_w_b, double dt=1)
QSharedPointer< QCPAxisTicker > ticker() const
Definition: qcustomplot.h:2019
virtual ~QCPAbstractPaintBuffer()
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
Q_SLOT void setGradient(const QCPColorGradient &gradient)
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint)
QBrush mainBrush() const
void toPainter(QCPPainter *painter, int width=0, int height=0)
void setTickCount(int count)
QVariant mMouseSignalLayerableDetails
Definition: qcustomplot.h:3795
double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
QByteArray mLabelParameterHash
Definition: qcustomplot.h:2277
void drawBackground(QCPPainter *painter)
void setTickLengthOut(int outside)
void setSelectedPen(const QPen &pen)
QCPMarginGroup * marginGroup(QCP::MarginSide side) const
Definition: qcustomplot.h:1234
virtual bool take(QCPLayoutElement *element) Q_DECL_OVERRIDE
virtual QCPLayoutElement * elementAt(int index) const Q_DECL_OVERRIDE
QColor mSelectedTickLabelColor
Definition: qcustomplot.h:2154
QCPAbstractPlottable * plottableAt(const QPointF &pos, bool onlySelectable=false) const
bool mAdaptiveSampling
Definition: qcustomplot.h:5224
bool stopsUseAlpha() const
QCPSelectionDecorator * mSelectionDecorator
Definition: qcustomplot.h:3372
friend class QCPLayer
Definition: qcustomplot.h:3843
QList< QCPGraph * > mGraphs
Definition: qcustomplot.h:3769
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Defines an abstract interface for one-dimensional plottables.
Definition: qcustomplot.h:3858
void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void clearItems()
virtual ~QCPLayer()
ResolutionUnit
Definition: qcustomplot.h:170
void setMaximumSize(const QSize &size)
virtual QSize maximumOuterSizeHint() const Q_DECL_OVERRIDE
QList< QCPAbstractPlottable * > plottables() const
void applyScattersAntialiasingHint(QCPPainter *painter) const
bool selected() const
Definition: qcustomplot.h:4964
bool registerGraph(QCPGraph *graph)
int getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:6263
QCPAxisRect * axisRectAt(const QPointF &pos) const
QCPAxis * axis(QCPAxis::AxisType type, int index=0) const
QString name() const
Definition: qcustomplot.h:3315
QCPLegend * mParentLegend
Definition: qcustomplot.h:4760
void getVisibleDataBounds(QCPFinancialDataContainer::const_iterator &begin, QCPFinancialDataContainer::const_iterator &end) const
QCPLineEnding mHead
Definition: qcustomplot.h:6220
virtual int getSubTickCount(double tickStep)
double pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const
Replots immediately, but queues the widget repaint, by calling QWidget::update() after the replot...
Definition: qcustomplot.h:3622
virtual void draw(QCPPainter *painter)
bool mInterpolate
Definition: qcustomplot.h:5801
void setKeySize(int keySize)
void replot()
0x04 top margin
Definition: qcustomplot.h:203
SignDomain
Definition: qcustomplot.h:191
0x0020 Main lines of plottables
Definition: qcustomplot.h:224
virtual QList< QCPLayoutElement * > elements(bool recursive) const Q_DECL_OVERRIDE
virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE
QCPLayoutInset * insetLayout() const
Definition: qcustomplot.h:4645
QCPRange keyRange() const
Definition: qcustomplot.h:5702
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
Colors suitable to represent different elevations on geographical maps.
Definition: qcustomplot.h:4464
bool ticks() const
Definition: qcustomplot.h:2020
friend class QCPPlottableLegendItem
Definition: qcustomplot.h:3395
QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
Fractions are displayed as regular decimal floating point numbers, e.g. "0.25" or "0...
Definition: qcustomplot.h:1785
void getCurveLines(QVector< QPointF > *lines, const QCPDataRange &dataRange, double penWidth) const
void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
virtual QByteArray generateLabelParameterHash() const
virtual ~QCPItemAnchor()
double mGraphKey
Definition: qcustomplot.h:6590
QList< QPointer< QCPAxis > > mRangeZoomVertAxis
Definition: qcustomplot.h:4682
unsigned char alpha(int keyIndex, int valueIndex)
bool isEmpty() const
Definition: qcustomplot.h:916
void setSelectedFont(const QFont &font)
QCPGrid * grid() const
Definition: qcustomplot.h:2056
QHash< QCP::MarginSide, QCPMarginGroup * > mMarginGroups
Definition: qcustomplot.h:1266
void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false)
static AxisType marginSideToAxisType(QCP::MarginSide side)
QRectF selectionHitBox(QCPFinancialDataContainer::const_iterator it) const
QSharedPointer< QCPStatisticalBoxDataContainer > data() const
Definition: qcustomplot.h:5629
virtual int dataCount() const Q_DECL_OVERRIDE
QCPSelectionRect(QCustomPlot *parentPlot)
virtual QVector< QString > createLabelVector(const QVector< double > &ticks, const QLocale &locale, QChar formatChar, int precision)
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:4687
enum QCPAxisTickerDateTime::DateStrategy mDateStrategy
QCPRange mValueRange
Definition: qcustomplot.h:5733
QPointer< QCPAbstractPlottable > mDataPlottable
Definition: qcustomplot.h:6061
void setBufferDevicePixelRatio(double ratio)
void setInsetPlacement(int index, InsetPlacement placement)
void zoom(const QRectF &pixelRect)
QPen mSelectedSubTickPen
Definition: qcustomplot.h:2164
double upper
Definition: qcustomplot.h:780
void setInterpolating(bool enabled)
bool contains(const QCPDataSelection &other) const
virtual double dataMainKey(int index) const Q_DECL_OVERRIDE
void drawPolyline(QCPPainter *painter, const QVector< QPointF > &lineData) const
Definition: qcustomplot.h:4388
{ssPlus.png} a plus
Definition: qcustomplot.h:2329
void selectableChanged(const QCPAxis::SelectableParts &parts)
QCPItemText(QCustomPlot *parentPlot)
When dragging the mouse, a selection rect becomes active. Upon releasing, the axes that are currently...
Definition: qcustomplot.h:273
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
void setWrap(int count)
void setInsetRect(int index, const QRectF &rect)
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
virtual int size() const
bool registerPlottable(QCPAbstractPlottable *plottable)
Replots immediately and repaints the widget immediately by calling QWidget::repaint() after the replo...
Definition: qcustomplot.h:3621
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QMap< double, QString > & ticks()
Definition: qcustomplot.h:1744
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
QCPGraph * graph() const
Definition: qcustomplot.h:6560
double mTickOrigin
Definition: qcustomplot.h:1574
Q_SLOT void setSelection(QCPDataSelection selection)
QCP::MarginSides mAutoMargins
Definition: qcustomplot.h:1265
QList< QCPItemPosition * > mPositions
Definition: qcustomplot.h:3556
QCPItemAnchor * parentAnchorX() const
Definition: qcustomplot.h:3472
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
bool visible() const
Definition: qcustomplot.h:661
AxisType axisType() const
Definition: qcustomplot.h:2014
double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
bool addElement(int row, int column, QCPLayoutElement *element)
QCPLayer * layer() const
Definition: qcustomplot.h:715
void setData(QSharedPointer< QCPBarsDataContainer > data)
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
virtual ~QCPItemStraightLine()
QList< QCPAbstractItem * > items() const
QCPItemAnchor * parentAnchorY() const
Definition: qcustomplot.h:3473
double lower
Definition: qcustomplot.h:780
bool mBackgroundScaled
Definition: qcustomplot.h:3779
Q_SLOT void setRange(const QCPRange &range)
static QCPFinancialDataContainer timeSeriesToOhlc(const QVector< double > &time, const QVector< double > &value, double timeBinSize, double timeBinOffset=0)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:5798
A brace with angled edges.
Definition: qcustomplot.h:6624
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:3777
void setSelectedTextColor(const QColor &color)
A layer that may contain objects, to control the rendering order.
Definition: qcustomplot.h:628
QPointer< QCPColorScaleAxisRectPrivate > mAxisRect
Definition: qcustomplot.h:5108
void mousePress(QMouseEvent *event)
void setSize(double size)
void setPen(const QPen &pen)
QCPItemPosition *const point2
Definition: qcustomplot.h:6114
QVariant mMouseEventLayerableDetails
Definition: qcustomplot.h:3794
0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixma...
Definition: qcustomplot.h:471
double lengthSquared() const
Definition: qcustomplot.h:407
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
0x02 Axis is vertical and on the right side of the axis rect
Definition: qcustomplot.h:1973
void setMode(PainterMode mode, bool enabled=true)
QColor mainColor() const
QPen pen() const
Definition: qcustomplot.h:6103
virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE
void setInverted(bool inverted)
static bool validRange(double lower, double upper)
Handles the different ending decorations for line-like items.
Definition: qcustomplot.h:1474
QRect viewport() const
Definition: qcustomplot.h:3632
double pixelToCoord(double value) const
A filled arrow head with a straight/flat back (a triangle)
Definition: qcustomplot.h:1490
bool mayTraverse(int prevRegion, int currentRegion) const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:3421
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > &scatters, const QCPScatterStyle &style) const
width is given by a fraction of the axis rect size
Definition: qcustomplot.h:5879
The positive sign domain, i.e. numbers greater than zero.
Definition: qcustomplot.h:193
void setColorScale(QCPColorScale *colorScale)
virtual QCPLayoutElement * elementAt(int index) const Q_DECL_OVERRIDE
Responsible for drawing the grid of a QCPAxis.
Definition: qcustomplot.h:1868
void applyBrush(QCPPainter *painter) const
virtual QSize sizeHint() const Q_DECL_OVERRIDE
void setDevicePixelRatio(double ratio)
Describes a data set by holding multiple QCPDataRange instances.
Definition: qcustomplot.h:932
QCP::SelectionType mSelectable
Definition: qcustomplot.h:3370
virtual int calculateAutoMargin(QCP::MarginSide side)
double mSpacing
Definition: qcustomplot.h:5432
QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
No line is drawn between data points (e.g. only scatters)
Definition: qcustomplot.h:5317
geometry_msgs::TransformStamped t
Q_SLOT void setDataRange(const QCPRange &dataRange)
0x004 The user can select multiple objects by holding the modifier set by QCustomPlot::setMultiSelect...
Definition: qcustomplot.h:258
0xFF all margins
Definition: qcustomplot.h:205
double value() const
Definition: qcustomplot.h:3475
static void connectBars(QCPBars *lower, QCPBars *upper)
bool createAlpha(bool initializeOpaque=true)
void itemClick(QCPAbstractItem *item, QMouseEvent *event)
void selectionChangedByUser()
void setBrush(const QBrush &brush)
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
void expand(const QCPRange &otherRange)
void setMode(LayerMode mode)
double distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const
double length() const
Definition: qcustomplot.h:406
void layerChanged(QCPLayer *newLayer)
void setBackgroundScaled(bool scaled)
TracerStyle style() const
Definition: qcustomplot.h:6559
void setBarsGroup(QCPBarsGroup *barsGroup)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
bool addItem(QCPAbstractLegendItem *item)
virtual void keyPressEvent(QKeyEvent *event)
void setIconTextPadding(int padding)
{ssCrossSquare.png} a square with a cross inside
Definition: qcustomplot.h:2337
void setBrushNegative(const QBrush &brush)
int rowCount() const
Definition: qcustomplot.h:1358
QCPItemRect(QCustomPlot *parentPlot)
void fillAlpha(unsigned char alpha)
void moveAbove(QCPBars *bars)
QList< QCPLayerable * > mChildren
Definition: qcustomplot.h:676
void setTickLabelPadding(int padding)
virtual void updateMapImage()
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
ExportPen
Definition: qcustomplot.h:180
Continuous lightness from black over firey colors to white (suited for non-biased data representation...
Definition: qcustomplot.h:4460
QCPDataRange adjusted(int changeBegin, int changeEnd) const
Definition: qcustomplot.h:920
QCPColorGradient mGradient
Definition: qcustomplot.h:5800
double length() const
Definition: qcustomplot.h:6637
virtual void moveSelection(QMouseEvent *event)
QSharedPointer< QCPBarsDataContainer > data() const
Definition: qcustomplot.h:5518
QCPAxis * yAxis
Definition: qcustomplot.h:3739
QPen mSelectedPen
Definition: qcustomplot.h:6364
virtual int dataCount() const Q_DECL_OVERRIDE
Definition: qcustomplot.h:4119
{ssCross.png} a cross
Definition: qcustomplot.h:2328
void setWhiskerPen(const QPen &pen)
0x08 Axis is horizontal and on the bottom side of the axis rect
Definition: qcustomplot.h:1975
QCustomPlot * mParentPlot
Definition: qcustomplot.h:673
void gradientChanged(const QCPColorGradient &newGradient)
0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts) ...
Definition: qcustomplot.h:261
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const =0
QBrush mBrush
Definition: qcustomplot.h:6365
Q_SLOT bool setLayer(QCPLayer *layer)
QBrush brush() const
Definition: qcustomplot.h:3257
Qt::TransformationMode mTransformationMode
Definition: qcustomplot.h:6503
void setCustomPath(const QPainterPath &customPath)
QCache< QString, CachedLabel > mLabelCache
Definition: qcustomplot.h:2278
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void getLines(QVector< QPointF > *lines, const QCPDataRange &dataRange) const
double mStackingGap
Definition: qcustomplot.h:5548
Defines a color gradient for use with e.g. QCPColorMap.
Definition: qcustomplot.h:4441
void rangeChanged(const QCPRange &newRange)
Q_SLOT void setSelectedParts(const SelectableParts &selectedParts)
void setInterpolate(bool enabled)
void getPixelWidth(double key, double &lower, double &upper) const
QSharedPointer< QCPCurveDataContainer > data() const
Definition: qcustomplot.h:5326
QCPItemPosition *const right
Definition: qcustomplot.h:6650
virtual void legendRemoved(QCPLegend *legend)
void setAdaptiveSampling(bool enabled)
QCPItemAnchor *const bottom
Definition: qcustomplot.h:6426
QPixmap mLegendIcon
Definition: qcustomplot.h:5807
QCPRange expanded(const QCPRange &otherRange) const
AntialiasedElement
Definition: qcustomplot.h:219
A half ellipse is drawn. The size of the ellipse is given by the bracket width/height properties...
Definition: qcustomplot.h:4536
QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId=-1)
QList< QCPBars * > bars() const
Definition: qcustomplot.h:5418
QVector< QCPDataRange > getNonNanSegments(const QVector< QPointF > *lineData, Qt::Orientation keyOrientation) const
void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
void updateLayerIndices() const
InsetPlacement insetPlacement(int index) const
ErrorType mErrorType
Definition: qcustomplot.h:6062
width is in key coordinates and thus scales with the key axis range
Definition: qcustomplot.h:5880
QCPDataRange intersection(const QCPDataRange &other) const
Q_SLOT void setSelectable(QCP::SelectionType selectable)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Q_SLOT void deselectAll()
virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:6264
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
Definition: qcustomplot.h:2334
QCPColorScale(QCustomPlot *parentPlot)
void setTangentAverage(int pointCount)
void setSelectedFont(const QFont &font)
QRgb color(double position, const QCPRange &range, bool logarithmic=false)
void setStyle(BracketStyle style)
void setPositionAlignment(Qt::Alignment alignment)
int numberPrecision() const
Definition: qcustomplot.h:2028
QPen mainPen() const
QCPRange mDataBounds
Definition: qcustomplot.h:5739
QFont getTickLabelFont() const
QCPRange dataRange() const
Definition: qcustomplot.h:5765
void setBackground(const QPixmap &pm)
QPen pen() const
Definition: qcustomplot.h:6635
Holds the data of one single data point for QCPStatisticalBox.
Definition: qcustomplot.h:5574
QCPAxis * addAxis(QCPAxis::AxisType type, QCPAxis *axis=0)
QBrush brush() const
Definition: qcustomplot.h:2359
Phase in which the margins are calculated and set.
Definition: qcustomplot.h:1203
int left() const
Definition: qcustomplot.h:4655
bool intersects(const QCPDataRange &other) const
void setRangeLower(double lower)
void update(const std::string &key, const XmlRpc::XmlRpcValue &v)
void setAntialiasedSubGrid(bool enabled)
Less readable tick steps are allowed which in turn facilitates getting closer to the requested tick c...
Definition: qcustomplot.h:1550
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QPen pen() const
Definition: qcustomplot.h:6148
QPen getTickPen() const
LayerMode mMode
Definition: qcustomplot.h:678
QVector< QPointF > dataToLines(const QVector< QCPGraphData > &data) const
QLatin1Char mNumberFormatChar
Definition: qcustomplot.h:2156
QPen pen() const
Definition: qcustomplot.h:6473
QCP::AntialiasedElements notAntialiasedElements() const
Definition: qcustomplot.h:3639
QCPAxisPainterPrivate * mAxisPainter
Definition: qcustomplot.h:2172
void setPlottingHint(QCP::PlottingHint hint, bool enabled=true)
QSize size() const
Definition: qcustomplot.h:4661
void setIconBorderPen(const QPen &pen)
void unregisterBars(QCPBars *bars)
virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
void setTextColor(const QColor &color)
QCP::AntialiasedElements mNotAntialiasedElements
Definition: qcustomplot.h:3772
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QPen mainPen() const
void loadPreset(GradientPreset preset)
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:3590
virtual void reallocateBuffer() Q_DECL_OVERRIDE
virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
A bar perpendicular to the line.
Definition: qcustomplot.h:1496
bool mAntialiasedZeroLine
Definition: qcustomplot.h:1901
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:3780
QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange)
QCPAxis::AxisType type() const
Definition: qcustomplot.h:5068
One individual data point can be selected at a time.
Definition: qcustomplot.h:298
virtual ~QCPSelectionDecorator()
a custom pixmap specified by setPixmap, centered on the data point coordinates
Definition: qcustomplot.h:2342
void getMaximumRowColSizes(QVector< int > *maxColWidths, QVector< int > *maxRowHeights) const
QCPScatterStyle scatterStyle() const
Definition: qcustomplot.h:3258
QPen mSelectedPen
Definition: qcustomplot.h:6276
void setBarWidth(int width)
QVector< QPointF > getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
Q_SLOT void setScaleType(QCPAxis::ScaleType type)
double mRangeZoomFactorVert
Definition: qcustomplot.h:4683
QCPLayerable * layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const
QSize iconSize() const
Definition: qcustomplot.h:4848
QPixmap mPixmap
Definition: qcustomplot.h:6498
QRect mTextBoundingRect
Definition: qcustomplot.h:4996
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:3778
QCPRange mDataRange
Definition: qcustomplot.h:5102
void setBasePen(const QPen &pen)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QBrush brush() const
Definition: qcustomplot.h:6407
QCPVector2D perpendicular() const
Definition: qcustomplot.h:414
Qt::AspectRatioMode aspectRatioMode() const
Definition: qcustomplot.h:6471
QPen mSelectedBasePen
Definition: qcustomplot.h:2142
QCPItemPosition *const left
Definition: qcustomplot.h:6649
LabelSide tickLabelSide() const
QCPItemPosition *const startDir
Definition: qcustomplot.h:6213
void setWidth(double width)
QPointer< QCPLayerable > mParentLayerable
Definition: qcustomplot.h:737
Continuous lightness from black over weak blueish colors to white (suited for non-biased data represe...
Definition: qcustomplot.h:4462
QCPItemPosition *const topLeft
Definition: qcustomplot.h:6485
void setSelectedLabelColor(const QColor &color)
void setSelectedPen(const QPen &pen)
QPointer< QCPAxisRect > mAxisRect
Definition: qcustomplot.h:3499
void setPen(const QPen &pen)
void setStyle(TracerStyle style)
0x0002 Grid lines
Definition: qcustomplot.h:220
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)
QBrush mSelectedBrush
Definition: qcustomplot.h:6436
double width() const
Definition: qcustomplot.h:1507
0x04 The size property, see setSize
Definition: qcustomplot.h:2311
QString name() const
Definition: qcustomplot.h:658
The errors are for the key dimension (bars appear parallel to the key axis)
Definition: qcustomplot.h:6013
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
Represents two doubles as a mathematical 2D vector.
Definition: qcustomplot.h:387
void setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
QCPDataSelection inverse(const QCPDataRange &outerRange) const
void drawShape(QCPPainter *painter, const QPointF &pos) const
bool hasInvalidatedPaintBuffers()
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QCPRange dataRange() const
Definition: qcustomplot.h:5069
bool selectable() const
Definition: qcustomplot.h:3529
QRect rect() const
Definition: qcustomplot.h:1226
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
Qt::Orientations rangeDrag() const
Definition: qcustomplot.h:4612
virtual void startSelection(QMouseEvent *event)
void dataRangeChanged(const QCPRange &newRange)
Layer is used only for rendering order, and shares paint buffer with all other adjacent logical layer...
Definition: qcustomplot.h:648
Layer is inserted above other layer.
Definition: qcustomplot.h:3612
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:6486
int getMarginValue(const QMargins &margins, QCP::MarginSide side)
Definition: qcustomplot.h:350
virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE
void setSubTickPen(const QPen &pen)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
void drawGridLines(QCPPainter *painter) const
The generic data container for one-dimensional plottables.
Definition: qcustomplot.h:2412
Manages a legend inside a QCustomPlot.
Definition: qcustomplot.h:4807
QPen getIconBorderPen() const
double mBufferDevicePixelRatio
Definition: qcustomplot.h:3765
virtual QCPLayoutElement * takeAt(int index) Q_DECL_OVERRIDE
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
void setColor(const QColor &color)
void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true)
virtual int dataCount() const =0
void setTypeY(PositionType type)
void setSubTickCount(int subTicks)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QPointF toPointF() const
Definition: qcustomplot.h:409
line is drawn as steps where the step height is the value of the left data point
Definition: qcustomplot.h:5182
void setAlpha(int keyIndex, int valueIndex, unsigned char alpha)
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
QCPItemAnchor *const bottomRight
Definition: qcustomplot.h:6354
QCPColorScale * colorScale() const
Definition: qcustomplot.h:5770
virtual int elementCount() const Q_DECL_OVERRIDE
void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
Definition: qcustomplot.h:330
virtual ~QCPAxis()
void adoptElement(QCPLayoutElement *el)
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
void setupFullAxesBox(bool connectRanges=false)
QPointer< QCPBars > mBarBelow
Definition: qcustomplot.h:5549
QSharedPointer< QCPFinancialDataContainer > data() const
Definition: qcustomplot.h:5898
A nicely readable tick step is prioritized over matching the requested number of ticks (see setTickCo...
Definition: qcustomplot.h:1549
Qt::Orientations rangeZoom() const
Definition: qcustomplot.h:4613
void setAntialiased(bool enabled)
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:4676
void setSelectedPen(const QPen &pen)
static QSize getFinalMaximumOuterSize(const QCPLayoutElement *el)
SelectableParts selectableParts() const
Definition: qcustomplot.h:2046
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QCPDataRange bounded(const QCPDataRange &other) const
QCPAxis * yAxis2
Definition: qcustomplot.h:3739
No ending decoration.
Definition: qcustomplot.h:1489
void setBrush(const QBrush &brush)
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
The selection rect is disabled, and all mouse events are forwarded to the underlying objects...
Definition: qcustomplot.h:272
void setValueAxis(QCPAxis *axis)
static AxisType opposite(AxisType type)
void setPen(const QPen &pen)
QCPAbstractPlottable * plottable()
Definition: qcustomplot.h:4790
void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
void addData(const QVector< double > &error)
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
void setBrushPositive(const QBrush &brush)
double boundingDistance() const
QList< double > mRowStretchFactors
Definition: qcustomplot.h:1403
void setSelectedFont(const QFont &font)
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
A layout that arranges child elements in a grid.
Definition: qcustomplot.h:1327
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE
virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const
void clicked(QMouseEvent *event)
void setPen(const QPen &pen)
void setBaseValue(double baseValue)
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:3283
void setAutoMargins(QCP::MarginSides sides)
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
0x04 Axis is horizontal and on the top side of the axis rect
Definition: qcustomplot.h:1974
void drawToPaintBuffer()
The axis label.
Definition: qcustomplot.h:2004
Qt::Orientation orientation() const
Definition: qcustomplot.h:2108
QRect mViewport
Definition: qcustomplot.h:3764
virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE
void simplifyFraction(int &numerator, int &denominator) const
QPainter subclass used internally.
Definition: qcustomplot.h:461
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
The abstract base class for all objects that form the layout system.
Definition: qcustomplot.h:1184
double width() const
Definition: qcustomplot.h:5630
bool isNone() const
Definition: qcustomplot.h:2373
QCPItemPosition * position(const QString &name) const
void setScatterStyle(const QCPScatterStyle &style)
bool savePdf(const QString &fileName, int width=0, int height=0, QCP::ExportPen exportPen=QCP::epAllowCosmetic, const QString &pdfCreator=QString(), const QString &pdfTitle=QString())
Tick labels (numbers) of this axis (as a whole, not individually)
Definition: qcustomplot.h:2003
bool setupOpenGl()
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:5132
QList< QCPAbstractItem * > selectedItems() const
double getMantissa(double input, double *magnitude=0) const
EndingStyle style() const
Definition: qcustomplot.h:1506
QCPAxis * keyAxis() const
Definition: qcustomplot.h:3477
virtual void copyFrom(const QCPSelectionDecorator *other)
void addChildY(QCPItemPosition *pos)
QPen mainPen() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QCPVector2D & operator-=(const QCPVector2D &vector)
int tickLabelPadding() const
QList< QCPAbstractPlottable * > mPlottables
Definition: qcustomplot.h:3768
void setSizeConstraintRect(SizeConstraintRect constraintRect)
QVector< QPointF > dataToImpulseLines(const QVector< QCPGraphData > &data) const
bool mDragging
Definition: qcustomplot.h:2179
double rangeZoomFactor(Qt::Orientation orientation)
QCPGrid * mGrid
Definition: qcustomplot.h:2171
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
QCPAbstractPlottable * plottable(int index)
Multiple contiguous data points (a data range) can be selected.
Definition: qcustomplot.h:299
Holds multiple axes and arranges them in a rectangular shape.
Definition: qcustomplot.h:4593
QPen pen() const
Definition: qcustomplot.h:6198
void setTail(const QCPLineEnding &tail)
void makeNonCosmetic()
bool registerItem(QCPAbstractItem *item)
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
int plottableCount() const
void setPen(const QPen &pen)
void setInvalidated(bool invalidated=true)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
void setPiValue(double pi)
void rescaleKeyAxis(bool onlyEnlarge=false) const
void setType(PositionType type)
bool isEmpty() const
QCPAxis::AxisType mType
Definition: qcustomplot.h:5101
double getPixelSpacing(const QCPBars *bars, double keyCoord)
bool mColorBufferInvalidated
Definition: qcustomplot.h:4509
virtual QRect clipRect() const Q_DECL_OVERRIDE
void setColorStopAt(double position, const QColor &color)
double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
void addData(const QVector< double > &t, const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE
Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion ...
Definition: qcustomplot.h:4469
bool contains(const QCPDataRange &other) const
void setSubTickCount(int subTicks)
Both sign domains, including zero, i.e. all numbers.
Definition: qcustomplot.h:192
void applyPen(QCPPainter *painter) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QCPLineEnding upperEnding() const
void setSize(double size)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE=0
0x00 no margin
Definition: qcustomplot.h:206
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
void setTickLabelSide(LabelSide side)
Bar spacing is in absolute pixels.
Definition: qcustomplot.h:5400
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
QPainterPath mCustomPath
Definition: qcustomplot.h:2387
void setClipAxisRect(QCPAxisRect *rect)
QPen mTickPen
Definition: qcustomplot.h:2163
void sizeConstraintsChanged() const
double rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const
virtual ~QCPColorScale()
virtual Q_SLOT void processRectSelection(QRect rect, QMouseEvent *event)
virtual void layoutChanged() Q_DECL_OVERRIDE
QString unicodeFraction(int numerator, int denominator) const
virtual ~QCPAxisTicker()
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
A color scale for use with color coding data such as QCPColorMap.
Definition: qcustomplot.h:5049
QPointer< QCPLayerable > mMouseSignalLayerable
Definition: qcustomplot.h:3793
QMap< double, QColor > mColorStops
Definition: qcustomplot.h:4503
Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the ...
Definition: qcustomplot.h:4451
Open-High-Low-Close bar representation.
Definition: qcustomplot.h:5889
void removeChild(QCP::MarginSide side, QCPLayoutElement *element)
int keySize() const
Definition: qcustomplot.h:5700
void restore()
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
QCPLineEnding head() const
Definition: qcustomplot.h:6150
bool selectable() const
Definition: qcustomplot.h:4963
void pixelsToCoords(double x, double y, double &key, double &value) const
0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)
Definition: qcustomplot.h:260
void remove(QCPBars *bars)
QCP::AntialiasedElements mAntialiasedElements
Definition: qcustomplot.h:3772
Linear scaling.
Definition: qcustomplot.h:1993
Groups multiple QCPBars together so they appear side by side.
Definition: qcustomplot.h:5386
QCPLineEnding head() const
Definition: qcustomplot.h:6200
virtual bool take(QCPLayoutElement *element) Q_DECL_OVERRIDE
Qt::Alignment insetAlignment(int index) const
QCPBars * barBelow() const
Definition: qcustomplot.h:5516
virtual ~QCPItemEllipse()
QCPScatterStyle mOutlierStyle
Definition: qcustomplot.h:5666
TracerStyle mStyle
Definition: qcustomplot.h:6588
QPen pen() const
Definition: qcustomplot.h:6405
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
virtual QPointF pixelPosition() const
void setOffset(int offset)
void setSelectedPen(const QPen &pen)
void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const
TimeUnit mSmallestUnit
Definition: qcustomplot.h:1677
double mRotation
Definition: qcustomplot.h:6370
bool removeFromLegend() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
int columnCount() const
Definition: qcustomplot.h:1359
int mScatterSkip
Definition: qcustomplot.h:5353
bool mTickLabels
Definition: qcustomplot.h:2151
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lines) const
QList< QCPColorMap * > colorMaps() const
bool hasAnchor(const QString &name) const
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
void normalize()
int pixelOrientation() const
Definition: qcustomplot.h:2109
QCPItemAnchor *const top
Definition: qcustomplot.h:6351
Tick labels will be displayed outside the axis rect.
Definition: qcustomplot.h:1986
void removeChildX(QCPItemPosition *pos)
0x0004 Sub grid lines
Definition: qcustomplot.h:221
QPen mainPen() const
bool mTightBoundary
Definition: qcustomplot.h:5802
double realLength() const
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const =0
QList< QPointer< QCPAxis > > mRangeDragVertAxis
Definition: qcustomplot.h:4681
QPointF getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const
The abstract base class for all entries in a QCPLegend.
Definition: qcustomplot.h:4719
void initializeParentPlot(QCustomPlot *parentPlot)
SelectableParts selectableParts() const
Definition: qcustomplot.h:4851
SelectionRectMode
Definition: qcustomplot.h:272
QCPItemAnchor *const topRight
Definition: qcustomplot.h:6266
void setColumnSpacing(int pixels)
void setTail(const QCPLineEnding &tail)
PositionType mPositionTypeY
Definition: qcustomplot.h:3497
QCPItemAnchor * createAnchor(const QString &name, int anchorId)
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
void setTicks(bool show)
QCP::AntialiasedElements mOpenGlAntialiasedElementsBackup
Definition: qcustomplot.h:3799
double epsilon
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
friend class QCPGraph
Definition: qcustomplot.h:3846
int bottom() const
Definition: qcustomplot.h:4658
double pickClosest(double target, const QVector< double > &candidates) const
unsigned char * mAlpha
Definition: qcustomplot.h:5738
double cell(int keyIndex, int valueIndex)
Minimum/Maximum size constraints apply to inner rect.
Definition: qcustomplot.h:1216
SelectableParts mSelectedParts
Definition: qcustomplot.h:2141
Describes a data range given by begin and end index.
Definition: qcustomplot.h:895
virtual ~QCPColorMap()
QBrush mSelectedBrush
Definition: qcustomplot.h:6586
QString mText
Definition: qcustomplot.h:6367
void setSelectedTickLabelFont(const QFont &font)
QCPAxis * rangeZoomAxis(Qt::Orientation orientation)
void setupTickVectors()
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
void normalize()
Definition: qcustomplot.h:801
QCPGraph * addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0)
bool operator==(const QCPDataSelection &other) const
void addChild(QCPLayerable *layerable, bool prepend)
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
bool mReplotQueued
Definition: qcustomplot.h:3797
virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE
QCPScatterStyle::ScatterProperties usedScatterProperties() const
Definition: qcustomplot.h:3259
QVector< QLineF > getWhiskerBackboneLines(QCPStatisticalBoxDataContainer::const_iterator it) const
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable) ...
Definition: qcustomplot.h:259
QBrush mainBrush() const
Phase used for any type of preparation that needs to be done before margin calculation and layout...
Definition: qcustomplot.h:1202
The abstract base class for paint buffers, which define the rendering backend.
Definition: qcustomplot.h:520
QCPAbstractPaintBuffer * createPaintBuffer()
double width() const
Definition: qcustomplot.h:5900
void setTextColor(const QColor &color)
void setPenNegative(const QPen &pen)
void setHead(const QCPLineEnding &head)
void setIconSize(const QSize &size)
void setSelectedBrush(const QBrush &brush)
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)
QCP::PlottingHints mPlottingHints
Definition: qcustomplot.h:3782
QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio)
QList< QCPGraph * > graphs() const
void selectableChanged(bool selectable)
void setMinimumSize(const QSize &size)
QCPRange sanitizedForLinScale() const
QCPLegend * legend
Definition: qcustomplot.h:3740
void applyTo(QCPPainter *painter, const QPen &defaultPen) const
void setCoords(double key, double value)
void addDataRange(const QCPDataRange &dataRange, bool simplify=true)
void setLength(double length)
QBrush mBackgroundBrush
Definition: qcustomplot.h:4674
A bar perpendicular to the line, pointing out to only one side (to which side can be changed with set...
Definition: qcustomplot.h:1497
void scaleRange(double factor)
QCPItemPosition * createPosition(const QString &name)
void setGraphKey(double key)
Minutes (%m in setTimeFormat)
Definition: qcustomplot.h:1655
QPointer< QCPBars > mBarAbove
Definition: qcustomplot.h:5549
void setBracketHeight(int height)
A legend item representing a plottable with an icon and the plottable name.
Definition: qcustomplot.h:4783
QCPAxis::LabelSide tickLabelSide
Definition: qcustomplot.h:2248
double y() const
Definition: qcustomplot.h:397
bool setCurrentLayer(const QString &name)
bool isPenDefined() const
Definition: qcustomplot.h:2374
void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
void setOpenGl(bool enabled, int multisampling=16)
bool mRangeReversed
Definition: qcustomplot.h:2167
QCPVector2D & operator+=(const QCPVector2D &vector)
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:657
void setSelectionRect(QCPSelectionRect *selectionRect)
QCPAxis * xAxis2
Definition: qcustomplot.h:3739
QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0)
bool selected() const
Definition: qcustomplot.h:3323
Bar spacing is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:5402
const QCP::Interactions interactions() const
Definition: qcustomplot.h:3641
QBrush brush() const
Definition: qcustomplot.h:1110
QPixmap mScaledPixmap
Definition: qcustomplot.h:6499
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
virtual double dataSortKey(int index) const Q_DECL_OVERRIDE
void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
WidthType widthType() const
Definition: qcustomplot.h:5512
void setLength(double length)
SelectableParts selectedParts() const
void setTicker(QSharedPointer< QCPAxisTicker > ticker)
QCP::SelectionRectMode mSelectionRectMode
Definition: qcustomplot.h:3784
void setPen(const QPen &pen)
void setSelectedTextColor(const QColor &color)
QMap< double, QString > mTicks
Definition: qcustomplot.h:1760
bool contains(double value) const
Definition: qcustomplot.h:809
void setBrush(const QBrush &brush)
void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true)
ChartStyle mChartStyle
Definition: qcustomplot.h:5935
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
int subTickLengthIn() const
QHash< TimeUnit, int > mFieldWidth
Definition: qcustomplot.h:1674
static double dateTimeToKey(const QDateTime dateTime)
QCPVector2D & operator*=(double factor)
Seconds (%s in setTimeFormat)
Definition: qcustomplot.h:1654
QCPItemAnchor * anchor(const QString &name) const
BracketStyle mStyle
Definition: qcustomplot.h:6658
bool isInvalidData(double value)
Definition: qcustomplot.h:309
void setStyle(EndingStyle style)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setTickPen(const QPen &pen)
0x0040 Main lines of items
Definition: qcustomplot.h:225
Manages a single axis inside a QCustomPlot.
Definition: qcustomplot.h:1919
void addTicks(const QMap< double, QString > &ticks)
void selectionChanged(bool selected)
void clear(const QColor &color) Q_DECL_OVERRIDE
QFont mainFont() const
void setAntialiasedFill(bool enabled)
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:3369
0x01 Axis is vertical and on the left side of the axis rect
Definition: qcustomplot.h:1972
Bar spacing is given by a fraction of the axis rect size.
Definition: qcustomplot.h:5401
void setColorInterpolation(ColorInterpolation interpolation)
QBrush mBrushPositive
Definition: qcustomplot.h:5939
void setWhiskerBarPen(const QPen &pen)
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
QList< QCPAxis * > axes() const
void rescaleAxes(bool onlyEnlarge=false) const
void setZeroLinePen(const QPen &pen)
PainterModes mModes
Definition: qcustomplot.h:505
virtual Q_SLOT void processRectZoom(QRect rect, QMouseEvent *event)
void setSubTickLength(int inside, int outside=0)
void rescaleDataRange(bool onlyVisibleMaps)
QMargins margins() const
Definition: qcustomplot.h:1228
Q_SLOT void setSelectable(bool selectable)
double mWidth
Definition: qcustomplot.h:5544
static QDateTime keyToDateTime(double key)
A template base class for plottables with one-dimensional data.
Definition: qcustomplot.h:3876
void clear()
0x01 The pen property, see setPen
Definition: qcustomplot.h:2309
PositionType mPositionTypeX
Definition: qcustomplot.h:3497
void setPen(const QPen &pen)
QRect tickLabelsSelectionBox() const
Definition: qcustomplot.h:2235
void setViewport(const QRect &rect)
QCPItemPosition *const end
Definition: qcustomplot.h:6163
void indexToRowCol(int index, int &row, int &column) const
QPen mPen
Definition: qcustomplot.h:1902
SelectableParts selectedParts() const
Definition: qcustomplot.h:2045
QList< QCPAbstractItem * > mItems
Definition: qcustomplot.h:3770
double getStackedBaseValue(double key, bool positive) const
bool subTicks() const
Definition: qcustomplot.h:2033
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
Definition: qcustomplot.h:3460
QCP::Interactions mInteractions
Definition: qcustomplot.h:3773
Qt::Orientations mRangeDrag
Definition: qcustomplot.h:4680
bool invalidated() const
Definition: qcustomplot.h:528
Q_SLOT void setSelectable(bool selectable)
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
void setTextColor(const QColor &color)
virtual void getOptimizedLineData(QVector< QCPGraphData > *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
bool removeAxis(QCPAxis *axis)
QCPAxis * keyAxis() const
Definition: qcustomplot.h:3320
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
double length() const
Definition: qcustomplot.h:1508
QVector< QRgb > mColorBuffer
Definition: qcustomplot.h:4508
void rescaleDataRange(bool recalculateDataBounds=false)
Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts)
bool mAntialiased
Definition: qcustomplot.h:739
virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE
QPen pen() const
Definition: qcustomplot.h:6554
void setLineStyle(LineStyle ls)
bool hasItem(QCPAbstractItem *item) const
Days (%d in setTimeFormat)
Definition: qcustomplot.h:1657
{ssDisc.png} a circle which is filled with the pen&#39;s color (not the brush as with ssCircle) ...
Definition: qcustomplot.h:2331
QPen pen() const
Definition: qcustomplot.h:2358
QPen mainPen() const
void setPen(const QPen &pen)
void setCell(int keyIndex, int valueIndex, double z)
QPoint mMousePressPos
Definition: qcustomplot.h:3790
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setData(QSharedPointer< QCPStatisticalBoxDataContainer > data)
QPointer< QCPAxis > mColorAxis
Definition: qcustomplot.h:5109
void setPadding(int padding)
void getDataSegments(QList< QCPDataRange > &selectedSegments, QList< QCPDataRange > &unselectedSegments) const
void setData(QSharedPointer< QCPFinancialDataContainer > data)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setSelectedLabelFont(const QFont &font)
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
QCPScatterStyle getFinalScatterStyle(const QCPScatterStyle &unselectedStyle) const
double data(double key, double value)
LayerMode mode() const
Definition: qcustomplot.h:662
void beforeReplot()
void addChildX(QCPItemPosition *pos)
A margin group allows synchronization of margin sides if working with multiple layout elements...
Definition: qcustomplot.h:1153
virtual ~QCPAxisPainterPrivate()
The tracer is not visible.
Definition: qcustomplot.h:6542
void setPen(const QPen &pen)
QCPDataSelection mSelection
Definition: qcustomplot.h:3371
Bar width is in absolute pixels.
Definition: qcustomplot.h:5501
QCPLineEnding lowerEnding() const
void setMedianPen(const QPen &pen)
QSharedPointer< QCPAxisTicker > mTicker
Definition: qcustomplot.h:2173
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
void setOutlierStyle(const QCPScatterStyle &style)
void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
QColor getLabelColor() const
void addTick(double position, const QString &label)
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
The errors are for the value dimension (bars appear parallel to the value axis)
Definition: qcustomplot.h:6014
void setVisible(bool on)
virtual ~QCPLayerable()
void setSelectedBrush(const QBrush &brush)
0x08 bottom margin
Definition: qcustomplot.h:204
void setPen(const QPen &pen)
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
QRectF insetRect(int index) const
void setTwoColored(bool twoColored)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPItemPosition *const start
Definition: qcustomplot.h:6212
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QCPPlottableLegendItem * itemWithPlottable(const QCPAbstractPlottable *plottable) const
QRect labelSelectionBox() const
Definition: qcustomplot.h:2236
void setRangeZoom(Qt::Orientations orientations)
virtual int calculateMargin()
void registerBars(QCPBars *bars)
void started(QMouseEvent *event)
void setWhiskerAntialiased(bool enabled)
QCPDataRange expanded(const QCPDataRange &other) const
{ssTriangleInverted.png} an equilateral triangle, standing on corner
Definition: qcustomplot.h:2336
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
void setData(QSharedPointer< QCPGraphDataContainer > data)
virtual ~QCPAxisRect()
Qt::KeyboardModifier mMultiSelectModifier
Definition: qcustomplot.h:3783
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPAxis::AxisType type
Definition: qcustomplot.h:2239
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual double getTickStep(const QCPRange &range)
QPen pen() const
Definition: qcustomplot.h:1886
void doubleClicked(QMouseEvent *event)
virtual ~QCPItemBracket()
void mouseWheel(QWheelEvent *event)
virtual void draw(QCPPainter *painter)=0
bool getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const
QList< QCPLayerable * > children() const
Definition: qcustomplot.h:660
An anchor of an item to which positions can be attached to.
Definition: qcustomplot.h:3405
{ssCircle.png} a circle
Definition: qcustomplot.h:2330
void setNumberFormat(const QString &formatCode)
QBrush mBackgroundBrush
Definition: qcustomplot.h:3776
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio)
int mPadding
Definition: qcustomplot.h:2139
QPixmap toPixmap(int width=0, int height=0, double scale=1.0)
QImage mMapImage
Definition: qcustomplot.h:5806
bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
friend class QCPItemAnchor
Definition: qcustomplot.h:3581
0x0100 Borders of fills (e.g. under or between graphs)
Definition: qcustomplot.h:227
The abstract base class for all items in a plot.
Definition: qcustomplot.h:3513
A filled diamond (45 degrees rotated square)
Definition: qcustomplot.h:1495
virtual QPointF anchorPixelPosition(int anchorId) const
QCPAxisRect * axisRect() const
void setSelectedBrush(const QBrush &brush)
void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
QFont font() const
Definition: qcustomplot.h:4959
static const double minRange
Definition: qcustomplot.h:813
void setFont(const QFont &font)
bool hasPlottable(QCPAbstractPlottable *plottable) const
QVector< double > mTickVector
Definition: qcustomplot.h:2174
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual ~QCPGraph()
An integer power of the specified tick step is allowed.
Definition: qcustomplot.h:1708
QList< QPointer< QCPAxis > > mRangeDragHorzAxis
Definition: qcustomplot.h:4681
virtual void simplify()
Selection behaves like stMultipleDataRanges, but if there are any data points selected, the entire plottable is drawn as selected.
Definition: qcustomplot.h:297
QPoint toPoint() const
Definition: qcustomplot.h:408
virtual QRect clipRect() const Q_DECL_OVERRIDE
QBrush brush() const
Definition: qcustomplot.h:6321
bool mAutoAddPlottableToLegend
Definition: qcustomplot.h:3767
virtual void layoutChanged()
virtual QCPLayoutElement * takeAt(int index) Q_DECL_OVERRIDE
unsigned int step
SelectableParts mSelectableParts
Definition: qcustomplot.h:2141
SelectionType
Definition: qcustomplot.h:296
bool mMapImageInvalidated
Definition: qcustomplot.h:5808
double mRangeZoomFactorHorz
Definition: qcustomplot.h:4683
void expandTo(int newRowCount, int newColumnCount)
Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle ...
Definition: qcustomplot.h:4467
bool selectable() const
Definition: qcustomplot.h:4740
void setSubGridPen(const QPen &pen)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
QList< QCPAxis * > axes(QCPAxis::AxisTypes types) const
QCPAbstractItem * itemAt(const QPointF &pos, bool onlySelectable=false) const
QCPColorGradient gradient() const
Definition: qcustomplot.h:5071
int width() const
Definition: qcustomplot.h:4659
QBrush mSelectedBrush
Definition: qcustomplot.h:6277
void replaceUnit(QString &text, TimeUnit unit, int value) const
A paint buffer based on QPixmap, using software raster rendering.
Definition: qcustomplot.h:555
virtual ~QCPCurve()
QImage mUndersampledMapImage
Definition: qcustomplot.h:5806
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual ~QCPItemText()
virtual QSize maximumOuterSizeHint() const
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
QCPAxisRect * axisRect() const
Definition: qcustomplot.h:2015
void setFractionStyle(FractionStyle style)
bool hasItem(QCPAbstractLegendItem *item) const
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:4675
virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const
QCPAbstractItem * mParentItem
Definition: qcustomplot.h:3422
void setText(const QString &text)
void setShape(ScatterShape shape)
BracketStyle style() const
Definition: qcustomplot.h:6638
void gradientChanged(const QCPColorGradient &newGradient)
QCPLineEnding mHead
Definition: qcustomplot.h:6168
void setWidth(double width)
QCPGraph * graph() const
virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection)
bool scaled() const
Definition: qcustomplot.h:6470
0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap) ...
Definition: qcustomplot.h:226
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
void moveBelow(QCPBars *bars)
bool noAntialiasingOnDrag() const
Definition: qcustomplot.h:3643
virtual Q_SLOT void processPointSelection(QMouseEvent *event)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
virtual int findBegin(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE
void setVisible(bool visible)
QPen mSubGridPen
Definition: qcustomplot.h:1902
TickStepStrategy mTickStepStrategy
Definition: qcustomplot.h:1572
QBrush getBrush() const
virtual int elementCount() const Q_DECL_OVERRIDE
Definition: qcustomplot.h:1379
int findIndexAboveX(const QVector< QPointF > *data, double x) const
void setGraph(QCPGraph *graph)
QCPSelectionRect * selectionRect() const
Definition: qcustomplot.h:3647
QString name() const
Definition: qcustomplot.h:3413
void setColumnStretchFactors(const QList< double > &factors)
void setText(const QString &text)
A layout that places child elements aligned to the border or arbitrarily positioned.
Definition: qcustomplot.h:1418
bool remove(QCPLayoutElement *element)
Data points are connected with a straight line.
Definition: qcustomplot.h:5318
void setTickLength(int inside, int outside=0)
bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
int itemCount() const
void setData(double key, double value, double z)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPAxisRect * mAxisRect
Definition: qcustomplot.h:2137
double key() const
Definition: qcustomplot.h:3474
double getPixelWidth(double key, double keyPixel) const
void setFont(const QFont &font)
void setMargins(const QMargins &margins)
void setSelectedBasePen(const QPen &pen)
int labelPadding() const
void accepted(const QRect &rect, QMouseEvent *event)
virtual void getOptimizedScatterData(QVector< QCPGraphData > *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const
virtual bool registerWithPlottable(QCPAbstractPlottable *plottable)
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
QFont mSelectedFont
Definition: qcustomplot.h:6366
0x080 All other objects are selectable (e.g. your own derived layerables, other layout elements...
Definition: qcustomplot.h:263
void setSelectedSubTickPen(const QPen &pen)
bool operator==(const QCPColorGradient &other) const
QCPAbstractPlottable * plottable()
virtual QList< QCPLayoutElement * > elements(bool recursive) const Q_DECL_OVERRIDE
Whether to use immediate or queued refresh depends on whether the plotting hint QCP::phImmediateRefre...
Definition: qcustomplot.h:3623
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom, QCPAxisRect::setRangeZoomAxes)
Definition: qcustomplot.h:257
void addChild(QCP::MarginSide side, QCPLayoutElement *element)
double baseValue() const
Definition: qcustomplot.h:5514
QCPAbstractItem * item() const
int itemCount() const
void setErrorType(ErrorType type)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
line is drawn as steps where the step height is the value of the right data point ...
Definition: qcustomplot.h:5183
QCPLayout * mParentLayout
Definition: qcustomplot.h:1260
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const =0
void coordsToPixels(double key, double value, double &x, double &y) const
void setSelectionTolerance(int pixels)
virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE
void setPeriodic(bool enabled)
bool removeFromLegend(QCPLegend *legend) const
A non-filled arrow head with open back.
Definition: qcustomplot.h:1492
QCPItemAnchor *const left
Definition: qcustomplot.h:6357
virtual ~QCPLayoutElement()
void setDateTimeSpec(Qt::TimeSpec spec)
int padding() const
Definition: qcustomplot.h:2043
Qt::TimeSpec mDateTimeSpec
Definition: qcustomplot.h:1626
QPen iconBorderPen() const
Definition: qcustomplot.h:4850
virtual ~QCPSelectionRect()
virtual ~QCPAbstractPlottable()
A plottable representing a bar chart in a plot.
Definition: qcustomplot.h:5482
void setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties=QCPScatterStyle::spPen)
bool mSubTicks
Definition: qcustomplot.h:2161
QCustomPlot(QWidget *parent=0)
bool isNull() const
Definition: qcustomplot.h:411
void setLowerEnding(const QCPLineEnding &ending)
Qt::AspectRatioMode mAspectRatioMode
Definition: qcustomplot.h:6502
double devicePixelRatio() const
Definition: qcustomplot.h:529
double symbolGap() const
Definition: qcustomplot.h:6025
double tickLabelRotation() const
void setFont(const QFont &font)
void mouseMove(QMouseEvent *event)
QCPColorMapData * mMapData
Definition: qcustomplot.h:5799
QString unicodeSuperscript(int number) const
0x8000 Other elements that don&#39;t fit into any of the existing categories
Definition: qcustomplot.h:229
QPen selectedIconBorderPen() const
Definition: qcustomplot.h:4854
bool hasElement(int row, int column)
int findIndexBelowY(const QVector< QPointF > *data, double y) const
QCPLineEnding tail() const
Definition: qcustomplot.h:6151
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
A brace with round edges.
Definition: qcustomplot.h:6625
QList< QCPGraph * > selectedGraphs() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
double mSymbolGap
Definition: qcustomplot.h:6064
Qt::Orientations mRangeZoom
Definition: qcustomplot.h:4680
Static positioning in pixels, starting from the top left corner of the viewport/widget.
Definition: qcustomplot.h:3453
A filled arrow head with an indented back.
Definition: qcustomplot.h:1491
double size() const
Definition: qcustomplot.h:799
QSharedPointer< QCPErrorBarsDataContainer > mDataContainer
Definition: qcustomplot.h:6060
{ssPlusCircle.png} a circle with a plus inside
Definition: qcustomplot.h:2340
QCPAbstractLegendItem(QCPLegend *parent)
int levelCount() const
Definition: qcustomplot.h:4480
void setSpacing(double spacing)
virtual double dataMainKey(int index) const =0
void plottableDoubleClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)
QBrush brush() const
Definition: qcustomplot.h:3319
void add(const QCPDataContainer< DataType > &data)
Definition: qcustomplot.h:2672
void setTypeX(PositionType type)
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
int valueSize() const
Definition: qcustomplot.h:5701
QPointF coords() const
Definition: qcustomplot.h:3476
QCPRange valueRange() const
Definition: qcustomplot.h:5703
QString fractionToString(int numerator, int denominator) const
QPen mainPen() const
ROSCPP_DECL std::string append(const std::string &left, const std::string &right)
void setBackground(const QPixmap &pm)
{ssPeace.png} a circle, with one vertical and two downward diagonal lines
Definition: qcustomplot.h:2341
QHash< QCPAxis::AxisType, QList< QCPAxis * > > mAxes
Definition: qcustomplot.h:4689
void getScatters(QVector< QPointF > *scatters, const QCPDataRange &dataRange) const
QVector< double > outliers
Definition: qcustomplot.h:5596
QCP::PlottingHints plottingHints() const
Definition: qcustomplot.h:3644
void setWidthType(WidthType widthType)
void getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector< QPointF > &beforeTraverse, QVector< QPointF > &afterTraverse) const
0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fix...
Definition: qcustomplot.h:470
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
QCPItemAnchor *const bottom
Definition: qcustomplot.h:6268
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:5352
Base class for all drawable objects.
Definition: qcustomplot.h:697
0x0008 Legend box
Definition: qcustomplot.h:222
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setPen(const QPen &pen)
Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts)
QCPDataSelection selection() const
Definition: qcustomplot.h:3324
void draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const
virtual void generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector< double > &ticks, QVector< double > *subTicks, QVector< QString > *tickLabels)
void insert(int i, QCPBars *bars)
QCPColorGradient inverted() const
void setData(QSharedPointer< QCPErrorBarsDataContainer > data)
QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale)
int tickLengthOut() const
virtual void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE
virtual ~QCPItemRect()
bool mOpenGlCacheLabelsBackup
Definition: qcustomplot.h:3800
int begin() const
Definition: qcustomplot.h:905
void setLabelColor(const QColor &color)
void setScatterStyle(const QCPScatterStyle &style)
virtual void reallocateBuffer()=0
QCPDataRange span() const
void setSelectedPen(const QPen &pen)
QMargins mMinimumMargins
Definition: qcustomplot.h:1264
void getVisibleDataBounds(QCPBarsDataContainer::const_iterator &begin, QCPBarsDataContainer::const_iterator &end) const
void setTickLabelRotation(double degrees)
void setWhiskerWidth(double width)
QCP::SelectionType selectable() const
Definition: qcustomplot.h:3322
bool isVisible(PanelDockWidget *widget)
QColor color() const
Definition: qcustomplot.h:6317
Tick labels will be displayed inside the axis rect and clipped to the inner axis rect.
Definition: qcustomplot.h:1985
QFont mTickLabelFont
Definition: qcustomplot.h:2153
{ssDot.png} a single pixel (use ssDisc or ssCircle if you want a round shape with a certain radius) ...
Definition: qcustomplot.h:2327
void setLineStyle(LineStyle style)
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual void draw(QCPPainter *painter) const Q_DECL_OVERRIDE
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:2181
void setHead(const QCPLineEnding &head)
QCPItemAnchor * mParentAnchorX
Definition: qcustomplot.h:3501
QCPItemPixmap(QCustomPlot *parentPlot)
virtual ~QCPItemPixmap()
QCPItemCurve(QCustomPlot *parentPlot)
QCPLayout * layout() const
Definition: qcustomplot.h:1225
QVector< double > mSubTickVector
Definition: qcustomplot.h:2176
void setRangeZoom(bool enabled)
virtual void simplify() Q_DECL_OVERRIDE
double bufferDevicePixelRatio() const
Definition: qcustomplot.h:3633
bool mVisible
Definition: qcustomplot.h:677
QCPBarsGroup(QCustomPlot *parentPlot)
The plottable is not selectable.
Definition: qcustomplot.h:296
An approximation of the visible light spectrum (creates banding illusion but allows more precise magn...
Definition: qcustomplot.h:4468
void setValueSize(int valueSize)
Resolution is given in dots per centimeter (dpcm)
Definition: qcustomplot.h:171
void fill(double z)
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
A curly brace with varying stroke width giving a calligraphic impression.
Definition: qcustomplot.h:6627
virtual bool take(QCPLayoutElement *element)=0
bool mScaledPixmapInvalidated
Definition: qcustomplot.h:6501
virtual ~QCPItemPosition()
Fractions are written using sub- and superscript UTF-8 digits and the fraction symbol.
Definition: qcustomplot.h:1787
bool twoColored() const
Definition: qcustomplot.h:5902
void setChartStyle(ChartStyle style)
void setBracketStyle(BracketStyle style)
bool isValid() const
Definition: qcustomplot.h:915
void setSubTicks(bool show)
virtual QVector< double > createSubTickVector(int subTickCount, const QVector< double > &ticks)
QCPItemLine(QCustomPlot *parentPlot)
virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
0xFFFF All elements
Definition: qcustomplot.h:230
int graphCount() const
QCPAbstractLegendItem * item(int index) const
bool selected() const
Definition: qcustomplot.h:3530
bool mCachedMarginValid
Definition: qcustomplot.h:2177
virtual ~QCPItemTracer()
void selectionChanged(const QCPAxis::SelectableParts &parts)
void plottableClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
void setTicks(const QMap< double, QString > &ticks)
void setModes(PainterModes modes)
void setStackingGap(double pixels)
QCPRange mRange
Definition: qcustomplot.h:2166
void getScatters(QVector< QPointF > *scatters, const QCPDataRange &dataRange, double scatterWidth) const
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)
void setRotation(double degrees)
QPen tickPen() const
Definition: qcustomplot.h:2037
Q_SLOT void setSelected(bool selected)
QVector< double > tickPositions
Definition: qcustomplot.h:2261
void setBackgroundScaled(bool scaled)
virtual void endSelection(QMouseEvent *event)
A bar that is skewed (skew controllable via setLength)
Definition: qcustomplot.h:1498
virtual void updateLayout()
QList< QCPLayoutElement * > elements(QCP::MarginSide side) const
Definition: qcustomplot.h:1161
Qt::Alignment mPositionAlignment
Definition: qcustomplot.h:6368
QCPRange range(const QCPAxis *axis) const
{ssCrossCircle.png} a circle with a cross inside
Definition: qcustomplot.h:2339
QCPItemAnchor *const right
Definition: qcustomplot.h:6353
void setEnd(int end)
Definition: qcustomplot.h:912
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:3498
virtual QList< QCPLayoutElement * > elements(bool recursive) const Q_DECL_OVERRIDE
int findIndexBelowX(const QVector< QPointF > *data, double x) const
void setLogBase(double base)
virtual void drawCurveLine(QCPPainter *painter, const QVector< QPointF > &lines) const
QFont font() const
Definition: qcustomplot.h:4736
virtual ~QCPItemLine()
QString mLabel
Definition: qcustomplot.h:2146
void trimTicks(const QCPRange &range, QVector< double > &ticks, bool keepOneOutlier) const
A plottable representing a graph in a plot.
Definition: qcustomplot.h:5163
int mCachedMargin
Definition: qcustomplot.h:2178
QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
int selectionTolerance() const
Definition: qcustomplot.h:3642
QHash< TimeUnit, QString > mFormatPattern
Definition: qcustomplot.h:1678
void setSubGridVisible(bool visible)
virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
QVector< QString > mTickVectorLabels
Definition: qcustomplot.h:2175
{ssSquare.png} a square
Definition: qcustomplot.h:2332
void mouseRelease(QMouseEvent *event)
QString numberFormat() const
Modifications are not allowed, the specified tick step is absolutely fixed. This might cause a high t...
Definition: qcustomplot.h:1706
QLineF getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const
QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
QCPAxis * valueAxis() const
Definition: qcustomplot.h:3478
QMargins mPadding
Definition: qcustomplot.h:6371
QColor mTickLabelColor
Definition: qcustomplot.h:2154
QCPItemStraightLine(QCustomPlot *parentPlot)
void setFillOrder(FillOrder order, bool rearrange=true)
int layerCount() const
QCPDataContainer< QCPFinancialData > QCPFinancialDataContainer
Definition: qcustomplot.h:5856
Bar width is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:5503
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range)
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual void drawLinePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
The negative sign domain, i.e. numbers smaller than zero.
Definition: qcustomplot.h:191
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
QCPRange mDragStartRange
Definition: qcustomplot.h:2180
void setupPaintBuffers()
QCPItemPosition *const position
Definition: qcustomplot.h:6349
QSize size() const
Definition: qcustomplot.h:527
void setParentLayerable(QCPLayerable *parentLayerable)
void selectableChanged(QCP::SelectionType selectable)
Continuous lightness from black to white (suited for non-biased data representation) ...
Definition: qcustomplot.h:4459
QPointer< QCPLayerable > mMouseEventLayerable
Definition: qcustomplot.h:3792
0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see QCPAbstractItem) ...
Definition: qcustomplot.h:262
QPen mainPen() const
QColor getTickLabelColor() const
void selectableChanged(bool selectable)
Holds the two-dimensional data of a QCPColorMap plottable.
Definition: qcustomplot.h:5691
QCPColorGradient mGradient
Definition: qcustomplot.h:5104
int height() const
Definition: qcustomplot.h:4660
void insertRow(int newIndex)
Final phase in which the layout system places the rects of the elements.
Definition: qcustomplot.h:1204
void setKeyRange(const QCPRange &keyRange)
void setAxisRect(QCPAxisRect *axisRect)
int subTickLengthOut() const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
void draw(QCPPainter *painter)
None of the selectable parts.
Definition: qcustomplot.h:2001
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QSize minimumSize() const
Definition: qcustomplot.h:1231
QCPColorMapData & operator=(const QCPColorMapData &other)
QCPRange mKeyRange
Definition: qcustomplot.h:5733
void setRange(const QCPRange &keyRange, const QCPRange &valueRange)
The abstract base class for all data representing objects in a plot.
Definition: qcustomplot.h:3295
QBrush brush() const
Definition: qcustomplot.h:6556
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
QCPItemAnchor *const top
Definition: qcustomplot.h:6422
QCPItemAnchor *const bottom
Definition: qcustomplot.h:6355
virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
void setRangeZoomFactor(double horizontalFactor, double verticalFactor)
QCPDataRange dataRange(int index=0) const
void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
void setPixmap(const QPixmap &pixmap)
bool mBackgroundScaled
Definition: qcustomplot.h:4677
QCPItemAnchor *const center
Definition: qcustomplot.h:6429
QList< QCPAxisRect * > axisRects() const
bool mMouseHasMoved
Definition: qcustomplot.h:3791
void setTextFlags(int flags)
bool tickLabels() const
Definition: qcustomplot.h:2021
void setPiSymbol(QString symbol)
virtual ~QCPPaintBufferPixmap()
virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE
virtual int calculateAutoMargin(QCP::MarginSide side) Q_DECL_OVERRIDE
FillOrder mFillOrder
Definition: qcustomplot.h:1406
A plus shaped crosshair with limited size.
Definition: qcustomplot.h:6543
virtual void drawImpulsePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
void selectableChanged(bool selectable)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
double value
Definition: qcustomplot.h:5466
friend class QCPAxisRect
Definition: qcustomplot.h:3844
QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name)
void drawLine(const QLineF &line)
void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE
QSharedPointer< QCPDataContainer< QCPGraphData > > mDataContainer
Definition: qcustomplot.h:3902
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
void setSelectedPen(const QPen &pen)
virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE
void setRowStretchFactors(const QList< double > &factors)
Color channels red, green and blue are linearly interpolated.
Definition: qcustomplot.h:4450
QList< double > mColumnStretchFactors
Definition: qcustomplot.h:1402
0x0001 Axis base line and tick marks
Definition: qcustomplot.h:219
void setAntialiasedScatters(bool enabled)
QColor mLabelColor
Definition: qcustomplot.h:2148
void setPenPositive(const QPen &pen)
A filled square.
Definition: qcustomplot.h:1494
r
void setSelectedIconBorderPen(const QPen &pen)
double max(double a, double b)
Q_SLOT void setSelected(bool selected)
int mSelectionTolerance
Definition: qcustomplot.h:3774
int right() const
Definition: qcustomplot.h:4656
QVector< QPointF > dataToStepRightLines(const QVector< QCPGraphData > &data) const
QCPAbstractItem(QCustomPlot *parentPlot)
QPen mBasePen
Definition: qcustomplot.h:2142
bool mNoAntialiasingOnDrag
Definition: qcustomplot.h:3775
void setLabelPadding(int padding)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual void updateLayout() Q_DECL_OVERRIDE
PositionType type() const
Definition: qcustomplot.h:3468
Blue over pink to white.
Definition: qcustomplot.h:4463
data points are connected by a straight line
Definition: qcustomplot.h:5181
Holds the data of one single error bar for QCPErrorBars.
Definition: qcustomplot.h:5966
SizeConstraintRect mSizeConstraintRect
Definition: qcustomplot.h:1262
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
PlottingHint
Definition: qcustomplot.h:240
int rowColToIndex(int row, int column) const
void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
QCPItemAnchor * mParentAnchorY
Definition: qcustomplot.h:3501
void getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector< QLineF > &backbones, QVector< QLineF > &whiskers) const
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
0x01 left margin
Definition: qcustomplot.h:201
void changed(const QRect &rect, QMouseEvent *event)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
FractionStyle mFractionStyle
Definition: qcustomplot.h:1810
virtual void drawBracket(QCPPainter *painter, int direction) const
MarginSide
Definition: qcustomplot.h:201
QMargins padding() const
Definition: qcustomplot.h:6329
0x0010 Legend items
Definition: qcustomplot.h:223
QPen pen() const
Definition: qcustomplot.h:6319
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > &points, const QCPScatterStyle &style) const
void setAntialiasing(bool enabled)
void setBorderPen(const QPen &pen)
void getDataSegments(QList< QCPDataRange > &selectedSegments, QList< QCPDataRange > &unselectedSegments) const
Definition: qcustomplot.h:4358
void getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
void applyFillAntialiasingHint(QCPPainter *painter) const
QPixmap pixmap() const
Definition: qcustomplot.h:2360
virtual ~QCPLayoutInset()
void setSize(int keySize, int valueSize)
virtual void drawStatisticalBox(QCPPainter *painter, QCPStatisticalBoxDataContainer::const_iterator it, const QCPScatterStyle &outlierStyle) const
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:3280
QCPItemPosition *const endDir
Definition: qcustomplot.h:6214
bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
virtual QSize maximumOuterSizeHint() const Q_DECL_OVERRIDE
A plottable representing a two-dimensional color map in a plot.
Definition: qcustomplot.h:5748
QCPDataSelection intersection(const QCPDataRange &other) const
QVector< QString > tickLabels
Definition: qcustomplot.h:2262
void setPadding(const QMargins &padding)
0x002 Legend items individually (see selectedItems)
Definition: qcustomplot.h:4834
virtual void draw(QCPPainter *painter)
void canceled(const QRect &rect, QInputEvent *event)
Q_SLOT void cancel()
double mWhiskerWidth
Definition: qcustomplot.h:6063
QList< QCPItemAnchor * > mAnchors
Definition: qcustomplot.h:3557
virtual QCPPainter * startPainting() Q_DECL_OVERRIDE
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
void setSize(const QSize &size)
An integer multiple of the specified tick step is allowed. The used factor follows the base class pro...
Definition: qcustomplot.h:1707
virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE
void setOuterRect(const QRect &rect)
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:4678
virtual QPointF pixelPosition() const Q_DECL_OVERRIDE
WidthType widthType() const
Definition: qcustomplot.h:5901
QCPItemAnchor * parentAnchor() const
Definition: qcustomplot.h:3471
virtual QRect clipRect() const
double keyPixelOffset(const QCPBars *bars, double keyCoord)
QVector< double > subTickPositions
Definition: qcustomplot.h:2260
virtual QCPPlottableInterface1D * interface1D()
Definition: qcustomplot.h:3341
void setSelectedPen(const QPen &pen)
void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
QList< QCPAbstractLegendItem * > selectedItems() const
static const double maxRange
Definition: qcustomplot.h:814
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const =0
double getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const
bool rangeReversed() const
Definition: qcustomplot.h:2018
QCPAxis * xAxis
Definition: qcustomplot.h:3739
QList< QList< QCPLayoutElement * > > mElements
Definition: qcustomplot.h:1401
double spacing() const
Definition: qcustomplot.h:5411
QHash< QCP::MarginSide, QList< QCPLayoutElement * > > mChildren
Definition: qcustomplot.h:1168
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
void setSubTickLengthOut(int outside)
QBrush brush() const
Definition: qcustomplot.h:6251
double size() const
Definition: qcustomplot.h:2356
void setTickStepStrategy(TickStepStrategy strategy)
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:5221
QCPItemAnchor *const top
Definition: qcustomplot.h:6265
QCPSelectionRect * mSelectionRect
Definition: qcustomplot.h:3785
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QList< QCPLayerable * > layerableListAt(const QPointF &pos, bool onlySelectable, QList< QVariant > *selectionDetails=0) const
QCPItemPosition *const point1
Definition: qcustomplot.h:6113
void addElement(QCPLayoutElement *element, Qt::Alignment alignment)
QCPAxis(QCPAxisRect *parent, AxisType type)
const QPolygonF getFillPolygon(const QVector< QPointF > *lineData, QCPDataRange segment) const
void setInsetAlignment(int index, Qt::Alignment alignment)
virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE
WidthType mWidthType
Definition: qcustomplot.h:5937
QCPLineEnding mTail
Definition: qcustomplot.h:6168
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void append(QCPBars *bars)
EndingStyle mStyle
Definition: qcustomplot.h:1525
QList< QCPAxis * > selectedAxes() const
QList< QCPAbstractItem * > items() const
QStack< bool > mAntialiasingStack
Definition: qcustomplot.h:509
virtual ~QCPAbstractItem()
void getVisibleDataBounds(QCPStatisticalBoxDataContainer::const_iterator &begin, QCPStatisticalBoxDataContainer::const_iterator &end) const
QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setWidth(double width)
void setTickLabelColor(const QColor &color)
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
{ssDiamond.png} a diamond
Definition: qcustomplot.h:2333
QCPAxis * rangeDragAxis(Qt::Orientation orientation)
QMap< double, QColor > colorStops() const
Definition: qcustomplot.h:4481
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
const QCPRange range() const
Definition: qcustomplot.h:2017
void setData(QCPColorMapData *data, bool copy=false)
virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection) Q_DECL_OVERRIDE
void setTickOrigin(double origin)
virtual ~QCPBars()
QList< QCPLayer * > mLayers
Definition: qcustomplot.h:3771
Holds the data of one single data point for QCPFinancial.
Definition: qcustomplot.h:5827
{ssTriangle.png} an equilateral triangle, standing on baseline
Definition: qcustomplot.h:2335
void setDataPlottable(QCPAbstractPlottable *plottable)
QCPAxisRect * axisRect(int index=0) const
QList< QCPAxis * > addAxes(QCPAxis::AxisTypes types)
void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
int dataPointCount() const
When dragging the mouse, a selection rect becomes active. Upon releasing, plottable data points that ...
Definition: qcustomplot.h:274
QColor mColor
Definition: qcustomplot.h:6363
Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
QCPRange dataBounds() const
Definition: qcustomplot.h:5704
Resolution is given in dots per meter (dpm)
Definition: qcustomplot.h:170
void setAutoAddPlottableToLegend(bool on)
void setTickStep(double step)
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
QPen subTickPen() const
Definition: qcustomplot.h:2038
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setSpacingType(SpacingType spacingType)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
double x() const
Definition: qcustomplot.h:396
virtual void update(UpdatePhase phase)
void updateAxesOffset(QCPAxis::AxisType type)
QSharedPointer< QCPGraphDataContainer > data() const
Definition: qcustomplot.h:5193
virtual void wheelEvent(QWheelEvent *event)
QCPItemTracer(QCustomPlot *parentPlot)
void setScaleStrategy(ScaleStrategy strategy)
void setSelectedBrush(const QBrush &brush)
void selectionChanged(bool selected)
double size() const
Definition: qcustomplot.h:6558
ColorInterpolation mColorInterpolation
Definition: qcustomplot.h:4504
QBrush mSelectedBrush
Definition: qcustomplot.h:6365
QCPVector2D normalized() const
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
LineStyle mLineStyle
Definition: qcustomplot.h:5354
void setSelectedTickPen(const QPen &pen)
void setLevelCount(int n)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPRange bounded(double lowerBound, double upperBound) const
void setRowStretchFactor(int row, double factor)
bool begin(QPaintDevice *device)
void setUpperEnding(const QCPLineEnding &ending)
QString label() const
virtual ~QCPLegend()
QCPItemAnchor *const right
Definition: qcustomplot.h:6267
QCPItemAnchor *const bottomLeft
Definition: qcustomplot.h:6269
double dot(const QCPVector2D &vec) const
Definition: qcustomplot.h:415
bool rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const
Interaction
Definition: qcustomplot.h:256
QCPLayer * layer(const QString &name) const
void setSelectionDecorator(QCPSelectionDecorator *decorator)
width is in absolute pixels
Definition: qcustomplot.h:5878
void setSelectionRectMode(QCP::SelectionRectMode mode)
QCPItemAnchor *const topLeft
Definition: qcustomplot.h:6350
QString text() const
Definition: qcustomplot.h:4957
int end() const
Definition: qcustomplot.h:906
bool addToLegend(QCPLegend *legend)
int offset() const
double coordToPixel(double value) const
void setPixelPosition(const QPointF &pixelPosition)
int axisRectCount() const
bool moveToLayer(QCPLayer *layer, bool prepend)
QCPItemAnchor *const left
Definition: qcustomplot.h:6428
QCPColorMapData * data() const
Definition: qcustomplot.h:5764


graph_rviz_plugin
Author(s): Édouard Pronier, Victor Lamoine - Institut Maupertuis
autogenerated on Mon Feb 28 2022 22:27:30