SimCenterCommon
Common functionality used within different SimCenter projects
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-2016 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: 13.09.16 **
23 ** Version: 2.0.0-beta **
24 ****************************************************************************/
25 
26 #include "qcustomplot.h"
27 
28 
29 /* including file 'src/vector2d.cpp', size 7340 */
30 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +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 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +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 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +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 37064 */
954 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +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.toStrongRef()->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.toStrongRef()->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.toStrongRef()->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.toStrongRef()->clear(Qt::transparent);
1180  mPaintBuffer.toStrongRef()->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.toStrongRef()->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.toStrongRef()->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 
1471 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1472 {
1473  Q_UNUSED(pos)
1474  Q_UNUSED(onlySelectable)
1475  Q_UNUSED(details)
1476  return -1.0;
1477 }
1478 
1497 {
1498  if (mParentPlot)
1499  {
1500  qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1501  return;
1502  }
1503 
1504  if (!parentPlot)
1505  qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1506 
1509 }
1510 
1523 {
1525 }
1526 
1536 {
1537  if (layer && !mParentPlot)
1538  {
1539  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1540  return false;
1541  }
1542  if (layer && layer->parentPlot() != mParentPlot)
1543  {
1544  qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1545  return false;
1546  }
1547 
1548  QCPLayer *oldLayer = mLayer;
1549  if (mLayer)
1550  mLayer->removeChild(this);
1551  mLayer = layer;
1552  if (mLayer)
1553  mLayer->addChild(this, prepend);
1554  if (mLayer != oldLayer)
1555  emit layerChanged(mLayer);
1556  return true;
1557 }
1558 
1566 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1567 {
1568  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1569  painter->setAntialiasing(false);
1570  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1571  painter->setAntialiasing(true);
1572  else
1573  painter->setAntialiasing(localAntialiased);
1574 }
1575 
1593 {
1594  Q_UNUSED(parentPlot)
1595 }
1596 
1609 {
1610  return QCP::iSelectOther;
1611 }
1612 
1623 {
1624  if (mParentPlot)
1625  return mParentPlot->viewport();
1626  else
1627  return QRect();
1628 }
1629 
1658 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1659 {
1660  Q_UNUSED(event)
1661  Q_UNUSED(additive)
1662  Q_UNUSED(details)
1663  Q_UNUSED(selectionStateChanged)
1664 }
1665 
1678 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1679 {
1680  Q_UNUSED(selectionStateChanged)
1681 }
1682 
1708 void QCPLayerable::mousePressEvent(QMouseEvent *event, const QVariant &details)
1709 {
1710  Q_UNUSED(details)
1711  event->ignore();
1712 }
1713 
1726 void QCPLayerable::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
1727 {
1728  Q_UNUSED(startPos)
1729  event->ignore();
1730 }
1731 
1744 void QCPLayerable::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
1745 {
1746  Q_UNUSED(startPos)
1747  event->ignore();
1748 }
1749 
1776 void QCPLayerable::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
1777 {
1778  Q_UNUSED(details)
1779  event->ignore();
1780 }
1781 
1799 void QCPLayerable::wheelEvent(QWheelEvent *event)
1800 {
1801  event->ignore();
1802 }
1803 /* end of 'src/layer.cpp' */
1804 
1805 
1806 /* including file 'src/axis/range.cpp', size 12221 */
1807 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
1808 
1812 
1821 /* start of documentation of inline functions */
1822 
1864 /* end of documentation of inline functions */
1865 
1877 const double QCPRange::minRange = 1e-280;
1878 
1890 const double QCPRange::maxRange = 1e250;
1891 
1896  lower(0),
1897  upper(0)
1898 {
1899 }
1900 
1908 QCPRange::QCPRange(double lower, double upper) :
1909  lower(lower),
1910  upper(upper)
1911 {
1912  normalize();
1913 }
1914 
1927 void QCPRange::expand(const QCPRange &otherRange)
1928 {
1929  if (lower > otherRange.lower || qIsNaN(lower))
1930  lower = otherRange.lower;
1931  if (upper < otherRange.upper || qIsNaN(upper))
1932  upper = otherRange.upper;
1933 }
1934 
1947 void QCPRange::expand(double includeCoord)
1948 {
1949  if (lower > includeCoord || qIsNaN(lower))
1950  lower = includeCoord;
1951  if (upper < includeCoord || qIsNaN(upper))
1952  upper = includeCoord;
1953 }
1954 
1955 
1966 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1967 {
1968  QCPRange result = *this;
1969  result.expand(otherRange);
1970  return result;
1971 }
1972 
1983 QCPRange QCPRange::expanded(double includeCoord) const
1984 {
1985  QCPRange result = *this;
1986  result.expand(includeCoord);
1987  return result;
1988 }
1989 
1998 QCPRange QCPRange::bounded(double lowerBound, double upperBound) const
1999 {
2000  if (lowerBound > upperBound)
2001  qSwap(lowerBound, upperBound);
2002 
2003  QCPRange result(lower, upper);
2004  if (result.lower < lowerBound)
2005  {
2006  result.lower = lowerBound;
2007  result.upper = lowerBound + size();
2008  if (result.upper > upperBound || qFuzzyCompare(size(), upperBound-lowerBound))
2009  result.upper = upperBound;
2010  } else if (result.upper > upperBound)
2011  {
2012  result.upper = upperBound;
2013  result.lower = upperBound - size();
2014  if (result.lower < lowerBound || qFuzzyCompare(size(), upperBound-lowerBound))
2015  result.lower = lowerBound;
2016  }
2017 
2018  return result;
2019 }
2020 
2034 {
2035  double rangeFac = 1e-3;
2036  QCPRange sanitizedRange(lower, upper);
2037  sanitizedRange.normalize();
2038  // can't have range spanning negative and positive values in log plot, so change range to fix it
2039  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
2040  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
2041  {
2042  // case lower is 0
2043  if (rangeFac < sanitizedRange.upper*rangeFac)
2044  sanitizedRange.lower = rangeFac;
2045  else
2046  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2047  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
2048  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
2049  {
2050  // case upper is 0
2051  if (-rangeFac > sanitizedRange.lower*rangeFac)
2052  sanitizedRange.upper = -rangeFac;
2053  else
2054  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2055  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
2056  {
2057  // find out whether negative or positive interval is wider to decide which sign domain will be chosen
2058  if (-sanitizedRange.lower > sanitizedRange.upper)
2059  {
2060  // negative is wider, do same as in case upper is 0
2061  if (-rangeFac > sanitizedRange.lower*rangeFac)
2062  sanitizedRange.upper = -rangeFac;
2063  else
2064  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2065  } else
2066  {
2067  // positive is wider, do same as in case lower is 0
2068  if (rangeFac < sanitizedRange.upper*rangeFac)
2069  sanitizedRange.lower = rangeFac;
2070  else
2071  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2072  }
2073  }
2074  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
2075  return sanitizedRange;
2076 }
2077 
2083 {
2084  QCPRange sanitizedRange(lower, upper);
2085  sanitizedRange.normalize();
2086  return sanitizedRange;
2087 }
2088 
2097 bool QCPRange::validRange(double lower, double upper)
2098 {
2099  return (lower > -maxRange &&
2100  upper < maxRange &&
2101  qAbs(lower-upper) > minRange &&
2102  qAbs(lower-upper) < maxRange &&
2103  !(lower > 0 && qIsInf(upper/lower)) &&
2104  !(upper < 0 && qIsInf(lower/upper)));
2105 }
2106 
2116 bool QCPRange::validRange(const QCPRange &range)
2117 {
2118  return (range.lower > -maxRange &&
2119  range.upper < maxRange &&
2120  qAbs(range.lower-range.upper) > minRange &&
2121  qAbs(range.lower-range.upper) < maxRange &&
2122  !(range.lower > 0 && qIsInf(range.upper/range.lower)) &&
2123  !(range.upper < 0 && qIsInf(range.lower/range.upper)));
2124 }
2125 /* end of 'src/axis/range.cpp' */
2126 
2127 
2128 /* including file 'src/selection.cpp', size 21898 */
2129 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
2130 
2134 
2160 /* start documentation of inline functions */
2161 
2220 /* end documentation of inline functions */
2221 
2226  mBegin(0),
2227  mEnd(0)
2228 {
2229 }
2230 
2237  mBegin(begin),
2238  mEnd(end)
2239 {
2240 }
2241 
2252 {
2253  QCPDataRange result(intersection(other));
2254  if (result.isEmpty()) // no intersection, preserve respective bounding side of otherRange as both begin and end of return value
2255  {
2256  if (mEnd <= other.mBegin)
2257  result = QCPDataRange(other.mBegin, other.mBegin);
2258  else
2259  result = QCPDataRange(other.mEnd, other.mEnd);
2260  }
2261  return result;
2262 }
2263 
2268 {
2269  return QCPDataRange(qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd));
2270 }
2271 
2283 {
2284  QCPDataRange result(qMax(mBegin, other.mBegin), qMin(mEnd, other.mEnd));
2285  if (result.isValid())
2286  return result;
2287  else
2288  return QCPDataRange();
2289 }
2290 
2296 bool QCPDataRange::intersects(const QCPDataRange &other) const
2297 {
2298  return !( (mBegin > other.mBegin && mBegin >= other.mEnd) ||
2299  (mEnd <= other.mBegin && mEnd < other.mEnd) );
2300 }
2301 
2307 bool QCPDataRange::contains(const QCPDataRange &other) const
2308 {
2309  return mBegin <= other.mBegin && mEnd >= other.mEnd;
2310 }
2311 
2312 
2313 
2317 
2351 /* start documentation of inline functions */
2352 
2378 /* end documentation of inline functions */
2379 
2384 {
2385 }
2386 
2391 {
2392  mDataRanges.append(range);
2393 }
2394 
2403 {
2404  if (mDataRanges.size() != other.mDataRanges.size())
2405  return false;
2406  for (int i=0; i<mDataRanges.size(); ++i)
2407  {
2408  if (mDataRanges.at(i) != other.mDataRanges.at(i))
2409  return false;
2410  }
2411  return true;
2412 }
2413 
2419 {
2420  mDataRanges << other.mDataRanges;
2421  simplify();
2422  return *this;
2423 }
2424 
2430 {
2431  addDataRange(other);
2432  return *this;
2433 }
2434 
2439 {
2440  for (int i=0; i<other.dataRangeCount(); ++i)
2441  *this -= other.dataRange(i);
2442 
2443  return *this;
2444 }
2445 
2450 {
2451  if (other.isEmpty() || isEmpty())
2452  return *this;
2453 
2454  simplify();
2455  int i=0;
2456  while (i < mDataRanges.size())
2457  {
2458  const int thisBegin = mDataRanges.at(i).begin();
2459  const int thisEnd = mDataRanges.at(i).end();
2460  if (thisBegin >= other.end())
2461  break; // since data ranges are sorted after the simplify() call, no ranges which contain other will come after this
2462 
2463  if (thisEnd > other.begin()) // ranges which don't fulfill this are entirely before other and can be ignored
2464  {
2465  if (thisBegin >= other.begin()) // range leading segment is encompassed
2466  {
2467  if (thisEnd <= other.end()) // range fully encompassed, remove completely
2468  {
2469  mDataRanges.removeAt(i);
2470  continue;
2471  } else // only leading segment is encompassed, trim accordingly
2472  mDataRanges[i].setBegin(other.end());
2473  } else // leading segment is not encompassed
2474  {
2475  if (thisEnd <= other.end()) // only trailing segment is encompassed, trim accordingly
2476  {
2477  mDataRanges[i].setEnd(other.begin());
2478  } else // other lies inside this range, so split range
2479  {
2480  mDataRanges[i].setEnd(other.begin());
2481  mDataRanges.insert(i+1, QCPDataRange(other.end(), thisEnd));
2482  break; // since data ranges are sorted (and don't overlap) after simplify() call, we're done here
2483  }
2484  }
2485  }
2486  ++i;
2487  }
2488 
2489  return *this;
2490 }
2491 
2497 {
2498  int result = 0;
2499  for (int i=0; i<mDataRanges.size(); ++i)
2500  result += mDataRanges.at(i).length();
2501  return result;
2502 }
2503 
2513 {
2514  if (index >= 0 && index < mDataRanges.size())
2515  {
2516  return mDataRanges.at(index);
2517  } else
2518  {
2519  qDebug() << Q_FUNC_INFO << "index out of range:" << index;
2520  return QCPDataRange();
2521  }
2522 }
2523 
2529 {
2530  if (isEmpty())
2531  return QCPDataRange();
2532  else
2533  return QCPDataRange(mDataRanges.first().begin(), mDataRanges.last().end());
2534 }
2535 
2542 void QCPDataSelection::addDataRange(const QCPDataRange &dataRange, bool simplify)
2543 {
2544  mDataRanges.append(dataRange);
2545  if (simplify)
2546  this->simplify();
2547 }
2548 
2555 {
2556  mDataRanges.clear();
2557 }
2558 
2569 {
2570  // remove any empty ranges:
2571  for (int i=mDataRanges.size()-1; i>=0; --i)
2572  {
2573  if (mDataRanges.at(i).isEmpty())
2574  mDataRanges.removeAt(i);
2575  }
2576  if (mDataRanges.isEmpty())
2577  return;
2578 
2579  // sort ranges by starting value, ascending:
2580  std::sort(mDataRanges.begin(), mDataRanges.end(), lessThanDataRangeBegin);
2581 
2582  // join overlapping/contiguous ranges:
2583  int i = 1;
2584  while (i < mDataRanges.size())
2585  {
2586  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
2587  {
2588  mDataRanges[i-1].setEnd(qMax(mDataRanges.at(i-1).end(), mDataRanges.at(i).end()));
2589  mDataRanges.removeAt(i);
2590  } else
2591  ++i;
2592  }
2593 }
2594 
2606 {
2607  simplify();
2608  switch (type)
2609  {
2610  case QCP::stNone:
2611  {
2612  mDataRanges.clear();
2613  break;
2614  }
2615  case QCP::stWhole:
2616  {
2617  // whole selection isn't defined by data range, so don't change anything (is handled in plottable methods)
2618  break;
2619  }
2620  case QCP::stSingleData:
2621  {
2622  // reduce all data ranges to the single first data point:
2623  if (!mDataRanges.isEmpty())
2624  {
2625  if (mDataRanges.size() > 1)
2626  mDataRanges = QList<QCPDataRange>() << mDataRanges.first();
2627  if (mDataRanges.first().length() > 1)
2628  mDataRanges.first().setEnd(mDataRanges.first().begin()+1);
2629  }
2630  break;
2631  }
2632  case QCP::stDataRange:
2633  {
2634  mDataRanges = QList<QCPDataRange>() << span();
2635  break;
2636  }
2638  {
2639  // this is the selection type that allows all concievable combinations of ranges, so do nothing
2640  break;
2641  }
2642  }
2643 }
2644 
2652 {
2653  if (other.isEmpty()) return false;
2654 
2655  int otherIndex = 0;
2656  int thisIndex = 0;
2657  while (thisIndex < mDataRanges.size() && otherIndex < other.mDataRanges.size())
2658  {
2659  if (mDataRanges.at(thisIndex).contains(other.mDataRanges.at(otherIndex)))
2660  ++otherIndex;
2661  else
2662  ++thisIndex;
2663  }
2664  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
2665 }
2666 
2676 {
2677  QCPDataSelection result;
2678  for (int i=0; i<mDataRanges.size(); ++i)
2679  result.addDataRange(mDataRanges.at(i).intersection(other), false);
2680  result.simplify();
2681  return result;
2682 }
2683 
2689 {
2690  QCPDataSelection result;
2691  for (int i=0; i<other.dataRangeCount(); ++i)
2692  result += intersection(other.dataRange(i));
2693  result.simplify();
2694  return result;
2695 }
2696 
2707 {
2708  if (isEmpty())
2709  return QCPDataSelection(outerRange);
2710  QCPDataRange fullRange = outerRange.expanded(span());
2711 
2712  QCPDataSelection result;
2713  // first unselected segment:
2714  if (mDataRanges.first().begin() != fullRange.begin())
2715  result.addDataRange(QCPDataRange(fullRange.begin(), mDataRanges.first().begin()), false);
2716  // intermediate unselected segments:
2717  for (int i=1; i<mDataRanges.size(); ++i)
2718  result.addDataRange(QCPDataRange(mDataRanges.at(i-1).end(), mDataRanges.at(i).begin()), false);
2719  // last unselected segment:
2720  if (mDataRanges.last().end() != fullRange.end())
2721  result.addDataRange(QCPDataRange(mDataRanges.last().end(), fullRange.end()), false);
2722  result.simplify();
2723  return result;
2724 }
2725 /* end of 'src/selection.cpp' */
2726 
2727 
2728 /* including file 'src/selectionrect.cpp', size 9224 */
2729 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
2730 
2734 
2757 /* start of documentation of inline functions */
2758 
2767 /* end of documentation of inline functions */
2768 /* start documentation of signals */
2769 
2806 /* end documentation of signals */
2807 
2814  QCPLayerable(parentPlot),
2815  mPen(QBrush(Qt::gray), 0, Qt::DashLine),
2816  mBrush(Qt::NoBrush),
2817  mActive(false)
2818 {
2819 }
2820 
2822 {
2823  cancel();
2824 }
2825 
2831 {
2832  if (axis)
2833  {
2834  if (axis->orientation() == Qt::Horizontal)
2835  return QCPRange(axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width()));
2836  else
2837  return QCPRange(axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top()));
2838  } else
2839  {
2840  qDebug() << Q_FUNC_INFO << "called with axis zero";
2841  return QCPRange();
2842  }
2843 }
2844 
2851 {
2852  mPen = pen;
2853 }
2854 
2862 {
2863  mBrush = brush;
2864 }
2865 
2871 {
2872  if (mActive)
2873  {
2874  mActive = false;
2875  emit canceled(mRect, 0);
2876  }
2877 }
2878 
2885 void QCPSelectionRect::startSelection(QMouseEvent *event)
2886 {
2887  mActive = true;
2888  mRect = QRect(event->pos(), event->pos());
2889  emit started(event);
2890 }
2891 
2898 void QCPSelectionRect::moveSelection(QMouseEvent *event)
2899 {
2900  mRect.setBottomRight(event->pos());
2901  emit changed(mRect, event);
2902  layer()->replot();
2903 }
2904 
2911 void QCPSelectionRect::endSelection(QMouseEvent *event)
2912 {
2913  mRect.setBottomRight(event->pos());
2914  mActive = false;
2915  emit accepted(mRect, event);
2916 }
2917 
2924 void QCPSelectionRect::keyPressEvent(QKeyEvent *event)
2925 {
2926  if (event->key() == Qt::Key_Escape && mActive)
2927  {
2928  mActive = false;
2929  emit canceled(mRect, event);
2930  }
2931 }
2932 
2933 /* inherits documentation from base class */
2935 {
2937 }
2938 
2946 {
2947  if (mActive)
2948  {
2949  painter->setPen(mPen);
2950  painter->setBrush(mBrush);
2951  painter->drawRect(mRect);
2952  }
2953 }
2954 /* end of 'src/selectionrect.cpp' */
2955 
2956 
2957 /* including file 'src/layout.cpp', size 74302 */
2958 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
2959 
2963 
2995 /* start documentation of inline functions */
2996 
3003 /* end documentation of inline functions */
3004 
3009  QObject(parentPlot),
3010  mParentPlot(parentPlot)
3011 {
3012  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
3013  mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
3014  mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
3015  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
3016 }
3017 
3019 {
3020  clear();
3021 }
3022 
3028 {
3029  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
3030  while (it.hasNext())
3031  {
3032  it.next();
3033  if (!it.value().isEmpty())
3034  return false;
3035  }
3036  return true;
3037 }
3038 
3044 {
3045  // make all children remove themselves from this margin group:
3046  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
3047  while (it.hasNext())
3048  {
3049  it.next();
3050  const QList<QCPLayoutElement*> elements = it.value();
3051  for (int i=elements.size()-1; i>=0; --i)
3052  elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
3053  }
3054 }
3055 
3067 {
3068  // query all automatic margins of the layout elements in this margin group side and find maximum:
3069  int result = 0;
3070  const QList<QCPLayoutElement*> elements = mChildren.value(side);
3071  for (int i=0; i<elements.size(); ++i)
3072  {
3073  if (!elements.at(i)->autoMargins().testFlag(side))
3074  continue;
3075  int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
3076  if (m > result)
3077  result = m;
3078  }
3079  return result;
3080 }
3081 
3089 {
3090  if (!mChildren[side].contains(element))
3091  mChildren[side].append(element);
3092  else
3093  qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
3094 }
3095 
3103 {
3104  if (!mChildren[side].removeOne(element))
3105  qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
3106 }
3107 
3108 
3112 
3139 /* start documentation of inline functions */
3140 
3159 /* end documentation of inline functions */
3160 
3165  QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
3166  mParentLayout(0),
3167  mMinimumSize(),
3168  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
3169  mRect(0, 0, 0, 0),
3170  mOuterRect(0, 0, 0, 0),
3171  mMargins(0, 0, 0, 0),
3172  mMinimumMargins(0, 0, 0, 0),
3173  mAutoMargins(QCP::msAll)
3174 {
3175 }
3176 
3178 {
3179  setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
3180  // unregister at layout:
3181  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
3182  mParentLayout->take(this);
3183 }
3184 
3197 {
3198  if (mOuterRect != rect)
3199  {
3200  mOuterRect = rect;
3201  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
3202  }
3203 }
3204 
3217 {
3218  if (mMargins != margins)
3219  {
3220  mMargins = margins;
3221  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
3222  }
3223 }
3224 
3235 {
3236  if (mMinimumMargins != margins)
3237  {
3239  }
3240 }
3241 
3252 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
3253 {
3254  mAutoMargins = sides;
3255 }
3256 
3267 {
3268  if (mMinimumSize != size)
3269  {
3270  mMinimumSize = size;
3271  if (mParentLayout)
3273  }
3274 }
3275 
3280 void QCPLayoutElement::setMinimumSize(int width, int height)
3281 {
3282  setMinimumSize(QSize(width, height));
3283 }
3284 
3290 {
3291  if (mMaximumSize != size)
3292  {
3293  mMaximumSize = size;
3294  if (mParentLayout)
3296  }
3297 }
3298 
3303 void QCPLayoutElement::setMaximumSize(int width, int height)
3304 {
3305  setMaximumSize(QSize(width, height));
3306 }
3307 
3321 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
3322 {
3323  QVector<QCP::MarginSide> sideVector;
3324  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
3325  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
3326  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
3327  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
3328 
3329  for (int i=0; i<sideVector.size(); ++i)
3330  {
3331  QCP::MarginSide side = sideVector.at(i);
3332  if (marginGroup(side) != group)
3333  {
3334  QCPMarginGroup *oldGroup = marginGroup(side);
3335  if (oldGroup) // unregister at old group
3336  oldGroup->removeChild(side, this);
3337 
3338  if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
3339  {
3340  mMarginGroups.remove(side);
3341  } else // setting to a new group
3342  {
3343  mMarginGroups[side] = group;
3344  group->addChild(side, this);
3345  }
3346  }
3347  }
3348 }
3349 
3363 {
3364  if (phase == upMargins)
3365  {
3366  if (mAutoMargins != QCP::msNone)
3367  {
3368  // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
3369  QMargins newMargins = mMargins;
3370  QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
3371  foreach (QCP::MarginSide side, allMarginSides)
3372  {
3373  if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
3374  {
3375  if (mMarginGroups.contains(side))
3376  QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
3377  else
3378  QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
3379  // apply minimum margin restrictions:
3380  if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
3381  QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
3382  }
3383  }
3384  setMargins(newMargins);
3385  }
3386  }
3387 }
3388 
3397 {
3398  return mMinimumSize;
3399 }
3400 
3409 {
3410  return mMaximumSize;
3411 }
3412 
3420 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
3421 {
3422  Q_UNUSED(recursive)
3423  return QList<QCPLayoutElement*>();
3424 }
3425 
3437 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3438 {
3439  Q_UNUSED(details)
3440 
3441  if (onlySelectable)
3442  return -1;
3443 
3444  if (QRectF(mOuterRect).contains(pos))
3445  {
3446  if (mParentPlot)
3447  return mParentPlot->selectionTolerance()*0.99;
3448  else
3449  {
3450  qDebug() << Q_FUNC_INFO << "parent plot not defined";
3451  return -1;
3452  }
3453  } else
3454  return -1;
3455 }
3456 
3463 {
3464  foreach (QCPLayoutElement* el, elements(false))
3465  {
3466  if (!el->parentPlot())
3467  el->initializeParentPlot(parentPlot);
3468  }
3469 }
3470 
3481 {
3483 }
3484 
3497 {
3498 }
3499 
3503 
3527 /* start documentation of pure virtual functions */
3528 
3571 /* end documentation of pure virtual functions */
3572 
3578 {
3579 }
3580 
3590 {
3591  QCPLayoutElement::update(phase);
3592 
3593  // set child element rects according to layout:
3594  if (phase == upLayout)
3595  updateLayout();
3596 
3597  // propagate update call to child elements:
3598  const int elCount = elementCount();
3599  for (int i=0; i<elCount; ++i)
3600  {
3601  if (QCPLayoutElement *el = elementAt(i))
3602  el->update(phase);
3603  }
3604 }
3605 
3606 /* inherits documentation from base class */
3607 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
3608 {
3609  const int c = elementCount();
3610  QList<QCPLayoutElement*> result;
3611 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
3612  result.reserve(c);
3613 #endif
3614  for (int i=0; i<c; ++i)
3615  result.append(elementAt(i));
3616  if (recursive)
3617  {
3618  for (int i=0; i<c; ++i)
3619  {
3620  if (result.at(i))
3621  result << result.at(i)->elements(recursive);
3622  }
3623  }
3624  return result;
3625 }
3626 
3635 {
3636 }
3637 
3649 bool QCPLayout::removeAt(int index)
3650 {
3651  if (QCPLayoutElement *el = takeAt(index))
3652  {
3653  delete el;
3654  return true;
3655  } else
3656  return false;
3657 }
3658 
3671 {
3672  if (take(element))
3673  {
3674  delete element;
3675  return true;
3676  } else
3677  return false;
3678 }
3679 
3687 {
3688  for (int i=elementCount()-1; i>=0; --i)
3689  {
3690  if (elementAt(i))
3691  removeAt(i);
3692  }
3693  simplify();
3694 }
3695 
3705 {
3706  if (QWidget *w = qobject_cast<QWidget*>(parent()))
3707  w->updateGeometry();
3708  else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
3709  l->sizeConstraintsChanged();
3710 }
3711 
3725 {
3726 }
3727 
3728 
3742 {
3743  if (el)
3744  {
3745  el->mParentLayout = this;
3746  el->setParentLayerable(this);
3747  el->setParent(this);
3748  if (!el->parentPlot())
3750  el->layoutChanged();
3751  } else
3752  qDebug() << Q_FUNC_INFO << "Null element passed";
3753 }
3754 
3766 {
3767  if (el)
3768  {
3769  el->mParentLayout = 0;
3770  el->setParentLayerable(0);
3771  el->setParent(mParentPlot);
3772  // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
3773  } else
3774  qDebug() << Q_FUNC_INFO << "Null element passed";
3775 }
3776 
3806 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
3807 {
3808  if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
3809  {
3810  qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
3811  return QVector<int>();
3812  }
3813  if (stretchFactors.isEmpty())
3814  return QVector<int>();
3815  int sectionCount = stretchFactors.size();
3816  QVector<double> sectionSizes(sectionCount);
3817  // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
3818  int minSizeSum = 0;
3819  for (int i=0; i<sectionCount; ++i)
3820  minSizeSum += minSizes.at(i);
3821  if (totalSize < minSizeSum)
3822  {
3823  // new stretch factors are minimum sizes and minimum sizes are set to zero:
3824  for (int i=0; i<sectionCount; ++i)
3825  {
3826  stretchFactors[i] = minSizes.at(i);
3827  minSizes[i] = 0;
3828  }
3829  }
3830 
3831  QList<int> minimumLockedSections;
3832  QList<int> unfinishedSections;
3833  for (int i=0; i<sectionCount; ++i)
3834  unfinishedSections.append(i);
3835  double freeSize = totalSize;
3836 
3837  int outerIterations = 0;
3838  while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
3839  {
3840  ++outerIterations;
3841  int innerIterations = 0;
3842  while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
3843  {
3844  ++innerIterations;
3845  // find section that hits its maximum next:
3846  int nextId = -1;
3847  double nextMax = 1e12;
3848  for (int i=0; i<unfinishedSections.size(); ++i)
3849  {
3850  int secId = unfinishedSections.at(i);
3851  double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
3852  if (hitsMaxAt < nextMax)
3853  {
3854  nextMax = hitsMaxAt;
3855  nextId = secId;
3856  }
3857  }
3858  // 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
3859  // actually hits its maximum, without exceeding the total size when we add up all sections)
3860  double stretchFactorSum = 0;
3861  for (int i=0; i<unfinishedSections.size(); ++i)
3862  stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
3863  double nextMaxLimit = freeSize/stretchFactorSum;
3864  if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
3865  {
3866  for (int i=0; i<unfinishedSections.size(); ++i)
3867  {
3868  sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
3869  freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
3870  }
3871  unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
3872  } else // next maximum isn't hit, just distribute rest of free space on remaining sections
3873  {
3874  for (int i=0; i<unfinishedSections.size(); ++i)
3875  sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
3876  unfinishedSections.clear();
3877  }
3878  }
3879  if (innerIterations == sectionCount*2)
3880  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
3881 
3882  // now check whether the resulting section sizes violate minimum restrictions:
3883  bool foundMinimumViolation = false;
3884  for (int i=0; i<sectionSizes.size(); ++i)
3885  {
3886  if (minimumLockedSections.contains(i))
3887  continue;
3888  if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
3889  {
3890  sectionSizes[i] = minSizes.at(i); // set it to minimum
3891  foundMinimumViolation = true; // make sure we repeat the whole optimization process
3892  minimumLockedSections.append(i);
3893  }
3894  }
3895  if (foundMinimumViolation)
3896  {
3897  freeSize = totalSize;
3898  for (int i=0; i<sectionCount; ++i)
3899  {
3900  if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
3901  unfinishedSections.append(i);
3902  else
3903  freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
3904  }
3905  // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
3906  for (int i=0; i<unfinishedSections.size(); ++i)
3907  sectionSizes[unfinishedSections.at(i)] = 0;
3908  }
3909  }
3910  if (outerIterations == sectionCount*2)
3911  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
3912 
3913  QVector<int> result(sectionCount);
3914  for (int i=0; i<sectionCount; ++i)
3915  result[i] = qRound(sectionSizes.at(i));
3916  return result;
3917 }
3918 
3919 
3923 
3944 /* start documentation of inline functions */
3945 
3960 /* end documentation of inline functions */
3961 
3966  mColumnSpacing(5),
3967  mRowSpacing(5),
3968  mWrap(0),
3969  mFillOrder(foRowsFirst)
3970 {
3971 }
3972 
3974 {
3975  // clear all child layout elements. This is important because only the specific layouts know how
3976  // to handle removing elements (clear calls virtual removeAt method to do that).
3977  clear();
3978 }
3979 
3988 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
3989 {
3990  if (row >= 0 && row < mElements.size())
3991  {
3992  if (column >= 0 && column < mElements.first().size())
3993  {
3994  if (QCPLayoutElement *result = mElements.at(row).at(column))
3995  return result;
3996  else
3997  qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
3998  } else
3999  qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
4000  } else
4001  qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
4002  return 0;
4003 }
4004 
4005 
4021 {
4022  if (!hasElement(row, column))
4023  {
4024  if (element && element->layout()) // remove from old layout first
4025  element->layout()->take(element);
4026  expandTo(row+1, column+1);
4027  mElements[row][column] = element;
4028  if (element)
4029  adoptElement(element);
4030  return true;
4031  } else
4032  qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
4033  return false;
4034 }
4035 
4047 {
4048  int rowIndex = 0;
4049  int colIndex = 0;
4050  if (mFillOrder == foColumnsFirst)
4051  {
4052  while (hasElement(rowIndex, colIndex))
4053  {
4054  ++colIndex;
4055  if (colIndex >= mWrap && mWrap > 0)
4056  {
4057  colIndex = 0;
4058  ++rowIndex;
4059  }
4060  }
4061  } else
4062  {
4063  while (hasElement(rowIndex, colIndex))
4064  {
4065  ++rowIndex;
4066  if (rowIndex >= mWrap && mWrap > 0)
4067  {
4068  rowIndex = 0;
4069  ++colIndex;
4070  }
4071  }
4072  }
4073  return addElement(rowIndex, colIndex, element);
4074 }
4075 
4082 bool QCPLayoutGrid::hasElement(int row, int column)
4083 {
4084  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
4085  return mElements.at(row).at(column);
4086  else
4087  return false;
4088 }
4089 
4101 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
4102 {
4103  if (column >= 0 && column < columnCount())
4104  {
4105  if (factor > 0)
4106  mColumnStretchFactors[column] = factor;
4107  else
4108  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
4109  } else
4110  qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
4111 }
4112 
4124 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
4125 {
4126  if (factors.size() == mColumnStretchFactors.size())
4127  {
4128  mColumnStretchFactors = factors;
4129  for (int i=0; i<mColumnStretchFactors.size(); ++i)
4130  {
4131  if (mColumnStretchFactors.at(i) <= 0)
4132  {
4133  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
4134  mColumnStretchFactors[i] = 1;
4135  }
4136  }
4137  } else
4138  qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
4139 }
4140 
4152 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
4153 {
4154  if (row >= 0 && row < rowCount())
4155  {
4156  if (factor > 0)
4157  mRowStretchFactors[row] = factor;
4158  else
4159  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
4160  } else
4161  qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
4162 }
4163 
4175 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
4176 {
4177  if (factors.size() == mRowStretchFactors.size())
4178  {
4179  mRowStretchFactors = factors;
4180  for (int i=0; i<mRowStretchFactors.size(); ++i)
4181  {
4182  if (mRowStretchFactors.at(i) <= 0)
4183  {
4184  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
4185  mRowStretchFactors[i] = 1;
4186  }
4187  }
4188  } else
4189  qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
4190 }
4191 
4198 {
4199  mColumnSpacing = pixels;
4200 }
4201 
4208 {
4209  mRowSpacing = pixels;
4210 }
4211 
4229 void QCPLayoutGrid::setWrap(int count)
4230 {
4231  mWrap = qMax(0, count);
4232 }
4233 
4258 void QCPLayoutGrid::setFillOrder(FillOrder order, bool rearrange)
4259 {
4260  // if rearranging, take all elements via linear index of old fill order:
4261  const int elCount = elementCount();
4262  QVector<QCPLayoutElement*> tempElements;
4263  if (rearrange)
4264  {
4265  tempElements.reserve(elCount);
4266  for (int i=0; i<elCount; ++i)
4267  {
4268  if (elementAt(i))
4269  tempElements.append(takeAt(i));
4270  }
4271  simplify();
4272  }
4273  // change fill order as requested:
4274  mFillOrder = order;
4275  // if rearranging, re-insert via linear index according to new fill order:
4276  if (rearrange)
4277  {
4278  for (int i=0; i<tempElements.size(); ++i)
4279  addElement(tempElements.at(i));
4280  }
4281 }
4282 
4297 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
4298 {
4299  // add rows as necessary:
4300  while (rowCount() < newRowCount)
4301  {
4302  mElements.append(QList<QCPLayoutElement*>());
4303  mRowStretchFactors.append(1);
4304  }
4305  // go through rows and expand columns as necessary:
4306  int newColCount = qMax(columnCount(), newColumnCount);
4307  for (int i=0; i<rowCount(); ++i)
4308  {
4309  while (mElements.at(i).size() < newColCount)
4310  mElements[i].append(0);
4311  }
4312  while (mColumnStretchFactors.size() < newColCount)
4313  mColumnStretchFactors.append(1);
4314 }
4315 
4322 void QCPLayoutGrid::insertRow(int newIndex)
4323 {
4324  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
4325  {
4326  expandTo(1, 1);
4327  return;
4328  }
4329 
4330  if (newIndex < 0)
4331  newIndex = 0;
4332  if (newIndex > rowCount())
4333  newIndex = rowCount();
4334 
4335  mRowStretchFactors.insert(newIndex, 1);
4336  QList<QCPLayoutElement*> newRow;
4337  for (int col=0; col<columnCount(); ++col)
4338  newRow.append((QCPLayoutElement*)0);
4339  mElements.insert(newIndex, newRow);
4340 }
4341 
4349 {
4350  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
4351  {
4352  expandTo(1, 1);
4353  return;
4354  }
4355 
4356  if (newIndex < 0)
4357  newIndex = 0;
4358  if (newIndex > columnCount())
4359  newIndex = columnCount();
4360 
4361  mColumnStretchFactors.insert(newIndex, 1);
4362  for (int row=0; row<rowCount(); ++row)
4363  mElements[row].insert(newIndex, (QCPLayoutElement*)0);
4364 }
4365 
4379 int QCPLayoutGrid::rowColToIndex(int row, int column) const
4380 {
4381  if (row >= 0 && row < rowCount())
4382  {
4383  if (column >= 0 && column < columnCount())
4384  {
4385  switch (mFillOrder)
4386  {
4387  case foRowsFirst: return column*rowCount() + row;
4388  case foColumnsFirst: return row*columnCount() + column;
4389  }
4390  } else
4391  qDebug() << Q_FUNC_INFO << "row index out of bounds:" << row;
4392  } else
4393  qDebug() << Q_FUNC_INFO << "column index out of bounds:" << column;
4394  return 0;
4395 }
4396 
4412 void QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const
4413 {
4414  row = -1;
4415  column = -1;
4416  if (columnCount() == 0 || rowCount() == 0)
4417  return;
4418  if (index < 0 || index >= elementCount())
4419  {
4420  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
4421  return;
4422  }
4423 
4424  switch (mFillOrder)
4425  {
4426  case foRowsFirst:
4427  {
4428  column = index / rowCount();
4429  row = index % rowCount();
4430  break;
4431  }
4432  case foColumnsFirst:
4433  {
4434  row = index / columnCount();
4435  column = index % columnCount();
4436  break;
4437  }
4438  }
4439 }
4440 
4441 /* inherits documentation from base class */
4443 {
4444  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
4445  getMinimumRowColSizes(&minColWidths, &minRowHeights);
4446  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4447 
4448  int totalRowSpacing = (rowCount()-1) * mRowSpacing;
4449  int totalColSpacing = (columnCount()-1) * mColumnSpacing;
4450  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
4451  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
4452 
4453  // go through cells and set rects accordingly:
4454  int yOffset = mRect.top();
4455  for (int row=0; row<rowCount(); ++row)
4456  {
4457  if (row > 0)
4458  yOffset += rowHeights.at(row-1)+mRowSpacing;
4459  int xOffset = mRect.left();
4460  for (int col=0; col<columnCount(); ++col)
4461  {
4462  if (col > 0)
4463  xOffset += colWidths.at(col-1)+mColumnSpacing;
4464  if (mElements.at(row).at(col))
4465  mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
4466  }
4467  }
4468 }
4469 
4479 {
4480  if (index >= 0 && index < elementCount())
4481  {
4482  int row, col;
4483  indexToRowCol(index, row, col);
4484  return mElements.at(row).at(col);
4485  } else
4486  return 0;
4487 }
4488 
4498 {
4499  if (QCPLayoutElement *el = elementAt(index))
4500  {
4501  releaseElement(el);
4502  int row, col;
4503  indexToRowCol(index, row, col);
4504  mElements[row][col] = 0;
4505  return el;
4506  } else
4507  {
4508  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
4509  return 0;
4510  }
4511 }
4512 
4513 /* inherits documentation from base class */
4515 {
4516  if (element)
4517  {
4518  for (int i=0; i<elementCount(); ++i)
4519  {
4520  if (elementAt(i) == element)
4521  {
4522  takeAt(i);
4523  return true;
4524  }
4525  }
4526  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
4527  } else
4528  qDebug() << Q_FUNC_INFO << "Can't take null element";
4529  return false;
4530 }
4531 
4532 /* inherits documentation from base class */
4533 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
4534 {
4535  QList<QCPLayoutElement*> result;
4536  const int elCount = elementCount();
4537 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
4538  result.reserve(elCount);
4539 #endif
4540  for (int i=0; i<elCount; ++i)
4541  result.append(elementAt(i));
4542  if (recursive)
4543  {
4544  for (int i=0; i<elCount; ++i)
4545  {
4546  if (result.at(i))
4547  result << result.at(i)->elements(recursive);
4548  }
4549  }
4550  return result;
4551 }
4552 
4557 {
4558  // remove rows with only empty cells:
4559  for (int row=rowCount()-1; row>=0; --row)
4560  {
4561  bool hasElements = false;
4562  for (int col=0; col<columnCount(); ++col)
4563  {
4564  if (mElements.at(row).at(col))
4565  {
4566  hasElements = true;
4567  break;
4568  }
4569  }
4570  if (!hasElements)
4571  {
4572  mRowStretchFactors.removeAt(row);
4573  mElements.removeAt(row);
4574  if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
4575  mColumnStretchFactors.clear();
4576  }
4577  }
4578 
4579  // remove columns with only empty cells:
4580  for (int col=columnCount()-1; col>=0; --col)
4581  {
4582  bool hasElements = false;
4583  for (int row=0; row<rowCount(); ++row)
4584  {
4585  if (mElements.at(row).at(col))
4586  {
4587  hasElements = true;
4588  break;
4589  }
4590  }
4591  if (!hasElements)
4592  {
4593  mColumnStretchFactors.removeAt(col);
4594  for (int row=0; row<rowCount(); ++row)
4595  mElements[row].removeAt(col);
4596  }
4597  }
4598 }
4599 
4600 /* inherits documentation from base class */
4602 {
4603  QVector<int> minColWidths, minRowHeights;
4604  getMinimumRowColSizes(&minColWidths, &minRowHeights);
4605  QSize result(0, 0);
4606  for (int i=0; i<minColWidths.size(); ++i)
4607  result.rwidth() += minColWidths.at(i);
4608  for (int i=0; i<minRowHeights.size(); ++i)
4609  result.rheight() += minRowHeights.at(i);
4610  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
4611  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
4612  return result;
4613 }
4614 
4615 /* inherits documentation from base class */
4617 {
4618  QVector<int> maxColWidths, maxRowHeights;
4619  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4620 
4621  QSize result(0, 0);
4622  for (int i=0; i<maxColWidths.size(); ++i)
4623  result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
4624  for (int i=0; i<maxRowHeights.size(); ++i)
4625  result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
4626  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
4627  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
4628  return result;
4629 }
4630 
4643 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
4644 {
4645  *minColWidths = QVector<int>(columnCount(), 0);
4646  *minRowHeights = QVector<int>(rowCount(), 0);
4647  for (int row=0; row<rowCount(); ++row)
4648  {
4649  for (int col=0; col<columnCount(); ++col)
4650  {
4651  if (mElements.at(row).at(col))
4652  {
4653  QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
4654  QSize min = mElements.at(row).at(col)->minimumSize();
4655  QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
4656  if (minColWidths->at(col) < final.width())
4657  (*minColWidths)[col] = final.width();
4658  if (minRowHeights->at(row) < final.height())
4659  (*minRowHeights)[row] = final.height();
4660  }
4661  }
4662  }
4663 }
4664 
4677 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
4678 {
4679  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
4680  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
4681  for (int row=0; row<rowCount(); ++row)
4682  {
4683  for (int col=0; col<columnCount(); ++col)
4684  {
4685  if (mElements.at(row).at(col))
4686  {
4687  QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
4688  QSize max = mElements.at(row).at(col)->maximumSize();
4689  QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
4690  if (maxColWidths->at(col) > final.width())
4691  (*maxColWidths)[col] = final.width();
4692  if (maxRowHeights->at(row) > final.height())
4693  (*maxRowHeights)[row] = final.height();
4694  }
4695  }
4696  }
4697 }
4698 
4699 
4703 
4721 /* start documentation of inline functions */
4722 
4729 /* end documentation of inline functions */
4730 
4735 {
4736 }
4737 
4739 {
4740  // clear all child layout elements. This is important because only the specific layouts know how
4741  // to handle removing elements (clear calls virtual removeAt method to do that).
4742  clear();
4743 }
4744 
4749 {
4750  if (elementAt(index))
4751  return mInsetPlacement.at(index);
4752  else
4753  {
4754  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4755  return ipFree;
4756  }
4757 }
4758 
4763 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
4764 {
4765  if (elementAt(index))
4766  return mInsetAlignment.at(index);
4767  else
4768  {
4769  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4770  return 0;
4771  }
4772 }
4773 
4778 QRectF QCPLayoutInset::insetRect(int index) const
4779 {
4780  if (elementAt(index))
4781  return mInsetRect.at(index);
4782  else
4783  {
4784  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4785  return QRectF();
4786  }
4787 }
4788 
4795 {
4796  if (elementAt(index))
4797  mInsetPlacement[index] = placement;
4798  else
4799  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4800 }
4801 
4810 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
4811 {
4812  if (elementAt(index))
4813  mInsetAlignment[index] = alignment;
4814  else
4815  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4816 }
4817 
4829 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
4830 {
4831  if (elementAt(index))
4832  mInsetRect[index] = rect;
4833  else
4834  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4835 }
4836 
4837 /* inherits documentation from base class */
4839 {
4840  for (int i=0; i<mElements.size(); ++i)
4841  {
4842  QRect insetRect;
4843  QSize finalMinSize, finalMaxSize;
4844  QSize minSizeHint = mElements.at(i)->minimumSizeHint();
4845  QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
4846  finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
4847  finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
4848  finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
4849  finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
4850  if (mInsetPlacement.at(i) == ipFree)
4851  {
4852  insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
4853  rect().y()+rect().height()*mInsetRect.at(i).y(),
4854  rect().width()*mInsetRect.at(i).width(),
4855  rect().height()*mInsetRect.at(i).height());
4856  if (insetRect.size().width() < finalMinSize.width())
4857  insetRect.setWidth(finalMinSize.width());
4858  if (insetRect.size().height() < finalMinSize.height())
4859  insetRect.setHeight(finalMinSize.height());
4860  if (insetRect.size().width() > finalMaxSize.width())
4861  insetRect.setWidth(finalMaxSize.width());
4862  if (insetRect.size().height() > finalMaxSize.height())
4863  insetRect.setHeight(finalMaxSize.height());
4864  } else if (mInsetPlacement.at(i) == ipBorderAligned)
4865  {
4866  insetRect.setSize(finalMinSize);
4867  Qt::Alignment al = mInsetAlignment.at(i);
4868  if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
4869  else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
4870  else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
4871  if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
4872  else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
4873  else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
4874  }
4875  mElements.at(i)->setOuterRect(insetRect);
4876  }
4877 }
4878 
4879 /* inherits documentation from base class */
4881 {
4882  return mElements.size();
4883 }
4884 
4885 /* inherits documentation from base class */
4887 {
4888  if (index >= 0 && index < mElements.size())
4889  return mElements.at(index);
4890  else
4891  return 0;
4892 }
4893 
4894 /* inherits documentation from base class */
4896 {
4897  if (QCPLayoutElement *el = elementAt(index))
4898  {
4899  releaseElement(el);
4900  mElements.removeAt(index);
4901  mInsetPlacement.removeAt(index);
4902  mInsetAlignment.removeAt(index);
4903  mInsetRect.removeAt(index);
4904  return el;
4905  } else
4906  {
4907  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
4908  return 0;
4909  }
4910 }
4911 
4912 /* inherits documentation from base class */
4914 {
4915  if (element)
4916  {
4917  for (int i=0; i<elementCount(); ++i)
4918  {
4919  if (elementAt(i) == element)
4920  {
4921  takeAt(i);
4922  return true;
4923  }
4924  }
4925  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
4926  } else
4927  qDebug() << Q_FUNC_INFO << "Can't take null element";
4928  return false;
4929 }
4930 
4940 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
4941 {
4942  Q_UNUSED(details)
4943  if (onlySelectable)
4944  return -1;
4945 
4946  for (int i=0; i<mElements.size(); ++i)
4947  {
4948  // inset layout shall only return positive selectTest, if actually an inset object is at pos
4949  // else it would block the entire underlying QCPAxisRect with its surface.
4950  if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
4951  return mParentPlot->selectionTolerance()*0.99;
4952  }
4953  return -1;
4954 }
4955 
4967 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
4968 {
4969  if (element)
4970  {
4971  if (element->layout()) // remove from old layout first
4972  element->layout()->take(element);
4973  mElements.append(element);
4974  mInsetPlacement.append(ipBorderAligned);
4975  mInsetAlignment.append(alignment);
4976  mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
4977  adoptElement(element);
4978  } else
4979  qDebug() << Q_FUNC_INFO << "Can't add null element";
4980 }
4981 
4994 {
4995  if (element)
4996  {
4997  if (element->layout()) // remove from old layout first
4998  element->layout()->take(element);
4999  mElements.append(element);
5000  mInsetPlacement.append(ipFree);
5001  mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
5002  mInsetRect.append(rect);
5003  adoptElement(element);
5004  } else
5005  qDebug() << Q_FUNC_INFO << "Can't add null element";
5006 }
5007 /* end of 'src/layout.cpp' */
5008 
5009 
5010 /* including file 'src/lineending.cpp', size 11536 */
5011 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
5012 
5016 
5041  mStyle(esNone),
5042  mWidth(8),
5043  mLength(10),
5044  mInverted(false)
5045 {
5046 }
5047 
5052  mStyle(style),
5053  mWidth(width),
5054  mLength(length),
5055  mInverted(inverted)
5056 {
5057 }
5058 
5063 {
5064  mStyle = style;
5065 }
5066 
5074 {
5075  mWidth = width;
5076 }
5077 
5085 {
5086  mLength = length;
5087 }
5088 
5098 {
5099  mInverted = inverted;
5100 }
5101 
5112 {
5113  switch (mStyle)
5114  {
5115  case esNone:
5116  return 0;
5117 
5118  case esFlatArrow:
5119  case esSpikeArrow:
5120  case esLineArrow:
5121  case esSkewedBar:
5122  return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
5123 
5124  case esDisc:
5125  case esSquare:
5126  case esDiamond:
5127  case esBar:
5128  case esHalfBar:
5129  return mWidth*1.42; // items that only have a width -> width*sqrt(2)
5130 
5131  }
5132  return 0;
5133 }
5134 
5147 {
5148  switch (mStyle)
5149  {
5150  case esNone:
5151  case esLineArrow:
5152  case esSkewedBar:
5153  case esBar:
5154  case esHalfBar:
5155  return 0;
5156 
5157  case esFlatArrow:
5158  return mLength;
5159 
5160  case esDisc:
5161  case esSquare:
5162  case esDiamond:
5163  return mWidth*0.5;
5164 
5165  case esSpikeArrow:
5166  return mLength*0.8;
5167  }
5168  return 0;
5169 }
5170 
5176 void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const
5177 {
5178  if (mStyle == esNone)
5179  return;
5180 
5181  QCPVector2D lengthVec = dir.normalized() * mLength*(mInverted ? -1 : 1);
5182  if (lengthVec.isNull())
5183  lengthVec = QCPVector2D(1, 0);
5184  QCPVector2D widthVec = dir.normalized().perpendicular() * mWidth*0.5*(mInverted ? -1 : 1);
5185 
5186  QPen penBackup = painter->pen();
5187  QBrush brushBackup = painter->brush();
5188  QPen miterPen = penBackup;
5189  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
5190  QBrush brush(painter->pen().color(), Qt::SolidPattern);
5191  switch (mStyle)
5192  {
5193  case esNone: break;
5194  case esFlatArrow:
5195  {
5196  QPointF points[3] = {pos.toPointF(),
5197  (pos-lengthVec+widthVec).toPointF(),
5198  (pos-lengthVec-widthVec).toPointF()
5199  };
5200  painter->setPen(miterPen);
5201  painter->setBrush(brush);
5202  painter->drawConvexPolygon(points, 3);
5203  painter->setBrush(brushBackup);
5204  painter->setPen(penBackup);
5205  break;
5206  }
5207  case esSpikeArrow:
5208  {
5209  QPointF points[4] = {pos.toPointF(),
5210  (pos-lengthVec+widthVec).toPointF(),
5211  (pos-lengthVec*0.8).toPointF(),
5212  (pos-lengthVec-widthVec).toPointF()
5213  };
5214  painter->setPen(miterPen);
5215  painter->setBrush(brush);
5216  painter->drawConvexPolygon(points, 4);
5217  painter->setBrush(brushBackup);
5218  painter->setPen(penBackup);
5219  break;
5220  }
5221  case esLineArrow:
5222  {
5223  QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
5224  pos.toPointF(),
5225  (pos-lengthVec-widthVec).toPointF()
5226  };
5227  painter->setPen(miterPen);
5228  painter->drawPolyline(points, 3);
5229  painter->setPen(penBackup);
5230  break;
5231  }
5232  case esDisc:
5233  {
5234  painter->setBrush(brush);
5235  painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
5236  painter->setBrush(brushBackup);
5237  break;
5238  }
5239  case esSquare:
5240  {
5241  QCPVector2D widthVecPerp = widthVec.perpendicular();
5242  QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
5243  (pos-widthVecPerp-widthVec).toPointF(),
5244  (pos+widthVecPerp-widthVec).toPointF(),
5245  (pos+widthVecPerp+widthVec).toPointF()
5246  };
5247  painter->setPen(miterPen);
5248  painter->setBrush(brush);
5249  painter->drawConvexPolygon(points, 4);
5250  painter->setBrush(brushBackup);
5251  painter->setPen(penBackup);
5252  break;
5253  }
5254  case esDiamond:
5255  {
5256  QCPVector2D widthVecPerp = widthVec.perpendicular();
5257  QPointF points[4] = {(pos-widthVecPerp).toPointF(),
5258  (pos-widthVec).toPointF(),
5259  (pos+widthVecPerp).toPointF(),
5260  (pos+widthVec).toPointF()
5261  };
5262  painter->setPen(miterPen);
5263  painter->setBrush(brush);
5264  painter->drawConvexPolygon(points, 4);
5265  painter->setBrush(brushBackup);
5266  painter->setPen(penBackup);
5267  break;
5268  }
5269  case esBar:
5270  {
5271  painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
5272  break;
5273  }
5274  case esHalfBar:
5275  {
5276  painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
5277  break;
5278  }
5279  case esSkewedBar:
5280  {
5281  if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
5282  {
5283  // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
5284  painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)).toPointF(),
5285  (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)).toPointF());
5286  } else
5287  {
5288  // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
5289  painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
5290  (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
5291  }
5292  break;
5293  }
5294  }
5295 }
5296 
5302 void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double angle) const
5303 {
5304  draw(painter, pos, QCPVector2D(qCos(angle), qSin(angle)));
5305 }
5306 /* end of 'src/lineending.cpp' */
5307 
5308 
5309 /* including file 'src/axis/axisticker.cpp', size 18664 */
5310 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
5311 
5315 
5373  mTickStepStrategy(tssReadability),
5374  mTickCount(5),
5375  mTickOrigin(0)
5376 {
5377 }
5378 
5380 {
5381 
5382 }
5383 
5389 {
5390  mTickStepStrategy = strategy;
5391 }
5392 
5402 {
5403  if (count > 0)
5404  mTickCount = count;
5405  else
5406  qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count;
5407 }
5408 
5418 {
5419  mTickOrigin = origin;
5420 }
5421 
5435 void QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector<double> &ticks, QVector<double> *subTicks, QVector<QString> *tickLabels)
5436 {
5437  // generate (major) ticks:
5438  double tickStep = getTickStep(range);
5439  ticks = createTickVector(tickStep, range);
5440  trimTicks(range, ticks, true); // trim ticks to visible range plus one outer tick on each side (incase a subclass createTickVector creates more)
5441 
5442  // generate sub ticks between major ticks:
5443  if (subTicks)
5444  {
5445  if (ticks.size() > 0)
5446  {
5447  *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks);
5448  trimTicks(range, *subTicks, false);
5449  } else
5450  *subTicks = QVector<double>();
5451  }
5452 
5453  // finally trim also outliers (no further clipping happens in axis drawing):
5454  trimTicks(range, ticks, false);
5455  // generate labels for visible ticks if requested:
5456  if (tickLabels)
5457  *tickLabels = createLabelVector(ticks, locale, formatChar, precision);
5458 }
5459 
5471 {
5472  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
5473  return cleanMantissa(exactStep);
5474 }
5475 
5484 {
5485  int result = 1; // default to 1, if no proper value can be found
5486 
5487  // separate integer and fractional part of mantissa:
5488  double epsilon = 0.01;
5489  double intPartf;
5490  int intPart;
5491  double fracPart = modf(getMantissa(tickStep), &intPartf);
5492  intPart = intPartf;
5493 
5494  // handle cases with (almost) integer mantissa:
5495  if (fracPart < epsilon || 1.0-fracPart < epsilon)
5496  {
5497  if (1.0-fracPart < epsilon)
5498  ++intPart;
5499  switch (intPart)
5500  {
5501  case 1: result = 4; break; // 1.0 -> 0.2 substep
5502  case 2: result = 3; break; // 2.0 -> 0.5 substep
5503  case 3: result = 2; break; // 3.0 -> 1.0 substep
5504  case 4: result = 3; break; // 4.0 -> 1.0 substep
5505  case 5: result = 4; break; // 5.0 -> 1.0 substep
5506  case 6: result = 2; break; // 6.0 -> 2.0 substep
5507  case 7: result = 6; break; // 7.0 -> 1.0 substep
5508  case 8: result = 3; break; // 8.0 -> 2.0 substep
5509  case 9: result = 2; break; // 9.0 -> 3.0 substep
5510  }
5511  } else
5512  {
5513  // handle cases with significantly fractional mantissa:
5514  if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5515  {
5516  switch (intPart)
5517  {
5518  case 1: result = 2; break; // 1.5 -> 0.5 substep
5519  case 2: result = 4; break; // 2.5 -> 0.5 substep
5520  case 3: result = 4; break; // 3.5 -> 0.7 substep
5521  case 4: result = 2; break; // 4.5 -> 1.5 substep
5522  case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on)
5523  case 6: result = 4; break; // 6.5 -> 1.3 substep
5524  case 7: result = 2; break; // 7.5 -> 2.5 substep
5525  case 8: result = 4; break; // 8.5 -> 1.7 substep
5526  case 9: result = 4; break; // 9.5 -> 1.9 substep
5527  }
5528  }
5529  // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5530  }
5531 
5532  return result;
5533 }
5534 
5546 QString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
5547 {
5548  return locale.toString(tick, formatChar.toLatin1(), precision);
5549 }
5550 
5560 QVector<double> QCPAxisTicker::createSubTickVector(int subTickCount, const QVector<double> &ticks)
5561 {
5562  QVector<double> result;
5563  if (subTickCount <= 0 || ticks.size() < 2)
5564  return result;
5565 
5566  result.reserve((ticks.size()-1)*subTickCount);
5567  for (int i=1; i<ticks.size(); ++i)
5568  {
5569  double subTickStep = (ticks.at(i)-ticks.at(i-1))/(double)(subTickCount+1);
5570  for (int k=1; k<=subTickCount; ++k)
5571  result.append(ticks.at(i-1) + k*subTickStep);
5572  }
5573  return result;
5574 }
5575 
5591 QVector<double> QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range)
5592 {
5593  QVector<double> result;
5594  // Generate tick positions according to tickStep:
5595  qint64 firstStep = floor((range.lower-mTickOrigin)/tickStep); // do not use qFloor here, or we'll lose 64 bit precision
5596  qint64 lastStep = ceil((range.upper-mTickOrigin)/tickStep); // do not use qCeil here, or we'll lose 64 bit precision
5597  int tickcount = lastStep-firstStep+1;
5598  if (tickcount < 0) tickcount = 0;
5599  result.resize(tickcount);
5600  for (int i=0; i<tickcount; ++i)
5601  result[i] = mTickOrigin + (firstStep+i)*tickStep;
5602  return result;
5603 }
5604 
5614 QVector<QString> QCPAxisTicker::createLabelVector(const QVector<double> &ticks, const QLocale &locale, QChar formatChar, int precision)
5615 {
5616  QVector<QString> result;
5617  result.reserve(ticks.size());
5618  for (int i=0; i<ticks.size(); ++i)
5619  result.append(getTickLabel(ticks.at(i), locale, formatChar, precision));
5620  return result;
5621 }
5622 
5630 void QCPAxisTicker::trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const
5631 {
5632  bool lowFound = false;
5633  bool highFound = false;
5634  int lowIndex = 0;
5635  int highIndex = -1;
5636 
5637  for (int i=0; i < ticks.size(); ++i)
5638  {
5639  if (ticks.at(i) >= range.lower)
5640  {
5641  lowFound = true;
5642  lowIndex = i;
5643  break;
5644  }
5645  }
5646  for (int i=ticks.size()-1; i >= 0; --i)
5647  {
5648  if (ticks.at(i) <= range.upper)
5649  {
5650  highFound = true;
5651  highIndex = i;
5652  break;
5653  }
5654  }
5655 
5656  if (highFound && lowFound)
5657  {
5658  int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0));
5659  int trimBack = qMax(0, ticks.size()-(keepOneOutlier ? 2 : 1)-highIndex);
5660  if (trimFront > 0 || trimBack > 0)
5661  ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack);
5662  } else // all ticks are either all below or all above the range
5663  ticks.clear();
5664 }
5665 
5672 double QCPAxisTicker::pickClosest(double target, const QVector<double> &candidates) const
5673 {
5674  if (candidates.size() == 1)
5675  return candidates.first();
5676  QVector<double>::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target);
5677  if (it == candidates.constEnd())
5678  return *(it-1);
5679  else if (it == candidates.constBegin())
5680  return *it;
5681  else
5682  return target-*(it-1) < *it-target ? *(it-1) : *it;
5683 }
5684 
5692 double QCPAxisTicker::getMantissa(double input, double *magnitude) const
5693 {
5694  const double mag = qPow(10.0, qFloor(qLn(input)/qLn(10.0)));
5695  if (magnitude) *magnitude = mag;
5696  return input/mag;
5697 }
5698 
5705 double QCPAxisTicker::cleanMantissa(double input) const
5706 {
5707  double magnitude;
5708  const double mantissa = getMantissa(input, &magnitude);
5709  switch (mTickStepStrategy)
5710  {
5711  case tssReadability:
5712  {
5713  return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude;
5714  }
5715  case tssMeetTickCount:
5716  {
5717  // 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
5718  if (mantissa <= 5.0)
5719  return (int)(mantissa*2)/2.0*magnitude; // round digit after decimal point to 0.5
5720  else
5721  return (int)(mantissa/2.0)*2.0*magnitude; // round to first digit in multiples of 2
5722  }
5723  }
5724  return input;
5725 }
5726 /* end of 'src/axis/axisticker.cpp' */
5727 
5728 
5729 /* including file 'src/axis/axistickerdatetime.cpp', size 14443 */
5730 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
5731 
5735 
5776  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
5777  mDateTimeSpec(Qt::LocalTime),
5778  mDateStrategy(dsNone)
5779 {
5780  setTickCount(4);
5781 }
5782 
5791 void QCPAxisTickerDateTime::setDateTimeFormat(const QString &format)
5792 {
5793  mDateTimeFormat = format;
5794 }
5795 
5807 {
5808  mDateTimeSpec = spec;
5809 }
5810 
5821 {
5823 }
5824 
5833 {
5834  setTickOrigin(dateTimeToKey(origin));
5835 }
5836 
5851 {
5852  double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
5853 
5855  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds
5856  {
5857  result = cleanMantissa(result);
5858  } else if (result < 86400*30.4375*12) // below a year
5859  {
5860  result = pickClosest(result, QVector<double>()
5861  << 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
5862  << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24 // hour to day range
5863  << 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)
5864  if (result > 86400*30.4375-1) // month tick intervals or larger
5866  else if (result > 3600*24-1) // day tick intervals or larger
5868  } else // more than a year, go back to normal clean mantissa algorithm but in units of years
5869  {
5870  const double secondsPerYear = 86400*30.4375*12; // average including leap years
5871  result = cleanMantissa(result/secondsPerYear)*secondsPerYear;
5873  }
5874  return result;
5875 }
5876 
5885 {
5886  int result = QCPAxisTicker::getSubTickCount(tickStep);
5887  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day/week/month range (as specified in getTickStep)
5888  {
5889  case 5*60: result = 4; break;
5890  case 10*60: result = 1; break;
5891  case 15*60: result = 2; break;
5892  case 30*60: result = 1; break;
5893  case 60*60: result = 3; break;
5894  case 3600*2: result = 3; break;
5895  case 3600*3: result = 2; break;
5896  case 3600*6: result = 1; break;
5897  case 3600*12: result = 3; break;
5898  case 3600*24: result = 3; break;
5899  case 86400*2: result = 1; break;
5900  case 86400*5: result = 4; break;
5901  case 86400*7: result = 6; break;
5902  case 86400*14: result = 1; break;
5903  case (int)(86400*30.4375+0.5): result = 3; break;
5904  case (int)(86400*30.4375*2+0.5): result = 1; break;
5905  case (int)(86400*30.4375*3+0.5): result = 2; break;
5906  case (int)(86400*30.4375*6+0.5): result = 5; break;
5907  case (int)(86400*30.4375*12+0.5): result = 3; break;
5908  }
5909  return result;
5910 }
5911 
5919 QString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
5920 {
5921  Q_UNUSED(precision)
5922  Q_UNUSED(formatChar)
5923  return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5924 }
5925 
5933 QVector<double> QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range)
5934 {
5935  QVector<double> result = QCPAxisTicker::createTickVector(tickStep, range);
5936  if (!result.isEmpty())
5937  {
5939  {
5940  QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // the time of this datetime will be set for all other ticks, if possible
5941  QDateTime tickDateTime;
5942  for (int i=0; i<result.size(); ++i)
5943  {
5944  tickDateTime = keyToDateTime(result.at(i));
5945  tickDateTime.setTime(uniformDateTime.time());
5946  result[i] = dateTimeToKey(tickDateTime);
5947  }
5948  } else if (mDateStrategy == dsUniformDayInMonth)
5949  {
5950  QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // this day (in month) and time will be set for all other ticks, if possible
5951  QDateTime tickDateTime;
5952  for (int i=0; i<result.size(); ++i)
5953  {
5954  tickDateTime = keyToDateTime(result.at(i));
5955  tickDateTime.setTime(uniformDateTime.time());
5956  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)
5957  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
5958  tickDateTime = tickDateTime.addMonths(1);
5959  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
5960  tickDateTime = tickDateTime.addMonths(-1);
5961  tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay));
5962  result[i] = dateTimeToKey(tickDateTime);
5963  }
5964  }
5965  }
5966  return result;
5967 }
5968 
5979 {
5980 # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
5981  return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000);
5982 # else
5983  return QDateTime::fromMSecsSinceEpoch(key*1000.0);
5984 # endif
5985 }
5986 
5998 double QCPAxisTickerDateTime::dateTimeToKey(const QDateTime dateTime)
5999 {
6000 # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6001  return dateTime.toTime_t()+dateTime.time().msec()/1000.0;
6002 # else
6003  return dateTime.toMSecsSinceEpoch()/1000.0;
6004 # endif
6005 }
6006 
6016 {
6017 # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6018  return QDateTime(date).toTime_t();
6019 # else
6020  return QDateTime(date).toMSecsSinceEpoch()/1000.0;
6021 # endif
6022 }
6023 /* end of 'src/axis/axistickerdatetime.cpp' */
6024 
6025 
6026 /* including file 'src/axis/axistickertime.cpp', size 11747 */
6027 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
6028 
6032 
6073  mTimeFormat(QLatin1String("%h:%m:%s")),
6074  mSmallestUnit(tuSeconds),
6075  mBiggestUnit(tuHours)
6076 {
6077  setTickCount(4);
6079  mFieldWidth[tuSeconds] = 2;
6080  mFieldWidth[tuMinutes] = 2;
6081  mFieldWidth[tuHours] = 2;
6082  mFieldWidth[tuDays] = 1;
6083 
6084  mFormatPattern[tuMilliseconds] = QLatin1String("%z");
6085  mFormatPattern[tuSeconds] = QLatin1String("%s");
6086  mFormatPattern[tuMinutes] = QLatin1String("%m");
6087  mFormatPattern[tuHours] = QLatin1String("%h");
6088  mFormatPattern[tuDays] = QLatin1String("%d");
6089 }
6090 
6109 void QCPAxisTickerTime::setTimeFormat(const QString &format)
6110 {
6111  mTimeFormat = format;
6112 
6113  // determine smallest and biggest unit in format, to optimize unit replacement and allow biggest
6114  // unit to consume remaining time of a tick value and grow beyond its modulo (e.g. min > 59)
6117  bool hasSmallest = false;
6118  for (int i = tuMilliseconds; i <= tuDays; ++i)
6119  {
6120  TimeUnit unit = static_cast<TimeUnit>(i);
6121  if (mTimeFormat.contains(mFormatPattern.value(unit)))
6122  {
6123  if (!hasSmallest)
6124  {
6125  mSmallestUnit = unit;
6126  hasSmallest = true;
6127  }
6128  mBiggestUnit = unit;
6129  }
6130  }
6131 }
6132 
6141 {
6142  mFieldWidth[unit] = qMax(width, 1);
6143 }
6144 
6155 {
6156  double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6157 
6158  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds
6159  {
6161  result = qMax(cleanMantissa(result), 0.001); // smallest tick step is 1 millisecond
6162  else // have no milliseconds available in format, so stick with 1 second tickstep
6163  result = 1.0;
6164  } else if (result < 3600*24) // below a day
6165  {
6166  // the filling of availableSteps seems a bit contorted but it fills in a sorted fashion and thus saves a post-fill sorting run
6167  QVector<double> availableSteps;
6168  // seconds range:
6169  if (mSmallestUnit <= tuSeconds)
6170  availableSteps << 1;
6172  availableSteps << 2.5; // only allow half second steps if milliseconds are there to display it
6173  else if (mSmallestUnit == tuSeconds)
6174  availableSteps << 2;
6175  if (mSmallestUnit <= tuSeconds)
6176  availableSteps << 5 << 10 << 15 << 30;
6177  // minutes range:
6178  if (mSmallestUnit <= tuMinutes)
6179  availableSteps << 1*60;
6180  if (mSmallestUnit <= tuSeconds)
6181  availableSteps << 2.5*60; // only allow half minute steps if seconds are there to display it
6182  else if (mSmallestUnit == tuMinutes)
6183  availableSteps << 2*60;
6184  if (mSmallestUnit <= tuMinutes)
6185  availableSteps << 5*60 << 10*60 << 15*60 << 30*60;
6186  // hours range:
6187  if (mSmallestUnit <= tuHours)
6188  availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600;
6189  // pick available step that is most appropriate to approximate ideal step:
6190  result = pickClosest(result, availableSteps);
6191  } else // more than a day, go back to normal clean mantissa algorithm but in units of days
6192  {
6193  const double secondsPerDay = 3600*24;
6194  result = cleanMantissa(result/secondsPerDay)*secondsPerDay;
6195  }
6196  return result;
6197 }
6198 
6206 {
6207  int result = QCPAxisTicker::getSubTickCount(tickStep);
6208  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day range (as specified in getTickStep)
6209  {
6210  case 5*60: result = 4; break;
6211  case 10*60: result = 1; break;
6212  case 15*60: result = 2; break;
6213  case 30*60: result = 1; break;
6214  case 60*60: result = 3; break;
6215  case 3600*2: result = 3; break;
6216  case 3600*3: result = 2; break;
6217  case 3600*6: result = 1; break;
6218  case 3600*12: result = 3; break;
6219  case 3600*24: result = 3; break;
6220  }
6221  return result;
6222 }
6223 
6231 QString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6232 {
6233  Q_UNUSED(precision)
6234  Q_UNUSED(formatChar)
6235  Q_UNUSED(locale)
6236  bool negative = tick < 0;
6237  if (negative) tick *= -1;
6238  double values[tuDays+1]; // contains the msec/sec/min/... value with its respective modulo (e.g. minute 0..59)
6239  double restValues[tuDays+1]; // contains the msec/sec/min/... value as if it's the largest available unit and thus consumes the remaining time
6240 
6241  restValues[tuMilliseconds] = tick*1000;
6242  values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000;
6243  values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60;
6244  values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60;
6245  values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24;
6246  // no need to set values[tuDays] because days are always a rest value (there is no higher unit so it consumes all remaining time)
6247 
6248  QString result = mTimeFormat;
6249  for (int i = mSmallestUnit; i <= mBiggestUnit; ++i)
6250  {
6251  TimeUnit iUnit = static_cast<TimeUnit>(i);
6252  replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit]));
6253  }
6254  if (negative)
6255  result.prepend(QLatin1Char('-'));
6256  return result;
6257 }
6258 
6264 void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const
6265 {
6266  QString valueStr = QString::number(value);
6267  while (valueStr.size() < mFieldWidth.value(unit))
6268  valueStr.prepend(QLatin1Char('0'));
6269 
6270  text.replace(mFormatPattern.value(unit), valueStr);
6271 }
6272 /* end of 'src/axis/axistickertime.cpp' */
6273 
6274 
6275 /* including file 'src/axis/axistickerfixed.cpp', size 5583 */
6276 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
6277 
6281 
6306  mTickStep(1.0),
6307  mScaleStrategy(ssNone)
6308 {
6309 }
6310 
6321 {
6322  if (step > 0)
6323  mTickStep = step;
6324  else
6325  qDebug() << Q_FUNC_INFO << "tick step must be greater than zero:" << step;
6326 }
6327 
6336 {
6337  mScaleStrategy = strategy;
6338 }
6339 
6351 {
6352  switch (mScaleStrategy)
6353  {
6354  case ssNone:
6355  {
6356  return mTickStep;
6357  }
6358  case ssMultiples:
6359  {
6360  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6361  if (exactStep < mTickStep)
6362  return mTickStep;
6363  else
6364  return (qint64)(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep;
6365  }
6366  case ssPowers:
6367  {
6368  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6369  return qPow(mTickStep, (int)(qLn(exactStep)/qLn(mTickStep)+0.5));
6370  }
6371  }
6372  return mTickStep;
6373 }
6374 /* end of 'src/axis/axistickerfixed.cpp' */
6375 
6376 
6377 /* including file 'src/axis/axistickertext.cpp', size 8653 */
6378 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
6379 
6383 
6403 /* start of documentation of inline functions */
6404 
6414 /* end of documentation of inline functions */
6415 
6421  mSubTickCount(0)
6422 {
6423 }
6424 
6435 void QCPAxisTickerText::setTicks(const QMap<double, QString> &ticks)
6436 {
6437  mTicks = ticks;
6438 }
6439 
6448 void QCPAxisTickerText::setTicks(const QVector<double> &positions, const QVector<QString> labels)
6449 {
6450  clear();
6451  addTicks(positions, labels);
6452 }
6453 
6460 {
6461  if (subTicks >= 0)
6462  mSubTickCount = subTicks;
6463  else
6464  qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
6465 }
6466 
6476 {
6477  mTicks.clear();
6478 }
6479 
6486 void QCPAxisTickerText::addTick(double position, QString label)
6487 {
6488  mTicks.insert(position, label);
6489 }
6490 
6501 void QCPAxisTickerText::addTicks(const QMap<double, QString> &ticks)
6502 {
6503  mTicks.unite(ticks);
6504 }
6505 
6517 void QCPAxisTickerText::addTicks(const QVector<double> &positions, const QVector<QString> &labels)
6518 {
6519  if (positions.size() != labels.size())
6520  qDebug() << Q_FUNC_INFO << "passed unequal length vectors for positions and labels:" << positions.size() << labels.size();
6521  int n = qMin(positions.size(), labels.size());
6522  for (int i=0; i<n; ++i)
6523  mTicks.insert(positions.at(i), labels.at(i));
6524 }
6525 
6532 {
6533  // text axis ticker has manual tick positions, so doesn't need this method
6534  Q_UNUSED(range)
6535  return 1.0;
6536 }
6537 
6544 {
6545  Q_UNUSED(tickStep)
6546  return mSubTickCount;
6547 }
6548 
6555 QString QCPAxisTickerText::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6556 {
6557  Q_UNUSED(locale)
6558  Q_UNUSED(formatChar)
6559  Q_UNUSED(precision)
6560  return mTicks.value(tick);
6561 }
6562 
6570 QVector<double> QCPAxisTickerText::createTickVector(double tickStep, const QCPRange &range)
6571 {
6572  Q_UNUSED(tickStep)
6573  QVector<double> result;
6574  if (mTicks.isEmpty())
6575  return result;
6576 
6577  QMap<double, QString>::const_iterator start = mTicks.lowerBound(range.lower);
6578  QMap<double, QString>::const_iterator end = mTicks.upperBound(range.upper);
6579  // this method should try to give one tick outside of range so proper subticks can be generated:
6580  if (start != mTicks.constBegin()) --start;
6581  if (end != mTicks.constEnd()) ++end;
6582  for (QMap<double, QString>::const_iterator it = start; it != end; ++it)
6583  result.append(it.key());
6584 
6585  return result;
6586 }
6587 /* end of 'src/axis/axistickertext.cpp' */
6588 
6589 
6590 /* including file 'src/axis/axistickerpi.cpp', size 11170 */
6591 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
6592 
6596 
6617  mPiSymbol(QLatin1String(" ")+QChar(0x03C0)),
6618  mPiValue(M_PI),
6619  mPeriodicity(0),
6620  mFractionStyle(fsUnicodeFractions),
6621  mPiTickStep(0)
6622 {
6623  setTickCount(4);
6624 }
6625 
6633 void QCPAxisTickerPi::setPiSymbol(QString symbol)
6634 {
6635  mPiSymbol = symbol;
6636 }
6637 
6645 {
6646  mPiValue = pi;
6647 }
6648 
6657 void QCPAxisTickerPi::setPeriodicity(int multiplesOfPi)
6658 {
6659  mPeriodicity = qAbs(multiplesOfPi);
6660 }
6661 
6667 {
6668  mFractionStyle = style;
6669 }
6670 
6680 {
6681  mPiTickStep = range.size()/mPiValue/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6683  return mPiTickStep*mPiValue;
6684 }
6685 
6695 {
6696  return QCPAxisTicker::getSubTickCount(tickStep/mPiValue);
6697 }
6698 
6707 QString QCPAxisTickerPi::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6708 {
6709  double tickInPis = tick/mPiValue;
6710  if (mPeriodicity > 0)
6711  tickInPis = fmod(tickInPis, mPeriodicity);
6712 
6713  if (mFractionStyle != fsFloatingPoint && mPiTickStep > 0.09 && mPiTickStep < 50)
6714  {
6715  // simply construct fraction from decimal like 1.234 -> 1234/1000 and then simplify fraction, smaller digits are irrelevant due to mPiTickStep conditional above
6716  int denominator = 1000;
6717  int numerator = qRound(tickInPis*denominator);
6718  simplifyFraction(numerator, denominator);
6719  if (qAbs(numerator) == 1 && denominator == 1)
6720  return (numerator < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed();
6721  else if (numerator == 0)
6722  return QLatin1String("0");
6723  else
6724  return fractionToString(numerator, denominator) + mPiSymbol;
6725  } else
6726  {
6727  if (qFuzzyIsNull(tickInPis))
6728  return QLatin1String("0");
6729  else if (qFuzzyCompare(qAbs(tickInPis), 1.0))
6730  return (tickInPis < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed();
6731  else
6732  return QCPAxisTicker::getTickLabel(tickInPis, locale, formatChar, precision) + mPiSymbol;
6733  }
6734 }
6735 
6742 void QCPAxisTickerPi::simplifyFraction(int &numerator, int &denominator) const
6743 {
6744  if (numerator == 0 || denominator == 0)
6745  return;
6746 
6747  int num = numerator;
6748  int denom = denominator;
6749  while (denom != 0) // euclidean gcd algorithm
6750  {
6751  int oldDenom = denom;
6752  denom = num % denom;
6753  num = oldDenom;
6754  }
6755  // num is now gcd of numerator and denominator
6756  numerator /= num;
6757  denominator /= num;
6758 }
6759 
6769 QString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const
6770 {
6771  if (denominator == 0)
6772  {
6773  qDebug() << Q_FUNC_INFO << "called with zero denominator";
6774  return QString();
6775  }
6776  if (mFractionStyle == fsFloatingPoint) // should never be the case when calling this function
6777  {
6778  qDebug() << Q_FUNC_INFO << "shouldn't be called with fraction style fsDecimal";
6779  return QString::number(numerator/(double)denominator); // failsafe
6780  }
6781  int sign = numerator*denominator < 0 ? -1 : 1;
6782  numerator = qAbs(numerator);
6783  denominator = qAbs(denominator);
6784 
6785  if (denominator == 1)
6786  {
6787  return QString::number(sign*numerator);
6788  } else
6789  {
6790  int integerPart = numerator/denominator;
6791  int remainder = numerator%denominator;
6792  if (remainder == 0)
6793  {
6794  return QString::number(sign*integerPart);
6795  } else
6796  {
6798  {
6799  return QString(QLatin1String("%1%2%3/%4"))
6800  .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
6801  .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(" ") : QLatin1String(""))
6802  .arg(remainder)
6803  .arg(denominator);
6804  } else if (mFractionStyle == fsUnicodeFractions)
6805  {
6806  return QString(QLatin1String("%1%2%3"))
6807  .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
6808  .arg(integerPart > 0 ? QString::number(integerPart) : QLatin1String(""))
6809  .arg(unicodeFraction(remainder, denominator));
6810  }
6811  }
6812  }
6813  return QString();
6814 }
6815 
6825 QString QCPAxisTickerPi::unicodeFraction(int numerator, int denominator) const
6826 {
6827  return unicodeSuperscript(numerator)+QChar(0x2044)+unicodeSubscript(denominator);
6828 }
6829 
6835 QString QCPAxisTickerPi::unicodeSuperscript(int number) const
6836 {
6837  if (number == 0)
6838  return QString(QChar(0x2070));
6839 
6840  QString result;
6841  while (number > 0)
6842  {
6843  const int digit = number%10;
6844  switch (digit)
6845  {
6846  case 1: { result.prepend(QChar(0x00B9)); break; }
6847  case 2: { result.prepend(QChar(0x00B2)); break; }
6848  case 3: { result.prepend(QChar(0x00B3)); break; }
6849  default: { result.prepend(QChar(0x2070+digit)); break; }
6850  }
6851  number /= 10;
6852  }
6853  return result;
6854 }
6855 
6861 QString QCPAxisTickerPi::unicodeSubscript(int number) const
6862 {
6863  if (number == 0)
6864  return QString(QChar(0x2080));
6865 
6866  QString result;
6867  while (number > 0)
6868  {
6869  result.prepend(QChar(0x2080+number%10));
6870  number /= 10;
6871  }
6872  return result;
6873 }
6874 /* end of 'src/axis/axistickerpi.cpp' */
6875 
6876 
6877 /* including file 'src/axis/axistickerlog.cpp', size 7106 */
6878 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
6879 
6883 
6908  mLogBase(10.0),
6909  mSubTickCount(8), // generates 10 intervals
6910  mLogBaseLnInv(1.0/qLn(mLogBase))
6911 {
6912 }
6913 
6919 {
6920  if (base > 0)
6921  {
6922  mLogBase = base;
6923  mLogBaseLnInv = 1.0/qLn(mLogBase);
6924  } else
6925  qDebug() << Q_FUNC_INFO << "log base has to be greater than zero:" << base;
6926 }
6927 
6939 {
6940  if (subTicks >= 0)
6941  mSubTickCount = subTicks;
6942  else
6943  qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
6944 }
6945 
6954 {
6955  // Logarithmic axis ticker has unequal tick spacing, so doesn't need this method
6956  Q_UNUSED(range)
6957  return 1.0;
6958 }
6959 
6968 {
6969  Q_UNUSED(tickStep)
6970  return mSubTickCount;
6971 }
6972 
6982 QVector<double> QCPAxisTickerLog::createTickVector(double tickStep, const QCPRange &range)
6983 {
6984  Q_UNUSED(tickStep)
6985  QVector<double> result;
6986  if (range.lower > 0 && range.upper > 0) // positive range
6987  {
6988  double exactPowerStep = qLn(range.upper/range.lower)*mLogBaseLnInv/(double)(mTickCount+1e-10);
6989  double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
6990  double currentTick = qPow(newLogBase, qFloor(qLn(range.lower)/qLn(newLogBase)));
6991  result.append(currentTick);
6992  while (currentTick < range.upper && currentTick > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
6993  {
6994  currentTick *= newLogBase;
6995  result.append(currentTick);
6996  }
6997  } else if (range.lower < 0 && range.upper < 0) // negative range
6998  {
6999  double exactPowerStep = qLn(range.lower/range.upper)*mLogBaseLnInv/(double)(mTickCount+1e-10);
7000  double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
7001  double currentTick = -qPow(newLogBase, qCeil(qLn(-range.lower)/qLn(newLogBase)));
7002  result.append(currentTick);
7003  while (currentTick < range.upper && currentTick < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
7004  {
7005  currentTick /= newLogBase;
7006  result.append(currentTick);
7007  }
7008  } else // invalid range for logarithmic scale, because lower and upper have different sign
7009  {
7010  qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << range.lower << ".." << range.upper;
7011  }
7012 
7013  return result;
7014 }
7015 /* end of 'src/axis/axistickerlog.cpp' */
7016 
7017 
7018 /* including file 'src/axis/axis.cpp', size 94458 */
7019 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
7020 
7021 
7025 
7045  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
7046  mParentAxis(parentAxis)
7047 {
7048  // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
7049  setParent(parentAxis);
7050  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
7051  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
7052  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
7053  setSubGridVisible(false);
7054  setAntialiased(false);
7055  setAntialiasedSubGrid(false);
7056  setAntialiasedZeroLine(false);
7057 }
7058 
7065 {
7067 }
7068 
7073 {
7074  mAntialiasedSubGrid = enabled;
7075 }
7076 
7081 {
7082  mAntialiasedZeroLine = enabled;
7083 }
7084 
7088 void QCPGrid::setPen(const QPen &pen)
7089 {
7090  mPen = pen;
7091 }
7092 
7096 void QCPGrid::setSubGridPen(const QPen &pen)
7097 {
7098  mSubGridPen = pen;
7099 }
7100 
7107 void QCPGrid::setZeroLinePen(const QPen &pen)
7108 {
7109  mZeroLinePen = pen;
7110 }
7111 
7126 {
7128 }
7129 
7136 {
7137  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
7138 
7140  drawSubGridLines(painter);
7141  drawGridLines(painter);
7142 }
7143 
7151 {
7152  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
7153 
7154  const int tickCount = mParentAxis->mTickVector.size();
7155  double t; // helper variable, result of coordinate-to-pixel transforms
7156  if (mParentAxis->orientation() == Qt::Horizontal)
7157  {
7158  // draw zeroline:
7159  int zeroLineIndex = -1;
7160  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
7161  {
7163  painter->setPen(mZeroLinePen);
7164  double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
7165  for (int i=0; i<tickCount; ++i)
7166  {
7167  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
7168  {
7169  zeroLineIndex = i;
7170  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
7171  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
7172  break;
7173  }
7174  }
7175  }
7176  // draw grid lines:
7178  painter->setPen(mPen);
7179  for (int i=0; i<tickCount; ++i)
7180  {
7181  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
7182  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
7183  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
7184  }
7185  } else
7186  {
7187  // draw zeroline:
7188  int zeroLineIndex = -1;
7189  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
7190  {
7192  painter->setPen(mZeroLinePen);
7193  double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
7194  for (int i=0; i<tickCount; ++i)
7195  {
7196  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
7197  {
7198  zeroLineIndex = i;
7199  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
7200  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
7201  break;
7202  }
7203  }
7204  }
7205  // draw grid lines:
7207  painter->setPen(mPen);
7208  for (int i=0; i<tickCount; ++i)
7209  {
7210  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
7211  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
7212  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
7213  }
7214  }
7215 }
7216 
7224 {
7225  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
7226 
7228  double t; // helper variable, result of coordinate-to-pixel transforms
7229  painter->setPen(mSubGridPen);
7230  if (mParentAxis->orientation() == Qt::Horizontal)
7231  {
7232  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
7233  {
7235  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
7236  }
7237  } else
7238  {
7239  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
7240  {
7242  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
7243  }
7244  }
7245 }
7246 
7247 
7251 
7274 /* start of documentation of inline functions */
7275 
7332 /* end of documentation of inline functions */
7333 /* start of documentation of signals */
7334 
7373 /* end of documentation of signals */
7374 
7383  QCPLayerable(parent->parentPlot(), QString(), parent),
7384  // axis base:
7385  mAxisType(type),
7386  mAxisRect(parent),
7387  mPadding(5),
7388  mOrientation(orientation(type)),
7389  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
7390  mSelectedParts(spNone),
7391  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7392  mSelectedBasePen(QPen(Qt::blue, 2)),
7393  // axis label:
7394  mLabel(),
7395  mLabelFont(mParentPlot->font()),
7396  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
7397  mLabelColor(Qt::black),
7398  mSelectedLabelColor(Qt::blue),
7399  // tick labels:
7400  mTickLabels(true),
7401  mTickLabelFont(mParentPlot->font()),
7402  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
7403  mTickLabelColor(Qt::black),
7404  mSelectedTickLabelColor(Qt::blue),
7405  mNumberPrecision(6),
7406  mNumberFormatChar('g'),
7407  mNumberBeautifulPowers(true),
7408  // ticks and subticks:
7409  mTicks(true),
7410  mSubTicks(true),
7411  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7412  mSelectedTickPen(QPen(Qt::blue, 2)),
7413  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7414  mSelectedSubTickPen(QPen(Qt::blue, 2)),
7415  // scale and range:
7416  mRange(0, 5),
7417  mRangeReversed(false),
7418  mScaleType(stLinear),
7419  // internal members:
7420  mGrid(new QCPGrid(this)),
7421  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
7422  mTicker(new QCPAxisTicker),
7423  mCachedMarginValid(false),
7424  mCachedMargin(0)
7425 {
7426  setParent(parent);
7427  mGrid->setVisible(false);
7428  setAntialiased(false);
7429  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
7430 
7431  if (type == atTop)
7432  {
7434  setLabelPadding(6);
7435  } else if (type == atRight)
7436  {
7438  setLabelPadding(12);
7439  } else if (type == atBottom)
7440  {
7442  setLabelPadding(3);
7443  } else if (type == atLeft)
7444  {
7446  setLabelPadding(10);
7447  }
7448 }
7449 
7451 {
7452  delete mAxisPainter;
7453  delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
7454 }
7455 
7456 /* No documentation as it is a property getter */
7458 {
7460 }
7461 
7462 /* No documentation as it is a property getter */
7464 {
7466 }
7467 
7468 /* No documentation as it is a property getter */
7470 {
7471  return mAxisPainter->tickLabelSide;
7472 }
7473 
7474 /* No documentation as it is a property getter */
7475 QString QCPAxis::numberFormat() const
7476 {
7477  QString result;
7478  result.append(mNumberFormatChar);
7480  {
7481  result.append(QLatin1Char('b'));
7483  result.append(QLatin1Char('c'));
7484  }
7485  return result;
7486 }
7487 
7488 /* No documentation as it is a property getter */
7490 {
7491  return mAxisPainter->tickLengthIn;
7492 }
7493 
7494 /* No documentation as it is a property getter */
7496 {
7497  return mAxisPainter->tickLengthOut;
7498 }
7499 
7500 /* No documentation as it is a property getter */
7502 {
7503  return mAxisPainter->subTickLengthIn;
7504 }
7505 
7506 /* No documentation as it is a property getter */
7508 {
7510 }
7511 
7512 /* No documentation as it is a property getter */
7514 {
7515  return mAxisPainter->labelPadding;
7516 }
7517 
7518 /* No documentation as it is a property getter */
7519 int QCPAxis::offset() const
7520 {
7521  return mAxisPainter->offset;
7522 }
7523 
7524 /* No documentation as it is a property getter */
7526 {
7527  return mAxisPainter->lowerEnding;
7528 }
7529 
7530 /* No documentation as it is a property getter */
7532 {
7533  return mAxisPainter->upperEnding;
7534 }
7535 
7547 {
7548  if (mScaleType != type)
7549  {
7550  mScaleType = type;
7551  if (mScaleType == stLogarithmic)
7553  mCachedMarginValid = false;
7555  }
7556 }
7557 
7567 {
7568  if (range.lower == mRange.lower && range.upper == mRange.upper)
7569  return;
7570 
7571  if (!QCPRange::validRange(range)) return;
7572  QCPRange oldRange = mRange;
7573  if (mScaleType == stLogarithmic)
7574  {
7575  mRange = range.sanitizedForLogScale();
7576  } else
7577  {
7578  mRange = range.sanitizedForLinScale();
7579  }
7580  emit rangeChanged(mRange);
7581  emit rangeChanged(mRange, oldRange);
7582 }
7583 
7594 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
7595 {
7596  if (mSelectableParts != selectable)
7597  {
7598  mSelectableParts = selectable;
7600  }
7601 }
7602 
7618 void QCPAxis::setSelectedParts(const SelectableParts &selected)
7619 {
7620  if (mSelectedParts != selected)
7621  {
7622  mSelectedParts = selected;
7624  }
7625 }
7626 
7636 void QCPAxis::setRange(double lower, double upper)
7637 {
7638  if (lower == mRange.lower && upper == mRange.upper)
7639  return;
7640 
7641  if (!QCPRange::validRange(lower, upper)) return;
7642  QCPRange oldRange = mRange;
7643  mRange.lower = lower;
7644  mRange.upper = upper;
7645  if (mScaleType == stLogarithmic)
7646  {
7648  } else
7649  {
7651  }
7652  emit rangeChanged(mRange);
7653  emit rangeChanged(mRange, oldRange);
7654 }
7655 
7667 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
7668 {
7669  if (alignment == Qt::AlignLeft)
7670  setRange(position, position+size);
7671  else if (alignment == Qt::AlignRight)
7672  setRange(position-size, position);
7673  else // alignment == Qt::AlignCenter
7674  setRange(position-size/2.0, position+size/2.0);
7675 }
7676 
7681 void QCPAxis::setRangeLower(double lower)
7682 {
7683  if (mRange.lower == lower)
7684  return;
7685 
7686  QCPRange oldRange = mRange;
7687  mRange.lower = lower;
7688  if (mScaleType == stLogarithmic)
7689  {
7691  } else
7692  {
7694  }
7695  emit rangeChanged(mRange);
7696  emit rangeChanged(mRange, oldRange);
7697 }
7698 
7703 void QCPAxis::setRangeUpper(double upper)
7704 {
7705  if (mRange.upper == upper)
7706  return;
7707 
7708  QCPRange oldRange = mRange;
7709  mRange.upper = upper;
7710  if (mScaleType == stLogarithmic)
7711  {
7713  } else
7714  {
7716  }
7717  emit rangeChanged(mRange);
7718  emit rangeChanged(mRange, oldRange);
7719 }
7720 
7730 void QCPAxis::setRangeReversed(bool reversed)
7731 {
7732  mRangeReversed = reversed;
7733 }
7734 
7748 void QCPAxis::setTicker(QSharedPointer<QCPAxisTicker> ticker)
7749 {
7750  if (ticker)
7751  mTicker = ticker;
7752  else
7753  qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker";
7754  // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector
7755 }
7756 
7765 void QCPAxis::setTicks(bool show)
7766 {
7767  if (mTicks != show)
7768  {
7769  mTicks = show;
7770  mCachedMarginValid = false;
7771  }
7772 }
7773 
7777 void QCPAxis::setTickLabels(bool show)
7778 {
7779  if (mTickLabels != show)
7780  {
7781  mTickLabels = show;
7782  mCachedMarginValid = false;
7783  if (!mTickLabels)
7784  mTickVectorLabels.clear();
7785  }
7786 }
7787 
7793 {
7794  if (mAxisPainter->tickLabelPadding != padding)
7795  {
7797  mCachedMarginValid = false;
7798  }
7799 }
7800 
7806 void QCPAxis::setTickLabelFont(const QFont &font)
7807 {
7808  if (font != mTickLabelFont)
7809  {
7810  mTickLabelFont = font;
7811  mCachedMarginValid = false;
7812  }
7813 }
7814 
7820 void QCPAxis::setTickLabelColor(const QColor &color)
7821 {
7822  mTickLabelColor = color;
7823 }
7824 
7834 void QCPAxis::setTickLabelRotation(double degrees)
7835 {
7836  if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
7837  {
7838  mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
7839  mCachedMarginValid = false;
7840  }
7841 }
7842 
7851 {
7852  mAxisPainter->tickLabelSide = side;
7853  mCachedMarginValid = false;
7854 }
7855 
7886 void QCPAxis::setNumberFormat(const QString &formatCode)
7887 {
7888  if (formatCode.isEmpty())
7889  {
7890  qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
7891  return;
7892  }
7893  mCachedMarginValid = false;
7894 
7895  // interpret first char as number format char:
7896  QString allowedFormatChars(QLatin1String("eEfgG"));
7897  if (allowedFormatChars.contains(formatCode.at(0)))
7898  {
7899  mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
7900  } else
7901  {
7902  qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
7903  return;
7904  }
7905  if (formatCode.length() < 2)
7906  {
7907  mNumberBeautifulPowers = false;
7909  return;
7910  }
7911 
7912  // interpret second char as indicator for beautiful decimal powers:
7913  if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
7914  {
7915  mNumberBeautifulPowers = true;
7916  } else
7917  {
7918  qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
7919  return;
7920  }
7921  if (formatCode.length() < 3)
7922  {
7924  return;
7925  }
7926 
7927  // interpret third char as indicator for dot or cross multiplication symbol:
7928  if (formatCode.at(2) == QLatin1Char('c'))
7929  {
7931  } else if (formatCode.at(2) == QLatin1Char('d'))
7932  {
7934  } else
7935  {
7936  qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
7937  return;
7938  }
7939 }
7940 
7946 void QCPAxis::setNumberPrecision(int precision)
7947 {
7948  if (mNumberPrecision != precision)
7949  {
7950  mNumberPrecision = precision;
7951  mCachedMarginValid = false;
7952  }
7953 }
7954 
7963 void QCPAxis::setTickLength(int inside, int outside)
7964 {
7965  setTickLengthIn(inside);
7966  setTickLengthOut(outside);
7967 }
7968 
7976 {
7977  if (mAxisPainter->tickLengthIn != inside)
7978  {
7979  mAxisPainter->tickLengthIn = inside;
7980  }
7981 }
7982 
7990 void QCPAxis::setTickLengthOut(int outside)
7991 {
7992  if (mAxisPainter->tickLengthOut != outside)
7993  {
7994  mAxisPainter->tickLengthOut = outside;
7995  mCachedMarginValid = false; // only outside tick length can change margin
7996  }
7997 }
7998 
8006 void QCPAxis::setSubTicks(bool show)
8007 {
8008  if (mSubTicks != show)
8009  {
8010  mSubTicks = show;
8011  mCachedMarginValid = false;
8012  }
8013 }
8014 
8023 void QCPAxis::setSubTickLength(int inside, int outside)
8024 {
8025  setSubTickLengthIn(inside);
8026  setSubTickLengthOut(outside);
8027 }
8028 
8036 {
8037  if (mAxisPainter->subTickLengthIn != inside)
8038  {
8039  mAxisPainter->subTickLengthIn = inside;
8040  }
8041 }
8042 
8051 {
8052  if (mAxisPainter->subTickLengthOut != outside)
8053  {
8054  mAxisPainter->subTickLengthOut = outside;
8055  mCachedMarginValid = false; // only outside tick length can change margin
8056  }
8057 }
8058 
8064 void QCPAxis::setBasePen(const QPen &pen)
8065 {
8066  mBasePen = pen;
8067 }
8068 
8074 void QCPAxis::setTickPen(const QPen &pen)
8075 {
8076  mTickPen = pen;
8077 }
8078 
8084 void QCPAxis::setSubTickPen(const QPen &pen)
8085 {
8086  mSubTickPen = pen;
8087 }
8088 
8094 void QCPAxis::setLabelFont(const QFont &font)
8095 {
8096  if (mLabelFont != font)
8097  {
8098  mLabelFont = font;
8099  mCachedMarginValid = false;
8100  }
8101 }
8102 
8108 void QCPAxis::setLabelColor(const QColor &color)
8109 {
8110  mLabelColor = color;
8111 }
8112 
8117 void QCPAxis::setLabel(const QString &str)
8118 {
8119  if (mLabel != str)
8120  {
8121  mLabel = str;
8122  mCachedMarginValid = false;
8123  }
8124 }
8125 
8132 {
8133  if (mAxisPainter->labelPadding != padding)
8134  {
8136  mCachedMarginValid = false;
8137  }
8138 }
8139 
8151 {
8152  if (mPadding != padding)
8153  {
8154  mPadding = padding;
8155  mCachedMarginValid = false;
8156  }
8157 }
8158 
8168 {
8170 }
8171 
8177 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
8178 {
8179  if (font != mSelectedTickLabelFont)
8180  {
8181  mSelectedTickLabelFont = font;
8182  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
8183  }
8184 }
8185 
8191 void QCPAxis::setSelectedLabelFont(const QFont &font)
8192 {
8193  mSelectedLabelFont = font;
8194  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
8195 }
8196 
8202 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
8203 {
8204  if (color != mSelectedTickLabelColor)
8205  {
8206  mSelectedTickLabelColor = color;
8207  }
8208 }
8209 
8215 void QCPAxis::setSelectedLabelColor(const QColor &color)
8216 {
8217  mSelectedLabelColor = color;
8218 }
8219 
8225 void QCPAxis::setSelectedBasePen(const QPen &pen)
8226 {
8227  mSelectedBasePen = pen;
8228 }
8229 
8235 void QCPAxis::setSelectedTickPen(const QPen &pen)
8236 {
8237  mSelectedTickPen = pen;
8238 }
8239 
8245 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
8246 {
8247  mSelectedSubTickPen = pen;
8248 }
8249 
8261 {
8262  mAxisPainter->lowerEnding = ending;
8263 }
8264 
8276 {
8277  mAxisPainter->upperEnding = ending;
8278 }
8279 
8287 void QCPAxis::moveRange(double diff)
8288 {
8289  QCPRange oldRange = mRange;
8290  if (mScaleType == stLinear)
8291  {
8292  mRange.lower += diff;
8293  mRange.upper += diff;
8294  } else // mScaleType == stLogarithmic
8295  {
8296  mRange.lower *= diff;
8297  mRange.upper *= diff;
8298  }
8299  emit rangeChanged(mRange);
8300  emit rangeChanged(mRange, oldRange);
8301 }
8302 
8312 void QCPAxis::scaleRange(double factor)
8313 {
8314  scaleRange(factor, range().center());
8315 }
8316 
8326 void QCPAxis::scaleRange(double factor, double center)
8327 {
8328  QCPRange oldRange = mRange;
8329  if (mScaleType == stLinear)
8330  {
8331  QCPRange newRange;
8332  newRange.lower = (mRange.lower-center)*factor + center;
8333  newRange.upper = (mRange.upper-center)*factor + center;
8334  if (QCPRange::validRange(newRange))
8335  mRange = newRange.sanitizedForLinScale();
8336  } else // mScaleType == stLogarithmic
8337  {
8338  if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
8339  {
8340  QCPRange newRange;
8341  newRange.lower = qPow(mRange.lower/center, factor)*center;
8342  newRange.upper = qPow(mRange.upper/center, factor)*center;
8343  if (QCPRange::validRange(newRange))
8344  mRange = newRange.sanitizedForLogScale();
8345  } else
8346  qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
8347  }
8348  emit rangeChanged(mRange);
8349  emit rangeChanged(mRange, oldRange);
8350 }
8351 
8365 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
8366 {
8367  int otherPixelSize, ownPixelSize;
8368 
8369  if (otherAxis->orientation() == Qt::Horizontal)
8370  otherPixelSize = otherAxis->axisRect()->width();
8371  else
8372  otherPixelSize = otherAxis->axisRect()->height();
8373 
8374  if (orientation() == Qt::Horizontal)
8375  ownPixelSize = axisRect()->width();
8376  else
8377  ownPixelSize = axisRect()->height();
8378 
8379  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
8380  setRange(range().center(), newRangeSize, Qt::AlignCenter);
8381 }
8382 
8389 void QCPAxis::rescale(bool onlyVisiblePlottables)
8390 {
8391  QList<QCPAbstractPlottable*> p = plottables();
8392  QCPRange newRange;
8393  bool haveRange = false;
8394  for (int i=0; i<p.size(); ++i)
8395  {
8396  if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
8397  continue;
8398  QCPRange plottableRange;
8399  bool currentFoundRange;
8400  QCP::SignDomain signDomain = QCP::sdBoth;
8401  if (mScaleType == stLogarithmic)
8402  signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);
8403  if (p.at(i)->keyAxis() == this)
8404  plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
8405  else
8406  plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
8407  if (currentFoundRange)
8408  {
8409  if (!haveRange)
8410  newRange = plottableRange;
8411  else
8412  newRange.expand(plottableRange);
8413  haveRange = true;
8414  }
8415  }
8416  if (haveRange)
8417  {
8418  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
8419  {
8420  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
8421  if (mScaleType == stLinear)
8422  {
8423  newRange.lower = center-mRange.size()/2.0;
8424  newRange.upper = center+mRange.size()/2.0;
8425  } else // mScaleType == stLogarithmic
8426  {
8427  newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
8428  newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
8429  }
8430  }
8431  setRange(newRange);
8432  }
8433 }
8434 
8438 double QCPAxis::pixelToCoord(double value) const
8439 {
8440  if (orientation() == Qt::Horizontal)
8441  {
8442  if (mScaleType == stLinear)
8443  {
8444  if (!mRangeReversed)
8445  return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
8446  else
8447  return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
8448  } else // mScaleType == stLogarithmic
8449  {
8450  if (!mRangeReversed)
8451  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
8452  else
8453  return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
8454  }
8455  } else // orientation() == Qt::Vertical
8456  {
8457  if (mScaleType == stLinear)
8458  {
8459  if (!mRangeReversed)
8460  return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
8461  else
8462  return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
8463  } else // mScaleType == stLogarithmic
8464  {
8465  if (!mRangeReversed)
8466  return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
8467  else
8468  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
8469  }
8470  }
8471 }
8472 
8476 double QCPAxis::coordToPixel(double value) const
8477 {
8478  if (orientation() == Qt::Horizontal)
8479  {
8480  if (mScaleType == stLinear)
8481  {
8482  if (!mRangeReversed)
8483  return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
8484  else
8485  return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
8486  } else // mScaleType == stLogarithmic
8487  {
8488  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
8489  return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
8490  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
8491  return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
8492  else
8493  {
8494  if (!mRangeReversed)
8495  return qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
8496  else
8497  return qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
8498  }
8499  }
8500  } else // orientation() == Qt::Vertical
8501  {
8502  if (mScaleType == stLinear)
8503  {
8504  if (!mRangeReversed)
8505  return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
8506  else
8507  return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
8508  } else // mScaleType == stLogarithmic
8509  {
8510  if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
8511  return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
8512  else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
8513  return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
8514  else
8515  {
8516  if (!mRangeReversed)
8517  return mAxisRect->bottom()-qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->height();
8518  else
8519  return mAxisRect->bottom()-qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->height();
8520  }
8521  }
8522  }
8523 }
8524 
8535 {
8536  if (!mVisible)
8537  return spNone;
8538 
8539  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
8540  return spAxis;
8541  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
8542  return spTickLabels;
8543  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
8544  return spAxisLabel;
8545  else
8546  return spNone;
8547 }
8548 
8549 /* inherits documentation from base class */
8550 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
8551 {
8552  if (!mParentPlot) return -1;
8553  SelectablePart part = getPartAt(pos);
8554  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
8555  return -1;
8556 
8557  if (details)
8558  details->setValue(part);
8559  return mParentPlot->selectionTolerance()*0.99;
8560 }
8561 
8569 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
8570 {
8571  QList<QCPAbstractPlottable*> result;
8572  if (!mParentPlot) return result;
8573 
8574  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
8575  {
8576  if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
8577  result.append(mParentPlot->mPlottables.at(i));
8578  }
8579  return result;
8580 }
8581 
8587 QList<QCPGraph*> QCPAxis::graphs() const
8588 {
8589  QList<QCPGraph*> result;
8590  if (!mParentPlot) return result;
8591 
8592  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
8593  {
8594  if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
8595  result.append(mParentPlot->mGraphs.at(i));
8596  }
8597  return result;
8598 }
8599 
8606 QList<QCPAbstractItem*> QCPAxis::items() const
8607 {
8608  QList<QCPAbstractItem*> result;
8609  if (!mParentPlot) return result;
8610 
8611  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
8612  {
8613  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
8614  for (int posId=0; posId<positions.size(); ++posId)
8615  {
8616  if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
8617  {
8618  result.append(mParentPlot->mItems.at(itemId));
8619  break;
8620  }
8621  }
8622  }
8623  return result;
8624 }
8625 
8631 {
8632  switch (side)
8633  {
8634  case QCP::msLeft: return atLeft;
8635  case QCP::msRight: return atRight;
8636  case QCP::msTop: return atTop;
8637  case QCP::msBottom: return atBottom;
8638  default: break;
8639  }
8640  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
8641  return atLeft;
8642 }
8643 
8648 {
8649  switch (type)
8650  {
8651  case atLeft: return atRight; break;
8652  case atRight: return atLeft; break;
8653  case atBottom: return atTop; break;
8654  case atTop: return atBottom; break;
8655  default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
8656  }
8657 }
8658 
8659 /* inherits documentation from base class */
8660 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8661 {
8662  Q_UNUSED(event)
8663  SelectablePart part = details.value<SelectablePart>();
8664  if (mSelectableParts.testFlag(part))
8665  {
8666  SelectableParts selBefore = mSelectedParts;
8667  setSelectedParts(additive ? mSelectedParts^part : part);
8668  if (selectionStateChanged)
8669  *selectionStateChanged = mSelectedParts != selBefore;
8670  }
8671 }
8672 
8673 /* inherits documentation from base class */
8674 void QCPAxis::deselectEvent(bool *selectionStateChanged)
8675 {
8676  SelectableParts selBefore = mSelectedParts;
8678  if (selectionStateChanged)
8679  *selectionStateChanged = mSelectedParts != selBefore;
8680 }
8681 
8698 {
8700 }
8701 
8709 {
8710  QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
8711  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
8712  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
8713  tickPositions.reserve(mTickVector.size());
8714  tickLabels.reserve(mTickVector.size());
8715  subTickPositions.reserve(mSubTickVector.size());
8716 
8717  if (mTicks)
8718  {
8719  for (int i=0; i<mTickVector.size(); ++i)
8720  {
8721  tickPositions.append(coordToPixel(mTickVector.at(i)));
8722  if (mTickLabels)
8723  tickLabels.append(mTickVectorLabels.at(i));
8724  }
8725 
8726  if (mSubTicks)
8727  {
8728  const int subTickCount = mSubTickVector.size();
8729  for (int i=0; i<subTickCount; ++i)
8730  subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
8731  }
8732  }
8733 
8734  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
8735  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
8750  mAxisPainter->tickPositions = tickPositions;
8752  mAxisPainter->subTickPositions = subTickPositions;
8753  mAxisPainter->draw(painter);
8754 }
8755 
8765 {
8766  if (!mParentPlot) return;
8767  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
8768 
8769  QVector<QString> oldLabels = mTickVectorLabels;
8771  mCachedMarginValid &= mTickVectorLabels == oldLabels; // if labels have changed, margin might have changed, too
8772 }
8773 
8780 {
8781  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
8782 }
8783 
8790 {
8791  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
8792 }
8793 
8800 {
8802 }
8803 
8810 {
8812 }
8813 
8820 {
8822 }
8823 
8830 {
8832 }
8833 
8840 {
8842 }
8843 
8859 {
8860  if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
8861  return 0;
8862 
8863  if (mCachedMarginValid)
8864  return mCachedMargin;
8865 
8866  // run through similar steps as QCPAxis::draw, and calculate margin needed to fit axis and its labels
8867  int margin = 0;
8868 
8869  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
8870  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
8871  tickPositions.reserve(mTickVector.size());
8872  tickLabels.reserve(mTickVector.size());
8873 
8874  if (mTicks)
8875  {
8876  for (int i=0; i<mTickVector.size(); ++i)
8877  {
8878  tickPositions.append(coordToPixel(mTickVector.at(i)));
8879  if (mTickLabels)
8880  tickLabels.append(mTickVectorLabels.at(i));
8881  }
8882  }
8883  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
8884  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
8891  mAxisPainter->tickPositions = tickPositions;
8893  margin += mAxisPainter->size();
8894  margin += mPadding;
8895 
8896  mCachedMargin = margin;
8897  mCachedMarginValid = true;
8898  return margin;
8899 }
8900 
8901 /* inherits documentation from base class */
8903 {
8904  return QCP::iSelectAxes;
8905 }
8906 
8907 
8911 
8929  type(QCPAxis::atLeft),
8930  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
8931  lowerEnding(QCPLineEnding::esNone),
8932  upperEnding(QCPLineEnding::esNone),
8933  labelPadding(0),
8934  tickLabelPadding(0),
8935  tickLabelRotation(0),
8937  substituteExponent(true),
8938  numberMultiplyCross(false),
8939  tickLengthIn(5),
8940  tickLengthOut(0),
8941  subTickLengthIn(2),
8942  subTickLengthOut(0),
8943  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
8944  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
8945  offset(0),
8946  abbreviateDecimalPowers(false),
8947  reversedEndings(false),
8948  mParentPlot(parentPlot),
8949  mLabelCache(16) // cache at most 16 (tick) labels
8950 {
8951 }
8952 
8954 {
8955 }
8956 
8965 {
8966  QByteArray newHash = generateLabelParameterHash();
8967  if (newHash != mLabelParameterHash)
8968  {
8969  mLabelCache.clear();
8970  mLabelParameterHash = newHash;
8971  }
8972 
8973  QPoint origin;
8974  switch (type)
8975  {
8976  case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
8977  case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
8978  case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break;
8979  case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
8980  }
8981 
8982  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
8983  switch (type)
8984  {
8985  case QCPAxis::atTop: yCor = -1; break;
8986  case QCPAxis::atRight: xCor = 1; break;
8987  default: break;
8988  }
8989  int margin = 0;
8990  // draw baseline:
8991  QLineF baseLine;
8992  painter->setPen(basePen);
8993  if (QCPAxis::orientation(type) == Qt::Horizontal)
8994  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
8995  else
8996  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
8997  if (reversedEndings)
8998  baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
8999  painter->drawLine(baseLine);
9000 
9001  // draw ticks:
9002  if (!tickPositions.isEmpty())
9003  {
9004  painter->setPen(tickPen);
9005  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
9006  if (QCPAxis::orientation(type) == Qt::Horizontal)
9007  {
9008  for (int i=0; i<tickPositions.size(); ++i)
9009  painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
9010  } else
9011  {
9012  for (int i=0; i<tickPositions.size(); ++i)
9013  painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
9014  }
9015  }
9016 
9017  // draw subticks:
9018  if (!subTickPositions.isEmpty())
9019  {
9020  painter->setPen(subTickPen);
9021  // direction of ticks ("inward" is right for left axis and left for right axis)
9022  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
9023  if (QCPAxis::orientation(type) == Qt::Horizontal)
9024  {
9025  for (int i=0; i<subTickPositions.size(); ++i)
9026  painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
9027  } else
9028  {
9029  for (int i=0; i<subTickPositions.size(); ++i)
9030  painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
9031  }
9032  }
9033  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
9034 
9035  // draw axis base endings:
9036  bool antialiasingBackup = painter->antialiasing();
9037  painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
9038  painter->setBrush(QBrush(basePen.color()));
9039  QCPVector2D baseLineVector(baseLine.dx(), baseLine.dy());
9041  lowerEnding.draw(painter, QCPVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
9043  upperEnding.draw(painter, QCPVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
9044  painter->setAntialiasing(antialiasingBackup);
9045 
9046  // tick labels:
9047  QRect oldClipRect;
9048  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
9049  {
9050  oldClipRect = painter->clipRegion().boundingRect();
9051  painter->setClipRect(axisRect);
9052  }
9053  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
9054  if (!tickLabels.isEmpty())
9055  {
9057  margin += tickLabelPadding;
9058  painter->setFont(tickLabelFont);
9059  painter->setPen(QPen(tickLabelColor));
9060  const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
9061  int distanceToAxis = margin;
9063  distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
9064  for (int i=0; i<maxLabelIndex; ++i)
9065  placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
9067  margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
9068  }
9070  painter->setClipRect(oldClipRect);
9071 
9072  // axis label:
9073  QRect labelBounds;
9074  if (!label.isEmpty())
9075  {
9076  margin += labelPadding;
9077  painter->setFont(labelFont);
9078  painter->setPen(QPen(labelColor));
9079  labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
9080  if (type == QCPAxis::atLeft)
9081  {
9082  QTransform oldTransform = painter->transform();
9083  painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
9084  painter->rotate(-90);
9085  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9086  painter->setTransform(oldTransform);
9087  }
9088  else if (type == QCPAxis::atRight)
9089  {
9090  QTransform oldTransform = painter->transform();
9091  painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
9092  painter->rotate(90);
9093  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9094  painter->setTransform(oldTransform);
9095  }
9096  else if (type == QCPAxis::atTop)
9097  painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9098  else if (type == QCPAxis::atBottom)
9099  painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9100  }
9101 
9102  // set selection boxes:
9103  int selectionTolerance = 0;
9104  if (mParentPlot)
9105  selectionTolerance = mParentPlot->selectionTolerance();
9106  else
9107  qDebug() << Q_FUNC_INFO << "mParentPlot is null";
9108  int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
9109  int selAxisInSize = selectionTolerance;
9110  int selTickLabelSize;
9111  int selTickLabelOffset;
9113  {
9114  selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
9115  selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
9116  } else
9117  {
9118  selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
9119  selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
9120  }
9121  int selLabelSize = labelBounds.height();
9122  int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
9123  if (type == QCPAxis::atLeft)
9124  {
9125  mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
9126  mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
9127  mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
9128  } else if (type == QCPAxis::atRight)
9129  {
9130  mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
9131  mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
9132  mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
9133  } else if (type == QCPAxis::atTop)
9134  {
9135  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
9136  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
9137  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
9138  } else if (type == QCPAxis::atBottom)
9139  {
9140  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
9141  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
9142  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
9143  }
9144  mAxisSelectionBox = mAxisSelectionBox.normalized();
9146  mLabelSelectionBox = mLabelSelectionBox.normalized();
9147  // draw hitboxes for debug purposes:
9148  //painter->setBrush(Qt::NoBrush);
9149  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
9150 }
9151 
9158 {
9159  int result = 0;
9160 
9161  // get length of tick marks pointing outwards:
9162  if (!tickPositions.isEmpty())
9163  result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
9164 
9165  // calculate size of tick labels:
9167  {
9168  QSize tickLabelsSize(0, 0);
9169  if (!tickLabels.isEmpty())
9170  {
9171  for (int i=0; i<tickLabels.size(); ++i)
9172  getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
9173  result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
9174  result += tickLabelPadding;
9175  }
9176  }
9177 
9178  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
9179  if (!label.isEmpty())
9180  {
9181  QFontMetrics fontMetrics(labelFont);
9182  QRect bounds;
9183  bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
9184  result += bounds.height() + labelPadding;
9185  }
9186 
9187  return result;
9188 }
9189 
9197 {
9198  mLabelCache.clear();
9199 }
9200 
9209 {
9210  QByteArray result;
9211  result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));
9212  result.append(QByteArray::number(tickLabelRotation));
9213  result.append(QByteArray::number((int)tickLabelSide));
9214  result.append(QByteArray::number((int)substituteExponent));
9215  result.append(QByteArray::number((int)numberMultiplyCross));
9216  result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
9217  result.append(tickLabelFont.toString().toLatin1());
9218  return result;
9219 }
9220 
9240 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
9241 {
9242  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
9243  if (text.isEmpty()) return;
9244  QSize finalSize;
9245  QPointF labelAnchor;
9246  switch (type)
9247  {
9248  case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
9249  case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
9250  case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
9251  case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
9252  }
9253  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
9254  {
9255  CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache
9256  if (!cachedLabel) // no cached label existed, create it
9257  {
9258  cachedLabel = new CachedLabel;
9259  TickLabelData labelData = getTickLabelData(painter->font(), text);
9260  cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
9261  if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio()))
9262  {
9263  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio());
9264 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
9265  cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio());
9266 #endif
9267  } else
9268  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
9269  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
9270  cachedLabel->pixmap.fill(Qt::transparent);
9271  QCPPainter cachePainter(&cachedLabel->pixmap);
9272  cachePainter.setPen(painter->pen());
9273  drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
9274  }
9275  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
9276  bool labelClippedByBorder = false;
9278  {
9279  if (QCPAxis::orientation(type) == Qt::Horizontal)
9280  labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
9281  else
9282  labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
9283  }
9284  if (!labelClippedByBorder)
9285  {
9286  painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
9287  finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
9288  }
9289  mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created
9290  } else // label caching disabled, draw text directly on surface:
9291  {
9292  TickLabelData labelData = getTickLabelData(painter->font(), text);
9293  QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
9294  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
9295  bool labelClippedByBorder = false;
9297  {
9298  if (QCPAxis::orientation(type) == Qt::Horizontal)
9299  labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
9300  else
9301  labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
9302  }
9303  if (!labelClippedByBorder)
9304  {
9305  drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
9306  finalSize = labelData.rotatedTotalBounds.size();
9307  }
9308  }
9309 
9310  // expand passed tickLabelsSize if current tick label is larger:
9311  if (finalSize.width() > tickLabelsSize->width())
9312  tickLabelsSize->setWidth(finalSize.width());
9313  if (finalSize.height() > tickLabelsSize->height())
9314  tickLabelsSize->setHeight(finalSize.height());
9315 }
9316 
9326 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
9327 {
9328  // backup painter settings that we're about to change:
9329  QTransform oldTransform = painter->transform();
9330  QFont oldFont = painter->font();
9331 
9332  // transform painter to position/rotation:
9333  painter->translate(x, y);
9334  if (!qFuzzyIsNull(tickLabelRotation))
9335  painter->rotate(tickLabelRotation);
9336 
9337  // draw text:
9338  if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
9339  {
9340  painter->setFont(labelData.baseFont);
9341  painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
9342  if (!labelData.suffixPart.isEmpty())
9343  painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart);
9344  painter->setFont(labelData.expFont);
9345  painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
9346  } else
9347  {
9348  painter->setFont(labelData.baseFont);
9349  painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
9350  }
9351 
9352  // reset painter settings to what it was before:
9353  painter->setTransform(oldTransform);
9354  painter->setFont(oldFont);
9355 }
9356 
9366 {
9367  TickLabelData result;
9368 
9369  // determine whether beautiful decimal powers should be used
9370  bool useBeautifulPowers = false;
9371  int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart
9372  int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart
9373  if (substituteExponent)
9374  {
9375  ePos = text.indexOf(QLatin1Char('e'));
9376  if (ePos > 0 && text.at(ePos-1).isDigit())
9377  {
9378  eLast = ePos;
9379  while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))
9380  ++eLast;
9381  if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power
9382  useBeautifulPowers = true;
9383  }
9384  }
9385 
9386  // calculate text bounding rects and do string preparation for beautiful decimal powers:
9387  result.baseFont = font;
9388  if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
9389  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
9390  if (useBeautifulPowers)
9391  {
9392  // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
9393  result.basePart = text.left(ePos);
9394  result.suffixPart = text.mid(eLast+1); // also drawn normally but after exponent
9395  // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
9396  if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
9397  result.basePart = QLatin1String("10");
9398  else
9399  result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
9400  result.expPart = text.mid(ePos+1, eLast-ePos);
9401  // clip "+" and leading zeros off expPart:
9402  while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
9403  result.expPart.remove(1, 1);
9404  if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
9405  result.expPart.remove(0, 1);
9406  // prepare smaller font for exponent:
9407  result.expFont = font;
9408  if (result.expFont.pointSize() > 0)
9409  result.expFont.setPointSize(result.expFont.pointSize()*0.75);
9410  else
9411  result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
9412  // calculate bounding rects of base part(s), exponent part and total one:
9413  result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
9414  result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
9415  if (!result.suffixPart.isEmpty())
9416  result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart);
9417  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
9418  } else // useBeautifulPowers == false
9419  {
9420  result.basePart = text;
9421  result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
9422  }
9423  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
9424 
9425  // calculate possibly different bounding rect after rotation:
9426  result.rotatedTotalBounds = result.totalBounds;
9427  if (!qFuzzyIsNull(tickLabelRotation))
9428  {
9429  QTransform transform;
9430  transform.rotate(tickLabelRotation);
9431  result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
9432  }
9433 
9434  return result;
9435 }
9436 
9448 {
9449  /*
9450  calculate label offset from base point at tick (non-trivial, for best visual appearance): short
9451  explanation for bottom axis: The anchor, i.e. the point in the label that is placed
9452  horizontally under the corresponding tick is always on the label side that is closer to the
9453  axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
9454  is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
9455  will be centered under the tick (i.e. displaced horizontally by half its height). At the same
9456  time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
9457  labels.
9458  */
9459  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
9460  bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
9461  double radians = tickLabelRotation/180.0*M_PI;
9462  int x=0, y=0;
9463  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
9464  {
9465  if (doRotation)
9466  {
9467  if (tickLabelRotation > 0)
9468  {
9469  x = -qCos(radians)*labelData.totalBounds.width();
9470  y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
9471  } else
9472  {
9473  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
9474  y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
9475  }
9476  } else
9477  {
9478  x = -labelData.totalBounds.width();
9479  y = -labelData.totalBounds.height()/2.0;
9480  }
9481  } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
9482  {
9483  if (doRotation)
9484  {
9485  if (tickLabelRotation > 0)
9486  {
9487  x = +qSin(radians)*labelData.totalBounds.height();
9488  y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
9489  } else
9490  {
9491  x = 0;
9492  y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
9493  }
9494  } else
9495  {
9496  x = 0;
9497  y = -labelData.totalBounds.height()/2.0;
9498  }
9499  } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
9500  {
9501  if (doRotation)
9502  {
9503  if (tickLabelRotation > 0)
9504  {
9505  x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
9506  y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
9507  } else
9508  {
9509  x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
9510  y = -qCos(-radians)*labelData.totalBounds.height();
9511  }
9512  } else
9513  {
9514  x = -labelData.totalBounds.width()/2.0;
9515  y = -labelData.totalBounds.height();
9516  }
9517  } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
9518  {
9519  if (doRotation)
9520  {
9521  if (tickLabelRotation > 0)
9522  {
9523  x = +qSin(radians)*labelData.totalBounds.height()/2.0;
9524  y = 0;
9525  } else
9526  {
9527  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
9528  y = +qSin(-radians)*labelData.totalBounds.width();
9529  }
9530  } else
9531  {
9532  x = -labelData.totalBounds.width()/2.0;
9533  y = 0;
9534  }
9535  }
9536 
9537  return QPointF(x, y);
9538 }
9539 
9547 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
9548 {
9549  // note: this function must return the same tick label sizes as the placeTickLabel function.
9550  QSize finalSize;
9551  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
9552  {
9553  const CachedLabel *cachedLabel = mLabelCache.object(text);
9554  finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
9555  } else // label caching disabled or no label with this text cached:
9556  {
9557  TickLabelData labelData = getTickLabelData(font, text);
9558  finalSize = labelData.rotatedTotalBounds.size();
9559  }
9560 
9561  // expand passed tickLabelsSize if current tick label is larger:
9562  if (finalSize.width() > tickLabelsSize->width())
9563  tickLabelsSize->setWidth(finalSize.width());
9564  if (finalSize.height() > tickLabelsSize->height())
9565  tickLabelsSize->setHeight(finalSize.height());
9566 }
9567 /* end of 'src/axis/axis.cpp' */
9568 
9569 
9570 /* including file 'src/scatterstyle.cpp', size 17420 */
9571 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
9572 
9576 
9627 /* start documentation of inline functions */
9628 
9650 /* end documentation of inline functions */
9651 
9659  mSize(6),
9660  mShape(ssNone),
9661  mPen(Qt::NoPen),
9662  mBrush(Qt::NoBrush),
9663  mPenDefined(false)
9664 {
9665 }
9666 
9675  mSize(size),
9676  mShape(shape),
9677  mPen(Qt::NoPen),
9678  mBrush(Qt::NoBrush),
9679  mPenDefined(false)
9680 {
9681 }
9682 
9688  mSize(size),
9689  mShape(shape),
9690  mPen(QPen(color)),
9691  mBrush(Qt::NoBrush),
9692  mPenDefined(true)
9693 {
9694 }
9695 
9700 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
9701  mSize(size),
9702  mShape(shape),
9703  mPen(QPen(color)),
9704  mBrush(QBrush(fill)),
9705  mPenDefined(true)
9706 {
9707 }
9708 
9724 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
9725  mSize(size),
9726  mShape(shape),
9727  mPen(pen),
9728  mBrush(brush),
9729  mPenDefined(pen.style() != Qt::NoPen)
9730 {
9731 }
9732 
9738  mSize(5),
9739  mShape(ssPixmap),
9740  mPen(Qt::NoPen),
9741  mBrush(Qt::NoBrush),
9742  mPixmap(pixmap),
9743  mPenDefined(false)
9744 {
9745 }
9746 
9756 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
9757  mSize(size),
9758  mShape(ssCustom),
9759  mPen(pen),
9760  mBrush(brush),
9761  mCustomPath(customPath),
9762  mPenDefined(pen.style() != Qt::NoPen)
9763 {
9764 }
9765 
9769 void QCPScatterStyle::setFromOther(const QCPScatterStyle &other, ScatterProperties properties)
9770 {
9771  if (properties.testFlag(spPen))
9772  {
9773  setPen(other.pen());
9774  if (!other.isPenDefined())
9775  undefinePen();
9776  }
9777  if (properties.testFlag(spBrush))
9778  setBrush(other.brush());
9779  if (properties.testFlag(spSize))
9780  setSize(other.size());
9781  if (properties.testFlag(spShape))
9782  {
9783  setShape(other.shape());
9784  if (other.shape() == ssPixmap)
9785  setPixmap(other.pixmap());
9786  else if (other.shape() == ssCustom)
9787  setCustomPath(other.customPath());
9788  }
9789 }
9790 
9797 {
9798  mSize = size;
9799 }
9800 
9810 {
9811  mShape = shape;
9812 }
9813 
9823 void QCPScatterStyle::setPen(const QPen &pen)
9824 {
9825  mPenDefined = true;
9826  mPen = pen;
9827 }
9828 
9836 {
9837  mBrush = brush;
9838 }
9839 
9848 {
9849  setShape(ssPixmap);
9850  mPixmap = pixmap;
9851 }
9852 
9859 {
9860  setShape(ssCustom);
9862 }
9863 
9871 {
9872  mPenDefined = false;
9873 }
9874 
9884 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
9885 {
9886  painter->setPen(mPenDefined ? mPen : defaultPen);
9887  painter->setBrush(mBrush);
9888 }
9889 
9898 void QCPScatterStyle::drawShape(QCPPainter *painter, const QPointF &pos) const
9899 {
9900  drawShape(painter, pos.x(), pos.y());
9901 }
9902 
9906 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
9907 {
9908  double w = mSize/2.0;
9909  switch (mShape)
9910  {
9911  case ssNone: break;
9912  case ssDot:
9913  {
9914  painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
9915  break;
9916  }
9917  case ssCross:
9918  {
9919  painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
9920  painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
9921  break;
9922  }
9923  case ssPlus:
9924  {
9925  painter->drawLine(QLineF(x-w, y, x+w, y));
9926  painter->drawLine(QLineF( x, y+w, x, y-w));
9927  break;
9928  }
9929  case ssCircle:
9930  {
9931  painter->drawEllipse(QPointF(x , y), w, w);
9932  break;
9933  }
9934  case ssDisc:
9935  {
9936  QBrush b = painter->brush();
9937  painter->setBrush(painter->pen().color());
9938  painter->drawEllipse(QPointF(x , y), w, w);
9939  painter->setBrush(b);
9940  break;
9941  }
9942  case ssSquare:
9943  {
9944  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
9945  break;
9946  }
9947  case ssDiamond:
9948  {
9949  painter->drawLine(QLineF(x-w, y, x, y-w));
9950  painter->drawLine(QLineF( x, y-w, x+w, y));
9951  painter->drawLine(QLineF(x+w, y, x, y+w));
9952  painter->drawLine(QLineF( x, y+w, x-w, y));
9953  break;
9954  }
9955  case ssStar:
9956  {
9957  painter->drawLine(QLineF(x-w, y, x+w, y));
9958  painter->drawLine(QLineF( x, y+w, x, y-w));
9959  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
9960  painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
9961  break;
9962  }
9963  case ssTriangle:
9964  {
9965  painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
9966  painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
9967  painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w));
9968  break;
9969  }
9970  case ssTriangleInverted:
9971  {
9972  painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
9973  painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
9974  painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w));
9975  break;
9976  }
9977  case ssCrossSquare:
9978  {
9979  painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
9980  painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
9981  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
9982  break;
9983  }
9984  case ssPlusSquare:
9985  {
9986  painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
9987  painter->drawLine(QLineF( x, y+w, x, y-w));
9988  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
9989  break;
9990  }
9991  case ssCrossCircle:
9992  {
9993  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
9994  painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
9995  painter->drawEllipse(QPointF(x, y), w, w);
9996  break;
9997  }
9998  case ssPlusCircle:
9999  {
10000  painter->drawLine(QLineF(x-w, y, x+w, y));
10001  painter->drawLine(QLineF( x, y+w, x, y-w));
10002  painter->drawEllipse(QPointF(x, y), w, w);
10003  break;
10004  }
10005  case ssPeace:
10006  {
10007  painter->drawLine(QLineF(x, y-w, x, y+w));
10008  painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
10009  painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
10010  painter->drawEllipse(QPointF(x, y), w, w);
10011  break;
10012  }
10013  case ssPixmap:
10014  {
10015  const double widthHalf = mPixmap.width()*0.5;
10016  const double heightHalf = mPixmap.height()*0.5;
10017 #if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
10018  const QRectF clipRect = painter->clipRegion().boundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);
10019 #else
10020  const QRectF clipRect = painter->clipBoundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);
10021 #endif
10022  if (clipRect.contains(x, y))
10023  painter->drawPixmap(x-widthHalf, y-heightHalf, mPixmap);
10024  break;
10025  }
10026  case ssCustom:
10027  {
10028  QTransform oldTransform = painter->transform();
10029  painter->translate(x, y);
10030  painter->scale(mSize/6.0, mSize/6.0);
10031  painter->drawPath(mCustomPath);
10032  painter->setTransform(oldTransform);
10033  break;
10034  }
10035  }
10036 }
10037 /* end of 'src/scatterstyle.cpp' */
10038 
10039 //amalgamation: add datacontainer.cpp
10040 
10041 /* including file 'src/plottable.cpp', size 38861 */
10042 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
10043 
10047 
10078  mPen(QColor(80, 80, 255), 2.5),
10079  mBrush(Qt::NoBrush),
10080  mScatterStyle(QCPScatterStyle::ssNone, QPen(Qt::blue, 2), Qt::NoBrush, 6.0),
10081  mUsedScatterProperties(QCPScatterStyle::spPen),
10082  mPlottable(0)
10083 {
10084 }
10085 
10087 {
10088 }
10089 
10094 {
10095  mPen = pen;
10096 }
10097 
10102 {
10103  mBrush = brush;
10104 }
10105 
10113 void QCPSelectionDecorator::setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties)
10114 {
10116  setUsedScatterProperties(usedProperties);
10117 }
10118 
10124 void QCPSelectionDecorator::setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties)
10125 {
10126  mUsedScatterProperties = properties;
10127 }
10128 
10135 {
10136  painter->setPen(mPen);
10137 }
10138 
10145 {
10146  painter->setBrush(mBrush);
10147 }
10148 
10158 {
10159  QCPScatterStyle result(unselectedStyle);
10161 
10162  // if style shall inherit pen from plottable (has no own pen defined), give it the selected
10163  // plottable pen explicitly, so it doesn't use the unselected plottable pen when used in the
10164  // plottable:
10165  if (!result.isPenDefined())
10166  result.setPen(mPen);
10167 
10168  return result;
10169 }
10170 
10176 {
10177  setPen(other->pen());
10178  setBrush(other->brush());
10180 }
10181 
10191 {
10192  Q_UNUSED(painter)
10193  Q_UNUSED(selection)
10194 }
10195 
10206 {
10207  if (!mPlottable)
10208  {
10209  mPlottable = plottable;
10210  return true;
10211  } else
10212  {
10213  qDebug() << Q_FUNC_INFO << "This selection decorator is already registered with plottable:" << reinterpret_cast<quintptr>(mPlottable);
10214  return false;
10215  }
10216 }
10217 
10218 
10222 
10304 /* start of documentation of inline functions */
10305 
10340 /* end of documentation of inline functions */
10341 /* start of documentation of pure virtual functions */
10342 
10393 /* end of documentation of pure virtual functions */
10394 /* start of documentation of signals */
10395 
10421 /* end of documentation of signals */
10422 
10435  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
10436  mName(),
10437  mAntialiasedFill(true),
10438  mAntialiasedScatters(true),
10439  mPen(Qt::black),
10440  mBrush(Qt::NoBrush),
10441  mKeyAxis(keyAxis),
10442  mValueAxis(valueAxis),
10443  mSelectable(QCP::stWhole),
10444  mSelectionDecorator(0)
10445 {
10446  if (keyAxis->parentPlot() != valueAxis->parentPlot())
10447  qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
10448  if (keyAxis->orientation() == valueAxis->orientation())
10449  qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
10450 
10453 }
10454 
10456 {
10457  if (mSelectionDecorator)
10458  {
10459  delete mSelectionDecorator;
10460  mSelectionDecorator = 0;
10461  }
10462 }
10463 
10469 {
10470  mName = name;
10471 }
10472 
10480 {
10481  mAntialiasedFill = enabled;
10482 }
10483 
10491 {
10492  mAntialiasedScatters = enabled;
10493 }
10494 
10504 {
10505  mPen = pen;
10506 }
10507 
10518 {
10519  mBrush = brush;
10520 }
10521 
10534 {
10535  mKeyAxis = axis;
10536 }
10537 
10550 {
10551  mValueAxis = axis;
10552 }
10553 
10554 
10574 {
10575  selection.enforceType(mSelectable);
10576  if (mSelection != selection)
10577  {
10579  emit selectionChanged(selected());
10581  }
10582 }
10583 
10594 {
10595  if (decorator)
10596  {
10597  if (decorator->registerWithPlottable(this))
10598  {
10599  if (mSelectionDecorator) // delete old decorator if necessary
10600  delete mSelectionDecorator;
10601  mSelectionDecorator = decorator;
10602  }
10603  } else if (mSelectionDecorator) // just clear decorator
10604  {
10605  delete mSelectionDecorator;
10606  mSelectionDecorator = 0;
10607  }
10608 }
10609 
10621 {
10622  if (mSelectable != selectable)
10623  {
10625  QCPDataSelection oldSelection = mSelection;
10628  if (mSelection != oldSelection)
10629  {
10630  emit selectionChanged(selected());
10632  }
10633  }
10634 }
10635 
10636 
10646 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
10647 {
10648  QCPAxis *keyAxis = mKeyAxis.data();
10649  QCPAxis *valueAxis = mValueAxis.data();
10650  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
10651 
10652  if (keyAxis->orientation() == Qt::Horizontal)
10653  {
10654  x = keyAxis->coordToPixel(key);
10655  y = valueAxis->coordToPixel(value);
10656  } else
10657  {
10658  y = keyAxis->coordToPixel(key);
10659  x = valueAxis->coordToPixel(value);
10660  }
10661 }
10662 
10667 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
10668 {
10669  QCPAxis *keyAxis = mKeyAxis.data();
10670  QCPAxis *valueAxis = mValueAxis.data();
10671  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
10672 
10673  if (keyAxis->orientation() == Qt::Horizontal)
10674  return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
10675  else
10676  return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
10677 }
10678 
10688 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
10689 {
10690  QCPAxis *keyAxis = mKeyAxis.data();
10691  QCPAxis *valueAxis = mValueAxis.data();
10692  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
10693 
10694  if (keyAxis->orientation() == Qt::Horizontal)
10695  {
10696  key = keyAxis->pixelToCoord(x);
10697  value = valueAxis->pixelToCoord(y);
10698  } else
10699  {
10700  key = keyAxis->pixelToCoord(y);
10701  value = valueAxis->pixelToCoord(x);
10702  }
10703 }
10704 
10709 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
10710 {
10711  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
10712 }
10713 
10727 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
10728 {
10729  rescaleKeyAxis(onlyEnlarge);
10730  rescaleValueAxis(onlyEnlarge);
10731 }
10732 
10738 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
10739 {
10740  QCPAxis *keyAxis = mKeyAxis.data();
10741  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
10742 
10743  QCP::SignDomain signDomain = QCP::sdBoth;
10744  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
10745  signDomain = (keyAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
10746 
10747  bool foundRange;
10748  QCPRange newRange = getKeyRange(foundRange, signDomain);
10749  if (foundRange)
10750  {
10751  if (onlyEnlarge)
10752  newRange.expand(keyAxis->range());
10753  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
10754  {
10755  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
10756  if (keyAxis->scaleType() == QCPAxis::stLinear)
10757  {
10758  newRange.lower = center-keyAxis->range().size()/2.0;
10759  newRange.upper = center+keyAxis->range().size()/2.0;
10760  } else // scaleType() == stLogarithmic
10761  {
10762  newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
10763  newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
10764  }
10765  }
10766  keyAxis->setRange(newRange);
10767  }
10768 }
10769 
10780 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const
10781 {
10782  QCPAxis *keyAxis = mKeyAxis.data();
10783  QCPAxis *valueAxis = mValueAxis.data();
10784  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
10785 
10786  QCP::SignDomain signDomain = QCP::sdBoth;
10787  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
10788  signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
10789 
10790  bool foundRange;
10791  QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange());
10792  if (foundRange)
10793  {
10794  if (onlyEnlarge)
10795  newRange.expand(valueAxis->range());
10796  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
10797  {
10798  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
10799  if (valueAxis->scaleType() == QCPAxis::stLinear)
10800  {
10801  newRange.lower = center-valueAxis->range().size()/2.0;
10802  newRange.upper = center+valueAxis->range().size()/2.0;
10803  } else // scaleType() == stLogarithmic
10804  {
10805  newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
10806  newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
10807  }
10808  }
10809  valueAxis->setRange(newRange);
10810  }
10811 }
10812 
10828 {
10829  if (!legend)
10830  {
10831  qDebug() << Q_FUNC_INFO << "passed legend is null";
10832  return false;
10833  }
10834  if (legend->parentPlot() != mParentPlot)
10835  {
10836  qDebug() << Q_FUNC_INFO << "passed legend isn't in the same QCustomPlot as this plottable";
10837  return false;
10838  }
10839 
10840  if (!legend->hasItemWithPlottable(this))
10841  {
10842  legend->addItem(new QCPPlottableLegendItem(legend, this));
10843  return true;
10844  } else
10845  return false;
10846 }
10847 
10855 {
10856  if (!mParentPlot || !mParentPlot->legend)
10857  return false;
10858  else
10859  return addToLegend(mParentPlot->legend);
10860 }
10861 
10873 {
10874  if (!legend)
10875  {
10876  qDebug() << Q_FUNC_INFO << "passed legend is null";
10877  return false;
10878  }
10879 
10880  if (QCPPlottableLegendItem *lip = legend->itemWithPlottable(this))
10881  return legend->removeItem(lip);
10882  else
10883  return false;
10884 }
10885 
10893 {
10894  if (!mParentPlot || !mParentPlot->legend)
10895  return false;
10896  else
10898 }
10899 
10900 /* inherits documentation from base class */
10902 {
10903  if (mKeyAxis && mValueAxis)
10904  return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
10905  else
10906  return QRect();
10907 }
10908 
10909 /* inherits documentation from base class */
10911 {
10912  return QCP::iSelectPlottables;
10913 }
10914 
10931 {
10933 }
10934 
10947 {
10949 }
10950 
10963 {
10965 }
10966 
10967 /* inherits documentation from base class */
10968 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
10969 {
10970  Q_UNUSED(event)
10971 
10972  if (mSelectable != QCP::stNone)
10973  {
10974  QCPDataSelection newSelection = details.value<QCPDataSelection>();
10975  QCPDataSelection selectionBefore = mSelection;
10976  if (additive)
10977  {
10978  if (mSelectable == QCP::stWhole) // in whole selection mode, we toggle to no selection even if currently unselected point was hit
10979  {
10980  if (selected())
10982  else
10983  setSelection(newSelection);
10984  } else // in all other selection modes we toggle selections of homogeneously selected/unselected segments
10985  {
10986  if (mSelection.contains(newSelection)) // if entire newSelection is already selected, toggle selection
10987  setSelection(mSelection-newSelection);
10988  else
10989  setSelection(mSelection+newSelection);
10990  }
10991  } else
10992  setSelection(newSelection);
10993  if (selectionStateChanged)
10994  *selectionStateChanged = mSelection != selectionBefore;
10995  }
10996 }
10997 
10998 /* inherits documentation from base class */
10999 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
11000 {
11001  if (mSelectable != QCP::stNone)
11002  {
11003  QCPDataSelection selectionBefore = mSelection;
11005  if (selectionStateChanged)
11006  *selectionStateChanged = mSelection != selectionBefore;
11007  }
11008 }
11009 /* end of 'src/plottable.cpp' */
11010 
11011 
11012 /* including file 'src/item.cpp', size 49269 */
11013 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
11014 
11018 
11040 /* start documentation of inline functions */
11041 
11052 /* end documentation of inline functions */
11053 
11059 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId) :
11060  mName(name),
11061  mParentPlot(parentPlot),
11062  mParentItem(parentItem),
11063  mAnchorId(anchorId)
11064 {
11065 }
11066 
11068 {
11069  // unregister as parent at children:
11070  foreach (QCPItemPosition *child, mChildrenX.values())
11071  {
11072  if (child->parentAnchorX() == this)
11073  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
11074  }
11075  foreach (QCPItemPosition *child, mChildrenY.values())
11076  {
11077  if (child->parentAnchorY() == this)
11078  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
11079  }
11080 }
11081 
11089 {
11090  if (mParentItem)
11091  {
11092  if (mAnchorId > -1)
11093  {
11095  } else
11096  {
11097  qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
11098  return QPointF();
11099  }
11100  } else
11101  {
11102  qDebug() << Q_FUNC_INFO << "no parent item set";
11103  return QPointF();
11104  }
11105 }
11106 
11116 {
11117  if (!mChildrenX.contains(pos))
11118  mChildrenX.insert(pos);
11119  else
11120  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
11121 }
11122 
11130 {
11131  if (!mChildrenX.remove(pos))
11132  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
11133 }
11134 
11144 {
11145  if (!mChildrenY.contains(pos))
11146  mChildrenY.insert(pos);
11147  else
11148  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
11149 }
11150 
11158 {
11159  if (!mChildrenY.remove(pos))
11160  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
11161 }
11162 
11163 
11167 
11202 /* start documentation of inline functions */
11203 
11225 /* end documentation of inline functions */
11226 
11232 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name) :
11233  QCPItemAnchor(parentPlot, parentItem, name),
11234  mPositionTypeX(ptAbsolute),
11235  mPositionTypeY(ptAbsolute),
11236  mKey(0),
11237  mValue(0),
11238  mParentAnchorX(0),
11239  mParentAnchorY(0)
11240 {
11241 }
11242 
11244 {
11245  // unregister as parent at children:
11246  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
11247  // the setParentAnchor(0) call the correct QCPItemPosition::pixelPosition function instead of QCPItemAnchor::pixelPosition
11248  foreach (QCPItemPosition *child, mChildrenX.values())
11249  {
11250  if (child->parentAnchorX() == this)
11251  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
11252  }
11253  foreach (QCPItemPosition *child, mChildrenY.values())
11254  {
11255  if (child->parentAnchorY() == this)
11256  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
11257  }
11258  // unregister as child in parent:
11259  if (mParentAnchorX)
11261  if (mParentAnchorY)
11263 }
11264 
11265 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
11267 {
11268  return mAxisRect.data();
11269 }
11270 
11297 {
11298  setTypeX(type);
11299  setTypeY(type);
11300 }
11301 
11310 {
11311  if (mPositionTypeX != type)
11312  {
11313  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
11314  // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning.
11315  bool retainPixelPosition = true;
11316  if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
11317  retainPixelPosition = false;
11318  if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
11319  retainPixelPosition = false;
11320 
11321  QPointF pixel;
11322  if (retainPixelPosition)
11323  pixel = pixelPosition();
11324 
11325  mPositionTypeX = type;
11326 
11327  if (retainPixelPosition)
11328  setPixelPosition(pixel);
11329  }
11330 }
11331 
11340 {
11341  if (mPositionTypeY != type)
11342  {
11343  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
11344  // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning.
11345  bool retainPixelPosition = true;
11346  if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
11347  retainPixelPosition = false;
11348  if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
11349  retainPixelPosition = false;
11350 
11351  QPointF pixel;
11352  if (retainPixelPosition)
11353  pixel = pixelPosition();
11354 
11355  mPositionTypeY = type;
11356 
11357  if (retainPixelPosition)
11358  setPixelPosition(pixel);
11359  }
11360 }
11361 
11381 {
11382  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
11383  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
11384  return successX && successY;
11385 }
11386 
11395 {
11396  // make sure self is not assigned as parent:
11397  if (parentAnchor == this)
11398  {
11399  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
11400  return false;
11401  }
11402  // make sure no recursive parent-child-relationships are created:
11403  QCPItemAnchor *currentParent = parentAnchor;
11404  while (currentParent)
11405  {
11406  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
11407  {
11408  // is a QCPItemPosition, might have further parent, so keep iterating
11409  if (currentParentPos == this)
11410  {
11411  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
11412  return false;
11413  }
11414  currentParent = currentParentPos->parentAnchorX();
11415  } else
11416  {
11417  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
11418  // same, to prevent a position being child of an anchor which itself depends on the position,
11419  // because they're both on the same item:
11420  if (currentParent->mParentItem == mParentItem)
11421  {
11422  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
11423  return false;
11424  }
11425  break;
11426  }
11427  }
11428 
11429  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
11432 
11433  // save pixel position:
11434  QPointF pixelP;
11435  if (keepPixelPosition)
11436  pixelP = pixelPosition();
11437  // unregister at current parent anchor:
11438  if (mParentAnchorX)
11440  // register at new parent anchor:
11441  if (parentAnchor)
11442  parentAnchor->addChildX(this);
11444  // restore pixel position under new parent:
11445  if (keepPixelPosition)
11446  setPixelPosition(pixelP);
11447  else
11448  setCoords(0, coords().y());
11449  return true;
11450 }
11451 
11460 {
11461  // make sure self is not assigned as parent:
11462  if (parentAnchor == this)
11463  {
11464  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
11465  return false;
11466  }
11467  // make sure no recursive parent-child-relationships are created:
11468  QCPItemAnchor *currentParent = parentAnchor;
11469  while (currentParent)
11470  {
11471  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
11472  {
11473  // is a QCPItemPosition, might have further parent, so keep iterating
11474  if (currentParentPos == this)
11475  {
11476  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
11477  return false;
11478  }
11479  currentParent = currentParentPos->parentAnchorY();
11480  } else
11481  {
11482  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
11483  // same, to prevent a position being child of an anchor which itself depends on the position,
11484  // because they're both on the same item:
11485  if (currentParent->mParentItem == mParentItem)
11486  {
11487  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
11488  return false;
11489  }
11490  break;
11491  }
11492  }
11493 
11494  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
11497 
11498  // save pixel position:
11499  QPointF pixelP;
11500  if (keepPixelPosition)
11501  pixelP = pixelPosition();
11502  // unregister at current parent anchor:
11503  if (mParentAnchorY)
11505  // register at new parent anchor:
11506  if (parentAnchor)
11507  parentAnchor->addChildY(this);
11509  // restore pixel position under new parent:
11510  if (keepPixelPosition)
11511  setPixelPosition(pixelP);
11512  else
11513  setCoords(coords().x(), 0);
11514  return true;
11515 }
11516 
11535 {
11536  mKey = key;
11537  mValue = value;
11538 }
11539 
11545 void QCPItemPosition::setCoords(const QPointF &pos)
11546 {
11547  setCoords(pos.x(), pos.y());
11548 }
11549 
11557 {
11558  QPointF result;
11559 
11560  // determine X:
11561  switch (mPositionTypeX)
11562  {
11563  case ptAbsolute:
11564  {
11565  result.rx() = mKey;
11566  if (mParentAnchorX)
11567  result.rx() += mParentAnchorX->pixelPosition().x();
11568  break;
11569  }
11570  case ptViewportRatio:
11571  {
11572  result.rx() = mKey*mParentPlot->viewport().width();
11573  if (mParentAnchorX)
11574  result.rx() += mParentAnchorX->pixelPosition().x();
11575  else
11576  result.rx() += mParentPlot->viewport().left();
11577  break;
11578  }
11579  case ptAxisRectRatio:
11580  {
11581  if (mAxisRect)
11582  {
11583  result.rx() = mKey*mAxisRect.data()->width();
11584  if (mParentAnchorX)
11585  result.rx() += mParentAnchorX->pixelPosition().x();
11586  else
11587  result.rx() += mAxisRect.data()->left();
11588  } else
11589  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
11590  break;
11591  }
11592  case ptPlotCoords:
11593  {
11594  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
11595  result.rx() = mKeyAxis.data()->coordToPixel(mKey);
11596  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
11597  result.rx() = mValueAxis.data()->coordToPixel(mValue);
11598  else
11599  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
11600  break;
11601  }
11602  }
11603 
11604  // determine Y:
11605  switch (mPositionTypeY)
11606  {
11607  case ptAbsolute:
11608  {
11609  result.ry() = mValue;
11610  if (mParentAnchorY)
11611  result.ry() += mParentAnchorY->pixelPosition().y();
11612  break;
11613  }
11614  case ptViewportRatio:
11615  {
11616  result.ry() = mValue*mParentPlot->viewport().height();
11617  if (mParentAnchorY)
11618  result.ry() += mParentAnchorY->pixelPosition().y();
11619  else
11620  result.ry() += mParentPlot->viewport().top();
11621  break;
11622  }
11623  case ptAxisRectRatio:
11624  {
11625  if (mAxisRect)
11626  {
11627  result.ry() = mValue*mAxisRect.data()->height();
11628  if (mParentAnchorY)
11629  result.ry() += mParentAnchorY->pixelPosition().y();
11630  else
11631  result.ry() += mAxisRect.data()->top();
11632  } else
11633  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
11634  break;
11635  }
11636  case ptPlotCoords:
11637  {
11638  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
11639  result.ry() = mKeyAxis.data()->coordToPixel(mKey);
11640  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
11641  result.ry() = mValueAxis.data()->coordToPixel(mValue);
11642  else
11643  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
11644  break;
11645  }
11646  }
11647 
11648  return result;
11649 }
11650 
11657 {
11658  mKeyAxis = keyAxis;
11660 }
11661 
11668 {
11669  mAxisRect = axisRect;
11670 }
11671 
11683 {
11684  double x = pixelPosition.x();
11685  double y = pixelPosition.y();
11686 
11687  switch (mPositionTypeX)
11688  {
11689  case ptAbsolute:
11690  {
11691  if (mParentAnchorX)
11692  x -= mParentAnchorX->pixelPosition().x();
11693  break;
11694  }
11695  case ptViewportRatio:
11696  {
11697  if (mParentAnchorX)
11698  x -= mParentAnchorX->pixelPosition().x();
11699  else
11700  x -= mParentPlot->viewport().left();
11701  x /= (double)mParentPlot->viewport().width();
11702  break;
11703  }
11704  case ptAxisRectRatio:
11705  {
11706  if (mAxisRect)
11707  {
11708  if (mParentAnchorX)
11709  x -= mParentAnchorX->pixelPosition().x();
11710  else
11711  x -= mAxisRect.data()->left();
11712  x /= (double)mAxisRect.data()->width();
11713  } else
11714  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
11715  break;
11716  }
11717  case ptPlotCoords:
11718  {
11719  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
11720  x = mKeyAxis.data()->pixelToCoord(x);
11721  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
11722  y = mValueAxis.data()->pixelToCoord(x);
11723  else
11724  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
11725  break;
11726  }
11727  }
11728 
11729  switch (mPositionTypeY)
11730  {
11731  case ptAbsolute:
11732  {
11733  if (mParentAnchorY)
11734  y -= mParentAnchorY->pixelPosition().y();
11735  break;
11736  }
11737  case ptViewportRatio:
11738  {
11739  if (mParentAnchorY)
11740  y -= mParentAnchorY->pixelPosition().y();
11741  else
11742  y -= mParentPlot->viewport().top();
11743  y /= (double)mParentPlot->viewport().height();
11744  break;
11745  }
11746  case ptAxisRectRatio:
11747  {
11748  if (mAxisRect)
11749  {
11750  if (mParentAnchorY)
11751  y -= mParentAnchorY->pixelPosition().y();
11752  else
11753  y -= mAxisRect.data()->top();
11754  y /= (double)mAxisRect.data()->height();
11755  } else
11756  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
11757  break;
11758  }
11759  case ptPlotCoords:
11760  {
11761  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
11762  x = mKeyAxis.data()->pixelToCoord(y);
11763  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
11764  y = mValueAxis.data()->pixelToCoord(y);
11765  else
11766  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
11767  break;
11768  }
11769  }
11770 
11771  setCoords(x, y);
11772 }
11773 
11774 
11778 
11904 /* start of documentation of inline functions */
11905 
11921 /* end of documentation of inline functions */
11922 /* start documentation of pure virtual functions */
11923 
11934 /* end documentation of pure virtual functions */
11935 /* start documentation of signals */
11936 
11942 /* end documentation of signals */
11943 
11948  QCPLayerable(parentPlot),
11949  mClipToAxisRect(false),
11950  mSelectable(true),
11951  mSelected(false)
11952 {
11953  parentPlot->registerItem(this);
11954 
11955  QList<QCPAxisRect*> rects = parentPlot->axisRects();
11956  if (rects.size() > 0)
11957  {
11958  setClipToAxisRect(true);
11959  setClipAxisRect(rects.first());
11960  }
11961 }
11962 
11964 {
11965  // don't delete mPositions because every position is also an anchor and thus in mAnchors
11966  qDeleteAll(mAnchors);
11967 }
11968 
11969 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
11971 {
11972  return mClipAxisRect.data();
11973 }
11974 
11982 {
11983  mClipToAxisRect = clip;
11984  if (mClipToAxisRect)
11986 }
11987 
11995 {
11996  mClipAxisRect = rect;
11997  if (mClipToAxisRect)
11999 }
12000 
12011 {
12012  if (mSelectable != selectable)
12013  {
12016  }
12017 }
12018 
12034 {
12035  if (mSelected != selected)
12036  {
12037  mSelected = selected;
12039  }
12040 }
12041 
12052 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
12053 {
12054  for (int i=0; i<mPositions.size(); ++i)
12055  {
12056  if (mPositions.at(i)->name() == name)
12057  return mPositions.at(i);
12058  }
12059  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
12060  return 0;
12061 }
12062 
12073 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
12074 {
12075  for (int i=0; i<mAnchors.size(); ++i)
12076  {
12077  if (mAnchors.at(i)->name() == name)
12078  return mAnchors.at(i);
12079  }
12080  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
12081  return 0;
12082 }
12083 
12092 bool QCPAbstractItem::hasAnchor(const QString &name) const
12093 {
12094  for (int i=0; i<mAnchors.size(); ++i)
12095  {
12096  if (mAnchors.at(i)->name() == name)
12097  return true;
12098  }
12099  return false;
12100 }
12101 
12112 {
12114  return mClipAxisRect.data()->rect();
12115  else
12116  return mParentPlot->viewport();
12117 }
12118 
12133 {
12135 }
12136 
12150 double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const
12151 {
12152  double result = -1;
12153 
12154  // distance to border:
12155  QList<QLineF> lines;
12156  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
12157  << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
12158  double minDistSqr = std::numeric_limits<double>::max();
12159  for (int i=0; i<lines.size(); ++i)
12160  {
12161  double distSqr = QCPVector2D(pos).distanceSquaredToLine(lines.at(i).p1(), lines.at(i).p2());
12162  if (distSqr < minDistSqr)
12163  minDistSqr = distSqr;
12164  }
12165  result = qSqrt(minDistSqr);
12166 
12167  // filled rect, allow click inside to count as hit:
12168  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
12169  {
12170  if (rect.contains(pos))
12171  result = mParentPlot->selectionTolerance()*0.99;
12172  }
12173  return result;
12174 }
12175 
12186 QPointF QCPAbstractItem::anchorPixelPosition(int anchorId) const
12187 {
12188  qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
12189  return QPointF();
12190 }
12191 
12207 {
12208  if (hasAnchor(name))
12209  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
12210  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
12211  mPositions.append(newPosition);
12212  mAnchors.append(newPosition); // every position is also an anchor
12213  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
12214  newPosition->setType(QCPItemPosition::ptPlotCoords);
12215  if (mParentPlot->axisRect())
12216  newPosition->setAxisRect(mParentPlot->axisRect());
12217  newPosition->setCoords(0, 0);
12218  return newPosition;
12219 }
12220 
12240 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
12241 {
12242  if (hasAnchor(name))
12243  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
12244  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
12245  mAnchors.append(newAnchor);
12246  return newAnchor;
12247 }
12248 
12249 /* inherits documentation from base class */
12250 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12251 {
12252  Q_UNUSED(event)
12253  Q_UNUSED(details)
12254  if (mSelectable)
12255  {
12256  bool selBefore = mSelected;
12257  setSelected(additive ? !mSelected : true);
12258  if (selectionStateChanged)
12259  *selectionStateChanged = mSelected != selBefore;
12260  }
12261 }
12262 
12263 /* inherits documentation from base class */
12264 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
12265 {
12266  if (mSelectable)
12267  {
12268  bool selBefore = mSelected;
12269  setSelected(false);
12270  if (selectionStateChanged)
12271  *selectionStateChanged = mSelected != selBefore;
12272  }
12273 }
12274 
12275 /* inherits documentation from base class */
12277 {
12278  return QCP::iSelectItems;
12279 }
12280 /* end of 'src/item.cpp' */
12281 
12282 
12283 /* including file 'src/core.cpp', size 124243 */
12284 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
12285 
12289 
12299 /* start of documentation of inline functions */
12300 
12315 /* end of documentation of inline functions */
12316 /* start of documentation of signals */
12317 
12488 /* end of documentation of signals */
12489 /* start of documentation of public members */
12490 
12587 /* end of documentation of public members */
12588 
12592 QCustomPlot::QCustomPlot(QWidget *parent) :
12593  QWidget(parent),
12594  xAxis(0),
12595  yAxis(0),
12596  xAxis2(0),
12597  yAxis2(0),
12598  legend(0),
12599  mBufferDevicePixelRatio(1.0), // will be adapted to primary screen below
12600  mPlotLayout(0),
12601  mAutoAddPlottableToLegend(true),
12602  mAntialiasedElements(QCP::aeNone),
12603  mNotAntialiasedElements(QCP::aeNone),
12604  mInteractions(0),
12605  mSelectionTolerance(8),
12606  mNoAntialiasingOnDrag(false),
12607  mBackgroundBrush(Qt::white, Qt::SolidPattern),
12608  mBackgroundScaled(true),
12609  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
12610  mCurrentLayer(0),
12611  mPlottingHints(QCP::phCacheLabels|QCP::phImmediateRefresh),
12612  mMultiSelectModifier(Qt::ControlModifier),
12613  mSelectionRectMode(QCP::srmNone),
12614  mSelectionRect(0),
12615  mOpenGl(false),
12616  mMouseHasMoved(false),
12617  mMouseEventLayerable(0),
12618  mReplotting(false),
12619  mReplotQueued(false),
12620  mOpenGlMultisamples(16),
12621  mOpenGlAntialiasedElementsBackup(QCP::aeNone),
12622  mOpenGlCacheLabelsBackup(true)
12623 {
12624  setAttribute(Qt::WA_NoMousePropagation);
12625  setAttribute(Qt::WA_OpaquePaintEvent);
12626  setFocusPolicy(Qt::ClickFocus);
12627  setMouseTracking(true);
12628  QLocale currentLocale = locale();
12629  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
12630  setLocale(currentLocale);
12631 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
12632  setBufferDevicePixelRatio(QWidget::devicePixelRatio());
12633 #endif
12634 
12637  // create initial layers:
12638  mLayers.append(new QCPLayer(this, QLatin1String("background")));
12639  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
12640  mLayers.append(new QCPLayer(this, QLatin1String("main")));
12641  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
12642  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
12643  mLayers.append(new QCPLayer(this, QLatin1String("overlay")));
12645  setCurrentLayer(QLatin1String("main"));
12646  layer(QLatin1String("overlay"))->setMode(QCPLayer::lmBuffered);
12647 
12648  // create initial layout, axis rect and legend:
12649  mPlotLayout = new QCPLayoutGrid;
12651  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
12652  mPlotLayout->setLayer(QLatin1String("main"));
12653  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
12654  mPlotLayout->addElement(0, 0, defaultAxisRect);
12655  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
12656  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
12657  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
12658  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
12659  legend = new QCPLegend;
12660  legend->setVisible(false);
12661  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
12662  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
12663 
12664  defaultAxisRect->setLayer(QLatin1String("background"));
12665  xAxis->setLayer(QLatin1String("axes"));
12666  yAxis->setLayer(QLatin1String("axes"));
12667  xAxis2->setLayer(QLatin1String("axes"));
12668  yAxis2->setLayer(QLatin1String("axes"));
12669  xAxis->grid()->setLayer(QLatin1String("grid"));
12670  yAxis->grid()->setLayer(QLatin1String("grid"));
12671  xAxis2->grid()->setLayer(QLatin1String("grid"));
12672  yAxis2->grid()->setLayer(QLatin1String("grid"));
12673  legend->setLayer(QLatin1String("legend"));
12674 
12675  // create selection rect instance:
12676  mSelectionRect = new QCPSelectionRect(this);
12677  mSelectionRect->setLayer(QLatin1String("overlay"));
12678 
12679  setViewport(rect()); // needs to be called after mPlotLayout has been created
12680 
12682 }
12683 
12685 {
12686  clearPlottables();
12687  clearItems();
12688 
12689  if (mPlotLayout)
12690  {
12691  delete mPlotLayout;
12692  mPlotLayout = 0;
12693  }
12694 
12695  mCurrentLayer = 0;
12696  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
12697  mLayers.clear();
12698 }
12699 
12717 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
12718 {
12720 
12721  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
12723  mNotAntialiasedElements |= ~mAntialiasedElements;
12724 }
12725 
12733 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
12734 {
12735  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
12736  mAntialiasedElements &= ~antialiasedElement;
12737  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
12738  mAntialiasedElements |= antialiasedElement;
12739 
12740  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
12742  mNotAntialiasedElements |= ~mAntialiasedElements;
12743 }
12744 
12764 {
12766 
12767  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
12769  mAntialiasedElements |= ~mNotAntialiasedElements;
12770 }
12771 
12779 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
12780 {
12781  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
12782  mNotAntialiasedElements &= ~notAntialiasedElement;
12783  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
12784  mNotAntialiasedElements |= notAntialiasedElement;
12785 
12786  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
12788  mAntialiasedElements |= ~mNotAntialiasedElements;
12789 }
12790 
12798 {
12800 }
12801 
12856 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
12857 {
12859 }
12860 
12868 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
12869 {
12870  if (!enabled && mInteractions.testFlag(interaction))
12871  mInteractions &= ~interaction;
12872  else if (enabled && !mInteractions.testFlag(interaction))
12873  mInteractions |= interaction;
12874 }
12875 
12890 {
12891  mSelectionTolerance = pixels;
12892 }
12893 
12904 {
12905  mNoAntialiasingOnDrag = enabled;
12906 }
12907 
12913 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
12914 {
12915  mPlottingHints = hints;
12916 }
12917 
12924 {
12925  QCP::PlottingHints newHints = mPlottingHints;
12926  if (!enabled)
12927  newHints &= ~hint;
12928  else
12929  newHints |= hint;
12930 
12931  if (newHints != mPlottingHints)
12932  setPlottingHints(newHints);
12933 }
12934 
12945 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
12946 {
12947  mMultiSelectModifier = modifier;
12948 }
12949 
12971 {
12972  if (mSelectionRect)
12973  {
12974  if (mode == QCP::srmNone)
12975  mSelectionRect->cancel(); // when switching to none, we immediately want to abort a potentially active selection rect
12976 
12977  // disconnect old connections:
12979  disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
12980  else if (mSelectionRectMode == QCP::srmZoom)
12981  disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
12982 
12983  // establish new ones:
12984  if (mode == QCP::srmSelect)
12985  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
12986  else if (mode == QCP::srmZoom)
12987  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
12988  }
12989 
12991 }
12992 
13004 {
13005  if (mSelectionRect)
13006  delete mSelectionRect;
13007 
13009 
13010  if (mSelectionRect)
13011  {
13012  // establish connections with new selection rect:
13014  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
13015  else if (mSelectionRectMode == QCP::srmZoom)
13016  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
13017  }
13018 }
13019 
13052 void QCustomPlot::setOpenGl(bool enabled, int multisampling)
13053 {
13054  mOpenGlMultisamples = qMax(0, multisampling);
13055 #ifdef QCUSTOMPLOT_USE_OPENGL
13056  mOpenGl = enabled;
13057  if (mOpenGl)
13058  {
13059  if (setupOpenGl())
13060  {
13061  // backup antialiasing override and labelcaching setting so we can restore upon disabling OpenGL
13064  // set antialiasing override to antialias all (aligns gl pixel grid properly), and disable label caching (would use software rasterizer for pixmap caches):
13067  } else
13068  {
13069  qDebug() << Q_FUNC_INFO << "Failed to enable OpenGL, continuing plotting without hardware acceleration.";
13070  mOpenGl = false;
13071  }
13072  } else
13073  {
13074  // restore antialiasing override and labelcaching to what it was before enabling OpenGL, if nobody changed it in the meantime:
13077  if (!mPlottingHints.testFlag(QCP::phCacheLabels))
13079  freeOpenGl();
13080  }
13081  // recreate all paint buffers:
13082  mPaintBuffers.clear();
13084 #else
13085  Q_UNUSED(enabled)
13086  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)";
13087 #endif
13088 }
13089 
13106 void QCustomPlot::setViewport(const QRect &rect)
13107 {
13108  mViewport = rect;
13109  if (mPlotLayout)
13111 }
13112 
13125 {
13126  if (!qFuzzyCompare(ratio, mBufferDevicePixelRatio))
13127  {
13128 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
13129  mBufferDevicePixelRatio = ratio;
13130  for (int i=0; i<mPaintBuffers.size(); ++i)
13131  mPaintBuffers.at(i)->setDevicePixelRatio(mBufferDevicePixelRatio);
13132  // Note: axis label cache has devicePixelRatio as part of cache hash, so no need to manually clear cache here
13133 #else
13134  qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
13136 #endif
13137  }
13138 }
13139 
13155 void QCustomPlot::setBackground(const QPixmap &pm)
13156 {
13157  mBackgroundPixmap = pm;
13158  mScaledBackgroundPixmap = QPixmap();
13159 }
13160 
13174 void QCustomPlot::setBackground(const QBrush &brush)
13175 {
13176  mBackgroundBrush = brush;
13177 }
13178 
13186 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
13187 {
13188  mBackgroundPixmap = pm;
13189  mScaledBackgroundPixmap = QPixmap();
13190  mBackgroundScaled = scaled;
13192 }
13193 
13205 {
13206  mBackgroundScaled = scaled;
13207 }
13208 
13216 {
13218 }
13219 
13229 {
13230  if (index >= 0 && index < mPlottables.size())
13231  {
13232  return mPlottables.at(index);
13233  } else
13234  {
13235  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13236  return 0;
13237  }
13238 }
13239 
13248 {
13249  if (!mPlottables.isEmpty())
13250  {
13251  return mPlottables.last();
13252  } else
13253  return 0;
13254 }
13255 
13265 {
13266  if (!mPlottables.contains(plottable))
13267  {
13268  qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
13269  return false;
13270  }
13271 
13272  // remove plottable from legend:
13273  plottable->removeFromLegend();
13274  // special handling for QCPGraphs to maintain the simple graph interface:
13275  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
13276  mGraphs.removeOne(graph);
13277  // remove plottable:
13278  delete plottable;
13279  mPlottables.removeOne(plottable);
13280  return true;
13281 }
13282 
13288 {
13289  if (index >= 0 && index < mPlottables.size())
13290  return removePlottable(mPlottables[index]);
13291  else
13292  {
13293  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13294  return false;
13295  }
13296 }
13297 
13307 {
13308  int c = mPlottables.size();
13309  for (int i=c-1; i >= 0; --i)
13311  return c;
13312 }
13313 
13320 {
13321  return mPlottables.size();
13322 }
13323 
13331 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
13332 {
13333  QList<QCPAbstractPlottable*> result;
13335  {
13336  if (plottable->selected())
13337  result.append(plottable);
13338  }
13339  return result;
13340 }
13341 
13354 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
13355 {
13356  QCPAbstractPlottable *resultPlottable = 0;
13357  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
13358 
13360  {
13361  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
13362  continue;
13363  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
13364  {
13365  double currentDistance = plottable->selectTest(pos, false);
13366  if (currentDistance >= 0 && currentDistance < resultDistance)
13367  {
13368  resultPlottable = plottable;
13369  resultDistance = currentDistance;
13370  }
13371  }
13372  }
13373 
13374  return resultPlottable;
13375 }
13376 
13381 {
13382  return mPlottables.contains(plottable);
13383 }
13384 
13393 QCPGraph *QCustomPlot::graph(int index) const
13394 {
13395  if (index >= 0 && index < mGraphs.size())
13396  {
13397  return mGraphs.at(index);
13398  } else
13399  {
13400  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13401  return 0;
13402  }
13403 }
13404 
13413 {
13414  if (!mGraphs.isEmpty())
13415  {
13416  return mGraphs.last();
13417  } else
13418  return 0;
13419 }
13420 
13434 {
13435  if (!keyAxis) keyAxis = xAxis;
13436  if (!valueAxis) valueAxis = yAxis;
13437  if (!keyAxis || !valueAxis)
13438  {
13439  qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
13440  return 0;
13441  }
13442  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
13443  {
13444  qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
13445  return 0;
13446  }
13447 
13448  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
13449  newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
13450  return newGraph;
13451 }
13452 
13464 {
13465  return removePlottable(graph);
13466 }
13467 
13473 {
13474  if (index >= 0 && index < mGraphs.size())
13475  return removeGraph(mGraphs[index]);
13476  else
13477  return false;
13478 }
13479 
13489 {
13490  int c = mGraphs.size();
13491  for (int i=c-1; i >= 0; --i)
13492  removeGraph(mGraphs[i]);
13493  return c;
13494 }
13495 
13502 {
13503  return mGraphs.size();
13504 }
13505 
13514 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
13515 {
13516  QList<QCPGraph*> result;
13517  foreach (QCPGraph *graph, mGraphs)
13518  {
13519  if (graph->selected())
13520  result.append(graph);
13521  }
13522  return result;
13523 }
13524 
13534 {
13535  if (index >= 0 && index < mItems.size())
13536  {
13537  return mItems.at(index);
13538  } else
13539  {
13540  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13541  return 0;
13542  }
13543 }
13544 
13553 {
13554  if (!mItems.isEmpty())
13555  {
13556  return mItems.last();
13557  } else
13558  return 0;
13559 }
13560 
13569 {
13570  if (mItems.contains(item))
13571  {
13572  delete item;
13573  mItems.removeOne(item);
13574  return true;
13575  } else
13576  {
13577  qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
13578  return false;
13579  }
13580 }
13581 
13587 {
13588  if (index >= 0 && index < mItems.size())
13589  return removeItem(mItems[index]);
13590  else
13591  {
13592  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13593  return false;
13594  }
13595 }
13596 
13605 {
13606  int c = mItems.size();
13607  for (int i=c-1; i >= 0; --i)
13608  removeItem(mItems[i]);
13609  return c;
13610 }
13611 
13618 {
13619  return mItems.size();
13620 }
13621 
13627 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
13628 {
13629  QList<QCPAbstractItem*> result;
13630  foreach (QCPAbstractItem *item, mItems)
13631  {
13632  if (item->selected())
13633  result.append(item);
13634  }
13635  return result;
13636 }
13637 
13651 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
13652 {
13653  QCPAbstractItem *resultItem = 0;
13654  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
13655 
13656  foreach (QCPAbstractItem *item, mItems)
13657  {
13658  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
13659  continue;
13660  if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
13661  {
13662  double currentDistance = item->selectTest(pos, false);
13663  if (currentDistance >= 0 && currentDistance < resultDistance)
13664  {
13665  resultItem = item;
13666  resultDistance = currentDistance;
13667  }
13668  }
13669  }
13670 
13671  return resultItem;
13672 }
13673 
13680 {
13681  return mItems.contains(item);
13682 }
13683 
13692 QCPLayer *QCustomPlot::layer(const QString &name) const
13693 {
13694  foreach (QCPLayer *layer, mLayers)
13695  {
13696  if (layer->name() == name)
13697  return layer;
13698  }
13699  return 0;
13700 }
13701 
13708 QCPLayer *QCustomPlot::layer(int index) const
13709 {
13710  if (index >= 0 && index < mLayers.size())
13711  {
13712  return mLayers.at(index);
13713  } else
13714  {
13715  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13716  return 0;
13717  }
13718 }
13719 
13724 {
13725  return mCurrentLayer;
13726 }
13727 
13738 bool QCustomPlot::setCurrentLayer(const QString &name)
13739 {
13740  if (QCPLayer *newCurrentLayer = layer(name))
13741  {
13742  return setCurrentLayer(newCurrentLayer);
13743  } else
13744  {
13745  qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
13746  return false;
13747  }
13748 }
13749 
13759 {
13760  if (!mLayers.contains(layer))
13761  {
13762  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
13763  return false;
13764  }
13765 
13766  mCurrentLayer = layer;
13767  return true;
13768 }
13769 
13776 {
13777  return mLayers.size();
13778 }
13779 
13793 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
13794 {
13795  if (!otherLayer)
13796  otherLayer = mLayers.last();
13797  if (!mLayers.contains(otherLayer))
13798  {
13799  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
13800  return false;
13801  }
13802  if (layer(name))
13803  {
13804  qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
13805  return false;
13806  }
13807 
13808  QCPLayer *newLayer = new QCPLayer(this, name);
13809  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
13811  setupPaintBuffers(); // associates new layer with the appropriate paint buffer
13812  return true;
13813 }
13814 
13830 {
13831  if (!mLayers.contains(layer))
13832  {
13833  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
13834  return false;
13835  }
13836  if (mLayers.size() < 2)
13837  {
13838  qDebug() << Q_FUNC_INFO << "can't remove last layer";
13839  return false;
13840  }
13841 
13842  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
13843  int removedIndex = layer->index();
13844  bool isFirstLayer = removedIndex==0;
13845  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
13846  QList<QCPLayerable*> children = layer->children();
13847  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
13848  {
13849  for (int i=children.size()-1; i>=0; --i)
13850  children.at(i)->moveToLayer(targetLayer, true);
13851  } else // append normally
13852  {
13853  for (int i=0; i<children.size(); ++i)
13854  children.at(i)->moveToLayer(targetLayer, false);
13855  }
13856  // if removed layer is current layer, change current layer to layer below/above:
13857  if (layer == mCurrentLayer)
13858  setCurrentLayer(targetLayer);
13859  // invalidate the paint buffer that was responsible for this layer:
13860  if (!layer->mPaintBuffer.isNull())
13861  layer->mPaintBuffer.toStrongRef()->setInvalidated();
13862  // remove layer:
13863  delete layer;
13864  mLayers.removeOne(layer);
13866  return true;
13867 }
13868 
13879 {
13880  if (!mLayers.contains(layer))
13881  {
13882  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
13883  return false;
13884  }
13885  if (!mLayers.contains(otherLayer))
13886  {
13887  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
13888  return false;
13889  }
13890 
13891  if (layer->index() > otherLayer->index())
13892  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
13893  else if (layer->index() < otherLayer->index())
13894  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1));
13895 
13896  // invalidate the paint buffers that are responsible for the layers:
13897  if (!layer->mPaintBuffer.isNull())
13898  layer->mPaintBuffer.toStrongRef()->setInvalidated();
13899  if (!otherLayer->mPaintBuffer.isNull())
13900  otherLayer->mPaintBuffer.toStrongRef()->setInvalidated();
13901 
13903  return true;
13904 }
13905 
13916 {
13917  return axisRects().size();
13918 }
13919 
13930 {
13931  const QList<QCPAxisRect*> rectList = axisRects();
13932  if (index >= 0 && index < rectList.size())
13933  {
13934  return rectList.at(index);
13935  } else
13936  {
13937  qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
13938  return 0;
13939  }
13940 }
13941 
13947 QList<QCPAxisRect*> QCustomPlot::axisRects() const
13948 {
13949  QList<QCPAxisRect*> result;
13950  QStack<QCPLayoutElement*> elementStack;
13951  if (mPlotLayout)
13952  elementStack.push(mPlotLayout);
13953 
13954  while (!elementStack.isEmpty())
13955  {
13956  foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
13957  {
13958  if (element)
13959  {
13960  elementStack.push(element);
13961  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
13962  result.append(ar);
13963  }
13964  }
13965  }
13966 
13967  return result;
13968 }
13969 
13980 {
13981  QCPLayoutElement *currentElement = mPlotLayout;
13982  bool searchSubElements = true;
13983  while (searchSubElements && currentElement)
13984  {
13985  searchSubElements = false;
13986  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
13987  {
13988  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
13989  {
13990  currentElement = subElement;
13991  searchSubElements = true;
13992  break;
13993  }
13994  }
13995  }
13996  return currentElement;
13997 }
13998 
14009 QCPAxisRect *QCustomPlot::axisRectAt(const QPointF &pos) const
14010 {
14011  QCPAxisRect *result = 0;
14012  QCPLayoutElement *currentElement = mPlotLayout;
14013  bool searchSubElements = true;
14014  while (searchSubElements && currentElement)
14015  {
14016  searchSubElements = false;
14017  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
14018  {
14019  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
14020  {
14021  currentElement = subElement;
14022  searchSubElements = true;
14023  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(currentElement))
14024  result = ar;
14025  break;
14026  }
14027  }
14028  }
14029  return result;
14030 }
14031 
14039 QList<QCPAxis*> QCustomPlot::selectedAxes() const
14040 {
14041  QList<QCPAxis*> result, allAxes;
14042  foreach (QCPAxisRect *rect, axisRects())
14043  allAxes << rect->axes();
14044 
14045  foreach (QCPAxis *axis, allAxes)
14046  {
14047  if (axis->selectedParts() != QCPAxis::spNone)
14048  result.append(axis);
14049  }
14050 
14051  return result;
14052 }
14053 
14061 QList<QCPLegend*> QCustomPlot::selectedLegends() const
14062 {
14063  QList<QCPLegend*> result;
14064 
14065  QStack<QCPLayoutElement*> elementStack;
14066  if (mPlotLayout)
14067  elementStack.push(mPlotLayout);
14068 
14069  while (!elementStack.isEmpty())
14070  {
14071  foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
14072  {
14073  if (subElement)
14074  {
14075  elementStack.push(subElement);
14076  if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
14077  {
14078  if (leg->selectedParts() != QCPLegend::spNone)
14079  result.append(leg);
14080  }
14081  }
14082  }
14083  }
14084 
14085  return result;
14086 }
14087 
14098 {
14099  foreach (QCPLayer *layer, mLayers)
14100  {
14101  foreach (QCPLayerable *layerable, layer->children())
14102  layerable->deselectEvent(0);
14103  }
14104 }
14105 
14132 {
14133  if (refreshPriority == QCustomPlot::rpQueuedReplot)
14134  {
14135  if (!mReplotQueued)
14136  {
14137  mReplotQueued = true;
14138  QTimer::singleShot(0, this, SLOT(replot()));
14139  }
14140  return;
14141  }
14142 
14143  if (mReplotting) // incase signals loop back to replot slot
14144  return;
14145  mReplotting = true;
14146  mReplotQueued = false;
14147  emit beforeReplot();
14148 
14149  updateLayout();
14150  // draw all layered objects (grid, axes, plottables, items, legend,...) into their buffers:
14152  foreach (QCPLayer *layer, mLayers)
14153  layer->drawToPaintBuffer();
14154  for (int i=0; i<mPaintBuffers.size(); ++i)
14155  mPaintBuffers.at(i)->setInvalidated(false);
14156 
14157  if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority==rpImmediateRefresh)
14158  repaint();
14159  else
14160  update();
14161 
14162  emit afterReplot();
14163  mReplotting = false;
14164 }
14165 
14174 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
14175 {
14176  QList<QCPAxis*> allAxes;
14177  foreach (QCPAxisRect *rect, axisRects())
14178  allAxes << rect->axes();
14179 
14180  foreach (QCPAxis *axis, allAxes)
14181  axis->rescale(onlyVisiblePlottables);
14182 }
14183 
14221 bool QCustomPlot::savePdf(const QString &fileName, int width, int height, QCP::ExportPen exportPen, const QString &pdfCreator, const QString &pdfTitle)
14222 {
14223  bool success = false;
14224 #ifdef QT_NO_PRINTER
14225  Q_UNUSED(fileName)
14226  Q_UNUSED(exportPen)
14227  Q_UNUSED(width)
14228  Q_UNUSED(height)
14229  Q_UNUSED(pdfCreator)
14230  Q_UNUSED(pdfTitle)
14231  qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
14232 #else
14233  int newWidth, newHeight;
14234  if (width == 0 || height == 0)
14235  {
14236  newWidth = this->width();
14237  newHeight = this->height();
14238  } else
14239  {
14240  newWidth = width;
14241  newHeight = height;
14242  }
14243 
14244  QPrinter printer(QPrinter::ScreenResolution);
14245  printer.setOutputFileName(fileName);
14246  printer.setOutputFormat(QPrinter::PdfFormat);
14247  printer.setColorMode(QPrinter::Color);
14248  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
14249  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
14250  QRect oldViewport = viewport();
14251  setViewport(QRect(0, 0, newWidth, newHeight));
14252 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
14253  printer.setFullPage(true);
14254  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
14255 #else
14256  QPageLayout pageLayout;
14257  pageLayout.setMode(QPageLayout::FullPageMode);
14258  pageLayout.setOrientation(QPageLayout::Portrait);
14259  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
14260  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
14261  printer.setPageLayout(pageLayout);
14262 #endif
14263  QCPPainter printpainter;
14264  if (printpainter.begin(&printer))
14265  {
14266  printpainter.setMode(QCPPainter::pmVectorized);
14267  printpainter.setMode(QCPPainter::pmNoCaching);
14268  printpainter.setMode(QCPPainter::pmNonCosmetic, exportPen==QCP::epNoCosmetic);
14269  printpainter.setWindow(mViewport);
14270  if (mBackgroundBrush.style() != Qt::NoBrush &&
14271  mBackgroundBrush.color() != Qt::white &&
14272  mBackgroundBrush.color() != Qt::transparent &&
14273  mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
14274  printpainter.fillRect(viewport(), mBackgroundBrush);
14275  draw(&printpainter);
14276  printpainter.end();
14277  success = true;
14278  }
14279  setViewport(oldViewport);
14280 #endif // QT_NO_PRINTER
14281  return success;
14282 }
14283 
14329 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
14330 {
14331  return saveRastered(fileName, width, height, scale, "PNG", quality, resolution, resolutionUnit);
14332 }
14333 
14376 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
14377 {
14378  return saveRastered(fileName, width, height, scale, "JPG", quality, resolution, resolutionUnit);
14379 }
14380 
14420 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale, int resolution, QCP::ResolutionUnit resolutionUnit)
14421 {
14422  return saveRastered(fileName, width, height, scale, "BMP", -1, resolution, resolutionUnit);
14423 }
14424 
14434 {
14435  return mPlotLayout->minimumSizeHint();
14436 }
14437 
14444 {
14445  return mPlotLayout->minimumSizeHint();
14446 }
14447 
14453 void QCustomPlot::paintEvent(QPaintEvent *event)
14454 {
14455  Q_UNUSED(event);
14456  QCPPainter painter(this);
14457  if (painter.isActive())
14458  {
14459  painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
14460  if (mBackgroundBrush.style() != Qt::NoBrush)
14461  painter.fillRect(mViewport, mBackgroundBrush);
14462  drawBackground(&painter);
14463  for (int bufferIndex = 0; bufferIndex < mPaintBuffers.size(); ++bufferIndex)
14464  mPaintBuffers.at(bufferIndex)->draw(&painter);
14465  }
14466 }
14467 
14473 void QCustomPlot::resizeEvent(QResizeEvent *event)
14474 {
14475  Q_UNUSED(event)
14476  // resize and repaint the buffer:
14477  setViewport(rect());
14478  replot(rpQueuedRefresh); // queued refresh is important here, to prevent painting issues in some contexts (e.g. MDI subwindow)
14479 }
14480 
14490 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
14491 {
14492  emit mouseDoubleClick(event);
14493  mMouseHasMoved = false;
14494  mMousePressPos = event->pos();
14495 
14496  // determine layerable under the cursor (this event is called instead of the second press event in a double-click):
14497  QList<QVariant> details;
14498  QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);
14499  for (int i=0; i<candidates.size(); ++i)
14500  {
14501  event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
14502  candidates.at(i)->mouseDoubleClickEvent(event, details.at(i));
14503  if (event->isAccepted())
14504  {
14505  mMouseEventLayerable = candidates.at(i);
14506  mMouseEventLayerableDetails = details.at(i);
14507  break;
14508  }
14509  }
14510 
14511  // emit specialized object double click signals:
14512  if (!candidates.isEmpty())
14513  {
14514  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(candidates.first()))
14515  {
14516  int dataIndex = 0;
14517  if (!details.first().value<QCPDataSelection>().isEmpty())
14518  dataIndex = details.first().value<QCPDataSelection>().dataRange().begin();
14519  emit plottableDoubleClick(ap, dataIndex, event);
14520  } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(candidates.first()))
14521  emit axisDoubleClick(ax, details.first().value<QCPAxis::SelectablePart>(), event);
14522  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(candidates.first()))
14523  emit itemDoubleClick(ai, event);
14524  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(candidates.first()))
14525  emit legendDoubleClick(lg, 0, event);
14526  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(candidates.first()))
14527  emit legendDoubleClick(li->parentLegend(), li, event);
14528  }
14529 
14530  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14531 }
14532 
14542 void QCustomPlot::mousePressEvent(QMouseEvent *event)
14543 {
14544  emit mousePress(event);
14545  // save some state to tell in releaseEvent whether it was a click:
14546  mMouseHasMoved = false;
14547  mMousePressPos = event->pos();
14548 
14550  {
14551  if (mSelectionRectMode != QCP::srmZoom || qobject_cast<QCPAxisRect*>(axisRectAt(mMousePressPos))) // in zoom mode only activate selection rect if on an axis rect
14553  } else
14554  {
14555  // no selection rect interaction, so forward event to layerable under the cursor:
14556  QList<QVariant> details;
14557  QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);
14558  for (int i=0; i<candidates.size(); ++i)
14559  {
14560  event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
14561  candidates.at(i)->mousePressEvent(event, details.at(i));
14562  if (event->isAccepted())
14563  {
14564  mMouseEventLayerable = candidates.at(i);
14565  mMouseEventLayerableDetails = details.at(i);
14566  break;
14567  }
14568  }
14569  }
14570 
14571  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14572 }
14573 
14586 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
14587 {
14588  emit mouseMove(event);
14589 
14590  if (!mMouseHasMoved && (mMousePressPos-event->pos()).manhattanLength() > 3)
14591  mMouseHasMoved = true; // moved too far from mouse press position, don't handle as click on mouse release
14592 
14594  mSelectionRect->moveSelection(event);
14595  else if (mMouseEventLayerable) // call event of affected layerable:
14596  mMouseEventLayerable->mouseMoveEvent(event, mMousePressPos);
14597 
14598  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14599 }
14600 
14615 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
14616 {
14617  emit mouseRelease(event);
14618 
14619  if (!mMouseHasMoved) // mouse hasn't moved (much) between press and release, so handle as click
14620  {
14621  if (mSelectionRect && mSelectionRect->isActive()) // a simple click shouldn't successfully finish a selection rect, so cancel it here
14623  if (event->button() == Qt::LeftButton)
14624  processPointSelection(event);
14625 
14626  // emit specialized click signals of QCustomPlot instance:
14627  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(mMouseEventLayerable))
14628  {
14629  int dataIndex = 0;
14632  emit plottableClick(ap, dataIndex, event);
14633  } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(mMouseEventLayerable))
14635  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(mMouseEventLayerable))
14636  emit itemClick(ai, event);
14637  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(mMouseEventLayerable))
14638  emit legendClick(lg, 0, event);
14639  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(mMouseEventLayerable))
14640  emit legendClick(li->parentLegend(), li, event);
14641  }
14642 
14643  if (mSelectionRect && mSelectionRect->isActive()) // Note: if a click was detected above, the selection rect is canceled there
14644  {
14645  // finish selection rect, the appropriate action will be taken via signal-slot connection:
14646  mSelectionRect->endSelection(event);
14647  } else
14648  {
14649  // call event of affected layerable:
14651  {
14652  mMouseEventLayerable->mouseReleaseEvent(event, mMousePressPos);
14654  }
14655  }
14656 
14657  if (noAntialiasingOnDrag())
14659 
14660  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14661 }
14662 
14668 void QCustomPlot::wheelEvent(QWheelEvent *event)
14669 {
14670  emit mouseWheel(event);
14671  // forward event to layerable under cursor:
14672  QList<QCPLayerable*> candidates = layerableListAt(event->pos(), false);
14673  for (int i=0; i<candidates.size(); ++i)
14674  {
14675  event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
14676  candidates.at(i)->wheelEvent(event);
14677  if (event->isAccepted())
14678  break;
14679  }
14680  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14681 }
14682 
14694 {
14695  updateLayout();
14696 
14697  // draw viewport background pixmap:
14698  drawBackground(painter);
14699 
14700  // draw all layered objects (grid, axes, plottables, items, legend,...):
14701  foreach (QCPLayer *layer, mLayers)
14702  layer->draw(painter);
14703 
14704  /* Debug code to draw all layout element rects
14705  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
14706  {
14707  painter->setBrush(Qt::NoBrush);
14708  painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
14709  painter->drawRect(el->rect());
14710  painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
14711  painter->drawRect(el->outerRect());
14712  }
14713  */
14714 }
14715 
14725 {
14726  // run through layout phases:
14730 }
14731 
14750 {
14751  // Note: background color is handled in individual replot/save functions
14752 
14753  // draw background pixmap (on top of fill, if brush specified):
14754  if (!mBackgroundPixmap.isNull())
14755  {
14756  if (mBackgroundScaled)
14757  {
14758  // check whether mScaledBackground needs to be updated:
14759  QSize scaledSize(mBackgroundPixmap.size());
14760  scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
14761  if (mScaledBackgroundPixmap.size() != scaledSize)
14762  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
14763  painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
14764  } else
14765  {
14766  painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
14767  }
14768  }
14769 }
14770 
14791 {
14792  int bufferIndex = 0;
14793  if (mPaintBuffers.isEmpty())
14794  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
14795 
14796  for (int layerIndex = 0; layerIndex < mLayers.size(); ++layerIndex)
14797  {
14798  QCPLayer *layer = mLayers.at(layerIndex);
14799  if (layer->mode() == QCPLayer::lmLogical)
14800  {
14801  layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
14802  } else if (layer->mode() == QCPLayer::lmBuffered)
14803  {
14804  ++bufferIndex;
14805  if (bufferIndex >= mPaintBuffers.size())
14806  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
14807  layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
14808  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
14809  {
14810  ++bufferIndex;
14811  if (bufferIndex >= mPaintBuffers.size())
14812  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
14813  }
14814  }
14815  }
14816  // remove unneeded buffers:
14817  while (mPaintBuffers.size()-1 > bufferIndex)
14818  mPaintBuffers.removeLast();
14819  // resize buffers to viewport size and clear contents:
14820  for (int i=0; i<mPaintBuffers.size(); ++i)
14821  {
14822  mPaintBuffers.at(i)->setSize(viewport().size()); // won't do anything if already correct size
14823  mPaintBuffers.at(i)->clear(Qt::transparent);
14824  mPaintBuffers.at(i)->setInvalidated();
14825  }
14826 }
14827 
14837 {
14838  if (mOpenGl)
14839  {
14840 #if defined(QCP_OPENGL_FBO)
14841  return new QCPPaintBufferGlFbo(viewport().size(), mBufferDevicePixelRatio, mGlContext, mGlPaintDevice);
14842 #elif defined(QCP_OPENGL_PBUFFER)
14843  return new QCPPaintBufferGlPbuffer(viewport().size(), mBufferDevicePixelRatio, mOpenGlMultisamples);
14844 #else
14845  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.";
14847 #endif
14848  } else
14850 }
14851 
14864 {
14865  for (int i=0; i<mPaintBuffers.size(); ++i)
14866  {
14867  if (mPaintBuffers.at(i)->invalidated())
14868  return true;
14869  }
14870  return false;
14871 }
14872 
14887 {
14888 #ifdef QCP_OPENGL_FBO
14889  freeOpenGl();
14890  QSurfaceFormat proposedSurfaceFormat;
14891  proposedSurfaceFormat.setSamples(mOpenGlMultisamples);
14892 #ifdef QCP_OPENGL_OFFSCREENSURFACE
14893  QOffscreenSurface *surface = new QOffscreenSurface;
14894 #else
14895  QWindow *surface = new QWindow;
14896  surface->setSurfaceType(QSurface::OpenGLSurface);
14897 #endif
14898  surface->setFormat(proposedSurfaceFormat);
14899  surface->create();
14900  mGlSurface = QSharedPointer<QSurface>(surface);
14901  mGlContext = QSharedPointer<QOpenGLContext>(new QOpenGLContext);
14902  mGlContext->setFormat(mGlSurface->format());
14903  if (!mGlContext->create())
14904  {
14905  qDebug() << Q_FUNC_INFO << "Failed to create OpenGL context";
14906  mGlContext.clear();
14907  mGlSurface.clear();
14908  return false;
14909  }
14910  if (!mGlContext->makeCurrent(mGlSurface.data())) // context needs to be current to create paint device
14911  {
14912  qDebug() << Q_FUNC_INFO << "Failed to make opengl context current";
14913  mGlContext.clear();
14914  mGlSurface.clear();
14915  return false;
14916  }
14917  if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
14918  {
14919  qDebug() << Q_FUNC_INFO << "OpenGL of this system doesn't support frame buffer objects";
14920  mGlContext.clear();
14921  mGlSurface.clear();
14922  return false;
14923  }
14924  mGlPaintDevice = QSharedPointer<QOpenGLPaintDevice>(new QOpenGLPaintDevice);
14925  return true;
14926 #elif defined(QCP_OPENGL_PBUFFER)
14927  return QGLFormat::hasOpenGL();
14928 #else
14929  return false;
14930 #endif
14931 }
14932 
14945 {
14946 #ifdef QCP_OPENGL_FBO
14947  mGlPaintDevice.clear();
14948  mGlContext.clear();
14949  mGlSurface.clear();
14950 #endif
14951 }
14952 
14959 {
14960  if (xAxis == axis)
14961  xAxis = 0;
14962  if (xAxis2 == axis)
14963  xAxis2 = 0;
14964  if (yAxis == axis)
14965  yAxis = 0;
14966  if (yAxis2 == axis)
14967  yAxis2 = 0;
14968 
14969  // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
14970 }
14971 
14978 {
14979  if (this->legend == legend)
14980  this->legend = 0;
14981 }
14982 
15000 void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event)
15001 {
15002  bool selectionStateChanged = false;
15003 
15004  if (mInteractions.testFlag(QCP::iSelectPlottables))
15005  {
15006  QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> > potentialSelections; // map key is number of selected data points, so we have selections sorted by size
15007  QRectF rectF(rect.normalized());
15008  if (QCPAxisRect *affectedAxisRect = axisRectAt(rectF.topLeft()))
15009  {
15010  // determine plottables that were hit by the rect and thus are candidates for selection:
15011  foreach (QCPAbstractPlottable *plottable, affectedAxisRect->plottables())
15012  {
15013  if (QCPPlottableInterface1D *plottableInterface = plottable->interface1D())
15014  {
15015  QCPDataSelection dataSel = plottableInterface->selectTestRect(rectF, true);
15016  if (!dataSel.isEmpty())
15017  potentialSelections.insertMulti(dataSel.dataPointCount(), QPair<QCPAbstractPlottable*, QCPDataSelection>(plottable, dataSel));
15018  }
15019  }
15020 
15021  if (!mInteractions.testFlag(QCP::iMultiSelect))
15022  {
15023  // only leave plottable with most selected points in map, since we will only select a single plottable:
15024  if (!potentialSelections.isEmpty())
15025  {
15026  QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> >::iterator it = potentialSelections.begin();
15027  while (it != potentialSelections.end()-1) // erase all except last element
15028  it = potentialSelections.erase(it);
15029  }
15030  }
15031 
15032  bool additive = event->modifiers().testFlag(mMultiSelectModifier);
15033  // deselect all other layerables if not additive selection:
15034  if (!additive)
15035  {
15036  // emit deselection except to those plottables who will be selected afterwards:
15037  foreach (QCPLayer *layer, mLayers)
15038  {
15039  foreach (QCPLayerable *layerable, layer->children())
15040  {
15041  if ((potentialSelections.isEmpty() || potentialSelections.constBegin()->first != layerable) && mInteractions.testFlag(layerable->selectionCategory()))
15042  {
15043  bool selChanged = false;
15044  layerable->deselectEvent(&selChanged);
15045  selectionStateChanged |= selChanged;
15046  }
15047  }
15048  }
15049  }
15050 
15051  // go through selections in reverse (largest selection first) and emit select events:
15052  QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> >::const_iterator it = potentialSelections.constEnd();
15053  while (it != potentialSelections.constBegin())
15054  {
15055  --it;
15056  if (mInteractions.testFlag(it.value().first->selectionCategory()))
15057  {
15058  bool selChanged = false;
15059  it.value().first->selectEvent(event, additive, QVariant::fromValue(it.value().second), &selChanged);
15060  selectionStateChanged |= selChanged;
15061  }
15062  }
15063  }
15064  }
15065 
15066  if (selectionStateChanged)
15067  {
15068  emit selectionChangedByUser();
15070  } else if (mSelectionRect)
15071  mSelectionRect->layer()->replot();
15072 }
15073 
15085 void QCustomPlot::processRectZoom(QRect rect, QMouseEvent *event)
15086 {
15087  Q_UNUSED(event)
15088  if (QCPAxisRect *axisRect = axisRectAt(rect.topLeft()))
15089  {
15090  QList<QCPAxis*> affectedAxes = QList<QCPAxis*>() << axisRect->rangeZoomAxes(Qt::Horizontal) << axisRect->rangeZoomAxes(Qt::Vertical);
15091  affectedAxes.removeAll(static_cast<QCPAxis*>(0));
15092  axisRect->zoom(QRectF(rect), affectedAxes);
15093  }
15094  replot(rpQueuedReplot); // always replot to make selection rect disappear
15095 }
15096 
15114 void QCustomPlot::processPointSelection(QMouseEvent *event)
15115 {
15116  QVariant details;
15117  QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
15118  bool selectionStateChanged = false;
15119  bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
15120  // deselect all other layerables if not additive selection:
15121  if (!additive)
15122  {
15123  foreach (QCPLayer *layer, mLayers)
15124  {
15125  foreach (QCPLayerable *layerable, layer->children())
15126  {
15127  if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
15128  {
15129  bool selChanged = false;
15130  layerable->deselectEvent(&selChanged);
15131  selectionStateChanged |= selChanged;
15132  }
15133  }
15134  }
15135  }
15136  if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
15137  {
15138  // a layerable was actually clicked, call its selectEvent:
15139  bool selChanged = false;
15140  clickedLayerable->selectEvent(event, additive, details, &selChanged);
15141  selectionStateChanged |= selChanged;
15142  }
15143  if (selectionStateChanged)
15144  {
15145  emit selectionChangedByUser();
15147  }
15148 }
15149 
15162 {
15163  if (mPlottables.contains(plottable))
15164  {
15165  qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
15166  return false;
15167  }
15168  if (plottable->parentPlot() != this)
15169  {
15170  qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
15171  return false;
15172  }
15173 
15174  mPlottables.append(plottable);
15175  // possibly add plottable to legend:
15177  plottable->addToLegend();
15178  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
15179  plottable->setLayer(currentLayer());
15180  return true;
15181 }
15182 
15193 {
15194  if (!graph)
15195  {
15196  qDebug() << Q_FUNC_INFO << "passed graph is zero";
15197  return false;
15198  }
15199  if (mGraphs.contains(graph))
15200  {
15201  qDebug() << Q_FUNC_INFO << "graph already registered with this QCustomPlot";
15202  return false;
15203  }
15204 
15205  mGraphs.append(graph);
15206  return true;
15207 }
15208 
15209 
15220 {
15221  if (mItems.contains(item))
15222  {
15223  qDebug() << Q_FUNC_INFO << "item already added to this QCustomPlot:" << reinterpret_cast<quintptr>(item);
15224  return false;
15225  }
15226  if (item->parentPlot() != this)
15227  {
15228  qDebug() << Q_FUNC_INFO << "item not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
15229  return false;
15230  }
15231 
15232  mItems.append(item);
15233  if (!item->layer()) // usually the layer is already set in the constructor of the item (via QCPLayerable constructor)
15234  item->setLayer(currentLayer());
15235  return true;
15236 }
15237 
15245 {
15246  for (int i=0; i<mLayers.size(); ++i)
15247  mLayers.at(i)->mIndex = i;
15248 }
15249 
15265 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
15266 {
15267  QList<QVariant> details;
15268  QList<QCPLayerable*> candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : 0);
15269  if (selectionDetails && !details.isEmpty())
15270  *selectionDetails = details.first();
15271  if (!candidates.isEmpty())
15272  return candidates.first();
15273  else
15274  return 0;
15275 }
15276 
15295 QList<QCPLayerable*> QCustomPlot::layerableListAt(const QPointF &pos, bool onlySelectable, QList<QVariant> *selectionDetails) const
15296 {
15297  QList<QCPLayerable*> result;
15298  for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
15299  {
15300  const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
15301  for (int i=layerables.size()-1; i>=0; --i)
15302  {
15303  if (!layerables.at(i)->realVisibility())
15304  continue;
15305  QVariant details;
15306  double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : 0);
15307  if (dist >= 0 && dist < selectionTolerance())
15308  {
15309  result.append(layerables.at(i));
15310  if (selectionDetails)
15311  selectionDetails->append(details);
15312  }
15313  }
15314  }
15315  return result;
15316 }
15317 
15336 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
15337 {
15338  QImage buffer = toPixmap(width, height, scale).toImage();
15339 
15340  int dotsPerMeter = 0;
15341  switch (resolutionUnit)
15342  {
15343  case QCP::ruDotsPerMeter: dotsPerMeter = resolution; break;
15344  case QCP::ruDotsPerCentimeter: dotsPerMeter = resolution*100; break;
15345  case QCP::ruDotsPerInch: dotsPerMeter = resolution/0.0254; break;
15346  }
15347  buffer.setDotsPerMeterX(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools
15348  buffer.setDotsPerMeterY(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools
15349  if (!buffer.isNull())
15350  return buffer.save(fileName, format, quality);
15351  else
15352  return false;
15353 }
15354 
15363 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
15364 {
15365  // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
15366  int newWidth, newHeight;
15367  if (width == 0 || height == 0)
15368  {
15369  newWidth = this->width();
15370  newHeight = this->height();
15371  } else
15372  {
15373  newWidth = width;
15374  newHeight = height;
15375  }
15376  int scaledWidth = qRound(scale*newWidth);
15377  int scaledHeight = qRound(scale*newHeight);
15378 
15379  QPixmap result(scaledWidth, scaledHeight);
15380  result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
15381  QCPPainter painter;
15382  painter.begin(&result);
15383  if (painter.isActive())
15384  {
15385  QRect oldViewport = viewport();
15386  setViewport(QRect(0, 0, newWidth, newHeight));
15388  if (!qFuzzyCompare(scale, 1.0))
15389  {
15390  if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
15392  painter.scale(scale, scale);
15393  }
15394  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill
15395  painter.fillRect(mViewport, mBackgroundBrush);
15396  draw(&painter);
15397  setViewport(oldViewport);
15398  painter.end();
15399  } else // might happen if pixmap has width or height zero
15400  {
15401  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
15402  return QPixmap();
15403  }
15404  return result;
15405 }
15406 
15419 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
15420 {
15421  // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
15422  int newWidth, newHeight;
15423  if (width == 0 || height == 0)
15424  {
15425  newWidth = this->width();
15426  newHeight = this->height();
15427  } else
15428  {
15429  newWidth = width;
15430  newHeight = height;
15431  }
15432 
15433  if (painter->isActive())
15434  {
15435  QRect oldViewport = viewport();
15436  setViewport(QRect(0, 0, newWidth, newHeight));
15437  painter->setMode(QCPPainter::pmNoCaching);
15438  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
15439  painter->fillRect(mViewport, mBackgroundBrush);
15440  draw(painter);
15441  setViewport(oldViewport);
15442  } else
15443  qDebug() << Q_FUNC_INFO << "Passed painter is not active";
15444 }
15445 /* end of 'src/core.cpp' */
15446 
15447 //amalgamation: add plottable1d.cpp
15448 
15449 /* including file 'src/colorgradient.cpp', size 24646 */
15450 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
15451 
15452 
15456 
15492  mLevelCount(350),
15493  mColorInterpolation(ciRGB),
15494  mPeriodic(false),
15495  mColorBufferInvalidated(true)
15496 {
15497  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
15498 }
15499 
15507  mLevelCount(350),
15509  mPeriodic(false),
15511 {
15512  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
15513  loadPreset(preset);
15514 }
15515 
15516 /* undocumented operator */
15518 {
15519  return ((other.mLevelCount == this->mLevelCount) &&
15520  (other.mColorInterpolation == this->mColorInterpolation) &&
15521  (other.mPeriodic == this->mPeriodic) &&
15522  (other.mColorStops == this->mColorStops));
15523 }
15524 
15532 {
15533  if (n < 2)
15534  {
15535  qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
15536  n = 2;
15537  }
15538  if (n != mLevelCount)
15539  {
15540  mLevelCount = n;
15541  mColorBufferInvalidated = true;
15542  }
15543 }
15544 
15556 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
15557 {
15559  mColorBufferInvalidated = true;
15560 }
15561 
15568 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
15569 {
15570  mColorStops.insert(position, color);
15571  mColorBufferInvalidated = true;
15572 }
15573 
15582 {
15583  if (interpolation != mColorInterpolation)
15584  {
15585  mColorInterpolation = interpolation;
15586  mColorBufferInvalidated = true;
15587  }
15588 }
15589 
15606 {
15607  mPeriodic = enabled;
15608 }
15609 
15628 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
15629 {
15630  // If you change something here, make sure to also adapt color() and the other colorize() overload
15631  if (!data)
15632  {
15633  qDebug() << Q_FUNC_INFO << "null pointer given as data";
15634  return;
15635  }
15636  if (!scanLine)
15637  {
15638  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
15639  return;
15640  }
15643 
15644  if (!logarithmic)
15645  {
15646  const double posToIndexFactor = (mLevelCount-1)/range.size();
15647  if (mPeriodic)
15648  {
15649  for (int i=0; i<n; ++i)
15650  {
15651  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
15652  if (index < 0)
15653  index += mLevelCount;
15654  scanLine[i] = mColorBuffer.at(index);
15655  }
15656  } else
15657  {
15658  for (int i=0; i<n; ++i)
15659  {
15660  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
15661  if (index < 0)
15662  index = 0;
15663  else if (index >= mLevelCount)
15664  index = mLevelCount-1;
15665  scanLine[i] = mColorBuffer.at(index);
15666  }
15667  }
15668  } else // logarithmic == true
15669  {
15670  if (mPeriodic)
15671  {
15672  for (int i=0; i<n; ++i)
15673  {
15674  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
15675  if (index < 0)
15676  index += mLevelCount;
15677  scanLine[i] = mColorBuffer.at(index);
15678  }
15679  } else
15680  {
15681  for (int i=0; i<n; ++i)
15682  {
15683  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
15684  if (index < 0)
15685  index = 0;
15686  else if (index >= mLevelCount)
15687  index = mLevelCount-1;
15688  scanLine[i] = mColorBuffer.at(index);
15689  }
15690  }
15691  }
15692 }
15693 
15702 void QCPColorGradient::colorize(const double *data, const unsigned char *alpha, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
15703 {
15704  // If you change something here, make sure to also adapt color() and the other colorize() overload
15705  if (!data)
15706  {
15707  qDebug() << Q_FUNC_INFO << "null pointer given as data";
15708  return;
15709  }
15710  if (!alpha)
15711  {
15712  qDebug() << Q_FUNC_INFO << "null pointer given as alpha";
15713  return;
15714  }
15715  if (!scanLine)
15716  {
15717  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
15718  return;
15719  }
15722 
15723  if (!logarithmic)
15724  {
15725  const double posToIndexFactor = (mLevelCount-1)/range.size();
15726  if (mPeriodic)
15727  {
15728  for (int i=0; i<n; ++i)
15729  {
15730  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
15731  if (index < 0)
15732  index += mLevelCount;
15733  if (alpha[dataIndexFactor*i] == 255)
15734  {
15735  scanLine[i] = mColorBuffer.at(index);
15736  } else
15737  {
15738  const QRgb rgb = mColorBuffer.at(index);
15739  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
15740  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
15741  }
15742  }
15743  } else
15744  {
15745  for (int i=0; i<n; ++i)
15746  {
15747  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
15748  if (index < 0)
15749  index = 0;
15750  else if (index >= mLevelCount)
15751  index = mLevelCount-1;
15752  if (alpha[dataIndexFactor*i] == 255)
15753  {
15754  scanLine[i] = mColorBuffer.at(index);
15755  } else
15756  {
15757  const QRgb rgb = mColorBuffer.at(index);
15758  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
15759  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
15760  }
15761  }
15762  }
15763  } else // logarithmic == true
15764  {
15765  if (mPeriodic)
15766  {
15767  for (int i=0; i<n; ++i)
15768  {
15769  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
15770  if (index < 0)
15771  index += mLevelCount;
15772  if (alpha[dataIndexFactor*i] == 255)
15773  {
15774  scanLine[i] = mColorBuffer.at(index);
15775  } else
15776  {
15777  const QRgb rgb = mColorBuffer.at(index);
15778  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
15779  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
15780  }
15781  }
15782  } else
15783  {
15784  for (int i=0; i<n; ++i)
15785  {
15786  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
15787  if (index < 0)
15788  index = 0;
15789  else if (index >= mLevelCount)
15790  index = mLevelCount-1;
15791  if (alpha[dataIndexFactor*i] == 255)
15792  {
15793  scanLine[i] = mColorBuffer.at(index);
15794  } else
15795  {
15796  const QRgb rgb = mColorBuffer.at(index);
15797  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
15798  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
15799  }
15800  }
15801  }
15802  }
15803 }
15804 
15817 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
15818 {
15819  // If you change something here, make sure to also adapt ::colorize()
15822  int index = 0;
15823  if (!logarithmic)
15824  index = (position-range.lower)*(mLevelCount-1)/range.size();
15825  else
15826  index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
15827  if (mPeriodic)
15828  {
15829  index = index % mLevelCount;
15830  if (index < 0)
15831  index += mLevelCount;
15832  } else
15833  {
15834  if (index < 0)
15835  index = 0;
15836  else if (index >= mLevelCount)
15837  index = mLevelCount-1;
15838  }
15839  return mColorBuffer.at(index);
15840 }
15841 
15850 {
15851  clearColorStops();
15852  switch (preset)
15853  {
15854  case gpGrayscale:
15856  setColorStopAt(0, Qt::black);
15857  setColorStopAt(1, Qt::white);
15858  break;
15859  case gpHot:
15861  setColorStopAt(0, QColor(50, 0, 0));
15862  setColorStopAt(0.2, QColor(180, 10, 0));
15863  setColorStopAt(0.4, QColor(245, 50, 0));
15864  setColorStopAt(0.6, QColor(255, 150, 10));
15865  setColorStopAt(0.8, QColor(255, 255, 50));
15866  setColorStopAt(1, QColor(255, 255, 255));
15867  break;
15868  case gpCold:
15870  setColorStopAt(0, QColor(0, 0, 50));
15871  setColorStopAt(0.2, QColor(0, 10, 180));
15872  setColorStopAt(0.4, QColor(0, 50, 245));
15873  setColorStopAt(0.6, QColor(10, 150, 255));
15874  setColorStopAt(0.8, QColor(50, 255, 255));
15875  setColorStopAt(1, QColor(255, 255, 255));
15876  break;
15877  case gpNight:
15879  setColorStopAt(0, QColor(10, 20, 30));
15880  setColorStopAt(1, QColor(250, 255, 250));
15881  break;
15882  case gpCandy:
15884  setColorStopAt(0, QColor(0, 0, 255));
15885  setColorStopAt(1, QColor(255, 250, 250));
15886  break;
15887  case gpGeography:
15889  setColorStopAt(0, QColor(70, 170, 210));
15890  setColorStopAt(0.20, QColor(90, 160, 180));
15891  setColorStopAt(0.25, QColor(45, 130, 175));
15892  setColorStopAt(0.30, QColor(100, 140, 125));
15893  setColorStopAt(0.5, QColor(100, 140, 100));
15894  setColorStopAt(0.6, QColor(130, 145, 120));
15895  setColorStopAt(0.7, QColor(140, 130, 120));
15896  setColorStopAt(0.9, QColor(180, 190, 190));
15897  setColorStopAt(1, QColor(210, 210, 230));
15898  break;
15899  case gpIon:
15901  setColorStopAt(0, QColor(50, 10, 10));
15902  setColorStopAt(0.45, QColor(0, 0, 255));
15903  setColorStopAt(0.8, QColor(0, 255, 255));
15904  setColorStopAt(1, QColor(0, 255, 0));
15905  break;
15906  case gpThermal:
15908  setColorStopAt(0, QColor(0, 0, 50));
15909  setColorStopAt(0.15, QColor(20, 0, 120));
15910  setColorStopAt(0.33, QColor(200, 30, 140));
15911  setColorStopAt(0.6, QColor(255, 100, 0));
15912  setColorStopAt(0.85, QColor(255, 255, 40));
15913  setColorStopAt(1, QColor(255, 255, 255));
15914  break;
15915  case gpPolar:
15917  setColorStopAt(0, QColor(50, 255, 255));
15918  setColorStopAt(0.18, QColor(10, 70, 255));
15919  setColorStopAt(0.28, QColor(10, 10, 190));
15920  setColorStopAt(0.5, QColor(0, 0, 0));
15921  setColorStopAt(0.72, QColor(190, 10, 10));
15922  setColorStopAt(0.82, QColor(255, 70, 10));
15923  setColorStopAt(1, QColor(255, 255, 50));
15924  break;
15925  case gpSpectrum:
15927  setColorStopAt(0, QColor(50, 0, 50));
15928  setColorStopAt(0.15, QColor(0, 0, 255));
15929  setColorStopAt(0.35, QColor(0, 255, 255));
15930  setColorStopAt(0.6, QColor(255, 255, 0));
15931  setColorStopAt(0.75, QColor(255, 30, 0));
15932  setColorStopAt(1, QColor(50, 0, 0));
15933  break;
15934  case gpJet:
15936  setColorStopAt(0, QColor(0, 0, 100));
15937  setColorStopAt(0.15, QColor(0, 50, 255));
15938  setColorStopAt(0.35, QColor(0, 255, 255));
15939  setColorStopAt(0.65, QColor(255, 255, 0));
15940  setColorStopAt(0.85, QColor(255, 30, 0));
15941  setColorStopAt(1, QColor(100, 0, 0));
15942  break;
15943  case gpHues:
15945  setColorStopAt(0, QColor(255, 0, 0));
15946  setColorStopAt(1.0/3.0, QColor(0, 0, 255));
15947  setColorStopAt(2.0/3.0, QColor(0, 255, 0));
15948  setColorStopAt(1, QColor(255, 0, 0));
15949  break;
15950  }
15951 }
15952 
15959 {
15960  mColorStops.clear();
15961  mColorBufferInvalidated = true;
15962 }
15963 
15971 {
15972  QCPColorGradient result(*this);
15973  result.clearColorStops();
15974  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
15975  result.setColorStopAt(1.0-it.key(), it.value());
15976  return result;
15977 }
15978 
15985 {
15986  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
15987  {
15988  if (it.value().alpha() < 255)
15989  return true;
15990  }
15991  return false;
15992 }
15993 
16000 {
16001  if (mColorBuffer.size() != mLevelCount)
16002  mColorBuffer.resize(mLevelCount);
16003  if (mColorStops.size() > 1)
16004  {
16005  double indexToPosFactor = 1.0/(double)(mLevelCount-1);
16006  const bool useAlpha = stopsUseAlpha();
16007  for (int i=0; i<mLevelCount; ++i)
16008  {
16009  double position = i*indexToPosFactor;
16010  QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
16011  if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
16012  {
16013  mColorBuffer[i] = (it-1).value().rgba();
16014  } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
16015  {
16016  mColorBuffer[i] = it.value().rgba();
16017  } else // position is in between stops (or on an intermediate stop), interpolate color
16018  {
16019  QMap<double, QColor>::const_iterator high = it;
16020  QMap<double, QColor>::const_iterator low = it-1;
16021  double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
16022  switch (mColorInterpolation)
16023  {
16024  case ciRGB:
16025  {
16026  if (useAlpha)
16027  {
16028  const int alpha = (1-t)*low.value().alpha() + t*high.value().alpha();
16029  const float alphaPremultiplier = alpha/255.0f; // since we use QImage::Format_ARGB32_Premultiplied
16030  mColorBuffer[i] = qRgba(((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier,
16031  ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier,
16032  ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier,
16033  alpha);
16034  } else
16035  {
16036  mColorBuffer[i] = qRgb(((1-t)*low.value().red() + t*high.value().red()),
16037  ((1-t)*low.value().green() + t*high.value().green()),
16038  ((1-t)*low.value().blue() + t*high.value().blue()));
16039  }
16040  break;
16041  }
16042  case ciHSV:
16043  {
16044  QColor lowHsv = low.value().toHsv();
16045  QColor highHsv = high.value().toHsv();
16046  double hue = 0;
16047  double hueDiff = highHsv.hueF()-lowHsv.hueF();
16048  if (hueDiff > 0.5)
16049  hue = lowHsv.hueF() - t*(1.0-hueDiff);
16050  else if (hueDiff < -0.5)
16051  hue = lowHsv.hueF() + t*(1.0+hueDiff);
16052  else
16053  hue = lowHsv.hueF() + t*hueDiff;
16054  if (hue < 0) hue += 1.0;
16055  else if (hue >= 1.0) hue -= 1.0;
16056  if (useAlpha)
16057  {
16058  const QRgb rgb = QColor::fromHsvF(hue,
16059  (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),
16060  (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
16061  const float alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF();
16062  mColorBuffer[i] = qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha);
16063  }
16064  else
16065  {
16066  mColorBuffer[i] = QColor::fromHsvF(hue,
16067  (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),
16068  (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
16069  }
16070  break;
16071  }
16072  }
16073  }
16074  }
16075  } else if (mColorStops.size() == 1)
16076  {
16077  const QRgb rgb = mColorStops.constBegin().value().rgb();
16078  const float alpha = mColorStops.constBegin().value().alphaF();
16079  mColorBuffer.fill(qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha));
16080  } else // mColorStops is empty, fill color buffer with black
16081  {
16082  mColorBuffer.fill(qRgb(0, 0, 0));
16083  }
16084  mColorBufferInvalidated = false;
16085 }
16086 /* end of 'src/colorgradient.cpp' */
16087 
16088 
16089 /* including file 'src/selectiondecorator-bracket.cpp', size 12313 */
16090 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
16091 
16095 
16116  mBracketPen(QPen(Qt::black)),
16117  mBracketBrush(Qt::NoBrush),
16118  mBracketWidth(5),
16119  mBracketHeight(50),
16120  mBracketStyle(bsSquareBracket),
16121  mTangentToData(false),
16122  mTangentAverage(2)
16123 {
16124 
16125 }
16126 
16128 {
16129 }
16130 
16136 {
16137  mBracketPen = pen;
16138 }
16139 
16145 {
16146  mBracketBrush = brush;
16147 }
16148 
16155 {
16156  mBracketWidth = width;
16157 }
16158 
16165 {
16166  mBracketHeight = height;
16167 }
16168 
16175 {
16176  mBracketStyle = style;
16177 }
16178 
16187 {
16188  mTangentToData = enabled;
16189 }
16190 
16200 {
16201  mTangentAverage = pointCount;
16202  if (mTangentAverage < 1)
16203  mTangentAverage = 1;
16204 }
16205 
16219 void QCPSelectionDecoratorBracket::drawBracket(QCPPainter *painter, int direction) const
16220 {
16221  switch (mBracketStyle)
16222  {
16223  case bsSquareBracket:
16224  {
16225  painter->drawLine(QLineF(mBracketWidth*direction, -mBracketHeight*0.5, 0, -mBracketHeight*0.5));
16226  painter->drawLine(QLineF(mBracketWidth*direction, mBracketHeight*0.5, 0, mBracketHeight*0.5));
16227  painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));
16228  break;
16229  }
16230  case bsHalfEllipse:
16231  {
16232  painter->drawArc(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight, -90*16, -180*16*direction);
16233  break;
16234  }
16235  case bsEllipse:
16236  {
16237  painter->drawEllipse(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight);
16238  break;
16239  }
16240  case bsPlus:
16241  {
16242  painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));
16243  painter->drawLine(QLineF(-mBracketWidth*0.5, 0, mBracketWidth*0.5, 0));
16244  break;
16245  }
16246  default:
16247  {
16248  qDebug() << Q_FUNC_INFO << "unknown/custom bracket style can't be handeld by default implementation:" << static_cast<int>(mBracketStyle);
16249  break;
16250  }
16251  }
16252 }
16253 
16263 {
16264  if (!mPlottable || selection.isEmpty()) return;
16265 
16266  if (QCPPlottableInterface1D *interface1d = mPlottable->interface1D())
16267  {
16268  foreach (const QCPDataRange &dataRange, selection.dataRanges())
16269  {
16270  // determine position and (if tangent mode is enabled) angle of brackets:
16271  int openBracketDir = (mPlottable->keyAxis() && !mPlottable->keyAxis()->rangeReversed()) ? 1 : -1;
16272  int closeBracketDir = -openBracketDir;
16273  QPointF openBracketPos = getPixelCoordinates(interface1d, dataRange.begin());
16274  QPointF closeBracketPos = getPixelCoordinates(interface1d, dataRange.end()-1);
16275  double openBracketAngle = 0;
16276  double closeBracketAngle = 0;
16277  if (mTangentToData)
16278  {
16279  openBracketAngle = getTangentAngle(interface1d, dataRange.begin(), openBracketDir);
16280  closeBracketAngle = getTangentAngle(interface1d, dataRange.end()-1, closeBracketDir);
16281  }
16282  // draw opening bracket:
16283  QTransform oldTransform = painter->transform();
16284  painter->setPen(mBracketPen);
16285  painter->setBrush(mBracketBrush);
16286  painter->translate(openBracketPos);
16287  painter->rotate(openBracketAngle/M_PI*180.0);
16288  drawBracket(painter, openBracketDir);
16289  painter->setTransform(oldTransform);
16290  // draw closing bracket:
16291  painter->setPen(mBracketPen);
16292  painter->setBrush(mBracketBrush);
16293  painter->translate(closeBracketPos);
16294  painter->rotate(closeBracketAngle/M_PI*180.0);
16295  drawBracket(painter, closeBracketDir);
16296  painter->setTransform(oldTransform);
16297  }
16298  }
16299 }
16300 
16314 double QCPSelectionDecoratorBracket::getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const
16315 {
16316  if (!interface1d || dataIndex < 0 || dataIndex >= interface1d->dataCount())
16317  return 0;
16318  direction = direction < 0 ? -1 : 1; // enforce direction is either -1 or 1
16319 
16320  // how many steps we can actually go from index in the given direction without exceeding data bounds:
16321  int averageCount;
16322  if (direction < 0)
16323  averageCount = qMin(mTangentAverage, dataIndex);
16324  else
16325  averageCount = qMin(mTangentAverage, interface1d->dataCount()-1-dataIndex);
16326  qDebug() << averageCount;
16327  // calculate point average of averageCount points:
16328  QVector<QPointF> points(averageCount);
16329  QPointF pointsAverage;
16330  int currentIndex = dataIndex;
16331  for (int i=0; i<averageCount; ++i)
16332  {
16333  points[i] = getPixelCoordinates(interface1d, currentIndex);
16334  pointsAverage += points[i];
16335  currentIndex += direction;
16336  }
16337  pointsAverage /= (double)averageCount;
16338 
16339  // calculate slope of linear regression through points:
16340  double numSum = 0;
16341  double denomSum = 0;
16342  for (int i=0; i<averageCount; ++i)
16343  {
16344  const double dx = points.at(i).x()-pointsAverage.x();
16345  const double dy = points.at(i).y()-pointsAverage.y();
16346  numSum += dx*dy;
16347  denomSum += dx*dx;
16348  }
16349  if (!qFuzzyIsNull(denomSum) && !qFuzzyIsNull(numSum))
16350  {
16351  return qAtan2(numSum, denomSum);
16352  } else // undetermined angle, probably mTangentAverage == 1, so using only one data point
16353  return 0;
16354 }
16355 
16361 QPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const
16362 {
16363  QCPAxis *keyAxis = mPlottable->keyAxis();
16364  QCPAxis *valueAxis = mPlottable->valueAxis();
16365  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(0, 0); }
16366 
16367  if (keyAxis->orientation() == Qt::Horizontal)
16368  return QPointF(keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)));
16369  else
16370  return QPointF(valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)));
16371 }
16372 /* end of 'src/selectiondecorator-bracket.cpp' */
16373 
16374 
16375 /* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47509 */
16376 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
16377 
16378 
16382 
16420 /* start documentation of inline functions */
16421 
16502 /* end documentation of inline functions */
16503 
16508 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
16509  QCPLayoutElement(parentPlot),
16510  mBackgroundBrush(Qt::NoBrush),
16511  mBackgroundScaled(true),
16512  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
16513  mInsetLayout(new QCPLayoutInset),
16514  mRangeDrag(Qt::Horizontal|Qt::Vertical),
16515  mRangeZoom(Qt::Horizontal|Qt::Vertical),
16516  mRangeZoomFactorHorz(0.85),
16517  mRangeZoomFactorVert(0.85),
16518  mDragging(false)
16519 {
16522  mInsetLayout->setParent(this);
16523 
16524  setMinimumSize(50, 50);
16525  setMinimumMargins(QMargins(15, 15, 15, 15));
16526  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
16527  mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
16528  mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
16529  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
16530 
16531  if (setupDefaultAxes)
16532  {
16533  QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
16534  QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
16535  QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
16536  QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
16537  setRangeDragAxes(xAxis, yAxis);
16538  setRangeZoomAxes(xAxis, yAxis);
16539  xAxis2->setVisible(false);
16540  yAxis2->setVisible(false);
16541  xAxis->grid()->setVisible(true);
16542  yAxis->grid()->setVisible(true);
16543  xAxis2->grid()->setVisible(false);
16544  yAxis2->grid()->setVisible(false);
16545  xAxis2->grid()->setZeroLinePen(Qt::NoPen);
16546  yAxis2->grid()->setZeroLinePen(Qt::NoPen);
16547  xAxis2->grid()->setVisible(false);
16548  yAxis2->grid()->setVisible(false);
16549  }
16550 }
16551 
16553 {
16554  delete mInsetLayout;
16555  mInsetLayout = 0;
16556 
16557  QList<QCPAxis*> axesList = axes();
16558  for (int i=0; i<axesList.size(); ++i)
16559  removeAxis(axesList.at(i));
16560 }
16561 
16568 {
16569  return mAxes.value(type).size();
16570 }
16571 
16578 {
16579  QList<QCPAxis*> ax(mAxes.value(type));
16580  if (index >= 0 && index < ax.size())
16581  {
16582  return ax.at(index);
16583  } else
16584  {
16585  qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
16586  return 0;
16587  }
16588 }
16589 
16598 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
16599 {
16600  QList<QCPAxis*> result;
16601  if (types.testFlag(QCPAxis::atLeft))
16602  result << mAxes.value(QCPAxis::atLeft);
16603  if (types.testFlag(QCPAxis::atRight))
16604  result << mAxes.value(QCPAxis::atRight);
16605  if (types.testFlag(QCPAxis::atTop))
16606  result << mAxes.value(QCPAxis::atTop);
16607  if (types.testFlag(QCPAxis::atBottom))
16608  result << mAxes.value(QCPAxis::atBottom);
16609  return result;
16610 }
16611 
16616 QList<QCPAxis*> QCPAxisRect::axes() const
16617 {
16618  QList<QCPAxis*> result;
16619  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
16620  while (it.hasNext())
16621  {
16622  it.next();
16623  result << it.value();
16624  }
16625  return result;
16626 }
16627 
16649 {
16650  QCPAxis *newAxis = axis;
16651  if (!newAxis)
16652  {
16653  newAxis = new QCPAxis(this, type);
16654  } else // user provided existing axis instance, do some sanity checks
16655  {
16656  if (newAxis->axisType() != type)
16657  {
16658  qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
16659  return 0;
16660  }
16661  if (newAxis->axisRect() != this)
16662  {
16663  qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
16664  return 0;
16665  }
16666  if (axes().contains(newAxis))
16667  {
16668  qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
16669  return 0;
16670  }
16671  }
16672  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
16673  {
16674  bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
16675  newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
16676  newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
16677  }
16678  mAxes[type].append(newAxis);
16679 
16680  // reset convenience axis pointers on parent QCustomPlot if they are unset:
16681  if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)
16682  {
16683  switch (type)
16684  {
16685  case QCPAxis::atBottom: { if (!mParentPlot->xAxis) mParentPlot->xAxis = newAxis; break; }
16686  case QCPAxis::atLeft: { if (!mParentPlot->yAxis) mParentPlot->yAxis = newAxis; break; }
16687  case QCPAxis::atTop: { if (!mParentPlot->xAxis2) mParentPlot->xAxis2 = newAxis; break; }
16688  case QCPAxis::atRight: { if (!mParentPlot->yAxis2) mParentPlot->yAxis2 = newAxis; break; }
16689  }
16690  }
16691 
16692  return newAxis;
16693 }
16694 
16703 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
16704 {
16705  QList<QCPAxis*> result;
16706  if (types.testFlag(QCPAxis::atLeft))
16707  result << addAxis(QCPAxis::atLeft);
16708  if (types.testFlag(QCPAxis::atRight))
16709  result << addAxis(QCPAxis::atRight);
16710  if (types.testFlag(QCPAxis::atTop))
16711  result << addAxis(QCPAxis::atTop);
16712  if (types.testFlag(QCPAxis::atBottom))
16713  result << addAxis(QCPAxis::atBottom);
16714  return result;
16715 }
16716 
16725 {
16726  // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
16727  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
16728  while (it.hasNext())
16729  {
16730  it.next();
16731  if (it.value().contains(axis))
16732  {
16733  mAxes[it.key()].removeOne(axis);
16734  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)
16735  parentPlot()->axisRemoved(axis);
16736  delete axis;
16737  return true;
16738  }
16739  }
16740  qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
16741  return false;
16742 }
16743 
16752 void QCPAxisRect::zoom(const QRectF &pixelRect)
16753 {
16754  zoom(pixelRect, axes());
16755 }
16756 
16765 void QCPAxisRect::zoom(const QRectF &pixelRect, const QList<QCPAxis*> &affectedAxes)
16766 {
16767  foreach (QCPAxis *axis, affectedAxes)
16768  {
16769  if (!axis)
16770  {
16771  qDebug() << Q_FUNC_INFO << "a passed axis was zero";
16772  continue;
16773  }
16774  QCPRange pixelRange;
16775  if (axis->orientation() == Qt::Horizontal)
16776  pixelRange = QCPRange(pixelRect.left(), pixelRect.right());
16777  else
16778  pixelRange = QCPRange(pixelRect.top(), pixelRect.bottom());
16779  axis->setRange(axis->pixelToCoord(pixelRange.lower), axis->pixelToCoord(pixelRange.upper));
16780  }
16781 }
16782 
16802 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
16803 {
16804  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
16805  if (axisCount(QCPAxis::atBottom) == 0)
16806  xAxis = addAxis(QCPAxis::atBottom);
16807  else
16808  xAxis = axis(QCPAxis::atBottom);
16809 
16810  if (axisCount(QCPAxis::atLeft) == 0)
16811  yAxis = addAxis(QCPAxis::atLeft);
16812  else
16813  yAxis = axis(QCPAxis::atLeft);
16814 
16815  if (axisCount(QCPAxis::atTop) == 0)
16816  xAxis2 = addAxis(QCPAxis::atTop);
16817  else
16818  xAxis2 = axis(QCPAxis::atTop);
16819 
16820  if (axisCount(QCPAxis::atRight) == 0)
16821  yAxis2 = addAxis(QCPAxis::atRight);
16822  else
16823  yAxis2 = axis(QCPAxis::atRight);
16824 
16825  xAxis->setVisible(true);
16826  yAxis->setVisible(true);
16827  xAxis2->setVisible(true);
16828  yAxis2->setVisible(true);
16829  xAxis2->setTickLabels(false);
16830  yAxis2->setTickLabels(false);
16831 
16832  xAxis2->setRange(xAxis->range());
16833  xAxis2->setRangeReversed(xAxis->rangeReversed());
16834  xAxis2->setScaleType(xAxis->scaleType());
16835  xAxis2->setTicks(xAxis->ticks());
16836  xAxis2->setNumberFormat(xAxis->numberFormat());
16837  xAxis2->setNumberPrecision(xAxis->numberPrecision());
16838  xAxis2->ticker()->setTickCount(xAxis->ticker()->tickCount());
16839  xAxis2->ticker()->setTickOrigin(xAxis->ticker()->tickOrigin());
16840 
16841  yAxis2->setRange(yAxis->range());
16842  yAxis2->setRangeReversed(yAxis->rangeReversed());
16843  yAxis2->setScaleType(yAxis->scaleType());
16844  yAxis2->setTicks(yAxis->ticks());
16845  yAxis2->setNumberFormat(yAxis->numberFormat());
16846  yAxis2->setNumberPrecision(yAxis->numberPrecision());
16847  yAxis2->ticker()->setTickCount(yAxis->ticker()->tickCount());
16848  yAxis2->ticker()->setTickOrigin(yAxis->ticker()->tickOrigin());
16849 
16850  if (connectRanges)
16851  {
16852  connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
16853  connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
16854  }
16855 }
16856 
16865 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
16866 {
16867  // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
16868  QList<QCPAbstractPlottable*> result;
16869  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
16870  {
16871  if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this || mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
16872  result.append(mParentPlot->mPlottables.at(i));
16873  }
16874  return result;
16875 }
16876 
16885 QList<QCPGraph*> QCPAxisRect::graphs() const
16886 {
16887  // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
16888  QList<QCPGraph*> result;
16889  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
16890  {
16891  if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
16892  result.append(mParentPlot->mGraphs.at(i));
16893  }
16894  return result;
16895 }
16896 
16907 QList<QCPAbstractItem *> QCPAxisRect::items() const
16908 {
16909  // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
16910  // and miss those items that have this axis rect as clipAxisRect.
16911  QList<QCPAbstractItem*> result;
16912  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
16913  {
16914  if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
16915  {
16916  result.append(mParentPlot->mItems.at(itemId));
16917  continue;
16918  }
16919  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
16920  for (int posId=0; posId<positions.size(); ++posId)
16921  {
16922  if (positions.at(posId)->axisRect() == this ||
16923  positions.at(posId)->keyAxis()->axisRect() == this ||
16924  positions.at(posId)->valueAxis()->axisRect() == this)
16925  {
16926  result.append(mParentPlot->mItems.at(itemId));
16927  break;
16928  }
16929  }
16930  }
16931  return result;
16932 }
16933 
16945 {
16946  QCPLayoutElement::update(phase);
16947 
16948  switch (phase)
16949  {
16950  case upPreparation:
16951  {
16952  QList<QCPAxis*> allAxes = axes();
16953  for (int i=0; i<allAxes.size(); ++i)
16954  allAxes.at(i)->setupTickVectors();
16955  break;
16956  }
16957  case upLayout:
16958  {
16960  break;
16961  }
16962  default: break;
16963  }
16964 
16965  // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
16966  mInsetLayout->update(phase);
16967 }
16968 
16969 /* inherits documentation from base class */
16970 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
16971 {
16972  QList<QCPLayoutElement*> result;
16973  if (mInsetLayout)
16974  {
16975  result << mInsetLayout;
16976  if (recursive)
16977  result << mInsetLayout->elements(recursive);
16978  }
16979  return result;
16980 }
16981 
16982 /* inherits documentation from base class */
16984 {
16985  painter->setAntialiasing(false);
16986 }
16987 
16988 /* inherits documentation from base class */
16990 {
16991  drawBackground(painter);
16992 }
16993 
17009 void QCPAxisRect::setBackground(const QPixmap &pm)
17010 {
17011  mBackgroundPixmap = pm;
17012  mScaledBackgroundPixmap = QPixmap();
17013 }
17014 
17028 void QCPAxisRect::setBackground(const QBrush &brush)
17029 {
17030  mBackgroundBrush = brush;
17031 }
17032 
17040 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
17041 {
17042  mBackgroundPixmap = pm;
17043  mScaledBackgroundPixmap = QPixmap();
17044  mBackgroundScaled = scaled;
17046 }
17047 
17059 {
17060  mBackgroundScaled = scaled;
17061 }
17062 
17069 {
17071 }
17072 
17079 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
17080 {
17081  if (orientation == Qt::Horizontal)
17082  return mRangeDragHorzAxis.isEmpty() ? 0 : mRangeDragHorzAxis.first().data();
17083  else
17084  return mRangeDragVertAxis.isEmpty() ? 0 : mRangeDragVertAxis.first().data();
17085 }
17086 
17093 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
17094 {
17095  if (orientation == Qt::Horizontal)
17096  return mRangeZoomHorzAxis.isEmpty() ? 0 : mRangeZoomHorzAxis.first().data();
17097  else
17098  return mRangeZoomVertAxis.isEmpty() ? 0 : mRangeZoomVertAxis.first().data();
17099 }
17100 
17106 QList<QCPAxis*> QCPAxisRect::rangeDragAxes(Qt::Orientation orientation)
17107 {
17108  QList<QCPAxis*> result;
17109  if (orientation == Qt::Horizontal)
17110  {
17111  for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
17112  {
17113  if (!mRangeDragHorzAxis.at(i).isNull())
17114  result.append(mRangeDragHorzAxis.at(i).data());
17115  }
17116  } else
17117  {
17118  for (int i=0; i<mRangeDragVertAxis.size(); ++i)
17119  {
17120  if (!mRangeDragVertAxis.at(i).isNull())
17121  result.append(mRangeDragVertAxis.at(i).data());
17122  }
17123  }
17124  return result;
17125 }
17126 
17132 QList<QCPAxis*> QCPAxisRect::rangeZoomAxes(Qt::Orientation orientation)
17133 {
17134  QList<QCPAxis*> result;
17135  if (orientation == Qt::Horizontal)
17136  {
17137  for (int i=0; i<mRangeZoomHorzAxis.size(); ++i)
17138  {
17139  if (!mRangeZoomHorzAxis.at(i).isNull())
17140  result.append(mRangeZoomHorzAxis.at(i).data());
17141  }
17142  } else
17143  {
17144  for (int i=0; i<mRangeZoomVertAxis.size(); ++i)
17145  {
17146  if (!mRangeZoomVertAxis.at(i).isNull())
17147  result.append(mRangeZoomVertAxis.at(i).data());
17148  }
17149  }
17150  return result;
17151 }
17152 
17158 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
17159 {
17160  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
17161 }
17162 
17179 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
17180 {
17181  mRangeDrag = orientations;
17182 }
17183 
17199 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
17200 {
17201  mRangeZoom = orientations;
17202 }
17203 
17214 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
17215 {
17216  QList<QCPAxis*> horz, vert;
17217  if (horizontal)
17218  horz.append(horizontal);
17219  if (vertical)
17220  vert.append(vertical);
17221  setRangeDragAxes(horz, vert);
17222 }
17223 
17234 {
17235  QList<QCPAxis*> horz, vert;
17236  foreach (QCPAxis *ax, axes)
17237  {
17238  if (ax->orientation() == Qt::Horizontal)
17239  horz.append(ax);
17240  else
17241  vert.append(ax);
17242  }
17243  setRangeDragAxes(horz, vert);
17244 }
17245 
17252 void QCPAxisRect::setRangeDragAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)
17253 {
17254  mRangeDragHorzAxis.clear();
17255  foreach (QCPAxis *ax, horizontal)
17256  {
17257  QPointer<QCPAxis> axPointer(ax);
17258  if (!axPointer.isNull())
17259  mRangeDragHorzAxis.append(axPointer);
17260  else
17261  qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast<quintptr>(ax);
17262  }
17263  mRangeDragVertAxis.clear();
17264  foreach (QCPAxis *ax, vertical)
17265  {
17266  QPointer<QCPAxis> axPointer(ax);
17267  if (!axPointer.isNull())
17268  mRangeDragVertAxis.append(axPointer);
17269  else
17270  qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast<quintptr>(ax);
17271  }
17272 }
17273 
17286 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
17287 {
17288  QList<QCPAxis*> horz, vert;
17289  if (horizontal)
17290  horz.append(horizontal);
17291  if (vertical)
17292  vert.append(vertical);
17293  setRangeZoomAxes(horz, vert);
17294 }
17295 
17306 {
17307  QList<QCPAxis*> horz, vert;
17308  foreach (QCPAxis *ax, axes)
17309  {
17310  if (ax->orientation() == Qt::Horizontal)
17311  horz.append(ax);
17312  else
17313  vert.append(ax);
17314  }
17315  setRangeZoomAxes(horz, vert);
17316 }
17317 
17324 void QCPAxisRect::setRangeZoomAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)
17325 {
17326  mRangeZoomHorzAxis.clear();
17327  foreach (QCPAxis *ax, horizontal)
17328  {
17329  QPointer<QCPAxis> axPointer(ax);
17330  if (!axPointer.isNull())
17331  mRangeZoomHorzAxis.append(axPointer);
17332  else
17333  qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast<quintptr>(ax);
17334  }
17335  mRangeZoomVertAxis.clear();
17336  foreach (QCPAxis *ax, vertical)
17337  {
17338  QPointer<QCPAxis> axPointer(ax);
17339  if (!axPointer.isNull())
17340  mRangeZoomVertAxis.append(axPointer);
17341  else
17342  qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast<quintptr>(ax);
17343  }
17344 }
17345 
17356 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
17357 {
17358  mRangeZoomFactorHorz = horizontalFactor;
17359  mRangeZoomFactorVert = verticalFactor;
17360 }
17361 
17367 {
17368  mRangeZoomFactorHorz = factor;
17369  mRangeZoomFactorVert = factor;
17370 }
17371 
17391 {
17392  // draw background fill:
17393  if (mBackgroundBrush != Qt::NoBrush)
17394  painter->fillRect(mRect, mBackgroundBrush);
17395 
17396  // draw background pixmap (on top of fill, if brush specified):
17397  if (!mBackgroundPixmap.isNull())
17398  {
17399  if (mBackgroundScaled)
17400  {
17401  // check whether mScaledBackground needs to be updated:
17402  QSize scaledSize(mBackgroundPixmap.size());
17403  scaledSize.scale(mRect.size(), mBackgroundScaledMode);
17404  if (mScaledBackgroundPixmap.size() != scaledSize)
17405  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
17406  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
17407  } else
17408  {
17409  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
17410  }
17411  }
17412 }
17413 
17425 {
17426  const QList<QCPAxis*> axesList = mAxes.value(type);
17427  if (axesList.isEmpty())
17428  return;
17429 
17430  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
17431  for (int i=1; i<axesList.size(); ++i)
17432  {
17433  int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
17434  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)
17435  {
17436  if (!isFirstVisible)
17437  offset += axesList.at(i)->tickLengthIn();
17438  isFirstVisible = false;
17439  }
17440  axesList.at(i)->setOffset(offset);
17441  }
17442 }
17443 
17444 /* inherits documentation from base class */
17446 {
17447  if (!mAutoMargins.testFlag(side))
17448  qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
17449 
17451 
17452  // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
17453  const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
17454  if (axesList.size() > 0)
17455  return axesList.last()->offset() + axesList.last()->calculateMargin();
17456  else
17457  return 0;
17458 }
17459 
17471 {
17472  if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)
17473  {
17482  }
17483 }
17484 
17496 void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)
17497 {
17498  Q_UNUSED(details)
17499  mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release)
17500  if (event->buttons() & Qt::LeftButton)
17501  {
17502  mDragging = true;
17503  // initialize antialiasing backup in case we start dragging:
17505  {
17508  }
17509  // Mouse range dragging interaction:
17510  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
17511  {
17512  mDragStartHorzRange.clear();
17513  for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
17514  mDragStartHorzRange.append(mRangeDragHorzAxis.at(i).isNull() ? QCPRange() : mRangeDragHorzAxis.at(i)->range());
17515  mDragStartVertRange.clear();
17516  for (int i=0; i<mRangeDragVertAxis.size(); ++i)
17517  mDragStartVertRange.append(mRangeDragVertAxis.at(i).isNull() ? QCPRange() : mRangeDragVertAxis.at(i)->range());
17518  }
17519  }
17520 }
17521 
17529 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
17530 {
17531  Q_UNUSED(startPos)
17532  // Mouse range dragging interaction:
17533  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
17534  {
17535 
17536  if (mRangeDrag.testFlag(Qt::Horizontal))
17537  {
17538  for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
17539  {
17540  QCPAxis *ax = mRangeDragHorzAxis.at(i).data();
17541  if (!ax)
17542  continue;
17543  if (i >= mDragStartHorzRange.size())
17544  break;
17545  if (ax->mScaleType == QCPAxis::stLinear)
17546  {
17547  double diff = ax->pixelToCoord(mDragStart.x()) - ax->pixelToCoord(event->pos().x());
17548  ax->setRange(mDragStartHorzRange.at(i).lower+diff, mDragStartHorzRange.at(i).upper+diff);
17549  } else if (ax->mScaleType == QCPAxis::stLogarithmic)
17550  {
17551  double diff = ax->pixelToCoord(mDragStart.x()) / ax->pixelToCoord(event->pos().x());
17552  ax->setRange(mDragStartHorzRange.at(i).lower*diff, mDragStartHorzRange.at(i).upper*diff);
17553  }
17554  }
17555  }
17556 
17557  if (mRangeDrag.testFlag(Qt::Vertical))
17558  {
17559  for (int i=0; i<mRangeDragVertAxis.size(); ++i)
17560  {
17561  QCPAxis *ax = mRangeDragVertAxis.at(i).data();
17562  if (!ax)
17563  continue;
17564  if (i >= mDragStartVertRange.size())
17565  break;
17566  if (ax->mScaleType == QCPAxis::stLinear)
17567  {
17568  double diff = ax->pixelToCoord(mDragStart.y()) - ax->pixelToCoord(event->pos().y());
17569  ax->setRange(mDragStartVertRange.at(i).lower+diff, mDragStartVertRange.at(i).upper+diff);
17570  } else if (ax->mScaleType == QCPAxis::stLogarithmic)
17571  {
17572  double diff = ax->pixelToCoord(mDragStart.y()) / ax->pixelToCoord(event->pos().y());
17573  ax->setRange(mDragStartVertRange.at(i).lower*diff, mDragStartVertRange.at(i).upper*diff);
17574  }
17575  }
17576  }
17577 
17578  if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
17579  {
17582  mParentPlot->replot();
17583  }
17584 
17585  }
17586 }
17587 
17588 /* inherits documentation from base class */
17589 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
17590 {
17591  Q_UNUSED(event)
17592  Q_UNUSED(startPos)
17593  mDragging = false;
17595  {
17598  }
17599 }
17600 
17615 void QCPAxisRect::wheelEvent(QWheelEvent *event)
17616 {
17617  // Mouse range zooming interaction:
17618  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
17619  {
17620  if (mRangeZoom != 0)
17621  {
17622  double factor;
17623  double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
17624  if (mRangeZoom.testFlag(Qt::Horizontal))
17625  {
17626  factor = qPow(mRangeZoomFactorHorz, wheelSteps);
17627  for (int i=0; i<mRangeZoomHorzAxis.size(); ++i)
17628  {
17629  if (!mRangeZoomHorzAxis.at(i).isNull())
17630  mRangeZoomHorzAxis.at(i)->scaleRange(factor, mRangeZoomHorzAxis.at(i)->pixelToCoord(event->pos().x()));
17631  }
17632  }
17633  if (mRangeZoom.testFlag(Qt::Vertical))
17634  {
17635  factor = qPow(mRangeZoomFactorVert, wheelSteps);
17636  for (int i=0; i<mRangeZoomVertAxis.size(); ++i)
17637  {
17638  if (!mRangeZoomVertAxis.at(i).isNull())
17639  mRangeZoomVertAxis.at(i)->scaleRange(factor, mRangeZoomVertAxis.at(i)->pixelToCoord(event->pos().y()));
17640  }
17641  }
17642  mParentPlot->replot();
17643  }
17644  }
17645 }
17646 /* end of 'src/layoutelements/layoutelement-axisrect.cpp' */
17647 
17648 
17649 /* including file 'src/layoutelements/layoutelement-legend.cpp', size 30933 */
17650 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
17651 
17655 
17680 /* start of documentation of signals */
17681 
17688 /* end of documentation of signals */
17689 
17695  QCPLayoutElement(parent->parentPlot()),
17696  mParentLegend(parent),
17697  mFont(parent->font()),
17698  mTextColor(parent->textColor()),
17699  mSelectedFont(parent->selectedFont()),
17700  mSelectedTextColor(parent->selectedTextColor()),
17701  mSelectable(true),
17702  mSelected(false)
17703 {
17704  setLayer(QLatin1String("legend"));
17705  setMargins(QMargins(0, 0, 0, 0));
17706 }
17707 
17714 {
17715  mFont = font;
17716 }
17717 
17723 void QCPAbstractLegendItem::setTextColor(const QColor &color)
17724 {
17725  mTextColor = color;
17726 }
17727 
17735 {
17736  mSelectedFont = font;
17737 }
17738 
17746 {
17747  mSelectedTextColor = color;
17748 }
17749 
17756 {
17757  if (mSelectable != selectable)
17758  {
17761  }
17762 }
17763 
17773 {
17774  if (mSelected != selected)
17775  {
17776  mSelected = selected;
17778  }
17779 }
17780 
17781 /* inherits documentation from base class */
17782 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17783 {
17784  Q_UNUSED(details)
17785  if (!mParentPlot) return -1;
17786  if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
17787  return -1;
17788 
17789  if (mRect.contains(pos.toPoint()))
17790  return mParentPlot->selectionTolerance()*0.99;
17791  else
17792  return -1;
17793 }
17794 
17795 /* inherits documentation from base class */
17797 {
17799 }
17800 
17801 /* inherits documentation from base class */
17803 {
17804  return mOuterRect;
17805 }
17806 
17807 /* inherits documentation from base class */
17808 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
17809 {
17810  Q_UNUSED(event)
17811  Q_UNUSED(details)
17813  {
17814  bool selBefore = mSelected;
17815  setSelected(additive ? !mSelected : true);
17816  if (selectionStateChanged)
17817  *selectionStateChanged = mSelected != selBefore;
17818  }
17819 }
17820 
17821 /* inherits documentation from base class */
17822 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
17823 {
17825  {
17826  bool selBefore = mSelected;
17827  setSelected(false);
17828  if (selectionStateChanged)
17829  *selectionStateChanged = mSelected != selBefore;
17830  }
17831 }
17832 
17836 
17871  QCPAbstractLegendItem(parent),
17872  mPlottable(plottable)
17873 {
17874  setAntialiased(false);
17875 }
17876 
17883 {
17885 }
17886 
17893 {
17895 }
17896 
17903 {
17904  return mSelected ? mSelectedFont : mFont;
17905 }
17906 
17914 {
17915  if (!mPlottable) return;
17916  painter->setFont(getFont());
17917  painter->setPen(QPen(getTextColor()));
17918  QSizeF iconSize = mParentLegend->iconSize();
17919  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
17920  QRectF iconRect(mRect.topLeft(), iconSize);
17921  int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
17922  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
17923  // draw icon:
17924  painter->save();
17925  painter->setClipRect(iconRect, Qt::IntersectClip);
17926  mPlottable->drawLegendIcon(painter, iconRect);
17927  painter->restore();
17928  // draw icon border:
17929  if (getIconBorderPen().style() != Qt::NoPen)
17930  {
17931  painter->setPen(getIconBorderPen());
17932  painter->setBrush(Qt::NoBrush);
17933  int halfPen = qCeil(painter->pen().widthF()*0.5)+1;
17934  painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen)); // extend default clip rect so thicker pens (especially during selection) are not clipped
17935  painter->drawRect(iconRect);
17936  }
17937 }
17938 
17947 {
17948  if (!mPlottable) return QSize();
17949  QSize result(0, 0);
17950  QRect textRect;
17951  QFontMetrics fontMetrics(getFont());
17952  QSize iconSize = mParentLegend->iconSize();
17953  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
17954  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
17955  result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
17956  return result;
17957 }
17958 
17959 
17963 
17994 /* start of documentation of signals */
17995 
18003 /* end of documentation of signals */
18004 
18012 {
18013  setFillOrder(QCPLayoutGrid::foRowsFirst);
18014  setWrap(0);
18015 
18016  setRowSpacing(3);
18017  setColumnSpacing(8);
18018  setMargins(QMargins(7, 5, 7, 4));
18019  setAntialiased(false);
18020  setIconSize(32, 18);
18021 
18022  setIconTextPadding(7);
18023 
18024  setSelectableParts(spLegendBox | spItems);
18025  setSelectedParts(spNone);
18026 
18027  setBorderPen(QPen(Qt::black, 0));
18028  setSelectedBorderPen(QPen(Qt::blue, 2));
18029  setIconBorderPen(Qt::NoPen);
18030  setSelectedIconBorderPen(QPen(Qt::blue, 2));
18031  setBrush(Qt::white);
18032  setSelectedBrush(Qt::white);
18033  setTextColor(Qt::black);
18034  setSelectedTextColor(Qt::blue);
18035 }
18036 
18038 {
18039  clearItems();
18040  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)
18041  mParentPlot->legendRemoved(this);
18042 }
18043 
18044 /* no doc for getter, see setSelectedParts */
18045 QCPLegend::SelectableParts QCPLegend::selectedParts() const
18046 {
18047  // check whether any legend elements selected, if yes, add spItems to return value
18048  bool hasSelectedItems = false;
18049  for (int i=0; i<itemCount(); ++i)
18050  {
18051  if (item(i) && item(i)->selected())
18052  {
18053  hasSelectedItems = true;
18054  break;
18055  }
18056  }
18057  if (hasSelectedItems)
18058  return mSelectedParts | spItems;
18059  else
18060  return mSelectedParts & ~spItems;
18061 }
18062 
18066 void QCPLegend::setBorderPen(const QPen &pen)
18067 {
18068  mBorderPen = pen;
18069 }
18070 
18074 void QCPLegend::setBrush(const QBrush &brush)
18075 {
18076  mBrush = brush;
18077 }
18078 
18088 void QCPLegend::setFont(const QFont &font)
18089 {
18090  mFont = font;
18091  for (int i=0; i<itemCount(); ++i)
18092  {
18093  if (item(i))
18094  item(i)->setFont(mFont);
18095  }
18096 }
18097 
18107 void QCPLegend::setTextColor(const QColor &color)
18108 {
18109  mTextColor = color;
18110  for (int i=0; i<itemCount(); ++i)
18111  {
18112  if (item(i))
18113  item(i)->setTextColor(color);
18114  }
18115 }
18116 
18121 void QCPLegend::setIconSize(const QSize &size)
18122 {
18123  mIconSize = size;
18124 }
18125 
18128 void QCPLegend::setIconSize(int width, int height)
18129 {
18130  mIconSize.setWidth(width);
18131  mIconSize.setHeight(height);
18132 }
18133 
18140 {
18141  mIconTextPadding = padding;
18142 }
18143 
18150 void QCPLegend::setIconBorderPen(const QPen &pen)
18151 {
18152  mIconBorderPen = pen;
18153 }
18154 
18165 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
18166 {
18167  if (mSelectableParts != selectable)
18168  {
18169  mSelectableParts = selectable;
18170  emit selectableChanged(mSelectableParts);
18171  }
18172 }
18173 
18195 void QCPLegend::setSelectedParts(const SelectableParts &selected)
18196 {
18197  SelectableParts newSelected = selected;
18198  mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
18199 
18200  if (mSelectedParts != newSelected)
18201  {
18202  if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
18203  {
18204  qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
18205  newSelected &= ~spItems;
18206  }
18207  if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
18208  {
18209  for (int i=0; i<itemCount(); ++i)
18210  {
18211  if (item(i))
18212  item(i)->setSelected(false);
18213  }
18214  }
18215  mSelectedParts = newSelected;
18216  emit selectionChanged(mSelectedParts);
18217  }
18218 }
18219 
18226 void QCPLegend::setSelectedBorderPen(const QPen &pen)
18227 {
18228  mSelectedBorderPen = pen;
18229 }
18230 
18237 {
18238  mSelectedIconBorderPen = pen;
18239 }
18240 
18247 void QCPLegend::setSelectedBrush(const QBrush &brush)
18248 {
18249  mSelectedBrush = brush;
18250 }
18251 
18260 {
18261  mSelectedFont = font;
18262  for (int i=0; i<itemCount(); ++i)
18263  {
18264  if (item(i))
18265  item(i)->setSelectedFont(font);
18266  }
18267 }
18268 
18276 void QCPLegend::setSelectedTextColor(const QColor &color)
18277 {
18278  mSelectedTextColor = color;
18279  for (int i=0; i<itemCount(); ++i)
18280  {
18281  if (item(i))
18282  item(i)->setSelectedTextColor(color);
18283  }
18284 }
18285 
18294 {
18295  return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
18296 }
18297 
18305 {
18306  for (int i=0; i<itemCount(); ++i)
18307  {
18308  if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
18309  {
18310  if (pli->plottable() == plottable)
18311  return pli;
18312  }
18313  }
18314  return 0;
18315 }
18316 
18326 {
18327  return elementCount();
18328 }
18329 
18336 {
18337  for (int i=0; i<itemCount(); ++i)
18338  {
18339  if (item == this->item(i))
18340  return true;
18341  }
18342  return false;
18343 }
18344 
18352 {
18353  return itemWithPlottable(plottable);
18354 }
18355 
18367 {
18368  return addElement(item);
18369 }
18370 
18384 bool QCPLegend::removeItem(int index)
18385 {
18386  if (QCPAbstractLegendItem *ali = item(index))
18387  {
18388  bool success = remove(ali);
18389  if (success)
18390  setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering
18391  return success;
18392  } else
18393  return false;
18394 }
18395 
18409 {
18410  bool success = remove(item);
18411  if (success)
18412  setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering
18413  return success;
18414 }
18415 
18420 {
18421  for (int i=itemCount()-1; i>=0; --i)
18422  removeItem(i);
18423 }
18424 
18431 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
18432 {
18433  QList<QCPAbstractLegendItem*> result;
18434  for (int i=0; i<itemCount(); ++i)
18435  {
18436  if (QCPAbstractLegendItem *ali = item(i))
18437  {
18438  if (ali->selected())
18439  result.append(ali);
18440  }
18441  }
18442  return result;
18443 }
18444 
18461 {
18463 }
18464 
18471 {
18472  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
18473 }
18474 
18480 QBrush QCPLegend::getBrush() const
18481 {
18482  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
18483 }
18484 
18491 {
18492  // draw background rect:
18493  painter->setBrush(getBrush());
18494  painter->setPen(getBorderPen());
18495  painter->drawRect(mOuterRect);
18496 }
18497 
18498 /* inherits documentation from base class */
18499 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18500 {
18501  if (!mParentPlot) return -1;
18502  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
18503  return -1;
18504 
18505  if (mOuterRect.contains(pos.toPoint()))
18506  {
18507  if (details) details->setValue(spLegendBox);
18508  return mParentPlot->selectionTolerance()*0.99;
18509  }
18510  return -1;
18511 }
18512 
18513 /* inherits documentation from base class */
18514 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
18515 {
18516  Q_UNUSED(event)
18517  mSelectedParts = selectedParts(); // in case item selection has changed
18518  if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
18519  {
18520  SelectableParts selBefore = mSelectedParts;
18521  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)
18522  if (selectionStateChanged)
18523  *selectionStateChanged = mSelectedParts != selBefore;
18524  }
18525 }
18526 
18527 /* inherits documentation from base class */
18528 void QCPLegend::deselectEvent(bool *selectionStateChanged)
18529 {
18530  mSelectedParts = selectedParts(); // in case item selection has changed
18531  if (mSelectableParts.testFlag(spLegendBox))
18532  {
18533  SelectableParts selBefore = mSelectedParts;
18534  setSelectedParts(selectedParts() & ~spLegendBox);
18535  if (selectionStateChanged)
18536  *selectionStateChanged = mSelectedParts != selBefore;
18537  }
18538 }
18539 
18540 /* inherits documentation from base class */
18542 {
18543  return QCP::iSelectLegend;
18544 }
18545 
18546 /* inherits documentation from base class */
18548 {
18549  return QCP::iSelectLegend;
18550 }
18551 
18552 /* inherits documentation from base class */
18554 {
18555  if (parentPlot && !parentPlot->legend)
18556  parentPlot->legend = this;
18557 }
18558 /* end of 'src/layoutelements/layoutelement-legend.cpp' */
18559 
18560 
18561 /* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12759 */
18562 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
18563 
18567 
18578 /* start documentation of signals */
18579 
18602 /* end documentation of signals */
18603 
18610  QCPLayoutElement(parentPlot),
18611  mText(),
18612  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18613  mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18614  mTextColor(Qt::black),
18615  mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18616  mSelectedTextColor(Qt::blue),
18617  mSelectable(false),
18618  mSelected(false)
18619 {
18620  if (parentPlot)
18621  {
18622  mFont = parentPlot->font();
18623  mSelectedFont = parentPlot->font();
18624  }
18625  setMargins(QMargins(2, 2, 2, 2));
18626 }
18627 
18635  QCPLayoutElement(parentPlot),
18636  mText(text),
18637  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18638  mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18639  mTextColor(Qt::black),
18640  mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18641  mSelectedTextColor(Qt::blue),
18642  mSelectable(false),
18643  mSelected(false)
18644 {
18645  if (parentPlot)
18646  {
18647  mFont = parentPlot->font();
18648  mSelectedFont = parentPlot->font();
18649  }
18650  setMargins(QMargins(2, 2, 2, 2));
18651 }
18652 
18659 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize) :
18660  QCPLayoutElement(parentPlot),
18661  mText(text),
18662  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18663  mFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below
18664  mTextColor(Qt::black),
18665  mSelectedFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below
18666  mSelectedTextColor(Qt::blue),
18667  mSelectable(false),
18668  mSelected(false)
18669 {
18670  if (parentPlot)
18671  {
18672  mFont = parentPlot->font();
18673  mFont.setPointSizeF(pointSize);
18674  mSelectedFont = parentPlot->font();
18675  mSelectedFont.setPointSizeF(pointSize);
18676  }
18677  setMargins(QMargins(2, 2, 2, 2));
18678 }
18679 
18686 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize) :
18687  QCPLayoutElement(parentPlot),
18688  mText(text),
18689  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18690  mFont(QFont(fontFamily, pointSize)),
18691  mTextColor(Qt::black),
18692  mSelectedFont(QFont(fontFamily, pointSize)),
18693  mSelectedTextColor(Qt::blue),
18694  mSelectable(false),
18695  mSelected(false)
18696 {
18697  setMargins(QMargins(2, 2, 2, 2));
18698 }
18699 
18707  QCPLayoutElement(parentPlot),
18708  mText(text),
18709  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18710  mFont(font),
18711  mTextColor(Qt::black),
18712  mSelectedFont(font),
18713  mSelectedTextColor(Qt::blue),
18714  mSelectable(false),
18715  mSelected(false)
18716 {
18717  setMargins(QMargins(2, 2, 2, 2));
18718 }
18719 
18725 void QCPTextElement::setText(const QString &text)
18726 {
18727  mText = text;
18728 }
18729 
18751 {
18752  mTextFlags = flags;
18753 }
18754 
18760 void QCPTextElement::setFont(const QFont &font)
18761 {
18762  mFont = font;
18763 }
18764 
18770 void QCPTextElement::setTextColor(const QColor &color)
18771 {
18772  mTextColor = color;
18773 }
18774 
18781 {
18782  mSelectedFont = font;
18783 }
18784 
18790 void QCPTextElement::setSelectedTextColor(const QColor &color)
18791 {
18792  mSelectedTextColor = color;
18793 }
18794 
18802 {
18803  if (mSelectable != selectable)
18804  {
18807  }
18808 }
18809 
18818 {
18819  if (mSelected != selected)
18820  {
18821  mSelected = selected;
18823  }
18824 }
18825 
18826 /* inherits documentation from base class */
18828 {
18830 }
18831 
18832 /* inherits documentation from base class */
18834 {
18835  painter->setFont(mainFont());
18836  painter->setPen(QPen(mainTextColor()));
18837  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
18838 }
18839 
18840 /* inherits documentation from base class */
18842 {
18843  QFontMetrics metrics(mFont);
18844  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
18845  result.rwidth() += mMargins.left() + mMargins.right();
18846  result.rheight() += mMargins.top() + mMargins.bottom();
18847  return result;
18848 }
18849 
18850 /* inherits documentation from base class */
18852 {
18853  QFontMetrics metrics(mFont);
18854  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
18855  result.rheight() += mMargins.top() + mMargins.bottom();
18856  result.setWidth(QWIDGETSIZE_MAX);
18857  return result;
18858 }
18859 
18860 /* inherits documentation from base class */
18861 void QCPTextElement::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
18862 {
18863  Q_UNUSED(event)
18864  Q_UNUSED(details)
18865  if (mSelectable)
18866  {
18867  bool selBefore = mSelected;
18868  setSelected(additive ? !mSelected : true);
18869  if (selectionStateChanged)
18870  *selectionStateChanged = mSelected != selBefore;
18871  }
18872 }
18873 
18874 /* inherits documentation from base class */
18875 void QCPTextElement::deselectEvent(bool *selectionStateChanged)
18876 {
18877  if (mSelectable)
18878  {
18879  bool selBefore = mSelected;
18880  setSelected(false);
18881  if (selectionStateChanged)
18882  *selectionStateChanged = mSelected != selBefore;
18883  }
18884 }
18885 
18896 double QCPTextElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18897 {
18898  Q_UNUSED(details)
18899  if (onlySelectable && !mSelectable)
18900  return -1;
18901 
18902  if (mTextBoundingRect.contains(pos.toPoint()))
18903  return mParentPlot->selectionTolerance()*0.99;
18904  else
18905  return -1;
18906 }
18907 
18914 void QCPTextElement::mousePressEvent(QMouseEvent *event, const QVariant &details)
18915 {
18916  Q_UNUSED(details)
18917  event->accept();
18918 }
18919 
18926 void QCPTextElement::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
18927 {
18928  if ((QPointF(event->pos())-startPos).manhattanLength() <= 3)
18929  emit clicked(event);
18930 }
18931 
18937 void QCPTextElement::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
18938 {
18939  Q_UNUSED(details)
18940  emit doubleClicked(event);
18941 }
18942 
18949 {
18950  return mSelected ? mSelectedFont : mFont;
18951 }
18952 
18959 {
18961 }
18962 /* end of 'src/layoutelements/layoutelement-textelement.cpp' */
18963 
18964 
18965 /* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 25910 */
18966 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
18967 
18968 
18972 
19011 /* start documentation of inline functions */
19012 
19026 /* end documentation of signals */
19027 /* start documentation of signals */
19028 
19050 /* end documentation of signals */
19051 
19056  QCPLayoutElement(parentPlot),
19057  mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
19058  mDataScaleType(QCPAxis::stLinear),
19059  mBarWidth(20),
19060  mAxisRect(new QCPColorScaleAxisRectPrivate(this))
19061 {
19062  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)
19064  setDataRange(QCPRange(0, 6));
19065 }
19066 
19068 {
19069  delete mAxisRect;
19070 }
19071 
19072 /* undocumented getter */
19073 QString QCPColorScale::label() const
19074 {
19075  if (!mColorAxis)
19076  {
19077  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
19078  return QString();
19079  }
19080 
19081  return mColorAxis.data()->label();
19082 }
19083 
19084 /* undocumented getter */
19086 {
19087  if (!mAxisRect)
19088  {
19089  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19090  return false;
19091  }
19092 
19093  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
19094  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
19095  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
19096 }
19097 
19098 /* undocumented getter */
19100 {
19101  if (!mAxisRect)
19102  {
19103  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19104  return false;
19105  }
19106 
19107  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
19108  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
19109  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
19110 }
19111 
19120 {
19121  if (!mAxisRect)
19122  {
19123  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19124  return;
19125  }
19126  if (mType != type)
19127  {
19128  mType = type;
19129  QCPRange rangeTransfer(0, 6);
19130  QString labelTransfer;
19131  QSharedPointer<QCPAxisTicker> tickerTransfer;
19132  // transfer/revert some settings on old axis if it exists:
19133  bool doTransfer = (bool)mColorAxis;
19134  if (doTransfer)
19135  {
19136  rangeTransfer = mColorAxis.data()->range();
19137  labelTransfer = mColorAxis.data()->label();
19138  tickerTransfer = mColorAxis.data()->ticker();
19139  mColorAxis.data()->setLabel(QString());
19140  disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19141  disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
19142  }
19143  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
19144  foreach (QCPAxis::AxisType atype, allAxisTypes)
19145  {
19146  mAxisRect.data()->axis(atype)->setTicks(atype == mType);
19147  mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
19148  }
19149  // set new mColorAxis pointer:
19150  mColorAxis = mAxisRect.data()->axis(mType);
19151  // transfer settings to new axis:
19152  if (doTransfer)
19153  {
19154  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)
19155  mColorAxis.data()->setLabel(labelTransfer);
19156  mColorAxis.data()->setTicker(tickerTransfer);
19157  }
19158  connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19159  connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
19160  mAxisRect.data()->setRangeDragAxes(QList<QCPAxis*>() << mColorAxis.data());
19161  }
19162 }
19163 
19174 {
19175  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
19176  {
19178  if (mColorAxis)
19179  mColorAxis.data()->setRange(mDataRange);
19181  }
19182 }
19183 
19195 {
19196  if (mDataScaleType != scaleType)
19197  {
19198  mDataScaleType = scaleType;
19199  if (mColorAxis)
19200  mColorAxis.data()->setScaleType(mDataScaleType);
19204  }
19205 }
19206 
19215 {
19216  if (mGradient != gradient)
19217  {
19218  mGradient = gradient;
19219  if (mAxisRect)
19220  mAxisRect.data()->mGradientImageInvalidated = true;
19221  emit gradientChanged(mGradient);
19222  }
19223 }
19224 
19229 void QCPColorScale::setLabel(const QString &str)
19230 {
19231  if (!mColorAxis)
19232  {
19233  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
19234  return;
19235  }
19236 
19237  mColorAxis.data()->setLabel(str);
19238 }
19239 
19245 {
19246  mBarWidth = width;
19247 }
19248 
19256 {
19257  if (!mAxisRect)
19258  {
19259  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19260  return;
19261  }
19262 
19263  if (enabled)
19264  mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
19265  else
19266  mAxisRect.data()->setRangeDrag(0);
19267 }
19268 
19276 {
19277  if (!mAxisRect)
19278  {
19279  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19280  return;
19281  }
19282 
19283  if (enabled)
19284  mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
19285  else
19286  mAxisRect.data()->setRangeZoom(0);
19287 }
19288 
19292 QList<QCPColorMap*> QCPColorScale::colorMaps() const
19293 {
19294  QList<QCPColorMap*> result;
19295  for (int i=0; i<mParentPlot->plottableCount(); ++i)
19296  {
19297  if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
19298  if (cm->colorScale() == this)
19299  result.append(cm);
19300  }
19301  return result;
19302 }
19303 
19310 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
19311 {
19312  QList<QCPColorMap*> maps = colorMaps();
19313  QCPRange newRange;
19314  bool haveRange = false;
19318  for (int i=0; i<maps.size(); ++i)
19319  {
19320  if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
19321  continue;
19322  QCPRange mapRange;
19323  if (maps.at(i)->colorScale() == this)
19324  {
19325  bool currentFoundRange = true;
19326  mapRange = maps.at(i)->data()->dataBounds();
19327  if (sign == QCP::sdPositive)
19328  {
19329  if (mapRange.lower <= 0 && mapRange.upper > 0)
19330  mapRange.lower = mapRange.upper*1e-3;
19331  else if (mapRange.lower <= 0 && mapRange.upper <= 0)
19332  currentFoundRange = false;
19333  } else if (sign == QCP::sdNegative)
19334  {
19335  if (mapRange.upper >= 0 && mapRange.lower < 0)
19336  mapRange.upper = mapRange.lower*1e-3;
19337  else if (mapRange.upper >= 0 && mapRange.lower >= 0)
19338  currentFoundRange = false;
19339  }
19340  if (currentFoundRange)
19341  {
19342  if (!haveRange)
19343  newRange = mapRange;
19344  else
19345  newRange.expand(mapRange);
19346  haveRange = true;
19347  }
19348  }
19349  }
19350  if (haveRange)
19351  {
19352  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
19353  {
19354  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
19356  {
19357  newRange.lower = center-mDataRange.size()/2.0;
19358  newRange.upper = center+mDataRange.size()/2.0;
19359  } else // mScaleType == stLogarithmic
19360  {
19361  newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
19362  newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
19363  }
19364  }
19365  setDataRange(newRange);
19366  }
19367 }
19368 
19369 /* inherits documentation from base class */
19371 {
19372  QCPLayoutElement::update(phase);
19373  if (!mAxisRect)
19374  {
19375  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19376  return;
19377  }
19378 
19379  mAxisRect.data()->update(phase);
19380 
19381  switch (phase)
19382  {
19383  case upMargins:
19384  {
19386  {
19387  setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
19388  setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
19389  } else
19390  {
19391  setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
19392  setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
19393  }
19394  break;
19395  }
19396  case upLayout:
19397  {
19398  mAxisRect.data()->setOuterRect(rect());
19399  break;
19400  }
19401  default: break;
19402  }
19403 }
19404 
19405 /* inherits documentation from base class */
19407 {
19408  painter->setAntialiasing(false);
19409 }
19410 
19411 /* inherits documentation from base class */
19412 void QCPColorScale::mousePressEvent(QMouseEvent *event, const QVariant &details)
19413 {
19414  if (!mAxisRect)
19415  {
19416  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19417  return;
19418  }
19419  mAxisRect.data()->mousePressEvent(event, details);
19420 }
19421 
19422 /* inherits documentation from base class */
19423 void QCPColorScale::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
19424 {
19425  if (!mAxisRect)
19426  {
19427  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19428  return;
19429  }
19430  mAxisRect.data()->mouseMoveEvent(event, startPos);
19431 }
19432 
19433 /* inherits documentation from base class */
19434 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
19435 {
19436  if (!mAxisRect)
19437  {
19438  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19439  return;
19440  }
19441  mAxisRect.data()->mouseReleaseEvent(event, startPos);
19442 }
19443 
19444 /* inherits documentation from base class */
19445 void QCPColorScale::wheelEvent(QWheelEvent *event)
19446 {
19447  if (!mAxisRect)
19448  {
19449  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19450  return;
19451  }
19452  mAxisRect.data()->wheelEvent(event);
19453 }
19454 
19458 
19474  QCPAxisRect(parentColorScale->parentPlot(), true),
19475  mParentColorScale(parentColorScale),
19476  mGradientImageInvalidated(true)
19477 {
19478  setParentLayerable(parentColorScale);
19479  setMinimumMargins(QMargins(0, 0, 0, 0));
19480  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
19481  foreach (QCPAxis::AxisType type, allAxisTypes)
19482  {
19483  axis(type)->setVisible(true);
19484  axis(type)->grid()->setVisible(false);
19485  axis(type)->setPadding(0);
19486  connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
19487  connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
19488  }
19489 
19490  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
19491  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
19492  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
19493  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
19494  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
19495  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
19496  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
19497  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
19498 
19499  // make layer transfers of color scale transfer to axis rect and axes
19500  // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
19501  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
19502  foreach (QCPAxis::AxisType type, allAxisTypes)
19503  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
19504 }
19505 
19514 {
19517 
19518  bool mirrorHorz = false;
19519  bool mirrorVert = false;
19521  {
19522  mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
19523  mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
19524  }
19525 
19526  painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
19527  QCPAxisRect::draw(painter);
19528 }
19529 
19536 {
19537  if (rect().isEmpty())
19538  return;
19539 
19540  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
19542  int w, h;
19543  QVector<double> data(n);
19544  for (int i=0; i<n; ++i)
19545  data[i] = i;
19547  {
19548  w = n;
19549  h = rect().height();
19550  mGradientImage = QImage(w, h, format);
19551  QVector<QRgb*> pixels;
19552  for (int y=0; y<h; ++y)
19553  pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
19554  mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
19555  for (int y=1; y<h; ++y)
19556  memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
19557  } else
19558  {
19559  w = rect().width();
19560  h = n;
19561  mGradientImage = QImage(w, h, format);
19562  for (int y=0; y<h; ++y)
19563  {
19564  QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
19565  const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
19566  for (int x=0; x<w; ++x)
19567  pixels[x] = lineColor;
19568  }
19569  }
19570  mGradientImageInvalidated = false;
19571 }
19572 
19578 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
19579 {
19580  // axis bases of four axes shall always (de-)selected synchronously:
19581  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
19582  foreach (QCPAxis::AxisType type, allAxisTypes)
19583  {
19584  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
19585  if (senderAxis->axisType() == type)
19586  continue;
19587 
19588  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
19589  {
19590  if (selectedParts.testFlag(QCPAxis::spAxis))
19591  axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
19592  else
19593  axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
19594  }
19595  }
19596 }
19597 
19603 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
19604 {
19605  // synchronize axis base selectability:
19606  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
19607  foreach (QCPAxis::AxisType type, allAxisTypes)
19608  {
19609  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
19610  if (senderAxis->axisType() == type)
19611  continue;
19612 
19613  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
19614  {
19615  if (selectableParts.testFlag(QCPAxis::spAxis))
19616  axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
19617  else
19618  axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
19619  }
19620  }
19621 }
19622 /* end of 'src/layoutelements/layoutelement-colorscale.cpp' */
19623 
19624 
19625 /* including file 'src/plottables/plottable-graph.cpp', size 72363 */
19626 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
19627 
19631 
19646 /* start documentation of inline functions */
19647 
19697 /* end documentation of inline functions */
19698 
19703  key(0),
19704  value(0)
19705 {
19706 }
19707 
19712  key(key),
19713  value(value)
19714 {
19715 }
19716 
19717 
19721 
19760 /* start of documentation of inline functions */
19761 
19769 /* end of documentation of inline functions */
19770 
19783 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
19784  QCPAbstractPlottable1D<QCPGraphData>(keyAxis, valueAxis)
19785 {
19786  // special handling for QCPGraphs to maintain the simple graph interface:
19787  mParentPlot->registerGraph(this);
19788 
19789  setPen(QPen(Qt::blue, 0));
19790  setBrush(Qt::NoBrush);
19791 
19793  setScatterSkip(0);
19795  setAdaptiveSampling(true);
19796 }
19797 
19799 {
19800 }
19801 
19817 void QCPGraph::setData(QSharedPointer<QCPGraphDataContainer> data)
19818 {
19819  mDataContainer = data;
19820 }
19821 
19833 void QCPGraph::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
19834 {
19835  mDataContainer->clear();
19836  addData(keys, values, alreadySorted);
19837 }
19838 
19846 {
19847  mLineStyle = ls;
19848 }
19849 
19857 {
19858  mScatterStyle = style;
19859 }
19860 
19873 {
19874  mScatterSkip = qMax(0, skip);
19875 }
19876 
19887 {
19888  // prevent setting channel target to this graph itself:
19889  if (targetGraph == this)
19890  {
19891  qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
19892  mChannelFillGraph = 0;
19893  return;
19894  }
19895  // prevent setting channel target to a graph not in the plot:
19896  if (targetGraph && targetGraph->mParentPlot != mParentPlot)
19897  {
19898  qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
19899  mChannelFillGraph = 0;
19900  return;
19901  }
19902 
19903  mChannelFillGraph = targetGraph;
19904 }
19905 
19938 {
19939  mAdaptiveSampling = enabled;
19940 }
19941 
19954 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
19955 {
19956  if (keys.size() != values.size())
19957  qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
19958  const int n = qMin(keys.size(), values.size());
19959  QVector<QCPGraphData> tempData(n);
19960  QVector<QCPGraphData>::iterator it = tempData.begin();
19961  const QVector<QCPGraphData>::iterator itEnd = tempData.end();
19962  int i = 0;
19963  while (it != itEnd)
19964  {
19965  it->key = keys[i];
19966  it->value = values[i];
19967  ++it;
19968  ++i;
19969  }
19970  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
19971 }
19972 
19980 void QCPGraph::addData(double key, double value)
19981 {
19982  mDataContainer->add(QCPGraphData(key, value));
19983 }
19984 
19985 /* inherits documentation from base class */
19986 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19987 {
19988  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
19989  return -1;
19990  if (!mKeyAxis || !mValueAxis)
19991  return -1;
19992 
19993  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19994  {
19995  QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
19996  double result = pointDistance(pos, closestDataPoint);
19997  if (details)
19998  {
19999  int pointIndex = closestDataPoint-mDataContainer->constBegin();
20000  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
20001  }
20002  return result;
20003  } else
20004  return -1;
20005 }
20006 
20007 /* inherits documentation from base class */
20008 QCPRange QCPGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
20009 {
20010  return mDataContainer->keyRange(foundRange, inSignDomain);
20011 }
20012 
20013 /* inherits documentation from base class */
20014 QCPRange QCPGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
20015 {
20016  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
20017 }
20018 
20019 /* inherits documentation from base class */
20021 {
20022  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
20023  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
20024  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
20025 
20026  QVector<QPointF> lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while iterating over segments
20027 
20028  // loop over and draw segments of unselected/selected data:
20029  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
20030  getDataSegments(selectedSegments, unselectedSegments);
20031  allSegments << unselectedSegments << selectedSegments;
20032  for (int i=0; i<allSegments.size(); ++i)
20033  {
20034  bool isSelectedSegment = i >= unselectedSegments.size();
20035  // get line pixel points appropriate to line style:
20036  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)
20037  getLines(&lines, lineDataRange);
20038 
20039  // check data validity if flag set:
20040 #ifdef QCUSTOMPLOT_CHECK_DATA
20042  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
20043  {
20044  if (QCP::isInvalidData(it->key, it->value))
20045  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
20046  }
20047 #endif
20048 
20049  // draw fill of graph:
20050  if (isSelectedSegment && mSelectionDecorator)
20051  mSelectionDecorator->applyBrush(painter);
20052  else
20053  painter->setBrush(mBrush);
20054  painter->setPen(Qt::NoPen);
20055  drawFill(painter, &lines);
20056 
20057  // draw line:
20058  if (mLineStyle != lsNone)
20059  {
20060  if (isSelectedSegment && mSelectionDecorator)
20061  mSelectionDecorator->applyPen(painter);
20062  else
20063  painter->setPen(mPen);
20064  painter->setBrush(Qt::NoBrush);
20065  if (mLineStyle == lsImpulse)
20066  drawImpulsePlot(painter, lines);
20067  else
20068  drawLinePlot(painter, lines); // also step plots can be drawn as a line plot
20069  }
20070 
20071  // draw scatters:
20072  QCPScatterStyle finalScatterStyle = mScatterStyle;
20073  if (isSelectedSegment && mSelectionDecorator)
20075  if (!finalScatterStyle.isNone())
20076  {
20077  getScatters(&scatters, allSegments.at(i));
20078  drawScatterPlot(painter, scatters, finalScatterStyle);
20079  }
20080  }
20081 
20082  // draw other selection decoration that isn't just line/scatter pens and brushes:
20083  if (mSelectionDecorator)
20085 }
20086 
20087 /* inherits documentation from base class */
20088 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20089 {
20090  // draw fill:
20091  if (mBrush.style() != Qt::NoBrush)
20092  {
20093  applyFillAntialiasingHint(painter);
20094  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
20095  }
20096  // draw line vertically centered:
20097  if (mLineStyle != lsNone)
20098  {
20100  painter->setPen(mPen);
20101  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
20102  }
20103  // draw scatter symbol:
20104  if (!mScatterStyle.isNone())
20105  {
20107  // scale scatter pixmap if it's too large to fit in legend icon rect:
20108  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
20109  {
20110  QCPScatterStyle scaledStyle(mScatterStyle);
20111  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
20112  scaledStyle.applyTo(painter, mPen);
20113  scaledStyle.drawShape(painter, QRectF(rect).center());
20114  } else
20115  {
20116  mScatterStyle.applyTo(painter, mPen);
20117  mScatterStyle.drawShape(painter, QRectF(rect).center());
20118  }
20119  }
20120 }
20121 
20142 void QCPGraph::getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const
20143 {
20144  if (!lines) return;
20146  getVisibleDataBounds(begin, end, dataRange);
20147  if (begin == end)
20148  {
20149  lines->clear();
20150  return;
20151  }
20152 
20153  QVector<QCPGraphData> lineData;
20154  if (mLineStyle != lsNone)
20155  getOptimizedLineData(&lineData, begin, end);
20156 
20157  switch (mLineStyle)
20158  {
20159  case lsNone: lines->clear(); break;
20160  case lsLine: *lines = dataToLines(lineData); break;
20161  case lsStepLeft: *lines = dataToStepLeftLines(lineData); break;
20162  case lsStepRight: *lines = dataToStepRightLines(lineData); break;
20163  case lsStepCenter: *lines = dataToStepCenterLines(lineData); break;
20164  case lsImpulse: *lines = dataToImpulseLines(lineData); break;
20165  }
20166 }
20167 
20180 void QCPGraph::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const
20181 {
20182  if (!scatters) return;
20183  QCPAxis *keyAxis = mKeyAxis.data();
20184  QCPAxis *valueAxis = mValueAxis.data();
20185  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; scatters->clear(); return; }
20186 
20188  getVisibleDataBounds(begin, end, dataRange);
20189  if (begin == end)
20190  {
20191  scatters->clear();
20192  return;
20193  }
20194 
20195  QVector<QCPGraphData> data;
20196  getOptimizedScatterData(&data, begin, end);
20197  scatters->resize(data.size());
20198  if (keyAxis->orientation() == Qt::Vertical)
20199  {
20200  for (int i=0; i<data.size(); ++i)
20201  {
20202  if (!qIsNaN(data.at(i).value))
20203  {
20204  (*scatters)[i].setX(valueAxis->coordToPixel(data.at(i).value));
20205  (*scatters)[i].setY(keyAxis->coordToPixel(data.at(i).key));
20206  }
20207  }
20208  } else
20209  {
20210  for (int i=0; i<data.size(); ++i)
20211  {
20212  if (!qIsNaN(data.at(i).value))
20213  {
20214  (*scatters)[i].setX(keyAxis->coordToPixel(data.at(i).key));
20215  (*scatters)[i].setY(valueAxis->coordToPixel(data.at(i).value));
20216  }
20217  }
20218  }
20219 }
20220 
20231 QVector<QPointF> QCPGraph::dataToLines(const QVector<QCPGraphData> &data) const
20232 {
20233  QVector<QPointF> result;
20234  QCPAxis *keyAxis = mKeyAxis.data();
20235  QCPAxis *valueAxis = mValueAxis.data();
20236  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20237 
20238  result.reserve(data.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
20239  result.resize(data.size());
20240 
20241  // transform data points to pixels:
20242  if (keyAxis->orientation() == Qt::Vertical)
20243  {
20244  for (int i=0; i<data.size(); ++i)
20245  {
20246  result[i].setX(valueAxis->coordToPixel(data.at(i).value));
20247  result[i].setY(keyAxis->coordToPixel(data.at(i).key));
20248  }
20249  } else // key axis is horizontal
20250  {
20251  for (int i=0; i<data.size(); ++i)
20252  {
20253  result[i].setX(keyAxis->coordToPixel(data.at(i).key));
20254  result[i].setY(valueAxis->coordToPixel(data.at(i).value));
20255  }
20256  }
20257  return result;
20258 }
20259 
20270 QVector<QPointF> QCPGraph::dataToStepLeftLines(const QVector<QCPGraphData> &data) const
20271 {
20272  QVector<QPointF> result;
20273  QCPAxis *keyAxis = mKeyAxis.data();
20274  QCPAxis *valueAxis = mValueAxis.data();
20275  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20276 
20277  result.reserve(data.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
20278  result.resize(data.size()*2);
20279 
20280  // calculate steps from data and transform to pixel coordinates:
20281  if (keyAxis->orientation() == Qt::Vertical)
20282  {
20283  double lastValue = valueAxis->coordToPixel(data.first().value);
20284  for (int i=0; i<data.size(); ++i)
20285  {
20286  const double key = keyAxis->coordToPixel(data.at(i).key);
20287  result[i*2+0].setX(lastValue);
20288  result[i*2+0].setY(key);
20289  lastValue = valueAxis->coordToPixel(data.at(i).value);
20290  result[i*2+1].setX(lastValue);
20291  result[i*2+1].setY(key);
20292  }
20293  } else // key axis is horizontal
20294  {
20295  double lastValue = valueAxis->coordToPixel(data.first().value);
20296  for (int i=0; i<data.size(); ++i)
20297  {
20298  const double key = keyAxis->coordToPixel(data.at(i).key);
20299  result[i*2+0].setX(key);
20300  result[i*2+0].setY(lastValue);
20301  lastValue = valueAxis->coordToPixel(data.at(i).value);
20302  result[i*2+1].setX(key);
20303  result[i*2+1].setY(lastValue);
20304  }
20305  }
20306  return result;
20307 }
20308 
20319 QVector<QPointF> QCPGraph::dataToStepRightLines(const QVector<QCPGraphData> &data) const
20320 {
20321  QVector<QPointF> result;
20322  QCPAxis *keyAxis = mKeyAxis.data();
20323  QCPAxis *valueAxis = mValueAxis.data();
20324  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20325 
20326  result.reserve(data.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
20327  result.resize(data.size()*2);
20328 
20329  // calculate steps from data and transform to pixel coordinates:
20330  if (keyAxis->orientation() == Qt::Vertical)
20331  {
20332  double lastKey = keyAxis->coordToPixel(data.first().key);
20333  for (int i=0; i<data.size(); ++i)
20334  {
20335  const double value = valueAxis->coordToPixel(data.at(i).value);
20336  result[i*2+0].setX(value);
20337  result[i*2+0].setY(lastKey);
20338  lastKey = keyAxis->coordToPixel(data.at(i).key);
20339  result[i*2+1].setX(value);
20340  result[i*2+1].setY(lastKey);
20341  }
20342  } else // key axis is horizontal
20343  {
20344  double lastKey = keyAxis->coordToPixel(data.first().key);
20345  for (int i=0; i<data.size(); ++i)
20346  {
20347  const double value = valueAxis->coordToPixel(data.at(i).value);
20348  result[i*2+0].setX(lastKey);
20349  result[i*2+0].setY(value);
20350  lastKey = keyAxis->coordToPixel(data.at(i).key);
20351  result[i*2+1].setX(lastKey);
20352  result[i*2+1].setY(value);
20353  }
20354  }
20355  return result;
20356 }
20357 
20368 QVector<QPointF> QCPGraph::dataToStepCenterLines(const QVector<QCPGraphData> &data) const
20369 {
20370  QVector<QPointF> result;
20371  QCPAxis *keyAxis = mKeyAxis.data();
20372  QCPAxis *valueAxis = mValueAxis.data();
20373  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20374 
20375  result.reserve(data.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
20376  result.resize(data.size()*2);
20377 
20378  // calculate steps from data and transform to pixel coordinates:
20379  if (keyAxis->orientation() == Qt::Vertical)
20380  {
20381  double lastKey = keyAxis->coordToPixel(data.first().key);
20382  double lastValue = valueAxis->coordToPixel(data.first().value);
20383  result[0].setX(lastValue);
20384  result[0].setY(lastKey);
20385  for (int i=1; i<data.size(); ++i)
20386  {
20387  const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;
20388  result[i*2-1].setX(lastValue);
20389  result[i*2-1].setY(key);
20390  lastValue = valueAxis->coordToPixel(data.at(i).value);
20391  lastKey = keyAxis->coordToPixel(data.at(i).key);
20392  result[i*2+0].setX(lastValue);
20393  result[i*2+0].setY(key);
20394  }
20395  result[data.size()*2-1].setX(lastValue);
20396  result[data.size()*2-1].setY(lastKey);
20397  } else // key axis is horizontal
20398  {
20399  double lastKey = keyAxis->coordToPixel(data.first().key);
20400  double lastValue = valueAxis->coordToPixel(data.first().value);
20401  result[0].setX(lastKey);
20402  result[0].setY(lastValue);
20403  for (int i=1; i<data.size(); ++i)
20404  {
20405  const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;
20406  result[i*2-1].setX(key);
20407  result[i*2-1].setY(lastValue);
20408  lastValue = valueAxis->coordToPixel(data.at(i).value);
20409  lastKey = keyAxis->coordToPixel(data.at(i).key);
20410  result[i*2+0].setX(key);
20411  result[i*2+0].setY(lastValue);
20412  }
20413  result[data.size()*2-1].setX(lastKey);
20414  result[data.size()*2-1].setY(lastValue);
20415  }
20416  return result;
20417 }
20418 
20429 QVector<QPointF> QCPGraph::dataToImpulseLines(const QVector<QCPGraphData> &data) const
20430 {
20431  QVector<QPointF> result;
20432  QCPAxis *keyAxis = mKeyAxis.data();
20433  QCPAxis *valueAxis = mValueAxis.data();
20434  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20435 
20436  result.resize(data.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
20437 
20438  // transform data points to pixels:
20439  if (keyAxis->orientation() == Qt::Vertical)
20440  {
20441  for (int i=0; i<data.size(); ++i)
20442  {
20443  const double key = keyAxis->coordToPixel(data.at(i).key);
20444  result[i*2+0].setX(valueAxis->coordToPixel(0));
20445  result[i*2+0].setY(key);
20446  result[i*2+1].setX(valueAxis->coordToPixel(data.at(i).value));
20447  result[i*2+1].setY(key);
20448  }
20449  } else // key axis is horizontal
20450  {
20451  for (int i=0; i<data.size(); ++i)
20452  {
20453  const double key = keyAxis->coordToPixel(data.at(i).key);
20454  result[i*2+0].setX(key);
20455  result[i*2+0].setY(valueAxis->coordToPixel(0));
20456  result[i*2+1].setX(key);
20457  result[i*2+1].setY(valueAxis->coordToPixel(data.at(i).value));
20458  }
20459  }
20460  return result;
20461 }
20462 
20479 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const
20480 {
20481  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
20482  if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0) return;
20483 
20484  applyFillAntialiasingHint(painter);
20485  if (!mChannelFillGraph)
20486  {
20487  // draw base fill under graph, fill goes all the way to the zero-value-line:
20488  addFillBasePoints(lines);
20489  painter->drawPolygon(QPolygonF(*lines));
20490  removeFillBasePoints(lines);
20491  } else
20492  {
20493  // draw channel fill between this graph and mChannelFillGraph:
20494  painter->drawPolygon(getChannelFillPolygon(lines));
20495  }
20496 }
20497 
20505 void QCPGraph::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const
20506 {
20508  style.applyTo(painter, mPen);
20509  for (int i=0; i<scatters.size(); ++i)
20510  style.drawShape(painter, scatters.at(i).x(), scatters.at(i).y());
20511 }
20512 
20519 void QCPGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
20520 {
20521  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
20522  {
20524  drawPolyline(painter, lines);
20525  }
20526 }
20527 
20536 void QCPGraph::drawImpulsePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
20537 {
20538  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
20539  {
20541  QPen oldPen = painter->pen();
20542  QPen newPen = painter->pen();
20543  newPen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
20544  painter->setPen(newPen);
20545  painter->drawLines(lines);
20546  painter->setPen(oldPen);
20547  }
20548 }
20549 
20562 void QCPGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
20563 {
20564  if (!lineData) return;
20565  QCPAxis *keyAxis = mKeyAxis.data();
20566  QCPAxis *valueAxis = mValueAxis.data();
20567  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
20568  if (begin == end) return;
20569 
20570  int dataCount = end-begin;
20571  int maxCount = std::numeric_limits<int>::max();
20572  if (mAdaptiveSampling)
20573  {
20574  double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));
20575  if (2*keyPixelSpan+2 < (double)std::numeric_limits<int>::max())
20576  maxCount = 2*keyPixelSpan+2;
20577  }
20578 
20579  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
20580  {
20582  double minValue = it->value;
20583  double maxValue = it->value;
20584  QCPGraphDataContainer::const_iterator currentIntervalFirstPoint = it;
20585  int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction
20586  int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
20587  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound));
20588  double lastIntervalEndKey = currentIntervalStartKey;
20589  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
20590  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
20591  int intervalDataCount = 1;
20592  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
20593  while (it != end)
20594  {
20595  if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
20596  {
20597  if (it->value < minValue)
20598  minValue = it->value;
20599  else if (it->value > maxValue)
20600  maxValue = it->value;
20601  ++intervalDataCount;
20602  } else // new pixel interval started
20603  {
20604  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
20605  {
20606  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
20607  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));
20608  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
20609  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
20610  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
20611  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.8, (it-1)->value));
20612  } else
20613  lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
20614  lastIntervalEndKey = (it-1)->key;
20615  minValue = it->value;
20616  maxValue = it->value;
20617  currentIntervalFirstPoint = it;
20618  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound));
20619  if (keyEpsilonVariable)
20620  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
20621  intervalDataCount = 1;
20622  }
20623  ++it;
20624  }
20625  // handle last interval:
20626  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
20627  {
20628  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
20629  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));
20630  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
20631  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
20632  } else
20633  lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
20634 
20635  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output
20636  {
20638  lineData->reserve(dataCount+2); // +2 for possible fill end points
20639  while (it != end)
20640  {
20641  lineData->append(*it);
20642  ++it;
20643  }
20644  }
20645 }
20646 
20660 {
20661  if (!scatterData) return;
20662  QCPAxis *keyAxis = mKeyAxis.data();
20663  QCPAxis *valueAxis = mValueAxis.data();
20664  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
20665 
20666  const int scatterModulo = mScatterSkip+1;
20667  const bool doScatterSkip = mScatterSkip > 0;
20668  int beginIndex = begin-mDataContainer->constBegin();
20669  int endIndex = end-mDataContainer->constBegin();
20670  while (doScatterSkip && begin != end && beginIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter
20671  {
20672  ++beginIndex;
20673  ++begin;
20674  }
20675  if (begin == end) return;
20676  int dataCount = end-begin;
20677  int maxCount = std::numeric_limits<int>::max();
20678  if (mAdaptiveSampling)
20679  {
20680  int keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));
20681  maxCount = 2*keyPixelSpan+2;
20682  }
20683 
20684  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
20685  {
20686  double valueMaxRange = valueAxis->range().upper;
20687  double valueMinRange = valueAxis->range().lower;
20689  int itIndex = beginIndex;
20690  double minValue = it->value;
20691  double maxValue = it->value;
20692  QCPGraphDataContainer::const_iterator minValueIt = it;
20693  QCPGraphDataContainer::const_iterator maxValueIt = it;
20694  QCPGraphDataContainer::const_iterator currentIntervalStart = it;
20695  int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction
20696  int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
20697  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound));
20698  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
20699  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
20700  int intervalDataCount = 1;
20701  // advance iterator to second (non-skipped) data point because adaptive sampling works in 1 point retrospect:
20702  if (!doScatterSkip)
20703  ++it;
20704  else
20705  {
20706  itIndex += scatterModulo;
20707  if (itIndex < endIndex) // make sure we didn't jump over end
20708  it += scatterModulo;
20709  else
20710  {
20711  it = end;
20712  itIndex = endIndex;
20713  }
20714  }
20715  // main loop over data points:
20716  while (it != end)
20717  {
20718  if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
20719  {
20720  if (it->value < minValue && it->value > valueMinRange && it->value < valueMaxRange)
20721  {
20722  minValue = it->value;
20723  minValueIt = it;
20724  } else if (it->value > maxValue && it->value > valueMinRange && it->value < valueMaxRange)
20725  {
20726  maxValue = it->value;
20727  maxValueIt = it;
20728  }
20729  ++intervalDataCount;
20730  } else // new pixel started
20731  {
20732  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
20733  {
20734  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
20735  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
20736  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
20737  QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;
20738  int c = 0;
20739  while (intervalIt != it)
20740  {
20741  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)
20742  scatterData->append(*intervalIt);
20743  ++c;
20744  if (!doScatterSkip)
20745  ++intervalIt;
20746  else
20747  intervalIt += scatterModulo; // since we know indices of "currentIntervalStart", "intervalIt" and "it" are multiples of scatterModulo, we can't accidentally jump over "it" here
20748  }
20749  } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)
20750  scatterData->append(*currentIntervalStart);
20751  minValue = it->value;
20752  maxValue = it->value;
20753  currentIntervalStart = it;
20754  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound));
20755  if (keyEpsilonVariable)
20756  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
20757  intervalDataCount = 1;
20758  }
20759  // advance to next data point:
20760  if (!doScatterSkip)
20761  ++it;
20762  else
20763  {
20764  itIndex += scatterModulo;
20765  if (itIndex < endIndex) // make sure we didn't jump over end
20766  it += scatterModulo;
20767  else
20768  {
20769  it = end;
20770  itIndex = endIndex;
20771  }
20772  }
20773  }
20774  // handle last interval:
20775  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
20776  {
20777  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
20778  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
20779  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
20780  QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;
20781  int intervalItIndex = intervalIt-mDataContainer->constBegin();
20782  int c = 0;
20783  while (intervalIt != it)
20784  {
20785  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)
20786  scatterData->append(*intervalIt);
20787  ++c;
20788  if (!doScatterSkip)
20789  ++intervalIt;
20790  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:
20791  {
20792  intervalItIndex += scatterModulo;
20793  if (intervalItIndex < itIndex)
20794  intervalIt += scatterModulo;
20795  else
20796  {
20797  intervalIt = it;
20798  intervalItIndex = itIndex;
20799  }
20800  }
20801  }
20802  } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)
20803  scatterData->append(*currentIntervalStart);
20804 
20805  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output
20806  {
20808  int itIndex = beginIndex;
20809  scatterData->reserve(dataCount);
20810  while (it != end)
20811  {
20812  scatterData->append(*it);
20813  // advance to next data point:
20814  if (!doScatterSkip)
20815  ++it;
20816  else
20817  {
20818  itIndex += scatterModulo;
20819  if (itIndex < endIndex)
20820  it += scatterModulo;
20821  else
20822  {
20823  it = end;
20824  itIndex = endIndex;
20825  }
20826  }
20827  }
20828  }
20829 }
20830 
20841 {
20842  if (rangeRestriction.isEmpty())
20843  {
20844  end = mDataContainer->constEnd();
20845  begin = end;
20846  } else
20847  {
20848  QCPAxis *keyAxis = mKeyAxis.data();
20849  QCPAxis *valueAxis = mValueAxis.data();
20850  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
20851  // get visible data range:
20852  begin = mDataContainer->findBegin(keyAxis->range().lower);
20853  end = mDataContainer->findEnd(keyAxis->range().upper);
20854  // limit lower/upperEnd to rangeRestriction:
20855  mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction); // this also ensures rangeRestriction outside data bounds doesn't break anything
20856  }
20857 }
20858 
20874 void QCPGraph::addFillBasePoints(QVector<QPointF> *lines) const
20875 {
20876  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
20877  if (!lines) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; }
20878  if (lines->isEmpty()) return;
20879 
20880  // append points that close the polygon fill at the key axis:
20881  if (mKeyAxis.data()->orientation() == Qt::Vertical)
20882  {
20883  *lines << upperFillBasePoint(lines->last().y());
20884  *lines << lowerFillBasePoint(lines->first().y());
20885  } else
20886  {
20887  *lines << upperFillBasePoint(lines->last().x());
20888  *lines << lowerFillBasePoint(lines->first().x());
20889  }
20890 }
20891 
20898 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lines) const
20899 {
20900  if (!lines) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; }
20901  if (lines->isEmpty()) return;
20902 
20903  lines->remove(lines->size()-2, 2);
20904 }
20905 
20920 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
20921 {
20922  QCPAxis *keyAxis = mKeyAxis.data();
20923  QCPAxis *valueAxis = mValueAxis.data();
20924  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
20925 
20926  QPointF point;
20927  if (valueAxis->scaleType() == QCPAxis::stLinear)
20928  {
20929  if (keyAxis->axisType() == QCPAxis::atLeft)
20930  {
20931  point.setX(valueAxis->coordToPixel(0));
20932  point.setY(lowerKey);
20933  } else if (keyAxis->axisType() == QCPAxis::atRight)
20934  {
20935  point.setX(valueAxis->coordToPixel(0));
20936  point.setY(lowerKey);
20937  } else if (keyAxis->axisType() == QCPAxis::atTop)
20938  {
20939  point.setX(lowerKey);
20940  point.setY(valueAxis->coordToPixel(0));
20941  } else if (keyAxis->axisType() == QCPAxis::atBottom)
20942  {
20943  point.setX(lowerKey);
20944  point.setY(valueAxis->coordToPixel(0));
20945  }
20946  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
20947  {
20948  // In logarithmic scaling we can't just draw to value zero so we just fill all the way
20949  // to the axis which is in the direction towards zero
20950  if (keyAxis->orientation() == Qt::Vertical)
20951  {
20952  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
20953  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
20954  point.setX(keyAxis->axisRect()->right());
20955  else
20956  point.setX(keyAxis->axisRect()->left());
20957  point.setY(lowerKey);
20958  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
20959  {
20960  point.setX(lowerKey);
20961  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
20962  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
20963  point.setY(keyAxis->axisRect()->top());
20964  else
20965  point.setY(keyAxis->axisRect()->bottom());
20966  }
20967  }
20968  return point;
20969 }
20970 
20985 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
20986 {
20987  QCPAxis *keyAxis = mKeyAxis.data();
20988  QCPAxis *valueAxis = mValueAxis.data();
20989  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
20990 
20991  QPointF point;
20992  if (valueAxis->scaleType() == QCPAxis::stLinear)
20993  {
20994  if (keyAxis->axisType() == QCPAxis::atLeft)
20995  {
20996  point.setX(valueAxis->coordToPixel(0));
20997  point.setY(upperKey);
20998  } else if (keyAxis->axisType() == QCPAxis::atRight)
20999  {
21000  point.setX(valueAxis->coordToPixel(0));
21001  point.setY(upperKey);
21002  } else if (keyAxis->axisType() == QCPAxis::atTop)
21003  {
21004  point.setX(upperKey);
21005  point.setY(valueAxis->coordToPixel(0));
21006  } else if (keyAxis->axisType() == QCPAxis::atBottom)
21007  {
21008  point.setX(upperKey);
21009  point.setY(valueAxis->coordToPixel(0));
21010  }
21011  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
21012  {
21013  // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
21014  // to the axis which is in the direction towards 0
21015  if (keyAxis->orientation() == Qt::Vertical)
21016  {
21017  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
21018  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
21019  point.setX(keyAxis->axisRect()->right());
21020  else
21021  point.setX(keyAxis->axisRect()->left());
21022  point.setY(upperKey);
21023  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
21024  {
21025  point.setX(upperKey);
21026  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
21027  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
21028  point.setY(keyAxis->axisRect()->top());
21029  else
21030  point.setY(keyAxis->axisRect()->bottom());
21031  }
21032  }
21033  return point;
21034 }
21035 
21048 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lines) const
21049 {
21050  if (!mChannelFillGraph)
21051  return QPolygonF();
21052 
21053  QCPAxis *keyAxis = mKeyAxis.data();
21054  QCPAxis *valueAxis = mValueAxis.data();
21055  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
21056  if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
21057 
21058  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
21059  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)
21060 
21061  if (lines->isEmpty()) return QPolygonF();
21062  QVector<QPointF> otherData;
21063  mChannelFillGraph.data()->getLines(&otherData, QCPDataRange(0, mChannelFillGraph.data()->dataCount()));
21064  if (otherData.isEmpty()) return QPolygonF();
21065  QVector<QPointF> thisData;
21066  thisData.reserve(lines->size()+otherData.size()); // because we will join both vectors at end of this function
21067  for (int i=0; i<lines->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
21068  thisData << lines->at(i);
21069 
21070  // pointers to be able to swap them, depending which data range needs cropping:
21071  QVector<QPointF> *staticData = &thisData;
21072  QVector<QPointF> *croppedData = &otherData;
21073 
21074  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
21075  if (keyAxis->orientation() == Qt::Horizontal)
21076  {
21077  // x is key
21078  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
21079  if (staticData->first().x() > staticData->last().x())
21080  {
21081  int size = staticData->size();
21082  for (int i=0; i<size/2; ++i)
21083  qSwap((*staticData)[i], (*staticData)[size-1-i]);
21084  }
21085  if (croppedData->first().x() > croppedData->last().x())
21086  {
21087  int size = croppedData->size();
21088  for (int i=0; i<size/2; ++i)
21089  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
21090  }
21091  // crop lower bound:
21092  if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
21093  qSwap(staticData, croppedData);
21094  int lowBound = findIndexBelowX(croppedData, staticData->first().x());
21095  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
21096  croppedData->remove(0, lowBound);
21097  // set lowest point of cropped data to fit exactly key position of first static data
21098  // point via linear interpolation:
21099  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21100  double slope;
21101  if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
21102  slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
21103  else
21104  slope = 0;
21105  (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
21106  (*croppedData)[0].setX(staticData->first().x());
21107 
21108  // crop upper bound:
21109  if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
21110  qSwap(staticData, croppedData);
21111  int highBound = findIndexAboveX(croppedData, staticData->last().x());
21112  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
21113  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
21114  // set highest point of cropped data to fit exactly key position of last static data
21115  // point via linear interpolation:
21116  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21117  int li = croppedData->size()-1; // last index
21118  if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
21119  slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
21120  else
21121  slope = 0;
21122  (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
21123  (*croppedData)[li].setX(staticData->last().x());
21124  } else // mKeyAxis->orientation() == Qt::Vertical
21125  {
21126  // y is key
21127  // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
21128  // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
21129  // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
21130  if (staticData->first().y() < staticData->last().y())
21131  {
21132  int size = staticData->size();
21133  for (int i=0; i<size/2; ++i)
21134  qSwap((*staticData)[i], (*staticData)[size-1-i]);
21135  }
21136  if (croppedData->first().y() < croppedData->last().y())
21137  {
21138  int size = croppedData->size();
21139  for (int i=0; i<size/2; ++i)
21140  qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
21141  }
21142  // crop lower bound:
21143  if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
21144  qSwap(staticData, croppedData);
21145  int lowBound = findIndexAboveY(croppedData, staticData->first().y());
21146  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
21147  croppedData->remove(0, lowBound);
21148  // set lowest point of cropped data to fit exactly key position of first static data
21149  // point via linear interpolation:
21150  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21151  double slope;
21152  if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
21153  slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
21154  else
21155  slope = 0;
21156  (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
21157  (*croppedData)[0].setY(staticData->first().y());
21158 
21159  // crop upper bound:
21160  if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
21161  qSwap(staticData, croppedData);
21162  int highBound = findIndexBelowY(croppedData, staticData->last().y());
21163  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
21164  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
21165  // set highest point of cropped data to fit exactly key position of last static data
21166  // point via linear interpolation:
21167  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21168  int li = croppedData->size()-1; // last index
21169  if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
21170  slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
21171  else
21172  slope = 0;
21173  (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
21174  (*croppedData)[li].setY(staticData->last().y());
21175  }
21176 
21177  // return joined:
21178  for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
21179  thisData << otherData.at(i);
21180  return QPolygonF(thisData);
21181 }
21182 
21190 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
21191 {
21192  for (int i=data->size()-1; i>=0; --i)
21193  {
21194  if (data->at(i).x() < x)
21195  {
21196  if (i<data->size()-1)
21197  return i+1;
21198  else
21199  return data->size()-1;
21200  }
21201  }
21202  return -1;
21203 }
21204 
21212 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
21213 {
21214  for (int i=0; i<data->size(); ++i)
21215  {
21216  if (data->at(i).x() > x)
21217  {
21218  if (i>0)
21219  return i-1;
21220  else
21221  return 0;
21222  }
21223  }
21224  return -1;
21225 }
21226 
21234 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
21235 {
21236  for (int i=0; i<data->size(); ++i)
21237  {
21238  if (data->at(i).y() < y)
21239  {
21240  if (i>0)
21241  return i-1;
21242  else
21243  return 0;
21244  }
21245  }
21246  return -1;
21247 }
21248 
21260 double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
21261 {
21262  closestData = mDataContainer->constEnd();
21263  if (mDataContainer->isEmpty())
21264  return -1.0;
21265  if (mLineStyle == lsNone && mScatterStyle.isNone())
21266  return -1.0;
21267 
21268  // calculate minimum distances to graph data points and find closestData iterator:
21269  double minDistSqr = std::numeric_limits<double>::max();
21270  // determine which key range comes into question, taking selection tolerance around pos into account:
21271  double posKeyMin, posKeyMax, dummy;
21272  pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);
21273  pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy);
21274  if (posKeyMin > posKeyMax)
21275  qSwap(posKeyMin, posKeyMax);
21276  // iterate over found data points and then choose the one with the shortest distance to pos:
21277  QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true);
21278  QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true);
21279  for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it)
21280  {
21281  const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
21282  if (currentDistSqr < minDistSqr)
21283  {
21284  minDistSqr = currentDistSqr;
21285  closestData = it;
21286  }
21287  }
21288 
21289  // calculate distance to graph line if there is one (if so, will probably be smaller than distance to closest data point):
21290  if (mLineStyle != lsNone)
21291  {
21292  // line displayed, calculate distance to line segments:
21293  QVector<QPointF> lineData;
21294  getLines(&lineData, QCPDataRange(0, dataCount()));
21295  QCPVector2D p(pixelPoint);
21296  const int step = mLineStyle==lsImpulse ? 2 : 1; // impulse plot differs from other line styles in that the lineData points are only pairwise connected
21297  for (int i=0; i<lineData.size()-1; i+=step)
21298  {
21299  const double currentDistSqr = p.distanceSquaredToLine(lineData.at(i), lineData.at(i+1));
21300  if (currentDistSqr < minDistSqr)
21301  minDistSqr = currentDistSqr;
21302  }
21303  }
21304 
21305  return qSqrt(minDistSqr);
21306 }
21307 
21316 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
21317 {
21318  for (int i=data->size()-1; i>=0; --i)
21319  {
21320  if (data->at(i).y() > y)
21321  {
21322  if (i<data->size()-1)
21323  return i+1;
21324  else
21325  return data->size()-1;
21326  }
21327  }
21328  return -1;
21329 }
21330 /* end of 'src/plottables/plottable-graph.cpp' */
21331 
21332 
21333 /* including file 'src/plottables/plottable-curve.cpp', size 60009 */
21334 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
21335 
21339 
21355 /* start documentation of inline functions */
21356 
21407 /* end documentation of inline functions */
21408 
21413  t(0),
21414  key(0),
21415  value(0)
21416 {
21417 }
21418 
21422 QCPCurveData::QCPCurveData(double t, double key, double value) :
21423  t(t),
21424  key(key),
21425  value(value)
21426 {
21427 }
21428 
21429 
21433 
21470 /* start of documentation of inline functions */
21471 
21479 /* end of documentation of inline functions */
21480 
21491 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
21492  QCPAbstractPlottable1D<QCPCurveData>(keyAxis, valueAxis)
21493 {
21494  // modify inherited properties from abstract plottable:
21495  setPen(QPen(Qt::blue, 0));
21496  setBrush(Qt::NoBrush);
21497 
21500 }
21501 
21503 {
21504 }
21505 
21521 void QCPCurve::setData(QSharedPointer<QCPCurveDataContainer> data)
21522 {
21523  mDataContainer = data;
21524 }
21525 
21537 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
21538 {
21539  mDataContainer->clear();
21540  addData(t, keys, values, alreadySorted);
21541 }
21542 
21543 
21555 void QCPCurve::setData(const QVector<double> &keys, const QVector<double> &values)
21556 {
21557  mDataContainer->clear();
21558  addData(keys, values);
21559 }
21560 
21569 {
21570  mScatterStyle = style;
21571 }
21572 
21585 {
21586  mScatterSkip = qMax(0, skip);
21587 }
21588 
21597 {
21598  mLineStyle = style;
21599 }
21600 
21613 void QCPCurve::addData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
21614 {
21615  if (t.size() != keys.size() || t.size() != values.size())
21616  qDebug() << Q_FUNC_INFO << "ts, keys and values have different sizes:" << t.size() << keys.size() << values.size();
21617  const int n = qMin(qMin(t.size(), keys.size()), values.size());
21618  QVector<QCPCurveData> tempData(n);
21619  QVector<QCPCurveData>::iterator it = tempData.begin();
21620  const QVector<QCPCurveData>::iterator itEnd = tempData.end();
21621  int i = 0;
21622  while (it != itEnd)
21623  {
21624  it->t = t[i];
21625  it->key = keys[i];
21626  it->value = values[i];
21627  ++it;
21628  ++i;
21629  }
21630  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
21631 }
21632 
21645 void QCPCurve::addData(const QVector<double> &keys, const QVector<double> &values)
21646 {
21647  if (keys.size() != values.size())
21648  qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
21649  const int n = qMin(keys.size(), values.size());
21650  double tStart;
21651  if (!mDataContainer->isEmpty())
21652  tStart = (mDataContainer->constEnd()-1)->t + 1.0;
21653  else
21654  tStart = 0;
21655  QVector<QCPCurveData> tempData(n);
21656  QVector<QCPCurveData>::iterator it = tempData.begin();
21657  const QVector<QCPCurveData>::iterator itEnd = tempData.end();
21658  int i = 0;
21659  while (it != itEnd)
21660  {
21661  it->t = tStart + i;
21662  it->key = keys[i];
21663  it->value = values[i];
21664  ++it;
21665  ++i;
21666  }
21667  mDataContainer->add(tempData, true); // don't modify tempData beyond this to prevent copy on write
21668 }
21669 
21676 void QCPCurve::addData(double t, double key, double value)
21677 {
21678  mDataContainer->add(QCPCurveData(t, key, value));
21679 }
21680 
21691 void QCPCurve::addData(double key, double value)
21692 {
21693  if (!mDataContainer->isEmpty())
21694  mDataContainer->add(QCPCurveData((mDataContainer->constEnd()-1)->t + 1.0, key, value));
21695  else
21696  mDataContainer->add(QCPCurveData(0.0, key, value));
21697 }
21698 
21699 /* inherits documentation from base class */
21700 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21701 {
21702  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
21703  return -1;
21704  if (!mKeyAxis || !mValueAxis)
21705  return -1;
21706 
21707  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
21708  {
21709  QCPCurveDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
21710  double result = pointDistance(pos, closestDataPoint);
21711  if (details)
21712  {
21713  int pointIndex = closestDataPoint-mDataContainer->constBegin();
21714  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
21715  }
21716  return result;
21717  } else
21718  return -1;
21719 }
21720 
21721 /* inherits documentation from base class */
21722 QCPRange QCPCurve::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
21723 {
21724  return mDataContainer->keyRange(foundRange, inSignDomain);
21725 }
21726 
21727 /* inherits documentation from base class */
21728 QCPRange QCPCurve::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
21729 {
21730  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
21731 }
21732 
21733 /* inherits documentation from base class */
21735 {
21736  if (mDataContainer->isEmpty()) return;
21737 
21738  // allocate line vector:
21739  QVector<QPointF> lines, scatters;
21740 
21741  // loop over and draw segments of unselected/selected data:
21742  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
21743  getDataSegments(selectedSegments, unselectedSegments);
21744  allSegments << unselectedSegments << selectedSegments;
21745  for (int i=0; i<allSegments.size(); ++i)
21746  {
21747  bool isSelectedSegment = i >= unselectedSegments.size();
21748 
21749  // fill with curve data:
21750  QPen finalCurvePen = mPen; // determine the final pen already here, because the line optimization depends on its stroke width
21751  if (isSelectedSegment && mSelectionDecorator)
21752  finalCurvePen = mSelectionDecorator->pen();
21753 
21754  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)
21755  getCurveLines(&lines, lineDataRange, finalCurvePen.widthF());
21756 
21757  // check data validity if flag set:
21758  #ifdef QCUSTOMPLOT_CHECK_DATA
21759  for (QCPCurveDataContainer::const_iterator it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
21760  {
21761  if (QCP::isInvalidData(it->t) ||
21762  QCP::isInvalidData(it->key, it->value))
21763  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
21764  }
21765  #endif
21766 
21767  // draw curve fill:
21768  applyFillAntialiasingHint(painter);
21769  if (isSelectedSegment && mSelectionDecorator)
21770  mSelectionDecorator->applyBrush(painter);
21771  else
21772  painter->setBrush(mBrush);
21773  painter->setPen(Qt::NoPen);
21774  if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0)
21775  painter->drawPolygon(QPolygonF(lines));
21776 
21777  // draw curve line:
21778  if (mLineStyle != lsNone)
21779  {
21780  painter->setPen(finalCurvePen);
21781  painter->setBrush(Qt::NoBrush);
21782  drawCurveLine(painter, lines);
21783  }
21784 
21785  // draw scatters:
21786  QCPScatterStyle finalScatterStyle = mScatterStyle;
21787  if (isSelectedSegment && mSelectionDecorator)
21789  if (!finalScatterStyle.isNone())
21790  {
21791  getScatters(&scatters, allSegments.at(i), finalScatterStyle.size());
21792  drawScatterPlot(painter, scatters, finalScatterStyle);
21793  }
21794  }
21795 
21796  // draw other selection decoration that isn't just line/scatter pens and brushes:
21797  if (mSelectionDecorator)
21799 }
21800 
21801 /* inherits documentation from base class */
21802 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
21803 {
21804  // draw fill:
21805  if (mBrush.style() != Qt::NoBrush)
21806  {
21807  applyFillAntialiasingHint(painter);
21808  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
21809  }
21810  // draw line vertically centered:
21811  if (mLineStyle != lsNone)
21812  {
21814  painter->setPen(mPen);
21815  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
21816  }
21817  // draw scatter symbol:
21818  if (!mScatterStyle.isNone())
21819  {
21821  // scale scatter pixmap if it's too large to fit in legend icon rect:
21822  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
21823  {
21824  QCPScatterStyle scaledStyle(mScatterStyle);
21825  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
21826  scaledStyle.applyTo(painter, mPen);
21827  scaledStyle.drawShape(painter, QRectF(rect).center());
21828  } else
21829  {
21830  mScatterStyle.applyTo(painter, mPen);
21831  mScatterStyle.drawShape(painter, QRectF(rect).center());
21832  }
21833  }
21834 }
21835 
21842 void QCPCurve::drawCurveLine(QCPPainter *painter, const QVector<QPointF> &lines) const
21843 {
21844  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
21845  {
21847  drawPolyline(painter, lines);
21848  }
21849 }
21850 
21858 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &points, const QCPScatterStyle &style) const
21859 {
21860  // draw scatter point symbols:
21862  style.applyTo(painter, mPen);
21863  for (int i=0; i<points.size(); ++i)
21864  if (!qIsNaN(points.at(i).x()) && !qIsNaN(points.at(i).y()))
21865  style.drawShape(painter, points.at(i));
21866 }
21867 
21896 void QCPCurve::getCurveLines(QVector<QPointF> *lines, const QCPDataRange &dataRange, double penWidth) const
21897 {
21898  if (!lines) return;
21899  lines->clear();
21900  QCPAxis *keyAxis = mKeyAxis.data();
21901  QCPAxis *valueAxis = mValueAxis.data();
21902  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21903 
21904  // add margins to rect to compensate for stroke width
21905  const double strokeMargin = qMax(qreal(1.0), qreal(penWidth*0.75)); // stroke radius + 50% safety
21906  const double keyMin = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*keyAxis->pixelOrientation());
21907  const double keyMax = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*keyAxis->pixelOrientation());
21908  const double valueMin = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)-strokeMargin*valueAxis->pixelOrientation());
21909  const double valueMax = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)+strokeMargin*valueAxis->pixelOrientation());
21910  QCPCurveDataContainer::const_iterator itBegin = mDataContainer->constBegin();
21912  mDataContainer->limitIteratorsToDataRange(itBegin, itEnd, dataRange);
21913  if (itBegin == itEnd)
21914  return;
21916  QCPCurveDataContainer::const_iterator prevIt = itEnd-1;
21917  int prevRegion = getRegion(prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin);
21918  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)
21919  while (it != itEnd)
21920  {
21921  const int currentRegion = getRegion(it->key, it->value, keyMin, valueMax, keyMax, valueMin);
21922  if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
21923  {
21924  if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
21925  {
21926  QPointF crossA, crossB;
21927  if (prevRegion == 5) // we're coming from R, so add this point optimized
21928  {
21929  lines->append(getOptimizedPoint(currentRegion, it->key, it->value, prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin));
21930  // 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
21931  *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
21932  } else if (mayTraverse(prevRegion, currentRegion) &&
21933  getTraverse(prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin, crossA, crossB))
21934  {
21935  // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
21936  QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
21937  getTraverseCornerPoints(prevRegion, currentRegion, keyMin, valueMax, keyMax, valueMin, beforeTraverseCornerPoints, afterTraverseCornerPoints);
21938  if (it != itBegin)
21939  {
21940  *lines << beforeTraverseCornerPoints;
21941  lines->append(crossA);
21942  lines->append(crossB);
21943  *lines << afterTraverseCornerPoints;
21944  } else
21945  {
21946  lines->append(crossB);
21947  *lines << afterTraverseCornerPoints;
21948  trailingPoints << beforeTraverseCornerPoints << crossA ;
21949  }
21950  } 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)
21951  {
21952  *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
21953  }
21954  } else // segment does end in R, so we add previous point optimized and this point at original position
21955  {
21956  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
21957  trailingPoints << getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
21958  else
21959  lines->append(getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin));
21960  lines->append(coordsToPixels(it->key, it->value));
21961  }
21962  } else // region didn't change
21963  {
21964  if (currentRegion == 5) // still in R, keep adding original points
21965  {
21966  lines->append(coordsToPixels(it->key, it->value));
21967  } else // still outside R, no need to add anything
21968  {
21969  // see how this is not doing anything? That's the main optimization...
21970  }
21971  }
21972  prevIt = it;
21973  prevRegion = currentRegion;
21974  ++it;
21975  }
21976  *lines << trailingPoints;
21977 }
21978 
21999 void QCPCurve::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange, double scatterWidth) const
22000 {
22001  if (!scatters) return;
22002  scatters->clear();
22003  QCPAxis *keyAxis = mKeyAxis.data();
22004  QCPAxis *valueAxis = mValueAxis.data();
22005  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
22006 
22009  mDataContainer->limitIteratorsToDataRange(begin, end, dataRange);
22010  if (begin == end)
22011  return;
22012  const int scatterModulo = mScatterSkip+1;
22013  const bool doScatterSkip = mScatterSkip > 0;
22014  int endIndex = end-mDataContainer->constBegin();
22015 
22016  QCPRange keyRange = keyAxis->range();
22017  QCPRange valueRange = valueAxis->range();
22018  // extend range to include width of scatter symbols:
22019  keyRange.lower = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.lower)-scatterWidth*keyAxis->pixelOrientation());
22020  keyRange.upper = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.upper)+scatterWidth*keyAxis->pixelOrientation());
22021  valueRange.lower = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.lower)-scatterWidth*valueAxis->pixelOrientation());
22022  valueRange.upper = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.upper)+scatterWidth*valueAxis->pixelOrientation());
22023 
22025  int itIndex = begin-mDataContainer->constBegin();
22026  while (doScatterSkip && it != end && itIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter
22027  {
22028  ++itIndex;
22029  ++it;
22030  }
22031  if (keyAxis->orientation() == Qt::Vertical)
22032  {
22033  while (it != end)
22034  {
22035  if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))
22036  scatters->append(QPointF(valueAxis->coordToPixel(it->value), keyAxis->coordToPixel(it->key)));
22037 
22038  // advance iterator to next (non-skipped) data point:
22039  if (!doScatterSkip)
22040  ++it;
22041  else
22042  {
22043  itIndex += scatterModulo;
22044  if (itIndex < endIndex) // make sure we didn't jump over end
22045  it += scatterModulo;
22046  else
22047  {
22048  it = end;
22049  itIndex = endIndex;
22050  }
22051  }
22052  }
22053  } else
22054  {
22055  while (it != end)
22056  {
22057  if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))
22058  scatters->append(QPointF(keyAxis->coordToPixel(it->key), valueAxis->coordToPixel(it->value)));
22059 
22060  // advance iterator to next (non-skipped) data point:
22061  if (!doScatterSkip)
22062  ++it;
22063  else
22064  {
22065  itIndex += scatterModulo;
22066  if (itIndex < endIndex) // make sure we didn't jump over end
22067  it += scatterModulo;
22068  else
22069  {
22070  it = end;
22071  itIndex = endIndex;
22072  }
22073  }
22074  }
22075  }
22076 }
22077 
22097 int QCPCurve::getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
22098 {
22099  if (key < keyMin) // region 123
22100  {
22101  if (value > valueMax)
22102  return 1;
22103  else if (value < valueMin)
22104  return 3;
22105  else
22106  return 2;
22107  } else if (key > keyMax) // region 789
22108  {
22109  if (value > valueMax)
22110  return 7;
22111  else if (value < valueMin)
22112  return 9;
22113  else
22114  return 8;
22115  } else // region 456
22116  {
22117  if (value > valueMax)
22118  return 4;
22119  else if (value < valueMin)
22120  return 6;
22121  else
22122  return 5;
22123  }
22124 }
22125 
22141 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
22142 {
22143  double intersectKey = keyMin; // initial value is just fail-safe
22144  double intersectValue = valueMax; // initial value is just fail-safe
22145  switch (otherRegion)
22146  {
22147  case 1: // top and left edge
22148  {
22149  intersectValue = valueMax;
22150  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
22151  if (intersectKey < keyMin || intersectKey > keyMax) // doesn't intersect, so must intersect other:
22152  {
22153  intersectKey = keyMin;
22154  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
22155  }
22156  break;
22157  }
22158  case 2: // left edge
22159  {
22160  intersectKey = keyMin;
22161  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
22162  break;
22163  }
22164  case 3: // bottom and left edge
22165  {
22166  intersectValue = valueMin;
22167  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
22168  if (intersectKey < keyMin || intersectKey > keyMax) // doesn't intersect, so must intersect other:
22169  {
22170  intersectKey = keyMin;
22171  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
22172  }
22173  break;
22174  }
22175  case 4: // top edge
22176  {
22177  intersectValue = valueMax;
22178  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
22179  break;
22180  }
22181  case 5:
22182  {
22183  break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
22184  }
22185  case 6: // bottom edge
22186  {
22187  intersectValue = valueMin;
22188  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
22189  break;
22190  }
22191  case 7: // top and right edge
22192  {
22193  intersectValue = valueMax;
22194  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
22195  if (intersectKey < keyMin || intersectKey > keyMax) // doesn't intersect, so must intersect other:
22196  {
22197  intersectKey = keyMax;
22198  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
22199  }
22200  break;
22201  }
22202  case 8: // right edge
22203  {
22204  intersectKey = keyMax;
22205  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
22206  break;
22207  }
22208  case 9: // bottom and right edge
22209  {
22210  intersectValue = valueMin;
22211  intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
22212  if (intersectKey < keyMin || intersectKey > keyMax) // doesn't intersect, so must intersect other:
22213  {
22214  intersectKey = keyMax;
22215  intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
22216  }
22217  break;
22218  }
22219  }
22220  return coordsToPixels(intersectKey, intersectValue);
22221 }
22222 
22241 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
22242 {
22243  QVector<QPointF> result;
22244  switch (prevRegion)
22245  {
22246  case 1:
22247  {
22248  switch (currentRegion)
22249  {
22250  case 2: { result << coordsToPixels(keyMin, valueMax); break; }
22251  case 4: { result << coordsToPixels(keyMin, valueMax); break; }
22252  case 3: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); break; }
22253  case 7: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); break; }
22254  case 6: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22255  case 8: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22256  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
22257  if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R
22258  { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }
22259  else
22260  { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }
22261  break;
22262  }
22263  }
22264  break;
22265  }
22266  case 2:
22267  {
22268  switch (currentRegion)
22269  {
22270  case 1: { result << coordsToPixels(keyMin, valueMax); break; }
22271  case 3: { result << coordsToPixels(keyMin, valueMin); break; }
22272  case 4: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22273  case 6: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22274  case 7: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }
22275  case 9: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }
22276  }
22277  break;
22278  }
22279  case 3:
22280  {
22281  switch (currentRegion)
22282  {
22283  case 2: { result << coordsToPixels(keyMin, valueMin); break; }
22284  case 6: { result << coordsToPixels(keyMin, valueMin); break; }
22285  case 1: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); break; }
22286  case 9: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); break; }
22287  case 4: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22288  case 8: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22289  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
22290  if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R
22291  { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }
22292  else
22293  { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }
22294  break;
22295  }
22296  }
22297  break;
22298  }
22299  case 4:
22300  {
22301  switch (currentRegion)
22302  {
22303  case 1: { result << coordsToPixels(keyMin, valueMax); break; }
22304  case 7: { result << coordsToPixels(keyMax, valueMax); break; }
22305  case 2: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22306  case 8: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22307  case 3: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }
22308  case 9: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }
22309  }
22310  break;
22311  }
22312  case 5:
22313  {
22314  switch (currentRegion)
22315  {
22316  case 1: { result << coordsToPixels(keyMin, valueMax); break; }
22317  case 7: { result << coordsToPixels(keyMax, valueMax); break; }
22318  case 9: { result << coordsToPixels(keyMax, valueMin); break; }
22319  case 3: { result << coordsToPixels(keyMin, valueMin); break; }
22320  }
22321  break;
22322  }
22323  case 6:
22324  {
22325  switch (currentRegion)
22326  {
22327  case 3: { result << coordsToPixels(keyMin, valueMin); break; }
22328  case 9: { result << coordsToPixels(keyMax, valueMin); break; }
22329  case 2: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22330  case 8: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22331  case 1: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }
22332  case 7: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }
22333  }
22334  break;
22335  }
22336  case 7:
22337  {
22338  switch (currentRegion)
22339  {
22340  case 4: { result << coordsToPixels(keyMax, valueMax); break; }
22341  case 8: { result << coordsToPixels(keyMax, valueMax); break; }
22342  case 1: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); break; }
22343  case 9: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); break; }
22344  case 2: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22345  case 6: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22346  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
22347  if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R
22348  { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }
22349  else
22350  { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }
22351  break;
22352  }
22353  }
22354  break;
22355  }
22356  case 8:
22357  {
22358  switch (currentRegion)
22359  {
22360  case 7: { result << coordsToPixels(keyMax, valueMax); break; }
22361  case 9: { result << coordsToPixels(keyMax, valueMin); break; }
22362  case 4: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22363  case 6: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22364  case 1: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }
22365  case 3: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }
22366  }
22367  break;
22368  }
22369  case 9:
22370  {
22371  switch (currentRegion)
22372  {
22373  case 6: { result << coordsToPixels(keyMax, valueMin); break; }
22374  case 8: { result << coordsToPixels(keyMax, valueMin); break; }
22375  case 3: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); break; }
22376  case 7: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); break; }
22377  case 2: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22378  case 4: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22379  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
22380  if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R
22381  { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }
22382  else
22383  { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }
22384  break;
22385  }
22386  }
22387  break;
22388  }
22389  }
22390  return result;
22391 }
22392 
22405 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
22406 {
22407  switch (prevRegion)
22408  {
22409  case 1:
22410  {
22411  switch (currentRegion)
22412  {
22413  case 4:
22414  case 7:
22415  case 2:
22416  case 3: return false;
22417  default: return true;
22418  }
22419  }
22420  case 2:
22421  {
22422  switch (currentRegion)
22423  {
22424  case 1:
22425  case 3: return false;
22426  default: return true;
22427  }
22428  }
22429  case 3:
22430  {
22431  switch (currentRegion)
22432  {
22433  case 1:
22434  case 2:
22435  case 6:
22436  case 9: return false;
22437  default: return true;
22438  }
22439  }
22440  case 4:
22441  {
22442  switch (currentRegion)
22443  {
22444  case 1:
22445  case 7: return false;
22446  default: return true;
22447  }
22448  }
22449  case 5: return false; // should never occur
22450  case 6:
22451  {
22452  switch (currentRegion)
22453  {
22454  case 3:
22455  case 9: return false;
22456  default: return true;
22457  }
22458  }
22459  case 7:
22460  {
22461  switch (currentRegion)
22462  {
22463  case 1:
22464  case 4:
22465  case 8:
22466  case 9: return false;
22467  default: return true;
22468  }
22469  }
22470  case 8:
22471  {
22472  switch (currentRegion)
22473  {
22474  case 7:
22475  case 9: return false;
22476  default: return true;
22477  }
22478  }
22479  case 9:
22480  {
22481  switch (currentRegion)
22482  {
22483  case 3:
22484  case 6:
22485  case 8:
22486  case 7: return false;
22487  default: return true;
22488  }
22489  }
22490  default: return true;
22491  }
22492 }
22493 
22494 
22508 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const
22509 {
22510  QList<QPointF> intersections; // x of QPointF corresponds to key and y to value
22511  if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
22512  {
22513  // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
22514  intersections.append(QPointF(key, valueMin)); // direction will be taken care of at end of method
22515  intersections.append(QPointF(key, valueMax));
22516  } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
22517  {
22518  // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
22519  intersections.append(QPointF(keyMin, value)); // direction will be taken care of at end of method
22520  intersections.append(QPointF(keyMax, value));
22521  } else // line is skewed
22522  {
22523  double gamma;
22524  double keyPerValue = (key-prevKey)/(value-prevValue);
22525  // check top of rect:
22526  gamma = prevKey + (valueMax-prevValue)*keyPerValue;
22527  if (gamma >= keyMin && gamma <= keyMax)
22528  intersections.append(QPointF(gamma, valueMax));
22529  // check bottom of rect:
22530  gamma = prevKey + (valueMin-prevValue)*keyPerValue;
22531  if (gamma >= keyMin && gamma <= keyMax)
22532  intersections.append(QPointF(gamma, valueMin));
22533  double valuePerKey = 1.0/keyPerValue;
22534  // check left of rect:
22535  gamma = prevValue + (keyMin-prevKey)*valuePerKey;
22536  if (gamma >= valueMin && gamma <= valueMax)
22537  intersections.append(QPointF(keyMin, gamma));
22538  // check right of rect:
22539  gamma = prevValue + (keyMax-prevKey)*valuePerKey;
22540  if (gamma >= valueMin && gamma <= valueMax)
22541  intersections.append(QPointF(keyMax, gamma));
22542  }
22543 
22544  // handle cases where found points isn't exactly 2:
22545  if (intersections.size() > 2)
22546  {
22547  // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
22548  double distSqrMax = 0;
22549  QPointF pv1, pv2;
22550  for (int i=0; i<intersections.size()-1; ++i)
22551  {
22552  for (int k=i+1; k<intersections.size(); ++k)
22553  {
22554  QPointF distPoint = intersections.at(i)-intersections.at(k);
22555  double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
22556  if (distSqr > distSqrMax)
22557  {
22558  pv1 = intersections.at(i);
22559  pv2 = intersections.at(k);
22560  distSqrMax = distSqr;
22561  }
22562  }
22563  }
22564  intersections = QList<QPointF>() << pv1 << pv2;
22565  } else if (intersections.size() != 2)
22566  {
22567  // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
22568  return false;
22569  }
22570 
22571  // possibly re-sort points so optimized point segment has same direction as original segment:
22572  if ((key-prevKey)*(intersections.at(1).x()-intersections.at(0).x()) + (value-prevValue)*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction
22573  intersections.move(0, 1);
22574  crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
22575  crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
22576  return true;
22577 }
22578 
22604 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
22605 {
22606  switch (prevRegion)
22607  {
22608  case 1:
22609  {
22610  switch (currentRegion)
22611  {
22612  case 6: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }
22613  case 9: { beforeTraverse << coordsToPixels(keyMin, valueMax); afterTraverse << coordsToPixels(keyMax, valueMin); break; }
22614  case 8: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }
22615  }
22616  break;
22617  }
22618  case 2:
22619  {
22620  switch (currentRegion)
22621  {
22622  case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }
22623  case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }
22624  }
22625  break;
22626  }
22627  case 3:
22628  {
22629  switch (currentRegion)
22630  {
22631  case 4: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }
22632  case 7: { beforeTraverse << coordsToPixels(keyMin, valueMin); afterTraverse << coordsToPixels(keyMax, valueMax); break; }
22633  case 8: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }
22634  }
22635  break;
22636  }
22637  case 4:
22638  {
22639  switch (currentRegion)
22640  {
22641  case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }
22642  case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }
22643  }
22644  break;
22645  }
22646  case 5: { break; } // shouldn't happen because this method only handles full traverses
22647  case 6:
22648  {
22649  switch (currentRegion)
22650  {
22651  case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }
22652  case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }
22653  }
22654  break;
22655  }
22656  case 7:
22657  {
22658  switch (currentRegion)
22659  {
22660  case 2: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }
22661  case 3: { beforeTraverse << coordsToPixels(keyMax, valueMax); afterTraverse << coordsToPixels(keyMin, valueMin); break; }
22662  case 6: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }
22663  }
22664  break;
22665  }
22666  case 8:
22667  {
22668  switch (currentRegion)
22669  {
22670  case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }
22671  case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }
22672  }
22673  break;
22674  }
22675  case 9:
22676  {
22677  switch (currentRegion)
22678  {
22679  case 2: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }
22680  case 1: { beforeTraverse << coordsToPixels(keyMax, valueMin); afterTraverse << coordsToPixels(keyMin, valueMax); break; }
22681  case 4: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }
22682  }
22683  break;
22684  }
22685  }
22686 }
22687 
22700 double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const
22701 {
22702  closestData = mDataContainer->constEnd();
22703  if (mDataContainer->isEmpty())
22704  return -1.0;
22705  if (mLineStyle == lsNone && mScatterStyle.isNone())
22706  return -1.0;
22707 
22708  if (mDataContainer->size() == 1)
22709  {
22710  QPointF dataPoint = coordsToPixels(mDataContainer->constBegin()->key, mDataContainer->constBegin()->value);
22711  closestData = mDataContainer->constBegin();
22712  return QCPVector2D(dataPoint-pixelPoint).length();
22713  }
22714 
22715  // calculate minimum distances to curve data points and find closestData iterator:
22716  double minDistSqr = std::numeric_limits<double>::max();
22717  // iterate over found data points and then choose the one with the shortest distance to pos:
22720  for (QCPCurveDataContainer::const_iterator it=begin; it!=end; ++it)
22721  {
22722  const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
22723  if (currentDistSqr < minDistSqr)
22724  {
22725  minDistSqr = currentDistSqr;
22726  closestData = it;
22727  }
22728  }
22729 
22730  // calculate distance to line if there is one (if so, will probably be smaller than distance to closest data point):
22731  if (mLineStyle != lsNone)
22732  {
22733  QVector<QPointF> lines;
22734  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
22735  for (int i=0; i<lines.size()-1; ++i)
22736  {
22737  double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(lines.at(i), lines.at(i+1));
22738  if (currentDistSqr < minDistSqr)
22739  minDistSqr = currentDistSqr;
22740  }
22741  }
22742 
22743  return qSqrt(minDistSqr);
22744 }
22745 /* end of 'src/plottables/plottable-curve.cpp' */
22746 
22747 
22748 /* including file 'src/plottables/plottable-bars.cpp', size 43512 */
22749 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
22750 
22751 
22755 
22790 /* start of documentation of inline functions */
22791 
22818 /* end of documentation of inline functions */
22819 
22824  QObject(parentPlot),
22825  mParentPlot(parentPlot),
22826  mSpacingType(stAbsolute),
22827  mSpacing(4)
22828 {
22829 }
22830 
22832 {
22833  clear();
22834 }
22835 
22844 {
22846 }
22847 
22855 {
22856  mSpacing = spacing;
22857 }
22858 
22865 QCPBars *QCPBarsGroup::bars(int index) const
22866 {
22867  if (index >= 0 && index < mBars.size())
22868  {
22869  return mBars.at(index);
22870  } else
22871  {
22872  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
22873  return 0;
22874  }
22875 }
22876 
22883 {
22884  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
22885  bars->setBarsGroup(0); // removes itself via removeBars
22886 }
22887 
22895 {
22896  if (!bars)
22897  {
22898  qDebug() << Q_FUNC_INFO << "bars is 0";
22899  return;
22900  }
22901 
22902  if (!mBars.contains(bars))
22903  bars->setBarsGroup(this);
22904  else
22905  qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
22906 }
22907 
22918 {
22919  if (!bars)
22920  {
22921  qDebug() << Q_FUNC_INFO << "bars is 0";
22922  return;
22923  }
22924 
22925  // first append to bars list normally:
22926  if (!mBars.contains(bars))
22927  bars->setBarsGroup(this);
22928  // then move to according position:
22929  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
22930 }
22931 
22938 {
22939  if (!bars)
22940  {
22941  qDebug() << Q_FUNC_INFO << "bars is 0";
22942  return;
22943  }
22944 
22945  if (mBars.contains(bars))
22946  bars->setBarsGroup(0);
22947  else
22948  qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
22949 }
22950 
22959 {
22960  if (!mBars.contains(bars))
22961  mBars.append(bars);
22962 }
22963 
22972 {
22973  mBars.removeOne(bars);
22974 }
22975 
22982 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
22983 {
22984  // find list of all base bars in case some mBars are stacked:
22985  QList<const QCPBars*> baseBars;
22986  foreach (const QCPBars *b, mBars)
22987  {
22988  while (b->barBelow())
22989  b = b->barBelow();
22990  if (!baseBars.contains(b))
22991  baseBars.append(b);
22992  }
22993  // find base bar this "bars" is stacked on:
22994  const QCPBars *thisBase = bars;
22995  while (thisBase->barBelow())
22996  thisBase = thisBase->barBelow();
22997 
22998  // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
22999  double result = 0;
23000  int index = baseBars.indexOf(thisBase);
23001  if (index >= 0)
23002  {
23003  if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
23004  {
23005  return result;
23006  } else
23007  {
23008  double lowerPixelWidth, upperPixelWidth;
23009  int startIndex;
23010  int dir = (index <= (baseBars.size()-1)/2) ? -1 : 1; // if bar is to lower keys of center, dir is negative
23011  if (baseBars.size() % 2 == 0) // even number of bars
23012  {
23013  startIndex = baseBars.size()/2 + (dir < 0 ? -1 : 0);
23014  result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
23015  } else // uneven number of bars
23016  {
23017  startIndex = (baseBars.size()-1)/2+dir;
23018  baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
23019  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
23020  result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
23021  }
23022  for (int i = startIndex; i != index; i += dir) // add widths and spacings of bars in between center and our bars
23023  {
23024  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
23025  result += qAbs(upperPixelWidth-lowerPixelWidth);
23026  result += getPixelSpacing(baseBars.at(i), keyCoord);
23027  }
23028  // finally half of our bars width:
23029  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
23030  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
23031  // correct sign of result depending on orientation and direction of key axis:
23032  result *= dir*thisBase->keyAxis()->pixelOrientation();
23033  }
23034  }
23035  return result;
23036 }
23037 
23048 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
23049 {
23050  switch (mSpacingType)
23051  {
23052  case stAbsolute:
23053  {
23054  return mSpacing;
23055  }
23056  case stAxisRectRatio:
23057  {
23058  if (bars->keyAxis()->orientation() == Qt::Horizontal)
23059  return bars->keyAxis()->axisRect()->width()*mSpacing;
23060  else
23061  return bars->keyAxis()->axisRect()->height()*mSpacing;
23062  }
23063  case stPlotCoords:
23064  {
23065  double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
23066  return qAbs(bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel);
23067  }
23068  }
23069  return 0;
23070 }
23071 
23072 
23076 
23091 /* start documentation of inline functions */
23092 
23142 /* end documentation of inline functions */
23143 
23148  key(0),
23149  value(0)
23150 {
23151 }
23152 
23157  key(key),
23158  value(value)
23159 {
23160 }
23161 
23162 
23166 
23202 /* start of documentation of inline functions */
23203 
23225 /* end of documentation of inline functions */
23226 
23237 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
23238  QCPAbstractPlottable1D<QCPBarsData>(keyAxis, valueAxis),
23239  mWidth(0.75),
23240  mWidthType(wtPlotCoords),
23241  mBarsGroup(0),
23242  mBaseValue(0),
23243  mStackingGap(0)
23244 {
23245  // modify inherited properties from abstract plottable:
23246  mPen.setColor(Qt::blue);
23247  mPen.setStyle(Qt::SolidLine);
23248  mBrush.setColor(QColor(40, 50, 255, 30));
23249  mBrush.setStyle(Qt::SolidPattern);
23250  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
23251 }
23252 
23254 {
23255  setBarsGroup(0);
23256  if (mBarBelow || mBarAbove)
23257  connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
23258 }
23259 
23275 void QCPBars::setData(QSharedPointer<QCPBarsDataContainer> data)
23276 {
23277  mDataContainer = data;
23278 }
23279 
23291 void QCPBars::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
23292 {
23293  mDataContainer->clear();
23294  addData(keys, values, alreadySorted);
23295 }
23296 
23304 {
23305  mWidth = width;
23306 }
23307 
23317 {
23319 }
23320 
23328 {
23329  // deregister at old group:
23330  if (mBarsGroup)
23331  mBarsGroup->unregisterBars(this);
23333  // register at new group:
23334  if (mBarsGroup)
23335  mBarsGroup->registerBars(this);
23336 }
23337 
23351 {
23353 }
23354 
23360 void QCPBars::setStackingGap(double pixels)
23361 {
23362  mStackingGap = pixels;
23363 }
23364 
23377 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
23378 {
23379  if (keys.size() != values.size())
23380  qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
23381  const int n = qMin(keys.size(), values.size());
23382  QVector<QCPBarsData> tempData(n);
23383  QVector<QCPBarsData>::iterator it = tempData.begin();
23384  const QVector<QCPBarsData>::iterator itEnd = tempData.end();
23385  int i = 0;
23386  while (it != itEnd)
23387  {
23388  it->key = keys[i];
23389  it->value = values[i];
23390  ++it;
23391  ++i;
23392  }
23393  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
23394 }
23395 
23402 void QCPBars::addData(double key, double value)
23403 {
23404  mDataContainer->add(QCPBarsData(key, value));
23405 }
23406 
23422 {
23423  if (bars == this) return;
23424  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
23425  {
23426  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
23427  return;
23428  }
23429  // remove from stacking:
23430  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
23431  // if new bar given, insert this bar below it:
23432  if (bars)
23433  {
23434  if (bars->mBarBelow)
23435  connectBars(bars->mBarBelow.data(), this);
23436  connectBars(this, bars);
23437  }
23438 }
23439 
23455 {
23456  if (bars == this) return;
23457  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
23458  {
23459  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
23460  return;
23461  }
23462  // remove from stacking:
23463  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
23464  // if new bar given, insert this bar above it:
23465  if (bars)
23466  {
23467  if (bars->mBarAbove)
23468  connectBars(this, bars->mBarAbove.data());
23469  connectBars(bars, this);
23470  }
23471 }
23472 
23476 QCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable) const
23477 {
23478  QCPDataSelection result;
23479  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
23480  return result;
23481  if (!mKeyAxis || !mValueAxis)
23482  return result;
23483 
23484  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
23485  getVisibleDataBounds(visibleBegin, visibleEnd);
23486 
23487  for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
23488  {
23489  if (rect.intersects(getBarRect(it->key, it->value)))
23490  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
23491  }
23492  result.simplify();
23493  return result;
23494 }
23495 
23496 /* inherits documentation from base class */
23497 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23498 {
23499  Q_UNUSED(details)
23500  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
23501  return -1;
23502  if (!mKeyAxis || !mValueAxis)
23503  return -1;
23504 
23505  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
23506  {
23507  // get visible data range:
23508  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
23509  getVisibleDataBounds(visibleBegin, visibleEnd);
23510  for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
23511  {
23512  if (getBarRect(it->key, it->value).contains(pos))
23513  {
23514  if (details)
23515  {
23516  int pointIndex = it-mDataContainer->constBegin();
23517  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
23518  }
23519  return mParentPlot->selectionTolerance()*0.99;
23520  }
23521  }
23522  }
23523  return -1;
23524 }
23525 
23526 /* inherits documentation from base class */
23527 QCPRange QCPBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
23528 {
23529  /* Note: If this QCPBars uses absolute pixels as width (or is in a QCPBarsGroup with spacing in
23530  absolute pixels), using this method to adapt the key axis range to fit the bars into the
23531  currently visible axis range will not work perfectly. Because in the moment the axis range is
23532  changed to the new range, the fixed pixel widths/spacings will represent different coordinate
23533  spans than before, which in turn would require a different key range to perfectly fit, and so on.
23534  The only solution would be to iteratively approach the perfect fitting axis range, but the
23535  mismatch isn't large enough in most applications, to warrant this here. If a user does need a
23536  better fit, he should call the corresponding axis rescale multiple times in a row.
23537  */
23538  QCPRange range;
23539  range = mDataContainer->keyRange(foundRange, inSignDomain);
23540 
23541  // determine exact range of bars by including bar width and barsgroup offset:
23542  if (foundRange && mKeyAxis)
23543  {
23544  double lowerPixelWidth, upperPixelWidth, keyPixel;
23545  // lower range bound:
23546  getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
23547  keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
23548  if (mBarsGroup)
23549  keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
23550  const double lowerCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
23551  if (!qIsNaN(lowerCorrected) && qIsFinite(lowerCorrected) && range.lower > lowerCorrected)
23552  range.lower = lowerCorrected;
23553  // upper range bound:
23554  getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
23555  keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
23556  if (mBarsGroup)
23557  keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
23558  const double upperCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
23559  if (!qIsNaN(upperCorrected) && qIsFinite(upperCorrected) && range.upper < upperCorrected)
23560  range.upper = upperCorrected;
23561  }
23562  return range;
23563 }
23564 
23565 /* inherits documentation from base class */
23566 QCPRange QCPBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
23567 {
23568  // Note: can't simply use mDataContainer->valueRange here because we need to
23569  // take into account bar base value and possible stacking of multiple bars
23570  QCPRange range;
23571  range.lower = mBaseValue;
23572  range.upper = mBaseValue;
23573  bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
23574  bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
23575  QCPBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();
23577  if (inKeyRange != QCPRange())
23578  {
23579  itBegin = mDataContainer->findBegin(inKeyRange.lower);
23580  itEnd = mDataContainer->findEnd(inKeyRange.upper);
23581  }
23582  for (QCPBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
23583  {
23584  const double current = it->value + getStackedBaseValue(it->key, it->value >= 0);
23585  if (qIsNaN(current)) continue;
23586  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
23587  {
23588  if (current < range.lower || !haveLower)
23589  {
23590  range.lower = current;
23591  haveLower = true;
23592  }
23593  if (current > range.upper || !haveUpper)
23594  {
23595  range.upper = current;
23596  haveUpper = true;
23597  }
23598  }
23599  }
23600 
23601  foundRange = true; // return true because bar charts always have the 0-line visible
23602  return range;
23603 }
23604 
23605 /* inherits documentation from base class */
23606 QPointF QCPBars::dataPixelPosition(int index) const
23607 {
23608  if (index >= 0 && index < mDataContainer->size())
23609  {
23610  QCPAxis *keyAxis = mKeyAxis.data();
23611  QCPAxis *valueAxis = mValueAxis.data();
23612  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
23613 
23614  const QCPDataContainer<QCPBarsData>::const_iterator it = mDataContainer->constBegin()+index;
23615  const double valuePixel = valueAxis->coordToPixel(getStackedBaseValue(it->key, it->value >= 0) + it->value);
23616  const double keyPixel = keyAxis->coordToPixel(it->key) + (mBarsGroup ? mBarsGroup->keyPixelOffset(this, it->key) : 0);
23617  if (keyAxis->orientation() == Qt::Horizontal)
23618  return QPointF(keyPixel, valuePixel);
23619  else
23620  return QPointF(valuePixel, keyPixel);
23621  } else
23622  {
23623  qDebug() << Q_FUNC_INFO << "Index out of bounds" << index;
23624  return QPointF();
23625  }
23626 }
23627 
23628 /* inherits documentation from base class */
23630 {
23631  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
23632  if (mDataContainer->isEmpty()) return;
23633 
23634  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
23635  getVisibleDataBounds(visibleBegin, visibleEnd);
23636 
23637  // loop over and draw segments of unselected/selected data:
23638  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
23639  getDataSegments(selectedSegments, unselectedSegments);
23640  allSegments << unselectedSegments << selectedSegments;
23641  for (int i=0; i<allSegments.size(); ++i)
23642  {
23643  bool isSelectedSegment = i >= unselectedSegments.size();
23644  QCPBarsDataContainer::const_iterator begin = visibleBegin;
23645  QCPBarsDataContainer::const_iterator end = visibleEnd;
23646  mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
23647  if (begin == end)
23648  continue;
23649 
23650  for (QCPBarsDataContainer::const_iterator it=begin; it!=end; ++it)
23651  {
23652  // check data validity if flag set:
23653 #ifdef QCUSTOMPLOT_CHECK_DATA
23654  if (QCP::isInvalidData(it->key, it->value))
23655  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
23656 #endif
23657  // draw bar:
23658  if (isSelectedSegment && mSelectionDecorator)
23659  {
23660  mSelectionDecorator->applyBrush(painter);
23661  mSelectionDecorator->applyPen(painter);
23662  } else
23663  {
23664  painter->setBrush(mBrush);
23665  painter->setPen(mPen);
23666  }
23668  painter->drawPolygon(getBarRect(it->key, it->value));
23669  }
23670  }
23671 
23672  // draw other selection decoration that isn't just line/scatter pens and brushes:
23673  if (mSelectionDecorator)
23675 }
23676 
23677 /* inherits documentation from base class */
23678 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
23679 {
23680  // draw filled rect:
23682  painter->setBrush(mBrush);
23683  painter->setPen(mPen);
23684  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
23685  r.moveCenter(rect.center());
23686  painter->drawRect(r);
23687 }
23688 
23704 {
23705  if (!mKeyAxis)
23706  {
23707  qDebug() << Q_FUNC_INFO << "invalid key axis";
23708  begin = mDataContainer->constEnd();
23709  end = mDataContainer->constEnd();
23710  return;
23711  }
23712  if (mDataContainer->isEmpty())
23713  {
23714  begin = mDataContainer->constEnd();
23715  end = mDataContainer->constEnd();
23716  return;
23717  }
23718 
23719  // get visible data range as QMap iterators
23720  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower);
23721  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper);
23722  double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
23723  double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
23724  bool isVisible = false;
23725  // walk left from begin to find lower bar that actually is completely outside visible pixel range:
23727  while (it != mDataContainer->constBegin())
23728  {
23729  --it;
23730  const QRectF barRect = getBarRect(it->key, it->value);
23731  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
23732  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.left() <= lowerPixelBound));
23733  else // keyaxis is vertical
23734  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.bottom() >= lowerPixelBound));
23735  if (isVisible)
23736  begin = it;
23737  else
23738  break;
23739  }
23740  // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
23741  it = end;
23742  while (it != mDataContainer->constEnd())
23743  {
23744  const QRectF barRect = getBarRect(it->key, it->value);
23745  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
23746  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.right() >= upperPixelBound));
23747  else // keyaxis is vertical
23748  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.top() <= upperPixelBound));
23749  if (isVisible)
23750  end = it+1;
23751  else
23752  break;
23753  ++it;
23754  }
23755 }
23756 
23763 QRectF QCPBars::getBarRect(double key, double value) const
23764 {
23765  QCPAxis *keyAxis = mKeyAxis.data();
23766  QCPAxis *valueAxis = mValueAxis.data();
23767  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); }
23768 
23769  double lowerPixelWidth, upperPixelWidth;
23770  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
23771  double base = getStackedBaseValue(key, value >= 0);
23772  double basePixel = valueAxis->coordToPixel(base);
23773  double valuePixel = valueAxis->coordToPixel(base+value);
23774  double keyPixel = keyAxis->coordToPixel(key);
23775  if (mBarsGroup)
23776  keyPixel += mBarsGroup->keyPixelOffset(this, key);
23777  double bottomOffset = (mBarBelow && mPen != Qt::NoPen ? 1 : 0)*(mPen.isCosmetic() ? 1 : mPen.widthF());
23778  bottomOffset += mBarBelow ? mStackingGap : 0;
23779  bottomOffset *= (value<0 ? -1 : 1)*valueAxis->pixelOrientation();
23780  if (qAbs(valuePixel-basePixel) <= qAbs(bottomOffset))
23781  bottomOffset = valuePixel-basePixel;
23782  if (keyAxis->orientation() == Qt::Horizontal)
23783  {
23784  return QRectF(QPointF(keyPixel+lowerPixelWidth, valuePixel), QPointF(keyPixel+upperPixelWidth, basePixel+bottomOffset)).normalized();
23785  } else
23786  {
23787  return QRectF(QPointF(basePixel+bottomOffset, keyPixel+lowerPixelWidth), QPointF(valuePixel, keyPixel+upperPixelWidth)).normalized();
23788  }
23789 }
23790 
23800 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
23801 {
23802  lower = 0;
23803  upper = 0;
23804  switch (mWidthType)
23805  {
23806  case wtAbsolute:
23807  {
23808  upper = mWidth*0.5*mKeyAxis.data()->pixelOrientation();
23809  lower = -upper;
23810  break;
23811  }
23812  case wtAxisRectRatio:
23813  {
23814  if (mKeyAxis && mKeyAxis.data()->axisRect())
23815  {
23816  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
23817  upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
23818  else
23819  upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
23820  lower = -upper;
23821  } else
23822  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
23823  break;
23824  }
23825  case wtPlotCoords:
23826  {
23827  if (mKeyAxis)
23828  {
23829  double keyPixel = mKeyAxis.data()->coordToPixel(key);
23830  upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
23831  lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
23832  // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
23833  // coordinate transform which includes range direction
23834  } else
23835  qDebug() << Q_FUNC_INFO << "No key axis defined";
23836  break;
23837  }
23838  }
23839 }
23840 
23850 double QCPBars::getStackedBaseValue(double key, bool positive) const
23851 {
23852  if (mBarBelow)
23853  {
23854  double max = 0; // don't initialize with mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
23855  // find bars of mBarBelow that are approximately at key and find largest one:
23856  double epsilon = qAbs(key)*(sizeof(key)==4 ? 1e-6 : 1e-14); // should be safe even when changed to use float at some point
23857  if (key == 0)
23858  epsilon = (sizeof(key)==4 ? 1e-6 : 1e-14);
23859  QCPBarsDataContainer::const_iterator it = mBarBelow.data()->mDataContainer->findBegin(key-epsilon);
23860  QCPBarsDataContainer::const_iterator itEnd = mBarBelow.data()->mDataContainer->findEnd(key+epsilon);
23861  while (it != itEnd)
23862  {
23863  if (it->key > key-epsilon && it->key < key+epsilon)
23864  {
23865  if ((positive && it->value > max) ||
23866  (!positive && it->value < max))
23867  max = it->value;
23868  }
23869  ++it;
23870  }
23871  // recurse down the bar-stack to find the total height:
23872  return max + mBarBelow.data()->getStackedBaseValue(key, positive);
23873  } else
23874  return mBaseValue;
23875 }
23876 
23886 {
23887  if (!lower && !upper) return;
23888 
23889  if (!lower) // disconnect upper at bottom
23890  {
23891  // disconnect old bar below upper:
23892  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
23893  upper->mBarBelow.data()->mBarAbove = 0;
23894  upper->mBarBelow = 0;
23895  } else if (!upper) // disconnect lower at top
23896  {
23897  // disconnect old bar above lower:
23898  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
23899  lower->mBarAbove.data()->mBarBelow = 0;
23900  lower->mBarAbove = 0;
23901  } else // connect lower and upper
23902  {
23903  // disconnect old bar above lower:
23904  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
23905  lower->mBarAbove.data()->mBarBelow = 0;
23906  // disconnect old bar below upper:
23907  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
23908  upper->mBarBelow.data()->mBarAbove = 0;
23909  lower->mBarAbove = upper;
23910  upper->mBarBelow = lower;
23911  }
23912 }
23913 /* end of 'src/plottables/plottable-bars.cpp' */
23914 
23915 
23916 /* including file 'src/plottables/plottable-statisticalbox.cpp', size 28622 */
23917 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
23918 
23922 
23958 /* start documentation of inline functions */
23959 
24010 /* end documentation of inline functions */
24011 
24016  key(0),
24017  minimum(0),
24018  lowerQuartile(0),
24019  median(0),
24020  upperQuartile(0),
24021  maximum(0)
24022 {
24023 }
24024 
24029 QCPStatisticalBoxData::QCPStatisticalBoxData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers) :
24030  key(key),
24031  minimum(minimum),
24032  lowerQuartile(lowerQuartile),
24033  median(median),
24034  upperQuartile(upperQuartile),
24035  maximum(maximum),
24036  outliers(outliers)
24037 {
24038 }
24039 
24040 
24044 
24093 /* start documentation of inline functions */
24094 
24102 /* end documentation of inline functions */
24103 
24115  QCPAbstractPlottable1D<QCPStatisticalBoxData>(keyAxis, valueAxis),
24116  mWidth(0.5),
24117  mWhiskerWidth(0.2),
24118  mWhiskerPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap),
24119  mWhiskerBarPen(Qt::black),
24120  mWhiskerAntialiased(false),
24121  mMedianPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap),
24122  mOutlierStyle(QCPScatterStyle::ssCircle, Qt::blue, 6)
24123 {
24124  setPen(QPen(Qt::black));
24125  setBrush(Qt::NoBrush);
24126 }
24127 
24144 void QCPStatisticalBox::setData(QSharedPointer<QCPStatisticalBoxDataContainer> data)
24145 {
24146  mDataContainer = data;
24147 }
24159 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)
24160 {
24161  mDataContainer->clear();
24162  addData(keys, minimum, lowerQuartile, median, upperQuartile, maximum, alreadySorted);
24163 }
24164 
24171 {
24172  mWidth = width;
24173 }
24174 
24184 {
24185  mWhiskerWidth = width;
24186 }
24187 
24200 {
24201  mWhiskerPen = pen;
24202 }
24203 
24214 {
24215  mWhiskerBarPen = pen;
24216 }
24217 
24225 {
24226  mWhiskerAntialiased = enabled;
24227 }
24228 
24233 {
24234  mMedianPen = pen;
24235 }
24236 
24244 {
24245  mOutlierStyle = style;
24246 }
24247 
24260 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)
24261 {
24262  if (keys.size() != minimum.size() || minimum.size() != lowerQuartile.size() || lowerQuartile.size() != median.size() ||
24263  median.size() != upperQuartile.size() || upperQuartile.size() != maximum.size() || maximum.size() != keys.size())
24264  qDebug() << Q_FUNC_INFO << "keys, minimum, lowerQuartile, median, upperQuartile, maximum have different sizes:"
24265  << keys.size() << minimum.size() << lowerQuartile.size() << median.size() << upperQuartile.size() << maximum.size();
24266  const int n = qMin(keys.size(), qMin(minimum.size(), qMin(lowerQuartile.size(), qMin(median.size(), qMin(upperQuartile.size(), maximum.size())))));
24267  QVector<QCPStatisticalBoxData> tempData(n);
24268  QVector<QCPStatisticalBoxData>::iterator it = tempData.begin();
24269  const QVector<QCPStatisticalBoxData>::iterator itEnd = tempData.end();
24270  int i = 0;
24271  while (it != itEnd)
24272  {
24273  it->key = keys[i];
24274  it->minimum = minimum[i];
24275  it->lowerQuartile = lowerQuartile[i];
24276  it->median = median[i];
24277  it->upperQuartile = upperQuartile[i];
24278  it->maximum = maximum[i];
24279  ++it;
24280  ++i;
24281  }
24282  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
24283 }
24284 
24293 void QCPStatisticalBox::addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers)
24294 {
24295  mDataContainer->add(QCPStatisticalBoxData(key, minimum, lowerQuartile, median, upperQuartile, maximum, outliers));
24296 }
24297 
24301 QCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool onlySelectable) const
24302 {
24303  QCPDataSelection result;
24304  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
24305  return result;
24306  if (!mKeyAxis || !mValueAxis)
24307  return result;
24308 
24309  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
24310  getVisibleDataBounds(visibleBegin, visibleEnd);
24311 
24312  for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
24313  {
24314  if (rect.intersects(getQuartileBox(it)))
24315  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
24316  }
24317  result.simplify();
24318  return result;
24319 }
24320 
24321 /* inherits documentation from base class */
24322 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
24323 {
24324  Q_UNUSED(details)
24325  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
24326  return -1;
24327  if (!mKeyAxis || !mValueAxis)
24328  return -1;
24329 
24330  if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
24331  {
24332  // get visible data range:
24333  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
24334  QCPStatisticalBoxDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
24335  getVisibleDataBounds(visibleBegin, visibleEnd);
24336  double minDistSqr = std::numeric_limits<double>::max();
24337  for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
24338  {
24339  if (getQuartileBox(it).contains(pos)) // quartile box
24340  {
24341  double currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
24342  if (currentDistSqr < minDistSqr)
24343  {
24344  minDistSqr = currentDistSqr;
24345  closestDataPoint = it;
24346  }
24347  } else // whiskers
24348  {
24349  const QVector<QLineF> whiskerBackbones(getWhiskerBackboneLines(it));
24350  for (int i=0; i<whiskerBackbones.size(); ++i)
24351  {
24352  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(whiskerBackbones.at(i));
24353  if (currentDistSqr < minDistSqr)
24354  {
24355  minDistSqr = currentDistSqr;
24356  closestDataPoint = it;
24357  }
24358  }
24359  }
24360  }
24361  if (details)
24362  {
24363  int pointIndex = closestDataPoint-mDataContainer->constBegin();
24364  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
24365  }
24366  return qSqrt(minDistSqr);
24367  }
24368  return -1;
24369 }
24370 
24371 /* inherits documentation from base class */
24372 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
24373 {
24374  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
24375  // determine exact range by including width of bars/flags:
24376  if (foundRange)
24377  {
24378  if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)
24379  range.lower -= mWidth*0.5;
24380  if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)
24381  range.upper += mWidth*0.5;
24382  }
24383  return range;
24384 }
24385 
24386 /* inherits documentation from base class */
24387 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
24388 {
24389  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
24390 }
24391 
24392 /* inherits documentation from base class */
24394 {
24395  if (mDataContainer->isEmpty()) return;
24396  QCPAxis *keyAxis = mKeyAxis.data();
24397  QCPAxis *valueAxis = mValueAxis.data();
24398  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
24399 
24400  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
24401  getVisibleDataBounds(visibleBegin, visibleEnd);
24402 
24403  // loop over and draw segments of unselected/selected data:
24404  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
24405  getDataSegments(selectedSegments, unselectedSegments);
24406  allSegments << unselectedSegments << selectedSegments;
24407  for (int i=0; i<allSegments.size(); ++i)
24408  {
24409  bool isSelectedSegment = i >= unselectedSegments.size();
24412  mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
24413  if (begin == end)
24414  continue;
24415 
24416  for (QCPStatisticalBoxDataContainer::const_iterator it=begin; it!=end; ++it)
24417  {
24418  // check data validity if flag set:
24419 # ifdef QCUSTOMPLOT_CHECK_DATA
24420  if (QCP::isInvalidData(it->key, it->minimum) ||
24421  QCP::isInvalidData(it->lowerQuartile, it->median) ||
24422  QCP::isInvalidData(it->upperQuartile, it->maximum))
24423  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range has invalid data." << "Plottable name:" << name();
24424  for (int i=0; i<it->outliers.size(); ++i)
24425  if (QCP::isInvalidData(it->outliers.at(i)))
24426  qDebug() << Q_FUNC_INFO << "Data point outlier at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
24427 # endif
24428 
24429  if (isSelectedSegment && mSelectionDecorator)
24430  {
24431  mSelectionDecorator->applyPen(painter);
24432  mSelectionDecorator->applyBrush(painter);
24433  } else
24434  {
24435  painter->setPen(mPen);
24436  painter->setBrush(mBrush);
24437  }
24438  QCPScatterStyle finalOutlierStyle = mOutlierStyle;
24439  if (isSelectedSegment && mSelectionDecorator)
24441  drawStatisticalBox(painter, it, finalOutlierStyle);
24442  }
24443  }
24444 
24445  // draw other selection decoration that isn't just line/scatter pens and brushes:
24446  if (mSelectionDecorator)
24448 }
24449 
24450 /* inherits documentation from base class */
24451 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
24452 {
24453  // draw filled rect:
24455  painter->setPen(mPen);
24456  painter->setBrush(mBrush);
24457  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
24458  r.moveCenter(rect.center());
24459  painter->drawRect(r);
24460 }
24461 
24471 {
24472  // draw quartile box:
24474  const QRectF quartileBox = getQuartileBox(it);
24475  painter->drawRect(quartileBox);
24476  // draw median line with cliprect set to quartile box:
24477  painter->save();
24478  painter->setClipRect(quartileBox, Qt::IntersectClip);
24479  painter->setPen(mMedianPen);
24480  painter->drawLine(QLineF(coordsToPixels(it->key-mWidth*0.5, it->median), coordsToPixels(it->key+mWidth*0.5, it->median)));
24481  painter->restore();
24482  // draw whisker lines:
24484  painter->setPen(mWhiskerPen);
24485  painter->drawLines(getWhiskerBackboneLines(it));
24486  painter->setPen(mWhiskerBarPen);
24487  painter->drawLines(getWhiskerBarLines(it));
24488  // draw outliers:
24490  outlierStyle.applyTo(painter, mPen);
24491  for (int i=0; i<it->outliers.size(); ++i)
24492  outlierStyle.drawShape(painter, coordsToPixels(it->key, it->outliers.at(i)));
24493 }
24494 
24510 {
24511  if (!mKeyAxis)
24512  {
24513  qDebug() << Q_FUNC_INFO << "invalid key axis";
24514  begin = mDataContainer->constEnd();
24515  end = mDataContainer->constEnd();
24516  return;
24517  }
24518  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of box to include partially visible data points
24519  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of box to include partially visible data points
24520 }
24521 
24530 {
24531  QRectF result;
24532  result.setTopLeft(coordsToPixels(it->key-mWidth*0.5, it->upperQuartile));
24533  result.setBottomRight(coordsToPixels(it->key+mWidth*0.5, it->lowerQuartile));
24534  return result;
24535 }
24536 
24546 {
24547  QVector<QLineF> result(2);
24548  result[0].setPoints(coordsToPixels(it->key, it->lowerQuartile), coordsToPixels(it->key, it->minimum)); // min backbone
24549  result[1].setPoints(coordsToPixels(it->key, it->upperQuartile), coordsToPixels(it->key, it->maximum)); // max backbone
24550  return result;
24551 }
24552 
24561 {
24562  QVector<QLineF> result(2);
24563  result[0].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->minimum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->minimum)); // min bar
24564  result[1].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->maximum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->maximum)); // max bar
24565  return result;
24566 }
24567 /* end of 'src/plottables/plottable-statisticalbox.cpp' */
24568 
24569 
24570 /* including file 'src/plottables/plottable-colormap.cpp', size 47531 */
24571 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
24572 
24576 
24610 /* start of documentation of inline functions */
24611 
24618 /* end of documentation of inline functions */
24619 
24627 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
24628  mKeySize(0),
24629  mValueSize(0),
24630  mKeyRange(keyRange),
24631  mValueRange(valueRange),
24632  mIsEmpty(true),
24633  mData(0),
24634  mAlpha(0),
24635  mDataModified(true)
24636 {
24637  setSize(keySize, valueSize);
24638  fill(0);
24639 }
24640 
24642 {
24643  if (mData)
24644  delete[] mData;
24645  if (mAlpha)
24646  delete[] mAlpha;
24647 }
24648 
24653  mKeySize(0),
24654  mValueSize(0),
24655  mIsEmpty(true),
24656  mData(0),
24657  mAlpha(0),
24658  mDataModified(true)
24659 {
24660  *this = other;
24661 }
24662 
24668 {
24669  if (&other != this)
24670  {
24671  const int keySize = other.keySize();
24672  const int valueSize = other.valueSize();
24673  if (!other.mAlpha && mAlpha)
24674  clearAlpha();
24675  setSize(keySize, valueSize);
24676  if (other.mAlpha && !mAlpha)
24677  createAlpha(false);
24678  setRange(other.keyRange(), other.valueRange());
24679  if (!isEmpty())
24680  {
24681  memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
24682  if (mAlpha)
24683  memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*keySize*valueSize);
24684  }
24685  mDataBounds = other.mDataBounds;
24686  mDataModified = true;
24687  }
24688  return *this;
24689 }
24690 
24691 /* undocumented getter */
24692 double QCPColorMapData::data(double key, double value)
24693 {
24694  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
24695  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
24696  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
24697  return mData[valueCell*mKeySize + keyCell];
24698  else
24699  return 0;
24700 }
24701 
24702 /* undocumented getter */
24703 double QCPColorMapData::cell(int keyIndex, int valueIndex)
24704 {
24705  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
24706  return mData[valueIndex*mKeySize + keyIndex];
24707  else
24708  return 0;
24709 }
24710 
24719 unsigned char QCPColorMapData::alpha(int keyIndex, int valueIndex)
24720 {
24721  if (mAlpha && keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
24722  return mAlpha[valueIndex*mKeySize + keyIndex];
24723  else
24724  return 255;
24725 }
24726 
24740 {
24741  if (keySize != mKeySize || valueSize != mValueSize)
24742  {
24743  mKeySize = keySize;
24745  if (mData)
24746  delete[] mData;
24747  mIsEmpty = mKeySize == 0 || mValueSize == 0;
24748  if (!mIsEmpty)
24749  {
24750 #ifdef __EXCEPTIONS
24751  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
24752 #endif
24753  mData = new double[mKeySize*mValueSize];
24754 #ifdef __EXCEPTIONS
24755  } catch (...) { mData = 0; }
24756 #endif
24757  if (mData)
24758  fill(0);
24759  else
24760  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
24761  } else
24762  mData = 0;
24763 
24764  if (mAlpha) // if we had an alpha map, recreate it with new size
24765  createAlpha();
24766 
24767  mDataModified = true;
24768  }
24769 }
24770 
24782 {
24783  setSize(keySize, mValueSize);
24784 }
24785 
24797 {
24798  setSize(mKeySize, valueSize);
24799 }
24800 
24812 {
24813  setKeyRange(keyRange);
24814  setValueRange(valueRange);
24815 }
24816 
24828 {
24829  mKeyRange = keyRange;
24830 }
24831 
24843 {
24845 }
24846 
24859 void QCPColorMapData::setData(double key, double value, double z)
24860 {
24861  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
24862  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
24863  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
24864  {
24865  mData[valueCell*mKeySize + keyCell] = z;
24866  if (z < mDataBounds.lower)
24867  mDataBounds.lower = z;
24868  if (z > mDataBounds.upper)
24869  mDataBounds.upper = z;
24870  mDataModified = true;
24871  }
24872 }
24873 
24885 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
24886 {
24887  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
24888  {
24889  mData[valueIndex*mKeySize + keyIndex] = z;
24890  if (z < mDataBounds.lower)
24891  mDataBounds.lower = z;
24892  if (z > mDataBounds.upper)
24893  mDataBounds.upper = z;
24894  mDataModified = true;
24895  } else
24896  qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex;
24897 }
24898 
24914 void QCPColorMapData::setAlpha(int keyIndex, int valueIndex, unsigned char alpha)
24915 {
24916  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
24917  {
24918  if (mAlpha || createAlpha())
24919  {
24920  mAlpha[valueIndex*mKeySize + keyIndex] = alpha;
24921  mDataModified = true;
24922  }
24923  } else
24924  qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex;
24925 }
24926 
24941 {
24942  if (mKeySize > 0 && mValueSize > 0)
24943  {
24944  double minHeight = mData[0];
24945  double maxHeight = mData[0];
24946  const int dataCount = mValueSize*mKeySize;
24947  for (int i=0; i<dataCount; ++i)
24948  {
24949  if (mData[i] > maxHeight)
24950  maxHeight = mData[i];
24951  if (mData[i] < minHeight)
24952  minHeight = mData[i];
24953  }
24954  mDataBounds.lower = minHeight;
24955  mDataBounds.upper = maxHeight;
24956  }
24957 }
24958 
24965 {
24966  setSize(0, 0);
24967 }
24968 
24973 {
24974  if (mAlpha)
24975  {
24976  delete[] mAlpha;
24977  mAlpha = 0;
24978  mDataModified = true;
24979  }
24980 }
24981 
24986 {
24987  const int dataCount = mValueSize*mKeySize;
24988  for (int i=0; i<dataCount; ++i)
24989  mData[i] = z;
24990  mDataBounds = QCPRange(z, z);
24991  mDataModified = true;
24992 }
24993 
25004 {
25005  if (mAlpha || createAlpha(false))
25006  {
25007  const int dataCount = mValueSize*mKeySize;
25008  for (int i=0; i<dataCount; ++i)
25009  mAlpha[i] = alpha;
25010  mDataModified = true;
25011  }
25012 }
25013 
25031 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
25032 {
25033  if (keyIndex)
25034  *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
25035  if (valueIndex)
25036  *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
25037 }
25038 
25054 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
25055 {
25056  if (key)
25057  *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
25058  if (value)
25059  *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
25060 }
25061 
25075 bool QCPColorMapData::createAlpha(bool initializeOpaque)
25076 {
25077  clearAlpha();
25078  if (isEmpty())
25079  return false;
25080 
25081 #ifdef __EXCEPTIONS
25082  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
25083 #endif
25084  mAlpha = new unsigned char[mKeySize*mValueSize];
25085 #ifdef __EXCEPTIONS
25086  } catch (...) { mAlpha = 0; }
25087 #endif
25088  if (mAlpha)
25089  {
25090  if (initializeOpaque)
25091  fillAlpha(255);
25092  return true;
25093  } else
25094  {
25095  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
25096  return false;
25097  }
25098 }
25099 
25100 
25104 
25180 /* start documentation of inline functions */
25181 
25190 /* end documentation of inline functions */
25191 
25192 /* start documentation of signals */
25193 
25215 /* end documentation of signals */
25216 
25225  QCPAbstractPlottable(keyAxis, valueAxis),
25226  mDataScaleType(QCPAxis::stLinear),
25227  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
25228  mInterpolate(true),
25229  mTightBoundary(false),
25230  mMapImageInvalidated(true)
25231 {
25232 }
25233 
25235 {
25236  delete mMapData;
25237 }
25238 
25247 {
25248  if (mMapData == data)
25249  {
25250  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
25251  return;
25252  }
25253  if (copy)
25254  {
25255  *mMapData = *data;
25256  } else
25257  {
25258  delete mMapData;
25259  mMapData = data;
25260  }
25261  mMapImageInvalidated = true;
25262 }
25263 
25273 {
25274  if (!QCPRange::validRange(dataRange)) return;
25275  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
25276  {
25278  mDataRange = dataRange.sanitizedForLogScale();
25279  else
25280  mDataRange = dataRange.sanitizedForLinScale();
25281  mMapImageInvalidated = true;
25283  }
25284 }
25285 
25292 {
25293  if (mDataScaleType != scaleType)
25294  {
25295  mDataScaleType = scaleType;
25296  mMapImageInvalidated = true;
25300  }
25301 }
25302 
25315 {
25316  if (mGradient != gradient)
25317  {
25318  mGradient = gradient;
25319  mMapImageInvalidated = true;
25320  emit gradientChanged(mGradient);
25321  }
25322 }
25323 
25331 {
25332  mInterpolate = enabled;
25333  mMapImageInvalidated = true; // because oversampling factors might need to change
25334 }
25335 
25348 {
25349  mTightBoundary = enabled;
25350 }
25351 
25367 {
25368  if (mColorScale) // unconnect signals from old color scale
25369  {
25370  disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
25371  disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
25372  disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
25373  disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
25374  disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
25375  disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
25376  }
25378  if (mColorScale) // connect signals to new color scale
25379  {
25380  setGradient(mColorScale.data()->gradient());
25381  setDataRange(mColorScale.data()->dataRange());
25382  setDataScaleType(mColorScale.data()->dataScaleType());
25383  connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
25384  connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
25385  connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
25386  connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
25387  connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
25388  connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
25389  }
25390 }
25391 
25412 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
25413 {
25414  if (recalculateDataBounds)
25417 }
25418 
25433 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
25434 {
25435  if (mMapImage.isNull() && !data()->isEmpty())
25436  updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
25437 
25438  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
25439  {
25440  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
25441  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
25442  mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
25443  }
25444 }
25445 
25446 /* inherits documentation from base class */
25447 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
25448 {
25449  Q_UNUSED(details)
25450  if ((onlySelectable && mSelectable == QCP::stNone) || mMapData->isEmpty())
25451  return -1;
25452  if (!mKeyAxis || !mValueAxis)
25453  return -1;
25454 
25455  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
25456  {
25457  double posKey, posValue;
25458  pixelsToCoords(pos, posKey, posValue);
25459  if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
25460  {
25461  if (details)
25462  details->setValue(QCPDataSelection(QCPDataRange(0, 1))); // temporary solution, to facilitate whole-plottable selection. Replace in future version with segmented 2D selection.
25463  return mParentPlot->selectionTolerance()*0.99;
25464  }
25465  }
25466  return -1;
25467 }
25468 
25469 /* inherits documentation from base class */
25470 QCPRange QCPColorMap::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
25471 {
25472  foundRange = true;
25473  QCPRange result = mMapData->keyRange();
25474  result.normalize();
25475  if (inSignDomain == QCP::sdPositive)
25476  {
25477  if (result.lower <= 0 && result.upper > 0)
25478  result.lower = result.upper*1e-3;
25479  else if (result.lower <= 0 && result.upper <= 0)
25480  foundRange = false;
25481  } else if (inSignDomain == QCP::sdNegative)
25482  {
25483  if (result.upper >= 0 && result.lower < 0)
25484  result.upper = result.lower*1e-3;
25485  else if (result.upper >= 0 && result.lower >= 0)
25486  foundRange = false;
25487  }
25488  return result;
25489 }
25490 
25491 /* inherits documentation from base class */
25492 QCPRange QCPColorMap::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
25493 {
25494  if (inKeyRange != QCPRange())
25495  {
25496  if (mMapData->keyRange().upper < inKeyRange.lower || mMapData->keyRange().lower > inKeyRange.upper)
25497  {
25498  foundRange = false;
25499  return QCPRange();
25500  }
25501  }
25502 
25503  foundRange = true;
25504  QCPRange result = mMapData->valueRange();
25505  result.normalize();
25506  if (inSignDomain == QCP::sdPositive)
25507  {
25508  if (result.lower <= 0 && result.upper > 0)
25509  result.lower = result.upper*1e-3;
25510  else if (result.lower <= 0 && result.upper <= 0)
25511  foundRange = false;
25512  } else if (inSignDomain == QCP::sdNegative)
25513  {
25514  if (result.upper >= 0 && result.lower < 0)
25515  result.upper = result.lower*1e-3;
25516  else if (result.upper >= 0 && result.lower >= 0)
25517  foundRange = false;
25518  }
25519  return result;
25520 }
25521 
25537 {
25538  QCPAxis *keyAxis = mKeyAxis.data();
25539  if (!keyAxis) return;
25540  if (mMapData->isEmpty()) return;
25541 
25542  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
25543  const int keySize = mMapData->keySize();
25544  const int valueSize = mMapData->valueSize();
25545  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
25546  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
25547 
25548  // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
25549  if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
25550  mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), format);
25551  else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
25552  mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), format);
25553 
25554  QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage
25555  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
25556  {
25557  // resize undersampled map image to actual key/value cell sizes:
25558  if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
25559  mUndersampledMapImage = QImage(QSize(keySize, valueSize), format);
25560  else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
25561  mUndersampledMapImage = QImage(QSize(valueSize, keySize), format);
25562  localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image
25563  } else if (!mUndersampledMapImage.isNull())
25564  mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it
25565 
25566  const double *rawData = mMapData->mData;
25567  const unsigned char *rawAlpha = mMapData->mAlpha;
25568  if (keyAxis->orientation() == Qt::Horizontal)
25569  {
25570  const int lineCount = valueSize;
25571  const int rowCount = keySize;
25572  for (int line=0; line<lineCount; ++line)
25573  {
25574  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)
25575  if (rawAlpha)
25576  mGradient.colorize(rawData+line*rowCount, rawAlpha+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
25577  else
25578  mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
25579  }
25580  } else // keyAxis->orientation() == Qt::Vertical
25581  {
25582  const int lineCount = keySize;
25583  const int rowCount = valueSize;
25584  for (int line=0; line<lineCount; ++line)
25585  {
25586  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)
25587  if (rawAlpha)
25588  mGradient.colorize(rawData+line, rawAlpha+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
25589  else
25590  mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
25591  }
25592  }
25593 
25594  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
25595  {
25596  if (keyAxis->orientation() == Qt::Horizontal)
25597  mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
25598  else
25599  mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
25600  }
25601  mMapData->mDataModified = false;
25602  mMapImageInvalidated = false;
25603 }
25604 
25605 /* inherits documentation from base class */
25607 {
25608  if (mMapData->isEmpty()) return;
25609  if (!mKeyAxis || !mValueAxis) return;
25611 
25613  updateMapImage();
25614 
25615  // use buffer if painting vectorized (PDF):
25616  const bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
25617  QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized
25618  QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in
25619  QPixmap mapBuffer;
25620  if (useBuffer)
25621  {
25622  const double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps
25623  mapBufferTarget = painter->clipRegion().boundingRect();
25624  mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
25625  mapBuffer.fill(Qt::transparent);
25626  localPainter = new QCPPainter(&mapBuffer);
25627  localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
25628  localPainter->translate(-mapBufferTarget.topLeft());
25629  }
25630 
25631  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
25633  // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):
25634  double halfCellWidth = 0; // in pixels
25635  double halfCellHeight = 0; // in pixels
25636  if (keyAxis()->orientation() == Qt::Horizontal)
25637  {
25638  if (mMapData->keySize() > 1)
25639  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
25640  if (mMapData->valueSize() > 1)
25641  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
25642  } else // keyAxis orientation is Qt::Vertical
25643  {
25644  if (mMapData->keySize() > 1)
25645  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
25646  if (mMapData->valueSize() > 1)
25647  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
25648  }
25649  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
25650  const bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
25651  const bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
25652  const bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
25653  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
25654  QRegion clipBackup;
25655  if (mTightBoundary)
25656  {
25657  clipBackup = localPainter->clipRegion();
25658  QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
25660  localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
25661  }
25662  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
25663  if (mTightBoundary)
25664  localPainter->setClipRegion(clipBackup);
25665  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
25666 
25667  if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter
25668  {
25669  delete localPainter;
25670  painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
25671  }
25672 }
25673 
25674 /* inherits documentation from base class */
25675 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
25676 {
25678  // draw map thumbnail:
25679  if (!mLegendIcon.isNull())
25680  {
25681  QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
25682  QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
25683  iconRect.moveCenter(rect.center());
25684  painter->drawPixmap(iconRect.topLeft(), scaledIcon);
25685  }
25686  /*
25687  // draw frame:
25688  painter->setBrush(Qt::NoBrush);
25689  painter->setPen(Qt::black);
25690  painter->drawRect(rect.adjusted(1, 1, 0, 0));
25691  */
25692 }
25693 /* end of 'src/plottables/plottable-colormap.cpp' */
25694 
25695 
25696 /* including file 'src/plottables/plottable-financial.cpp', size 42610 */
25697 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
25698 
25702 
25720 /* start documentation of inline functions */
25721 
25771 /* end documentation of inline functions */
25772 
25777  key(0),
25778  open(0),
25779  high(0),
25780  low(0),
25781  close(0)
25782 {
25783 }
25784 
25788 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
25789  key(key),
25790  open(open),
25791  high(high),
25792  low(low),
25793  close(close)
25794 {
25795 }
25796 
25797 
25801 
25851 /* start of documentation of inline functions */
25852 
25860 /* end of documentation of inline functions */
25861 
25873  QCPAbstractPlottable1D<QCPFinancialData>(keyAxis, valueAxis),
25874  mChartStyle(csCandlestick),
25875  mWidth(0.5),
25876  mWidthType(wtPlotCoords),
25877  mTwoColored(true),
25878  mBrushPositive(QBrush(QColor(50, 160, 0))),
25879  mBrushNegative(QBrush(QColor(180, 0, 15))),
25880  mPenPositive(QPen(QColor(40, 150, 0))),
25881  mPenNegative(QPen(QColor(170, 5, 5)))
25882 {
25883  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
25884 }
25885 
25887 {
25888 }
25889 
25905 void QCPFinancial::setData(QSharedPointer<QCPFinancialDataContainer> data)
25906 {
25907  mDataContainer = data;
25908 }
25909 
25921 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)
25922 {
25923  mDataContainer->clear();
25924  addData(keys, open, high, low, close, alreadySorted);
25925 }
25926 
25931 {
25932  mChartStyle = style;
25933 }
25934 
25941 {
25942  mWidth = width;
25943 }
25944 
25954 {
25956 }
25957 
25968 {
25970 }
25971 
25982 {
25984 }
25985 
25996 {
25998 }
25999 
26010 {
26011  mPenPositive = pen;
26012 }
26013 
26024 {
26025  mPenNegative = pen;
26026 }
26027 
26042 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)
26043 {
26044  if (keys.size() != open.size() || open.size() != high.size() || high.size() != low.size() || low.size() != close.size() || close.size() != keys.size())
26045  qDebug() << Q_FUNC_INFO << "keys, open, high, low, close have different sizes:" << keys.size() << open.size() << high.size() << low.size() << close.size();
26046  const int n = qMin(keys.size(), qMin(open.size(), qMin(high.size(), qMin(low.size(), close.size()))));
26047  QVector<QCPFinancialData> tempData(n);
26048  QVector<QCPFinancialData>::iterator it = tempData.begin();
26049  const QVector<QCPFinancialData>::iterator itEnd = tempData.end();
26050  int i = 0;
26051  while (it != itEnd)
26052  {
26053  it->key = keys[i];
26054  it->open = open[i];
26055  it->high = high[i];
26056  it->low = low[i];
26057  it->close = close[i];
26058  ++it;
26059  ++i;
26060  }
26061  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
26062 }
26063 
26074 void QCPFinancial::addData(double key, double open, double high, double low, double close)
26075 {
26076  mDataContainer->add(QCPFinancialData(key, open, high, low, close));
26077 }
26078 
26082 QCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelectable) const
26083 {
26084  QCPDataSelection result;
26085  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
26086  return result;
26087  if (!mKeyAxis || !mValueAxis)
26088  return result;
26089 
26090  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
26091  getVisibleDataBounds(visibleBegin, visibleEnd);
26092 
26093  for (QCPFinancialDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
26094  {
26095  if (rect.intersects(selectionHitBox(it)))
26096  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
26097  }
26098  result.simplify();
26099  return result;
26100 }
26101 
26102 /* inherits documentation from base class */
26103 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
26104 {
26105  Q_UNUSED(details)
26106  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
26107  return -1;
26108  if (!mKeyAxis || !mValueAxis)
26109  return -1;
26110 
26111  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
26112  {
26113  // get visible data range:
26114  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
26115  QCPFinancialDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
26116  getVisibleDataBounds(visibleBegin, visibleEnd);
26117  // perform select test according to configured style:
26118  double result = -1;
26119  switch (mChartStyle)
26120  {
26121  case QCPFinancial::csOhlc:
26122  result = ohlcSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;
26124  result = candlestickSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;
26125  }
26126  if (details)
26127  {
26128  int pointIndex = closestDataPoint-mDataContainer->constBegin();
26129  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
26130  }
26131  return result;
26132  }
26133 
26134  return -1;
26135 }
26136 
26137 /* inherits documentation from base class */
26138 QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
26139 {
26140  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
26141  // determine exact range by including width of bars/flags:
26142  if (foundRange)
26143  {
26144  if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)
26145  range.lower -= mWidth*0.5;
26146  if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)
26147  range.upper += mWidth*0.5;
26148  }
26149  return range;
26150 }
26151 
26152 /* inherits documentation from base class */
26153 QCPRange QCPFinancial::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
26154 {
26155  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
26156 }
26157 
26172 QCPFinancialDataContainer QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
26173 {
26175  int count = qMin(time.size(), value.size());
26176  if (count == 0)
26177  return QCPFinancialDataContainer();
26178 
26179  QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
26180  int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
26181  for (int i=0; i<count; ++i)
26182  {
26183  int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
26184  if (currentBinIndex == index) // data point still in current bin, extend high/low:
26185  {
26186  if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
26187  if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
26188  if (i == count-1) // last data point is in current bin, finalize bin:
26189  {
26190  currentBinData.close = value.at(i);
26191  currentBinData.key = timeBinOffset+(index)*timeBinSize;
26192  data.add(currentBinData);
26193  }
26194  } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
26195  {
26196  // finalize current bin:
26197  currentBinData.close = value.at(i-1);
26198  currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
26199  data.add(currentBinData);
26200  // start next bin:
26201  currentBinIndex = index;
26202  currentBinData.open = value.at(i);
26203  currentBinData.high = value.at(i);
26204  currentBinData.low = value.at(i);
26205  }
26206  }
26207 
26208  return data;
26209 }
26210 
26211 /* inherits documentation from base class */
26213 {
26214  // get visible data range:
26215  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
26216  getVisibleDataBounds(visibleBegin, visibleEnd);
26217 
26218  // loop over and draw segments of unselected/selected data:
26219  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
26220  getDataSegments(selectedSegments, unselectedSegments);
26221  allSegments << unselectedSegments << selectedSegments;
26222  for (int i=0; i<allSegments.size(); ++i)
26223  {
26224  bool isSelectedSegment = i >= unselectedSegments.size();
26225  QCPFinancialDataContainer::const_iterator begin = visibleBegin;
26227  mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
26228  if (begin == end)
26229  continue;
26230 
26231  // draw data segment according to configured style:
26232  switch (mChartStyle)
26233  {
26234  case QCPFinancial::csOhlc:
26235  drawOhlcPlot(painter, begin, end, isSelectedSegment); break;
26237  drawCandlestickPlot(painter, begin, end, isSelectedSegment); break;
26238  }
26239  }
26240 
26241  // draw other selection decoration that isn't just line/scatter pens and brushes:
26242  if (mSelectionDecorator)
26244 }
26245 
26246 /* inherits documentation from base class */
26247 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
26248 {
26249  painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
26250  if (mChartStyle == csOhlc)
26251  {
26252  if (mTwoColored)
26253  {
26254  // draw upper left half icon with positive color:
26255  painter->setBrush(mBrushPositive);
26256  painter->setPen(mPenPositive);
26257  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
26258  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26259  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
26260  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
26261  // draw bottom right half icon with negative color:
26262  painter->setBrush(mBrushNegative);
26263  painter->setPen(mPenNegative);
26264  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
26265  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26266  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
26267  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
26268  } else
26269  {
26270  painter->setBrush(mBrush);
26271  painter->setPen(mPen);
26272  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26273  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
26274  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
26275  }
26276  } else if (mChartStyle == csCandlestick)
26277  {
26278  if (mTwoColored)
26279  {
26280  // draw upper left half icon with positive color:
26281  painter->setBrush(mBrushPositive);
26282  painter->setPen(mPenPositive);
26283  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
26284  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
26285  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26286  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
26287  // draw bottom right half icon with negative color:
26288  painter->setBrush(mBrushNegative);
26289  painter->setPen(mPenNegative);
26290  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
26291  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
26292  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26293  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
26294  } else
26295  {
26296  painter->setBrush(mBrush);
26297  painter->setPen(mPen);
26298  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
26299  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26300  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
26301  }
26302  }
26303 }
26304 
26312 {
26313  QCPAxis *keyAxis = mKeyAxis.data();
26314  QCPAxis *valueAxis = mValueAxis.data();
26315  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
26316 
26317  if (keyAxis->orientation() == Qt::Horizontal)
26318  {
26319  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26320  {
26321  if (isSelected && mSelectionDecorator)
26322  mSelectionDecorator->applyPen(painter);
26323  else if (mTwoColored)
26324  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26325  else
26326  painter->setPen(mPen);
26327  double keyPixel = keyAxis->coordToPixel(it->key);
26328  double openPixel = valueAxis->coordToPixel(it->open);
26329  double closePixel = valueAxis->coordToPixel(it->close);
26330  // draw backbone:
26331  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(it->low)));
26332  // draw open:
26333  double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides
26334  painter->drawLine(QPointF(keyPixel-pixelWidth, openPixel), QPointF(keyPixel, openPixel));
26335  // draw close:
26336  painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+pixelWidth, closePixel));
26337  }
26338  } else
26339  {
26340  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26341  {
26342  if (isSelected && mSelectionDecorator)
26343  mSelectionDecorator->applyPen(painter);
26344  else if (mTwoColored)
26345  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26346  else
26347  painter->setPen(mPen);
26348  double keyPixel = keyAxis->coordToPixel(it->key);
26349  double openPixel = valueAxis->coordToPixel(it->open);
26350  double closePixel = valueAxis->coordToPixel(it->close);
26351  // draw backbone:
26352  painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(it->low), keyPixel));
26353  // draw open:
26354  double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides
26355  painter->drawLine(QPointF(openPixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel));
26356  // draw close:
26357  painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+pixelWidth));
26358  }
26359  }
26360 }
26361 
26369 {
26370  QCPAxis *keyAxis = mKeyAxis.data();
26371  QCPAxis *valueAxis = mValueAxis.data();
26372  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
26373 
26374  if (keyAxis->orientation() == Qt::Horizontal)
26375  {
26376  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26377  {
26378  if (isSelected && mSelectionDecorator)
26379  {
26380  mSelectionDecorator->applyPen(painter);
26381  mSelectionDecorator->applyBrush(painter);
26382  } else if (mTwoColored)
26383  {
26384  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26385  painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);
26386  } else
26387  {
26388  painter->setPen(mPen);
26389  painter->setBrush(mBrush);
26390  }
26391  double keyPixel = keyAxis->coordToPixel(it->key);
26392  double openPixel = valueAxis->coordToPixel(it->open);
26393  double closePixel = valueAxis->coordToPixel(it->close);
26394  // draw high:
26395  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));
26396  // draw low:
26397  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));
26398  // draw open-close box:
26399  double pixelWidth = getPixelWidth(it->key, keyPixel);
26400  painter->drawRect(QRectF(QPointF(keyPixel-pixelWidth, closePixel), QPointF(keyPixel+pixelWidth, openPixel)));
26401  }
26402  } else // keyAxis->orientation() == Qt::Vertical
26403  {
26404  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26405  {
26406  if (isSelected && mSelectionDecorator)
26407  {
26408  mSelectionDecorator->applyPen(painter);
26409  mSelectionDecorator->applyBrush(painter);
26410  } else if (mTwoColored)
26411  {
26412  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26413  painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);
26414  } else
26415  {
26416  painter->setPen(mPen);
26417  painter->setBrush(mBrush);
26418  }
26419  double keyPixel = keyAxis->coordToPixel(it->key);
26420  double openPixel = valueAxis->coordToPixel(it->open);
26421  double closePixel = valueAxis->coordToPixel(it->close);
26422  // draw high:
26423  painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));
26424  // draw low:
26425  painter->drawLine(QPointF(valueAxis->coordToPixel(it->low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));
26426  // draw open-close box:
26427  double pixelWidth = getPixelWidth(it->key, keyPixel);
26428  painter->drawRect(QRectF(QPointF(closePixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel+pixelWidth)));
26429  }
26430  }
26431 }
26432 
26445 double QCPFinancial::getPixelWidth(double key, double keyPixel) const
26446 {
26447  double result = 0;
26448  switch (mWidthType)
26449  {
26450  case wtAbsolute:
26451  {
26452  if (mKeyAxis)
26453  result = mWidth*0.5*mKeyAxis.data()->pixelOrientation();
26454  break;
26455  }
26456  case wtAxisRectRatio:
26457  {
26458  if (mKeyAxis && mKeyAxis.data()->axisRect())
26459  {
26460  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
26461  result = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
26462  else
26463  result = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
26464  } else
26465  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
26466  break;
26467  }
26468  case wtPlotCoords:
26469  {
26470  if (mKeyAxis)
26471  result = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
26472  else
26473  qDebug() << Q_FUNC_INFO << "No key axis defined";
26474  break;
26475  }
26476  }
26477  return result;
26478 }
26479 
26489 {
26490  closestDataPoint = mDataContainer->constEnd();
26491  QCPAxis *keyAxis = mKeyAxis.data();
26492  QCPAxis *valueAxis = mValueAxis.data();
26493  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
26494 
26495  double minDistSqr = std::numeric_limits<double>::max();
26496  if (keyAxis->orientation() == Qt::Horizontal)
26497  {
26498  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26499  {
26500  double keyPixel = keyAxis->coordToPixel(it->key);
26501  // calculate distance to backbone:
26502  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)));
26503  if (currentDistSqr < minDistSqr)
26504  {
26505  minDistSqr = currentDistSqr;
26506  closestDataPoint = it;
26507  }
26508  }
26509  } else // keyAxis->orientation() == Qt::Vertical
26510  {
26511  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26512  {
26513  double keyPixel = keyAxis->coordToPixel(it->key);
26514  // calculate distance to backbone:
26515  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel));
26516  if (currentDistSqr < minDistSqr)
26517  {
26518  minDistSqr = currentDistSqr;
26519  closestDataPoint = it;
26520  }
26521  }
26522  }
26523  return qSqrt(minDistSqr);
26524 }
26525 
26536 {
26537  closestDataPoint = mDataContainer->constEnd();
26538  QCPAxis *keyAxis = mKeyAxis.data();
26539  QCPAxis *valueAxis = mValueAxis.data();
26540  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
26541 
26542  double minDistSqr = std::numeric_limits<double>::max();
26543  if (keyAxis->orientation() == Qt::Horizontal)
26544  {
26545  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26546  {
26547  double currentDistSqr;
26548  // determine whether pos is in open-close-box:
26549  QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);
26550  QCPRange boxValueRange(it->close, it->open);
26551  double posKey, posValue;
26552  pixelsToCoords(pos, posKey, posValue);
26553  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
26554  {
26555  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
26556  } else
26557  {
26558  // calculate distance to high/low lines:
26559  double keyPixel = keyAxis->coordToPixel(it->key);
26560  double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));
26561  double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));
26562  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
26563  }
26564  if (currentDistSqr < minDistSqr)
26565  {
26566  minDistSqr = currentDistSqr;
26567  closestDataPoint = it;
26568  }
26569  }
26570  } else // keyAxis->orientation() == Qt::Vertical
26571  {
26572  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26573  {
26574  double currentDistSqr;
26575  // determine whether pos is in open-close-box:
26576  QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);
26577  QCPRange boxValueRange(it->close, it->open);
26578  double posKey, posValue;
26579  pixelsToCoords(pos, posKey, posValue);
26580  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
26581  {
26582  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
26583  } else
26584  {
26585  // calculate distance to high/low lines:
26586  double keyPixel = keyAxis->coordToPixel(it->key);
26587  double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));
26588  double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));
26589  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
26590  }
26591  if (currentDistSqr < minDistSqr)
26592  {
26593  minDistSqr = currentDistSqr;
26594  closestDataPoint = it;
26595  }
26596  }
26597  }
26598  return qSqrt(minDistSqr);
26599 }
26600 
26616 {
26617  if (!mKeyAxis)
26618  {
26619  qDebug() << Q_FUNC_INFO << "invalid key axis";
26620  begin = mDataContainer->constEnd();
26621  end = mDataContainer->constEnd();
26622  return;
26623  }
26624  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of ohlc/candlestick to include partially visible data points
26625  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of ohlc/candlestick to include partially visible data points
26626 }
26627 
26634 {
26635  QCPAxis *keyAxis = mKeyAxis.data();
26636  QCPAxis *valueAxis = mValueAxis.data();
26637  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); }
26638 
26639  double keyPixel = keyAxis->coordToPixel(it->key);
26640  double highPixel = valueAxis->coordToPixel(it->high);
26641  double lowPixel = valueAxis->coordToPixel(it->low);
26642  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it->key-mWidth*0.5);
26643  if (keyAxis->orientation() == Qt::Horizontal)
26644  return QRectF(keyPixel-keyWidthPixels, highPixel, keyWidthPixels*2, lowPixel-highPixel).normalized();
26645  else
26646  return QRectF(highPixel, keyPixel-keyWidthPixels, lowPixel-highPixel, keyWidthPixels*2).normalized();
26647 }
26648 /* end of 'src/plottables/plottable-financial.cpp' */
26649 
26650 
26651 /* including file 'src/plottables/plottable-errorbar.cpp', size 37210 */
26652 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
26653 
26657 
26677  errorMinus(0),
26678  errorPlus(0)
26679 {
26680 }
26681 
26686  errorMinus(error),
26687  errorPlus(error)
26688 {
26689 }
26690 
26696  errorMinus(errorMinus),
26697  errorPlus(errorPlus)
26698 {
26699 }
26700 
26701 
26705 
26735 /* start of documentation of inline functions */
26736 
26744 /* end of documentation of inline functions */
26745 
26760  QCPAbstractPlottable(keyAxis, valueAxis),
26761  mDataContainer(new QVector<QCPErrorBarsData>),
26762  mErrorType(etValueError),
26763  mWhiskerWidth(9),
26764  mSymbolGap(10)
26765 {
26766  setPen(QPen(Qt::black, 0));
26767  setBrush(Qt::NoBrush);
26768 }
26769 
26771 {
26772 }
26773 
26792 void QCPErrorBars::setData(QSharedPointer<QCPErrorBarsDataContainer> data)
26793 {
26794  mDataContainer = data;
26795 }
26796 
26806 void QCPErrorBars::setData(const QVector<double> &error)
26807 {
26808  mDataContainer->clear();
26809  addData(error);
26810 }
26811 
26822 void QCPErrorBars::setData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)
26823 {
26824  mDataContainer->clear();
26825  addData(errorMinus, errorPlus);
26826 }
26827 
26843 {
26844  if (plottable && qobject_cast<QCPErrorBars*>(plottable))
26845  {
26846  mDataPlottable = 0;
26847  qDebug() << Q_FUNC_INFO << "can't set another QCPErrorBars instance as data plottable";
26848  return;
26849  }
26850  if (plottable && !plottable->interface1D())
26851  {
26852  mDataPlottable = 0;
26853  qDebug() << Q_FUNC_INFO << "passed plottable doesn't implement 1d interface, can't associate with QCPErrorBars";
26854  return;
26855  }
26856 
26857  mDataPlottable = plottable;
26858 }
26859 
26865 {
26866  mErrorType = type;
26867 }
26868 
26874 {
26875  mWhiskerWidth = pixels;
26876 }
26877 
26883 void QCPErrorBars::setSymbolGap(double pixels)
26884 {
26885  mSymbolGap = pixels;
26886 }
26887 
26897 void QCPErrorBars::addData(const QVector<double> &error)
26898 {
26899  addData(error, error);
26900 }
26901 
26912 void QCPErrorBars::addData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)
26913 {
26914  if (errorMinus.size() != errorPlus.size())
26915  qDebug() << Q_FUNC_INFO << "minus and plus error vectors have different sizes:" << errorMinus.size() << errorPlus.size();
26916  const int n = qMin(errorMinus.size(), errorPlus.size());
26917  mDataContainer->reserve(n);
26918  for (int i=0; i<n; ++i)
26919  mDataContainer->append(QCPErrorBarsData(errorMinus.at(i), errorPlus.at(i)));
26920 }
26921 
26931 void QCPErrorBars::addData(double error)
26932 {
26933  mDataContainer->append(QCPErrorBarsData(error));
26934 }
26935 
26946 void QCPErrorBars::addData(double errorMinus, double errorPlus)
26947 {
26948  mDataContainer->append(QCPErrorBarsData(errorMinus, errorPlus));
26949 }
26950 
26951 /* inherits documentation from base class */
26953 {
26954  return mDataContainer->size();
26955 }
26956 
26957 /* inherits documentation from base class */
26958 double QCPErrorBars::dataMainKey(int index) const
26959 {
26960  if (mDataPlottable)
26961  return mDataPlottable->interface1D()->dataMainKey(index);
26962  else
26963  qDebug() << Q_FUNC_INFO << "no data plottable set";
26964  return 0;
26965 }
26966 
26967 /* inherits documentation from base class */
26968 double QCPErrorBars::dataSortKey(int index) const
26969 {
26970  if (mDataPlottable)
26971  return mDataPlottable->interface1D()->dataSortKey(index);
26972  else
26973  qDebug() << Q_FUNC_INFO << "no data plottable set";
26974  return 0;
26975 }
26976 
26977 /* inherits documentation from base class */
26978 double QCPErrorBars::dataMainValue(int index) const
26979 {
26980  if (mDataPlottable)
26981  return mDataPlottable->interface1D()->dataMainValue(index);
26982  else
26983  qDebug() << Q_FUNC_INFO << "no data plottable set";
26984  return 0;
26985 }
26986 
26987 /* inherits documentation from base class */
26989 {
26990  if (mDataPlottable)
26991  {
26992  const double value = mDataPlottable->interface1D()->dataMainValue(index);
26993  if (index >= 0 && index < mDataContainer->size() && mErrorType == etValueError)
26994  return QCPRange(value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus);
26995  else
26996  return QCPRange(value, value);
26997  } else
26998  {
26999  qDebug() << Q_FUNC_INFO << "no data plottable set";
27000  return QCPRange();
27001  }
27002 }
27003 
27004 /* inherits documentation from base class */
27005 QPointF QCPErrorBars::dataPixelPosition(int index) const
27006 {
27007  if (mDataPlottable)
27008  return mDataPlottable->interface1D()->dataPixelPosition(index);
27009  else
27010  qDebug() << Q_FUNC_INFO << "no data plottable set";
27011  return QPointF();
27012 }
27013 
27014 /* inherits documentation from base class */
27016 {
27017  if (mDataPlottable)
27018  {
27019  return mDataPlottable->interface1D()->sortKeyIsMainKey();
27020  } else
27021  {
27022  qDebug() << Q_FUNC_INFO << "no data plottable set";
27023  return true;
27024  }
27025 }
27026 
27030 QCPDataSelection QCPErrorBars::selectTestRect(const QRectF &rect, bool onlySelectable) const
27031 {
27032  QCPDataSelection result;
27033  if (!mDataPlottable)
27034  return result;
27035  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
27036  return result;
27037  if (!mKeyAxis || !mValueAxis)
27038  return result;
27039 
27040  QCPErrorBarsDataContainer::const_iterator visibleBegin, visibleEnd;
27041  getVisibleDataBounds(visibleBegin, visibleEnd, QCPDataRange(0, dataCount()));
27042 
27043  QVector<QLineF> backbones, whiskers;
27044  for (QCPErrorBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
27045  {
27046  backbones.clear();
27047  whiskers.clear();
27048  getErrorBarLines(it, backbones, whiskers);
27049  for (int i=0; i<backbones.size(); ++i)
27050  {
27051  if (rectIntersectsLine(rect, backbones.at(i)))
27052  {
27053  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
27054  break;
27055  }
27056  }
27057  }
27058  result.simplify();
27059  return result;
27060 }
27061 
27062 /* inherits documentation from base class */
27063 int QCPErrorBars::findBegin(double sortKey, bool expandedRange) const
27064 {
27065  if (mDataPlottable)
27066  {
27067  if (mDataContainer->isEmpty())
27068  return 0;
27069  int beginIndex = mDataPlottable->interface1D()->findBegin(sortKey, expandedRange);
27070  if (beginIndex >= mDataContainer->size())
27071  beginIndex = mDataContainer->size()-1;
27072  return beginIndex;
27073  } else
27074  qDebug() << Q_FUNC_INFO << "no data plottable set";
27075  return 0;
27076 }
27077 
27078 /* inherits documentation from base class */
27079 int QCPErrorBars::findEnd(double sortKey, bool expandedRange) const
27080 {
27081  if (mDataPlottable)
27082  {
27083  if (mDataContainer->isEmpty())
27084  return 0;
27085  int endIndex = mDataPlottable->interface1D()->findEnd(sortKey, expandedRange);
27086  if (endIndex > mDataContainer->size())
27087  endIndex = mDataContainer->size();
27088  return endIndex;
27089  } else
27090  qDebug() << Q_FUNC_INFO << "no data plottable set";
27091  return 0;
27092 }
27093 
27094 /* inherits documentation from base class */
27095 double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
27096 {
27097  if (!mDataPlottable) return -1;
27098 
27099  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
27100  return -1;
27101  if (!mKeyAxis || !mValueAxis)
27102  return -1;
27103 
27104  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
27105  {
27106  QCPErrorBarsDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
27107  double result = pointDistance(pos, closestDataPoint);
27108  if (details)
27109  {
27110  int pointIndex = closestDataPoint-mDataContainer->constBegin();
27111  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
27112  }
27113  return result;
27114  } else
27115  return -1;
27116 }
27117 
27118 /* inherits documentation from base class */
27120 {
27121  if (!mDataPlottable) return;
27122  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
27123  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
27124 
27125  // if the sort key isn't the main key, we must check the visibility for each data point/error bar individually
27126  // (getVisibleDataBounds applies range restriction, but otherwise can only return full data range):
27127  bool checkPointVisibility = !mDataPlottable->interface1D()->sortKeyIsMainKey();
27128 
27129  // check data validity if flag set:
27130 #ifdef QCUSTOMPLOT_CHECK_DATA
27131  QCPErrorBarsDataContainer::const_iterator it;
27132  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
27133  {
27134  if (QCP::isInvalidData(it->errorMinus, it->errorPlus))
27135  qDebug() << Q_FUNC_INFO << "Data point at index" << it-mDataContainer->constBegin() << "invalid." << "Plottable name:" << name();
27136  }
27137 #endif
27138 
27140  painter->setBrush(Qt::NoBrush);
27141  // loop over and draw segments of unselected/selected data:
27142  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
27143  getDataSegments(selectedSegments, unselectedSegments);
27144  allSegments << unselectedSegments << selectedSegments;
27145  QVector<QLineF> backbones, whiskers;
27146  for (int i=0; i<allSegments.size(); ++i)
27147  {
27148  QCPErrorBarsDataContainer::const_iterator begin, end;
27149  getVisibleDataBounds(begin, end, allSegments.at(i));
27150  if (begin == end)
27151  continue;
27152 
27153  bool isSelectedSegment = i >= unselectedSegments.size();
27154  if (isSelectedSegment && mSelectionDecorator)
27155  mSelectionDecorator->applyPen(painter);
27156  else
27157  painter->setPen(mPen);
27158  if (painter->pen().capStyle() == Qt::SquareCap)
27159  {
27160  QPen capFixPen(painter->pen());
27161  capFixPen.setCapStyle(Qt::FlatCap);
27162  painter->setPen(capFixPen);
27163  }
27164  backbones.clear();
27165  whiskers.clear();
27166  for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
27167  {
27168  if (!checkPointVisibility || errorBarVisible(it-mDataContainer->constBegin()))
27169  getErrorBarLines(it, backbones, whiskers);
27170  }
27171  painter->drawLines(backbones);
27172  painter->drawLines(whiskers);
27173  }
27174 
27175  // draw other selection decoration that isn't just line/scatter pens and brushes:
27176  if (mSelectionDecorator)
27178 }
27179 
27180 /* inherits documentation from base class */
27181 void QCPErrorBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
27182 {
27184  painter->setPen(mPen);
27185  if (mErrorType == etValueError && mValueAxis && mValueAxis->orientation() == Qt::Vertical)
27186  {
27187  painter->drawLine(QLineF(rect.center().x(), rect.top()+2, rect.center().x(), rect.bottom()-1));
27188  painter->drawLine(QLineF(rect.center().x()-4, rect.top()+2, rect.center().x()+4, rect.top()+2));
27189  painter->drawLine(QLineF(rect.center().x()-4, rect.bottom()-1, rect.center().x()+4, rect.bottom()-1));
27190  } else
27191  {
27192  painter->drawLine(QLineF(rect.left()+2, rect.center().y(), rect.right()-2, rect.center().y()));
27193  painter->drawLine(QLineF(rect.left()+2, rect.center().y()-4, rect.left()+2, rect.center().y()+4));
27194  painter->drawLine(QLineF(rect.right()-2, rect.center().y()-4, rect.right()-2, rect.center().y()+4));
27195  }
27196 }
27197 
27198 /* inherits documentation from base class */
27199 QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
27200 {
27201  if (!mDataPlottable)
27202  {
27203  foundRange = false;
27204  return QCPRange();
27205  }
27206 
27207  QCPRange range;
27208  bool haveLower = false;
27209  bool haveUpper = false;
27210  QCPErrorBarsDataContainer::const_iterator it;
27211  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
27212  {
27213  if (mErrorType == etValueError)
27214  {
27215  // error bar doesn't extend in key dimension (except whisker but we ignore that here), so only use data point center
27216  const double current = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
27217  if (qIsNaN(current)) continue;
27218  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27219  {
27220  if (current < range.lower || !haveLower)
27221  {
27222  range.lower = current;
27223  haveLower = true;
27224  }
27225  if (current > range.upper || !haveUpper)
27226  {
27227  range.upper = current;
27228  haveUpper = true;
27229  }
27230  }
27231  } else // mErrorType == etKeyError
27232  {
27233  const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
27234  if (qIsNaN(dataKey)) continue;
27235  // plus error:
27236  double current = dataKey + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
27237  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27238  {
27239  if (current > range.upper || !haveUpper)
27240  {
27241  range.upper = current;
27242  haveUpper = true;
27243  }
27244  }
27245  // minus error:
27246  current = dataKey - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
27247  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27248  {
27249  if (current < range.lower || !haveLower)
27250  {
27251  range.lower = current;
27252  haveLower = true;
27253  }
27254  }
27255  }
27256  }
27257 
27258  if (haveUpper && !haveLower)
27259  {
27260  range.lower = range.upper;
27261  haveLower = true;
27262  } else if (haveLower && !haveUpper)
27263  {
27264  range.upper = range.lower;
27265  haveUpper = true;
27266  }
27267 
27268  foundRange = haveLower && haveUpper;
27269  return range;
27270 }
27271 
27272 /* inherits documentation from base class */
27273 QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
27274 {
27275  if (!mDataPlottable)
27276  {
27277  foundRange = false;
27278  return QCPRange();
27279  }
27280 
27281  QCPRange range;
27282  const bool restrictKeyRange = inKeyRange != QCPRange();
27283  bool haveLower = false;
27284  bool haveUpper = false;
27285  QCPErrorBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();
27286  QCPErrorBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd();
27287  if (mDataPlottable->interface1D()->sortKeyIsMainKey() && restrictKeyRange)
27288  {
27289  itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower);
27290  itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper);
27291  }
27292  for (QCPErrorBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
27293  {
27294  if (restrictKeyRange)
27295  {
27296  const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
27297  if (dataKey < inKeyRange.lower || dataKey > inKeyRange.upper)
27298  continue;
27299  }
27300  if (mErrorType == etValueError)
27301  {
27302  const double dataValue = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin());
27303  if (qIsNaN(dataValue)) continue;
27304  // plus error:
27305  double current = dataValue + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
27306  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27307  {
27308  if (current > range.upper || !haveUpper)
27309  {
27310  range.upper = current;
27311  haveUpper = true;
27312  }
27313  }
27314  // minus error:
27315  current = dataValue - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
27316  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27317  {
27318  if (current < range.lower || !haveLower)
27319  {
27320  range.lower = current;
27321  haveLower = true;
27322  }
27323  }
27324  } else // mErrorType == etKeyError
27325  {
27326  // error bar doesn't extend in value dimension (except whisker but we ignore that here), so only use data point center
27327  const double current = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin());
27328  if (qIsNaN(current)) continue;
27329  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27330  {
27331  if (current < range.lower || !haveLower)
27332  {
27333  range.lower = current;
27334  haveLower = true;
27335  }
27336  if (current > range.upper || !haveUpper)
27337  {
27338  range.upper = current;
27339  haveUpper = true;
27340  }
27341  }
27342  }
27343  }
27344 
27345  if (haveUpper && !haveLower)
27346  {
27347  range.lower = range.upper;
27348  haveLower = true;
27349  } else if (haveLower && !haveUpper)
27350  {
27351  range.upper = range.lower;
27352  haveUpper = true;
27353  }
27354 
27355  foundRange = haveLower && haveUpper;
27356  return range;
27357 }
27358 
27370 void QCPErrorBars::getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector<QLineF> &backbones, QVector<QLineF> &whiskers) const
27371 {
27372  if (!mDataPlottable) return;
27373 
27374  int index = it-mDataContainer->constBegin();
27375  QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);
27376  if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y()))
27377  return;
27378  QCPAxis *errorAxis = mErrorType == etValueError ? mValueAxis : mKeyAxis;
27379  QCPAxis *orthoAxis = mErrorType == etValueError ? mKeyAxis : mValueAxis;
27380  const double centerErrorAxisPixel = errorAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
27381  const double centerOrthoAxisPixel = orthoAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
27382  const double centerErrorAxisCoord = errorAxis->pixelToCoord(centerErrorAxisPixel); // depending on plottable, this might be different from just mDataPlottable->interface1D()->dataMainKey/Value
27383  const double symbolGap = mSymbolGap*0.5*errorAxis->pixelOrientation();
27384  // plus error:
27385  double errorStart, errorEnd;
27386  if (!qIsNaN(it->errorPlus))
27387  {
27388  errorStart = centerErrorAxisPixel+symbolGap;
27389  errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord+it->errorPlus);
27390  if (errorAxis->orientation() == Qt::Vertical)
27391  {
27392  if ((errorStart > errorEnd) != errorAxis->rangeReversed())
27393  backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));
27394  whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));
27395  } else
27396  {
27397  if ((errorStart < errorEnd) != errorAxis->rangeReversed())
27398  backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));
27399  whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));
27400  }
27401  }
27402  // minus error:
27403  if (!qIsNaN(it->errorMinus))
27404  {
27405  errorStart = centerErrorAxisPixel-symbolGap;
27406  errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord-it->errorMinus);
27407  if (errorAxis->orientation() == Qt::Vertical)
27408  {
27409  if ((errorStart < errorEnd) != errorAxis->rangeReversed())
27410  backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));
27411  whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));
27412  } else
27413  {
27414  if ((errorStart > errorEnd) != errorAxis->rangeReversed())
27415  backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));
27416  whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));
27417  }
27418  }
27419 }
27420 
27439 void QCPErrorBars::getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
27440 {
27441  QCPAxis *keyAxis = mKeyAxis.data();
27442  QCPAxis *valueAxis = mValueAxis.data();
27443  if (!keyAxis || !valueAxis)
27444  {
27445  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
27446  end = mDataContainer->constEnd();
27447  begin = end;
27448  return;
27449  }
27450  if (!mDataPlottable || rangeRestriction.isEmpty())
27451  {
27452  end = mDataContainer->constEnd();
27453  begin = end;
27454  return;
27455  }
27456  if (!mDataPlottable->interface1D()->sortKeyIsMainKey())
27457  {
27458  // if the sort key isn't the main key, it's not possible to find a contiguous range of visible
27459  // data points, so this method then only applies the range restriction and otherwise returns
27460  // the full data range. Visibility checks must be done on a per-datapoin-basis during drawing
27461  QCPDataRange dataRange(0, mDataContainer->size());
27462  dataRange = dataRange.bounded(rangeRestriction);
27463  begin = mDataContainer->constBegin()+dataRange.begin();
27464  end = mDataContainer->constBegin()+dataRange.end();
27465  return;
27466  }
27467 
27468  // get visible data range via interface from data plottable, and then restrict to available error data points:
27469  const int n = qMin(mDataContainer->size(), mDataPlottable->interface1D()->dataCount());
27470  int beginIndex = mDataPlottable->interface1D()->findBegin(keyAxis->range().lower);
27471  int endIndex = mDataPlottable->interface1D()->findEnd(keyAxis->range().upper);
27472  int i = beginIndex;
27473  while (i > 0 && i < n && i > rangeRestriction.begin())
27474  {
27475  if (errorBarVisible(i))
27476  beginIndex = i;
27477  --i;
27478  }
27479  i = endIndex;
27480  while (i >= 0 && i < n && i < rangeRestriction.end())
27481  {
27482  if (errorBarVisible(i))
27483  endIndex = i+1;
27484  ++i;
27485  }
27486  QCPDataRange dataRange(beginIndex, endIndex);
27487  dataRange = dataRange.bounded(rangeRestriction.bounded(QCPDataRange(0, mDataContainer->size())));
27488  begin = mDataContainer->constBegin()+dataRange.begin();
27489  end = mDataContainer->constBegin()+dataRange.end();
27490 }
27491 
27498 double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const
27499 {
27500  closestData = mDataContainer->constEnd();
27501  if (!mDataPlottable || mDataContainer->isEmpty())
27502  return -1.0;
27503 
27504  QCPErrorBarsDataContainer::const_iterator begin, end;
27505  getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount()));
27506 
27507  // calculate minimum distances to error backbones (whiskers are ignored for speed) and find closestData iterator:
27508  double minDistSqr = std::numeric_limits<double>::max();
27509  QVector<QLineF> backbones, whiskers;
27510  for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
27511  {
27512  getErrorBarLines(it, backbones, whiskers);
27513  for (int i=0; i<backbones.size(); ++i)
27514  {
27515  const double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(backbones.at(i));
27516  if (currentDistSqr < minDistSqr)
27517  {
27518  minDistSqr = currentDistSqr;
27519  closestData = it;
27520  }
27521  }
27522  }
27523  return qSqrt(minDistSqr);
27524 }
27525 
27533 void QCPErrorBars::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const
27534 {
27535  selectedSegments.clear();
27536  unselectedSegments.clear();
27537  if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty
27538  {
27539  if (selected())
27540  selectedSegments << QCPDataRange(0, dataCount());
27541  else
27542  unselectedSegments << QCPDataRange(0, dataCount());
27543  } else
27544  {
27545  QCPDataSelection sel(selection());
27546  sel.simplify();
27547  selectedSegments = sel.dataRanges();
27548  unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges();
27549  }
27550 }
27551 
27561 bool QCPErrorBars::errorBarVisible(int index) const
27562 {
27563  QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);
27564  const double centerKeyPixel = mKeyAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
27565  if (qIsNaN(centerKeyPixel))
27566  return false;
27567 
27568  double keyMin, keyMax;
27569  if (mErrorType == etKeyError)
27570  {
27571  const double centerKey = mKeyAxis->pixelToCoord(centerKeyPixel);
27572  const double errorPlus = mDataContainer->at(index).errorPlus;
27573  const double errorMinus = mDataContainer->at(index).errorMinus;
27574  keyMax = centerKey+(qIsNaN(errorPlus) ? 0 : errorPlus);
27575  keyMin = centerKey-(qIsNaN(errorMinus) ? 0 : errorMinus);
27576  } else // mErrorType == etValueError
27577  {
27578  keyMax = mKeyAxis->pixelToCoord(centerKeyPixel+mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());
27579  keyMin = mKeyAxis->pixelToCoord(centerKeyPixel-mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());
27580  }
27581  return ((keyMax > mKeyAxis->range().lower) && (keyMin < mKeyAxis->range().upper));
27582 }
27583 
27591 bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const
27592 {
27593  if (pixelRect.left() > line.x1() && pixelRect.left() > line.x2())
27594  return false;
27595  else if (pixelRect.right() < line.x1() && pixelRect.right() < line.x2())
27596  return false;
27597  else if (pixelRect.top() > line.y1() && pixelRect.top() > line.y2())
27598  return false;
27599  else if (pixelRect.bottom() < line.y1() && pixelRect.bottom() < line.y2())
27600  return false;
27601  else
27602  return true;
27603 }
27604 /* end of 'src/plottables/plottable-errorbar.cpp' */
27605 
27606 
27607 /* including file 'src/items/item-straightline.cpp', size 7592 */
27608 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
27609 
27613 
27629  QCPAbstractItem(parentPlot),
27630  point1(createPosition(QLatin1String("point1"))),
27631  point2(createPosition(QLatin1String("point2")))
27632 {
27633  point1->setCoords(0, 0);
27634  point2->setCoords(1, 1);
27635 
27636  setPen(QPen(Qt::black));
27637  setSelectedPen(QPen(Qt::blue,2));
27638 }
27639 
27641 {
27642 }
27643 
27650 {
27651  mPen = pen;
27652 }
27653 
27660 {
27661  mSelectedPen = pen;
27662 }
27663 
27664 /* inherits documentation from base class */
27665 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
27666 {
27667  Q_UNUSED(details)
27668  if (onlySelectable && !mSelectable)
27669  return -1;
27670 
27672 }
27673 
27674 /* inherits documentation from base class */
27676 {
27677  QCPVector2D start(point1->pixelPosition());
27679  // get visible segment of straight line inside clipRect:
27680  double clipPad = mainPen().widthF();
27681  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
27682  // paint visible segment, if existent:
27683  if (!line.isNull())
27684  {
27685  painter->setPen(mainPen());
27686  painter->drawLine(line);
27687  }
27688 }
27689 
27697 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRect &rect) const
27698 {
27699  double bx, by;
27700  double gamma;
27701  QLineF result;
27702  if (vec.x() == 0 && vec.y() == 0)
27703  return result;
27704  if (qFuzzyIsNull(vec.x())) // line is vertical
27705  {
27706  // check top of rect:
27707  bx = rect.left();
27708  by = rect.top();
27709  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
27710  if (gamma >= 0 && gamma <= rect.width())
27711  result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
27712  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
27713  {
27714  // check left of rect:
27715  bx = rect.left();
27716  by = rect.top();
27717  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
27718  if (gamma >= 0 && gamma <= rect.height())
27719  result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
27720  } else // line is skewed
27721  {
27722  QList<QCPVector2D> pointVectors;
27723  // check top of rect:
27724  bx = rect.left();
27725  by = rect.top();
27726  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
27727  if (gamma >= 0 && gamma <= rect.width())
27728  pointVectors.append(QCPVector2D(bx+gamma, by));
27729  // check bottom of rect:
27730  bx = rect.left();
27731  by = rect.bottom();
27732  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
27733  if (gamma >= 0 && gamma <= rect.width())
27734  pointVectors.append(QCPVector2D(bx+gamma, by));
27735  // check left of rect:
27736  bx = rect.left();
27737  by = rect.top();
27738  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
27739  if (gamma >= 0 && gamma <= rect.height())
27740  pointVectors.append(QCPVector2D(bx, by+gamma));
27741  // check right of rect:
27742  bx = rect.right();
27743  by = rect.top();
27744  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
27745  if (gamma >= 0 && gamma <= rect.height())
27746  pointVectors.append(QCPVector2D(bx, by+gamma));
27747 
27748  // evaluate points:
27749  if (pointVectors.size() == 2)
27750  {
27751  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
27752  } else if (pointVectors.size() > 2)
27753  {
27754  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
27755  double distSqrMax = 0;
27756  QCPVector2D pv1, pv2;
27757  for (int i=0; i<pointVectors.size()-1; ++i)
27758  {
27759  for (int k=i+1; k<pointVectors.size(); ++k)
27760  {
27761  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
27762  if (distSqr > distSqrMax)
27763  {
27764  pv1 = pointVectors.at(i);
27765  pv2 = pointVectors.at(k);
27766  distSqrMax = distSqr;
27767  }
27768  }
27769  }
27770  result.setPoints(pv1.toPointF(), pv2.toPointF());
27771  }
27772  }
27773  return result;
27774 }
27775 
27782 {
27783  return mSelected ? mSelectedPen : mPen;
27784 }
27785 /* end of 'src/items/item-straightline.cpp' */
27786 
27787 
27788 /* including file 'src/items/item-line.cpp', size 8498 */
27789 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
27790 
27794 
27812  QCPAbstractItem(parentPlot),
27813  start(createPosition(QLatin1String("start"))),
27814  end(createPosition(QLatin1String("end")))
27815 {
27816  start->setCoords(0, 0);
27817  end->setCoords(1, 1);
27818 
27819  setPen(QPen(Qt::black));
27820  setSelectedPen(QPen(Qt::blue,2));
27821 }
27822 
27824 {
27825 }
27826 
27832 void QCPItemLine::setPen(const QPen &pen)
27833 {
27834  mPen = pen;
27835 }
27836 
27843 {
27844  mSelectedPen = pen;
27845 }
27846 
27856 {
27857  mHead = head;
27858 }
27859 
27869 {
27870  mTail = tail;
27871 }
27872 
27873 /* inherits documentation from base class */
27874 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
27875 {
27876  Q_UNUSED(details)
27877  if (onlySelectable && !mSelectable)
27878  return -1;
27879 
27880  return qSqrt(QCPVector2D(pos).distanceSquaredToLine(start->pixelPosition(), end->pixelPosition()));
27881 }
27882 
27883 /* inherits documentation from base class */
27885 {
27886  QCPVector2D startVec(start->pixelPosition());
27887  QCPVector2D endVec(end->pixelPosition());
27888  if (qFuzzyIsNull((startVec-endVec).lengthSquared()))
27889  return;
27890  // get visible segment of straight line inside clipRect:
27891  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
27892  clipPad = qMax(clipPad, (double)mainPen().widthF());
27893  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
27894  // paint visible segment, if existent:
27895  if (!line.isNull())
27896  {
27897  painter->setPen(mainPen());
27898  painter->drawLine(line);
27899  painter->setBrush(Qt::SolidPattern);
27901  mTail.draw(painter, startVec, startVec-endVec);
27903  mHead.draw(painter, endVec, endVec-startVec);
27904  }
27905 }
27906 
27914 QLineF QCPItemLine::getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const
27915 {
27916  bool containsStart = rect.contains(start.x(), start.y());
27917  bool containsEnd = rect.contains(end.x(), end.y());
27918  if (containsStart && containsEnd)
27919  return QLineF(start.toPointF(), end.toPointF());
27920 
27921  QCPVector2D base = start;
27922  QCPVector2D vec = end-start;
27923  double bx, by;
27924  double gamma, mu;
27925  QLineF result;
27926  QList<QCPVector2D> pointVectors;
27927 
27928  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
27929  {
27930  // check top of rect:
27931  bx = rect.left();
27932  by = rect.top();
27933  mu = (by-base.y())/vec.y();
27934  if (mu >= 0 && mu <= 1)
27935  {
27936  gamma = base.x()-bx + mu*vec.x();
27937  if (gamma >= 0 && gamma <= rect.width())
27938  pointVectors.append(QCPVector2D(bx+gamma, by));
27939  }
27940  // check bottom of rect:
27941  bx = rect.left();
27942  by = rect.bottom();
27943  mu = (by-base.y())/vec.y();
27944  if (mu >= 0 && mu <= 1)
27945  {
27946  gamma = base.x()-bx + mu*vec.x();
27947  if (gamma >= 0 && gamma <= rect.width())
27948  pointVectors.append(QCPVector2D(bx+gamma, by));
27949  }
27950  }
27951  if (!qFuzzyIsNull(vec.x())) // line is not vertical
27952  {
27953  // check left of rect:
27954  bx = rect.left();
27955  by = rect.top();
27956  mu = (bx-base.x())/vec.x();
27957  if (mu >= 0 && mu <= 1)
27958  {
27959  gamma = base.y()-by + mu*vec.y();
27960  if (gamma >= 0 && gamma <= rect.height())
27961  pointVectors.append(QCPVector2D(bx, by+gamma));
27962  }
27963  // check right of rect:
27964  bx = rect.right();
27965  by = rect.top();
27966  mu = (bx-base.x())/vec.x();
27967  if (mu >= 0 && mu <= 1)
27968  {
27969  gamma = base.y()-by + mu*vec.y();
27970  if (gamma >= 0 && gamma <= rect.height())
27971  pointVectors.append(QCPVector2D(bx, by+gamma));
27972  }
27973  }
27974 
27975  if (containsStart)
27976  pointVectors.append(start);
27977  if (containsEnd)
27978  pointVectors.append(end);
27979 
27980  // evaluate points:
27981  if (pointVectors.size() == 2)
27982  {
27983  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
27984  } else if (pointVectors.size() > 2)
27985  {
27986  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
27987  double distSqrMax = 0;
27988  QCPVector2D pv1, pv2;
27989  for (int i=0; i<pointVectors.size()-1; ++i)
27990  {
27991  for (int k=i+1; k<pointVectors.size(); ++k)
27992  {
27993  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
27994  if (distSqr > distSqrMax)
27995  {
27996  pv1 = pointVectors.at(i);
27997  pv2 = pointVectors.at(k);
27998  distSqrMax = distSqr;
27999  }
28000  }
28001  }
28002  result.setPoints(pv1.toPointF(), pv2.toPointF());
28003  }
28004  return result;
28005 }
28006 
28013 {
28014  return mSelected ? mSelectedPen : mPen;
28015 }
28016 /* end of 'src/items/item-line.cpp' */
28017 
28018 
28019 /* including file 'src/items/item-curve.cpp', size 7159 */
28020 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
28021 
28025 
28050  QCPAbstractItem(parentPlot),
28051  start(createPosition(QLatin1String("start"))),
28052  startDir(createPosition(QLatin1String("startDir"))),
28053  endDir(createPosition(QLatin1String("endDir"))),
28054  end(createPosition(QLatin1String("end")))
28055 {
28056  start->setCoords(0, 0);
28057  startDir->setCoords(0.5, 0);
28058  endDir->setCoords(0, 0.5);
28059  end->setCoords(1, 1);
28060 
28061  setPen(QPen(Qt::black));
28062  setSelectedPen(QPen(Qt::blue,2));
28063 }
28064 
28066 {
28067 }
28068 
28074 void QCPItemCurve::setPen(const QPen &pen)
28075 {
28076  mPen = pen;
28077 }
28078 
28085 {
28086  mSelectedPen = pen;
28087 }
28088 
28098 {
28099  mHead = head;
28100 }
28101 
28111 {
28112  mTail = tail;
28113 }
28114 
28115 /* inherits documentation from base class */
28116 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28117 {
28118  Q_UNUSED(details)
28119  if (onlySelectable && !mSelectable)
28120  return -1;
28121 
28122  QPointF startVec(start->pixelPosition());
28123  QPointF startDirVec(startDir->pixelPosition());
28124  QPointF endDirVec(endDir->pixelPosition());
28125  QPointF endVec(end->pixelPosition());
28126 
28127  QPainterPath cubicPath(startVec);
28128  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
28129 
28130  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
28131  QCPVector2D p(pos);
28132  double minDistSqr = std::numeric_limits<double>::max();
28133  for (int i=1; i<polygon.size(); ++i)
28134  {
28135  double distSqr = p.distanceSquaredToLine(polygon.at(i-1), polygon.at(i));
28136  if (distSqr < minDistSqr)
28137  minDistSqr = distSqr;
28138  }
28139  return qSqrt(minDistSqr);
28140 }
28141 
28142 /* inherits documentation from base class */
28144 {
28145  QCPVector2D startVec(start->pixelPosition());
28146  QCPVector2D startDirVec(startDir->pixelPosition());
28147  QCPVector2D endDirVec(endDir->pixelPosition());
28148  QCPVector2D endVec(end->pixelPosition());
28149  if ((endVec-startVec).length() > 1e10) // too large curves cause crash
28150  return;
28151 
28152  QPainterPath cubicPath(startVec.toPointF());
28153  cubicPath.cubicTo(startDirVec.toPointF(), endDirVec.toPointF(), endVec.toPointF());
28154 
28155  // paint visible segment, if existent:
28156  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
28157  QRect cubicRect = cubicPath.controlPointRect().toRect();
28158  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
28159  cubicRect.adjust(0, 0, 1, 1);
28160  if (clip.intersects(cubicRect))
28161  {
28162  painter->setPen(mainPen());
28163  painter->drawPath(cubicPath);
28164  painter->setBrush(Qt::SolidPattern);
28166  mTail.draw(painter, startVec, M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
28168  mHead.draw(painter, endVec, -cubicPath.angleAtPercent(1)/180.0*M_PI);
28169  }
28170 }
28171 
28178 {
28179  return mSelected ? mSelectedPen : mPen;
28180 }
28181 /* end of 'src/items/item-curve.cpp' */
28182 
28183 
28184 /* including file 'src/items/item-rect.cpp', size 6479 */
28185 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
28186 
28190 
28206  QCPAbstractItem(parentPlot),
28207  topLeft(createPosition(QLatin1String("topLeft"))),
28208  bottomRight(createPosition(QLatin1String("bottomRight"))),
28209  top(createAnchor(QLatin1String("top"), aiTop)),
28210  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
28211  right(createAnchor(QLatin1String("right"), aiRight)),
28212  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
28213  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
28214  left(createAnchor(QLatin1String("left"), aiLeft))
28215 {
28216  topLeft->setCoords(0, 1);
28217  bottomRight->setCoords(1, 0);
28218 
28219  setPen(QPen(Qt::black));
28220  setSelectedPen(QPen(Qt::blue,2));
28221  setBrush(Qt::NoBrush);
28222  setSelectedBrush(Qt::NoBrush);
28223 }
28224 
28226 {
28227 }
28228 
28234 void QCPItemRect::setPen(const QPen &pen)
28235 {
28236  mPen = pen;
28237 }
28238 
28245 {
28246  mSelectedPen = pen;
28247 }
28248 
28255 void QCPItemRect::setBrush(const QBrush &brush)
28256 {
28257  mBrush = brush;
28258 }
28259 
28267 {
28269 }
28270 
28271 /* inherits documentation from base class */
28272 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28273 {
28274  Q_UNUSED(details)
28275  if (onlySelectable && !mSelectable)
28276  return -1;
28277 
28278  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition()).normalized();
28279  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
28280  return rectDistance(rect, pos, filledRect);
28281 }
28282 
28283 /* inherits documentation from base class */
28285 {
28286  QPointF p1 = topLeft->pixelPosition();
28287  QPointF p2 = bottomRight->pixelPosition();
28288  if (p1.toPoint() == p2.toPoint())
28289  return;
28290  QRectF rect = QRectF(p1, p2).normalized();
28291  double clipPad = mainPen().widthF();
28292  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
28293  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
28294  {
28295  painter->setPen(mainPen());
28296  painter->setBrush(mainBrush());
28297  painter->drawRect(rect);
28298  }
28299 }
28300 
28301 /* inherits documentation from base class */
28302 QPointF QCPItemRect::anchorPixelPosition(int anchorId) const
28303 {
28304  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());
28305  switch (anchorId)
28306  {
28307  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
28308  case aiTopRight: return rect.topRight();
28309  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
28310  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
28311  case aiBottomLeft: return rect.bottomLeft();
28312  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
28313  }
28314 
28315  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
28316  return QPointF();
28317 }
28318 
28325 {
28326  return mSelected ? mSelectedPen : mPen;
28327 }
28328 
28335 {
28336  return mSelected ? mSelectedBrush : mBrush;
28337 }
28338 /* end of 'src/items/item-rect.cpp' */
28339 
28340 
28341 /* including file 'src/items/item-text.cpp', size 13338 */
28342 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
28343 
28347 
28369  QCPAbstractItem(parentPlot),
28370  position(createPosition(QLatin1String("position"))),
28371  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
28372  top(createAnchor(QLatin1String("top"), aiTop)),
28373  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
28374  right(createAnchor(QLatin1String("right"), aiRight)),
28375  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
28376  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
28377  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
28378  left(createAnchor(QLatin1String("left"), aiLeft)),
28379  mText(QLatin1String("text")),
28380  mPositionAlignment(Qt::AlignCenter),
28381  mTextAlignment(Qt::AlignTop|Qt::AlignHCenter),
28382  mRotation(0)
28383 {
28384  position->setCoords(0, 0);
28385 
28386  setPen(Qt::NoPen);
28387  setSelectedPen(Qt::NoPen);
28388  setBrush(Qt::NoBrush);
28389  setSelectedBrush(Qt::NoBrush);
28390  setColor(Qt::black);
28391  setSelectedColor(Qt::blue);
28392 }
28393 
28395 {
28396 }
28397 
28401 void QCPItemText::setColor(const QColor &color)
28402 {
28403  mColor = color;
28404 }
28405 
28410 {
28412 }
28413 
28420 void QCPItemText::setPen(const QPen &pen)
28421 {
28422  mPen = pen;
28423 }
28424 
28432 {
28433  mSelectedPen = pen;
28434 }
28435 
28442 void QCPItemText::setBrush(const QBrush &brush)
28443 {
28444  mBrush = brush;
28445 }
28446 
28454 {
28456 }
28457 
28463 void QCPItemText::setFont(const QFont &font)
28464 {
28465  mFont = font;
28466 }
28467 
28474 {
28475  mSelectedFont = font;
28476 }
28477 
28484 void QCPItemText::setText(const QString &text)
28485 {
28486  mText = text;
28487 }
28488 
28501 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
28502 {
28503  mPositionAlignment = alignment;
28504 }
28505 
28509 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
28510 {
28511  mTextAlignment = alignment;
28512 }
28513 
28518 void QCPItemText::setRotation(double degrees)
28519 {
28520  mRotation = degrees;
28521 }
28522 
28527 void QCPItemText::setPadding(const QMargins &padding)
28528 {
28529  mPadding = padding;
28530 }
28531 
28532 /* inherits documentation from base class */
28533 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28534 {
28535  Q_UNUSED(details)
28536  if (onlySelectable && !mSelectable)
28537  return -1;
28538 
28539  // The rect may be rotated, so we transform the actual clicked pos to the rotated
28540  // coordinate system, so we can use the normal rectDistance function for non-rotated rects:
28541  QPointF positionPixels(position->pixelPosition());
28542  QTransform inputTransform;
28543  inputTransform.translate(positionPixels.x(), positionPixels.y());
28544  inputTransform.rotate(-mRotation);
28545  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
28546  QPointF rotatedPos = inputTransform.map(pos);
28547  QFontMetrics fontMetrics(mFont);
28548  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
28549  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
28550  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
28551  textBoxRect.moveTopLeft(textPos.toPoint());
28552 
28553  return rectDistance(textBoxRect, rotatedPos, true);
28554 }
28555 
28556 /* inherits documentation from base class */
28558 {
28559  QPointF pos(position->pixelPosition());
28560  QTransform transform = painter->transform();
28561  transform.translate(pos.x(), pos.y());
28562  if (!qFuzzyIsNull(mRotation))
28563  transform.rotate(mRotation);
28564  painter->setFont(mainFont());
28565  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
28566  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
28567  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
28568  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
28569  textBoxRect.moveTopLeft(textPos.toPoint());
28570  double clipPad = mainPen().widthF();
28571  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
28572  if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
28573  {
28574  painter->setTransform(transform);
28575  if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
28576  (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
28577  {
28578  painter->setPen(mainPen());
28579  painter->setBrush(mainBrush());
28580  painter->drawRect(textBoxRect);
28581  }
28582  painter->setBrush(Qt::NoBrush);
28583  painter->setPen(QPen(mainColor()));
28584  painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
28585  }
28586 }
28587 
28588 /* inherits documentation from base class */
28589 QPointF QCPItemText::anchorPixelPosition(int anchorId) const
28590 {
28591  // get actual rect points (pretty much copied from draw function):
28592  QPointF pos(position->pixelPosition());
28593  QTransform transform;
28594  transform.translate(pos.x(), pos.y());
28595  if (!qFuzzyIsNull(mRotation))
28596  transform.rotate(mRotation);
28597  QFontMetrics fontMetrics(mainFont());
28598  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
28599  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
28600  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
28601  textBoxRect.moveTopLeft(textPos.toPoint());
28602  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
28603 
28604  switch (anchorId)
28605  {
28606  case aiTopLeft: return rectPoly.at(0);
28607  case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
28608  case aiTopRight: return rectPoly.at(1);
28609  case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
28610  case aiBottomRight: return rectPoly.at(2);
28611  case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
28612  case aiBottomLeft: return rectPoly.at(3);
28613  case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
28614  }
28615 
28616  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
28617  return QPointF();
28618 }
28619 
28630 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
28631 {
28632  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
28633  return pos;
28634 
28635  QPointF result = pos; // start at top left
28636  if (positionAlignment.testFlag(Qt::AlignHCenter))
28637  result.rx() -= rect.width()/2.0;
28638  else if (positionAlignment.testFlag(Qt::AlignRight))
28639  result.rx() -= rect.width();
28640  if (positionAlignment.testFlag(Qt::AlignVCenter))
28641  result.ry() -= rect.height()/2.0;
28642  else if (positionAlignment.testFlag(Qt::AlignBottom))
28643  result.ry() -= rect.height();
28644  return result;
28645 }
28646 
28653 {
28654  return mSelected ? mSelectedFont : mFont;
28655 }
28656 
28663 {
28664  return mSelected ? mSelectedColor : mColor;
28665 }
28666 
28673 {
28674  return mSelected ? mSelectedPen : mPen;
28675 }
28676 
28683 {
28684  return mSelected ? mSelectedBrush : mBrush;
28685 }
28686 /* end of 'src/items/item-text.cpp' */
28687 
28688 
28689 /* including file 'src/items/item-ellipse.cpp', size 7863 */
28690 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
28691 
28695 
28711  QCPAbstractItem(parentPlot),
28712  topLeft(createPosition(QLatin1String("topLeft"))),
28713  bottomRight(createPosition(QLatin1String("bottomRight"))),
28714  topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
28715  top(createAnchor(QLatin1String("top"), aiTop)),
28716  topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
28717  right(createAnchor(QLatin1String("right"), aiRight)),
28718  bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
28719  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
28720  bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
28721  left(createAnchor(QLatin1String("left"), aiLeft)),
28722  center(createAnchor(QLatin1String("center"), aiCenter))
28723 {
28724  topLeft->setCoords(0, 1);
28725  bottomRight->setCoords(1, 0);
28726 
28727  setPen(QPen(Qt::black));
28728  setSelectedPen(QPen(Qt::blue, 2));
28729  setBrush(Qt::NoBrush);
28730  setSelectedBrush(Qt::NoBrush);
28731 }
28732 
28734 {
28735 }
28736 
28742 void QCPItemEllipse::setPen(const QPen &pen)
28743 {
28744  mPen = pen;
28745 }
28746 
28753 {
28754  mSelectedPen = pen;
28755 }
28756 
28763 void QCPItemEllipse::setBrush(const QBrush &brush)
28764 {
28765  mBrush = brush;
28766 }
28767 
28775 {
28777 }
28778 
28779 /* inherits documentation from base class */
28780 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28781 {
28782  Q_UNUSED(details)
28783  if (onlySelectable && !mSelectable)
28784  return -1;
28785 
28786  QPointF p1 = topLeft->pixelPosition();
28787  QPointF p2 = bottomRight->pixelPosition();
28788  QPointF center((p1+p2)/2.0);
28789  double a = qAbs(p1.x()-p2.x())/2.0;
28790  double b = qAbs(p1.y()-p2.y())/2.0;
28791  double x = pos.x()-center.x();
28792  double y = pos.y()-center.y();
28793 
28794  // distance to border:
28795  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
28796  double result = qAbs(c-1)*qSqrt(x*x+y*y);
28797  // filled ellipse, allow click inside to count as hit:
28798  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
28799  {
28800  if (x*x/(a*a) + y*y/(b*b) <= 1)
28801  result = mParentPlot->selectionTolerance()*0.99;
28802  }
28803  return result;
28804 }
28805 
28806 /* inherits documentation from base class */
28808 {
28809  QPointF p1 = topLeft->pixelPosition();
28810  QPointF p2 = bottomRight->pixelPosition();
28811  if (p1.toPoint() == p2.toPoint())
28812  return;
28813  QRectF ellipseRect = QRectF(p1, p2).normalized();
28814  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
28815  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
28816  {
28817  painter->setPen(mainPen());
28818  painter->setBrush(mainBrush());
28819 #ifdef __EXCEPTIONS
28820  try // drawEllipse sometimes throws exceptions if ellipse is too big
28821  {
28822 #endif
28823  painter->drawEllipse(ellipseRect);
28824 #ifdef __EXCEPTIONS
28825  } catch (...)
28826  {
28827  qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
28828  setVisible(false);
28829  }
28830 #endif
28831  }
28832 }
28833 
28834 /* inherits documentation from base class */
28835 QPointF QCPItemEllipse::anchorPixelPosition(int anchorId) const
28836 {
28837  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());
28838  switch (anchorId)
28839  {
28840  case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
28841  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
28842  case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
28843  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
28844  case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
28845  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
28846  case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
28847  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
28848  case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
28849  }
28850 
28851  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
28852  return QPointF();
28853 }
28854 
28861 {
28862  return mSelected ? mSelectedPen : mPen;
28863 }
28864 
28871 {
28872  return mSelected ? mSelectedBrush : mBrush;
28873 }
28874 /* end of 'src/items/item-ellipse.cpp' */
28875 
28876 
28877 /* including file 'src/items/item-pixmap.cpp', size 10615 */
28878 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
28879 
28883 
28905  QCPAbstractItem(parentPlot),
28906  topLeft(createPosition(QLatin1String("topLeft"))),
28907  bottomRight(createPosition(QLatin1String("bottomRight"))),
28908  top(createAnchor(QLatin1String("top"), aiTop)),
28909  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
28910  right(createAnchor(QLatin1String("right"), aiRight)),
28911  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
28912  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
28913  left(createAnchor(QLatin1String("left"), aiLeft)),
28914  mScaled(false),
28915  mScaledPixmapInvalidated(true),
28916  mAspectRatioMode(Qt::KeepAspectRatio),
28917  mTransformationMode(Qt::SmoothTransformation)
28918 {
28919  topLeft->setCoords(0, 1);
28920  bottomRight->setCoords(1, 0);
28921 
28922  setPen(Qt::NoPen);
28923  setSelectedPen(QPen(Qt::blue));
28924 }
28925 
28927 {
28928 }
28929 
28933 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
28934 {
28935  mPixmap = pixmap;
28936  mScaledPixmapInvalidated = true;
28937  if (mPixmap.isNull())
28938  qDebug() << Q_FUNC_INFO << "pixmap is null";
28939 }
28940 
28945 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
28946 {
28947  mScaled = scaled;
28950  mScaledPixmapInvalidated = true;
28951 }
28952 
28958 void QCPItemPixmap::setPen(const QPen &pen)
28959 {
28960  mPen = pen;
28961 }
28962 
28969 {
28970  mSelectedPen = pen;
28971 }
28972 
28973 /* inherits documentation from base class */
28974 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28975 {
28976  Q_UNUSED(details)
28977  if (onlySelectable && !mSelectable)
28978  return -1;
28979 
28980  return rectDistance(getFinalRect(), pos, true);
28981 }
28982 
28983 /* inherits documentation from base class */
28985 {
28986  bool flipHorz = false;
28987  bool flipVert = false;
28988  QRect rect = getFinalRect(&flipHorz, &flipVert);
28989  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
28990  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
28991  if (boundingRect.intersects(clipRect()))
28992  {
28993  updateScaledPixmap(rect, flipHorz, flipVert);
28994  painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
28995  QPen pen = mainPen();
28996  if (pen.style() != Qt::NoPen)
28997  {
28998  painter->setPen(pen);
28999  painter->setBrush(Qt::NoBrush);
29000  painter->drawRect(rect);
29001  }
29002  }
29003 }
29004 
29005 /* inherits documentation from base class */
29006 QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const
29007 {
29008  bool flipHorz;
29009  bool flipVert;
29010  QRect rect = getFinalRect(&flipHorz, &flipVert);
29011  // we actually want denormal rects (negative width/height) here, so restore
29012  // the flipped state:
29013  if (flipHorz)
29014  rect.adjust(rect.width(), 0, -rect.width(), 0);
29015  if (flipVert)
29016  rect.adjust(0, rect.height(), 0, -rect.height());
29017 
29018  switch (anchorId)
29019  {
29020  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
29021  case aiTopRight: return rect.topRight();
29022  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
29023  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
29024  case aiBottomLeft: return rect.bottomLeft();
29025  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
29026  }
29027 
29028  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
29029  return QPointF();
29030 }
29031 
29045 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
29046 {
29047  if (mPixmap.isNull())
29048  return;
29049 
29050  if (mScaled)
29051  {
29052 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29053  double devicePixelRatio = mPixmap.devicePixelRatio();
29054 #else
29055  double devicePixelRatio = 1.0;
29056 #endif
29057  if (finalRect.isNull())
29058  finalRect = getFinalRect(&flipHorz, &flipVert);
29059  if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size()/devicePixelRatio)
29060  {
29061  mScaledPixmap = mPixmap.scaled(finalRect.size()*devicePixelRatio, mAspectRatioMode, mTransformationMode);
29062  if (flipHorz || flipVert)
29063  mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
29064 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29065  mScaledPixmap.setDevicePixelRatio(devicePixelRatio);
29066 #endif
29067  }
29068  } else if (!mScaledPixmap.isNull())
29069  mScaledPixmap = QPixmap();
29070  mScaledPixmapInvalidated = false;
29071 }
29072 
29087 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
29088 {
29089  QRect result;
29090  bool flipHorz = false;
29091  bool flipVert = false;
29092  QPoint p1 = topLeft->pixelPosition().toPoint();
29093  QPoint p2 = bottomRight->pixelPosition().toPoint();
29094  if (p1 == p2)
29095  return QRect(p1, QSize(0, 0));
29096  if (mScaled)
29097  {
29098  QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
29099  QPoint topLeft = p1;
29100  if (newSize.width() < 0)
29101  {
29102  flipHorz = true;
29103  newSize.rwidth() *= -1;
29104  topLeft.setX(p2.x());
29105  }
29106  if (newSize.height() < 0)
29107  {
29108  flipVert = true;
29109  newSize.rheight() *= -1;
29110  topLeft.setY(p2.y());
29111  }
29112  QSize scaledSize = mPixmap.size();
29113 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29114  scaledSize /= mPixmap.devicePixelRatio();
29115  scaledSize.scale(newSize*mPixmap.devicePixelRatio(), mAspectRatioMode);
29116 #else
29117  scaledSize.scale(newSize, mAspectRatioMode);
29118 #endif
29119  result = QRect(topLeft, scaledSize);
29120  } else
29121  {
29122 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29123  result = QRect(p1, mPixmap.size()/mPixmap.devicePixelRatio());
29124 #else
29125  result = QRect(p1, mPixmap.size());
29126 #endif
29127  }
29128  if (flippedHorz)
29129  *flippedHorz = flipHorz;
29130  if (flippedVert)
29131  *flippedVert = flipVert;
29132  return result;
29133 }
29134 
29141 {
29142  return mSelected ? mSelectedPen : mPen;
29143 }
29144 /* end of 'src/items/item-pixmap.cpp' */
29145 
29146 
29147 /* including file 'src/items/item-tracer.cpp', size 14624 */
29148 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
29149 
29153 
29191  QCPAbstractItem(parentPlot),
29192  position(createPosition(QLatin1String("position"))),
29193  mSize(6),
29194  mStyle(tsCrosshair),
29195  mGraph(0),
29196  mGraphKey(0),
29197  mInterpolating(false)
29198 {
29199  position->setCoords(0, 0);
29200 
29201  setBrush(Qt::NoBrush);
29202  setSelectedBrush(Qt::NoBrush);
29203  setPen(QPen(Qt::black));
29204  setSelectedPen(QPen(Qt::blue, 2));
29205 }
29206 
29208 {
29209 }
29210 
29216 void QCPItemTracer::setPen(const QPen &pen)
29217 {
29218  mPen = pen;
29219 }
29220 
29227 {
29228  mSelectedPen = pen;
29229 }
29230 
29236 void QCPItemTracer::setBrush(const QBrush &brush)
29237 {
29238  mBrush = brush;
29239 }
29240 
29247 {
29249 }
29250 
29256 {
29257  mSize = size;
29258 }
29259 
29267 {
29268  mStyle = style;
29269 }
29270 
29282 {
29283  if (graph)
29284  {
29285  if (graph->parentPlot() == mParentPlot)
29286  {
29288  position->setAxes(graph->keyAxis(), graph->valueAxis());
29289  mGraph = graph;
29290  updatePosition();
29291  } else
29292  qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
29293  } else
29294  {
29295  mGraph = 0;
29296  }
29297 }
29298 
29309 {
29310  mGraphKey = key;
29311 }
29312 
29325 {
29326  mInterpolating = enabled;
29327 }
29328 
29329 /* inherits documentation from base class */
29330 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
29331 {
29332  Q_UNUSED(details)
29333  if (onlySelectable && !mSelectable)
29334  return -1;
29335 
29336  QPointF center(position->pixelPosition());
29337  double w = mSize/2.0;
29338  QRect clip = clipRect();
29339  switch (mStyle)
29340  {
29341  case tsNone: return -1;
29342  case tsPlus:
29343  {
29344  if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29345  return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(center+QPointF(-w, 0), center+QPointF(w, 0)),
29346  QCPVector2D(pos).distanceSquaredToLine(center+QPointF(0, -w), center+QPointF(0, w))));
29347  break;
29348  }
29349  case tsCrosshair:
29350  {
29351  return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(clip.left(), center.y()), QCPVector2D(clip.right(), center.y())),
29352  QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(center.x(), clip.top()), QCPVector2D(center.x(), clip.bottom()))));
29353  }
29354  case tsCircle:
29355  {
29356  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29357  {
29358  // distance to border:
29359  double centerDist = QCPVector2D(center-pos).length();
29360  double circleLine = w;
29361  double result = qAbs(centerDist-circleLine);
29362  // filled ellipse, allow click inside to count as hit:
29363  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
29364  {
29365  if (centerDist <= circleLine)
29366  result = mParentPlot->selectionTolerance()*0.99;
29367  }
29368  return result;
29369  }
29370  break;
29371  }
29372  case tsSquare:
29373  {
29374  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29375  {
29376  QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
29377  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
29378  return rectDistance(rect, pos, filledRect);
29379  }
29380  break;
29381  }
29382  }
29383  return -1;
29384 }
29385 
29386 /* inherits documentation from base class */
29388 {
29389  updatePosition();
29390  if (mStyle == tsNone)
29391  return;
29392 
29393  painter->setPen(mainPen());
29394  painter->setBrush(mainBrush());
29395  QPointF center(position->pixelPosition());
29396  double w = mSize/2.0;
29397  QRect clip = clipRect();
29398  switch (mStyle)
29399  {
29400  case tsNone: return;
29401  case tsPlus:
29402  {
29403  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29404  {
29405  painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
29406  painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
29407  }
29408  break;
29409  }
29410  case tsCrosshair:
29411  {
29412  if (center.y() > clip.top() && center.y() < clip.bottom())
29413  painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
29414  if (center.x() > clip.left() && center.x() < clip.right())
29415  painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
29416  break;
29417  }
29418  case tsCircle:
29419  {
29420  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29421  painter->drawEllipse(center, w, w);
29422  break;
29423  }
29424  case tsSquare:
29425  {
29426  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29427  painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
29428  break;
29429  }
29430  }
29431 }
29432 
29446 {
29447  if (mGraph)
29448  {
29450  {
29451  if (mGraph->data()->size() > 1)
29452  {
29453  QCPGraphDataContainer::const_iterator first = mGraph->data()->constBegin();
29454  QCPGraphDataContainer::const_iterator last = mGraph->data()->constEnd()-1;
29455  if (mGraphKey <= first->key)
29456  position->setCoords(first->key, first->value);
29457  else if (mGraphKey >= last->key)
29458  position->setCoords(last->key, last->value);
29459  else
29460  {
29462  if (it != mGraph->data()->constEnd()) // mGraphKey is not exactly on last iterator, but somewhere between iterators
29463  {
29465  ++it; // won't advance to constEnd because we handled that case (mGraphKey >= last->key) before
29466  if (mInterpolating)
29467  {
29468  // interpolate between iterators around mGraphKey:
29469  double slope = 0;
29470  if (!qFuzzyCompare((double)it->key, (double)prevIt->key))
29471  slope = (it->value-prevIt->value)/(it->key-prevIt->key);
29472  position->setCoords(mGraphKey, (mGraphKey-prevIt->key)*slope+prevIt->value);
29473  } else
29474  {
29475  // find iterator with key closest to mGraphKey:
29476  if (mGraphKey < (prevIt->key+it->key)*0.5)
29477  position->setCoords(prevIt->key, prevIt->value);
29478  else
29479  position->setCoords(it->key, it->value);
29480  }
29481  } else // mGraphKey is exactly on last iterator (should actually be caught when comparing first/last keys, but this is a failsafe for fp uncertainty)
29482  position->setCoords(it->key, it->value);
29483  }
29484  } else if (mGraph->data()->size() == 1)
29485  {
29486  QCPGraphDataContainer::const_iterator it = mGraph->data()->constBegin();
29487  position->setCoords(it->key, it->value);
29488  } else
29489  qDebug() << Q_FUNC_INFO << "graph has no data";
29490  } else
29491  qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
29492  }
29493 }
29494 
29501 {
29502  return mSelected ? mSelectedPen : mPen;
29503 }
29504 
29511 {
29512  return mSelected ? mSelectedBrush : mBrush;
29513 }
29514 /* end of 'src/items/item-tracer.cpp' */
29515 
29516 
29517 /* including file 'src/items/item-bracket.cpp', size 10687 */
29518 /* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */
29519 
29523 
29551  QCPAbstractItem(parentPlot),
29552  left(createPosition(QLatin1String("left"))),
29553  right(createPosition(QLatin1String("right"))),
29554  center(createAnchor(QLatin1String("center"), aiCenter)),
29555  mLength(8),
29556  mStyle(bsCalligraphic)
29557 {
29558  left->setCoords(0, 0);
29559  right->setCoords(1, 1);
29560 
29561  setPen(QPen(Qt::black));
29562  setSelectedPen(QPen(Qt::blue, 2));
29563 }
29564 
29566 {
29567 }
29568 
29578 void QCPItemBracket::setPen(const QPen &pen)
29579 {
29580  mPen = pen;
29581 }
29582 
29589 {
29590  mSelectedPen = pen;
29591 }
29592 
29602 {
29603  mLength = length;
29604 }
29605 
29612 {
29613  mStyle = style;
29614 }
29615 
29616 /* inherits documentation from base class */
29617 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
29618 {
29619  Q_UNUSED(details)
29620  if (onlySelectable && !mSelectable)
29621  return -1;
29622 
29623  QCPVector2D p(pos);
29624  QCPVector2D leftVec(left->pixelPosition());
29625  QCPVector2D rightVec(right->pixelPosition());
29626  if (leftVec.toPoint() == rightVec.toPoint())
29627  return -1;
29628 
29629  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
29630  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
29631  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
29632 
29633  switch (mStyle)
29634  {
29637  {
29638  double a = p.distanceSquaredToLine(centerVec-widthVec, centerVec+widthVec);
29639  double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec, centerVec-widthVec);
29640  double c = p.distanceSquaredToLine(centerVec+widthVec+lengthVec, centerVec+widthVec);
29641  return qSqrt(qMin(qMin(a, b), c));
29642  }
29645  {
29646  double a = p.distanceSquaredToLine(centerVec-widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
29647  double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec*0.7, centerVec-widthVec*0.75+lengthVec*0.15);
29648  double c = p.distanceSquaredToLine(centerVec+widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
29649  double d = p.distanceSquaredToLine(centerVec+widthVec+lengthVec*0.7, centerVec+widthVec*0.75+lengthVec*0.15);
29650  return qSqrt(qMin(qMin(a, b), qMin(c, d)));
29651  }
29652  }
29653  return -1;
29654 }
29655 
29656 /* inherits documentation from base class */
29658 {
29659  QCPVector2D leftVec(left->pixelPosition());
29660  QCPVector2D rightVec(right->pixelPosition());
29661  if (leftVec.toPoint() == rightVec.toPoint())
29662  return;
29663 
29664  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
29665  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
29666  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
29667 
29668  QPolygon boundingPoly;
29669  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
29670  << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
29671  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
29672  if (clip.intersects(boundingPoly.boundingRect()))
29673  {
29674  painter->setPen(mainPen());
29675  switch (mStyle)
29676  {
29677  case bsSquare:
29678  {
29679  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
29680  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
29681  painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
29682  break;
29683  }
29684  case bsRound:
29685  {
29686  painter->setBrush(Qt::NoBrush);
29687  QPainterPath path;
29688  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
29689  path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
29690  path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
29691  painter->drawPath(path);
29692  break;
29693  }
29694  case bsCurly:
29695  {
29696  painter->setBrush(Qt::NoBrush);
29697  QPainterPath path;
29698  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
29699  path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF());
29700  path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
29701  painter->drawPath(path);
29702  break;
29703  }
29704  case bsCalligraphic:
29705  {
29706  painter->setPen(Qt::NoPen);
29707  painter->setBrush(QBrush(mainPen().color()));
29708  QPainterPath path;
29709  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
29710 
29711  path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
29712  path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
29713 
29714  path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
29715  path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
29716 
29717  painter->drawPath(path);
29718  break;
29719  }
29720  }
29721  }
29722 }
29723 
29724 /* inherits documentation from base class */
29725 QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const
29726 {
29727  QCPVector2D leftVec(left->pixelPosition());
29728  QCPVector2D rightVec(right->pixelPosition());
29729  if (leftVec.toPoint() == rightVec.toPoint())
29730  return leftVec.toPointF();
29731 
29732  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
29733  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
29734  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
29735 
29736  switch (anchorId)
29737  {
29738  case aiCenter:
29739  return centerVec.toPointF();
29740  }
29741  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
29742  return QPointF();
29743 }
29744 
29751 {
29752  return mSelected ? mSelectedPen : mPen;
29753 }
29754 /* end of 'src/items/item-bracket.cpp' */
29755 
29756 
double devicePixelRatio() const
Definition: qcustomplot.h:521
double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
QList< QList< QCPLayoutElement * > > mElements
Definition: qcustomplot.h:1374
QSharedPointer< QCPCurveDataContainer > data() const
Definition: qcustomplot.h:5281
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:4642
double cleanMantissa(double input) const
double size() const
Definition: qcustomplot.h:791
void setType(PositionType type)
QCPLayoutElement(QCustomPlot *parentPlot=0)
QPen basePen() const
Definition: qcustomplot.h:2006
QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale)
void itemClick(QCPAbstractItem *item, QMouseEvent *event)
void drawSubGridLines(QCPPainter *painter) const
Q_SLOT void setSelection(QCPDataSelection selection)
QPen mSelectedPen
Definition: qcustomplot.h:6231
QList< QSharedPointer< QCPAbstractPaintBuffer > > mPaintBuffers
Definition: qcustomplot.h:3750
void addElement(QCPLayoutElement *element, Qt::Alignment alignment)
bool rangeZoom() const
QCPItemPosition * position(const QString &name) const
void insertRow(int newIndex)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QVector< QPointF > dataToStepLeftLines(const QVector< QCPGraphData > &data) const
A margin group allows synchronization of margin sides if working with multiple layout elements...
Definition: qcustomplot.h:1145
One individual data point can be selected at a time.
Definition: qcustomplot.h:290
QList< QCPGraph * > mGraphs
Definition: qcustomplot.h:3730
QCPGraph * addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0)
virtual ~QCPAxisPainterPrivate()
void replot()
Holds the data of one single error bar for QCPErrorBars.
Definition: qcustomplot.h:5921
QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setSubTickLengthIn(int inside)
QMargins padding() const
Definition: qcustomplot.h:6284
void setMargins(const QMargins &margins)
0x04 Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width...
Definition: qcustomplot.h:464
void setBracketBrush(const QBrush &brush)
QCPDataRange intersection(const QCPDataRange &other) const
bool stopsUseAlpha() const
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
0xFFFF All elements
Definition: qcustomplot.h:222
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QList< QCPLayoutElement * > elements(QCP::MarginSide side) const
Definition: qcustomplot.h:1153
void setWhiskerBarPen(const QPen &pen)
double bufferDevicePixelRatio() const
Definition: qcustomplot.h:3594
int clearPlottables()
WidthType mWidthType
Definition: qcustomplot.h:5892
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)
void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true)
Qt::Orientations mRangeDrag
Definition: qcustomplot.h:4635
A plus shaped crosshair which spans the complete axis rect.
Definition: qcustomplot.h:6499
virtual void updateLayout()
static const double maxRange
Definition: qcustomplot.h:806
void setPeriodic(bool enabled)
void setLevelCount(int n)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QRgb color(double position, const QCPRange &range, bool logarithmic=false)
void setTimeFormat(const QString &format)
void setInsetPlacement(int index, InsetPlacement placement)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
friend class QCPGraph
Definition: qcustomplot.h:3805
Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
bool isInvalidData(double value)
Definition: qcustomplot.h:301
QPen mainPen() const
void clear()
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE
virtual void updateMapImage()
QSharedPointer< QCPDataContainer< QCPGraphData > > mDataContainer
Definition: qcustomplot.h:3860
virtual int elementCount() const Q_DECL_OVERRIDE
void setTickOrigin(double origin)
void setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties=QCPScatterStyle::spPen)
QCPColorGradient gradient() const
Definition: qcustomplot.h:5027
QCPAxis * rangeZoomAxis(Qt::Orientation orientation)
void setLowerEnding(const QCPLineEnding &ending)
0x08 bottom margin
Definition: qcustomplot.h:196
virtual ~QCPAxisRect()
Open-High-Low-Close bar representation.
Definition: qcustomplot.h:5844
An ellipse is drawn. The size of the ellipse is given by the bracket width/height properties...
Definition: qcustomplot.h:4492
bool clipToAxisRect() const
Definition: qcustomplot.h:3488
bool registerGraph(QCPGraph *graph)
QCPAxisRect * axisRectAt(const QPointF &pos) const
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
QCPItemPosition *const end
Definition: qcustomplot.h:6170
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint)
void plottableDoubleClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)
void updateAxesOffset(QCPAxis::AxisType type)
QCPVector2D & operator+=(const QCPVector2D &vector)
QCPColorMapData * mMapData
Definition: qcustomplot.h:5754
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
virtual void draw(QCPPainter *painter)
int plottableCount() const
QCPLayout * mParentLayout
Definition: qcustomplot.h:1236
virtual int calculateAutoMargin(QCP::MarginSide side) Q_DECL_OVERRIDE
double cell(int keyIndex, int valueIndex)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
QCPAxis * mParentAxis
Definition: qcustomplot.h:1875
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
void dataRangeChanged(const QCPRange &newRange)
void setMinimumMargins(const QMargins &margins)
virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection)
The base class tick generator used by QCPAxis to create tick positions and tick labels.
Definition: qcustomplot.h:1511
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
A filled square.
Definition: qcustomplot.h:1467
QFont mLabelFont
Definition: qcustomplot.h:2117
QCPAxis(QCPAxisRect *parent, AxisType type)
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
PainterModes modes() const
Definition: qcustomplot.h:475
QCPRange dataRange() const
Definition: qcustomplot.h:5025
QPoint toPoint() const
Definition: qcustomplot.h:400
void setBrush(const QBrush &brush)
void applyPen(QCPPainter *painter) const
Fractions are written using sub- and superscript UTF-8 digits and the fraction symbol.
Definition: qcustomplot.h:1757
virtual void reallocateBuffer()=0
void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true)
void setTickOrigin(double origin)
void setKeySize(int keySize)
The abstract base class for paint buffers, which define the rendering backend.
Definition: qcustomplot.h:512
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual QCPLayoutElement * elementAt(int index) const Q_DECL_OVERRIDE
virtual ~QCPColorScale()
void remove(QCPBars *bars)
QPen mSubGridPen
Definition: qcustomplot.h:1872
QCPGrid * mGrid
Definition: qcustomplot.h:2141
int padding() const
Definition: qcustomplot.h:2013
QCP::AntialiasedElements antialiasedElements() const
Definition: qcustomplot.h:3599
QCPDataSelection selection() const
Definition: qcustomplot.h:3285
void setMaximumSize(const QSize &size)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the ...
Definition: qcustomplot.h:4406
void setDevicePixelRatio(double ratio)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QString text() const
Definition: qcustomplot.h:4913
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Holds the data of one single data point for QCPCurve.
Definition: qcustomplot.h:5226
void mouseMove(QMouseEvent *event)
QCPRange keyRange() const
Definition: qcustomplot.h:5657
No line is drawn between data points (e.g. only scatters)
Definition: qcustomplot.h:5272
QCustomPlot(QWidget *parent=0)
QList< QCPDataRange > dataRanges() const
Definition: qcustomplot.h:949
void setParentLayerable(QCPLayerable *parentLayerable)
QCPItemBracket(QCustomPlot *parentPlot)
QCPLineEnding tail() const
Definition: qcustomplot.h:6156
void rescaleKeyAxis(bool onlyEnlarge=false) const
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
bool mNumberBeautifulPowers
Definition: qcustomplot.h:2127
QCPItemAnchor *const center
Definition: qcustomplot.h:6384
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
A legend item representing a plottable with an icon and the plottable name.
Definition: qcustomplot.h:4739
QHash< QCP::MarginSide, QCPMarginGroup * > mMarginGroups
Definition: qcustomplot.h:1241
Bar spacing is given by a fraction of the axis rect size.
Definition: qcustomplot.h:5356
bool antialiasing() const
Definition: qcustomplot.h:474
virtual double dataMainValue(int index) const =0
void setType(QCPAxis::AxisType type)
void setIconTextPadding(int padding)
each data point is represented by a line parallel to the value axis, which reaches from the data poin...
Definition: qcustomplot.h:5141
QCPItemPosition *const start
Definition: qcustomplot.h:6167
void selectionChanged(bool selected)
int numberPrecision() const
Definition: qcustomplot.h:1998
virtual bool sortKeyIsMainKey() const
virtual QList< QCPLayoutElement * > elements(bool recursive) const
void expandTo(int newRowCount, int newColumnCount)
SelectableParts selectedParts() const
virtual void reallocateBuffer() Q_DECL_OVERRIDE
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lines) const
QByteArray mLabelParameterHash
Definition: qcustomplot.h:2239
0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fix...
Definition: qcustomplot.h:462
void applyBrush(QCPPainter *painter) const
QBrush mSelectedBrush
Definition: qcustomplot.h:6541
void setMedianPen(const QPen &pen)
QCPRange bounded(double lowerBound, double upperBound) const
QList< QCPAbstractPlottable * > selectedPlottables() const
QBrush mBackgroundBrush
Definition: qcustomplot.h:4629
void selectionChanged(bool selected)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
Q_SLOT bool setLayer(QCPLayer *layer)
QList< QCPGraph * > graphs() const
QCPAbstractPlottable * plottable()
Definition: qcustomplot.h:4746
bool contains(double value) const
Definition: qcustomplot.h:801
bool registerPlottable(QCPAbstractPlottable *plottable)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
The axis backbone and tick marks.
Definition: qcustomplot.h:1972
void setTickStepStrategy(TickStepStrategy strategy)
Fractions are displayed as regular decimal floating point numbers, e.g. "0.25" or "0...
Definition: qcustomplot.h:1755
double realLength() const
bool scaled() const
Definition: qcustomplot.h:6425
virtual ~QCPGraph()
QCPLineEnding lowerEnding
Definition: qcustomplot.h:2203
static double dateTimeToKey(const QDateTime dateTime)
The errors are for the key dimension (bars appear parallel to the key axis)
Definition: qcustomplot.h:5968
EndingStyle style() const
Definition: qcustomplot.h:1479
Bar spacing is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:5357
QCPItemAnchor * anchor(const QString &name) const
void drawGridLines(QCPPainter *painter) const
void setSpacingType(SpacingType spacingType)
SelectionType
Definition: qcustomplot.h:288
void setSelectedTickLabelFont(const QFont &font)
void setOffset(int offset)
QColor mSelectedTickLabelColor
Definition: qcustomplot.h:2124
virtual void wheelEvent(QWheelEvent *event)
void setBasePen(const QPen &pen)
bool hasPlottable(QCPAbstractPlottable *plottable) const
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
virtual ~QCPItemText()
void addData(const QVector< double > &t, const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
void setVisible(bool visible)
The tracer is not visible.
Definition: qcustomplot.h:6497
Controls how a plottable&#39;s data selection is drawn.
Definition: qcustomplot.h:3209
void simplifyFraction(int &numerator, int &denominator) const
QString text() const
Definition: qcustomplot.h:6280
int end() const
Definition: qcustomplot.h:898
void setSelectedBorderPen(const QPen &pen)
void zoom(const QRectF &pixelRect)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
bool isActive() const
Definition: qcustomplot.h:1103
int bottom() const
Definition: qcustomplot.h:4613
void setErrorType(ErrorType type)
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
void setPixmap(const QPixmap &pixmap)
QColor getTickLabelColor() const
virtual void updateLayout() Q_DECL_OVERRIDE
QCPItemPosition *const topLeft
Definition: qcustomplot.h:6218
SelectableParts mSelectedParts
Definition: qcustomplot.h:2111
QList< QCPLayerable * > layerableListAt(const QPointF &pos, bool onlySelectable, QList< QVariant > *selectionDetails=0) const
custom painter operations are performed per scatter (As QPainterPath, see setCustomPath) ...
Definition: qcustomplot.h:2305
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
The abstract base class for all entries in a QCPLegend.
Definition: qcustomplot.h:4675
0x08 The shape property, see setShape
Definition: qcustomplot.h:2274
void getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
QFont mSelectedTickLabelFont
Definition: qcustomplot.h:2123
QColor mColor
Definition: qcustomplot.h:6318
void setHead(const QCPLineEnding &head)
double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
bool remove(QCPLayoutElement *element)
{ssPeace.png} a circle, with one vertical and two downward diagonal lines
Definition: qcustomplot.h:2303
QBrush mBrush
Definition: qcustomplot.h:6232
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
bool twoColored() const
Definition: qcustomplot.h:5857
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Queues the entire replot for the next event loop iteration. This way multiple redundant replots can b...
Definition: qcustomplot.h:3585
Tick labels (numbers) of this axis (as a whole, not individually)
Definition: qcustomplot.h:1973
bool mAntialiasedZeroLine
Definition: qcustomplot.h:1871
void setStyle(BracketStyle style)
virtual void draw(QCPPainter *painter)
void setSubTickCount(int subTicks)
void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
QString fractionToString(int numerator, int denominator) const
An integer multiple of the specified tick step is allowed. The used factor follows the base class pro...
Definition: qcustomplot.h:1676
QColor getTextColor() const
void getVisibleDataBounds(QCPBarsDataContainer::const_iterator &begin, QCPBarsDataContainer::const_iterator &end) const
QColor mSelectedTextColor
Definition: qcustomplot.h:4951
double dot(const QCPVector2D &vec) const
Definition: qcustomplot.h:407
void addChild(QCPLayerable *layerable, bool prepend)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setPenPositive(const QPen &pen)
QCPAxisRect * mAxisRect
Definition: qcustomplot.h:2107
static AxisType marginSideToAxisType(QCP::MarginSide side)
0x0001 Axis base line and tick marks
Definition: qcustomplot.h:211
QCPLineEnding mHead
Definition: qcustomplot.h:6123
QCPItemAnchor * createAnchor(const QString &name, int anchorId)
Q_SLOT void setDataRange(const QCPRange &dataRange)
void setScatterSkip(int skip)
void setName(const QString &name)
void normalize()
QCPAxis::AxisType mType
Definition: qcustomplot.h:5057
QPainterPath customPath() const
Definition: qcustomplot.h:2323
void setRowStretchFactors(const QList< double > &factors)
{ssSquare.png} a square
Definition: qcustomplot.h:2294
void setRangeReversed(bool reversed)
QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
QCP::SelectionType selectable() const
Definition: qcustomplot.h:3283
QPointF upperFillBasePoint(double upperKey) const
void setStackingGap(double pixels)
void setSelectedBasePen(const QPen &pen)
0x0008 Legend box
Definition: qcustomplot.h:214
double getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const
Responsible for drawing the grid of a QCPAxis.
Definition: qcustomplot.h:1838
Qt::Alignment positionAlignment() const
Definition: qcustomplot.h:6281
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes) ...
Definition: qcustomplot.h:248
QCPItemAnchor * parentAnchor() const
Definition: qcustomplot.h:3432
void setTangentToData(bool enabled)
0x00 no margin
Definition: qcustomplot.h:198
QVector< QString > tickLabels
Definition: qcustomplot.h:2224
void getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector< QPointF > &beforeTraverse, QVector< QPointF > &afterTraverse) const
QImage mMapImage
Definition: qcustomplot.h:5761
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
void setWhiskerWidth(double width)
void setPen(const QPen &pen)
void changed(const QRect &rect, QMouseEvent *event)
QCPLayer * mLayer
Definition: qcustomplot.h:730
void setTextAlignment(Qt::Alignment alignment)
bool visible() const
Definition: qcustomplot.h:704
void gradientChanged(const QCPColorGradient &newGradient)
const QCPRange range() const
Definition: qcustomplot.h:1987
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
virtual ~QCPItemLine()
void dataRangeChanged(const QCPRange &newRange)
width is in key coordinates and thus scales with the key axis range
Definition: qcustomplot.h:5835
QCPColorGradient mGradient
Definition: qcustomplot.h:5060
void setTicker(QSharedPointer< QCPAxisTicker > ticker)
int itemCount() const
0x04 top margin
Definition: qcustomplot.h:195
QList< QCPGraph * > graphs() const
Defines an abstract interface for one-dimensional plottables.
Definition: qcustomplot.h:3817
void setSelectedSubTickPen(const QPen &pen)
void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
virtual ~QCPAxis()
int dataPointCount() const
QFont font() const
Definition: qcustomplot.h:4915
ErrorType mErrorType
Definition: qcustomplot.h:6017
QSharedPointer< QCPStatisticalBoxDataContainer > data() const
Definition: qcustomplot.h:5584
The selection rect is disabled, and all mouse events are forwarded to the underlying objects...
Definition: qcustomplot.h:264
QCPColorMapData * data() const
Definition: qcustomplot.h:5719
double size() const
Definition: qcustomplot.h:2318
bool noAntialiasingOnDrag() const
Definition: qcustomplot.h:3604
QRect axisSelectionBox() const
Definition: qcustomplot.h:2196
Provides rect/rubber-band data selection and range zoom interaction.
Definition: qcustomplot.h:1091
void setCustomPath(const QPainterPath &customPath)
void getCurveLines(QVector< QPointF > *lines, const QCPDataRange &dataRange, double penWidth) const
virtual ~QCPColorMap()
Linear scaling.
Definition: qcustomplot.h:1963
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Fractions are written as rationals using ASCII characters only, e.g. "1/4" or "1/8".
Definition: qcustomplot.h:1756
QColor color() const
Definition: qcustomplot.h:6272
double rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const
QList< QCPItemPosition * > mPositions
Definition: qcustomplot.h:3517
virtual void drawBracket(QCPPainter *painter, int direction) const
void setShape(ScatterShape shape)
QCP::PlottingHints plottingHints() const
Definition: qcustomplot.h:3605
QCustomPlot * mParentPlot
Definition: qcustomplot.h:3382
void makeNonCosmetic()
QCPLayer * layer() const
Definition: qcustomplot.h:707
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setBackgroundScaled(bool scaled)
voidpf uLong int origin
Definition: ioapi.h:42
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white.
Definition: qcustomplot.h:4421
void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
void setTickLabels(bool show)
An anchor of an item to which positions can be attached to.
Definition: qcustomplot.h:3366
bool addToLegend(QCPLegend *legend)
ScaleStrategy mScaleStrategy
Definition: qcustomplot.h:1694
void setSelectedPen(const QPen &pen)
Qt::Orientations mRangeZoom
Definition: qcustomplot.h:4635
void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void enforceType(QCP::SelectionType type)
void setBrush(const QBrush &brush)
void setData(QSharedPointer< QCPErrorBarsDataContainer > data)
void setWrap(int count)
int mSelectionTolerance
Definition: qcustomplot.h:3735
QBrush brush() const
Definition: qcustomplot.h:1102
void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
QBrush mainBrush() const
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:3241
virtual QList< QCPLayoutElement * > elements(bool recursive) const Q_DECL_OVERRIDE
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
QCPAxis * keyAxis() const
Definition: qcustomplot.h:3438
Phase in which the margins are calculated and set.
Definition: qcustomplot.h:1194
QPen mainPen() const
Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts)
int labelPadding() const
void setSelectedIconBorderPen(const QPen &pen)
Static positioning in pixels, starting from the top left corner of the viewport/widget.
Definition: qcustomplot.h:3414
void setAntialiasedScatters(bool enabled)
double width() const
Definition: qcustomplot.h:5585
void setRangeZoom(Qt::Orientations orientations)
virtual QRect clipRect() const Q_DECL_OVERRIDE
SelectionRectMode
Definition: qcustomplot.h:264
QCPItemRect(QCustomPlot *parentPlot)
QCPItemAnchor * mParentAnchorX
Definition: qcustomplot.h:3462
void indexToRowCol(int index, int &row, int &column) const
0x0004 Sub grid lines
Definition: qcustomplot.h:213
QCache< QString, CachedLabel > mLabelCache
Definition: qcustomplot.h:2240
void mouseRelease(QMouseEvent *event)
A brace with angled edges.
Definition: qcustomplot.h:6579
void setSelectedPen(const QPen &pen)
QCPItemPosition *const right
Definition: qcustomplot.h:6605
void setWhiskerPen(const QPen &pen)
void setAdaptiveSampling(bool enabled)
int findIndexAboveY(const QVector< QPointF > *data, double y) const
void getScatters(QVector< QPointF > *scatters, const QCPDataRange &dataRange, double scatterWidth) const
QVector< double > subTickPositions
Definition: qcustomplot.h:2222
void rangeChanged(const QCPRange &newRange)
void setSelectionDecorator(QCPSelectionDecorator *decorator)
virtual QList< QCPLayoutElement * > elements(bool recursive) const Q_DECL_OVERRIDE
A plus shaped crosshair with limited size.
Definition: qcustomplot.h:6498
void rescaleDataRange(bool onlyVisibleMaps)
void mouseDoubleClick(QMouseEvent *event)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const
void setupFullAxesBox(bool connectRanges=false)
virtual ~QCustomPlot()
void setText(const QString &text)
QCPItemLine(QCustomPlot *parentPlot)
void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
Q_SLOT void setSelectableParts(const SelectableParts &selectableParts)
QCPLineEnding mTail
Definition: qcustomplot.h:6175
QCPAxis * addAxis(QCPAxis::AxisType type, QCPAxis *axis=0)
double tickLabelRotation() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
0x0002 Grid lines
Definition: qcustomplot.h:212
QColor mainColor() const
QCPLineEnding head() const
Definition: qcustomplot.h:6155
0x01 left margin
Definition: qcustomplot.h:193
void setFromOther(const QCPScatterStyle &other, ScatterProperties properties)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
static bool validRange(double lower, double upper)
Base class for all drawable objects.
Definition: qcustomplot.h:689
QBrush mBrushNegative
Definition: qcustomplot.h:5894
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:3738
void setInterpolating(bool enabled)
virtual int findBegin(double sortKey, bool expandedRange=true) const
void setValueAxis(QCPAxis *axis)
QSharedPointer< QCPAxisTicker > ticker() const
Definition: qcustomplot.h:1989
double distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const
void setBackground(const QPixmap &pm)
double getPixelSpacing(const QCPBars *bars, double keyCoord)
void setPadding(int padding)
QCPAxis * xAxis
Definition: qcustomplot.h:3700
bool removeFromLegend(QCPLegend *legend) const
virtual Q_SLOT void processRectSelection(QRect rect, QMouseEvent *event)
A plottable representing a bar chart in a plot.
Definition: qcustomplot.h:5437
const QCP::Interactions interactions() const
Definition: qcustomplot.h:3602
virtual QCPPlottableInterface1D * interface1D()
Definition: qcustomplot.h:3302
QPen subTickPen() const
Definition: qcustomplot.h:2008
QFont font() const
Definition: qcustomplot.h:6278
bool removeGraph(QCPGraph *graph)
QVector< double > mTickVector
Definition: qcustomplot.h:2144
void setRange(const QCPRange &keyRange, const QCPRange &valueRange)
void setBrush(const QBrush &brush)
0x080 All other objects are selectable (e.g. your own derived layerables, other layout elements...
Definition: qcustomplot.h:255
QList< QCPAbstractPlottable * > plottables() const
virtual void layoutChanged() Q_DECL_OVERRIDE
voidpf void uLong size
Definition: ioapi.h:39
double mStackingGap
Definition: qcustomplot.h:5503
void setCoords(double key, double value)
void addChild(QCP::MarginSide side, QCPLayoutElement *element)
void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false)
SpacingType mSpacingType
Definition: qcustomplot.h:5386
0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap) ...
Definition: qcustomplot.h:218
friend class QCPItemAnchor
Definition: qcustomplot.h:3542
Q_SLOT void setSelectable(bool selectable)
void setWidth(double width)
void loadPreset(GradientPreset preset)
void moveAbove(QCPBars *bars)
void addFillBasePoints(QVector< QPointF > *lines) const
double mSymbolGap
Definition: qcustomplot.h:6019
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setSize(int keySize, int valueSize)
void moveBelow(QCPBars *bars)
QSharedPointer< QCPFinancialDataContainer > data() const
Definition: qcustomplot.h:5853
void setScatterStyle(const QCPScatterStyle &style)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
virtual void simplify() Q_DECL_OVERRIDE
void setRangeUpper(double upper)
virtual ~QCPMarginGroup()
void setModes(PainterModes modes)
virtual QPointF pixelPosition() const
void setData(QSharedPointer< QCPBarsDataContainer > data)
QCPItemTracer(QCustomPlot *parentPlot)
QPen mSubTickPen
Definition: qcustomplot.h:2134
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
void recalculateDataBounds()
double getPixelWidth(double key, double keyPixel) const
int valueSize() const
Definition: qcustomplot.h:5656
bool invalidated() const
Definition: qcustomplot.h:520
bool contains(const QCPDataSelection &other) const
QPoint mDragStart
Definition: qcustomplot.h:4643
virtual QRect clipRect() const
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
bool mScaledPixmapInvalidated
Definition: qcustomplot.h:6456
virtual QCP::Interaction selectionCategory() const
void trimTicks(const QCPRange &range, QVector< double > &ticks, bool keepOneOutlier) const
void layerChanged(QCPLayer *newLayer)
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
bool addElement(int row, int column, QCPLayoutElement *element)
QPen pen() const
Definition: qcustomplot.h:6153
void setStyle(EndingStyle style)
QPointer< QCPBars > mBarBelow
Definition: qcustomplot.h:5504
QPixmap mScaledPixmap
Definition: qcustomplot.h:6454
void setValueSize(int valueSize)
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)
virtual ~QCPLayoutElement()
QList< QCPRange > mDragStartHorzRange
Definition: qcustomplot.h:4641
QBrush mBrush
Definition: qcustomplot.h:6320
QList< QCPLayerable * > children() const
Definition: qcustomplot.h:652
void setTickLabelRotation(double degrees)
double spacing() const
Definition: qcustomplot.h:5366
QRect mTextBoundingRect
Definition: qcustomplot.h:4952
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:5307
void setTypeX(PositionType type)
virtual ~QCPItemTracer()
TracerStyle mStyle
Definition: qcustomplot.h:6543
0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixma...
Definition: qcustomplot.h:463
QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
A layout that arranges child elements in a grid.
Definition: qcustomplot.h:1300
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QCPVector2D & operator-=(const QCPVector2D &vector)
void setPen(const QPen &pen)
int pixelOrientation() const
Definition: qcustomplot.h:2079
virtual ~QCPBars()
void setFont(const QFont &font)
int levelCount() const
Definition: qcustomplot.h:4435
void setColor(const QColor &color)
bool registerItem(QCPAbstractItem *item)
virtual int findEnd(double sortKey, bool expandedRange=true) const
void selectionChangedByUser()
virtual bool take(QCPLayoutElement *element) Q_DECL_OVERRIDE
QMargins margins() const
Definition: qcustomplot.h:1206
0x08 Axis is horizontal and on the bottom side of the axis rect
Definition: qcustomplot.h:1945
QPointer< QCPAbstractPlottable > mDataPlottable
Definition: qcustomplot.h:6016
QPen mBasePen
Definition: qcustomplot.h:2112
double mBufferDevicePixelRatio
Definition: qcustomplot.h:3726
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
QPointer< QCPBars > mBarAbove
Definition: qcustomplot.h:5504
QCPScatterStyle mOutlierStyle
Definition: qcustomplot.h:5621
bool rangeDrag() const
void setUpperEnding(const QCPLineEnding &ending)
void setPixmap(const QPixmap &pixmap)
void setWidth(double width)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
double mSpacing
Definition: qcustomplot.h:5387
Multiple contiguous data points (a data range) can be selected.
Definition: qcustomplot.h:291
QCPItemText(QCustomPlot *parentPlot)
virtual bool registerWithPlottable(QCPAbstractPlottable *plottable)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties)
QList< QCPLayer * > mLayers
Definition: qcustomplot.h:3732
virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE
QBrush mainBrush() const
void setRangeDrag(Qt::Orientations orientations)
void setTangentAverage(int pointCount)
QCP::AntialiasedElements mNotAntialiasedElements
Definition: qcustomplot.h:3733
virtual void generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector< double > &ticks, QVector< double > *subTicks, QVector< QString > *tickLabels)
TimeUnit mSmallestUnit
Definition: qcustomplot.h:1646
double mTickOrigin
Definition: qcustomplot.h:1547
virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE
QCPItemAnchor *const bottom
Definition: qcustomplot.h:6310
void setSize(const QSize &size)
QCPDataRange expanded(const QCPDataRange &other) const
double y() const
Definition: qcustomplot.h:389
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
0x0040 Main lines of items
Definition: qcustomplot.h:217
void removeChild(QCPLayerable *layerable)
QCPAxisRect * axisRect() const
QImage mUndersampledMapImage
Definition: qcustomplot.h:5761
Represents two doubles as a mathematical 2D vector.
Definition: qcustomplot.h:379
QCPVector2D perpendicular() const
Definition: qcustomplot.h:406
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:5088
QCPDataSelection & operator-=(const QCPDataSelection &other)
no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines) ...
Definition: qcustomplot.h:2288
TimeUnit mBiggestUnit
Definition: qcustomplot.h:1646
bool mMouseHasMoved
Definition: qcustomplot.h:3752
bool begin(QPaintDevice *device)
void setSelectedTextColor(const QColor &color)
virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE
virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
double value() const
Definition: qcustomplot.h:3436
virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false)
double getMantissa(double input, double *magnitude=0) const
QCPLineEnding upperEnding() const
The abstract base class for all data representing objects in a plot.
Definition: qcustomplot.h:3256
QCP::MarginSides mAutoMargins
Definition: qcustomplot.h:1240
FractionStyle mFractionStyle
Definition: qcustomplot.h:1780
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void selectionChanged(bool selected)
void setColorScale(QCPColorScale *colorScale)
void fill(double z)
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)
EndingStyle mStyle
Definition: qcustomplot.h:1498
QCPScatterStyle outlierStyle() const
Definition: qcustomplot.h:5591
QCPColorGradient inverted() const
Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion ...
Definition: qcustomplot.h:4424
QRectF insetRect(int index) const
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void getScatters(QVector< QPointF > *scatters, const QCPDataRange &dataRange) const
QCPRange sanitizedForLogScale() const
void setSelectedBrush(const QBrush &brush)
void setBackground(const QPixmap &pm)
virtual QByteArray generateLabelParameterHash() const
QPointF coords() const
Definition: qcustomplot.h:3437
void releaseElement(QCPLayoutElement *el)
ExportPen
Definition: qcustomplot.h:172
void removeChildX(QCPItemPosition *pos)
virtual int dataCount() const
QCPItemAnchor *const left
Definition: qcustomplot.h:6383
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:3330
void setSubTickLengthOut(int outside)
bool removeAt(int index)
double mGraphKey
Definition: qcustomplot.h:6545
virtual QSize sizeHint() const Q_DECL_OVERRIDE
void setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
double mWhiskerWidth
Definition: qcustomplot.h:6018
bool subTicks() const
Definition: qcustomplot.h:2003
void setDateTimeFormat(const QString &format)
QList< QCPBars * > bars() const
Definition: qcustomplot.h:5373
QCPDataRange adjusted(int changeBegin, int changeEnd) const
Definition: qcustomplot.h:912
QList< QPointer< QCPAxis > > mRangeDragVertAxis
Definition: qcustomplot.h:4636
virtual void drawImpulsePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
void setTwoColored(bool twoColored)
QPointF lowerFillBasePoint(double lowerKey) const
virtual ~QCPCurve()
QVector< QPointF > dataToStepRightLines(const QVector< QCPGraphData > &data) const
virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE
bool moveToLayer(QCPLayer *layer, bool prepend)
QPointer< QCPAxis > mColorAxis
Definition: qcustomplot.h:5065
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable) ...
Definition: qcustomplot.h:251
bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
virtual ~QCPLayer()
void setSubGridPen(const QPen &pen)
virtual QSize minimumSizeHint() const
QList< QCPAbstractLegendItem * > selectedItems() const
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
void setAntialiasedSubGrid(bool enabled)
A filled circle.
Definition: qcustomplot.h:1466
Q_SLOT void setSelectable(QCP::SelectionType selectable)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
virtual ~QCPItemStraightLine()
Tick labels will be displayed outside the axis rect.
Definition: qcustomplot.h:1956
QCPItemAnchor * parentAnchorY() const
Definition: qcustomplot.h:3434
Continuous lightness from black over icey colors to white (suited for non-biased data representation)...
Definition: qcustomplot.h:4416
void scaleRange(double factor)
line is drawn as steps where the step height is the value of the left data point
Definition: qcustomplot.h:5138
void selectionChanged(const QCPAxis::SelectableParts &parts)
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
void plottableClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)
virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const
QCPItemPosition *const endDir
Definition: qcustomplot.h:6169
QPen getBasePen() const
Columns are filled first, and a new element is wrapped to the next row if the column count would exce...
Definition: qcustomplot.h:1323
QCPVector2D normalized() const
0x02 Axis is vertical and on the right side of the axis rect
Definition: qcustomplot.h:1943
An approximation of the visible light spectrum (creates banding illusion but allows more precise magn...
Definition: qcustomplot.h:4423
void setBackgroundScaled(bool scaled)
void setTickLabelFont(const QFont &font)
SignDomain
Definition: qcustomplot.h:183
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:6375
QList< QPointer< QCPAxis > > mRangeDragHorzAxis
Definition: qcustomplot.h:4636
QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange)
double width() const
Definition: qcustomplot.h:1480
void setFieldWidth(TimeUnit unit, int width)
bool mInterpolate
Definition: qcustomplot.h:5756
int subTickLengthIn() const
QCPColorGradient mGradient
Definition: qcustomplot.h:5755
void setTextColor(const QColor &color)
virtual ~QCPItemEllipse()
QList< QPointer< QCPAxis > > mRangeZoomVertAxis
Definition: qcustomplot.h:4637
Bar width is in absolute pixels.
Definition: qcustomplot.h:5456
int tickLengthOut() const
Qt::AspectRatioMode aspectRatioMode() const
Definition: qcustomplot.h:6426
void setSelectionRectMode(QCP::SelectionRectMode mode)
QList< QCPAxis * > axes() const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:665
bool removeLayer(QCPLayer *layer)
void setSelectedLabelFont(const QFont &font)
virtual int size() const
bool getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const
void setPiValue(double pi)
void getPixelWidth(double key, double &lower, double &upper) const
QCPColorScale * colorScale() const
Definition: qcustomplot.h:5725
A plottable representing a graph in a plot.
Definition: qcustomplot.h:5119
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:3244
void applyFillAntialiasingHint(QCPPainter *painter) const
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
QCPItemPosition *const start
Definition: qcustomplot.h:6117
int iconTextPadding() const
Definition: qcustomplot.h:4805
The abstract base class for all items in a plot.
Definition: qcustomplot.h:3474
QCPAbstractLegendItem(QCPLegend *parent)
QString name() const
Definition: qcustomplot.h:650
QPen mainPen() const
QCPLayoutInset * mInsetLayout
Definition: qcustomplot.h:4634
void setData(double key, double value, double z)
virtual QCPLayoutElement * takeAt(int index) Q_DECL_OVERRIDE
double length() const
Definition: qcustomplot.h:6592
QVector< QPointF > dataToStepCenterLines(const QVector< QCPGraphData > &data) const
Q_SLOT void setSelectable(bool selectable)
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
Q_SLOT void setGradient(const QCPColorGradient &gradient)
0x004 axis (tick) labels will be cached as pixmaps, increasing replot performance.
Definition: qcustomplot.h:237
QRect tickLabelsSelectionBox() const
Definition: qcustomplot.h:2197
void setPadding(const QMargins &padding)
Logarithmic scaling with correspondingly transformed axis coordinates (possibly also setTicker to a Q...
Definition: qcustomplot.h:1964
Modifications are not allowed, the specified tick step is absolutely fixed. This might cause a high t...
Definition: qcustomplot.h:1675
QCPLegend * mParentLegend
Definition: qcustomplot.h:4716
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:3330
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const =0
QBrush brush() const
Definition: qcustomplot.h:6511
QVector< int > getSectionSizes(QVector< int > maxSizes, QVector< int > minSizes, QVector< double > stretchFactors, int totalSize) const
virtual QSize maximumSizeHint() const
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
void setRangeLower(double lower)
void setBrush(const QBrush &brush)
QCPItemCurve(QCustomPlot *parentPlot)
QCPLineEnding mHead
Definition: qcustomplot.h:6175
void setTickCount(int count)
QFont mSelectedLabelFont
Definition: qcustomplot.h:2117
QCPColorScale(QCustomPlot *parentPlot)
virtual void moveSelection(QMouseEvent *event)
QCPItemAnchor *const right
Definition: qcustomplot.h:6222
bool mNoAntialiasingOnDrag
Definition: qcustomplot.h:3736
QCPAbstractPaintBuffer * createPaintBuffer()
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:3551
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
virtual ~QCPPaintBufferPixmap()
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
void setAntialiasing(bool enabled)
void setPen(const QPen &pen)
0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see QCPAbstractItem) ...
Definition: qcustomplot.h:254
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
int mScatterSkip
Definition: qcustomplot.h:5308
Candlestick representation.
Definition: qcustomplot.h:5845
void setWidth(double width)
bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
int rowColToIndex(int row, int column) const
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
bool selectable() const
Definition: qcustomplot.h:3490
void setBrushNegative(const QBrush &brush)
TickStepStrategy mTickStepStrategy
Definition: qcustomplot.h:1545
Manages a single axis inside a QCustomPlot.
Definition: qcustomplot.h:1889
QCPLineEnding tail() const
Definition: qcustomplot.h:6106
bool isEmpty() const
Definition: qcustomplot.h:955
QCPAxis::AxisType type() const
Definition: qcustomplot.h:5024
bool mSubTicks
Definition: qcustomplot.h:2131
QCPBarsGroup * barsGroup() const
Definition: qcustomplot.h:5468
virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
virtual ~QCPLegend()
bool isPenDefined() const
Definition: qcustomplot.h:2336
bool setupOpenGl()
void drawLine(const QLineF &line)
QList< QCPAbstractItem * > selectedItems() const
QBrush brush() const
Definition: qcustomplot.h:2321
double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allo...
Definition: qcustomplot.h:4420
double width() const
Definition: qcustomplot.h:5466
bool mIsAntialiasing
Definition: qcustomplot.h:498
void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
virtual int commonMargin(QCP::MarginSide side) const
void setTickPen(const QPen &pen)
Replots immediately, but queues the widget repaint, by calling QWidget::update() after the replot...
Definition: qcustomplot.h:3583
void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
QHash< TimeUnit, QString > mFormatPattern
Definition: qcustomplot.h:1647
QCPColorScale * mParentColorScale
Definition: qcustomplot.h:4987
void setPen(const QPen &pen)
void setColorStops(const QMap< double, QColor > &colorStops)
QCPLayer * layer(const QString &name) const
QPen pen() const
Definition: qcustomplot.h:6103
A filled arrow head with a straight/flat back (a triangle)
Definition: qcustomplot.h:1463
PositionType type() const
Definition: qcustomplot.h:3429
virtual bool take(QCPLayoutElement *element) Q_DECL_OVERRIDE
int findIndexAboveX(const QVector< QPointF > *data, double x) const
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:2258
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
{ssCircle.png} a circle
Definition: qcustomplot.h:2292
int index() const
Definition: qcustomplot.h:651
virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const
void setOutlierStyle(const QCPScatterStyle &style)
bool isEmpty() const
Definition: qcustomplot.h:908
Manages a legend inside a QCustomPlot.
Definition: qcustomplot.h:4763
Layer is used only for rendering order, and shares paint buffer with all other adjacent logical layer...
Definition: qcustomplot.h:640
QList< QCPLayerable * > mChildren
Definition: qcustomplot.h:668
QSet< QCPItemPosition * > mChildrenY
Definition: qcustomplot.h:3385
void insert(int i, QCPBars *bars)
void setSelectionRect(QCPSelectionRect *selectionRect)
QCPLineEnding lowerEnding() const
QSharedPointer< QCPGraphDataContainer > data() const
Definition: qcustomplot.h:5149
void setCell(int keyIndex, int valueIndex, double z)
QCPRange mDataRange
Definition: qcustomplot.h:5752
LayerMode mMode
Definition: qcustomplot.h:670
virtual void getOptimizedLineData(QVector< QCPGraphData > *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
PositionType mPositionTypeY
Definition: qcustomplot.h:3458
QCPLayerable * parentLayerable() const
Definition: qcustomplot.h:706
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
QPointer< QCPColorScaleAxisRectPrivate > mAxisRect
Definition: qcustomplot.h:5064
void setInsetRect(int index, const QRectF &rect)
QCPSelectionRect * mSelectionRect
Definition: qcustomplot.h:3746
line is drawn as steps where the step is in between two data points
Definition: qcustomplot.h:5140
Cosmetic pens are converted to pens with pixel width 1 when exporting.
Definition: qcustomplot.h:172
virtual bool take(QCPLayoutElement *element)=0
void setTextColor(const QColor &color)
int columnCount() const
Definition: qcustomplot.h:1332
void setData(QSharedPointer< QCPStatisticalBoxDataContainer > data)
virtual void keyPressEvent(QKeyEvent *event)
void selectableChanged(bool selectable)
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:4633
When dragging the mouse, a selection rect becomes active. Upon releasing, the axes that are currently...
Definition: qcustomplot.h:265
void setBrush(const QBrush &brush)
void setSelectedFont(const QFont &font)
0x0100 Borders of fills (e.g. under or between graphs)
Definition: qcustomplot.h:219
0x004 The user can select multiple objects by holding the modifier set by QCustomPlot::setMultiSelect...
Definition: qcustomplot.h:250
void addTick(double position, QString label)
virtual QPointF pixelPosition() const
QCPAbstractPlottable * plottable()
virtual QSize maximumSizeHint() const Q_DECL_OVERRIDE
QString label() const
QCPItemAnchor *const bottomRight
Definition: qcustomplot.h:6309
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
void setTicks(const QMap< double, QString > &ticks)
virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE
virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE
virtual ~QCPItemPixmap()
void setSpacing(double spacing)
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:3459
int begin() const
Definition: qcustomplot.h:897
Bar width is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:5458
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setRowSpacing(int pixels)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const =0
The plottable is not selectable.
Definition: qcustomplot.h:288
QCPRange valueRange() const
Definition: qcustomplot.h:5658
QCPItemPosition * createPosition(const QString &name)
void setMinimumSize(const QSize &size)
QVector< double > mSubTickVector
Definition: qcustomplot.h:2146
void setWidthType(WidthType widthType)
QPen tickPen() const
Definition: qcustomplot.h:2007
QList< QCPBars * > mBars
Definition: qcustomplot.h:5388
int itemCount() const
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
QPen getSubTickPen() const
bool isEmpty() const
Definition: qcustomplot.h:5681
QCPDataRange dataRange(int index=0) const
QPen pen() const
Definition: qcustomplot.h:6274
LayerMode mode() const
Definition: qcustomplot.h:654
virtual ~QCPAbstractPaintBuffer()
QCPGraph * graph() const
QCPDataRange span() const
void setKeyAxis(QCPAxis *axis)
bool hasInvalidatedPaintBuffers()
QPointer< QCPLayerable > mMouseEventLayerable
Definition: qcustomplot.h:3753
virtual ~QCPItemCurve()
virtual ~QCPSelectionRect()
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
double width() const
Definition: qcustomplot.h:5855
int height() const
Definition: qcustomplot.h:4615
void fillAlpha(unsigned char alpha)
int dataRangeCount() const
Definition: qcustomplot.h:946
A layer that may contain objects, to control the rendering order.
Definition: qcustomplot.h:620
bool mTickLabels
Definition: qcustomplot.h:2121
Qt::Orientation orientation() const
Definition: qcustomplot.h:2078
Continuous lightness from black over firey colors to white (suited for non-biased data representation...
Definition: qcustomplot.h:4415
void setScaleStrategy(ScaleStrategy strategy)
void setPen(const QPen &pen)
QCPItemPosition *const position
Definition: qcustomplot.h:6536
void rescaleDataRange(bool recalculateDataBounds=false)
virtual ~QCPItemRect()
virtual ~QCPErrorBars()
void setInverted(bool inverted)
LabelSide tickLabelSide() const
void setAutoMargins(QCP::MarginSides sides)
void getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector< QLineF > &backbones, QVector< QLineF > &whiskers) const
virtual void axisRemoved(QCPAxis *axis)
0x00 Default mode for painting on screen devices
Definition: qcustomplot.h:461
Selection behaves like stMultipleDataRanges, but if there are any data points selected, the entire plottable is drawn as selected.
Definition: qcustomplot.h:289
Replots immediately and repaints the widget immediately by calling QWidget::repaint() after the replo...
Definition: qcustomplot.h:3582
QSize size() const
Definition: qcustomplot.h:519
LineStyle mLineStyle
Definition: qcustomplot.h:5309
QFont getLabelFont() const
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
SpacingType spacingType() const
Definition: qcustomplot.h:5365
The abstract base class for all objects that form the layout system.
Definition: qcustomplot.h:1176
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > &points, const QCPScatterStyle &style) const
bool savePdf(const QString &fileName, int width=0, int height=0, QCP::ExportPen exportPen=QCP::epAllowCosmetic, const QString &pdfCreator=QString(), const QString &pdfTitle=QString())
QCPBarsGroup(QCustomPlot *parentPlot)
int getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
PlottingHint
Definition: qcustomplot.h:232
QColor getLabelColor() const
QPen pen() const
Definition: qcustomplot.h:1101
void setGraph(QCPGraph *graph)
void mousePress(QMouseEvent *event)
QCPRange mValueRange
Definition: qcustomplot.h:5688
Continuous lightness from black to white (suited for non-biased data representation) ...
Definition: qcustomplot.h:4414
void draw(QCPPainter *painter)
QCPTextElement(QCustomPlot *parentPlot)
void setFont(const QFont &font)
{ssPlus.png} a plus
Definition: qcustomplot.h:2291
static QDateTime keyToDateTime(double key)
int findIndexBelowX(const QVector< QPointF > *data, double x) const
void getDataSegments(QList< QCPDataRange > &selectedSegments, QList< QCPDataRange > &unselectedSegments) const
double pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const
QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
QCPRange expanded(const QCPRange &otherRange) const
QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio)
virtual QRect clipRect() const Q_DECL_OVERRIDE
Q_SLOT void setGradient(const QCPColorGradient &gradient)
void applyScattersAntialiasingHint(QCPPainter *painter) const
void getVisibleDataBounds(QCPStatisticalBoxDataContainer::const_iterator &begin, QCPStatisticalBoxDataContainer::const_iterator &end) const
QCPAxis * keyAxis() const
Definition: qcustomplot.h:3281
QBrush brush() const
Definition: qcustomplot.h:6276
void setOuterRect(const QRect &rect)
void setPen(const QPen &pen)
bool selected() const
Definition: qcustomplot.h:4920
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
bool mayTraverse(int prevRegion, int currentRegion) const
QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
void setLineStyle(LineStyle ls)
int offset() const
QCPColorGradient gradient() const
Definition: qcustomplot.h:5724
int keySize() const
Definition: qcustomplot.h:5655
void setPen(const QPen &pen)
void updateLayerIndices() const
Blue over pink to white.
Definition: qcustomplot.h:4418
QCPLineEnding upperEnding
Definition: qcustomplot.h:2203
void selectableChanged(bool selectable)
QMap< double, QString > & ticks()
Definition: qcustomplot.h:1713
QString mLabel
Definition: qcustomplot.h:2116
void setTextColor(const QColor &color)
FillOrder mFillOrder
Definition: qcustomplot.h:1379
Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts)
void getMaximumRowColSizes(QVector< int > *maxColWidths, QVector< int > *maxRowHeights) const
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
void insertColumn(int newIndex)
void setColumnSpacing(int pixels)
virtual double dataSortKey(int index) const
QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const
void removeFillBasePoints(QVector< QPointF > *lines) const
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
Qt::AspectRatioMode mAspectRatioMode
Definition: qcustomplot.h:6457
QCPItemStraightLine(QCustomPlot *parentPlot)
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
bool selected() const
Definition: qcustomplot.h:3284
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QBrush mSelectedBrush
Definition: qcustomplot.h:6320
An integer power of the specified tick step is allowed.
Definition: qcustomplot.h:1677
QPen selectedIconBorderPen() const
Definition: qcustomplot.h:4810
virtual void draw(QCPPainter *painter)=0
Describes a data set by holding multiple QCPDataRange instances.
Definition: qcustomplot.h:924
QCPItemAnchor * parentAnchorX() const
Definition: qcustomplot.h:3433
double mRotation
Definition: qcustomplot.h:6325
int findIndexBelowY(const QVector< QPointF > *data, double y) const
QCPLayer * mCurrentLayer
Definition: qcustomplot.h:3742
virtual ~QCPAbstractItem()
QPainterPath mCustomPath
Definition: qcustomplot.h:2349
WidthType mWidthType
Definition: qcustomplot.h:5500
void setPen(const QPen &pen)
bool removeFromLegend() const
QCPBarsGroup * mBarsGroup
Definition: qcustomplot.h:5501
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
QVector< QPointF > getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
virtual int dataCount() const =0
QCPItemAnchor *const left
Definition: qcustomplot.h:6312
virtual ~QCPItemAnchor()
double getStackedBaseValue(double key, bool positive) const
QFont mainFont() const
virtual void updateLayout()
void setTightBoundary(bool enabled)
bool mReplotting
Definition: qcustomplot.h:3755
bool operator==(const QCPColorGradient &other) const
QList< QCPAxis * > addAxes(QCPAxis::AxisTypes types)
QPen mSelectedTickPen
Definition: qcustomplot.h:2133
virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE
{ssCross.png} a cross
Definition: qcustomplot.h:2290
void setWidthType(WidthType widthType)
void setPeriodicity(int multiplesOfPi)
PainterModes mModes
Definition: qcustomplot.h:497
QPen mainPen() const
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
A brace with round edges.
Definition: qcustomplot.h:6580
A layout that places child elements aligned to the border or arbitrarily positioned.
Definition: qcustomplot.h:1391
void setData(QCPColorMapData *data, bool copy=false)
QPen getIconBorderPen() const
void setData(QSharedPointer< QCPGraphDataContainer > data)
void setViewport(const QRect &rect)
QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio)
0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts) ...
Definition: qcustomplot.h:253
void setVisible(bool on)
void setStyle(TracerStyle style)
void setNoAntialiasingOnDrag(bool enabled)
Handles the different ending decorations for line-like items.
Definition: qcustomplot.h:1447
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE=0
A color scale for use with color coding data such as QCPColorMap.
Definition: qcustomplot.h:5005
double length() const
Definition: qcustomplot.h:398
virtual void update(UpdatePhase phase)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
Q_SLOT void setSelectable(bool selectable)
QPen pen() const
Definition: qcustomplot.h:6360
QPen mTickPen
Definition: qcustomplot.h:2133
QCP::PlottingHints mPlottingHints
Definition: qcustomplot.h:3743
virtual QCPPainter * startPainting() Q_DECL_OVERRIDE
BracketStyle mStyle
Definition: qcustomplot.h:6613
QCPAxis * valueAxis() const
Definition: qcustomplot.h:3439
void setDateTimeSpec(Qt::TimeSpec spec)
QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
Rows are filled first, and a new element is wrapped to the next column if the row count would exceed ...
Definition: qcustomplot.h:1322
int mCachedMargin
Definition: qcustomplot.h:2148
bool selectable() const
Definition: qcustomplot.h:4919
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
virtual QCPItemPosition * toQCPItemPosition()
Definition: qcustomplot.h:3388
QPointer< QCPAxisRect > mClipAxisRect
Definition: qcustomplot.h:3516
Q_SLOT void cancel()
QVector< QString > mTickVectorLabels
Definition: qcustomplot.h:2145
int getMarginValue(const QMargins &margins, QCP::MarginSide side)
Definition: qcustomplot.h:342
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const =0
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
void draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Both sign domains, including zero, i.e. all numbers.
Definition: qcustomplot.h:184
virtual QList< QCPLayoutElement * > elements(bool recursive) const Q_DECL_OVERRIDE
bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
QList< QCPAbstractPlottable * > mPlottables
Definition: qcustomplot.h:3729
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:649
friend class QCPAxisRect
Definition: qcustomplot.h:3803
void applyTo(QCPPainter *painter, const QPen &defaultPen) const
QPen pen() const
Definition: qcustomplot.h:6590
QPen pen() const
Definition: qcustomplot.h:3279
QPainter subclass used internally.
Definition: qcustomplot.h:453
void append(QCPBars *bars)
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
Qt::TransformationMode transformationMode() const
Definition: qcustomplot.h:6427
void setTickLength(int inside, int outside=0)
int mScatterSkip
Definition: qcustomplot.h:5178
unsigned char * mAlpha
Definition: qcustomplot.h:5693
ScaleType mScaleType
Definition: qcustomplot.h:2138
bool selectable() const
Definition: qcustomplot.h:4696
Q_SLOT void setSelected(bool selected)
QPixmap mLegendIcon
Definition: qcustomplot.h:5762
QMap< double, QString > mTicks
Definition: qcustomplot.h:1729
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
QSet< QCPItemPosition * > mChildrenX
Definition: qcustomplot.h:3385
WidthType widthType() const
Definition: qcustomplot.h:5467
void canceled(const QRect &rect, QInputEvent *event)
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE
bool operator==(const QCPDataSelection &other) const
QCPItemEllipse(QCustomPlot *parentPlot)
{ssCrossCircle.png} a circle with a cross inside
Definition: qcustomplot.h:2301
void setPlottingHints(const QCP::PlottingHints &hints)
void setMode(LayerMode mode)
void coordsToPixels(double key, double value, double &x, double &y) const
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
void drawBackground(QCPPainter *painter)
double data(double key, double value)
Holds the data of one single data point (one bar) for QCPBars.
Definition: qcustomplot.h:5406
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
int left() const
Definition: qcustomplot.h:4610
bool mCachedMarginValid
Definition: qcustomplot.h:2147
double boundingDistance() const
QCPItemAnchor *const bottom
Definition: qcustomplot.h:6381
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
QPixmap mPixmap
Definition: qcustomplot.h:6453
virtual double getTickStep(const QCPRange &range)
QCPSelectionDecorator * mSelectionDecorator
Definition: qcustomplot.h:3333
QCPGrid(QCPAxis *parentAxis)
QCPScatterStyle::ScatterProperties usedScatterProperties() const
Definition: qcustomplot.h:3220
void setColorInterpolation(ColorInterpolation interpolation)
QColor mSelectedColor
Definition: qcustomplot.h:6318
QPen mSelectedBasePen
Definition: qcustomplot.h:2112
QCPLineEnding head() const
Definition: qcustomplot.h:6105
{ssDot.png} a single pixel (use ssDisc or ssCircle if you want a round shape with a certain radius) ...
Definition: qcustomplot.h:2289
void normalize()
Definition: qcustomplot.h:793
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection)
virtual QCPLayoutElement * elementAt(int index) const Q_DECL_OVERRIDE
Layer has its own paint buffer and may be replotted individually (see replot).
Definition: qcustomplot.h:641
QPen mainPen() const
int mPadding
Definition: qcustomplot.h:2109
void setChartStyle(ChartStyle style)
void setBarsGroup(QCPBarsGroup *barsGroup)
void setPiSymbol(QString symbol)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range)
QCPAxis * yAxis2
Definition: qcustomplot.h:3700
QColor mainTextColor() const
QVector< QRgb > mColorBuffer
Definition: qcustomplot.h:4463
void registerBars(QCPBars *bars)
QCPAxisRect * axisRect() const
Definition: qcustomplot.h:1985
QCPAbstractItem * mParentItem
Definition: qcustomplot.h:3383
{ssPlusSquare.png} a square with a plus inside
Definition: qcustomplot.h:2300
A filled diamond (45 degrees rotated square)
Definition: qcustomplot.h:1468
QSharedPointer< QCPErrorBarsDataContainer > data() const
Definition: qcustomplot.h:5976
void setIconBorderPen(const QPen &pen)
double lengthSquared() const
Definition: qcustomplot.h:399
QCP::SelectionRectMode mSelectionRectMode
Definition: qcustomplot.h:3745
QPointF getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const
void removeChildY(QCPItemPosition *pos)
Colors suitable to represent different elevations on geographical maps.
Definition: qcustomplot.h:4419
int selectionTolerance() const
Definition: qcustomplot.h:3603
void setSelectedFont(const QFont &font)
Qt::TimeSpec mDateTimeSpec
Definition: qcustomplot.h:1595
QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId=-1)
QCPAxis * xAxis2
Definition: qcustomplot.h:3700
QPen mSelectedSubTickPen
Definition: qcustomplot.h:2134
QCPVector2D & operator/=(double divisor)
QBrush mSelectedBrush
Definition: qcustomplot.h:6391
0x01 The pen property, see setPen
Definition: qcustomplot.h:2271
void setNumberPrecision(int precision)
QVector< QLineF > getWhiskerBarLines(QCPStatisticalBoxDataContainer::const_iterator it) const
bool errorBarVisible(int index) const
QRect labelSelectionBox() const
Definition: qcustomplot.h:2198
static const double minRange
Definition: qcustomplot.h:805
QList< QCPAbstractItem * > items() const
void setSelectedPen(const QPen &pen)
QCPLayer * currentLayer() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
void setOpenGl(bool enabled, int multisampling=16)
virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE
void setKeyRange(const QCPRange &keyRange)
int layerCount() const
{ssTriangle.png} an equilateral triangle, standing on baseline
Definition: qcustomplot.h:2297
{ssDisc.png} a circle which is filled with the pen&#39;s color (not the brush as with ssCircle) ...
Definition: qcustomplot.h:2293
QCPGraph * graph() const
Definition: qcustomplot.h:6515
QCPAxis::AxisType type
Definition: qcustomplot.h:2201
Q_SLOT void setSelected(bool selected)
bool isNone() const
Definition: qcustomplot.h:2335
void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
void rescaleAxes(bool onlyEnlarge=false) const
0x0020 Main lines of plottables
Definition: qcustomplot.h:216
friend class QCPLayer
Definition: qcustomplot.h:3802
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const =0
QCPRange mKeyRange
Definition: qcustomplot.h:5688
virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
Interaction
Definition: qcustomplot.h:248
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
static void connectBars(QCPBars *lower, QCPBars *upper)
virtual void drawLinePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
QVector< DataType >::const_iterator const_iterator
Definition: qcustomplot.h:2377
bool createAlpha(bool initializeOpaque=true)
QCPBars * barBelow() const
Definition: qcustomplot.h:5471
bool rangeReversed() const
Definition: qcustomplot.h:1988
void setBrush(const QBrush &brush)
int top() const
Definition: qcustomplot.h:4612
QPen mSelectedPen
Definition: qcustomplot.h:6122
A bar perpendicular to the line.
Definition: qcustomplot.h:1469
Q_SLOT void setSelectedParts(const SelectableParts &selectedParts)
void setRangeDrag(bool enabled)
QBrush mBrushPositive
Definition: qcustomplot.h:5894
QPen pen() const
Definition: qcustomplot.h:6509
virtual ~QCPSelectionDecorator()
data points are connected by a straight line
Definition: qcustomplot.h:5137
Final phase in which the layout system places the rects of the elements.
Definition: qcustomplot.h:1195
0x01 Axis is vertical and on the left side of the axis rect
Definition: qcustomplot.h:1942
void setLabelColor(const QColor &color)
void setAntialiasedFill(bool enabled)
QCP::AntialiasedElements mAntialiasedElements
Definition: qcustomplot.h:3733
bool inverted() const
Definition: qcustomplot.h:1482
QCPItemPosition *const topLeft
Definition: qcustomplot.h:6440
void setHead(const QCPLineEnding &head)
void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
QCPItemAnchor *const bottom
Definition: qcustomplot.h:6223
bool mAutoAddPlottableToLegend
Definition: qcustomplot.h:3728
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setAntialiased(bool enabled)
void setSelectedBrush(const QBrush &brush)
QSharedPointer< QCPBarsDataContainer > data() const
Definition: qcustomplot.h:5473
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:705
static QCPFinancialDataContainer timeSeriesToOhlc(const QVector< double > &time, const QVector< double > &value, double timeBinSize, double timeBinOffset=0)
QPen pen() const
Definition: qcustomplot.h:1856
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPItemPixmap(QCustomPlot *parentPlot)
QCPLayout * layout() const
Definition: qcustomplot.h:1203
QCPAxis * rangeDragAxis(Qt::Orientation orientation)
QCPAxisRect * axisRect(int index=0) const
void setFractionStyle(FractionStyle style)
0x0000 No elements
Definition: qcustomplot.h:223
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
bool contains(const QCPDataRange &other) const
void setFont(const QFont &font)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setRowStretchFactor(int row, double factor)
QCPAbstractPlottable * plottableAt(const QPointF &pos, bool onlySelectable=false) const
void setBarWidth(int width)
QList< double > mColumnStretchFactors
Definition: qcustomplot.h:1375
bool mMapImageInvalidated
Definition: qcustomplot.h:5763
BracketStyle style() const
Definition: qcustomplot.h:6593
QList< QCPGraph * > selectedGraphs() const
QCPItemAnchor *const top
Definition: qcustomplot.h:6306
bool removeItem(QCPAbstractItem *item)
void drawBackground(QCPPainter *painter)
Bar width is given by a fraction of the axis rect size.
Definition: qcustomplot.h:5457
0x02 right margin
Definition: qcustomplot.h:194
void setInterpolate(bool enabled)
SelectableParts selectedParts() const
Definition: qcustomplot.h:2015
void setPen(const QPen &pen)
Q_SLOT void deselectAll()
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
QRectF getQuartileBox(QCPStatisticalBoxDataContainer::const_iterator it) const
QPointer< QCPColorScale > mColorScale
Definition: qcustomplot.h:5758
void setBufferDevicePixelRatio(double ratio)
QCPScatterStyle scatterStyle() const
Definition: qcustomplot.h:3219
bool selected() const
Definition: qcustomplot.h:3491
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
QCPLineEnding mTail
Definition: qcustomplot.h:6123
void setTickLabelSide(LabelSide side)
QCPLayoutInset * insetLayout() const
Definition: qcustomplot.h:4600
QCPRange mDataRange
Definition: qcustomplot.h:5058
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual ~QCPItemBracket()
void removeChild(QCP::MarginSide side, QCPLayoutElement *element)
void setupPaintBuffers()
void setBrush(const QBrush &brush)
void setPixelPosition(const QPointF &pixelPosition)
void setSelectionTolerance(int pixels)
void setSelectedPen(const QPen &pen)
void clear(const QColor &color) Q_DECL_OVERRIDE
void setPen(const QPen &pen)
static AxisType opposite(AxisType type)
QString name() const
Definition: qcustomplot.h:3276
void setSymbolGap(double pixels)
Less readable tick steps are allowed which in turn facilitates getting closer to the requested tick c...
Definition: qcustomplot.h:1523
void gradientChanged(const QCPColorGradient &newGradient)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
int mNumberPrecision
Definition: qcustomplot.h:2125
virtual void draw(QCPPainter *painter)
double pixelToCoord(double value) const
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
enum QCPAxisTickerDateTime::DateStrategy mDateStrategy
void setWhiskerAntialiased(bool enabled)
QMap< double, QColor > mColorStops
Definition: qcustomplot.h:4458
{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
Definition: qcustomplot.h:2296
void addTicks(const QMap< double, QString > &ticks)
QStack< bool > mAntialiasingStack
Definition: qcustomplot.h:501
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
QPen mainPen() const
QSize iconSize() const
Definition: qcustomplot.h:4804
void clearItems()
Phase used for any type of preparation that needs to be done before margin calculation and layout...
Definition: qcustomplot.h:1193
QList< QPointer< QCPAxis > > mRangeZoomHorzAxis
Definition: qcustomplot.h:4637
virtual void layoutChanged()
bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove)
void selectableChanged(bool selectable)
void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
double mRangeZoomFactorHorz
Definition: qcustomplot.h:4638
Q_SLOT void setDataRange(const QCPRange &dataRange)
virtual int calculateAutoMargin(QCP::MarginSide side)
void setSelectedTextColor(const QColor &color)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setTickLengthOut(int outside)
double rangeZoomFactor(Qt::Orientation orientation)
QCPItemPosition *const point1
Definition: qcustomplot.h:6068
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
virtual QVector< QString > createLabelVector(const QVector< double > &ticks, const QLocale &locale, QChar formatChar, int precision)
QPen getBorderPen() const
QList< QCPColorMap * > colorMaps() const
bool hasItem(QCPAbstractLegendItem *item) const
virtual void legendRemoved(QCPLegend *legend)
bool mSubGridVisible
Definition: qcustomplot.h:1870
void unregisterBars(QCPBars *bars)
void setSelectedTickPen(const QPen &pen)
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:5753
void setSelectedFont(const QFont &font)
Describes a data range given by begin and end index.
Definition: qcustomplot.h:887
virtual void simplify()
void moveRange(double diff)
void setLabelFont(const QFont &font)
QCP::SelectionType mSelectable
Definition: qcustomplot.h:3331
A bar perpendicular to the line, pointing out to only one side (to which side can be changed with set...
Definition: qcustomplot.h:1470
0x002 Legend items individually (see selectedItems)
Definition: qcustomplot.h:4790
virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE
QPixmap pixmap() const
Definition: qcustomplot.h:2322
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:5059
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QList< QCPLegend * > selectedLegends() const
void restore()
QCPAbstractItem * item() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void addChildY(QCPItemPosition *pos)
QWeakPointer< QCPAbstractPaintBuffer > mPaintBuffer
Definition: qcustomplot.h:673
{ssPlusCircle.png} a circle with a plus inside
Definition: qcustomplot.h:2302
QSharedPointer< QCPErrorBarsDataContainer > mDataContainer
Definition: qcustomplot.h:6015
QPen pen() const
Definition: qcustomplot.h:6428
bool mBackgroundScaled
Definition: qcustomplot.h:3740
QRect rect() const
Definition: qcustomplot.h:1204
void setRotation(double degrees)
QRectF getBarRect(double key, double value) const
void setSubTickPen(const QPen &pen)
{ssCrossSquare.png} a square with a cross inside
Definition: qcustomplot.h:2299
QHash< QCPAxis::AxisType, QList< QCPAxis * > > mAxes
Definition: qcustomplot.h:4645
QString name() const
Definition: qcustomplot.h:3374
QCPItemAnchor *const right
Definition: qcustomplot.h:6308
void setLength(double length)
void setBracketHeight(int height)
virtual ~QCPAbstractPlottable()
virtual void getOptimizedScatterData(QVector< QCPGraphData > *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const
Data points are connected with a straight line.
Definition: qcustomplot.h:5273
QBrush mSelectedBrush
Definition: qcustomplot.h:6232
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
Definition: qcustomplot.h:3421
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual int getSubTickCount(double tickStep)
QCPDataSelection mSelection
Definition: qcustomplot.h:3332
void setNumberFormat(const QString &formatCode)
SelectableParts mSelectableParts
Definition: qcustomplot.h:2111
bool visible() const
Definition: qcustomplot.h:653
virtual double dataMainValue(int index) const
QCPItemPosition *const startDir
Definition: qcustomplot.h:6168
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name)
virtual QRect clipRect() const Q_DECL_OVERRIDE
The axis label.
Definition: qcustomplot.h:1974
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
QCPRange sanitizedForLinScale() const
void setLineStyle(LineStyle style)
QCPSelectionRect * selectionRect() const
Definition: qcustomplot.h:3608
virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE
bool isValid() const
Definition: qcustomplot.h:907
bool mAntialiased
Definition: qcustomplot.h:731
Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle ...
Definition: qcustomplot.h:4422
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
void replaceUnit(QString &text, TimeUnit unit, int value) const
QCPLayoutElement * element(int row, int column) const
QPen pen() const
Definition: qcustomplot.h:2320
The generic data container for one-dimensional plottables.
Definition: qcustomplot.h:2374
void setLogBase(double base)
QCPAxisPainterPrivate(QCustomPlot *parentPlot)
Qt::Alignment insetAlignment(int index) const
void add(const QCPDataContainer< DataType > &data)
Definition: qcustomplot.h:2634
double pickClosest(double target, const QVector< double > &candidates) const
SelectablePart getPartAt(const QPointF &pos) const
QPen mainPen() const
bool intersects(const QCPDataRange &other) const
void accepted(const QRect &rect, QMouseEvent *event)
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
void setTail(const QCPLineEnding &tail)
Holds the two-dimensional data of a QCPColorMap plottable.
Definition: qcustomplot.h:5646
bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
virtual QPointF dataPixelPosition(int index) const
QBrush mainBrush() const
void setSelectedFont(const QFont &font)
QList< QCPAbstractItem * > mItems
Definition: qcustomplot.h:3731
virtual ~QCPLayoutGrid()
bool rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom, QCPAxisRect::setRangeZoomAxes)
Definition: qcustomplot.h:249
QCPAbstractLegendItem * item(int index) const
virtual void startSelection(QMouseEvent *event)
void getVisibleDataBounds(QCPFinancialDataContainer::const_iterator &begin, QCPFinancialDataContainer::const_iterator &end) const
QCPItemAnchor *const top
Definition: qcustomplot.h:6377
virtual QVector< double > createSubTickVector(int subTickCount, const QVector< double > &ticks)
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
void addDataRange(const QCPDataRange &dataRange, bool simplify=true)
QString numberFormat() const
width is given by a fraction of the axis rect size
Definition: qcustomplot.h:5834
void setTickLengthIn(int inside)
virtual void endSelection(QMouseEvent *event)
double mRangeZoomFactorVert
Definition: qcustomplot.h:4638
void setSelectedColor(const QColor &color)
double size() const
Definition: qcustomplot.h:6513
void setSelectedPen(const QPen &pen)
void setRangeZoomFactor(double horizontalFactor, double verticalFactor)
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)
void setSelectedBrush(const QBrush &brush)
virtual void copyFrom(const QCPSelectionDecorator *other)
Q_SLOT void setScaleType(QCPAxis::ScaleType type)
void setClipToAxisRect(bool clip)
TracerStyle style() const
Definition: qcustomplot.h:6514
a custom pixmap specified by setPixmap, centered on the data point coordinates
Definition: qcustomplot.h:2304
void setColumnStretchFactors(const QList< double > &factors)
void mouseWheel(QWheelEvent *event)
bool addItem(QCPAbstractLegendItem *item)
0x0010 Legend items
Definition: qcustomplot.h:215
Continuous lightness from black over weak blueish colors to white (suited for non-biased data represe...
Definition: qcustomplot.h:4417
QBrush brush() const
Definition: qcustomplot.h:3218
void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
Definition: qcustomplot.h:322
void beforeReplot()
void setPen(const QPen &pen)
void drawPolyline(QCPPainter *painter, const QVector< QPointF > &lineData) const
QFont getTickLabelFont() const
QCPAxisPainterPrivate * mAxisPainter
Definition: qcustomplot.h:2142
void setWhiskerWidth(double pixels)
virtual QPointF anchorPixelPosition(int anchorId) const
void setMode(PainterMode mode, bool enabled=true)
The abstract base class for layouts.
Definition: qcustomplot.h:1262
void setSubGridVisible(bool visible)
int rowCount() const
Definition: qcustomplot.h:1331
QString unicodeSubscript(int number) const
Holds the data of one single data point for QCPFinancial.
Definition: qcustomplot.h:5782
void setTickLabelPadding(int padding)
QBrush mBackgroundBrush
Definition: qcustomplot.h:3737
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QLineF getRectClippedStraightLine(const QCPVector2D &point1, const QCPVector2D &vec, const QRect &rect) const
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setSelectedLabelColor(const QColor &color)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:6441
None of the selectable parts.
Definition: qcustomplot.h:1971
void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
QCPLayoutElement * layoutElementAt(const QPointF &pos) const
void setInteractions(const QCP::Interactions &interactions)
Any combination of data points/ranges can be selected.
Definition: qcustomplot.h:292
void setBrush(const QBrush &brush)
0x04 Axis is horizontal and on the top side of the axis rect
Definition: qcustomplot.h:1944
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
Resolution is given in dots per centimeter (dpcm)
Definition: qcustomplot.h:163
bool mOpenGlCacheLabelsBackup
Definition: qcustomplot.h:3759
virtual int elementCount() const Q_DECL_OVERRIDE
Definition: qcustomplot.h:1352
bool realVisibility() const
const char int mode
Definition: ioapi.h:38
QCPItemAnchor *const left
Definition: qcustomplot.h:6225
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:4750
QCPAbstractPlottable * plottable(int index)
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
ColorInterpolation mColorInterpolation
Definition: qcustomplot.h:4459
QCPPlottableLegendItem * itemWithPlottable(const QCPAbstractPlottable *plottable) const
QString unicodeSuperscript(int number) const
QSize size() const
Definition: qcustomplot.h:4616
QCPItemAnchor *const bottomLeft
Definition: qcustomplot.h:6224
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QCPAxis * axis(QCPAxis::AxisType type, int index=0) const
{ssTriangleInverted.png} an equilateral triangle, standing on corner
Definition: qcustomplot.h:2298
void setLength(double length)
void setAlpha(int keyIndex, int valueIndex, unsigned char alpha)
double x() const
Definition: qcustomplot.h:388
A plottable representing a two-dimensional color map in a plot.
Definition: qcustomplot.h:5703
Layer is inserted above other layer.
Definition: qcustomplot.h:3573
void rescale(bool onlyVisiblePlottables=false)
bool mAdaptiveSampling
Definition: qcustomplot.h:5180
QHash< QCP::MarginSide, QList< QCPLayoutElement * > > mChildren
Definition: qcustomplot.h:1160
void setPlottingHint(QCP::PlottingHint hint, bool enabled=true)
void setTextFlags(int flags)
QCPAxis * valueAxis() const
Definition: qcustomplot.h:3282
QColor mTickLabelColor
Definition: qcustomplot.h:2124
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:3459
InsetPlacement insetPlacement(int index) const
QRect viewport() const
Definition: qcustomplot.h:3593
void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation)
void setColumnStretchFactor(int column, double factor)
void setSelectedPen(const QPen &pen)
void setBracketStyle(BracketStyle style)
QCPDataSelection & operator+=(const QCPDataSelection &other)
virtual QSize maximumSizeHint() const Q_DECL_OVERRIDE
bool mVisible
Definition: qcustomplot.h:669
double distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const
bool setCurrentLayer(const QString &name)
QCPItemPosition *const position
Definition: qcustomplot.h:6304
void setAxisRect(QCPAxisRect *axisRect)
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details)
int tickLabelPadding() const
void addChildX(QCPItemPosition *pos)
The positive sign domain, i.e. numbers greater than zero.
Definition: qcustomplot.h:185
QCPAxis::LabelSide tickLabelSide
Definition: qcustomplot.h:2210
QPointer< QCPLayerable > mParentLayerable
Definition: qcustomplot.h:729
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
int subTickLengthOut() const
QList< QCPAbstractPlottable * > plottables() const
QCPItemAnchor *const topLeft
Definition: qcustomplot.h:6305
QPen mZeroLinePen
Definition: qcustomplot.h:1872
QPen mainPen() const
QPoint mMousePressPos
Definition: qcustomplot.h:3751
bool isNull() const
Definition: qcustomplot.h:403
Color channels red, green and blue are linearly interpolated.
Definition: qcustomplot.h:4405
bool ticks() const
Definition: qcustomplot.h:1990
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QCPItemAnchor *const right
Definition: qcustomplot.h:6379
WidthType widthType() const
Definition: qcustomplot.h:5856
Tick labels will be displayed inside the axis rect and clipped to the inner axis rect.
Definition: qcustomplot.h:1955
ChartStyle mChartStyle
Definition: qcustomplot.h:5890
void doubleClicked(QMouseEvent *event)
QList< double > mRowStretchFactors
Definition: qcustomplot.h:1376
void setInsetAlignment(int index, Qt::Alignment alignment)
void setData(QSharedPointer< QCPFinancialDataContainer > data)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QRect mViewport
Definition: qcustomplot.h:3725
void setPen(const QPen &pen)
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:4631
When dragging the mouse, a selection rect becomes active. Upon releasing, plottable data points that ...
Definition: qcustomplot.h:266
friend class QCPLegend
Definition: qcustomplot.h:3800
QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
bool removeAxis(QCPAxis *axis)
double key() const
Definition: qcustomplot.h:3435
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
The negative sign domain, i.e. numbers smaller than zero.
Definition: qcustomplot.h:183
void setRangeZoom(bool enabled)
virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
double value
Definition: qcustomplot.h:5421
void setAutoAddPlottableToLegend(bool on)
QPointer< QCPAxisRect > mAxisRect
Definition: qcustomplot.h:3460
void setClipAxisRect(QCPAxisRect *rect)
Qt::TransformationMode mTransformationMode
Definition: qcustomplot.h:6458
virtual Q_SLOT void processPointSelection(QMouseEvent *event)
double baseValue() const
Definition: qcustomplot.h:5469
virtual int calculateMargin()
void setText(const QString &text)
QPen pen() const
Definition: qcustomplot.h:6058
QFont mTickLabelFont
Definition: qcustomplot.h:2123
void setPen(const QPen &pen)
QVector< double > tickPositions
Definition: qcustomplot.h:2223
QCPDataSelection intersection(const QCPDataRange &other) const
QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true)
width is in absolute pixels
Definition: qcustomplot.h:5833
void selectionChanged(bool selected)
QHash< TimeUnit, int > mFieldWidth
Definition: qcustomplot.h:1643
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QPen pen() const
Definition: qcustomplot.h:6204
void setSelectedTextColor(const QColor &color)
virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
QCPDataSelection inverse(const QCPDataRange &outerRange) const
void expand(const QCPRange &otherRange)
QCPAbstractItem * itemAt(const QPointF &pos, bool onlySelectable=false) const
QCPRange dataBounds() const
Definition: qcustomplot.h:5659
QCPLayerable * layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const
Holds the data of one single data point for QCPStatisticalBox.
Definition: qcustomplot.h:5529
bool mReplotQueued
Definition: qcustomplot.h:3756
A non-filled arrow head with open back.
Definition: qcustomplot.h:1465
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
0x04 The size property, see setSize
Definition: qcustomplot.h:2273
virtual void updateLayout() Q_DECL_OVERRIDE
A paint buffer based on QPixmap, using software raster rendering.
Definition: qcustomplot.h:547
void setSelectedPen(const QPen &pen)
void setBorderPen(const QPen &pen)
A nicely readable tick step is prioritized over matching the requested number of ticks (see setTickCo...
Definition: qcustomplot.h:1522
SelectableParts selectableParts() const
Definition: qcustomplot.h:2016
void setZeroLinePen(const QPen &pen)
QCPItemPosition *const point2
Definition: qcustomplot.h:6069
bool hasAnchor(const QString &name) const
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
virtual QCPLayoutElement * takeAt(int index) Q_DECL_OVERRIDE
QVariant mMouseEventLayerableDetails
Definition: qcustomplot.h:3754
Bar spacing is in absolute pixels.
Definition: qcustomplot.h:5355
QMargins mPadding
Definition: qcustomplot.h:6326
QCPRange mDataBounds
Definition: qcustomplot.h:5694
void setFillOrder(FillOrder order, bool rearrange=true)
double length() const
Definition: qcustomplot.h:1481
void selectableChanged(const QCPAxis::SelectableParts &parts)
void setIconSize(const QSize &size)
void setBracketPen(const QPen &pen)
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
void setBaseValue(double baseValue)
QCPItemAnchor * mParentAnchorY
Definition: qcustomplot.h:3462
0x0200 Zero-lines, see QCPGrid::setZeroLinePen
Definition: qcustomplot.h:220
int mOpenGlMultisamples
Definition: qcustomplot.h:3757
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove)
QString mText
Definition: qcustomplot.h:6322
void setSelectedPen(const QPen &pen)
double coordToPixel(double value) const
virtual void draw(QCPPainter *painter) const Q_DECL_OVERRIDE
void setScatterStyle(const QCPScatterStyle &style)
QList< QCPAxis * > axes(QCPAxis::AxisTypes types) const
void getLines(QVector< QPointF > *lines, const QCPDataRange &dataRange) const
QBrush getBrush() const
virtual ~QCPItemPosition()
Manages the position of an item.
Definition: qcustomplot.h:3404
unsigned char alpha(int keyIndex, int valueIndex)
void adoptElement(QCPLayoutElement *el)
void setValueRange(const QCPRange &valueRange)
QList< QCPRange > mDragStartVertRange
Definition: qcustomplot.h:4641
bool hasItem(QCPAbstractItem *item) const
void setDataPlottable(QCPAbstractPlottable *plottable)
ResolutionUnit
Definition: qcustomplot.h:162
virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE
void drawToPaintBuffer()
A half ellipse is drawn. The size of the ellipse is given by the bracket width/height properties...
Definition: qcustomplot.h:4491
void setSubTickCount(int subTicks)
QPen iconBorderPen() const
Definition: qcustomplot.h:4806
QCPItemPosition *const end
Definition: qcustomplot.h:6118
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
A curly brace with varying stroke width giving a calligraphic impression.
Definition: qcustomplot.h:6582
QList< QCPAbstractItem * > items() const
void setSize(double size)
0x02 The brush property, see setBrush
Definition: qcustomplot.h:2272
void getDataSegments(QList< QCPDataRange > &selectedSegments, QList< QCPDataRange > &unselectedSegments) const
QCPDataContainer< QCPFinancialData > QCPFinancialDataContainer
Definition: qcustomplot.h:5811
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:4630
void setTicks(bool show)
QFont mSelectedFont
Definition: qcustomplot.h:6321
void setGraphKey(double key)
Defines a color gradient for use with e.g. QCPColorMap.
Definition: qcustomplot.h:4396
QCPMarginGroup(QCustomPlot *parentPlot)
QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setTypeY(PositionType type)
QRectF selectionHitBox(QCPFinancialDataContainer::const_iterator it) const
QCPGrid * grid() const
Definition: qcustomplot.h:2026
double upper
Definition: qcustomplot.h:772
virtual void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE
int tickLengthIn() const
Resolution is given in dots per inch (DPI/PPI)
Definition: qcustomplot.h:164
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lines) const
QCPMarginGroup * marginGroup(QCP::MarginSide side) const
Definition: qcustomplot.h:1211
QLineF getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:3739
virtual Q_SLOT void processRectZoom(QRect rect, QMouseEvent *event)
QCPAbstractItem(QCustomPlot *parentPlot)
void setSubTickLength(int inside, int outside=0)
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
void setBrushPositive(const QBrush &brush)
voidpf uLong offset
Definition: ioapi.h:42
QMap< double, QColor > colorStops() const
Definition: qcustomplot.h:4436
QMargins mMinimumMargins
Definition: qcustomplot.h:1239
virtual void drawStatisticalBox(QCPPainter *painter, QCPStatisticalBoxDataContainer::const_iterator it, const QCPScatterStyle &outlierStyle) const
A bar that is skewed (skew controllable via setLength)
Definition: qcustomplot.h:1471
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
Represents the range an axis is encompassing.
Definition: qcustomplot.h:769
QColor mLabelColor
Definition: qcustomplot.h:2118
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
0x8000 Other elements that don&#39;t fit into any of the existing categories
Definition: qcustomplot.h:221
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
void addData(const QVector< double > &error)
QCPRange dataRange() const
Definition: qcustomplot.h:5720
bool tickLabels() const
Definition: qcustomplot.h:1991
QCPLayoutGrid * mPlotLayout
Definition: qcustomplot.h:3727
virtual ~QCPBarsGroup()
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setAntialiasedZeroLine(bool enabled)
QVector< QLineF > getWhiskerBackboneLines(QCPStatisticalBoxDataContainer::const_iterator it) const
void setPositionAlignment(Qt::Alignment alignment)
QList< QCPItemAnchor * > mAnchors
Definition: qcustomplot.h:3518
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:5177
QList< QCPAxis * > rangeZoomAxes(Qt::Orientation orientation)
void started(QMouseEvent *event)
QString unicodeFraction(int numerator, int denominator) const
Groups multiple QCPBars together so they appear side by side.
Definition: qcustomplot.h:5341
0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)
Definition: qcustomplot.h:252
void initializeParentPlot(QCustomPlot *parentPlot)
double lower
Definition: qcustomplot.h:772
QCPScatterStyle getFinalScatterStyle(const QCPScatterStyle &unselectedStyle) const
void setSelectedBrush(const QBrush &brush)
void setScatterSkip(int skip)
int graphCount() const
void setSelectedTickLabelColor(const QColor &color)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QFont font() const
Definition: qcustomplot.h:4692
void setTickStep(double step)
bool removePlottable(QCPAbstractPlottable *plottable)
void setTail(const QCPLineEnding &tail)
bool isEmpty() const
QList< QCPAxis * > rangeDragAxes(Qt::Orientation orientation)
void setTickLabelColor(const QColor &color)
A template base class for plottables with one-dimensional data.
Definition: qcustomplot.h:3834
void setSize(double size)
void setBrush(const QBrush &brush)
ScaleType scaleType() const
Definition: qcustomplot.h:1986
ScatterShape mShape
Definition: qcustomplot.h:2345
Resolution is given in dots per meter (dpm)
Definition: qcustomplot.h:162
Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and pha...
Definition: qcustomplot.h:4425
void pixelsToCoords(double x, double y, double &key, double &value) const
void setSelectedPen(const QPen &pen)
QPen mainPen() const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:6374
bool removeItem(int index)
SelectableParts selectableParts() const
Definition: qcustomplot.h:4807
0xFF all margins
Definition: qcustomplot.h:197
QPixmap pixmap() const
Definition: qcustomplot.h:6424
QFont mainFont() const
bool mTightBoundary
Definition: qcustomplot.h:5757
friend class QCPPlottableLegendItem
Definition: qcustomplot.h:3356
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
bool mRangeReversed
Definition: qcustomplot.h:2137
void setupTickVectors()
virtual ~QCPLayerable()
PositionType mPositionTypeX
Definition: qcustomplot.h:3458
Qt::Alignment mPositionAlignment
Definition: qcustomplot.h:6323
QList< QCPAxisRect * > axisRects() const
double symbolGap() const
Definition: qcustomplot.h:5980
Holds multiple axes and arranges them in a rectangular shape.
Definition: qcustomplot.h:4548
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QCPDataRange bounded(const QCPDataRange &other) const
virtual ~QCPAxisTicker()
QCPAxis * yAxis
Definition: qcustomplot.h:3700
void setPen(const QPen &pen)
QBrush brush() const
Definition: qcustomplot.h:6362
Whether to use immediate or queued refresh depends on whether the plotting hint QCP::phImmediateRefre...
Definition: qcustomplot.h:3584
int width() const
Definition: qcustomplot.h:4614
void clicked(QMouseEvent *event)
virtual ~QCPLayoutInset()
virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE
virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE
{ssDiamond.png} a diamond
Definition: qcustomplot.h:2295
QCPAxisRect * clipAxisRect() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const
virtual double dataMainKey(int index) const =0
virtual QCPRange dataValueRange(int index) const
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:4642
QCPColorMapData & operator=(const QCPColorMapData &other)
bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
void toPainter(QCPPainter *painter, int width=0, int height=0)
QPixmap toPixmap(int width=0, int height=0, double scale=1.0)
QCPRange mRange
Definition: qcustomplot.h:2136
QCPScatterStyle::ScatterProperties mUsedScatterProperties
Definition: qcustomplot.h:3242
double mBaseValue
Definition: qcustomplot.h:5502
QPen getTickPen() const
QBrush brush() const
Definition: qcustomplot.h:6206
Qt::KeyboardModifier mMultiSelectModifier
Definition: qcustomplot.h:3744
QCPLegend * legend
Definition: qcustomplot.h:3701
QCPRange range(const QCPAxis *axis) const
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > &scatters, const QCPScatterStyle &style) const
void setPenNegative(const QPen &pen)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setLabel(const QString &str)
QVector< QPointF > dataToImpulseLines(const QVector< QCPGraphData > &data) const
QCP::AntialiasedElements notAntialiasedElements() const
Definition: qcustomplot.h:3600
QCPItemAnchor *const top
Definition: qcustomplot.h:6220
void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
AxisType mAxisType
Definition: qcustomplot.h:2106
QCPSelectionRect(QCustomPlot *parentPlot)
Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18))
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
bool mAntialiasedSubGrid
Definition: qcustomplot.h:1871
void sizeConstraintsChanged() const
Q_SLOT void setRange(const QCPRange &range)
QVector< double > outliers
Definition: qcustomplot.h:5551
LineStyle mLineStyle
Definition: qcustomplot.h:5176
QList< QCPAxis * > selectedAxes() const
A filled arrow head with an indented back.
Definition: qcustomplot.h:1464
line is drawn as steps where the step height is the value of the right data point ...
Definition: qcustomplot.h:5139
virtual void deselectEvent(bool *selectionStateChanged)
QCP::Interactions mInteractions
Definition: qcustomplot.h:3734
QCPGraph * mGraph
Definition: qcustomplot.h:6544
QCPItemAnchor *const topRight
Definition: qcustomplot.h:6221
MarginSide
Definition: qcustomplot.h:193
Qt::Alignment mTextAlignment
Definition: qcustomplot.h:6324
void setLabel(const QString &str)
QPen mPen
Definition: qcustomplot.h:1872
QLatin1Char mNumberFormatChar
Definition: qcustomplot.h:2126
bool mBackgroundScaled
Definition: qcustomplot.h:4632
void setData(QSharedPointer< QCPCurveDataContainer > data)
The errors are for the value dimension (bars appear parallel to the value axis)
Definition: qcustomplot.h:5969
int axisCount(QCPAxis::AxisType type) const
QCP::AntialiasedElements mOpenGlAntialiasedElementsBackup
Definition: qcustomplot.h:3758
QBrush brush() const
Definition: qcustomplot.h:3280
AntialiasedElement
Definition: qcustomplot.h:211
QCPItemPosition *const left
Definition: qcustomplot.h:6604
bool mColorBufferInvalidated
Definition: qcustomplot.h:4464
int axisRectCount() const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:2238
void setSubTicks(bool show)
QSharedPointer< QCPAxisTicker > mTicker
Definition: qcustomplot.h:2143
QPointer< QCPGraph > mChannelFillGraph
Definition: qcustomplot.h:5179
void setInvalidated(bool invalidated=true)
QVector< QPointF > dataToLines(const QVector< QCPGraphData > &data) const
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
virtual double dataMainKey(int index) const
AxisType axisType() const
Definition: qcustomplot.h:1984
void setChannelFillGraph(QCPGraph *targetGraph)
void selectableChanged(QCP::SelectionType selectable)
void setWidth(double width)
QPointF toPointF() const
Definition: qcustomplot.h:401
double keyPixelOffset(const QCPBars *bars, double keyCoord)
double mWidth
Definition: qcustomplot.h:5499
No ending decoration.
Definition: qcustomplot.h:1462
bool hasElement(int row, int column)
QPen mSelectedPen
Definition: qcustomplot.h:6319
QCustomPlot * mParentPlot
Definition: qcustomplot.h:728
ScatterShape shape() const
Definition: qcustomplot.h:2319
void setSelectedBrush(const QBrush &brush)
void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
void drawShape(QCPPainter *painter, const QPointF &pos) const
void setPen(const QPen &pen)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:6219
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:3741
QBrush mainBrush() const
virtual void drawCurveLine(QCPPainter *painter, const QVector< QPointF > &lines) const
Q_SLOT void setSelected(bool selected)
QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0)
double pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
bool selected() const
Definition: qcustomplot.h:4697
virtual ~QCPFinancial()
virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE
int right() const
Definition: qcustomplot.h:4611
void afterReplot()
QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
QColor mSelectedLabelColor
Definition: qcustomplot.h:2118
void setFont(const QFont &font)
void setColorStopAt(double position, const QColor &color)
bool mTicks
Definition: qcustomplot.h:2130
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
QCPVector2D & operator*=(double factor)
void setLabelPadding(int padding)