diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index 58fb20f30c3a4c6797e794fb8d743f33654419aa..0000000000000000000000000000000000000000
Binary files a/.DS_Store and /dev/null differ
diff --git a/.gitignore b/.gitignore
index 99c11f8ebdf7a0a8d4f2bb69ebf56f4cd2615e17..f5ebc0d081ee086e2ef758ab8d0726f8cfd82f03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
 SkywardHub.pro.user
 SkywardHub.pro.user.*
-SkywardHub.pro.user.*
 .build
+**/.DS_Store
\ No newline at end of file
diff --git a/Modules/Graph/qcustomplot.cpp b/Core/QCustomPlot/QCustomPlot.cpp
similarity index 81%
rename from Modules/Graph/qcustomplot.cpp
rename to Core/QCustomPlot/QCustomPlot.cpp
index 154576a66458c44531a47bdfc5875a6f7bd963e3..04f3147dd54cc08b646a41f378528bc722302f09 100644
--- a/Modules/Graph/qcustomplot.cpp
+++ b/Core/QCustomPlot/QCustomPlot.cpp
@@ -1,7 +1,7 @@
 /***************************************************************************
 **                                                                        **
 **  QCustomPlot, an easy to use, modern plotting widget for Qt            **
-**  Copyright (C) 2011-2017 Emanuel Eichhammer                            **
+**  Copyright (C) 2011-2021 Emanuel Eichhammer                            **
 **                                                                        **
 **  This program is free software: you can redistribute it and/or modify  **
 **  it under the terms of the GNU General Public License as published by  **
@@ -19,15 +19,15 @@
 ****************************************************************************
 **           Author: Emanuel Eichhammer                                   **
 **  Website/Contact: http://www.qcustomplot.com/                          **
-**             Date: 04.09.17                                             **
-**          Version: 2.0.0                                                **
+**             Date: 29.03.21                                             **
+**          Version: 2.1.0                                                **
 ****************************************************************************/
 
 #include "qcustomplot.h"
 
 
-/* including file 'src/vector2d.cpp', size 7340                              */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/vector2d.cpp'       */
+/* modified 2021-03-29T02:30:44, size 7973 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPVector2D
@@ -72,6 +72,13 @@
   \see length
 */
 
+/*! \fn double QCPVector2D::angle() const
+  
+  Returns the angle of the vector in radians. The angle is measured between the positive x line and
+  the vector, counter-clockwise in a mathematical coordinate system (y axis upwards positive). In
+  screen/widget coordinates where the y axis is inverted, the angle appears clockwise.
+*/
+
 /*! \fn QPoint QCPVector2D::toPoint() const
   
   Returns a QPoint which has the x and y coordinates of this vector, truncating any floating point
@@ -147,25 +154,30 @@ QCPVector2D::QCPVector2D(const QPointF &point) :
 /*!
   Normalizes this vector. After this operation, the length of the vector is equal to 1.
   
+  If the vector has both entries set to zero, this method does nothing.
+  
   \see normalized, length, lengthSquared
 */
 void QCPVector2D::normalize()
 {
-  double len = length();
-  mX /= len;
-  mY /= len;
+  if (mX == 0.0 && mY == 0.0) return;
+  const double lenInv = 1.0/length();
+  mX *= lenInv;
+  mY *= lenInv;
 }
 
 /*!
   Returns a normalized version of this vector. The length of the returned vector is equal to 1.
   
+  If the vector has both entries set to zero, this method returns the vector unmodified.
+  
   \see normalize, length, lengthSquared
 */
 QCPVector2D QCPVector2D::normalized() const
 {
-  QCPVector2D result(mX, mY);
-  result.normalize();
-  return result;
+  if (mX == 0.0 && mY == 0.0) return *this;
+  const double lenInv = 1.0/length();
+  return QCPVector2D(mX*lenInv, mY*lenInv);
 }
 
 /*! \overload
@@ -177,11 +189,11 @@ QCPVector2D QCPVector2D::normalized() const
 */
 double QCPVector2D::distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const
 {
-  QCPVector2D v(end-start);
-  double vLengthSqr = v.lengthSquared();
+  const QCPVector2D v(end-start);
+  const double vLengthSqr = v.lengthSquared();
   if (!qFuzzyIsNull(vLengthSqr))
   {
-    double mu = v.dot(*this-start)/vLengthSqr;
+    const double mu = v.dot(*this-start)/vLengthSqr;
     if (mu < 0)
       return (*this-start).lengthSquared();
     else if (mu > 1)
@@ -259,8 +271,8 @@ QCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector)
 /* end of 'src/vector2d.cpp' */
 
 
-/* including file 'src/painter.cpp', size 8670                               */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/painter.cpp'        */
+/* modified 2021-03-29T02:30:44, size 8656 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPPainter
@@ -283,7 +295,6 @@ QCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector)
   Creates a new QCPPainter instance and sets default values
 */
 QCPPainter::QCPPainter() :
-  QPainter(),
   mModes(pmDefault),
   mIsAntialiasing(false)
 {
@@ -477,8 +488,8 @@ void QCPPainter::makeNonCosmetic()
 /* end of 'src/painter.cpp' */
 
 
-/* including file 'src/paintbuffer.cpp', size 18502                          */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/paintbuffer.cpp'     */
+/* modified 2021-03-29T02:30:44, size 18915 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPAbstractPaintBuffer
@@ -616,7 +627,7 @@ void QCPAbstractPaintBuffer::setInvalidated(bool invalidated)
 }
 
 /*!
-  Sets the the device pixel ratio to \a ratio. This is useful to render on high-DPI output devices.
+  Sets the device pixel ratio to \a ratio. This is useful to render on high-DPI output devices.
   The ratio is automatically set to the device pixel ratio used by the parent QCustomPlot instance.
 
   The buffer is reallocated (by calling \ref reallocateBuffer), so any painters that were obtained
@@ -667,7 +678,9 @@ QCPPaintBufferPixmap::~QCPPaintBufferPixmap()
 QCPPainter *QCPPaintBufferPixmap::startPainting()
 {
   QCPPainter *result = new QCPPainter(&mBuffer);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
   result->setRenderHint(QPainter::HighQualityAntialiasing);
+#endif
   return result;
 }
 
@@ -845,22 +858,31 @@ QCPPaintBufferGlFbo::~QCPPaintBufferGlFbo()
 /* inherits documentation from base class */
 QCPPainter *QCPPaintBufferGlFbo::startPainting()
 {
-  if (mGlPaintDevice.isNull())
+  QSharedPointer<QOpenGLPaintDevice> paintDevice = mGlPaintDevice.toStrongRef();
+  QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();
+  if (!paintDevice)
   {
     qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
     return 0;
   }
+  if (!context)
+  {
+    qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
+    return 0;
+  }
   if (!mGlFrameBuffer)
   {
     qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
     return 0;
   }
   
-  if (QOpenGLContext::currentContext() != mGlContext.data())
-    mGlContext.data()->makeCurrent(mGlContext.data()->surface());
+  if (QOpenGLContext::currentContext() != context.data())
+    context->makeCurrent(context->surface());
   mGlFrameBuffer->bind();
-  QCPPainter *result = new QCPPainter(mGlPaintDevice.data());
+  QCPPainter *result = new QCPPainter(paintDevice.data());
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
   result->setRenderHint(QPainter::HighQualityAntialiasing);
+#endif
   return result;
 }
 
@@ -892,7 +914,8 @@ void QCPPaintBufferGlFbo::draw(QCPPainter *painter) const
 /* inherits documentation from base class */
 void QCPPaintBufferGlFbo::clear(const QColor &color)
 {
-  if (mGlContext.isNull())
+  QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();
+  if (!context)
   {
     qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
     return;
@@ -903,8 +926,8 @@ void QCPPaintBufferGlFbo::clear(const QColor &color)
     return;
   }
   
-  if (QOpenGLContext::currentContext() != mGlContext.data())
-    mGlContext.data()->makeCurrent(mGlContext.data()->surface());
+  if (QOpenGLContext::currentContext() != context.data())
+    context->makeCurrent(context->surface());
   mGlFrameBuffer->bind();
   glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -923,35 +946,37 @@ void QCPPaintBufferGlFbo::reallocateBuffer()
     mGlFrameBuffer = 0;
   }
   
-  if (mGlContext.isNull())
+  QSharedPointer<QOpenGLPaintDevice> paintDevice = mGlPaintDevice.toStrongRef();
+  QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();
+  if (!paintDevice)
   {
-    qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
+    qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
     return;
   }
-  if (mGlPaintDevice.isNull())
+  if (!context)
   {
-    qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
+    qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
     return;
   }
   
   // create new fbo with appropriate size:
-  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
+  context->makeCurrent(context->surface());
   QOpenGLFramebufferObjectFormat frameBufferFormat;
-  frameBufferFormat.setSamples(mGlContext.data()->format().samples());
+  frameBufferFormat.setSamples(context->format().samples());
   frameBufferFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
   mGlFrameBuffer = new QOpenGLFramebufferObject(mSize*mDevicePixelRatio, frameBufferFormat);
-  if (mGlPaintDevice.data()->size() != mSize*mDevicePixelRatio)
-    mGlPaintDevice.data()->setSize(mSize*mDevicePixelRatio);
+  if (paintDevice->size() != mSize*mDevicePixelRatio)
+    paintDevice->setSize(mSize*mDevicePixelRatio);
 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
-  mGlPaintDevice.data()->setDevicePixelRatio(mDevicePixelRatio);
+  paintDevice->setDevicePixelRatio(mDevicePixelRatio);
 #endif
 }
 #endif // QCP_OPENGL_FBO
 /* end of 'src/paintbuffer.cpp' */
 
 
-/* including file 'src/layer.cpp', size 37064                                */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/layer.cpp'           */
+/* modified 2021-03-29T02:30:44, size 37615 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPLayer
@@ -1059,10 +1084,10 @@ QCPLayer::~QCPLayer()
   // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
   
   while (!mChildren.isEmpty())
-    mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
+    mChildren.last()->setLayer(nullptr); // removes itself from mChildren via removeChild()
   
   if (mParentPlot->currentLayer() == this)
-    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.";
+    qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or nullptr beforehand.";
 }
 
 /*!
@@ -1104,8 +1129,8 @@ void QCPLayer::setMode(QCPLayer::LayerMode mode)
   if (mMode != mode)
   {
     mMode = mode;
-    if (!mPaintBuffer.isNull())
-      mPaintBuffer.data()->setInvalidated();
+    if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
+      pb->setInvalidated();
   }
 }
 
@@ -1140,18 +1165,18 @@ void QCPLayer::draw(QCPPainter *painter)
 */
 void QCPLayer::drawToPaintBuffer()
 {
-  if (!mPaintBuffer.isNull())
+  if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
   {
-    if (QCPPainter *painter = mPaintBuffer.data()->startPainting())
+    if (QCPPainter *painter = pb->startPainting())
     {
       if (painter->isActive())
         draw(painter);
       else
         qDebug() << Q_FUNC_INFO << "paint buffer returned inactive painter";
       delete painter;
-      mPaintBuffer.data()->donePainting();
+      pb->donePainting();
     } else
-      qDebug() << Q_FUNC_INFO << "paint buffer returned zero painter";
+      qDebug() << Q_FUNC_INFO << "paint buffer returned nullptr painter";
   } else
     qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
 }
@@ -1161,27 +1186,28 @@ void QCPLayer::drawToPaintBuffer()
   the layerables on this specific layer, without the need to replot all other layers (as a call to
   \ref QCustomPlot::replot would do).
 
+  QCustomPlot also makes sure to replot all layers instead of only this one, if the layer ordering
+  or any layerable-layer-association has changed since the last full replot and any other paint
+  buffers were thus invalidated.
+
   If the layer mode is \ref lmLogical however, this method simply calls \ref QCustomPlot::replot on
   the parent QCustomPlot instance.
 
-  QCustomPlot also makes sure to replot all layers instead of only this one, if the layer ordering
-  has changed since the last full replot and the other paint buffers were thus invalidated.
-
   \see draw
 */
 void QCPLayer::replot()
 {
   if (mMode == lmBuffered && !mParentPlot->hasInvalidatedPaintBuffers())
   {
-    if (!mPaintBuffer.isNull())
+    if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
     {
-      mPaintBuffer.data()->clear(Qt::transparent);
+      pb->clear(Qt::transparent);
       drawToPaintBuffer();
-      mPaintBuffer.data()->setInvalidated(false);
+      pb->setInvalidated(false); // since layer is lmBuffered, we know only this layer is on buffer and we can reset invalidated flag
       mParentPlot->update();
     } else
       qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
-  } else if (mMode == lmLogical)
+  } else
     mParentPlot->replot();
 }
 
@@ -1203,8 +1229,8 @@ void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
       mChildren.prepend(layerable);
     else
       mChildren.append(layerable);
-    if (!mPaintBuffer.isNull())
-      mPaintBuffer.data()->setInvalidated();
+    if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
+      pb->setInvalidated();
   } else
     qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
 }
@@ -1222,8 +1248,8 @@ void QCPLayer::removeChild(QCPLayerable *layerable)
 {
   if (mChildren.removeOne(layerable))
   {
-    if (!mPaintBuffer.isNull())
-      mPaintBuffer.data()->setInvalidated();
+    if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())
+      pb->setInvalidated();
   } else
     qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
 }
@@ -1254,7 +1280,8 @@ void QCPLayer::removeChild(QCPLayerable *layerable)
   only get drawn if their parent layerables are visible, too.
   
   Note that a parent layerable is not necessarily also the QObject parent for memory management.
-  Further, a layerable doesn't always have a parent layerable, so this function may return 0.
+  Further, a layerable doesn't always have a parent layerable, so this function may return \c
+  nullptr.
   
   A parent layerable is set implicitly when placed inside layout elements and doesn't need to be
   set manually by the user.
@@ -1326,8 +1353,8 @@ void QCPLayer::removeChild(QCPLayerable *layerable)
   targetLayer is an empty string, it places itself on the current layer of the plot (see \ref
   QCustomPlot::setCurrentLayer).
   
-  It is possible to provide 0 as \a plot. In that case, you should assign a parent plot at a later
-  time with \ref initializeParentPlot.
+  It is possible to provide \c nullptr as \a plot. In that case, you should assign a parent plot at
+  a later time with \ref initializeParentPlot.
   
   The layerable's parent layerable is set to \a parentLayerable, if provided. Direct layerable
   parents are mainly used to control visibility in a hierarchy of layerables. This means a
@@ -1341,7 +1368,7 @@ QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable
   mVisible(true),
   mParentPlot(plot),
   mParentLayerable(parentLayerable),
-  mLayer(0),
+  mLayer(nullptr),
   mAntialiased(true)
 {
   if (mParentPlot)
@@ -1358,7 +1385,7 @@ QCPLayerable::~QCPLayerable()
   if (mLayer)
   {
     mLayer->removeChild(this);
-    mLayer = 0;
+    mLayer = nullptr;
   }
 }
 
@@ -1464,9 +1491,14 @@ bool QCPLayerable::realVisibility() const
   placed in \a details. So in the subsequent \ref selectEvent, the decision which part was
   selected doesn't have to be done a second time for a single selection operation.
   
-  You may pass 0 as \a details to indicate that you are not interested in those selection details.
+  In the case of 1D Plottables (\ref QCPAbstractPlottable1D, like \ref QCPGraph or \ref QCPBars) \a
+  details will be set to a \ref QCPDataSelection, describing the closest data point to \a pos.
+  
+  You may pass \c nullptr as \a details to indicate that you are not interested in those selection
+  details.
   
-  \see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions
+  \see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions,
+  QCPAbstractPlottable1D::selectTestRect
 */
 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
 {
@@ -1479,11 +1511,11 @@ double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVarian
 /*! \internal
   
   Sets the parent plot of this layerable. Use this function once to set the parent plot if you have
-  passed 0 in the constructor. It can not be used to move a layerable from one QCustomPlot to
-  another one.
+  passed \c nullptr in the constructor. It can not be used to move a layerable from one QCustomPlot
+  to another one.
   
-  Note that, unlike when passing a non-null parent plot in the constructor, this function does not
-  make \a parentPlot the QObject-parent of this layerable. If you want this, call
+  Note that, unlike when passing a non \c nullptr parent plot in the constructor, this function
+  does not make \a parentPlot the QObject-parent of this layerable. If you want this, call
   QObject::setParent(\a parentPlot) in addition to this function.
   
   Further, you will probably want to set a layer (\ref setLayer) after calling this function, to
@@ -1576,8 +1608,8 @@ void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialia
 /*! \internal
 
   This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting
-  of a parent plot. This is the case when 0 was passed as parent plot in the constructor, and the
-  parent plot is set at a later time.
+  of a parent plot. This is the case when \c nullptr was passed as parent plot in the constructor,
+  and the parent plot is set at a later time.
   
   For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any
   QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level
@@ -1624,7 +1656,7 @@ QRect QCPLayerable::clipRect() const
   if (mParentPlot)
     return mParentPlot->viewport();
   else
-    return QRect();
+    return {};
 }
 
 /*! \internal
@@ -1717,7 +1749,7 @@ void QCPLayerable::mousePressEvent(QMouseEvent *event, const QVariant &details)
 
   The current pixel position of the cursor on the QCustomPlot widget is accessible via \c
   event->pos(). The parameter \a startPos indicates the position where the initial \ref
-  mousePressEvent occured, that started the mouse interaction.
+  mousePressEvent occurred, that started the mouse interaction.
 
   The default implementation does nothing.
 
@@ -1735,7 +1767,7 @@ void QCPLayerable::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
 
   The current pixel position of the cursor on the QCustomPlot widget is accessible via \c
   event->pos(). The parameter \a startPos indicates the position where the initial \ref
-  mousePressEvent occured, that started the mouse interaction.
+  mousePressEvent occurred, that started the mouse interaction.
 
   The default implementation does nothing.
 
@@ -1787,10 +1819,10 @@ void QCPLayerable::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &det
   The current pixel position of the cursor on the QCustomPlot widget is accessible via \c
   event->pos().
 
-  The \c event->delta() indicates how far the mouse wheel was turned, which is usually +/- 120 for
-  single rotation steps. However, if the mouse wheel is turned rapidly, multiple steps may
-  accumulate to one event, making \c event->delta() larger. On the other hand, if the wheel has
-  very smooth steps or none at all, the delta may be smaller.
+  The \c event->angleDelta() indicates how far the mouse wheel was turned, which is usually +/- 120
+  for single rotation steps. However, if the mouse wheel is turned rapidly, multiple steps may
+  accumulate to one event, making the delta larger. On the other hand, if the wheel has very smooth
+  steps or none at all, the delta may be smaller.
 
   The default implementation does nothing.
 
@@ -1803,8 +1835,8 @@ void QCPLayerable::wheelEvent(QWheelEvent *event)
 /* end of 'src/layer.cpp' */
 
 
-/* including file 'src/axis/range.cpp', size 12221                           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/range.cpp'      */
+/* modified 2021-03-29T02:30:44, size 12221 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPRange
@@ -2125,8 +2157,8 @@ bool QCPRange::validRange(const QCPRange &range)
 /* end of 'src/axis/range.cpp' */
 
 
-/* including file 'src/selection.cpp', size 21906                            */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/selection.cpp'       */
+/* modified 2021-03-29T02:30:44, size 21837 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPDataRange
@@ -2136,9 +2168,9 @@ bool QCPRange::validRange(const QCPRange &range)
   \brief Describes a data range given by begin and end index
   
   QCPDataRange holds two integers describing the begin (\ref setBegin) and end (\ref setEnd) index
-  of a contiguous set of data points. The end index points to the data point above the last data point that's part of
-  the data range, similarly to the nomenclature used in standard iterators.
-  
+  of a contiguous set of data points. The \a end index corresponds to the data point just after the
+  last data point of the data range, like in standard iterators.
+
   Data Ranges are not bound to a certain plottable, thus they can be freely exchanged, created and
   modified. If a non-contiguous data set shall be described, the class \ref QCPDataSelection is
   used, which holds and manages multiple instances of \ref QCPDataRange. In most situations, \ref
@@ -2184,7 +2216,7 @@ bool QCPRange::validRange(const QCPRange &range)
 
 /*! \fn void QCPDataRange::setEnd(int end)
   
-  Sets the end of this data range. The \a end index points to the data point just above the last
+  Sets the end of this data range. The \a end index points to the data point just after the last
   data point that is part of the data range.
   
   No checks or corrections are made to ensure the resulting range is valid (\ref isValid).
@@ -2266,7 +2298,7 @@ QCPDataRange QCPDataRange::bounded(const QCPDataRange &other) const
 */
 QCPDataRange QCPDataRange::expanded(const QCPDataRange &other) const
 {
-  return QCPDataRange(qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd));
+  return {qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd)};
 }
 
 /*!
@@ -2285,7 +2317,7 @@ QCPDataRange QCPDataRange::intersection(const QCPDataRange &other) const
   if (result.isValid())
     return result;
   else
-    return QCPDataRange();
+    return {};
 }
 
 /*!
@@ -2300,7 +2332,7 @@ bool QCPDataRange::intersects(const QCPDataRange &other) const
 }
 
 /*!
-  Returns whether all data points described by this data range are also in \a other.
+  Returns whether all data points of \a other are also contained inside this data range.
   
   \see intersects
 */
@@ -2496,8 +2528,8 @@ QCPDataSelection &QCPDataSelection::operator-=(const QCPDataRange &other)
 int QCPDataSelection::dataPointCount() const
 {
   int result = 0;
-  for (int i=0; i<mDataRanges.size(); ++i)
-    result += mDataRanges.at(i).length();
+  foreach (QCPDataRange dataRange, mDataRanges)
+    result += dataRange.length();
   return result;
 }
 
@@ -2517,7 +2549,7 @@ QCPDataRange QCPDataSelection::dataRange(int index) const
   } else
   {
     qDebug() << Q_FUNC_INFO << "index out of range:" << index;
-    return QCPDataRange();
+    return {};
   }
 }
 
@@ -2528,9 +2560,9 @@ QCPDataRange QCPDataSelection::dataRange(int index) const
 QCPDataRange QCPDataSelection::span() const
 {
   if (isEmpty())
-    return QCPDataRange();
+    return {};
   else
-    return QCPDataRange(mDataRanges.first().begin(), mDataRanges.last().end());
+    return {mDataRanges.first().begin(), mDataRanges.last().end()};
 }
 
 /*!
@@ -2631,7 +2663,8 @@ void QCPDataSelection::enforceType(QCP::SelectionType type)
     }
     case QCP::stDataRange:
     {
-      mDataRanges = QList<QCPDataRange>() << span();
+      if (!isEmpty())
+        mDataRanges = QList<QCPDataRange>() << span();
       break;
     }
     case QCP::stMultipleDataRanges:
@@ -2675,8 +2708,8 @@ bool QCPDataSelection::contains(const QCPDataSelection &other) const
 QCPDataSelection QCPDataSelection::intersection(const QCPDataRange &other) const
 {
   QCPDataSelection result;
-  for (int i=0; i<mDataRanges.size(); ++i)
-    result.addDataRange(mDataRanges.at(i).intersection(other), false);
+  foreach (QCPDataRange dataRange, mDataRanges)
+    result.addDataRange(dataRange.intersection(other), false);
   result.simplify();
   return result;
 }
@@ -2725,8 +2758,8 @@ QCPDataSelection QCPDataSelection::inverse(const QCPDataRange &outerRange) const
 /* end of 'src/selection.cpp' */
 
 
-/* including file 'src/selectionrect.cpp', size 9224                         */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/selectionrect.cpp'  */
+/* modified 2021-03-29T02:30:44, size 9215 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPSelectionRect
@@ -2784,8 +2817,8 @@ QCPDataSelection QCPDataSelection::inverse(const QCPDataRange &outerRange) const
 
 /*! \fn void QCPSelectionRect::canceled(const QRect &rect, QInputEvent *event);
   
-  This signal is emitted when the selection interaction was cancelled. Note that \a event is 0 if
-  the selection interaction was cancelled programmatically, by a call to \ref cancel.
+  This signal is emitted when the selection interaction was cancelled. Note that \a event is \c
+  nullptr if the selection interaction was cancelled programmatically, by a call to \ref cancel.
   
   The user may cancel the selection interaction by pressing the escape key. In this case, \a event
   holds the respective input event.
@@ -2832,13 +2865,13 @@ QCPRange QCPSelectionRect::range(const QCPAxis *axis) const
   if (axis)
   {
     if (axis->orientation() == Qt::Horizontal)
-      return QCPRange(axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width()));
+      return {axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width())};
     else
-      return QCPRange(axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top()));
+      return {axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top())};
   } else
   {
     qDebug() << Q_FUNC_INFO << "called with axis zero";
-    return QCPRange();
+    return {};
   }
 }
 
@@ -2872,7 +2905,7 @@ void QCPSelectionRect::cancel()
   if (mActive)
   {
     mActive = false;
-    emit canceled(mRect, 0);
+    emit canceled(mRect, nullptr);
   }
 }
 
@@ -2954,8 +2987,8 @@ void QCPSelectionRect::draw(QCPPainter *painter)
 /* end of 'src/selectionrect.cpp' */
 
 
-/* including file 'src/layout.cpp', size 79064                               */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/layout.cpp'          */
+/* modified 2021-03-29T02:30:44, size 78863 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPMarginGroup
@@ -3049,7 +3082,7 @@ void QCPMarginGroup::clear()
     it.next();
     const QList<QCPLayoutElement*> elements = it.value();
     for (int i=elements.size()-1; i>=0; --i)
-      elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
+      elements.at(i)->setMarginGroup(it.key(), nullptr); // removes itself from mChildren via removeChild
   }
 }
 
@@ -3067,12 +3100,11 @@ int QCPMarginGroup::commonMargin(QCP::MarginSide side) const
 {
   // query all automatic margins of the layout elements in this margin group side and find maximum:
   int result = 0;
-  const QList<QCPLayoutElement*> elements = mChildren.value(side);
-  for (int i=0; i<elements.size(); ++i)
+  foreach (QCPLayoutElement *el, mChildren.value(side))
   {
-    if (!elements.at(i)->autoMargins().testFlag(side))
+    if (!el->autoMargins().testFlag(side))
       continue;
-    int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
+    int m = qMax(el->calculateAutoMargin(side), QCP::getMarginValue(el->minimumMargins(), side));
     if (m > result)
       result = m;
   }
@@ -3174,7 +3206,7 @@ void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element
 */
 QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) :
   QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
-  mParentLayout(0),
+  mParentLayout(nullptr),
   mMinimumSize(),
   mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
   mSizeConstraintRect(scrInnerRect),
@@ -3188,7 +3220,7 @@ QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) :
 
 QCPLayoutElement::~QCPLayoutElement()
 {
-  setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
+  setMarginGroup(QCP::msAll, nullptr); // unregister at margin groups, if there are any
   // unregister at layout:
   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
     mParentLayout->take(this);
@@ -3354,7 +3386,7 @@ void QCPLayoutElement::setSizeConstraintRect(SizeConstraintRect constraintRect)
   Margin groups allow synchronizing specified margins across layout elements, see the documentation
   of \ref QCPMarginGroup.
   
-  To unset the margin group of \a sides, set \a group to 0.
+  To unset the margin group of \a sides, set \a group to \c nullptr.
   
   Note that margin groups only work for margin sides that are set to automatic (\ref
   setAutoMargins).
@@ -3369,9 +3401,8 @@ void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *gr
   if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
   if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
   
-  for (int i=0; i<sideVector.size(); ++i)
+  foreach (QCP::MarginSide side, sideVector)
   {
-    QCP::MarginSide side = sideVector.at(i);
     if (marginGroup(side) != group)
     {
       QCPMarginGroup *oldGroup = marginGroup(side);
@@ -3410,7 +3441,7 @@ void QCPLayoutElement::update(UpdatePhase phase)
     {
       // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
       QMargins newMargins = mMargins;
-      QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
+      const QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
       foreach (QCP::MarginSide side, allMarginSides)
       {
         if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
@@ -3445,7 +3476,7 @@ void QCPLayoutElement::update(UpdatePhase phase)
 */
 QSize QCPLayoutElement::minimumOuterSizeHint() const
 {
-  return QSize(mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom());
+  return {mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom()};
 }
 
 /*!
@@ -3464,15 +3495,15 @@ QSize QCPLayoutElement::minimumOuterSizeHint() const
 */
 QSize QCPLayoutElement::maximumOuterSizeHint() const
 {
-  return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+  return {QWIDGETSIZE_MAX, QWIDGETSIZE_MAX};
 }
 
 /*!
   Returns a list of all child elements in this layout element. If \a recursive is true, all
   sub-child elements are included in the list, too.
   
-  \warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
-  empty cells which yield 0 at the respective index.)
+  \warning There may be \c nullptr entries in the returned list. For example, QCPLayoutGrid may
+  have empty cells which yield \c nullptr at the respective index.
 */
 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
 {
@@ -3518,7 +3549,7 @@ double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVa
 */
 void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
 {
-  foreach (QCPLayoutElement* el, elements(false))
+  foreach (QCPLayoutElement *el, elements(false))
   {
     if (!el->parentPlot())
       el->initializeParentPlot(parentPlot);
@@ -3592,11 +3623,12 @@ void QCPLayoutElement::layoutChanged()
 
 /*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0
   
-  Returns the element in the cell with the given \a index. If \a index is invalid, returns 0.
+  Returns the element in the cell with the given \a index. If \a index is invalid, returns \c
+  nullptr.
   
   Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g.
-  QCPLayoutGrid), so this function may return 0 in those cases. You may use this function to check
-  whether a cell is empty or not.
+  QCPLayoutGrid), so this function may return \c nullptr in those cases. You may use this function
+  to check whether a cell is empty or not.
   
   \see elements, elementCount, takeAt
 */
@@ -3605,7 +3637,7 @@ void QCPLayoutElement::layoutChanged()
   
   Removes the element with the given \a index from the layout and returns it.
   
-  If the \a index is invalid or the cell with that index is empty, returns 0.
+  If the \a index is invalid or the cell with that index is empty, returns \c nullptr.
   
   Note that some layouts don't remove the respective cell right away but leave an empty cell after
   successful removal of the layout element. To collapse empty cells, use \ref simplify.
@@ -3824,8 +3856,8 @@ void QCPLayout::releaseElement(QCPLayoutElement *el)
 {
   if (el)
   {
-    el->mParentLayout = 0;
-    el->setParentLayerable(0);
+    el->mParentLayout = nullptr;
+    el->setParentLayerable(nullptr);
     el->setParent(mParentPlot);
     // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
   } else
@@ -3903,9 +3935,8 @@ QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minS
       // find section that hits its maximum next:
       int nextId = -1;
       double nextMax = 1e12;
-      for (int i=0; i<unfinishedSections.size(); ++i)
+      foreach (int secId, unfinishedSections)
       {
-        int secId = unfinishedSections.at(i);
         double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
         if (hitsMaxAt < nextMax)
         {
@@ -3916,21 +3947,21 @@ QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minS
       // 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
       // actually hits its maximum, without exceeding the total size when we add up all sections)
       double stretchFactorSum = 0;
-      for (int i=0; i<unfinishedSections.size(); ++i)
-        stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
+      foreach (int secId, unfinishedSections)
+        stretchFactorSum += stretchFactors.at(secId);
       double nextMaxLimit = freeSize/stretchFactorSum;
       if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
       {
-        for (int i=0; i<unfinishedSections.size(); ++i)
+        foreach (int secId, unfinishedSections)
         {
-          sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
-          freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
+          sectionSizes[secId] += nextMax*stretchFactors.at(secId); // increment all sections
+          freeSize -= nextMax*stretchFactors.at(secId);
         }
         unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
       } else // next maximum isn't hit, just distribute rest of free space on remaining sections
       {
-        for (int i=0; i<unfinishedSections.size(); ++i)
-          sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
+        foreach (int secId, unfinishedSections)
+          sectionSizes[secId] += nextMaxLimit*stretchFactors.at(secId); // increment all sections
         unfinishedSections.clear();
       }
     }
@@ -3961,8 +3992,8 @@ QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minS
           freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
       }
       // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
-      for (int i=0; i<unfinishedSections.size(); ++i)
-        sectionSizes[unfinishedSections.at(i)] = 0;
+      foreach (int secId, unfinishedSections)
+        sectionSizes[secId] = 0;
     }
   }
   if (outerIterations == sectionCount*2)
@@ -3995,8 +4026,8 @@ QSize QCPLayout::getFinalMinimumOuterSize(const QCPLayoutElement *el)
   if (minOuter.height() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
     minOuter.rheight() += el->margins().top() + el->margins().bottom();
   
-  return QSize(minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(),
-               minOuter.height() > 0 ? minOuter.height() : minOuterHint.height());;
+  return {minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(),
+               minOuter.height() > 0 ? minOuter.height() : minOuterHint.height()};
 }
 
 /*! \internal
@@ -4020,8 +4051,8 @@ QSize QCPLayout::getFinalMaximumOuterSize(const QCPLayoutElement *el)
   if (maxOuter.height() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
     maxOuter.rheight() += el->margins().top() + el->margins().bottom();
   
-  return QSize(maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(),
-               maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height());
+  return {maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(),
+               maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height()};
 }
 
 
@@ -4074,7 +4105,7 @@ QCPLayoutGrid::QCPLayoutGrid() :
   mColumnSpacing(5),
   mRowSpacing(5),
   mWrap(0),
-  mFillOrder(foRowsFirst)
+  mFillOrder(foColumnsFirst)
 {
 }
 
@@ -4088,8 +4119,8 @@ QCPLayoutGrid::~QCPLayoutGrid()
 /*!
   Returns the element in the cell in \a row and \a column.
   
-  Returns 0 if either the row/column is invalid or if the cell is empty. In those cases, a qDebug
-  message is printed. To check whether a cell exists and isn't empty, use \ref hasElement.
+  Returns \c nullptr if either the row/column is invalid or if the cell is empty. In those cases, a
+  qDebug message is printed. To check whether a cell exists and isn't empty, use \ref hasElement.
   
   \see addElement, hasElement
 */
@@ -4107,7 +4138,7 @@ QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
       qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
   } else
     qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
-  return 0;
+  return nullptr;
 }
 
 
@@ -4350,7 +4381,8 @@ void QCPLayoutGrid::setWrap(int count)
   The specified \a order defines whether rows or columns are filled first. Using \ref setWrap, you
   can control at which row/column count wrapping into the next column/row will occur. If you set it
   to zero, no wrapping will ever occur. Changing the fill order also changes the meaning of the
-  linear index used e.g. in \ref elementAt and \ref takeAt.
+  linear index used e.g. in \ref elementAt and \ref takeAt. The default fill order for \ref
+  QCPLayoutGrid is \ref foColumnsFirst.
 
   If you want to have all current elements arranged in the new order, set \a rearrange to true. The
   elements will be rearranged in a way that tries to preserve their linear index. However, empty
@@ -4387,8 +4419,8 @@ void QCPLayoutGrid::setFillOrder(FillOrder order, bool rearrange)
   // if rearranging, re-insert via linear index according to new fill order:
   if (rearrange)
   {
-    for (int i=0; i<tempElements.size(); ++i)
-      addElement(tempElements.at(i));
+    foreach (QCPLayoutElement *tempElement, tempElements)
+      addElement(tempElement);
   }
 }
 
@@ -4419,7 +4451,7 @@ void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
   for (int i=0; i<rowCount(); ++i)
   {
     while (mElements.at(i).size() < newColCount)
-      mElements[i].append(0);
+      mElements[i].append(nullptr);
   }
   while (mColumnStretchFactors.size() < newColCount)
     mColumnStretchFactors.append(1);
@@ -4447,7 +4479,7 @@ void QCPLayoutGrid::insertRow(int newIndex)
   mRowStretchFactors.insert(newIndex, 1);
   QList<QCPLayoutElement*> newRow;
   for (int col=0; col<columnCount(); ++col)
-    newRow.append((QCPLayoutElement*)0);
+    newRow.append(nullptr);
   mElements.insert(newIndex, newRow);
 }
 
@@ -4473,7 +4505,7 @@ void QCPLayoutGrid::insertColumn(int newIndex)
   
   mColumnStretchFactors.insert(newIndex, 1);
   for (int row=0; row<rowCount(); ++row)
-    mElements[row].insert(newIndex, (QCPLayoutElement*)0);
+    mElements[row].insert(newIndex, nullptr);
 }
 
 /*!
@@ -4598,7 +4630,7 @@ QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const
     indexToRowCol(index, row, col);
     return mElements.at(row).at(col);
   } else
-    return 0;
+    return nullptr;
 }
 
 /*!
@@ -4616,12 +4648,12 @@ QCPLayoutElement *QCPLayoutGrid::takeAt(int index)
     releaseElement(el);
     int row, col;
     indexToRowCol(index, row, col);
-    mElements[row][col] = 0;
+    mElements[row][col] = nullptr;
     return el;
   } else
   {
     qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
-    return 0;
+    return nullptr;
   }
 }
 
@@ -4640,7 +4672,7 @@ bool QCPLayoutGrid::take(QCPLayoutElement *element)
     }
     qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
   } else
-    qDebug() << Q_FUNC_INFO << "Can't take null element";
+    qDebug() << Q_FUNC_INFO << "Can't take nullptr element";
   return false;
 }
 
@@ -4718,10 +4750,10 @@ QSize QCPLayoutGrid::minimumOuterSizeHint() const
   QVector<int> minColWidths, minRowHeights;
   getMinimumRowColSizes(&minColWidths, &minRowHeights);
   QSize result(0, 0);
-  for (int i=0; i<minColWidths.size(); ++i)
-    result.rwidth() += minColWidths.at(i);
-  for (int i=0; i<minRowHeights.size(); ++i)
-    result.rheight() += minRowHeights.at(i);
+  foreach (int w, minColWidths)
+    result.rwidth() += w;
+  foreach (int h, minRowHeights)
+    result.rheight() += h;
   result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
   result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
   result.rwidth() += mMargins.left()+mMargins.right();
@@ -4736,10 +4768,10 @@ QSize QCPLayoutGrid::maximumOuterSizeHint() const
   getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
   
   QSize result(0, 0);
-  for (int i=0; i<maxColWidths.size(); ++i)
-    result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
-  for (int i=0; i<maxRowHeights.size(); ++i)
-    result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
+  foreach (int w, maxColWidths)
+    result.setWidth(qMin(result.width()+w, QWIDGETSIZE_MAX));
+  foreach (int h, maxRowHeights)
+    result.setHeight(qMin(result.height()+h, QWIDGETSIZE_MAX));
   result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
   result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
   result.rwidth() += mMargins.left()+mMargins.right();
@@ -4888,7 +4920,11 @@ Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
   else
   {
     qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
-    return 0;
+#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
+    return nullptr;
+#else
+    return {};
+#endif
   }
 }
 
@@ -4903,7 +4939,7 @@ QRectF QCPLayoutInset::insetRect(int index) const
   else
   {
     qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
-    return QRectF();
+    return {};
   }
 }
 
@@ -4966,10 +5002,10 @@ void QCPLayoutInset::updateLayout()
     QSize finalMaxSize = getFinalMaximumOuterSize(el);
     if (mInsetPlacement.at(i) == ipFree)
     {
-      insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
-                        rect().y()+rect().height()*mInsetRect.at(i).y(),
-                        rect().width()*mInsetRect.at(i).width(),
-                        rect().height()*mInsetRect.at(i).height());
+      insetRect = QRect(int( rect().x()+rect().width()*mInsetRect.at(i).x() ),
+                        int( rect().y()+rect().height()*mInsetRect.at(i).y() ),
+                        int( rect().width()*mInsetRect.at(i).width() ),
+                        int( rect().height()*mInsetRect.at(i).height() ));
       if (insetRect.size().width() < finalMinSize.width())
         insetRect.setWidth(finalMinSize.width());
       if (insetRect.size().height() < finalMinSize.height())
@@ -4984,10 +5020,10 @@ void QCPLayoutInset::updateLayout()
       Qt::Alignment al = mInsetAlignment.at(i);
       if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
       else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
-      else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
+      else insetRect.moveLeft(int( rect().x()+rect().width()*0.5-finalMinSize.width()*0.5 )); // default to Qt::AlignHCenter
       if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
       else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
-      else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
+      else insetRect.moveTop(int( rect().y()+rect().height()*0.5-finalMinSize.height()*0.5 )); // default to Qt::AlignVCenter
     }
     mElements.at(i)->setOuterRect(insetRect);
   }
@@ -5005,7 +5041,7 @@ QCPLayoutElement *QCPLayoutInset::elementAt(int index) const
   if (index >= 0 && index < mElements.size())
     return mElements.at(index);
   else
-    return 0;
+    return nullptr;
 }
 
 /* inherits documentation from base class */
@@ -5022,7 +5058,7 @@ QCPLayoutElement *QCPLayoutInset::takeAt(int index)
   } else
   {
     qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
-    return 0;
+    return nullptr;
   }
 }
 
@@ -5041,7 +5077,7 @@ bool QCPLayoutInset::take(QCPLayoutElement *element)
     }
     qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
   } else
-    qDebug() << Q_FUNC_INFO << "Can't take null element";
+    qDebug() << Q_FUNC_INFO << "Can't take nullptr element";
   return false;
 }
 
@@ -5060,11 +5096,11 @@ double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVari
   if (onlySelectable)
     return -1;
   
-  for (int i=0; i<mElements.size(); ++i)
+  foreach (QCPLayoutElement *el, mElements)
   {
     // inset layout shall only return positive selectTest, if actually an inset object is at pos
     // else it would block the entire underlying QCPAxisRect with its surface.
-    if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
+    if (el->realVisibility() && el->selectTest(pos, onlySelectable) >= 0)
       return mParentPlot->selectionTolerance()*0.99;
   }
   return -1;
@@ -5093,7 +5129,7 @@ void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignme
     mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
     adoptElement(element);
   } else
-    qDebug() << Q_FUNC_INFO << "Can't add null element";
+    qDebug() << Q_FUNC_INFO << "Can't add nullptr element";
 }
 
 /*!
@@ -5119,13 +5155,13 @@ void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
     mInsetRect.append(rect);
     adoptElement(element);
   } else
-    qDebug() << Q_FUNC_INFO << "Can't add null element";
+    qDebug() << Q_FUNC_INFO << "Can't add nullptr element";
 }
 /* end of 'src/layout.cpp' */
 
 
-/* including file 'src/lineending.cpp', size 11536                           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/lineending.cpp'      */
+/* modified 2021-03-29T02:30:44, size 11189 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPLineEnding
@@ -5395,17 +5431,12 @@ void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, const QCPV
     }
     case esSkewedBar:
     {
-      if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
-      {
-        // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
-        painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)).toPointF(),
-                          (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)).toPointF());
-      } else
-      {
-        // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
-        painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
-                          (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
-      }
+      QCPVector2D shift;
+      if (!qFuzzyIsNull(painter->pen().widthF()) || painter->modes().testFlag(QCPPainter::pmNonCosmetic))
+        shift = dir.normalized()*qMax(qreal(1.0), painter->pen().widthF())*qreal(0.5);
+      // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
+      painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+shift).toPointF(),
+                        (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+shift).toPointF());
       break;
     }
   }
@@ -5423,906 +5454,1231 @@ void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double ang
 /* end of 'src/lineending.cpp' */
 
 
-/* including file 'src/axis/axisticker.cpp', size 18664                      */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/labelpainter.cpp' */
+/* modified 2021-03-29T02:30:44, size 27296   */
+
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////// QCPAxisTicker
+//////////////////// QCPLabelPainterPrivate
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-/*! \class QCPAxisTicker
-  \brief The base class tick generator used by QCPAxis to create tick positions and tick labels
-  
-  Each QCPAxis has an internal QCPAxisTicker (or a subclass) in order to generate tick positions
-  and tick labels for the current axis range. The ticker of an axis can be set via \ref
-  QCPAxis::setTicker. Since that method takes a <tt>QSharedPointer<QCPAxisTicker></tt>, multiple
-  axes can share the same ticker instance.
-  
-  This base class generates normal tick coordinates and numeric labels for linear axes. It picks a
-  reasonable tick step (the separation between ticks) which results in readable tick labels. The
-  number of ticks that should be approximately generated can be set via \ref setTickCount.
-  Depending on the current tick step strategy (\ref setTickStepStrategy), the algorithm either
-  sacrifices readability to better match the specified tick count (\ref
-  QCPAxisTicker::tssMeetTickCount) or relaxes the tick count in favor of better tick steps (\ref
-  QCPAxisTicker::tssReadability), which is the default.
-  
-  The following more specialized axis ticker subclasses are available, see details in the
-  respective class documentation:
-  
-  <center>
-  <table>
-  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerFixed</td><td>\image html axisticker-fixed.png</td></tr>
-  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerLog</td><td>\image html axisticker-log.png</td></tr>
-  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerPi</td><td>\image html axisticker-pi.png</td></tr>
-  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerText</td><td>\image html axisticker-text.png</td></tr>
-  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerDateTime</td><td>\image html axisticker-datetime.png</td></tr>
-  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerTime</td><td>\image html axisticker-time.png
-    \image html axisticker-time2.png</td></tr>
-  </table>
-  </center>
-  
-  \section axisticker-subclassing Creating own axis tickers
-  
-  Creating own axis tickers can be achieved very easily by sublassing QCPAxisTicker and
-  reimplementing some or all of the available virtual methods.
 
-  In the simplest case you might wish to just generate different tick steps than the other tickers,
-  so you only reimplement the method \ref getTickStep. If you additionally want control over the
-  string that will be shown as tick label, reimplement \ref getTickLabel.
-  
-  If you wish to have complete control, you can generate the tick vectors and tick label vectors
-  yourself by reimplementing \ref createTickVector and \ref createLabelVector. The default
-  implementations use the previously mentioned virtual methods \ref getTickStep and \ref
-  getTickLabel, but your reimplementations don't necessarily need to do so. For example in the case
-  of unequal tick steps, the method \ref getTickStep loses its usefulness and can be ignored.
+/*! \class QCPLabelPainterPrivate
+
+  \internal
+  \brief (Private)
   
-  The sub tick count between major ticks can be controlled with \ref getSubTickCount. Full sub tick
-  placement control is obtained by reimplementing \ref createSubTickVector.
+  This is a private class and not part of the public QCustomPlot interface.
   
-  See the documentation of all these virtual methods in QCPAxisTicker for detailed information
-  about the parameters and expected return values.
 */
 
+const QChar QCPLabelPainterPrivate::SymbolDot(183);
+const QChar QCPLabelPainterPrivate::SymbolCross(215);
+
 /*!
-  Constructs the ticker and sets reasonable default values. Axis tickers are commonly created
-  managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.
+  Constructs a QCPLabelPainterPrivate instance. Make sure to not create a new
+  instance on every redraw, to utilize the caching mechanisms.
+  
+  the \a parentPlot does not take ownership of the label painter. Make sure
+  to delete it appropriately.
 */
-QCPAxisTicker::QCPAxisTicker() :
-  mTickStepStrategy(tssReadability),
-  mTickCount(5),
-  mTickOrigin(0)
+QCPLabelPainterPrivate::QCPLabelPainterPrivate(QCustomPlot *parentPlot) :
+  mAnchorMode(amRectangular),
+  mAnchorSide(asLeft),
+  mAnchorReferenceType(artNormal),
+  mColor(Qt::black),
+  mPadding(0),
+  mRotation(0),
+  mSubstituteExponent(true),
+  mMultiplicationSymbol(QChar(215)),
+  mAbbreviateDecimalPowers(false),
+  mParentPlot(parentPlot),
+  mLabelCache(16)
 {
+  analyzeFontMetrics();
 }
 
-QCPAxisTicker::~QCPAxisTicker()
+QCPLabelPainterPrivate::~QCPLabelPainterPrivate()
 {
-  
 }
 
-/*!
-  Sets which strategy the axis ticker follows when choosing the size of the tick step. For the
-  available strategies, see \ref TickStepStrategy.
-*/
-void QCPAxisTicker::setTickStepStrategy(QCPAxisTicker::TickStepStrategy strategy)
+void QCPLabelPainterPrivate::setAnchorSide(AnchorSide side)
 {
-  mTickStepStrategy = strategy;
+  mAnchorSide = side;
 }
 
-/*!
-  Sets how many ticks this ticker shall aim to generate across the axis range. Note that \a count
-  is not guaranteed to be matched exactly, as generating readable tick intervals may conflict with
-  the requested number of ticks.
+void QCPLabelPainterPrivate::setAnchorMode(AnchorMode mode)
+{
+  mAnchorMode = mode;
+}
 
-  Whether the readability has priority over meeting the requested \a count can be specified with
-  \ref setTickStepStrategy.
-*/
-void QCPAxisTicker::setTickCount(int count)
+void QCPLabelPainterPrivate::setAnchorReference(const QPointF &pixelPoint)
 {
-  if (count > 0)
-    mTickCount = count;
-  else
-    qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count;
+  mAnchorReference = pixelPoint;
 }
 
-/*!
-  Sets the mathematical coordinate (or "offset") of the zeroth tick. This tick coordinate is just a
-  concept and doesn't need to be inside the currently visible axis range.
-  
-  By default \a origin is zero, which for example yields ticks {-5, 0, 5, 10, 15,...} when the tick
-  step is five. If \a origin is now set to 1 instead, the correspondingly generated ticks would be
-  {-4, 1, 6, 11, 16,...}.
-*/
-void QCPAxisTicker::setTickOrigin(double origin)
+void QCPLabelPainterPrivate::setAnchorReferenceType(AnchorReferenceType type)
 {
-  mTickOrigin = origin;
+  mAnchorReferenceType = type;
 }
 
-/*!
-  This is the method called by QCPAxis in order to actually generate tick coordinates (\a ticks),
-  tick label strings (\a tickLabels) and sub tick coordinates (\a subTicks).
+void QCPLabelPainterPrivate::setFont(const QFont &font)
+{
+  if (mFont != font)
+  {
+    mFont = font;
+    analyzeFontMetrics();
+  }
+}
+
+void QCPLabelPainterPrivate::setColor(const QColor &color)
+{
+  mColor = color;
+}
+
+void QCPLabelPainterPrivate::setPadding(int padding)
+{
+  mPadding = padding;
+}
+
+void QCPLabelPainterPrivate::setRotation(double rotation)
+{
+  mRotation = qBound(-90.0, rotation, 90.0);
+}
+
+void QCPLabelPainterPrivate::setSubstituteExponent(bool enabled)
+{
+  mSubstituteExponent = enabled;
+}
+
+void QCPLabelPainterPrivate::setMultiplicationSymbol(QChar symbol)
+{
+  mMultiplicationSymbol = symbol;
+}
+
+void QCPLabelPainterPrivate::setAbbreviateDecimalPowers(bool enabled)
+{
+  mAbbreviateDecimalPowers = enabled;
+}
+
+void QCPLabelPainterPrivate::setCacheSize(int labelCount)
+{
+  mLabelCache.setMaxCost(labelCount);
+}
+
+int QCPLabelPainterPrivate::cacheSize() const
+{
+  return mLabelCache.maxCost();
+}
+
+void QCPLabelPainterPrivate::drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text)
+{
+  double realRotation = mRotation;
   
-  The ticks are generated for the specified \a range. The generated labels typically follow the
-  specified \a locale, \a formatChar and number \a precision, however this might be different (or
-  even irrelevant) for certain QCPAxisTicker subclasses.
+  AnchorSide realSide = mAnchorSide;
+  // for circular axes, the anchor side is determined depending on the quadrant of tickPos with respect to mCircularReference
+  if (mAnchorMode == amSkewedUpright)
+  {
+    realSide = skewedAnchorSide(tickPos, 0.2, 0.3); 
+  } else if (mAnchorMode == amSkewedRotated) // in this mode every label is individually rotated to match circle tangent
+  {
+    realSide = skewedAnchorSide(tickPos, 0, 0);
+    realRotation += QCPVector2D(tickPos-mAnchorReference).angle()/M_PI*180.0;
+    if (realRotation > 90) realRotation -= 180;
+    else if (realRotation < -90) realRotation += 180;
+  }
   
-  The output parameter \a ticks is filled with the generated tick positions in axis coordinates.
-  The output parameters \a subTicks and \a tickLabels are optional (set them to 0 if not needed)
-  and are respectively filled with sub tick coordinates, and tick label strings belonging to \a
-  ticks by index.
+  realSide = rotationCorrectedSide(realSide, realRotation); // rotation angles may change the true anchor side of the label
+  drawLabelMaybeCached(painter, mFont, mColor, getAnchorPos(tickPos), realSide, realRotation, text);
+}
+
+/*! \internal
+  
+  Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
+  direction) needed to fit the axis.
 */
-void QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector<double> &ticks, QVector<double> *subTicks, QVector<QString> *tickLabels)
+/* TODO: needed?
+int QCPLabelPainterPrivate::size() const
 {
-  // generate (major) ticks:
-  double tickStep = getTickStep(range);
-  ticks = createTickVector(tickStep, range);
-  trimTicks(range, ticks, true); // trim ticks to visible range plus one outer tick on each side (incase a subclass createTickVector creates more)
+  int result = 0;
+  // get length of tick marks pointing outwards:
+  if (!tickPositions.isEmpty())
+    result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
   
-  // generate sub ticks between major ticks:
-  if (subTicks)
+  // calculate size of tick labels:
+  if (tickLabelSide == QCPAxis::lsOutside)
   {
-    if (ticks.size() > 0)
+    QSize tickLabelsSize(0, 0);
+    if (!tickLabels.isEmpty())
     {
-      *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks);
-      trimTicks(range, *subTicks, false);
-    } else
-      *subTicks = QVector<double>();
+      for (int i=0; i<tickLabels.size(); ++i)
+        getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
+      result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
+    result += tickLabelPadding;
+    }
   }
   
-  // finally trim also outliers (no further clipping happens in axis drawing):
-  trimTicks(range, ticks, false);
-  // generate labels for visible ticks if requested:
-  if (tickLabels)
-    *tickLabels = createLabelVector(ticks, locale, formatChar, precision);
+  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
+  if (!label.isEmpty())
+  {
+    QFontMetrics fontMetrics(labelFont);
+    QRect bounds;
+    bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
+    result += bounds.height() + labelPadding;
+  }
+
+  return result;
 }
+*/
 
 /*! \internal
   
-  Takes the entire currently visible axis range and returns a sensible tick step in
-  order to provide readable tick labels as well as a reasonable number of tick counts (see \ref
-  setTickCount, \ref setTickStepStrategy).
-  
-  If a QCPAxisTicker subclass only wants a different tick step behaviour than the default
-  implementation, it should reimplement this method. See \ref cleanMantissa for a possible helper
-  function.
+  Clears the internal label cache. Upon the next \ref draw, all labels will be created new. This
+  method is called automatically if any parameters have changed that invalidate the cached labels,
+  such as font, color, etc. Usually you won't need to call this method manually.
 */
-double QCPAxisTicker::getTickStep(const QCPRange &range)
+void QCPLabelPainterPrivate::clearCache()
 {
-  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
-  return cleanMantissa(exactStep);
+  mLabelCache.clear();
 }
 
 /*! \internal
   
-  Takes the \a tickStep, i.e. the distance between two consecutive ticks, and returns
-  an appropriate number of sub ticks for that specific tick step.
-  
-  Note that a returned sub tick count of e.g. 4 will split each tick interval into 5 sections.
+  Returns a hash that allows uniquely identifying whether the label parameters have changed such
+  that the cached labels must be refreshed (\ref clearCache). It is used in \ref draw. If the
+  return value of this method hasn't changed since the last redraw, the respective label parameters
+  haven't changed and cached labels may be used.
 */
-int QCPAxisTicker::getSubTickCount(double tickStep)
+QByteArray QCPLabelPainterPrivate::generateLabelParameterHash() const
 {
-  int result = 1; // default to 1, if no proper value can be found
+  QByteArray result;
+  result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));
+  result.append(QByteArray::number(mRotation));
+  //result.append(QByteArray::number((int)tickLabelSide)); TODO: check whether this is really a cache-invalidating property
+  result.append(QByteArray::number((int)mSubstituteExponent));
+  result.append(QString(mMultiplicationSymbol).toUtf8());
+  result.append(mColor.name().toLatin1()+QByteArray::number(mColor.alpha(), 16));
+  result.append(mFont.toString().toLatin1());
+  return result;
+}
+
+/*! \internal
   
-  // separate integer and fractional part of mantissa:
-  double epsilon = 0.01;
-  double intPartf;
-  int intPart;
-  double fracPart = modf(getMantissa(tickStep), &intPartf);
-  intPart = intPartf;
+  Draws a single tick label with the provided \a painter, utilizing the internal label cache to
+  significantly speed up drawing of labels that were drawn in previous calls. The tick label is
+  always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
+  pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
+  for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
+  at which the label should be drawn.
   
-  // handle cases with (almost) integer mantissa:
-  if (fracPart < epsilon || 1.0-fracPart < epsilon)
+  In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
+  largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
+  drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
+  tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
+  holds.
+  
+  The label is drawn with the font and pen that are currently set on the \a painter. To draw
+  superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
+  getTickLabelData).
+*/
+void QCPLabelPainterPrivate::drawLabelMaybeCached(QCPPainter *painter, const QFont &font, const QColor &color, const QPointF &pos, AnchorSide side, double rotation, const QString &text)
+{
+  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
+  if (text.isEmpty()) return;
+  QSize finalSize;
+
+  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
   {
-    if (1.0-fracPart < epsilon)
-      ++intPart;
-    switch (intPart)
+    QByteArray key = cacheKey(text, color, rotation, side);
+    CachedLabel *cachedLabel = mLabelCache.take(QString::fromUtf8(key)); // attempt to take label from cache (don't use object() because we want ownership/prevent deletion during our operations, we re-insert it afterwards)
+    if (!cachedLabel)  // no cached label existed, create it
     {
-      case 1: result = 4; break; // 1.0 -> 0.2 substep
-      case 2: result = 3; break; // 2.0 -> 0.5 substep
-      case 3: result = 2; break; // 3.0 -> 1.0 substep
-      case 4: result = 3; break; // 4.0 -> 1.0 substep
-      case 5: result = 4; break; // 5.0 -> 1.0 substep
-      case 6: result = 2; break; // 6.0 -> 2.0 substep
-      case 7: result = 6; break; // 7.0 -> 1.0 substep
-      case 8: result = 3; break; // 8.0 -> 2.0 substep
-      case 9: result = 2; break; // 9.0 -> 3.0 substep
+      LabelData labelData = getTickLabelData(font, color, rotation, side, text);
+      cachedLabel = createCachedLabel(labelData);
     }
-  } else
+    // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
+    bool labelClippedByBorder = false;
+    /*
+    if (tickLabelSide == QCPAxis::lsOutside)
+    {
+      if (QCPAxis::orientation(type) == Qt::Horizontal)
+        labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
+      else
+        labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
+    }
+    */
+    if (!labelClippedByBorder)
+    {
+      painter->drawPixmap(pos+cachedLabel->offset, cachedLabel->pixmap);
+      finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio(); // TODO: collect this in a member rect list?
+    }
+    mLabelCache.insert(QString::fromUtf8(key), cachedLabel);
+  } else // label caching disabled, draw text directly on surface:
   {
-    // handle cases with significantly fractional mantissa:
-    if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
+    LabelData labelData = getTickLabelData(font, color, rotation, side, text);
+    // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
+     bool labelClippedByBorder = false;
+     /*
+    if (tickLabelSide == QCPAxis::lsOutside)
     {
-      switch (intPart)
+      if (QCPAxis::orientation(type) == Qt::Horizontal)
+        labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
+      else
+        labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
+    }
+    */
+    if (!labelClippedByBorder)
+    {
+      drawText(painter, pos, labelData);
+      finalSize = labelData.rotatedTotalBounds.size();
+    }
+  }
+  /*
+  // expand passed tickLabelsSize if current tick label is larger:
+  if (finalSize.width() > tickLabelsSize->width())
+    tickLabelsSize->setWidth(finalSize.width());
+  if (finalSize.height() > tickLabelsSize->height())
+    tickLabelsSize->setHeight(finalSize.height());
+  */
+}
+
+QPointF QCPLabelPainterPrivate::getAnchorPos(const QPointF &tickPos)
+{
+  switch (mAnchorMode)
+  {
+    case amRectangular:
+    {
+      switch (mAnchorSide)
       {
-        case 1: result = 2; break; // 1.5 -> 0.5 substep
-        case 2: result = 4; break; // 2.5 -> 0.5 substep
-        case 3: result = 4; break; // 3.5 -> 0.7 substep
-        case 4: result = 2; break; // 4.5 -> 1.5 substep
-        case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on)
-        case 6: result = 4; break; // 6.5 -> 1.3 substep
-        case 7: result = 2; break; // 7.5 -> 2.5 substep
-        case 8: result = 4; break; // 8.5 -> 1.7 substep
-        case 9: result = 4; break; // 9.5 -> 1.9 substep
+        case asLeft:   return tickPos+QPointF(mPadding, 0);
+        case asRight:  return tickPos+QPointF(-mPadding, 0);
+        case asTop:    return tickPos+QPointF(0, mPadding);
+        case asBottom: return tickPos+QPointF(0, -mPadding);
+        case asTopLeft:     return tickPos+QPointF(mPadding*M_SQRT1_2, mPadding*M_SQRT1_2);
+        case asTopRight:    return tickPos+QPointF(-mPadding*M_SQRT1_2, mPadding*M_SQRT1_2);
+        case asBottomRight: return tickPos+QPointF(-mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2);
+        case asBottomLeft:  return tickPos+QPointF(mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2);
       }
     }
-    // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default
+    case amSkewedUpright:
+    case amSkewedRotated:
+    {
+      QCPVector2D anchorNormal(tickPos-mAnchorReference);
+      if (mAnchorReferenceType == artTangent)
+        anchorNormal = anchorNormal.perpendicular();
+      anchorNormal.normalize();
+      return tickPos+(anchorNormal*mPadding).toPointF();
+    }
   }
-  
-  return result;
+  return tickPos;
 }
 
 /*! \internal
   
-  This method returns the tick label string as it should be printed under the \a tick coordinate.
-  If a textual number is returned, it should respect the provided \a locale, \a formatChar and \a
-  precision.
+  This is a \ref placeTickLabel helper function.
   
-  If the returned value contains exponentials of the form "2e5" and beautifully typeset powers is
-  enabled in the QCPAxis number format (\ref QCPAxis::setNumberFormat), the exponential part will
-  be formatted accordingly using multiplication symbol and superscript during rendering of the
-  label automatically.
+  Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
+  y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
+  directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
+  QCP::phCacheLabels plotting hint is not set.
 */
-QString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
+void QCPLabelPainterPrivate::drawText(QCPPainter *painter, const QPointF &pos, const LabelData &labelData) const
 {
-  return locale.toString(tick, formatChar.toLatin1(), precision);
+  // backup painter settings that we're about to change:
+  QTransform oldTransform = painter->transform();
+  QFont oldFont = painter->font();
+  QPen oldPen = painter->pen();
+  
+  // transform painter to position/rotation:
+  painter->translate(pos);
+  painter->setTransform(labelData.transform, true);
+  
+  // draw text:
+  painter->setFont(labelData.baseFont);
+  painter->setPen(QPen(labelData.color));
+  if (!labelData.expPart.isEmpty()) // use superscripted exponent typesetting
+  {
+    painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
+    if (!labelData.suffixPart.isEmpty())
+      painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart);
+    painter->setFont(labelData.expFont);
+    painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip,  labelData.expPart);
+  } else
+  {
+    painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
+  }
+  
+  /* Debug code to draw label bounding boxes, baseline, and capheight
+  painter->save();
+  painter->setPen(QPen(QColor(0, 0, 0, 150)));
+  painter->drawRect(labelData.totalBounds);
+  const int baseline = labelData.totalBounds.height()-mLetterDescent;
+  painter->setPen(QPen(QColor(255, 0, 0, 150)));
+  painter->drawLine(QLineF(0, baseline, labelData.totalBounds.width(), baseline));
+  painter->setPen(QPen(QColor(0, 0, 255, 150)));
+  painter->drawLine(QLineF(0, baseline-mLetterCapHeight, labelData.totalBounds.width(), baseline-mLetterCapHeight));
+  painter->restore();
+  */
+  
+  // reset painter settings to what it was before:
+  painter->setTransform(oldTransform);
+  painter->setFont(oldFont);
+  painter->setPen(oldPen);
 }
 
 /*! \internal
   
-  Returns a vector containing all coordinates of sub ticks that should be drawn. It generates \a
-  subTickCount sub ticks between each tick pair given in \a ticks.
+  This is a \ref placeTickLabel helper function.
   
-  If a QCPAxisTicker subclass needs maximal control over the generated sub ticks, it should
-  reimplement this method. Depending on the purpose of the subclass it doesn't necessarily need to
-  base its result on \a subTickCount or \a ticks.
+  Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
+  processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
+  exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.
 */
-QVector<double> QCPAxisTicker::createSubTickVector(int subTickCount, const QVector<double> &ticks)
+QCPLabelPainterPrivate::LabelData QCPLabelPainterPrivate::getTickLabelData(const QFont &font, const QColor &color, double rotation, AnchorSide side, const QString &text) const
 {
-  QVector<double> result;
-  if (subTickCount <= 0 || ticks.size() < 2)
-    return result;
+  LabelData result;
+  result.rotation = rotation;
+  result.side = side;
+  result.color = color;
   
-  result.reserve((ticks.size()-1)*subTickCount);
-  for (int i=1; i<ticks.size(); ++i)
+  // determine whether beautiful decimal powers should be used
+  bool useBeautifulPowers = false;
+  int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart
+  int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart
+  if (mSubstituteExponent)
   {
-    double subTickStep = (ticks.at(i)-ticks.at(i-1))/(double)(subTickCount+1);
-    for (int k=1; k<=subTickCount; ++k)
-      result.append(ticks.at(i-1) + k*subTickStep);
+    ePos = text.indexOf(QLatin1Char('e'));
+    if (ePos > 0 && text.at(ePos-1).isDigit())
+    {
+      eLast = ePos;
+      while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))
+        ++eLast;
+      if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power
+        useBeautifulPowers = true;
+    }
   }
-  return result;
-}
-
-/*! \internal
   
-  Returns a vector containing all coordinates of ticks that should be drawn. The default
-  implementation generates ticks with a spacing of \a tickStep (mathematically starting at the tick
-  step origin, see \ref setTickOrigin) distributed over the passed \a range.
+  // calculate text bounding rects and do string preparation for beautiful decimal powers:
+  result.baseFont = font;
+  if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
+    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
   
-  In order for the axis ticker to generate proper sub ticks, it is necessary that the first and
-  last tick coordinates returned by this method are just below/above the provided \a range.
-  Otherwise the outer intervals won't contain any sub ticks.
+  QFontMetrics baseFontMetrics(result.baseFont);
+  if (useBeautifulPowers)
+  {
+    // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
+    result.basePart = text.left(ePos);
+    result.suffixPart = text.mid(eLast+1); // also drawn normally but after exponent
+    // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
+    if (mAbbreviateDecimalPowers && result.basePart == QLatin1String("1"))
+      result.basePart = QLatin1String("10");
+    else
+      result.basePart += QString(mMultiplicationSymbol) + QLatin1String("10");
+    result.expPart = text.mid(ePos+1, eLast-ePos);
+    // clip "+" and leading zeros off expPart:
+    while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
+      result.expPart.remove(1, 1);
+    if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
+      result.expPart.remove(0, 1);
+    // prepare smaller font for exponent:
+    result.expFont = font;
+    if (result.expFont.pointSize() > 0)
+      result.expFont.setPointSize(result.expFont.pointSize()*0.75);
+    else
+      result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
+    // calculate bounding rects of base part(s), exponent part and total one:
+    result.baseBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
+    result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
+    if (!result.suffixPart.isEmpty())
+      result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart);
+    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
+  } else // useBeautifulPowers == false
+  {
+    result.basePart = text;
+    result.totalBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
+  }
+  result.totalBounds.moveTopLeft(QPoint(0, 0));
+  applyAnchorTransform(result);
+  result.rotatedTotalBounds = result.transform.mapRect(result.totalBounds);
   
-  If a QCPAxisTicker subclass needs maximal control over the generated ticks, it should reimplement
-  this method. Depending on the purpose of the subclass it doesn't necessarily need to base its
-  result on \a tickStep, e.g. when the ticks are spaced unequally like in the case of
-  QCPAxisTickerLog.
-*/
-QVector<double> QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range)
-{
-  QVector<double> result;
-  // Generate tick positions according to tickStep:
-  qint64 firstStep = floor((range.lower-mTickOrigin)/tickStep); // do not use qFloor here, or we'll lose 64 bit precision
-  qint64 lastStep = ceil((range.upper-mTickOrigin)/tickStep); // do not use qCeil here, or we'll lose 64 bit precision
-  int tickcount = lastStep-firstStep+1;
-  if (tickcount < 0) tickcount = 0;
-  result.resize(tickcount);
-  for (int i=0; i<tickcount; ++i)
-    result[i] = mTickOrigin + (firstStep+i)*tickStep;
   return result;
 }
 
-/*! \internal
+void QCPLabelPainterPrivate::applyAnchorTransform(LabelData &labelData) const
+{
+  if (!qFuzzyIsNull(labelData.rotation))
+    labelData.transform.rotate(labelData.rotation); // rotates effectively clockwise (due to flipped y axis of painter vs widget coordinate system)
   
-  Returns a vector containing all tick label strings corresponding to the tick coordinates provided
-  in \a ticks. The default implementation calls \ref getTickLabel to generate the respective
-  strings.
+  // from now on we translate in rotated label-local coordinate system.
+  // shift origin of coordinate system to appropriate point on label:
+  labelData.transform.translate(0, -labelData.totalBounds.height()+mLetterDescent+mLetterCapHeight); // shifts origin to true top of capital (or number) characters
   
-  It is possible but uncommon for QCPAxisTicker subclasses to reimplement this method, as
-  reimplementing \ref getTickLabel often achieves the intended result easier.
-*/
-QVector<QString> QCPAxisTicker::createLabelVector(const QVector<double> &ticks, const QLocale &locale, QChar formatChar, int precision)
-{
-  QVector<QString> result;
-  result.reserve(ticks.size());
-  for (int i=0; i<ticks.size(); ++i)
-    result.append(getTickLabel(ticks.at(i), locale, formatChar, precision));
-  return result;
+  if (labelData.side == asLeft || labelData.side == asRight) // anchor is centered vertically
+    labelData.transform.translate(0, -mLetterCapHeight/2.0);
+  else if (labelData.side == asTop || labelData.side == asBottom) // anchor is centered horizontally
+    labelData.transform.translate(-labelData.totalBounds.width()/2.0, 0);
+  
+  if (labelData.side == asTopRight || labelData.side == asRight || labelData.side == asBottomRight) // anchor is at right
+    labelData.transform.translate(-labelData.totalBounds.width(), 0);
+  if (labelData.side == asBottomLeft || labelData.side == asBottom || labelData.side == asBottomRight) // anchor is at bottom (no elseif!)
+    labelData.transform.translate(0, -mLetterCapHeight);
 }
 
 /*! \internal
   
-  Removes tick coordinates from \a ticks which lie outside the specified \a range. If \a
-  keepOneOutlier is true, it preserves one tick just outside the range on both sides, if present.
-  
-  The passed \a ticks must be sorted in ascending order.
+  Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
+  to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
+  margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
+  smaller width/height.
 */
-void QCPAxisTicker::trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const
+/*
+void QCPLabelPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text,  QSize *tickLabelsSize) const
 {
-  bool lowFound = false;
-  bool highFound = false;
-  int lowIndex = 0;
-  int highIndex = -1;
-  
-  for (int i=0; i < ticks.size(); ++i)
+  // note: this function must return the same tick label sizes as the placeTickLabel function.
+  QSize finalSize;
+  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
   {
-    if (ticks.at(i) >= range.lower)
-    {
-      lowFound = true;
-      lowIndex = i;
-      break;
-    }
-  }
-  for (int i=ticks.size()-1; i >= 0; --i)
+    const CachedLabel *cachedLabel = mLabelCache.object(text);
+    finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
+  } else // label caching disabled or no label with this text cached:
   {
-    if (ticks.at(i) <= range.upper)
-    {
-      highFound = true;
-      highIndex = i;
-      break;
-    }
+    // TODO: LabelData labelData = getTickLabelData(font, text);
+    // TODO: finalSize = labelData.rotatedTotalBounds.size();
   }
   
-  if (highFound && lowFound)
-  {
-    int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0));
-    int trimBack = qMax(0, ticks.size()-(keepOneOutlier ? 2 : 1)-highIndex);
-    if (trimFront > 0 || trimBack > 0)
-      ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack);
-  } else // all ticks are either all below or all above the range
-    ticks.clear();
+  // expand passed tickLabelsSize if current tick label is larger:
+  if (finalSize.width() > tickLabelsSize->width())
+    tickLabelsSize->setWidth(finalSize.width());
+  if (finalSize.height() > tickLabelsSize->height())
+    tickLabelsSize->setHeight(finalSize.height());
 }
+*/
 
-/*! \internal
-  
-  Returns the coordinate contained in \a candidates which is closest to the provided \a target.
+QCPLabelPainterPrivate::CachedLabel *QCPLabelPainterPrivate::createCachedLabel(const LabelData &labelData) const
+{
+  CachedLabel *result = new CachedLabel;
   
-  This method assumes \a candidates is not empty and sorted in ascending order.
-*/
-double QCPAxisTicker::pickClosest(double target, const QVector<double> &candidates) const
+  // allocate pixmap with the correct size and pixel ratio:
+  if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio()))
+  {
+    result->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio());
+#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
+#  ifdef QCP_DEVICEPIXELRATIO_FLOAT
+    result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF());
+#  else
+    result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio());
+#  endif
+#endif
+  } else
+    result->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
+  result->pixmap.fill(Qt::transparent);
+  
+  // draw the label into the pixmap
+  // offset is between label anchor and topleft of cache pixmap, so pixmap can be drawn at pos+offset to make the label anchor appear at pos.
+  // We use rotatedTotalBounds.topLeft() because rotatedTotalBounds is in a coordinate system where the label anchor is at (0, 0)
+  result->offset = labelData.rotatedTotalBounds.topLeft();
+  QCPPainter cachePainter(&result->pixmap);
+  drawText(&cachePainter, -result->offset, labelData);
+  return result;
+}
+
+QByteArray QCPLabelPainterPrivate::cacheKey(const QString &text, const QColor &color, double rotation, AnchorSide side) const
 {
-  if (candidates.size() == 1)
-    return candidates.first();
-  QVector<double>::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target);
-  if (it == candidates.constEnd())
-    return *(it-1);
-  else if (it == candidates.constBegin())
-    return *it;
-  else
-    return target-*(it-1) < *it-target ? *(it-1) : *it;
+  return text.toUtf8()+
+      QByteArray::number(color.red()+256*color.green()+65536*color.blue(), 36)+
+      QByteArray::number(color.alpha()+256*(int)side, 36)+
+      QByteArray::number((int)(rotation*100)%36000, 36);
 }
 
-/*! \internal
-  
-  Returns the decimal mantissa of \a input. Optionally, if \a magnitude is not set to zero, it also
-  returns the magnitude of \a input as a power of 10.
-  
-  For example, an input of 142.6 will return a mantissa of 1.426 and a magnitude of 100.
-*/
-double QCPAxisTicker::getMantissa(double input, double *magnitude) const
+QCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::skewedAnchorSide(const QPointF &tickPos, double sideExpandHorz, double sideExpandVert) const
 {
-  const double mag = qPow(10.0, qFloor(qLn(input)/qLn(10.0)));
-  if (magnitude) *magnitude = mag;
-  return input/mag;
+  QCPVector2D anchorNormal = QCPVector2D(tickPos-mAnchorReference);
+  if (mAnchorReferenceType == artTangent)
+    anchorNormal = anchorNormal.perpendicular();
+  const double radius = anchorNormal.length();
+  const double sideHorz = sideExpandHorz*radius;
+  const double sideVert = sideExpandVert*radius;
+  if (anchorNormal.x() > sideHorz)
+  {
+    if (anchorNormal.y() > sideVert) return asTopLeft;
+    else if (anchorNormal.y() < -sideVert) return asBottomLeft;
+    else return asLeft;
+  } else if (anchorNormal.x() < -sideHorz)
+  {
+    if (anchorNormal.y() > sideVert) return asTopRight;
+    else if (anchorNormal.y() < -sideVert) return asBottomRight;
+    else return asRight;
+  } else
+  {
+    if (anchorNormal.y() > 0) return asTop;
+    else return asBottom;
+  }
+  return asBottom; // should never be reached
 }
 
-/*! \internal
-  
-  Returns a number that is close to \a input but has a clean, easier human readable mantissa. How
-  strongly the mantissa is altered, and thus how strong the result deviates from the original \a
-  input, depends on the current tick step strategy (see \ref setTickStepStrategy).
-*/
-double QCPAxisTicker::cleanMantissa(double input) const
+QCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::rotationCorrectedSide(AnchorSide side, double rotation) const
 {
-  double magnitude;
-  const double mantissa = getMantissa(input, &magnitude);
-  switch (mTickStepStrategy)
+  AnchorSide result = side;
+  const bool rotateClockwise = rotation > 0;
+  if (!qFuzzyIsNull(rotation))
   {
-    case tssReadability:
+    if (!qFuzzyCompare(qAbs(rotation), 90)) // avoid graphical collision with anchor tangent (e.g. axis line) when rotating, so change anchor side appropriately:
     {
-      return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude;
-    }
-    case tssMeetTickCount:
+      if (side == asTop) result = rotateClockwise ? asLeft : asRight;
+      else if (side == asBottom) result = rotateClockwise ? asRight : asLeft;
+      else if (side == asTopLeft) result = rotateClockwise ? asLeft : asTop;
+      else if (side == asTopRight) result = rotateClockwise ? asTop : asRight;
+      else if (side == asBottomLeft) result = rotateClockwise ? asBottom : asLeft;
+      else if (side == asBottomRight) result = rotateClockwise ? asRight : asBottom;
+    } else // for full rotation by +/-90 degrees, other sides are more appropriate for centering on anchor:
     {
-      // 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
-      if (mantissa <= 5.0)
-        return (int)(mantissa*2)/2.0*magnitude; // round digit after decimal point to 0.5
-      else
-        return (int)(mantissa/2.0)*2.0*magnitude; // round to first digit in multiples of 2
+      if (side == asLeft) result = rotateClockwise ? asBottom : asTop;
+      else if (side == asRight) result = rotateClockwise ? asTop : asBottom;
+      else if (side == asTop) result = rotateClockwise ? asLeft : asRight;
+      else if (side == asBottom) result = rotateClockwise ? asRight : asLeft;
+      else if (side == asTopLeft) result = rotateClockwise ? asBottomLeft : asTopRight;
+      else if (side == asTopRight) result = rotateClockwise ? asTopLeft : asBottomRight;
+      else if (side == asBottomLeft) result = rotateClockwise ? asBottomRight : asTopLeft;
+      else if (side == asBottomRight) result = rotateClockwise ? asTopRight : asBottomLeft;
     }
   }
-  return input;
+  return result;
 }
-/* end of 'src/axis/axisticker.cpp' */
 
+void QCPLabelPainterPrivate::analyzeFontMetrics()
+{
+  const QFontMetrics fm(mFont);
+  mLetterCapHeight = fm.tightBoundingRect(QLatin1String("8")).height(); // this method is slow, that's why we query it only upon font change
+  mLetterDescent = fm.descent();
+}
+/* end of 'src/axis/labelpainter.cpp' */
 
-/* including file 'src/axis/axistickerdatetime.cpp', size 14443              */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+
+/* including file 'src/axis/axisticker.cpp' */
+/* modified 2021-03-29T02:30:44, size 18688 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////// QCPAxisTickerDateTime
+//////////////////// QCPAxisTicker
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-/*! \class QCPAxisTickerDateTime
-  \brief Specialized axis ticker for calendar dates and times as axis ticks
+/*! \class QCPAxisTicker
+  \brief The base class tick generator used by QCPAxis to create tick positions and tick labels
   
-  \image html axisticker-datetime.png
+  Each QCPAxis has an internal QCPAxisTicker (or a subclass) in order to generate tick positions
+  and tick labels for the current axis range. The ticker of an axis can be set via \ref
+  QCPAxis::setTicker. Since that method takes a <tt>QSharedPointer<QCPAxisTicker></tt>, multiple
+  axes can share the same ticker instance.
   
-  This QCPAxisTicker subclass generates ticks that correspond to real calendar dates and times. The
-  plot axis coordinate is interpreted as Unix Time, so seconds since Epoch (January 1, 1970, 00:00
-  UTC). This is also used for example by QDateTime in the <tt>toTime_t()/setTime_t()</tt> methods
-  with a precision of one second. Since Qt 4.7, millisecond accuracy can be obtained from QDateTime
-  by using <tt>QDateTime::fromMSecsSinceEpoch()/1000.0</tt>. The static methods \ref dateTimeToKey
-  and \ref keyToDateTime conveniently perform this conversion achieving a precision of one
-  millisecond on all Qt versions.
+  This base class generates normal tick coordinates and numeric labels for linear axes. It picks a
+  reasonable tick step (the separation between ticks) which results in readable tick labels. The
+  number of ticks that should be approximately generated can be set via \ref setTickCount.
+  Depending on the current tick step strategy (\ref setTickStepStrategy), the algorithm either
+  sacrifices readability to better match the specified tick count (\ref
+  QCPAxisTicker::tssMeetTickCount) or relaxes the tick count in favor of better tick steps (\ref
+  QCPAxisTicker::tssReadability), which is the default.
   
-  The format of the date/time display in the tick labels is controlled with \ref setDateTimeFormat.
-  If a different time spec (time zone) shall be used, see \ref setDateTimeSpec.
+  The following more specialized axis ticker subclasses are available, see details in the
+  respective class documentation:
   
-  This ticker produces unequal tick spacing in order to provide intuitive date and time-of-day
-  ticks. For example, if the axis range spans a few years such that there is one tick per year,
-  ticks will be positioned on 1. January of every year. This is intuitive but, due to leap years,
-  will result in slightly unequal tick intervals (visually unnoticeable). The same can be seen in
-  the image above: even though the number of days varies month by month, this ticker generates
-  ticks on the same day of each month.
+  <center>
+  <table>
+  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerFixed</td><td>\image html axisticker-fixed.png</td></tr>
+  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerLog</td><td>\image html axisticker-log.png</td></tr>
+  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerPi</td><td>\image html axisticker-pi.png</td></tr>
+  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerText</td><td>\image html axisticker-text.png</td></tr>
+  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerDateTime</td><td>\image html axisticker-datetime.png</td></tr>
+  <tr><td style="text-align:right; padding: 0 1em">QCPAxisTickerTime</td><td>\image html axisticker-time.png
+    \image html axisticker-time2.png</td></tr>
+  </table>
+  </center>
   
-  If you would like to change the date/time that is used as a (mathematical) starting date for the
-  ticks, use the \ref setTickOrigin(const QDateTime &origin) method overload, which takes a
-  QDateTime. If you pass 15. July, 9:45 to this method, the yearly ticks will end up on 15. July at
-  9:45 of every year.
+  \section axisticker-subclassing Creating own axis tickers
   
-  The ticker can be created and assigned to an axis like this:
-  \snippet documentation/doc-image-generator/mainwindow.cpp axistickerdatetime-creation
+  Creating own axis tickers can be achieved very easily by sublassing QCPAxisTicker and
+  reimplementing some or all of the available virtual methods.
+
+  In the simplest case you might wish to just generate different tick steps than the other tickers,
+  so you only reimplement the method \ref getTickStep. If you additionally want control over the
+  string that will be shown as tick label, reimplement \ref getTickLabel.
   
-  \note If you rather wish to display relative times in terms of days, hours, minutes, seconds and
-  milliseconds, and are not interested in the intricacies of real calendar dates with months and
-  (leap) years, have a look at QCPAxisTickerTime instead.
+  If you wish to have complete control, you can generate the tick vectors and tick label vectors
+  yourself by reimplementing \ref createTickVector and \ref createLabelVector. The default
+  implementations use the previously mentioned virtual methods \ref getTickStep and \ref
+  getTickLabel, but your reimplementations don't necessarily need to do so. For example in the case
+  of unequal tick steps, the method \ref getTickStep loses its usefulness and can be ignored.
+  
+  The sub tick count between major ticks can be controlled with \ref getSubTickCount. Full sub tick
+  placement control is obtained by reimplementing \ref createSubTickVector.
+  
+  See the documentation of all these virtual methods in QCPAxisTicker for detailed information
+  about the parameters and expected return values.
 */
 
 /*!
   Constructs the ticker and sets reasonable default values. Axis tickers are commonly created
   managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.
 */
-QCPAxisTickerDateTime::QCPAxisTickerDateTime() :
-  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
-  mDateTimeSpec(Qt::LocalTime),
-  mDateStrategy(dsNone)
+QCPAxisTicker::QCPAxisTicker() :
+  mTickStepStrategy(tssReadability),
+  mTickCount(5),
+  mTickOrigin(0)
 {
-  setTickCount(4);
 }
 
-/*!
-  Sets the format in which dates and times are displayed as tick labels. For details about the \a
-  format string, see the documentation of QDateTime::toString().
-  
-  Newlines can be inserted with "\n".
+QCPAxisTicker::~QCPAxisTicker()
+{
   
-  \see setDateTimeSpec
+}
+
+/*!
+  Sets which strategy the axis ticker follows when choosing the size of the tick step. For the
+  available strategies, see \ref TickStepStrategy.
 */
-void QCPAxisTickerDateTime::setDateTimeFormat(const QString &format)
+void QCPAxisTicker::setTickStepStrategy(QCPAxisTicker::TickStepStrategy strategy)
 {
-  mDateTimeFormat = format;
+  mTickStepStrategy = strategy;
 }
 
 /*!
-  Sets the time spec that is used for creating the tick labels from corresponding dates/times.
+  Sets how many ticks this ticker shall aim to generate across the axis range. Note that \a count
+  is not guaranteed to be matched exactly, as generating readable tick intervals may conflict with
+  the requested number of ticks.
 
-  The default value of QDateTime objects (and also QCPAxisTickerDateTime) is
-  <tt>Qt::LocalTime</tt>. However, if the date time values passed to QCustomPlot (e.g. in the form
-  of axis ranges or keys of a plottable) are given in the UTC spec, set \a spec to <tt>Qt::UTC</tt>
-  to get the correct axis labels.
-  
-  \see setDateTimeFormat
+  Whether the readability has priority over meeting the requested \a count can be specified with
+  \ref setTickStepStrategy.
 */
-void QCPAxisTickerDateTime::setDateTimeSpec(Qt::TimeSpec spec)
+void QCPAxisTicker::setTickCount(int count)
 {
-  mDateTimeSpec = spec;
+  if (count > 0)
+    mTickCount = count;
+  else
+    qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count;
 }
 
 /*!
-  Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) in seconds since Epoch (1. Jan 1970,
-  00:00 UTC). For the date time ticker it might be more intuitive to use the overload which
-  directly takes a QDateTime, see \ref setTickOrigin(const QDateTime &origin).
+  Sets the mathematical coordinate (or "offset") of the zeroth tick. This tick coordinate is just a
+  concept and doesn't need to be inside the currently visible axis range.
   
-  This is useful to define the month/day/time recurring at greater tick interval steps. For
-  example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick
-  per year, the ticks will end up on 15. July at 9:45 of every year.
+  By default \a origin is zero, which for example yields ticks {-5, 0, 5, 10, 15,...} when the tick
+  step is five. If \a origin is now set to 1 instead, the correspondingly generated ticks would be
+  {-4, 1, 6, 11, 16,...}.
 */
-void QCPAxisTickerDateTime::setTickOrigin(double origin)
+void QCPAxisTicker::setTickOrigin(double origin)
 {
-  QCPAxisTicker::setTickOrigin(origin);
+  mTickOrigin = origin;
 }
 
 /*!
-  Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) as a QDateTime \a origin.
+  This is the method called by QCPAxis in order to actually generate tick coordinates (\a ticks),
+  tick label strings (\a tickLabels) and sub tick coordinates (\a subTicks).
   
-  This is useful to define the month/day/time recurring at greater tick interval steps. For
-  example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick
-  per year, the ticks will end up on 15. July at 9:45 of every year.
+  The ticks are generated for the specified \a range. The generated labels typically follow the
+  specified \a locale, \a formatChar and number \a precision, however this might be different (or
+  even irrelevant) for certain QCPAxisTicker subclasses.
+  
+  The output parameter \a ticks is filled with the generated tick positions in axis coordinates.
+  The output parameters \a subTicks and \a tickLabels are optional (set them to \c nullptr if not
+  needed) and are respectively filled with sub tick coordinates, and tick label strings belonging
+  to \a ticks by index.
 */
-void QCPAxisTickerDateTime::setTickOrigin(const QDateTime &origin)
+void QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector<double> &ticks, QVector<double> *subTicks, QVector<QString> *tickLabels)
 {
-  setTickOrigin(dateTimeToKey(origin));
+  // generate (major) ticks:
+  double tickStep = getTickStep(range);
+  ticks = createTickVector(tickStep, range);
+  trimTicks(range, ticks, true); // trim ticks to visible range plus one outer tick on each side (incase a subclass createTickVector creates more)
+  
+  // generate sub ticks between major ticks:
+  if (subTicks)
+  {
+    if (!ticks.isEmpty())
+    {
+      *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks);
+      trimTicks(range, *subTicks, false);
+    } else
+      *subTicks = QVector<double>();
+  }
+  
+  // finally trim also outliers (no further clipping happens in axis drawing):
+  trimTicks(range, ticks, false);
+  // generate labels for visible ticks if requested:
+  if (tickLabels)
+    *tickLabels = createLabelVector(ticks, locale, formatChar, precision);
 }
 
 /*! \internal
   
-  Returns a sensible tick step with intervals appropriate for a date-time-display, such as weekly,
-  monthly, bi-monthly, etc.
+  Takes the entire currently visible axis range and returns a sensible tick step in
+  order to provide readable tick labels as well as a reasonable number of tick counts (see \ref
+  setTickCount, \ref setTickStepStrategy).
   
-  Note that this tick step isn't used exactly when generating the tick vector in \ref
-  createTickVector, but only as a guiding value requiring some correction for each individual tick
-  interval. Otherwise this would lead to unintuitive date displays, e.g. jumping between first day
-  in the month to the last day in the previous month from tick to tick, due to the non-uniform
-  length of months. The same problem arises with leap years.
-  
-  \seebaseclassmethod
+  If a QCPAxisTicker subclass only wants a different tick step behaviour than the default
+  implementation, it should reimplement this method. See \ref cleanMantissa for a possible helper
+  function.
 */
-double QCPAxisTickerDateTime::getTickStep(const QCPRange &range)
+double QCPAxisTicker::getTickStep(const QCPRange &range)
 {
-  double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
-  
-  mDateStrategy = dsNone;
-  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds
-  {
-    result = cleanMantissa(result);
-  } else if (result < 86400*30.4375*12) // below a year
-  {
-    result = pickClosest(result, QVector<double>()
-                             << 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
-                             << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24 // hour to day range
-                             << 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)
-    if (result > 86400*30.4375-1) // month tick intervals or larger
-      mDateStrategy = dsUniformDayInMonth;
-    else if (result > 3600*24-1) // day tick intervals or larger
-      mDateStrategy = dsUniformTimeInDay;
-  } else // more than a year, go back to normal clean mantissa algorithm but in units of years
-  {
-    const double secondsPerYear = 86400*30.4375*12; // average including leap years
-    result = cleanMantissa(result/secondsPerYear)*secondsPerYear;
-    mDateStrategy = dsUniformDayInMonth;
-  }
-  return result;
+  double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
+  return cleanMantissa(exactStep);
 }
 
 /*! \internal
   
-  Returns a sensible sub tick count with intervals appropriate for a date-time-display, such as weekly,
-  monthly, bi-monthly, etc.
+  Takes the \a tickStep, i.e. the distance between two consecutive ticks, and returns
+  an appropriate number of sub ticks for that specific tick step.
   
-  \seebaseclassmethod
+  Note that a returned sub tick count of e.g. 4 will split each tick interval into 5 sections.
 */
-int QCPAxisTickerDateTime::getSubTickCount(double tickStep)
+int QCPAxisTicker::getSubTickCount(double tickStep)
 {
-  int result = QCPAxisTicker::getSubTickCount(tickStep);
-  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day/week/month range (as specified in getTickStep)
+  int result = 1; // default to 1, if no proper value can be found
+  
+  // separate integer and fractional part of mantissa:
+  double epsilon = 0.01;
+  double intPartf;
+  int intPart;
+  double fracPart = modf(getMantissa(tickStep), &intPartf);
+  intPart = int(intPartf);
+  
+  // handle cases with (almost) integer mantissa:
+  if (fracPart < epsilon || 1.0-fracPart < epsilon)
   {
-    case 5*60: result = 4; break;
-    case 10*60: result = 1; break;
-    case 15*60: result = 2; break;
-    case 30*60: result = 1; break;
-    case 60*60: result = 3; break;
-    case 3600*2: result = 3; break;
-    case 3600*3: result = 2; break;
-    case 3600*6: result = 1; break;
-    case 3600*12: result = 3; break;
-    case 3600*24: result = 3; break;
-    case 86400*2: result = 1; break;
-    case 86400*5: result = 4; break;
-    case 86400*7: result = 6; break;
-    case 86400*14: result = 1; break;
-    case (int)(86400*30.4375+0.5): result = 3; break;
-    case (int)(86400*30.4375*2+0.5): result = 1; break;
-    case (int)(86400*30.4375*3+0.5): result = 2; break;
-    case (int)(86400*30.4375*6+0.5): result = 5; break;
-    case (int)(86400*30.4375*12+0.5): result = 3; break;
+    if (1.0-fracPart < epsilon)
+      ++intPart;
+    switch (intPart)
+    {
+      case 1: result = 4; break; // 1.0 -> 0.2 substep
+      case 2: result = 3; break; // 2.0 -> 0.5 substep
+      case 3: result = 2; break; // 3.0 -> 1.0 substep
+      case 4: result = 3; break; // 4.0 -> 1.0 substep
+      case 5: result = 4; break; // 5.0 -> 1.0 substep
+      case 6: result = 2; break; // 6.0 -> 2.0 substep
+      case 7: result = 6; break; // 7.0 -> 1.0 substep
+      case 8: result = 3; break; // 8.0 -> 2.0 substep
+      case 9: result = 2; break; // 9.0 -> 3.0 substep
+    }
+  } else
+  {
+    // handle cases with significantly fractional mantissa:
+    if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
+    {
+      switch (intPart)
+      {
+        case 1: result = 2; break; // 1.5 -> 0.5 substep
+        case 2: result = 4; break; // 2.5 -> 0.5 substep
+        case 3: result = 4; break; // 3.5 -> 0.7 substep
+        case 4: result = 2; break; // 4.5 -> 1.5 substep
+        case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on)
+        case 6: result = 4; break; // 6.5 -> 1.3 substep
+        case 7: result = 2; break; // 7.5 -> 2.5 substep
+        case 8: result = 4; break; // 8.5 -> 1.7 substep
+        case 9: result = 4; break; // 9.5 -> 1.9 substep
+      }
+    }
+    // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default
   }
+  
   return result;
 }
 
 /*! \internal
   
-  Generates a date/time tick label for tick coordinate \a tick, based on the currently set format
-  (\ref setDateTimeFormat) and time spec (\ref setDateTimeSpec).
+  This method returns the tick label string as it should be printed under the \a tick coordinate.
+  If a textual number is returned, it should respect the provided \a locale, \a formatChar and \a
+  precision.
   
-  \seebaseclassmethod
+  If the returned value contains exponentials of the form "2e5" and beautifully typeset powers is
+  enabled in the QCPAxis number format (\ref QCPAxis::setNumberFormat), the exponential part will
+  be formatted accordingly using multiplication symbol and superscript during rendering of the
+  label automatically.
 */
-QString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
+QString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
 {
-  Q_UNUSED(precision)
-  Q_UNUSED(formatChar)
-  return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
+  return locale.toString(tick, formatChar.toLatin1(), precision);
 }
 
 /*! \internal
   
-  Uses the passed \a tickStep as a guiding value and applies corrections in order to obtain
-  non-uniform tick intervals but intuitive tick labels, e.g. falling on the same day of each month.
+  Returns a vector containing all coordinates of sub ticks that should be drawn. It generates \a
+  subTickCount sub ticks between each tick pair given in \a ticks.
   
-  \seebaseclassmethod
+  If a QCPAxisTicker subclass needs maximal control over the generated sub ticks, it should
+  reimplement this method. Depending on the purpose of the subclass it doesn't necessarily need to
+  base its result on \a subTickCount or \a ticks.
 */
-QVector<double> QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range)
+QVector<double> QCPAxisTicker::createSubTickVector(int subTickCount, const QVector<double> &ticks)
 {
-  QVector<double> result = QCPAxisTicker::createTickVector(tickStep, range);
-  if (!result.isEmpty())
+  QVector<double> result;
+  if (subTickCount <= 0 || ticks.size() < 2)
+    return result;
+  
+  result.reserve((ticks.size()-1)*subTickCount);
+  for (int i=1; i<ticks.size(); ++i)
   {
-    if (mDateStrategy == dsUniformTimeInDay)
-    {
-      QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // the time of this datetime will be set for all other ticks, if possible
-      QDateTime tickDateTime;
-      for (int i=0; i<result.size(); ++i)
-      {
-        tickDateTime = keyToDateTime(result.at(i));
-        tickDateTime.setTime(uniformDateTime.time());
-        result[i] = dateTimeToKey(tickDateTime);
-      }
-    } else if (mDateStrategy == dsUniformDayInMonth)
-    {
-      QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // this day (in month) and time will be set for all other ticks, if possible
-      QDateTime tickDateTime;
-      for (int i=0; i<result.size(); ++i)
-      {
-        tickDateTime = keyToDateTime(result.at(i));
-        tickDateTime.setTime(uniformDateTime.time());
-        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)
-        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
-          tickDateTime = tickDateTime.addMonths(1);
-        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
-          tickDateTime = tickDateTime.addMonths(-1);
-        tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay));
-        result[i] = dateTimeToKey(tickDateTime);
-      }
-    }
+    double subTickStep = (ticks.at(i)-ticks.at(i-1))/double(subTickCount+1);
+    for (int k=1; k<=subTickCount; ++k)
+      result.append(ticks.at(i-1) + k*subTickStep);
   }
   return result;
 }
 
-/*!
-  A convenience method which turns \a key (in seconds since Epoch 1. Jan 1970, 00:00 UTC) into a
-  QDateTime object. This can be used to turn axis coordinates to actual QDateTimes.
+/*! \internal
   
-  The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it
-  works around the lack of a QDateTime::fromMSecsSinceEpoch in Qt 4.6)
+  Returns a vector containing all coordinates of ticks that should be drawn. The default
+  implementation generates ticks with a spacing of \a tickStep (mathematically starting at the tick
+  step origin, see \ref setTickOrigin) distributed over the passed \a range.
   
-  \see dateTimeToKey
+  In order for the axis ticker to generate proper sub ticks, it is necessary that the first and
+  last tick coordinates returned by this method are just below/above the provided \a range.
+  Otherwise the outer intervals won't contain any sub ticks.
+  
+  If a QCPAxisTicker subclass needs maximal control over the generated ticks, it should reimplement
+  this method. Depending on the purpose of the subclass it doesn't necessarily need to base its
+  result on \a tickStep, e.g. when the ticks are spaced unequally like in the case of
+  QCPAxisTickerLog.
 */
-QDateTime QCPAxisTickerDateTime::keyToDateTime(double key)
+QVector<double> QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range)
 {
-# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
-  return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000);
-# else
-  return QDateTime::fromMSecsSinceEpoch(key*1000.0);
-# endif
+  QVector<double> result;
+  // Generate tick positions according to tickStep:
+  qint64 firstStep = qint64(floor((range.lower-mTickOrigin)/tickStep)); // do not use qFloor here, or we'll lose 64 bit precision
+  qint64 lastStep = qint64(ceil((range.upper-mTickOrigin)/tickStep)); // do not use qCeil here, or we'll lose 64 bit precision
+  int tickcount = int(lastStep-firstStep+1);
+  if (tickcount < 0) tickcount = 0;
+  result.resize(tickcount);
+  for (int i=0; i<tickcount; ++i)
+    result[i] = mTickOrigin + (firstStep+i)*tickStep;
+  return result;
 }
 
-/*! \overload
-  
-  A convenience method which turns a QDateTime object into a double value that corresponds to
-  seconds since Epoch (1. Jan 1970, 00:00 UTC). This is the format used as axis coordinates by
-  QCPAxisTickerDateTime.
+/*! \internal
   
-  The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it
-  works around the lack of a QDateTime::toMSecsSinceEpoch in Qt 4.6)
+  Returns a vector containing all tick label strings corresponding to the tick coordinates provided
+  in \a ticks. The default implementation calls \ref getTickLabel to generate the respective
+  strings.
   
-  \see keyToDateTime
+  It is possible but uncommon for QCPAxisTicker subclasses to reimplement this method, as
+  reimplementing \ref getTickLabel often achieves the intended result easier.
 */
-double QCPAxisTickerDateTime::dateTimeToKey(const QDateTime dateTime)
+QVector<QString> QCPAxisTicker::createLabelVector(const QVector<double> &ticks, const QLocale &locale, QChar formatChar, int precision)
 {
-# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
-  return dateTime.toTime_t()+dateTime.time().msec()/1000.0;
-# else
-  return dateTime.toMSecsSinceEpoch()/1000.0;
-# endif
+  QVector<QString> result;
+  result.reserve(ticks.size());
+  foreach (double tickCoord, ticks)
+    result.append(getTickLabel(tickCoord, locale, formatChar, precision));
+  return result;
 }
 
-/*! \overload
+/*! \internal
   
-  A convenience method which turns a QDate object into a double value that corresponds to
-  seconds since Epoch (1. Jan 1970, 00:00 UTC). This is the format used as axis coordinates by
-  QCPAxisTickerDateTime.
+  Removes tick coordinates from \a ticks which lie outside the specified \a range. If \a
+  keepOneOutlier is true, it preserves one tick just outside the range on both sides, if present.
   
-  \see keyToDateTime
+  The passed \a ticks must be sorted in ascending order.
 */
-double QCPAxisTickerDateTime::dateTimeToKey(const QDate date)
+void QCPAxisTicker::trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const
 {
-# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
-  return QDateTime(date).toTime_t();
-# else
-  return QDateTime(date).toMSecsSinceEpoch()/1000.0;
-# endif
-}
-/* end of 'src/axis/axistickerdatetime.cpp' */
-
-
-/* including file 'src/axis/axistickertime.cpp', size 11747                  */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////// QCPAxisTickerTime
-////////////////////////////////////////////////////////////////////////////////////////////////////
-/*! \class QCPAxisTickerTime
-  \brief Specialized axis ticker for time spans in units of milliseconds to days
-  
-  \image html axisticker-time.png
-  
-  This QCPAxisTicker subclass generates ticks that corresponds to time intervals.
-  
-  The format of the time display in the tick labels is controlled with \ref setTimeFormat and \ref
-  setFieldWidth. The time coordinate is in the unit of seconds with respect to the time coordinate
-  zero. Unlike with QCPAxisTickerDateTime, the ticks don't correspond to a specific calendar date
-  and time.
+  bool lowFound = false;
+  bool highFound = false;
+  int lowIndex = 0;
+  int highIndex = -1;
   
-  The time can be displayed in milliseconds, seconds, minutes, hours and days. Depending on the
-  largest available unit in the format specified with \ref setTimeFormat, any time spans above will
-  be carried in that largest unit. So for example if the format string is "%m:%s" and a tick at
-  coordinate value 7815 (being 2 hours, 10 minutes and 15 seconds) is created, the resulting tick
-  label will show "130:15" (130 minutes, 15 seconds). If the format string is "%h:%m:%s", the hour
-  unit will be used and the label will thus be "02:10:15". Negative times with respect to the axis
-  zero will carry a leading minus sign.
+  for (int i=0; i < ticks.size(); ++i)
+  {
+    if (ticks.at(i) >= range.lower)
+    {
+      lowFound = true;
+      lowIndex = i;
+      break;
+    }
+  }
+  for (int i=ticks.size()-1; i >= 0; --i)
+  {
+    if (ticks.at(i) <= range.upper)
+    {
+      highFound = true;
+      highIndex = i;
+      break;
+    }
+  }
   
-  The ticker can be created and assigned to an axis like this:
-  \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation
+  if (highFound && lowFound)
+  {
+    int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0));
+    int trimBack = qMax(0, ticks.size()-(keepOneOutlier ? 2 : 1)-highIndex);
+    if (trimFront > 0 || trimBack > 0)
+      ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack);
+  } else // all ticks are either all below or all above the range
+    ticks.clear();
+}
+
+/*! \internal
   
-  Here is an example of a time axis providing time information in days, hours and minutes. Due to
-  the axis range spanning a few days and the wanted tick count (\ref setTickCount), the ticker
-  decided to use tick steps of 12 hours:
+  Returns the coordinate contained in \a candidates which is closest to the provided \a target.
   
-  \image html axisticker-time2.png
+  This method assumes \a candidates is not empty and sorted in ascending order.
+*/
+double QCPAxisTicker::pickClosest(double target, const QVector<double> &candidates) const
+{
+  if (candidates.size() == 1)
+    return candidates.first();
+  QVector<double>::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target);
+  if (it == candidates.constEnd())
+    return *(it-1);
+  else if (it == candidates.constBegin())
+    return *it;
+  else
+    return target-*(it-1) < *it-target ? *(it-1) : *it;
+}
+
+/*! \internal
   
-  The format string for this example is
-  \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation-2
+  Returns the decimal mantissa of \a input. Optionally, if \a magnitude is not set to zero, it also
+  returns the magnitude of \a input as a power of 10.
   
-  \note If you rather wish to display calendar dates and times, have a look at QCPAxisTickerDateTime
-  instead.
+  For example, an input of 142.6 will return a mantissa of 1.426 and a magnitude of 100.
+*/
+double QCPAxisTicker::getMantissa(double input, double *magnitude) const
+{
+  const double mag = qPow(10.0, qFloor(qLn(input)/qLn(10.0)));
+  if (magnitude) *magnitude = mag;
+  return input/mag;
+}
+
+/*! \internal
+  
+  Returns a number that is close to \a input but has a clean, easier human readable mantissa. How
+  strongly the mantissa is altered, and thus how strong the result deviates from the original \a
+  input, depends on the current tick step strategy (see \ref setTickStepStrategy).
+*/
+double QCPAxisTicker::cleanMantissa(double input) const
+{
+  double magnitude;
+  const double mantissa = getMantissa(input, &magnitude);
+  switch (mTickStepStrategy)
+  {
+    case tssReadability:
+    {
+      return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude;
+    }
+    case tssMeetTickCount:
+    {
+      // 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
+      if (mantissa <= 5.0)
+        return int(mantissa*2)/2.0*magnitude; // round digit after decimal point to 0.5
+      else
+        return int(mantissa/2.0)*2.0*magnitude; // round to first digit in multiples of 2
+    }
+  }
+  return input;
+}
+/* end of 'src/axis/axisticker.cpp' */
+
+
+/* including file 'src/axis/axistickerdatetime.cpp' */
+/* modified 2021-03-29T02:30:44, size 18829         */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPAxisTickerDateTime
+////////////////////////////////////////////////////////////////////////////////////////////////////
+/*! \class QCPAxisTickerDateTime
+  \brief Specialized axis ticker for calendar dates and times as axis ticks
+  
+  \image html axisticker-datetime.png
+  
+  This QCPAxisTicker subclass generates ticks that correspond to real calendar dates and times. The
+  plot axis coordinate is interpreted as Unix Time, so seconds since Epoch (January 1, 1970, 00:00
+  UTC). This is also used for example by QDateTime in the <tt>toTime_t()/setTime_t()</tt> methods
+  with a precision of one second. Since Qt 4.7, millisecond accuracy can be obtained from QDateTime
+  by using <tt>QDateTime::fromMSecsSinceEpoch()/1000.0</tt>. The static methods \ref dateTimeToKey
+  and \ref keyToDateTime conveniently perform this conversion achieving a precision of one
+  millisecond on all Qt versions.
+  
+  The format of the date/time display in the tick labels is controlled with \ref setDateTimeFormat.
+  If a different time spec or time zone shall be used for the tick label appearance, see \ref
+  setDateTimeSpec or \ref setTimeZone, respectively.
+  
+  This ticker produces unequal tick spacing in order to provide intuitive date and time-of-day
+  ticks. For example, if the axis range spans a few years such that there is one tick per year,
+  ticks will be positioned on 1. January of every year. This is intuitive but, due to leap years,
+  will result in slightly unequal tick intervals (visually unnoticeable). The same can be seen in
+  the image above: even though the number of days varies month by month, this ticker generates
+  ticks on the same day of each month.
+  
+  If you would like to change the date/time that is used as a (mathematical) starting date for the
+  ticks, use the \ref setTickOrigin(const QDateTime &origin) method overload, which takes a
+  QDateTime. If you pass 15. July, 9:45 to this method, the yearly ticks will end up on 15. July at
+  9:45 of every year.
+  
+  The ticker can be created and assigned to an axis like this:
+  \snippet documentation/doc-image-generator/mainwindow.cpp axistickerdatetime-creation
+  
+  \note If you rather wish to display relative times in terms of days, hours, minutes, seconds and
+  milliseconds, and are not interested in the intricacies of real calendar dates with months and
+  (leap) years, have a look at QCPAxisTickerTime instead.
 */
 
 /*!
   Constructs the ticker and sets reasonable default values. Axis tickers are commonly created
   managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.
 */
-QCPAxisTickerTime::QCPAxisTickerTime() :
-  mTimeFormat(QLatin1String("%h:%m:%s")),
-  mSmallestUnit(tuSeconds),
-  mBiggestUnit(tuHours)
+QCPAxisTickerDateTime::QCPAxisTickerDateTime() :
+  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
+  mDateTimeSpec(Qt::LocalTime),
+  mDateStrategy(dsNone)
 {
   setTickCount(4);
-  mFieldWidth[tuMilliseconds] = 3;
-  mFieldWidth[tuSeconds] = 2;
-  mFieldWidth[tuMinutes] = 2;
-  mFieldWidth[tuHours] = 2;
-  mFieldWidth[tuDays] = 1;
+}
+
+/*!
+  Sets the format in which dates and times are displayed as tick labels. For details about the \a
+  format string, see the documentation of QDateTime::toString().
   
-  mFormatPattern[tuMilliseconds] = QLatin1String("%z");
-  mFormatPattern[tuSeconds] = QLatin1String("%s");
-  mFormatPattern[tuMinutes] = QLatin1String("%m");
-  mFormatPattern[tuHours] = QLatin1String("%h");
-  mFormatPattern[tuDays] = QLatin1String("%d");
+  Typical expressions are
+  <table>
+    <tr><td>\c d</td><td>The day as a number without a leading zero (1 to 31)</td></tr>
+    <tr><td>\c dd</td><td>The day as a number with a leading zero (01 to 31)</td></tr>
+    <tr><td>\c ddd</td><td>The abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name, i.e. QLocale::system().</td></tr>
+    <tr><td>\c dddd</td><td>The long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name, i.e. QLocale::system().</td></tr>
+    <tr><td>\c M</td><td>The month as a number without a leading zero (1 to 12)</td></tr>
+    <tr><td>\c MM</td><td>The month as a number with a leading zero (01 to 12)</td></tr>
+    <tr><td>\c MMM</td><td>The abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name, i.e. QLocale::system().</td></tr>
+    <tr><td>\c MMMM</td><td>The long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name, i.e. QLocale::system().</td></tr>
+    <tr><td>\c yy</td><td>The year as a two digit number (00 to 99)</td></tr>
+    <tr><td>\c yyyy</td><td>The year as a four digit number. If the year is negative, a minus sign is prepended, making five characters.</td></tr>
+    <tr><td>\c h</td><td>The hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)</td></tr>
+    <tr><td>\c hh</td><td>The hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)</td></tr>
+    <tr><td>\c H</td><td>The hour without a leading zero (0 to 23, even with AM/PM display)</td></tr>
+    <tr><td>\c HH</td><td>The hour with a leading zero (00 to 23, even with AM/PM display)</td></tr>
+    <tr><td>\c m</td><td>The minute without a leading zero (0 to 59)</td></tr>
+    <tr><td>\c mm</td><td>The minute with a leading zero (00 to 59)</td></tr>
+    <tr><td>\c s</td><td>The whole second, without any leading zero (0 to 59)</td></tr>
+    <tr><td>\c ss</td><td>The whole second, with a leading zero where applicable (00 to 59)</td></tr>
+    <tr><td>\c z</td><td>The fractional part of the second, to go after a decimal point, without trailing zeroes (0 to 999). Thus "s.z" reports the seconds to full available (millisecond) precision without trailing zeroes.</td></tr>
+    <tr><td>\c zzz</td><td>The fractional part of the second, to millisecond precision, including trailing zeroes where applicable (000 to 999).</td></tr>
+    <tr><td>\c AP or \c A</td><td>Use AM/PM display. A/AP will be replaced by an upper-case version of either QLocale::amText() or QLocale::pmText().</td></tr>
+    <tr><td>\c ap or \c a</td><td>Use am/pm display. a/ap will be replaced by a lower-case version of either QLocale::amText() or QLocale::pmText().</td></tr>
+    <tr><td>\c t</td><td>The timezone (for example "CEST")</td></tr>
+  </table>
+  
+  Newlines can be inserted with \c "\n", literal strings (even when containing above expressions)
+  by encapsulating them using single-quotes. A literal single quote can be generated by using two
+  consecutive single quotes in the format.
+  
+  \see setDateTimeSpec, setTimeZone
+*/
+void QCPAxisTickerDateTime::setDateTimeFormat(const QString &format)
+{
+  mDateTimeFormat = format;
 }
 
 /*!
-  Sets the format that will be used to display time in the tick labels.
+  Sets the time spec that is used for creating the tick labels from corresponding dates/times.
+
+  The default value of QDateTime objects (and also QCPAxisTickerDateTime) is
+  <tt>Qt::LocalTime</tt>. However, if the displayed tick labels shall be given in UTC, set \a spec
+  to <tt>Qt::UTC</tt>.
   
-  The available patterns are:
-  - %%z for milliseconds
-  - %%s for seconds
-  - %%m for minutes
-  - %%h for hours
-  - %%d for days
+  Tick labels corresponding to other time zones can be achieved with \ref setTimeZone (which sets
+  \a spec to \c Qt::TimeZone internally). Note that if \a spec is afterwards set to not be \c
+  Qt::TimeZone again, the \ref setTimeZone setting will be ignored accordingly.
   
-  The field width (zero padding) can be controlled for each unit with \ref setFieldWidth.
+  \see setDateTimeFormat, setTimeZone
+*/
+void QCPAxisTickerDateTime::setDateTimeSpec(Qt::TimeSpec spec)
+{
+  mDateTimeSpec = spec;
+}
+
+# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+/*!
+  Sets the time zone that is used for creating the tick labels from corresponding dates/times. The
+  time spec (\ref setDateTimeSpec) is set to \c Qt::TimeZone.
   
-  The largest unit that appears in \a format will carry all the remaining time of a certain tick
-  coordinate, even if it overflows the natural limit of the unit. For example, if %%m is the
-  largest unit it might become larger than 59 in order to consume larger time values. If on the
-  other hand %%h is available, the minutes will wrap around to zero after 59 and the time will
-  carry to the hour digit.
+  \see setDateTimeFormat, setTimeZone
 */
-void QCPAxisTickerTime::setTimeFormat(const QString &format)
+void QCPAxisTickerDateTime::setTimeZone(const QTimeZone &zone)
 {
-  mTimeFormat = format;
+  mTimeZone = zone;
+  mDateTimeSpec = Qt::TimeZone;
+}
+#endif
+
+/*!
+  Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) in seconds since Epoch (1. Jan 1970,
+  00:00 UTC). For the date time ticker it might be more intuitive to use the overload which
+  directly takes a QDateTime, see \ref setTickOrigin(const QDateTime &origin).
   
-  // determine smallest and biggest unit in format, to optimize unit replacement and allow biggest
-  // unit to consume remaining time of a tick value and grow beyond its modulo (e.g. min > 59)
-  mSmallestUnit = tuMilliseconds;
-  mBiggestUnit = tuMilliseconds;
-  bool hasSmallest = false;
-  for (int i = tuMilliseconds; i <= tuDays; ++i)
-  {
-    TimeUnit unit = static_cast<TimeUnit>(i);
-    if (mTimeFormat.contains(mFormatPattern.value(unit)))
-    {
-      if (!hasSmallest)
-      {
-        mSmallestUnit = unit;
-        hasSmallest = true;
-      }
-      mBiggestUnit = unit;
-    }
-  }
+  This is useful to define the month/day/time recurring at greater tick interval steps. For
+  example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick
+  per year, the ticks will end up on 15. July at 9:45 of every year.
+*/
+void QCPAxisTickerDateTime::setTickOrigin(double origin)
+{
+  QCPAxisTicker::setTickOrigin(origin);
 }
 
 /*!
-  Sets the field widh of the specified \a unit to be \a width digits, when displayed in the tick
-  label. If the number for the specific unit is shorter than \a width, it will be padded with an
-  according number of zeros to the left in order to reach the field width.
+  Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) as a QDateTime \a origin.
   
-  \see setTimeFormat
+  This is useful to define the month/day/time recurring at greater tick interval steps. For
+  example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick
+  per year, the ticks will end up on 15. July at 9:45 of every year.
 */
-void QCPAxisTickerTime::setFieldWidth(QCPAxisTickerTime::TimeUnit unit, int width)
+void QCPAxisTickerDateTime::setTickOrigin(const QDateTime &origin)
 {
-  mFieldWidth[unit] = qMax(width, 1);
+  setTickOrigin(dateTimeToKey(origin));
 }
 
 /*! \internal
-
-  Returns the tick step appropriate for time displays, depending on the provided \a range and the
-  smallest available time unit in the current format (\ref setTimeFormat). For example if the unit
-  of seconds isn't available in the format, this method will not generate steps (like 2.5 minutes)
-  that require sub-minute precision to be displayed correctly.
+  
+  Returns a sensible tick step with intervals appropriate for a date-time-display, such as weekly,
+  monthly, bi-monthly, etc.
+  
+  Note that this tick step isn't used exactly when generating the tick vector in \ref
+  createTickVector, but only as a guiding value requiring some correction for each individual tick
+  interval. Otherwise this would lead to unintuitive date displays, e.g. jumping between first day
+  in the month to the last day in the previous month from tick to tick, due to the non-uniform
+  length of months. The same problem arises with leap years.
   
   \seebaseclassmethod
 */
-double QCPAxisTickerTime::getTickStep(const QCPRange &range)
+double QCPAxisTickerDateTime::getTickStep(const QCPRange &range)
 {
-  double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
+  double result = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
   
+  mDateStrategy = dsNone; // leaving it at dsNone means tick coordinates will not be tuned in any special way in createTickVector
   if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds
   {
-    if (mSmallestUnit == tuMilliseconds)
-      result = qMax(cleanMantissa(result), 0.001); // smallest tick step is 1 millisecond
-    else // have no milliseconds available in format, so stick with 1 second tickstep
-      result = 1.0;
-  } else if (result < 3600*24) // below a day
+    result = cleanMantissa(result);
+  } else if (result < 86400*30.4375*12) // below a year
   {
-    // the filling of availableSteps seems a bit contorted but it fills in a sorted fashion and thus saves a post-fill sorting run
-    QVector<double> availableSteps;
-    // seconds range:
-    if (mSmallestUnit <= tuSeconds)
-      availableSteps << 1;
-    if (mSmallestUnit == tuMilliseconds)
-      availableSteps << 2.5; // only allow half second steps if milliseconds are there to display it
-    else if (mSmallestUnit == tuSeconds)
-      availableSteps << 2;
-    if (mSmallestUnit <= tuSeconds)
-      availableSteps << 5 << 10 << 15 << 30;
-    // minutes range:
-    if (mSmallestUnit <= tuMinutes)
-      availableSteps << 1*60;
-    if (mSmallestUnit <= tuSeconds)
-      availableSteps << 2.5*60; // only allow half minute steps if seconds are there to display it
-    else if (mSmallestUnit == tuMinutes)
-      availableSteps << 2*60;
-    if (mSmallestUnit <= tuMinutes)
-      availableSteps << 5*60 << 10*60 << 15*60 << 30*60;
-    // hours range:
-    if (mSmallestUnit <= tuHours)
-      availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600;
-    // pick available step that is most appropriate to approximate ideal step:
-    result = pickClosest(result, availableSteps);
-  } else // more than a day, go back to normal clean mantissa algorithm but in units of days
+    result = pickClosest(result, QVector<double>()
+                             << 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
+                             << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24 // hour to day range
+                             << 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)
+    if (result > 86400*30.4375-1) // month tick intervals or larger
+      mDateStrategy = dsUniformDayInMonth;
+    else if (result > 3600*24-1) // day tick intervals or larger
+      mDateStrategy = dsUniformTimeInDay;
+  } else // more than a year, go back to normal clean mantissa algorithm but in units of years
   {
-    const double secondsPerDay = 3600*24;
-    result = cleanMantissa(result/secondsPerDay)*secondsPerDay;
+    const double secondsPerYear = 86400*30.4375*12; // average including leap years
+    result = cleanMantissa(result/secondsPerYear)*secondsPerYear;
+    mDateStrategy = dsUniformDayInMonth;
   }
   return result;
 }
 
 /*! \internal
-
-  Returns the sub tick count appropriate for the provided \a tickStep and time displays.
+  
+  Returns a sensible sub tick count with intervals appropriate for a date-time-display, such as weekly,
+  monthly, bi-monthly, etc.
   
   \seebaseclassmethod
 */
-int QCPAxisTickerTime::getSubTickCount(double tickStep)
+int QCPAxisTickerDateTime::getSubTickCount(double tickStep)
 {
   int result = QCPAxisTicker::getSubTickCount(tickStep);
-  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day range (as specified in getTickStep)
+  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day/week/month range (as specified in getTickStep)
   {
     case 5*60: result = 4; break;
     case 10*60: result = 1; break;
@@ -6334,107 +6690,441 @@ int QCPAxisTickerTime::getSubTickCount(double tickStep)
     case 3600*6: result = 1; break;
     case 3600*12: result = 3; break;
     case 3600*24: result = 3; break;
+    case 86400*2: result = 1; break;
+    case 86400*5: result = 4; break;
+    case 86400*7: result = 6; break;
+    case 86400*14: result = 1; break;
+    case int(86400*30.4375+0.5): result = 3; break;
+    case int(86400*30.4375*2+0.5): result = 1; break;
+    case int(86400*30.4375*3+0.5): result = 2; break;
+    case int(86400*30.4375*6+0.5): result = 5; break;
+    case int(86400*30.4375*12+0.5): result = 3; break;
   }
   return result;
 }
 
 /*! \internal
   
-  Returns the tick label corresponding to the provided \a tick and the configured format and field
-  widths (\ref setTimeFormat, \ref setFieldWidth).
+  Generates a date/time tick label for tick coordinate \a tick, based on the currently set format
+  (\ref setDateTimeFormat), time spec (\ref setDateTimeSpec), and possibly time zone (\ref
+  setTimeZone).
   
   \seebaseclassmethod
 */
-QString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
+QString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
 {
   Q_UNUSED(precision)
   Q_UNUSED(formatChar)
-  Q_UNUSED(locale)
-  bool negative = tick < 0;
-  if (negative) tick *= -1;
-  double values[tuDays+1]; // contains the msec/sec/min/... value with its respective modulo (e.g. minute 0..59)
-  double restValues[tuDays+1]; // contains the msec/sec/min/... value as if it's the largest available unit and thus consumes the remaining time
+# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+  if (mDateTimeSpec == Qt::TimeZone)
+    return locale.toString(keyToDateTime(tick).toTimeZone(mTimeZone), mDateTimeFormat);
+  else
+    return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
+# else
+  return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
+# endif
+}
+
+/*! \internal
   
-  restValues[tuMilliseconds] = tick*1000;
-  values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000;
-  values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60;
-  values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60;
-  values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24;
-  // no need to set values[tuDays] because days are always a rest value (there is no higher unit so it consumes all remaining time)
+  Uses the passed \a tickStep as a guiding value and applies corrections in order to obtain
+  non-uniform tick intervals but intuitive tick labels, e.g. falling on the same day of each month.
   
-  QString result = mTimeFormat;
-  for (int i = mSmallestUnit; i <= mBiggestUnit; ++i)
+  \seebaseclassmethod
+*/
+QVector<double> QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range)
+{
+  QVector<double> result = QCPAxisTicker::createTickVector(tickStep, range);
+  if (!result.isEmpty())
   {
-    TimeUnit iUnit = static_cast<TimeUnit>(i);
-    replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit]));
+    if (mDateStrategy == dsUniformTimeInDay)
+    {
+      QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // the time of this datetime will be set for all other ticks, if possible
+      QDateTime tickDateTime;
+      for (int i=0; i<result.size(); ++i)
+      {
+        tickDateTime = keyToDateTime(result.at(i));
+        tickDateTime.setTime(uniformDateTime.time());
+        result[i] = dateTimeToKey(tickDateTime);
+      }
+    } else if (mDateStrategy == dsUniformDayInMonth)
+    {
+      QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // this day (in month) and time will be set for all other ticks, if possible
+      QDateTime tickDateTime;
+      for (int i=0; i<result.size(); ++i)
+      {
+        tickDateTime = keyToDateTime(result.at(i));
+        tickDateTime.setTime(uniformDateTime.time());
+        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)
+        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
+          tickDateTime = tickDateTime.addMonths(1);
+        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
+          tickDateTime = tickDateTime.addMonths(-1);
+        tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay));
+        result[i] = dateTimeToKey(tickDateTime);
+      }
+    }
   }
-  if (negative)
-    result.prepend(QLatin1Char('-'));
   return result;
 }
 
-/*! \internal
+/*!
+  A convenience method which turns \a key (in seconds since Epoch 1. Jan 1970, 00:00 UTC) into a
+  QDateTime object. This can be used to turn axis coordinates to actual QDateTimes.
   
-  Replaces all occurrences of the format pattern belonging to \a unit in \a text with the specified
-  \a value, using the field width as specified with \ref setFieldWidth for the \a unit.
+  The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it
+  works around the lack of a QDateTime::fromMSecsSinceEpoch in Qt 4.6)
+  
+  \see dateTimeToKey
 */
-void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const
+QDateTime QCPAxisTickerDateTime::keyToDateTime(double key)
 {
-  QString valueStr = QString::number(value);
-  while (valueStr.size() < mFieldWidth.value(unit))
-    valueStr.prepend(QLatin1Char('0'));
+# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
+  return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000);
+# else
+  return QDateTime::fromMSecsSinceEpoch(qint64(key*1000.0));
+# endif
+}
+
+/*! \overload
   
-  text.replace(mFormatPattern.value(unit), valueStr);
+  A convenience method which turns a QDateTime object into a double value that corresponds to
+  seconds since Epoch (1. Jan 1970, 00:00 UTC). This is the format used as axis coordinates by
+  QCPAxisTickerDateTime.
+  
+  The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it
+  works around the lack of a QDateTime::toMSecsSinceEpoch in Qt 4.6)
+  
+  \see keyToDateTime
+*/
+double QCPAxisTickerDateTime::dateTimeToKey(const QDateTime &dateTime)
+{
+# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
+  return dateTime.toTime_t()+dateTime.time().msec()/1000.0;
+# else
+  return dateTime.toMSecsSinceEpoch()/1000.0;
+# endif
 }
-/* end of 'src/axis/axistickertime.cpp' */
+
+/*! \overload
+  
+  A convenience method which turns a QDate object into a double value that corresponds to seconds
+  since Epoch (1. Jan 1970, 00:00 UTC). This is the format used
+  as axis coordinates by QCPAxisTickerDateTime.
+  
+  The returned value will be the start of the passed day of \a date, interpreted in the given \a
+  timeSpec.
+  
+  \see keyToDateTime
+*/
+double QCPAxisTickerDateTime::dateTimeToKey(const QDate &date, Qt::TimeSpec timeSpec)
+{
+# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
+  return QDateTime(date, QTime(0, 0), timeSpec).toTime_t();
+# elif QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
+  return QDateTime(date, QTime(0, 0), timeSpec).toMSecsSinceEpoch()/1000.0;
+# else
+  return date.startOfDay(timeSpec).toMSecsSinceEpoch()/1000.0;
+# endif
+}
+/* end of 'src/axis/axistickerdatetime.cpp' */
 
 
-/* including file 'src/axis/axistickerfixed.cpp', size 5583                  */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickertime.cpp' */
+/* modified 2021-03-29T02:30:44, size 11745     */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////// QCPAxisTickerFixed
+//////////////////// QCPAxisTickerTime
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-/*! \class QCPAxisTickerFixed
-  \brief Specialized axis ticker with a fixed tick step
+/*! \class QCPAxisTickerTime
+  \brief Specialized axis ticker for time spans in units of milliseconds to days
   
-  \image html axisticker-fixed.png
+  \image html axisticker-time.png
   
-  This QCPAxisTicker subclass generates ticks with a fixed tick step set with \ref setTickStep. It
-  is also possible to allow integer multiples and integer powers of the specified tick step with
-  \ref setScaleStrategy.
+  This QCPAxisTicker subclass generates ticks that corresponds to time intervals.
   
-  A typical application of this ticker is to make an axis only display integers, by setting the
-  tick step of the ticker to 1.0 and the scale strategy to \ref ssMultiples.
+  The format of the time display in the tick labels is controlled with \ref setTimeFormat and \ref
+  setFieldWidth. The time coordinate is in the unit of seconds with respect to the time coordinate
+  zero. Unlike with QCPAxisTickerDateTime, the ticks don't correspond to a specific calendar date
+  and time.
   
-  Another case is when a certain number has a special meaning and axis ticks should only appear at
-  multiples of that value. In this case you might also want to consider \ref QCPAxisTickerPi
-  because despite the name it is not limited to only pi symbols/values.
+  The time can be displayed in milliseconds, seconds, minutes, hours and days. Depending on the
+  largest available unit in the format specified with \ref setTimeFormat, any time spans above will
+  be carried in that largest unit. So for example if the format string is "%m:%s" and a tick at
+  coordinate value 7815 (being 2 hours, 10 minutes and 15 seconds) is created, the resulting tick
+  label will show "130:15" (130 minutes, 15 seconds). If the format string is "%h:%m:%s", the hour
+  unit will be used and the label will thus be "02:10:15". Negative times with respect to the axis
+  zero will carry a leading minus sign.
   
   The ticker can be created and assigned to an axis like this:
-  \snippet documentation/doc-image-generator/mainwindow.cpp axistickerfixed-creation
+  \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation
+  
+  Here is an example of a time axis providing time information in days, hours and minutes. Due to
+  the axis range spanning a few days and the wanted tick count (\ref setTickCount), the ticker
+  decided to use tick steps of 12 hours:
+  
+  \image html axisticker-time2.png
+  
+  The format string for this example is
+  \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation-2
+  
+  \note If you rather wish to display calendar dates and times, have a look at QCPAxisTickerDateTime
+  instead.
 */
 
 /*!
   Constructs the ticker and sets reasonable default values. Axis tickers are commonly created
   managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.
 */
-QCPAxisTickerFixed::QCPAxisTickerFixed() :
-  mTickStep(1.0),
-  mScaleStrategy(ssNone)
+QCPAxisTickerTime::QCPAxisTickerTime() :
+  mTimeFormat(QLatin1String("%h:%m:%s")),
+  mSmallestUnit(tuSeconds),
+  mBiggestUnit(tuHours)
 {
+  setTickCount(4);
+  mFieldWidth[tuMilliseconds] = 3;
+  mFieldWidth[tuSeconds] = 2;
+  mFieldWidth[tuMinutes] = 2;
+  mFieldWidth[tuHours] = 2;
+  mFieldWidth[tuDays] = 1;
+  
+  mFormatPattern[tuMilliseconds] = QLatin1String("%z");
+  mFormatPattern[tuSeconds] = QLatin1String("%s");
+  mFormatPattern[tuMinutes] = QLatin1String("%m");
+  mFormatPattern[tuHours] = QLatin1String("%h");
+  mFormatPattern[tuDays] = QLatin1String("%d");
 }
 
 /*!
-  Sets the fixed tick interval to \a step.
+  Sets the format that will be used to display time in the tick labels.
   
-  The axis ticker will only use this tick step when generating axis ticks. This might cause a very
-  high tick density and overlapping labels if the axis range is zoomed out. Using \ref
-  setScaleStrategy it is possible to relax the fixed step and also allow multiples or powers of \a
-  step. This will enable the ticker to reduce the number of ticks to a reasonable amount (see \ref
-  setTickCount).
+  The available patterns are:
+  - %%z for milliseconds
+  - %%s for seconds
+  - %%m for minutes
+  - %%h for hours
+  - %%d for days
+  
+  The field width (zero padding) can be controlled for each unit with \ref setFieldWidth.
+  
+  The largest unit that appears in \a format will carry all the remaining time of a certain tick
+  coordinate, even if it overflows the natural limit of the unit. For example, if %%m is the
+  largest unit it might become larger than 59 in order to consume larger time values. If on the
+  other hand %%h is available, the minutes will wrap around to zero after 59 and the time will
+  carry to the hour digit.
 */
-void QCPAxisTickerFixed::setTickStep(double step)
+void QCPAxisTickerTime::setTimeFormat(const QString &format)
+{
+  mTimeFormat = format;
+  
+  // determine smallest and biggest unit in format, to optimize unit replacement and allow biggest
+  // unit to consume remaining time of a tick value and grow beyond its modulo (e.g. min > 59)
+  mSmallestUnit = tuMilliseconds;
+  mBiggestUnit = tuMilliseconds;
+  bool hasSmallest = false;
+  for (int i = tuMilliseconds; i <= tuDays; ++i)
+  {
+    TimeUnit unit = static_cast<TimeUnit>(i);
+    if (mTimeFormat.contains(mFormatPattern.value(unit)))
+    {
+      if (!hasSmallest)
+      {
+        mSmallestUnit = unit;
+        hasSmallest = true;
+      }
+      mBiggestUnit = unit;
+    }
+  }
+}
+
+/*!
+  Sets the field widh of the specified \a unit to be \a width digits, when displayed in the tick
+  label. If the number for the specific unit is shorter than \a width, it will be padded with an
+  according number of zeros to the left in order to reach the field width.
+  
+  \see setTimeFormat
+*/
+void QCPAxisTickerTime::setFieldWidth(QCPAxisTickerTime::TimeUnit unit, int width)
+{
+  mFieldWidth[unit] = qMax(width, 1);
+}
+
+/*! \internal
+
+  Returns the tick step appropriate for time displays, depending on the provided \a range and the
+  smallest available time unit in the current format (\ref setTimeFormat). For example if the unit
+  of seconds isn't available in the format, this method will not generate steps (like 2.5 minutes)
+  that require sub-minute precision to be displayed correctly.
+  
+  \seebaseclassmethod
+*/
+double QCPAxisTickerTime::getTickStep(const QCPRange &range)
+{
+  double result = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
+  
+  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds
+  {
+    if (mSmallestUnit == tuMilliseconds)
+      result = qMax(cleanMantissa(result), 0.001); // smallest tick step is 1 millisecond
+    else // have no milliseconds available in format, so stick with 1 second tickstep
+      result = 1.0;
+  } else if (result < 3600*24) // below a day
+  {
+    // the filling of availableSteps seems a bit contorted but it fills in a sorted fashion and thus saves a post-fill sorting run
+    QVector<double> availableSteps;
+    // seconds range:
+    if (mSmallestUnit <= tuSeconds)
+      availableSteps << 1;
+    if (mSmallestUnit == tuMilliseconds)
+      availableSteps << 2.5; // only allow half second steps if milliseconds are there to display it
+    else if (mSmallestUnit == tuSeconds)
+      availableSteps << 2;
+    if (mSmallestUnit <= tuSeconds)
+      availableSteps << 5 << 10 << 15 << 30;
+    // minutes range:
+    if (mSmallestUnit <= tuMinutes)
+      availableSteps << 1*60;
+    if (mSmallestUnit <= tuSeconds)
+      availableSteps << 2.5*60; // only allow half minute steps if seconds are there to display it
+    else if (mSmallestUnit == tuMinutes)
+      availableSteps << 2*60;
+    if (mSmallestUnit <= tuMinutes)
+      availableSteps << 5*60 << 10*60 << 15*60 << 30*60;
+    // hours range:
+    if (mSmallestUnit <= tuHours)
+      availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600;
+    // pick available step that is most appropriate to approximate ideal step:
+    result = pickClosest(result, availableSteps);
+  } else // more than a day, go back to normal clean mantissa algorithm but in units of days
+  {
+    const double secondsPerDay = 3600*24;
+    result = cleanMantissa(result/secondsPerDay)*secondsPerDay;
+  }
+  return result;
+}
+
+/*! \internal
+
+  Returns the sub tick count appropriate for the provided \a tickStep and time displays.
+  
+  \seebaseclassmethod
+*/
+int QCPAxisTickerTime::getSubTickCount(double tickStep)
+{
+  int result = QCPAxisTicker::getSubTickCount(tickStep);
+  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day range (as specified in getTickStep)
+  {
+    case 5*60: result = 4; break;
+    case 10*60: result = 1; break;
+    case 15*60: result = 2; break;
+    case 30*60: result = 1; break;
+    case 60*60: result = 3; break;
+    case 3600*2: result = 3; break;
+    case 3600*3: result = 2; break;
+    case 3600*6: result = 1; break;
+    case 3600*12: result = 3; break;
+    case 3600*24: result = 3; break;
+  }
+  return result;
+}
+
+/*! \internal
+  
+  Returns the tick label corresponding to the provided \a tick and the configured format and field
+  widths (\ref setTimeFormat, \ref setFieldWidth).
+  
+  \seebaseclassmethod
+*/
+QString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
+{
+  Q_UNUSED(precision)
+  Q_UNUSED(formatChar)
+  Q_UNUSED(locale)
+  bool negative = tick < 0;
+  if (negative) tick *= -1;
+  double values[tuDays+1]; // contains the msec/sec/min/... value with its respective modulo (e.g. minute 0..59)
+  double restValues[tuDays+1]; // contains the msec/sec/min/... value as if it's the largest available unit and thus consumes the remaining time
+  
+  restValues[tuMilliseconds] = tick*1000;
+  values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000;
+  values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60;
+  values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60;
+  values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24;
+  // no need to set values[tuDays] because days are always a rest value (there is no higher unit so it consumes all remaining time)
+  
+  QString result = mTimeFormat;
+  for (int i = mSmallestUnit; i <= mBiggestUnit; ++i)
+  {
+    TimeUnit iUnit = static_cast<TimeUnit>(i);
+    replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit]));
+  }
+  if (negative)
+    result.prepend(QLatin1Char('-'));
+  return result;
+}
+
+/*! \internal
+  
+  Replaces all occurrences of the format pattern belonging to \a unit in \a text with the specified
+  \a value, using the field width as specified with \ref setFieldWidth for the \a unit.
+*/
+void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const
+{
+  QString valueStr = QString::number(value);
+  while (valueStr.size() < mFieldWidth.value(unit))
+    valueStr.prepend(QLatin1Char('0'));
+  
+  text.replace(mFormatPattern.value(unit), valueStr);
+}
+/* end of 'src/axis/axistickertime.cpp' */
+
+
+/* including file 'src/axis/axistickerfixed.cpp' */
+/* modified 2021-03-29T02:30:44, size 5575       */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPAxisTickerFixed
+////////////////////////////////////////////////////////////////////////////////////////////////////
+/*! \class QCPAxisTickerFixed
+  \brief Specialized axis ticker with a fixed tick step
+  
+  \image html axisticker-fixed.png
+  
+  This QCPAxisTicker subclass generates ticks with a fixed tick step set with \ref setTickStep. It
+  is also possible to allow integer multiples and integer powers of the specified tick step with
+  \ref setScaleStrategy.
+  
+  A typical application of this ticker is to make an axis only display integers, by setting the
+  tick step of the ticker to 1.0 and the scale strategy to \ref ssMultiples.
+  
+  Another case is when a certain number has a special meaning and axis ticks should only appear at
+  multiples of that value. In this case you might also want to consider \ref QCPAxisTickerPi
+  because despite the name it is not limited to only pi symbols/values.
+  
+  The ticker can be created and assigned to an axis like this:
+  \snippet documentation/doc-image-generator/mainwindow.cpp axistickerfixed-creation
+*/
+
+/*!
+  Constructs the ticker and sets reasonable default values. Axis tickers are commonly created
+  managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.
+*/
+QCPAxisTickerFixed::QCPAxisTickerFixed() :
+  mTickStep(1.0),
+  mScaleStrategy(ssNone)
+{
+}
+
+/*!
+  Sets the fixed tick interval to \a step.
+  
+  The axis ticker will only use this tick step when generating axis ticks. This might cause a very
+  high tick density and overlapping labels if the axis range is zoomed out. Using \ref
+  setScaleStrategy it is possible to relax the fixed step and also allow multiples or powers of \a
+  step. This will enable the ticker to reduce the number of ticks to a reasonable amount (see \ref
+  setTickCount).
+*/
+void QCPAxisTickerFixed::setTickStep(double step)
 {
   if (step > 0)
     mTickStep = step;
@@ -6474,16 +7164,16 @@ double QCPAxisTickerFixed::getTickStep(const QCPRange &range)
     }
     case ssMultiples:
     {
-      double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
+      double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
       if (exactStep < mTickStep)
         return mTickStep;
       else
-        return (qint64)(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep;
+        return qint64(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep;
     }
     case ssPowers:
     {
-      double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
-      return qPow(mTickStep, (int)(qLn(exactStep)/qLn(mTickStep)+0.5));
+      double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
+      return qPow(mTickStep, int(qLn(exactStep)/qLn(mTickStep)+0.5));
     }
   }
   return mTickStep;
@@ -6491,8 +7181,8 @@ double QCPAxisTickerFixed::getTickStep(const QCPRange &range)
 /* end of 'src/axis/axistickerfixed.cpp' */
 
 
-/* including file 'src/axis/axistickertext.cpp', size 8653                   */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickertext.cpp' */
+/* modified 2021-03-29T02:30:44, size 8742      */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPAxisTickerText
@@ -6562,7 +7252,7 @@ void QCPAxisTickerText::setTicks(const QMap<double, QString> &ticks)
   
   \see addTicks, addTick, clear
 */
-void QCPAxisTickerText::setTicks(const QVector<double> &positions, const QVector<QString> labels)
+void QCPAxisTickerText::setTicks(const QVector<double> &positions, const QVector<QString> &labels)
 {
   clear();
   addTicks(positions, labels);
@@ -6600,7 +7290,7 @@ void QCPAxisTickerText::clear()
   
   \see addTicks, setTicks, clear
 */
-void QCPAxisTickerText::addTick(double position, QString label)
+void QCPAxisTickerText::addTick(double position, const QString &label)
 {
   mTicks.insert(position, label);
 }
@@ -6617,7 +7307,11 @@ void QCPAxisTickerText::addTick(double position, QString label)
 */
 void QCPAxisTickerText::addTicks(const QMap<double, QString> &ticks)
 {
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
   mTicks.unite(ticks);
+#else
+  mTicks.insert(ticks);
+#endif
 }
 
 /*! \overload
@@ -6704,8 +7398,8 @@ QVector<double> QCPAxisTickerText::createTickVector(double tickStep, const QCPRa
 /* end of 'src/axis/axistickertext.cpp' */
 
 
-/* including file 'src/axis/axistickerpi.cpp', size 11170                    */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickerpi.cpp' */
+/* modified 2021-03-29T02:30:44, size 11177   */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPAxisTickerPi
@@ -6795,7 +7489,7 @@ void QCPAxisTickerPi::setFractionStyle(QCPAxisTickerPi::FractionStyle style)
 */
 double QCPAxisTickerPi::getTickStep(const QCPRange &range)
 {
-  mPiTickStep = range.size()/mPiValue/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
+  mPiTickStep = range.size()/mPiValue/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
   mPiTickStep = cleanMantissa(mPiTickStep);
   return mPiTickStep*mPiValue;
 }
@@ -6893,7 +7587,7 @@ QString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const
   if (mFractionStyle == fsFloatingPoint) // should never be the case when calling this function
   {
     qDebug() << Q_FUNC_INFO << "shouldn't be called with fraction style fsDecimal";
-    return QString::number(numerator/(double)denominator); // failsafe
+    return QString::number(numerator/double(denominator)); // failsafe
   }
   int sign = numerator*denominator < 0 ? -1 : 1;
   numerator = qAbs(numerator);
@@ -6915,7 +7609,7 @@ QString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const
       {
         return QString(QLatin1String("%1%2%3/%4"))
             .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
-            .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(" ") : QLatin1String(""))
+            .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(" ") : QString(QLatin1String("")))
             .arg(remainder)
             .arg(denominator);
       } else if (mFractionStyle == fsUnicodeFractions)
@@ -6991,8 +7685,8 @@ QString QCPAxisTickerPi::unicodeSubscript(int number) const
 /* end of 'src/axis/axistickerpi.cpp' */
 
 
-/* including file 'src/axis/axistickerlog.cpp', size 7106                    */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickerlog.cpp' */
+/* modified 2021-03-29T02:30:44, size 7890     */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPAxisTickerLog
@@ -7015,6 +7709,12 @@ QString QCPAxisTickerPi::unicodeSubscript(int number) const
 
   The ticker can be created and assigned to an axis like this:
   \snippet documentation/doc-image-generator/mainwindow.cpp axistickerlog-creation
+  
+  Note that the nature of logarithmic ticks imply that there exists a smallest possible tick step,
+  corresponding to one multiplication by the log base. If the user zooms in further than that, no
+  new ticks would appear, leading to very sparse or even no axis ticks on the axis. To prevent this
+  situation, this ticker falls back to regular tick generation if the axis range would be covered
+  by too few logarithmically placed ticks.
 */
 
 /*!
@@ -7060,20 +7760,6 @@ void QCPAxisTickerLog::setSubTickCount(int subTicks)
     qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
 }
 
-/*! \internal
-  
-  Since logarithmic tick steps are necessarily different for each tick interval, this method does
-  nothing in the case of QCPAxisTickerLog
-  
-  \seebaseclassmethod
-*/
-double QCPAxisTickerLog::getTickStep(const QCPRange &range)
-{
-  // Logarithmic axis ticker has unequal tick spacing, so doesn't need this method
-  Q_UNUSED(range)
-  return 1.0;
-}
-
 /*! \internal
   
   Returns the sub tick count specified in \ref setSubTickCount. For QCPAxisTickerLog, there is no
@@ -7091,19 +7777,24 @@ int QCPAxisTickerLog::getSubTickCount(double tickStep)
   
   Creates ticks with a spacing given by the logarithm base and an increasing integer power in the
   provided \a range. The step in which the power increases tick by tick is chosen in order to keep
-  the total number of ticks as close as possible to the tick count (\ref setTickCount). The
-  parameter \a tickStep is ignored for QCPAxisTickerLog
+  the total number of ticks as close as possible to the tick count (\ref setTickCount).
+
+  The parameter \a tickStep is ignored for the normal logarithmic ticker generation. Only when
+  zoomed in very far such that not enough logarithmically placed ticks would be visible, this
+  function falls back to the regular QCPAxisTicker::createTickVector, which then uses \a tickStep.
   
   \seebaseclassmethod
 */
 QVector<double> QCPAxisTickerLog::createTickVector(double tickStep, const QCPRange &range)
 {
-  Q_UNUSED(tickStep)
   QVector<double> result;
   if (range.lower > 0 && range.upper > 0) // positive range
   {
-    double exactPowerStep =  qLn(range.upper/range.lower)*mLogBaseLnInv/(double)(mTickCount+1e-10);
-    double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
+    const double baseTickCount = qLn(range.upper/range.lower)*mLogBaseLnInv;
+    if (baseTickCount < 1.6) // if too few log ticks would be visible in axis range, fall back to regular tick vector generation
+      return QCPAxisTicker::createTickVector(tickStep, range);
+    const double exactPowerStep =  baseTickCount/double(mTickCount+1e-10);
+    const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1));
     double currentTick = qPow(newLogBase, qFloor(qLn(range.lower)/qLn(newLogBase)));
     result.append(currentTick);
     while (currentTick < range.upper && currentTick > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
@@ -7113,8 +7804,11 @@ QVector<double> QCPAxisTickerLog::createTickVector(double tickStep, const QCPRan
     }
   } else if (range.lower < 0 && range.upper < 0) // negative range
   {
-    double exactPowerStep =  qLn(range.lower/range.upper)*mLogBaseLnInv/(double)(mTickCount+1e-10);
-    double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
+    const double baseTickCount = qLn(range.lower/range.upper)*mLogBaseLnInv;
+    if (baseTickCount < 1.6) // if too few log ticks would be visible in axis range, fall back to regular tick vector generation
+      return QCPAxisTicker::createTickVector(tickStep, range);
+    const double exactPowerStep =  baseTickCount/double(mTickCount+1e-10);
+    const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1));
     double currentTick = -qPow(newLogBase, qCeil(qLn(-range.lower)/qLn(newLogBase)));
     result.append(currentTick);
     while (currentTick < range.upper && currentTick < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
@@ -7132,8 +7826,8 @@ QVector<double> QCPAxisTickerLog::createTickVector(double tickStep, const QCPRan
 /* end of 'src/axis/axistickerlog.cpp' */
 
 
-/* including file 'src/axis/axis.cpp', size 99397                            */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axis.cpp'       */
+/* modified 2021-03-29T02:30:44, size 99883 */
 
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -7160,6 +7854,9 @@ QVector<double> QCPAxisTickerLog::createTickVector(double tickStep, const QCPRan
 */
 QCPGrid::QCPGrid(QCPAxis *parentAxis) :
   QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
+  mSubGridVisible{},
+  mAntialiasedSubGrid{},
+  mAntialiasedZeroLine{},
   mParentAxis(parentAxis)
 {
   // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
@@ -7346,16 +8043,16 @@ void QCPGrid::drawSubGridLines(QCPPainter *painter) const
   painter->setPen(mSubGridPen);
   if (mParentAxis->orientation() == Qt::Horizontal)
   {
-    for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
+    foreach (double tickCoord, mParentAxis->mSubTickVector)
     {
-      t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x
+      t = mParentAxis->coordToPixel(tickCoord); // x
       painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
     }
   } else
   {
-    for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
+    foreach (double tickCoord, mParentAxis->mSubTickVector)
     {
-      t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y
+      t = mParentAxis->coordToPixel(tickCoord); // y
       painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
     }
   }
@@ -7538,7 +8235,8 @@ QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) :
   mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
   mTicker(new QCPAxisTicker),
   mCachedMarginValid(false),
-  mCachedMargin(0)
+  mCachedMargin(0),
+  mDragging(false)
 {
   setParent(parent);
   mGrid->setVisible(false);
@@ -7653,10 +8351,14 @@ QCPLineEnding QCPAxis::upperEnding() const
 /*!
   Sets whether the axis uses a linear scale or a logarithmic scale.
   
-  Note that this method controls the coordinate transformation. You will likely also want to use a
-  logarithmic tick spacing and labeling, which can be achieved by setting an instance of \ref
-  QCPAxisTickerLog via \ref setTicker. See the documentation of \ref QCPAxisTickerLog about the
-  details of logarithmic axis tick creation.
+  Note that this method controls the coordinate transformation. For logarithmic scales, you will
+  likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting
+  the axis ticker to an instance of \ref QCPAxisTickerLog :
+  
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-creation
+  
+  See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick
+  creation.
   
   \ref setNumberPrecision
 */
@@ -7867,7 +8569,7 @@ void QCPAxis::setTicker(QSharedPointer<QCPAxisTicker> ticker)
   if (ticker)
     mTicker = ticker;
   else
-    qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker";
+    qDebug() << Q_FUNC_INFO << "can not set nullptr as axis ticker";
   // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector
 }
 
@@ -7975,11 +8677,16 @@ void QCPAxis::setTickLabelSide(LabelSide side)
   of the format code used e.g. by QString::number() and QLocale::toString(). For reference about
   that, see the "Argument Formats" section in the detailed description of the QString class.
   
-  \a formatCode is a string of one, two or three characters. The first character is identical to
+  \a formatCode is a string of one, two or three characters.
+
+  <b>The first character</b> is identical to
   the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed
-  format, 'g'/'G' scientific or fixed, whichever is shorter.
-  
-  The second and third characters are optional and specific to QCustomPlot:\n
+  format, 'g'/'G' scientific or fixed, whichever is shorter. For the 'e', 'E', and 'f' formats,
+  the precision set by \ref setNumberPrecision represents the number of digits after the decimal
+  point. For the 'g' and 'G' formats, the precision represents the maximum number of significant
+  digits, trailing zeroes are omitted.
+
+  <b>The second and third characters</b> are optional and specific to QCustomPlot:\n
   If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.
   "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for
   "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
@@ -8493,7 +9200,7 @@ void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
   else
     ownPixelSize = axisRect()->height();
   
-  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
+  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/double(otherPixelSize);
   setRange(range().center(), newRangeSize, Qt::AlignCenter);
 }
 
@@ -8505,22 +9212,21 @@ void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
 */
 void QCPAxis::rescale(bool onlyVisiblePlottables)
 {
-  QList<QCPAbstractPlottable*> p = plottables();
   QCPRange newRange;
   bool haveRange = false;
-  for (int i=0; i<p.size(); ++i)
+  foreach (QCPAbstractPlottable *plottable, plottables())
   {
-    if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
+    if (!plottable->realVisibility() && onlyVisiblePlottables)
       continue;
     QCPRange plottableRange;
     bool currentFoundRange;
     QCP::SignDomain signDomain = QCP::sdBoth;
     if (mScaleType == stLogarithmic)
       signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);
-    if (p.at(i)->keyAxis() == this)
-      plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
+    if (plottable->keyAxis() == this)
+      plottableRange = plottable->getKeyRange(currentFoundRange, signDomain);
     else
-      plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
+      plottableRange = plottable->getValueRange(currentFoundRange, signDomain);
     if (currentFoundRange)
     {
       if (!haveRange)
@@ -8559,30 +9265,30 @@ double QCPAxis::pixelToCoord(double value) const
     if (mScaleType == stLinear)
     {
       if (!mRangeReversed)
-        return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
+        return (value-mAxisRect->left())/double(mAxisRect->width())*mRange.size()+mRange.lower;
       else
-        return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
+        return -(value-mAxisRect->left())/double(mAxisRect->width())*mRange.size()+mRange.upper;
     } else // mScaleType == stLogarithmic
     {
       if (!mRangeReversed)
-        return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
+        return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/double(mAxisRect->width()))*mRange.lower;
       else
-        return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
+        return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/double(mAxisRect->width()))*mRange.upper;
     }
   } else // orientation() == Qt::Vertical
   {
     if (mScaleType == stLinear)
     {
       if (!mRangeReversed)
-        return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
+        return (mAxisRect->bottom()-value)/double(mAxisRect->height())*mRange.size()+mRange.lower;
       else
-        return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
+        return -(mAxisRect->bottom()-value)/double(mAxisRect->height())*mRange.size()+mRange.upper;
     } else // mScaleType == stLogarithmic
     {
       if (!mRangeReversed)
-        return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
+        return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/double(mAxisRect->height()))*mRange.lower;
       else
-        return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
+        return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/double(mAxisRect->height()))*mRange.upper;
     }
   }
 }
@@ -8688,10 +9394,10 @@ QList<QCPAbstractPlottable*> QCPAxis::plottables() const
   QList<QCPAbstractPlottable*> result;
   if (!mParentPlot) return result;
   
-  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
+  foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables)
   {
-    if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
-      result.append(mParentPlot->mPlottables.at(i));
+    if (plottable->keyAxis() == this || plottable->valueAxis() == this)
+      result.append(plottable);
   }
   return result;
 }
@@ -8706,10 +9412,10 @@ QList<QCPGraph*> QCPAxis::graphs() const
   QList<QCPGraph*> result;
   if (!mParentPlot) return result;
   
-  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
+  foreach (QCPGraph *graph, mParentPlot->mGraphs)
   {
-    if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
-      result.append(mParentPlot->mGraphs.at(i));
+    if (graph->keyAxis() == this || graph->valueAxis() == this)
+      result.append(graph);
   }
   return result;
 }
@@ -8725,14 +9431,13 @@ QList<QCPAbstractItem*> QCPAxis::items() const
   QList<QCPAbstractItem*> result;
   if (!mParentPlot) return result;
   
-  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
+  foreach (QCPAbstractItem *item, mParentPlot->mItems)
   {
-    QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
-    for (int posId=0; posId<positions.size(); ++posId)
+    foreach (QCPItemPosition *position, item->positions())
     {
-      if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
+      if (position->keyAxis() == this || position->valueAxis() == this)
       {
-        result.append(mParentPlot->mItems.at(itemId));
+        result.append(item);
         break;
       }
     }
@@ -8754,7 +9459,7 @@ QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side)
     case QCP::msBottom: return atBottom;
     default: break;
   }
-  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
+  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << static_cast<int>(side);
   return atLeft;
 }
 
@@ -8765,12 +9470,13 @@ QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)
 {
   switch (type)
   {
-    case atLeft: return atRight; break;
-    case atRight: return atLeft; break;
-    case atBottom: return atTop; break;
-    case atTop: return atBottom; break;
-    default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
+    case atLeft: return atRight;
+    case atRight: return atLeft;
+    case atBottom: return atTop;
+    case atTop: return atBottom;
   }
+  qDebug() << Q_FUNC_INFO << "invalid axis type";
+  return atLeft;
 }
 
 /* inherits documentation from base class */
@@ -8921,9 +9627,21 @@ void QCPAxis::wheelEvent(QWheelEvent *event)
     return;
   }
   
-  const double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+  const double delta = event->delta();
+#else
+  const double delta = event->angleDelta().y();
+#endif
+  
+#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
+  const QPointF pos = event->pos();
+#else
+  const QPointF pos = event->position();
+#endif
+  
+  const double wheelSteps = delta/120.0; // a single step delta is +/-120 usually
   const double factor = qPow(mAxisRect->rangeZoomFactor(orientation()), wheelSteps);
-  scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y()));
+  scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? pos.x() : pos.y()));
   mParentPlot->replot();
 }
 
@@ -9015,7 +9733,7 @@ void QCPAxis::setupTickVectors()
   if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
   
   QVector<QString> oldLabels = mTickVectorLabels;
-  mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0);
+  mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : nullptr, mTickLabels ? &mTickVectorLabels : nullptr);
   mCachedMarginValid &= mTickVectorLabels == oldLabels; // if labels have changed, margin might have changed, too
 }
 
@@ -9253,12 +9971,12 @@ void QCPAxisPainterPrivate::draw(QCPPainter *painter)
     int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
     if (QCPAxis::orientation(type) == Qt::Horizontal)
     {
-      for (int i=0; i<tickPositions.size(); ++i)
-        painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
+      foreach (double tickPos, tickPositions)
+        painter->drawLine(QLineF(tickPos+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPos+xCor, origin.y()+tickLengthIn*tickDir+yCor));
     } else
     {
-      for (int i=0; i<tickPositions.size(); ++i)
-        painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
+      foreach (double tickPos, tickPositions)
+        painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPos+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPos+yCor));
     }
   }
   
@@ -9270,12 +9988,12 @@ void QCPAxisPainterPrivate::draw(QCPPainter *painter)
     int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
     if (QCPAxis::orientation(type) == Qt::Horizontal)
     {
-      for (int i=0; i<subTickPositions.size(); ++i)
-        painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
+      foreach (double subTickPos, subTickPositions)
+        painter->drawLine(QLineF(subTickPos+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPos+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
     } else
     {
-      for (int i=0; i<subTickPositions.size(); ++i)
-        painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
+      foreach (double subTickPos, subTickPositions)
+        painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPos+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPos+yCor));
     }
   }
   margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
@@ -9402,9 +10120,16 @@ void QCPAxisPainterPrivate::draw(QCPPainter *painter)
   Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
   direction) needed to fit the axis.
 */
-int QCPAxisPainterPrivate::size() const
+int QCPAxisPainterPrivate::size()
 {
   int result = 0;
+
+  QByteArray newHash = generateLabelParameterHash();
+  if (newHash != mLabelParameterHash)
+  {
+    mLabelCache.clear();
+    mLabelParameterHash = newHash;
+  }
   
   // get length of tick marks pointing outwards:
   if (!tickPositions.isEmpty())
@@ -9416,8 +10141,8 @@ int QCPAxisPainterPrivate::size() const
     QSize tickLabelsSize(0, 0);
     if (!tickLabels.isEmpty())
     {
-      for (int i=0; i<tickLabels.size(); ++i)
-        getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
+      foreach (const QString &tickLabel, tickLabels)
+        getMaxTickLabelSize(tickLabelFont, tickLabel, &tickLabelsSize);
       result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
     result += tickLabelPadding;
     }
@@ -9431,7 +10156,7 @@ int QCPAxisPainterPrivate::size() const
     bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
     result += bounds.height() + labelPadding;
   }
-  
+
   return result;
 }
 
@@ -9458,9 +10183,9 @@ QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const
   QByteArray result;
   result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));
   result.append(QByteArray::number(tickLabelRotation));
-  result.append(QByteArray::number((int)tickLabelSide));
-  result.append(QByteArray::number((int)substituteExponent));
-  result.append(QByteArray::number((int)numberMultiplyCross));
+  result.append(QByteArray::number(int(tickLabelSide)));
+  result.append(QByteArray::number(int(substituteExponent)));
+  result.append(QByteArray::number(int(numberMultiplyCross)));
   result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
   result.append(tickLabelFont.toString().toLatin1());
   return result;
@@ -9657,9 +10382,9 @@ QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(con
     // prepare smaller font for exponent:
     result.expFont = font;
     if (result.expFont.pointSize() > 0)
-      result.expFont.setPointSize(result.expFont.pointSize()*0.75);
+      result.expFont.setPointSize(int(result.expFont.pointSize()*0.75));
     else
-      result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
+      result.expFont.setPixelSize(int(result.expFont.pixelSize()*0.75));
     // calculate bounding rects of base part(s), exponent part and total one:
     result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
     result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
@@ -9710,7 +10435,8 @@ QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &label
   bool doRotation = !qFuzzyIsNull(tickLabelRotation);
   bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
   double radians = tickLabelRotation/180.0*M_PI;
-  int x=0, y=0;
+  double x = 0;
+  double y = 0;
   if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
   {
     if (doRotation)
@@ -9785,7 +10511,7 @@ QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &label
     }
   }
   
-  return QPointF(x, y);
+  return {x, y};
 }
 
 /*! \internal
@@ -9818,8 +10544,8 @@ void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString
 /* end of 'src/axis/axis.cpp' */
 
 
-/* including file 'src/scatterstyle.cpp', size 17450                         */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/scatterstyle.cpp'    */
+/* modified 2021-03-29T02:30:44, size 17466 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPScatterStyle
@@ -10274,7 +11000,7 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
       const QRectF clipRect = painter->clipBoundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);
 #endif
       if (clipRect.contains(x, y))
-        painter->drawPixmap(x-widthHalf, y-heightHalf, mPixmap);
+        painter->drawPixmap(qRound(x-widthHalf), qRound(y-heightHalf), mPixmap);
       break;
     }
     case ssCustom:
@@ -10290,10 +11016,9 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
 }
 /* end of 'src/scatterstyle.cpp' */
 
-//amalgamation: add datacontainer.cpp
 
-/* including file 'src/plottable.cpp', size 38845                            */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottable.cpp'       */
+/* modified 2021-03-29T02:30:44, size 38818 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPSelectionDecorator
@@ -10331,9 +11056,8 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
 QCPSelectionDecorator::QCPSelectionDecorator() :
   mPen(QColor(80, 80, 255), 2.5),
   mBrush(Qt::NoBrush),
-  mScatterStyle(),
   mUsedScatterProperties(QCPScatterStyle::spNone),
-  mPlottable(0)
+  mPlottable(nullptr)
 {
 }
 
@@ -10539,7 +11263,7 @@ bool QCPSelectionDecorator::registerWithPlottable(QCPAbstractPlottable *plottabl
   </tr><tr>
     <td>QPointer<\ref QCPAxis> \b mKeyAxis, \b mValueAxis</td>
     <td>The key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates
-        to pixels in either the key or value dimension. Make sure to check whether the pointer is null before using it. If one of
+        to pixels in either the key or value dimension. Make sure to check whether the pointer is \c nullptr before using it. If one of
         the axes is null, don't draw the plottable.</td>
   </tr><tr>
     <td>\ref QCPSelectionDecorator \b mSelectionDecorator</td>
@@ -10697,7 +11421,7 @@ QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
   mKeyAxis(keyAxis),
   mValueAxis(valueAxis),
   mSelectable(QCP::stWhole),
-  mSelectionDecorator(0)
+  mSelectionDecorator(nullptr)
 {
   if (keyAxis->parentPlot() != valueAxis->parentPlot())
     qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
@@ -10713,7 +11437,7 @@ QCPAbstractPlottable::~QCPAbstractPlottable()
   if (mSelectionDecorator)
   {
     delete mSelectionDecorator;
-    mSelectionDecorator = 0;
+    mSelectionDecorator = nullptr;
   }
 }
 
@@ -10852,14 +11576,13 @@ void QCPAbstractPlottable::setSelectionDecorator(QCPSelectionDecorator *decorato
   {
     if (decorator->registerWithPlottable(this))
     {
-      if (mSelectionDecorator) // delete old decorator if necessary
-        delete mSelectionDecorator;
+      delete mSelectionDecorator; // delete old decorator if necessary
       mSelectionDecorator = decorator;
     }
   } else if (mSelectionDecorator) // just clear decorator
   {
     delete mSelectionDecorator;
-    mSelectionDecorator = 0;
+    mSelectionDecorator = nullptr;
   }
 }
 
@@ -11159,7 +11882,7 @@ QRect QCPAbstractPlottable::clipRect() const
   if (mKeyAxis && mValueAxis)
     return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
   else
-    return QRect();
+    return {};
 }
 
 /* inherits documentation from base class */
@@ -11265,8 +11988,8 @@ void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
 /* end of 'src/plottable.cpp' */
 
 
-/* including file 'src/item.cpp', size 49269                                 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/item.cpp'            */
+/* modified 2021-03-29T02:30:44, size 49486 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemAnchor
@@ -11297,8 +12020,8 @@ void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
 
 /*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition()
   
-  Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if
-  it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor).
+  Returns \c nullptr if this instance is merely a QCPItemAnchor, and a valid pointer of type
+  QCPItemPosition* if it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor).
   
   This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids
   dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with
@@ -11323,15 +12046,15 @@ QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentIte
 QCPItemAnchor::~QCPItemAnchor()
 {
   // unregister as parent at children:
-  foreach (QCPItemPosition *child, mChildrenX.toList())
+  foreach (QCPItemPosition *child, mChildrenX.values())
   {
     if (child->parentAnchorX() == this)
-      child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
+      child->setParentAnchorX(nullptr); // this acts back on this anchor and child removes itself from mChildrenX
   }
-  foreach (QCPItemPosition *child, mChildrenY.toList())
+  foreach (QCPItemPosition *child, mChildrenY.values())
   {
     if (child->parentAnchorY() == this)
-      child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
+      child->setParentAnchorY(nullptr); // this acts back on this anchor and child removes itself from mChildrenY
   }
 }
 
@@ -11351,12 +12074,12 @@ QPointF QCPItemAnchor::pixelPosition() const
     } else
     {
       qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
-      return QPointF();
+      return {};
     }
   } else
   {
     qDebug() << Q_FUNC_INFO << "no parent item set";
-    return QPointF();
+    return {};
   }
 }
 
@@ -11430,10 +12153,11 @@ void QCPItemAnchor::removeChildY(QCPItemPosition *pos)
 
   QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type
   defines how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel
-  coordinates, as plot coordinates of certain axes, etc. For more advanced plots it is also
+  coordinates, as plot coordinates of certain axes (\ref QCPItemPosition::setAxes), as fractions of
+  the axis rect (\ref QCPItemPosition::setAxisRect), etc. For more advanced plots it is also
   possible to assign different types per X/Y coordinate of the position (see \ref setTypeX, \ref
-  setTypeY). This way an item could be positioned at a fixed pixel distance from the top in the Y
-  direction, while following a plot coordinate in the X direction.
+  setTypeY). This way an item could be positioned for example at a fixed pixel distance from the
+  top in the Y direction, while following a plot coordinate in the X direction.
 
   A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. This way you can tie
   multiple items together. If the QCPItemPosition has a parent, its coordinates (\ref setCoords)
@@ -11491,8 +12215,8 @@ QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *paren
   mPositionTypeY(ptAbsolute),
   mKey(0),
   mValue(0),
-  mParentAnchorX(0),
-  mParentAnchorY(0)
+  mParentAnchorX(nullptr),
+  mParentAnchorY(nullptr)
 {
 }
 
@@ -11501,15 +12225,15 @@ QCPItemPosition::~QCPItemPosition()
   // unregister as parent at children:
   // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
   //       the setParentAnchor(0) call the correct QCPItemPosition::pixelPosition function instead of QCPItemAnchor::pixelPosition
-  foreach (QCPItemPosition *child, mChildrenX.toList())
+  foreach (QCPItemPosition *child, mChildrenX.values())
   {
     if (child->parentAnchorX() == this)
-      child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
+      child->setParentAnchorX(nullptr); // this acts back on this anchor and child removes itself from mChildrenX
   }
-  foreach (QCPItemPosition *child, mChildrenY.toList())
+  foreach (QCPItemPosition *child, mChildrenY.values())
   {
     if (child->parentAnchorY() == this)
-      child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
+      child->setParentAnchorY(nullptr); // this acts back on this anchor and child removes itself from mChildrenY
   }
   // unregister as child in parent:
   if (mParentAnchorX)
@@ -11625,7 +12349,7 @@ void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type)
   during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position
   will be exactly on top of the parent anchor.
   
-  To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0.
+  To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to \c nullptr.
   
   If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type is
   set to \ref ptAbsolute, to keep the position in a valid state.
@@ -11954,7 +12678,7 @@ void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition)
         x -= mParentAnchorX->pixelPosition().x();
       else
         x -= mParentPlot->viewport().left();
-      x /= (double)mParentPlot->viewport().width();
+      x /= double(mParentPlot->viewport().width());
       break;
     }
     case ptAxisRectRatio:
@@ -11965,7 +12689,7 @@ void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition)
           x -= mParentAnchorX->pixelPosition().x();
         else
           x -= mAxisRect.data()->left();
-        x /= (double)mAxisRect.data()->width();
+        x /= double(mAxisRect.data()->width());
       } else
         qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
       break;
@@ -11996,7 +12720,7 @@ void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition)
         y -= mParentAnchorY->pixelPosition().y();
       else
         y -= mParentPlot->viewport().top();
-      y /= (double)mParentPlot->viewport().height();
+      y /= double(mParentPlot->viewport().height());
       break;
     }
     case ptAxisRectRatio:
@@ -12007,7 +12731,7 @@ void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition)
           y -= mParentAnchorY->pixelPosition().y();
         else
           y -= mAxisRect.data()->top();
-        y /= (double)mAxisRect.data()->height();
+        y /= double(mAxisRect.data()->height());
       } else
         qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
       break;
@@ -12209,7 +12933,7 @@ QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :
   parentPlot->registerItem(this);
   
   QList<QCPAxisRect*> rects = parentPlot->axisRects();
-  if (rects.size() > 0)
+  if (!rects.isEmpty())
   {
     setClipToAxisRect(true);
     setClipAxisRect(rects.first());
@@ -12297,7 +13021,7 @@ void QCPAbstractItem::setSelected(bool selected)
 
 /*!
   Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by
-  that name, returns 0.
+  that name, returns \c nullptr.
   
   This function provides an alternative way to access item positions. Normally, you access
   positions direcly by their member pointers (which typically have the same variable name as \a
@@ -12307,18 +13031,18 @@ void QCPAbstractItem::setSelected(bool selected)
 */
 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
 {
-  for (int i=0; i<mPositions.size(); ++i)
+  foreach (QCPItemPosition *position, mPositions)
   {
-    if (mPositions.at(i)->name() == name)
-      return mPositions.at(i);
+    if (position->name() == name)
+      return position;
   }
   qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
-  return 0;
+  return nullptr;
 }
 
 /*!
   Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by
-  that name, returns 0.
+  that name, returns \c nullptr.
   
   This function provides an alternative way to access item anchors. Normally, you access
   anchors direcly by their member pointers (which typically have the same variable name as \a
@@ -12328,13 +13052,13 @@ QCPItemPosition *QCPAbstractItem::position(const QString &name) const
 */
 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
 {
-  for (int i=0; i<mAnchors.size(); ++i)
+  foreach (QCPItemAnchor *anchor, mAnchors)
   {
-    if (mAnchors.at(i)->name() == name)
-      return mAnchors.at(i);
+    if (anchor->name() == name)
+      return anchor;
   }
   qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
-  return 0;
+  return nullptr;
 }
 
 /*!
@@ -12347,9 +13071,9 @@ QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
 */
 bool QCPAbstractItem::hasAnchor(const QString &name) const
 {
-  for (int i=0; i<mAnchors.size(); ++i)
+  foreach (QCPItemAnchor *anchor, mAnchors)
   {
-    if (mAnchors.at(i)->name() == name)
+    if (anchor->name() == name)
       return true;
   }
   return false;
@@ -12408,13 +13132,13 @@ double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, boo
   double result = -1;
 
   // distance to border:
-  QList<QLineF> lines;
-  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
-        << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
-  double minDistSqr = std::numeric_limits<double>::max();
-  for (int i=0; i<lines.size(); ++i)
+  const QList<QLineF> lines = QList<QLineF>() << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
+                                              << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
+  const QCPVector2D posVec(pos);
+  double minDistSqr = (std::numeric_limits<double>::max)();
+  foreach (const QLineF &line, lines)
   {
-    double distSqr = QCPVector2D(pos).distanceSquaredToLine(lines.at(i).p1(), lines.at(i).p2());
+    double distSqr = posVec.distanceSquaredToLine(line.p1(), line.p2());
     if (distSqr < minDistSqr)
       minDistSqr = distSqr;
   }
@@ -12442,7 +13166,7 @@ double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, boo
 QPointF QCPAbstractItem::anchorPixelPosition(int anchorId) const
 {
   qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
-  return QPointF();
+  return {};
 }
 
 /*! \internal
@@ -12536,8 +13260,8 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
 /* end of 'src/item.cpp' */
 
 
-/* including file 'src/core.cpp', size 125037                                */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/core.cpp'             */
+/* modified 2021-03-29T02:30:44, size 127198 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCustomPlot
@@ -12685,8 +13409,8 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
   
   \a event is the mouse event that caused the click, \a legend is the legend that received the
   click and \a item is the legend item that received the click. If only the legend and no item is
-  clicked, \a item is 0. This happens for a click inside the legend padding or the space between
-  two items.
+  clicked, \a item is \c nullptr. This happens for a click inside the legend padding or the space
+  between two items.
   
   \see legendDoubleClick
 */
@@ -12697,8 +13421,8 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
   
   \a event is the mouse event that caused the click, \a legend is the legend that received the
   click and \a item is the legend item that received the click. If only the legend and no item is
-  clicked, \a item is 0. This happens for a click inside the legend padding or the space between
-  two items.
+  clicked, \a item is \c nullptr. This happens for a click inside the legend padding or the space
+  between two items.
   
   \see legendClick
 */
@@ -12727,7 +13451,28 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
   It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
   replot synchronously, it won't cause an infinite recursion.
   
-  \see replot, afterReplot
+  \see replot, afterReplot, afterLayout
+*/
+
+/*! \fn void QCustomPlot::afterLayout()
+
+  This signal is emitted immediately after the layout step has been completed, which occurs right
+  before drawing the plot. This is typically during a call to \ref replot, and in such cases this
+  signal is emitted in between the signals \ref beforeReplot and \ref afterReplot. Unlike those
+  signals however, this signal is also emitted during off-screen painting, such as when calling
+  \ref toPixmap or \ref savePdf.
+
+  The layout step queries all layouts and layout elements in the plot for their proposed size and
+  arranges the objects accordingly as preparation for the subsequent drawing step. Through this
+  signal, you have the opportunity to update certain things in your plot that depend crucially on
+  the exact dimensions/positioning of layout elements such as axes and axis rects.
+
+  \warning However, changing any parameters of this QCustomPlot instance which would normally
+  affect the layouting (e.g. axis range order of magnitudes, tick label sizes, etc.) will not issue
+  a second run of the layout step. It will propagate directly to the draw step and may cause
+  graphical inconsistencies such as overlapping objects, if sizes or positions have changed.
+
+  \see updateLayout, beforeReplot, afterReplot
 */
 
 /*! \fn void QCustomPlot::afterReplot()
@@ -12738,7 +13483,7 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
   It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
   replot synchronously, it won't cause an infinite recursion.
   
-  \see replot, beforeReplot
+  \see replot, beforeReplot, afterLayout
 */
 
 /* end of documentation of signals */
@@ -12754,12 +13499,12 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
   layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
   QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
   default legend is removed due to manipulation of the layout system (e.g. by removing the main
-  axis rect), the corresponding pointers become 0.
+  axis rect), the corresponding pointers become \c nullptr.
   
-  If an axis convenience pointer is currently zero and a new axis rect or a corresponding axis is
-  added in the place of the main axis rect, QCustomPlot resets the convenience pointers to the
-  according new axes. Similarly the \ref legend convenience pointer will be reset if a legend is
-  added after the main legend was removed before.
+  If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding
+  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to
+  the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend
+  is added after the main legend was removed before.
 */
 
 /*! \var QCPAxis *QCustomPlot::yAxis
@@ -12772,12 +13517,12 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
   layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
   QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
   default legend is removed due to manipulation of the layout system (e.g. by removing the main
-  axis rect), the corresponding pointers become 0.
+  axis rect), the corresponding pointers become \c nullptr.
   
-  If an axis convenience pointer is currently zero and a new axis rect or a corresponding axis is
-  added in the place of the main axis rect, QCustomPlot resets the convenience pointers to the
-  according new axes. Similarly the \ref legend convenience pointer will be reset if a legend is
-  added after the main legend was removed before.
+  If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding
+  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to
+  the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend
+  is added after the main legend was removed before.
 */
 
 /*! \var QCPAxis *QCustomPlot::xAxis2
@@ -12792,12 +13537,12 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
   layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
   QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
   default legend is removed due to manipulation of the layout system (e.g. by removing the main
-  axis rect), the corresponding pointers become 0.
+  axis rect), the corresponding pointers become \c nullptr.
   
-  If an axis convenience pointer is currently zero and a new axis rect or a corresponding axis is
-  added in the place of the main axis rect, QCustomPlot resets the convenience pointers to the
-  according new axes. Similarly the \ref legend convenience pointer will be reset if a legend is
-  added after the main legend was removed before.
+  If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding
+  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to
+  the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend
+  is added after the main legend was removed before.
 */
 
 /*! \var QCPAxis *QCustomPlot::yAxis2
@@ -12812,12 +13557,12 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
   layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
   QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
   default legend is removed due to manipulation of the layout system (e.g. by removing the main
-  axis rect), the corresponding pointers become 0.
+  axis rect), the corresponding pointers become \c nullptr.
   
-  If an axis convenience pointer is currently zero and a new axis rect or a corresponding axis is
-  added in the place of the main axis rect, QCustomPlot resets the convenience pointers to the
-  according new axes. Similarly the \ref legend convenience pointer will be reset if a legend is
-  added after the main legend was removed before.
+  If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding
+  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to
+  the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend
+  is added after the main legend was removed before.
 */
 
 /*! \var QCPLegend *QCustomPlot::legend
@@ -12832,12 +13577,12 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
   access the new legend. For example, legends can be placed inside an axis rect's \ref
   QCPAxisRect::insetLayout "inset layout", and must then also be accessed via the inset layout. If
   the default legend is removed due to manipulation of the layout system (e.g. by removing the main
-  axis rect), the corresponding pointer becomes 0.
+  axis rect), the corresponding pointer becomes \c nullptr.
   
-  If an axis convenience pointer is currently zero and a new axis rect or a corresponding axis is
-  added in the place of the main axis rect, QCustomPlot resets the convenience pointers to the
-  according new axes. Similarly the \ref legend convenience pointer will be reset if a legend is
-  added after the main legend was removed before.
+  If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding
+  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to
+  the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend
+  is added after the main legend was removed before.
 */
 
 /* end of documentation of public members */
@@ -12847,33 +13592,35 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const
 */
 QCustomPlot::QCustomPlot(QWidget *parent) :
   QWidget(parent),
-  xAxis(0),
-  yAxis(0),
-  xAxis2(0),
-  yAxis2(0),
-  legend(0),
+  xAxis(nullptr),
+  yAxis(nullptr),
+  xAxis2(nullptr),
+  yAxis2(nullptr),
+  legend(nullptr),
   mBufferDevicePixelRatio(1.0), // will be adapted to primary screen below
-  mPlotLayout(0),
+  mPlotLayout(nullptr),
   mAutoAddPlottableToLegend(true),
   mAntialiasedElements(QCP::aeNone),
   mNotAntialiasedElements(QCP::aeNone),
-  mInteractions(0),
+  mInteractions(QCP::iNone),
   mSelectionTolerance(8),
   mNoAntialiasingOnDrag(false),
   mBackgroundBrush(Qt::white, Qt::SolidPattern),
   mBackgroundScaled(true),
   mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
-  mCurrentLayer(0),
+  mCurrentLayer(nullptr),
   mPlottingHints(QCP::phCacheLabels|QCP::phImmediateRefresh),
   mMultiSelectModifier(Qt::ControlModifier),
   mSelectionRectMode(QCP::srmNone),
-  mSelectionRect(0),
+  mSelectionRect(nullptr),
   mOpenGl(false),
   mMouseHasMoved(false),
-  mMouseEventLayerable(0),
-  mMouseSignalLayerable(0),
+  mMouseEventLayerable(nullptr),
+  mMouseSignalLayerable(nullptr),
   mReplotting(false),
   mReplotQueued(false),
+  mReplotTime(0),
+  mReplotTimeAverage(0),
   mOpenGlMultisamples(16),
   mOpenGlAntialiasedElementsBackup(QCP::aeNone),
   mOpenGlCacheLabelsBackup(true)
@@ -12950,10 +13697,10 @@ QCustomPlot::~QCustomPlot()
   if (mPlotLayout)
   {
     delete mPlotLayout;
-    mPlotLayout = 0;
+    mPlotLayout = nullptr;
   }
   
-  mCurrentLayer = 0;
+  mCurrentLayer = nullptr;
   qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
   mLayers.clear();
 }
@@ -13263,8 +14010,7 @@ void QCustomPlot::setSelectionRectMode(QCP::SelectionRectMode mode)
 */
 void QCustomPlot::setSelectionRect(QCPSelectionRect *selectionRect)
 {
-  if (mSelectionRect)
-    delete mSelectionRect;
+  delete mSelectionRect;
   
   mSelectionRect = selectionRect;
   
@@ -13356,7 +14102,7 @@ void QCustomPlot::setOpenGl(bool enabled, int multisampling)
   Sets the viewport of this QCustomPlot. Usually users of QCustomPlot don't need to change the
   viewport manually.
 
-  The viewport is the area in which the plot is drawn. All mechanisms, e.g. margin caluclation take
+  The viewport is the area in which the plot is drawn. All mechanisms, e.g. margin calculation take
   the viewport to be the outer border of the plot. The viewport normally is the rect() of the
   QCustomPlot widget, i.e. a rect with top left (0, 0) and size of the QCustomPlot widget.
 
@@ -13392,8 +14138,8 @@ void QCustomPlot::setBufferDevicePixelRatio(double ratio)
   {
 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
     mBufferDevicePixelRatio = ratio;
-    for (int i=0; i<mPaintBuffers.size(); ++i)
-      mPaintBuffers.at(i)->setDevicePixelRatio(mBufferDevicePixelRatio);
+    foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
+      buffer->setDevicePixelRatio(mBufferDevicePixelRatio);
     // Note: axis label cache has devicePixelRatio as part of cache hash, so no need to manually clear cache here
 #else
     qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
@@ -13483,7 +14229,7 @@ void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
 }
 
 /*!
-  Returns the plottable with \a index. If the index is invalid, returns 0.
+  Returns the plottable with \a index. If the index is invalid, returns \c nullptr.
   
   There is an overloaded version of this function with no parameter which returns the last added
   plottable, see QCustomPlot::plottable()
@@ -13498,14 +14244,14 @@ QCPAbstractPlottable *QCustomPlot::plottable(int index)
   } else
   {
     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
-    return 0;
+    return nullptr;
   }
 }
 
 /*! \overload
   
   Returns the last plottable that was added to the plot. If there are no plottables in the plot,
-  returns 0.
+  returns \c nullptr.
   
   \see plottableCount
 */
@@ -13515,7 +14261,7 @@ QCPAbstractPlottable *QCustomPlot::plottable()
   {
     return mPlottables.last();
   } else
-    return 0;
+    return nullptr;
 }
 
 /*!
@@ -13605,38 +14351,17 @@ QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
 }
 
 /*!
-  Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines
-  (like graphs) have a tolerance band around them, see \ref setSelectionTolerance. If multiple
-  plottables come into consideration, the one closest to \a pos is returned.
-  
-  If \a onlySelectable is true, only plottables that are selectable
-  (QCPAbstractPlottable::setSelectable) are considered.
+  Returns any plottable at the pixel position \a pos. Since it can capture all plottables, the
+  return type is the abstract base class of all plottables, QCPAbstractPlottable.
   
-  If there is no plottable at \a pos, the return value is 0.
+  For details, and if you wish to specify a certain plottable type (e.g. QCPGraph), see the
+  template method plottableAt<PlottableType>()
   
-  \see itemAt, layoutElementAt
+  \see plottableAt<PlottableType>(), itemAt, layoutElementAt
 */
-QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
+QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable, int *dataIndex) const
 {
-  QCPAbstractPlottable *resultPlottable = 0;
-  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
-  
-  foreach (QCPAbstractPlottable *plottable, mPlottables)
-  {
-    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
-      continue;
-    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
-    {
-      double currentDistance = plottable->selectTest(pos, false);
-      if (currentDistance >= 0 && currentDistance < resultDistance)
-      {
-        resultPlottable = plottable;
-        resultDistance = currentDistance;
-      }
-    }
-  }
-  
-  return resultPlottable;
+  return plottableAt<QCPAbstractPlottable>(pos, onlySelectable, dataIndex);
 }
 
 /*!
@@ -13648,7 +14373,7 @@ bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const
 }
 
 /*!
-  Returns the graph with \a index. If the index is invalid, returns 0.
+  Returns the graph with \a index. If the index is invalid, returns \c nullptr.
   
   There is an overloaded version of this function with no parameter which returns the last created
   graph, see QCustomPlot::graph()
@@ -13663,14 +14388,14 @@ QCPGraph *QCustomPlot::graph(int index) const
   } else
   {
     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
-    return 0;
+    return nullptr;
   }
 }
 
 /*! \overload
   
   Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot,
-  returns 0.
+  returns \c nullptr.
   
   \see graphCount, addGraph
 */
@@ -13680,7 +14405,7 @@ QCPGraph *QCustomPlot::graph() const
   {
     return mGraphs.last();
   } else
-    return 0;
+    return nullptr;
 }
 
 /*!
@@ -13691,7 +14416,7 @@ QCPGraph *QCustomPlot::graph() const
   \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically
   "y") for the graph.
   
-  Returns a pointer to the newly created graph, or 0 if adding the graph failed.
+  Returns a pointer to the newly created graph, or \c nullptr if adding the graph failed.
   
   \see graph, graphCount, removeGraph, clearGraphs
 */
@@ -13702,12 +14427,12 @@ QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
   if (!keyAxis || !valueAxis)
   {
     qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
-    return 0;
+    return nullptr;
   }
   if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
   {
     qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
-    return 0;
+    return nullptr;
   }
   
   QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
@@ -13719,7 +14444,7 @@ QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
   Removes the specified \a graph from the plot and deletes it. If necessary, the corresponding
   legend item is also removed from the default legend (QCustomPlot::legend). If any other graphs in
   the plot have a channel fill set towards the removed graph, the channel fill property of those
-  graphs is reset to zero (no channel fill).
+  graphs is reset to \c nullptr (no channel fill).
   
   Returns true on success.
   
@@ -13788,7 +14513,7 @@ QList<QCPGraph*> QCustomPlot::selectedGraphs() const
 }
 
 /*!
-  Returns the item with \a index. If the index is invalid, returns 0.
+  Returns the item with \a index. If the index is invalid, returns \c nullptr.
   
   There is an overloaded version of this function with no parameter which returns the last added
   item, see QCustomPlot::item()
@@ -13803,14 +14528,14 @@ QCPAbstractItem *QCustomPlot::item(int index) const
   } else
   {
     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
-    return 0;
+    return nullptr;
   }
 }
 
 /*! \overload
   
   Returns the last item that was added to this plot. If there are no items in the plot,
-  returns 0.
+  returns \c nullptr.
   
   \see itemCount
 */
@@ -13820,7 +14545,7 @@ QCPAbstractItem *QCustomPlot::item() const
   {
     return mItems.last();
   } else
-    return 0;
+    return nullptr;
 }
 
 /*!
@@ -13901,39 +14626,17 @@ QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
 }
 
 /*!
-  Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref
-  QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref
-  setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is
-  returned.
+  Returns the item at the pixel position \a pos. Since it can capture all items, the
+  return type is the abstract base class of all items, QCPAbstractItem.
   
-  If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are
-  considered.
-  
-  If there is no item at \a pos, the return value is 0.
+  For details, and if you wish to specify a certain item type (e.g. QCPItemLine), see the
+  template method itemAt<ItemType>()
   
-  \see plottableAt, layoutElementAt
+  \see itemAt<ItemType>(), plottableAt, layoutElementAt
 */
 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
 {
-  QCPAbstractItem *resultItem = 0;
-  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
-  
-  foreach (QCPAbstractItem *item, mItems)
-  {
-    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
-      continue;
-    if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
-    {
-      double currentDistance = item->selectTest(pos, false);
-      if (currentDistance >= 0 && currentDistance < resultDistance)
-      {
-        resultItem = item;
-        resultDistance = currentDistance;
-      }
-    }
-  }
-  
-  return resultItem;
+  return itemAt<QCPAbstractItem>(pos, onlySelectable);
 }
 
 /*!
@@ -13947,8 +14650,8 @@ bool QCustomPlot::hasItem(QCPAbstractItem *item) const
 }
 
 /*!
-  Returns the layer with the specified \a name. If there is no layer with the specified name, 0 is
-  returned.
+  Returns the layer with the specified \a name. If there is no layer with the specified name, \c
+  nullptr is returned.
   
   Layer names are case-sensitive.
   
@@ -13961,12 +14664,12 @@ QCPLayer *QCustomPlot::layer(const QString &name) const
     if (layer->name() == name)
       return layer;
   }
-  return 0;
+  return nullptr;
 }
 
 /*! \overload
   
-  Returns the layer by \a index. If the index is invalid, 0 is returned.
+  Returns the layer by \a index. If the index is invalid, \c nullptr is returned.
   
   \see addLayer, moveLayer, removeLayer
 */
@@ -13978,7 +14681,7 @@ QCPLayer *QCustomPlot::layer(int index) const
   } else
   {
     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
-    return 0;
+    return nullptr;
   }
 }
 
@@ -14109,21 +14812,19 @@ bool QCustomPlot::removeLayer(QCPLayer *layer)
   bool isFirstLayer = removedIndex==0;
   QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
   QList<QCPLayerable*> children = layer->children();
-  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
-  {
-    for (int i=children.size()-1; i>=0; --i)
-      children.at(i)->moveToLayer(targetLayer, true);
-  } else  // append normally
-  {
-    for (int i=0; i<children.size(); ++i)
-      children.at(i)->moveToLayer(targetLayer, false);
-  }
+  if (isFirstLayer) // prepend in reverse order (such that relative order stays the same)
+    std::reverse(children.begin(), children.end());
+  foreach (QCPLayerable *child, children)
+    child->moveToLayer(targetLayer, isFirstLayer); // prepend if isFirstLayer, otherwise append
+  
   // if removed layer is current layer, change current layer to layer below/above:
   if (layer == mCurrentLayer)
     setCurrentLayer(targetLayer);
+  
   // invalidate the paint buffer that was responsible for this layer:
-  if (!layer->mPaintBuffer.isNull())
-    layer->mPaintBuffer.data()->setInvalidated();
+  if (QSharedPointer<QCPAbstractPaintBuffer> pb = layer->mPaintBuffer.toStrongRef())
+    pb->setInvalidated();
+  
   // remove layer:
   delete layer;
   mLayers.removeOne(layer);
@@ -14159,10 +14860,10 @@ bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::
     mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1));
   
   // invalidate the paint buffers that are responsible for the layers:
-  if (!layer->mPaintBuffer.isNull())
-    layer->mPaintBuffer.data()->setInvalidated();
-  if (!otherLayer->mPaintBuffer.isNull())
-    otherLayer->mPaintBuffer.data()->setInvalidated();
+  if (QSharedPointer<QCPAbstractPaintBuffer> pb = layer->mPaintBuffer.toStrongRef())
+    pb->setInvalidated();
+  if (QSharedPointer<QCPAbstractPaintBuffer> pb = otherLayer->mPaintBuffer.toStrongRef())
+    pb->setInvalidated();
   
   updateLayerIndices();
   return true;
@@ -14189,7 +14890,17 @@ int QCustomPlot::axisRectCount() const
   added, all of them may be accessed with this function in a linear fashion (even when they are
   nested in a layout hierarchy or inside other axis rects via QCPAxisRect::insetLayout).
   
-  \see axisRectCount, axisRects
+  The order of the axis rects is given by the fill order of the \ref QCPLayout that is holding
+  them. For example, if the axis rects are in the top level grid layout (accessible via \ref
+  QCustomPlot::plotLayout), they are ordered from left to right, top to bottom, if the layout's
+  default \ref QCPLayoutGrid::setFillOrder "setFillOrder" of \ref QCPLayoutGrid::foColumnsFirst
+  "foColumnsFirst" wasn't changed.
+  
+  If you want to access axis rects by their row and column index, use the layout interface. For
+  example, use \ref QCPLayoutGrid::element of the top level grid layout, and \c qobject_cast the
+  returned layout element to \ref QCPAxisRect. (See also \ref thelayoutsystem.)
+  
+  \see axisRectCount, axisRects, QCPLayoutGrid::setFillOrder
 */
 QCPAxisRect *QCustomPlot::axisRect(int index) const
 {
@@ -14200,14 +14911,20 @@ QCPAxisRect *QCustomPlot::axisRect(int index) const
   } else
   {
     qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
-    return 0;
+    return nullptr;
   }
 }
 
 /*!
   Returns all axis rects in the plot.
   
-  \see axisRectCount, axisRect
+  The order of the axis rects is given by the fill order of the \ref QCPLayout that is holding
+  them. For example, if the axis rects are in the top level grid layout (accessible via \ref
+  QCustomPlot::plotLayout), they are ordered from left to right, top to bottom, if the layout's
+  default \ref QCPLayoutGrid::setFillOrder "setFillOrder" of \ref QCPLayoutGrid::foColumnsFirst
+  "foColumnsFirst" wasn't changed.
+  
+  \see axisRectCount, axisRect, QCPLayoutGrid::setFillOrder
 */
 QList<QCPAxisRect*> QCustomPlot::axisRects() const
 {
@@ -14234,7 +14951,7 @@ QList<QCPAxisRect*> QCustomPlot::axisRects() const
 
 /*!
   Returns the layout element at pixel position \a pos. If there is no element at that position,
-  returns 0.
+  returns \c nullptr.
   
   Only visible elements are used. If \ref QCPLayoutElement::setVisible on the element itself or on
   any of its parent elements is set to false, it will not be considered.
@@ -14264,7 +14981,7 @@ QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
 /*!
   Returns the layout element of type \ref QCPAxisRect at pixel position \a pos. This method ignores
   other layout elements even if they are visually in front of the axis rect (e.g. a \ref
-  QCPLegend). If there is no axis rect at that position, returns 0.
+  QCPLegend). If there is no axis rect at that position, returns \c nullptr.
 
   Only visible axis rects are used. If \ref QCPLayoutElement::setVisible on the axis rect itself or
   on any of its parent elements is set to false, it will not be considered.
@@ -14273,7 +14990,7 @@ QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
 */
 QCPAxisRect *QCustomPlot::axisRectAt(const QPointF &pos) const
 {
-  QCPAxisRect *result = 0;
+  QCPAxisRect *result = nullptr;
   QCPLayoutElement *currentElement = mPlotLayout;
   bool searchSubElements = true;
   while (searchSubElements && currentElement)
@@ -14364,7 +15081,7 @@ void QCustomPlot::deselectAll()
   foreach (QCPLayer *layer, mLayers)
   {
     foreach (QCPLayerable *layerable, layer->children())
-      layerable->deselectEvent(0);
+      layerable->deselectEvent(nullptr);
   }
 }
 
@@ -14392,6 +15109,8 @@ void QCustomPlot::deselectAll()
   If a layer is in mode \ref QCPLayer::lmBuffered (\ref QCPLayer::setMode), it is also possible to
   replot only that specific layer via \ref QCPLayer::replot. See the documentation there for
   details.
+  
+  \see replotTime
 */
 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
 {
@@ -14411,23 +15130,52 @@ void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
   mReplotQueued = false;
   emit beforeReplot();
   
+# if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
+  QTime replotTimer;
+  replotTimer.start();
+# else
+  QElapsedTimer replotTimer;
+  replotTimer.start();
+# endif
+  
   updateLayout();
   // draw all layered objects (grid, axes, plottables, items, legend,...) into their buffers:
   setupPaintBuffers();
   foreach (QCPLayer *layer, mLayers)
     layer->drawToPaintBuffer();
-  for (int i=0; i<mPaintBuffers.size(); ++i)
-    mPaintBuffers.at(i)->setInvalidated(false);
+  foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
+    buffer->setInvalidated(false);
   
   if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority==rpImmediateRefresh)
     repaint();
   else
     update();
   
+# if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
+  mReplotTime = replotTimer.elapsed();
+# else
+  mReplotTime = replotTimer.nsecsElapsed()*1e-6;
+# endif
+  if (!qFuzzyIsNull(mReplotTimeAverage))
+    mReplotTimeAverage = mReplotTimeAverage*0.9 + mReplotTime*0.1; // exponential moving average with a time constant of 10 last replots
+  else
+    mReplotTimeAverage = mReplotTime; // no previous replots to average with, so initialize with replot time
+  
   emit afterReplot();
   mReplotting = false;
 }
 
+/*!
+  Returns the time in milliseconds that the last replot took. If \a average is set to true, an
+  exponential moving average over the last couple of replots is returned.
+  
+  \see replot
+*/
+double QCustomPlot::replotTime(bool average) const
+{
+  return average ? mReplotTimeAverage : mReplotTime;
+}
+
 /*!
   Rescales the axes such that all plottables (like graphs) in the plot are fully visible.
   
@@ -14717,16 +15465,18 @@ QSize QCustomPlot::sizeHint() const
 */
 void QCustomPlot::paintEvent(QPaintEvent *event)
 {
-  Q_UNUSED(event);
+  Q_UNUSED(event)
   QCPPainter painter(this);
   if (painter.isActive())
   {
-    painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+  painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
+#endif
     if (mBackgroundBrush.style() != Qt::NoBrush)
       painter.fillRect(mViewport, mBackgroundBrush);
     drawBackground(&painter);
-    for (int bufferIndex = 0; bufferIndex < mPaintBuffers.size(); ++bufferIndex)
-      mPaintBuffers.at(bufferIndex)->draw(&painter);
+    foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
+      buffer->draw(&painter);
   }
 }
 
@@ -14787,7 +15537,7 @@ void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
     else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(candidates.first()))
       emit itemDoubleClick(ai, event);
     else if (QCPLegend *lg = qobject_cast<QCPLegend*>(candidates.first()))
-      emit legendDoubleClick(lg, 0, event);
+      emit legendDoubleClick(lg, nullptr, event);
     else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(candidates.first()))
       emit legendDoubleClick(li->parentLegend(), li, event);
   }
@@ -14906,10 +15656,10 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
     else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(mMouseSignalLayerable))
       emit itemClick(ai, event);
     else if (QCPLegend *lg = qobject_cast<QCPLegend*>(mMouseSignalLayerable))
-      emit legendClick(lg, 0, event);
+      emit legendClick(lg, nullptr, event);
     else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(mMouseSignalLayerable))
       emit legendClick(li->parentLegend(), li, event);
-    mMouseSignalLayerable = 0;
+    mMouseSignalLayerable = nullptr;
   }
   
   if (mSelectionRect && mSelectionRect->isActive()) // Note: if a click was detected above, the selection rect is canceled there
@@ -14922,7 +15672,7 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
     if (mMouseEventLayerable)
     {
       mMouseEventLayerable->mouseReleaseEvent(event, mMousePressPos);
-      mMouseEventLayerable = 0;
+      mMouseEventLayerable = nullptr;
     }
   }
   
@@ -14940,12 +15690,18 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
 void QCustomPlot::wheelEvent(QWheelEvent *event)
 {
   emit mouseWheel(event);
+  
+#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
+  const QPointF pos = event->pos();
+#else
+  const QPointF pos = event->position();
+#endif
+  
   // forward event to layerable under cursor:
-  QList<QCPLayerable*> candidates = layerableListAt(event->pos(), false);
-  for (int i=0; i<candidates.size(); ++i)
+  foreach (QCPLayerable *candidate, layerableListAt(pos, false))
   {
     event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
-    candidates.at(i)->wheelEvent(event);
+    candidate->wheelEvent(event);
     if (event->isAccepted())
       break;
   }
@@ -14974,7 +15730,7 @@ void QCustomPlot::draw(QCPPainter *painter)
     layer->draw(painter);
   
   /* Debug code to draw all layout element rects
-  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
+  foreach (QCPLayoutElement *el, findChildren<QCPLayoutElement*>())
   {
     painter->setBrush(Qt::NoBrush);
     painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
@@ -14999,6 +15755,8 @@ void QCustomPlot::updateLayout()
   mPlotLayout->update(QCPLayoutElement::upPreparation);
   mPlotLayout->update(QCPLayoutElement::upMargins);
   mPlotLayout->update(QCPLayoutElement::upLayout);
+
+  emit afterLayout();
 }
 
 /*! \internal
@@ -15089,11 +15847,11 @@ void QCustomPlot::setupPaintBuffers()
   while (mPaintBuffers.size()-1 > bufferIndex)
     mPaintBuffers.removeLast();
   // resize buffers to viewport size and clear contents:
-  for (int i=0; i<mPaintBuffers.size(); ++i)
+  foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
   {
-    mPaintBuffers.at(i)->setSize(viewport().size()); // won't do anything if already correct size
-    mPaintBuffers.at(i)->clear(Qt::transparent);
-    mPaintBuffers.at(i)->setInvalidated();
+    buffer->setSize(viewport().size()); // won't do anything if already correct size
+    buffer->clear(Qt::transparent);
+    buffer->setInvalidated();
   }
 }
 
@@ -15127,16 +15885,16 @@ QCPAbstractPaintBuffer *QCustomPlot::createPaintBuffer()
 
   If any buffer is invalidated, a partial replot (\ref QCPLayer::replot) is not allowed and always
   causes a full replot (\ref QCustomPlot::replot) of all layers. This is the case when for example
-  the layer order has changed, new layers were added, layers were removed, or layer modes were
-  changed (\ref QCPLayer::setMode).
+  the layer order has changed, new layers were added or removed, layer modes were changed (\ref
+  QCPLayer::setMode), or layerables were added or removed.
 
   \see QCPAbstractPaintBuffer::setInvalidated
 */
 bool QCustomPlot::hasInvalidatedPaintBuffers()
 {
-  for (int i=0; i<mPaintBuffers.size(); ++i)
+  foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)
   {
-    if (mPaintBuffers.at(i)->invalidated())
+    if (buffer->invalidated())
       return true;
   }
   return false;
@@ -15230,13 +15988,13 @@ void QCustomPlot::freeOpenGl()
 void QCustomPlot::axisRemoved(QCPAxis *axis)
 {
   if (xAxis == axis)
-    xAxis = 0;
+    xAxis = nullptr;
   if (xAxis2 == axis)
-    xAxis2 = 0;
+    xAxis2 = nullptr;
   if (yAxis == axis)
-    yAxis = 0;
+    yAxis = nullptr;
   if (yAxis2 == axis)
-    yAxis2 = 0;
+    yAxis2 = nullptr;
   
   // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
 }
@@ -15249,7 +16007,7 @@ void QCustomPlot::axisRemoved(QCPAxis *axis)
 void QCustomPlot::legendRemoved(QCPLegend *legend)
 {
   if (this->legend == legend)
-    this->legend = 0;
+    this->legend = nullptr;
 }
 
 /*! \internal
@@ -15271,11 +16029,14 @@ void QCustomPlot::legendRemoved(QCPLegend *legend)
 */
 void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event)
 {
+  typedef QPair<QCPAbstractPlottable*, QCPDataSelection> SelectionCandidate;
+  typedef QMultiMap<int, SelectionCandidate> SelectionCandidates; // map key is number of selected data points, so we have selections sorted by size
+  
   bool selectionStateChanged = false;
   
   if (mInteractions.testFlag(QCP::iSelectPlottables))
   {
-    QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> > potentialSelections; // map key is number of selected data points, so we have selections sorted by size
+    SelectionCandidates potentialSelections;
     QRectF rectF(rect.normalized());
     if (QCPAxisRect *affectedAxisRect = axisRectAt(rectF.topLeft()))
     {
@@ -15286,7 +16047,7 @@ void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event)
         {
           QCPDataSelection dataSel = plottableInterface->selectTestRect(rectF, true);
           if (!dataSel.isEmpty())
-            potentialSelections.insertMulti(dataSel.dataPointCount(), QPair<QCPAbstractPlottable*, QCPDataSelection>(plottable, dataSel));
+            potentialSelections.insert(dataSel.dataPointCount(), SelectionCandidate(plottable, dataSel));
         }
       }
       
@@ -15295,8 +16056,8 @@ void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event)
         // only leave plottable with most selected points in map, since we will only select a single plottable:
         if (!potentialSelections.isEmpty())
         {
-          QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> >::iterator it = potentialSelections.begin();
-          while (it != potentialSelections.end()-1) // erase all except last element
+          SelectionCandidates::iterator it = potentialSelections.begin();
+          while (it != std::prev(potentialSelections.end())) // erase all except last element
             it = potentialSelections.erase(it);
         }
       }
@@ -15321,7 +16082,7 @@ void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event)
       }
       
       // go through selections in reverse (largest selection first) and emit select events:
-      QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> >::const_iterator it = potentialSelections.constEnd();
+      SelectionCandidates::const_iterator it = potentialSelections.constEnd();
       while (it != potentialSelections.constBegin())
       {
         --it;
@@ -15360,7 +16121,7 @@ void QCustomPlot::processRectZoom(QRect rect, QMouseEvent *event)
   if (QCPAxisRect *axisRect = axisRectAt(rect.topLeft()))
   {
     QList<QCPAxis*> affectedAxes = QList<QCPAxis*>() << axisRect->rangeZoomAxes(Qt::Horizontal) << axisRect->rangeZoomAxes(Qt::Vertical);
-    affectedAxes.removeAll(static_cast<QCPAxis*>(0));
+    affectedAxes.removeAll(static_cast<QCPAxis*>(nullptr));
     axisRect->zoom(QRectF(rect), affectedAxes);
   }
   replot(rpQueuedReplot); // always replot to make selection rect disappear
@@ -15537,13 +16298,13 @@ void QCustomPlot::updateLayerIndices() const
 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
 {
   QList<QVariant> details;
-  QList<QCPLayerable*> candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : 0);
+  QList<QCPLayerable*> candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : nullptr);
   if (selectionDetails && !details.isEmpty())
     *selectionDetails = details.first();
   if (!candidates.isEmpty())
     return candidates.first();
   else
-    return 0;
+    return nullptr;
 }
 
 /*! \internal
@@ -15552,7 +16313,8 @@ QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable,
   layerables that are selectable will be considered. (Layerable subclasses communicate their
   selectability via the QCPLayerable::selectTest method, by returning -1.)
 
-  The returned list is sorted by the layerable/drawing order. If you only need to know the top-most
+  The returned list is sorted by the layerable/drawing order such that the layerable that appears
+  on top in the plot is at index 0 of the returned list. If you only need to know the top
   layerable, rather use \ref layerableAt.
 
   \a selectionDetails is an output parameter that contains selection specifics of the affected
@@ -15575,7 +16337,7 @@ QList<QCPLayerable*> QCustomPlot::layerableListAt(const QPointF &pos, bool onlyS
       if (!layerables.at(i)->realVisibility())
         continue;
       QVariant details;
-      double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : 0);
+      double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : nullptr);
       if (dist >= 0 && dist < selectionTolerance())
       {
         result.append(layerables.at(i));
@@ -15614,7 +16376,7 @@ bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, d
   {
     case QCP::ruDotsPerMeter: dotsPerMeter = resolution; break;
     case QCP::ruDotsPerCentimeter: dotsPerMeter = resolution*100; break;
-    case QCP::ruDotsPerInch: dotsPerMeter = resolution/0.0254; break;
+    case QCP::ruDotsPerInch: dotsPerMeter = int(resolution/0.0254); break;
   }
   buffer.setDotsPerMeterX(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools
   buffer.setDotsPerMeterY(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools
@@ -15716,10 +16478,9 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
 }
 /* end of 'src/core.cpp' */
 
-//amalgamation: add plottable1d.cpp
 
-/* including file 'src/colorgradient.cpp', size 24646                        */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/colorgradient.cpp'   */
+/* modified 2021-03-29T02:30:44, size 25278 */
 
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15742,9 +16503,11 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
   configured color stops. This allows to display some portions of the data range as transparent in
   the plot.
   
+  How NaN values are interpreted can be configured with \ref setNanHandling.
+  
   \image html QCPColorGradient.png
   
-  The \ref QCPColorGradient(GradientPreset preset) constructor allows directly converting a \ref
+  The constructor \ref QCPColorGradient(GradientPreset preset) allows directly converting a \ref
   GradientPreset to a QCPColorGradient. This means that you can directly pass \ref GradientPreset
   to all the \a setGradient methods, e.g.:
   \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient
@@ -15763,6 +16526,8 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
 QCPColorGradient::QCPColorGradient() :
   mLevelCount(350),
   mColorInterpolation(ciRGB),
+  mNanHandling(nhNone),
+  mNanColor(Qt::black),
   mPeriodic(false),
   mColorBufferInvalidated(true)
 {
@@ -15778,6 +16543,8 @@ QCPColorGradient::QCPColorGradient() :
 QCPColorGradient::QCPColorGradient(GradientPreset preset) :
   mLevelCount(350),
   mColorInterpolation(ciRGB),
+  mNanHandling(nhNone),
+  mNanColor(Qt::black),
   mPeriodic(false),
   mColorBufferInvalidated(true)
 {
@@ -15790,6 +16557,8 @@ bool QCPColorGradient::operator==(const QCPColorGradient &other) const
 {
   return ((other.mLevelCount == this->mLevelCount) &&
           (other.mColorInterpolation == this->mColorInterpolation) &&
+          (other.mNanHandling == this ->mNanHandling) &&
+          (other.mNanColor == this->mNanColor) &&
           (other.mPeriodic == this->mPeriodic) &&
           (other.mColorStops == this->mColorStops));
 }
@@ -15859,6 +16628,27 @@ void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolatio
   }
 }
 
+/*!
+  Sets how NaNs in the data are displayed in the plot.
+  
+  \see setNanColor
+*/
+void QCPColorGradient::setNanHandling(QCPColorGradient::NanHandling handling)
+{
+  mNanHandling = handling;
+}
+
+/*!
+  Sets the color that NaN data is represented by, if \ref setNanHandling is set
+  to ref nhNanColor.
+  
+  \see setNanHandling
+*/
+void QCPColorGradient::setNanColor(const QColor &color)
+{
+  mNanColor = color;
+}
+
 /*!
   Sets whether data points that are outside the configured data range (e.g. \ref
   QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether
@@ -15894,7 +16684,7 @@ void QCPColorGradient::setPeriodic(bool enabled)
   
   Use the overloaded method to additionally provide alpha map data.
 
-  The QRgb values that are placed in \a scanLine have their r, g and b components premultiplied
+  The QRgb values that are placed in \a scanLine have their r, g, and b components premultiplied
   with alpha (see QImage::Format_ARGB32_Premultiplied).
 */
 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
@@ -15913,51 +16703,33 @@ void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb
   if (mColorBufferInvalidated)
     updateColorBuffer();
   
-  if (!logarithmic)
+  const bool skipNanCheck = mNanHandling == nhNone;
+  const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower);
+  for (int i=0; i<n; ++i)
   {
-    const double posToIndexFactor = (mLevelCount-1)/range.size();
-    if (mPeriodic)
-    {
-      for (int i=0; i<n; ++i)
-      {
-        int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
-        if (index < 0)
-          index += mLevelCount;
-        scanLine[i] = mColorBuffer.at(index);
-      }
-    } else
+    const double value = data[dataIndexFactor*i];
+    if (skipNanCheck || !std::isnan(value))
     {
-      for (int i=0; i<n; ++i)
+      int index = int((!logarithmic ? value-range.lower : qLn(value/range.lower)) * posToIndexFactor);
+      if (!mPeriodic)
       {
-        int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
-        if (index < 0)
-          index = 0;
-        else if (index >= mLevelCount)
-          index = mLevelCount-1;
-        scanLine[i] = mColorBuffer.at(index);
-      }
-    }
-  } else // logarithmic == true
-  {
-    if (mPeriodic)
-    {
-      for (int i=0; i<n; ++i)
+        index = qBound(0, index, mLevelCount-1);
+      } else
       {
-        int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
+        index %= mLevelCount;
         if (index < 0)
           index += mLevelCount;
-        scanLine[i] = mColorBuffer.at(index);
       }
+      scanLine[i] = mColorBuffer.at(index);
     } else
     {
-      for (int i=0; i<n; ++i)
+      switch(mNanHandling)
       {
-        int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
-        if (index < 0)
-          index = 0;
-        else if (index >= mLevelCount)
-          index = mLevelCount-1;
-        scanLine[i] = mColorBuffer.at(index);
+      case nhLowestColor: scanLine[i] = mColorBuffer.first(); break;
+      case nhHighestColor: scanLine[i] = mColorBuffer.last(); break;
+      case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break;
+      case nhNanColor: scanLine[i] = mNanColor.rgba(); break;
+      case nhNone: break; // shouldn't happen
       }
     }
   }
@@ -15992,83 +16764,41 @@ void QCPColorGradient::colorize(const double *data, const unsigned char *alpha,
   if (mColorBufferInvalidated)
     updateColorBuffer();
   
-  if (!logarithmic)
+  const bool skipNanCheck = mNanHandling == nhNone;
+  const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower);
+  for (int i=0; i<n; ++i)
   {
-    const double posToIndexFactor = (mLevelCount-1)/range.size();
-    if (mPeriodic)
+    const double value = data[dataIndexFactor*i];
+    if (skipNanCheck || !std::isnan(value))
     {
-      for (int i=0; i<n; ++i)
+      int index = int((!logarithmic ? value-range.lower : qLn(value/range.lower)) * posToIndexFactor);
+      if (!mPeriodic)
+      {
+        index = qBound(0, index, mLevelCount-1);
+      } else
       {
-        int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
+        index %= mLevelCount;
         if (index < 0)
           index += mLevelCount;
-        if (alpha[dataIndexFactor*i] == 255)
-        {
-          scanLine[i] = mColorBuffer.at(index);
-        } else
-        {
-          const QRgb rgb = mColorBuffer.at(index);
-          const float alphaF = alpha[dataIndexFactor*i]/255.0f;
-          scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
-        }
       }
-    } else
-    {
-      for (int i=0; i<n; ++i)
+      if (alpha[dataIndexFactor*i] == 255)
       {
-        int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
-        if (index < 0)
-          index = 0;
-        else if (index >= mLevelCount)
-          index = mLevelCount-1;
-        if (alpha[dataIndexFactor*i] == 255)
-        {
-          scanLine[i] = mColorBuffer.at(index);
-        } else
-        {
-          const QRgb rgb = mColorBuffer.at(index);
-          const float alphaF = alpha[dataIndexFactor*i]/255.0f;
-          scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
-        }
-      }
-    }
-  } else // logarithmic == true
-  {
-    if (mPeriodic)
-    {
-      for (int i=0; i<n; ++i)
+        scanLine[i] = mColorBuffer.at(index);
+      } else
       {
-        int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
-        if (index < 0)
-          index += mLevelCount;
-        if (alpha[dataIndexFactor*i] == 255)
-        {
-          scanLine[i] = mColorBuffer.at(index);
-        } else
-        {
-          const QRgb rgb = mColorBuffer.at(index);
-          const float alphaF = alpha[dataIndexFactor*i]/255.0f;
-          scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
-        }
+        const QRgb rgb = mColorBuffer.at(index);
+        const float alphaF = alpha[dataIndexFactor*i]/255.0f;
+        scanLine[i] = qRgba(int(qRed(rgb)*alphaF), int(qGreen(rgb)*alphaF), int(qBlue(rgb)*alphaF), int(qAlpha(rgb)*alphaF)); // also multiply r,g,b with alpha, to conform to Format_ARGB32_Premultiplied
       }
     } else
     {
-      for (int i=0; i<n; ++i)
+      switch(mNanHandling)
       {
-        int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
-        if (index < 0)
-          index = 0;
-        else if (index >= mLevelCount)
-          index = mLevelCount-1;
-        if (alpha[dataIndexFactor*i] == 255)
-        {
-          scanLine[i] = mColorBuffer.at(index);
-        } else
-        {
-          const QRgb rgb = mColorBuffer.at(index);
-          const float alphaF = alpha[dataIndexFactor*i]/255.0f;
-          scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
-        }
+      case nhLowestColor: scanLine[i] = mColorBuffer.first(); break;
+      case nhHighestColor: scanLine[i] = mColorBuffer.last(); break;
+      case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break;
+      case nhNanColor: scanLine[i] = mNanColor.rgba(); break;
+      case nhNone: break; // shouldn't happen
       }
     }
   }
@@ -16091,22 +16821,30 @@ QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logari
   // If you change something here, make sure to also adapt ::colorize()
   if (mColorBufferInvalidated)
     updateColorBuffer();
-  int index = 0;
-  if (!logarithmic)
-    index = (position-range.lower)*(mLevelCount-1)/range.size();
-  else
-    index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
-  if (mPeriodic)
+  
+  const bool skipNanCheck = mNanHandling == nhNone;
+  if (!skipNanCheck && std::isnan(position))
   {
-    index = index % mLevelCount;
-    if (index < 0)
-      index += mLevelCount;
+    switch(mNanHandling)
+    {
+    case nhLowestColor: return mColorBuffer.first();
+    case nhHighestColor: return mColorBuffer.last();
+    case nhTransparent: return qRgba(0, 0, 0, 0);
+    case nhNanColor: return mNanColor.rgba();
+    case nhNone: return qRgba(0, 0, 0, 0); // shouldn't happen
+    }
+  }
+  
+  const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower);
+  int index = int((!logarithmic ? position-range.lower : qLn(position/range.lower)) * posToIndexFactor);
+  if (!mPeriodic)
+  {
+    index = qBound(0, index, mLevelCount-1);
   } else
   {
+    index %= mLevelCount;
     if (index < 0)
-      index = 0;
-    else if (index >= mLevelCount)
-      index = mLevelCount-1;
+      index += mLevelCount;
   }
   return mColorBuffer.at(index);
 }
@@ -16274,7 +17012,7 @@ void QCPColorGradient::updateColorBuffer()
     mColorBuffer.resize(mLevelCount);
   if (mColorStops.size() > 1)
   {
-    double indexToPosFactor = 1.0/(double)(mLevelCount-1);
+    double indexToPosFactor = 1.0/double(mLevelCount-1);
     const bool useAlpha = stopsUseAlpha();
     for (int i=0; i<mLevelCount; ++i)
     {
@@ -16282,14 +17020,32 @@ void QCPColorGradient::updateColorBuffer()
       QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
       if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
       {
-        mColorBuffer[i] = (it-1).value().rgba();
+        if (useAlpha)
+        {
+          const QColor col = std::prev(it).value();
+          const double alphaPremultiplier = col.alpha()/255.0; // since we use QImage::Format_ARGB32_Premultiplied
+          mColorBuffer[i] = qRgba(int(col.red()*alphaPremultiplier),
+                                  int(col.green()*alphaPremultiplier),
+                                  int(col.blue()*alphaPremultiplier),
+                                  col.alpha());
+        } else
+          mColorBuffer[i] = std::prev(it).value().rgba();
       } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
       {
-        mColorBuffer[i] = it.value().rgba();
+        if (useAlpha)
+        {
+          const QColor &col = it.value();
+          const double alphaPremultiplier = col.alpha()/255.0; // since we use QImage::Format_ARGB32_Premultiplied
+          mColorBuffer[i] = qRgba(int(col.red()*alphaPremultiplier),
+                                  int(col.green()*alphaPremultiplier),
+                                  int(col.blue()*alphaPremultiplier),
+                                  col.alpha());
+        } else
+          mColorBuffer[i] = it.value().rgba();
       } else // position is in between stops (or on an intermediate stop), interpolate color
       {
         QMap<double, QColor>::const_iterator high = it;
-        QMap<double, QColor>::const_iterator low = it-1;
+        QMap<double, QColor>::const_iterator low = std::prev(it);
         double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
         switch (mColorInterpolation)
         {
@@ -16297,17 +17053,17 @@ void QCPColorGradient::updateColorBuffer()
           {
             if (useAlpha)
             {
-              const int alpha = (1-t)*low.value().alpha() + t*high.value().alpha();
-              const float alphaPremultiplier = alpha/255.0f; // since we use QImage::Format_ARGB32_Premultiplied
-              mColorBuffer[i] = qRgba(((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier,
-                                      ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier,
-                                      ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier,
+              const int alpha = int((1-t)*low.value().alpha() + t*high.value().alpha());
+              const double alphaPremultiplier = alpha/255.0; // since we use QImage::Format_ARGB32_Premultiplied
+              mColorBuffer[i] = qRgba(int( ((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier ),
+                                      int( ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier ),
+                                      int( ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier ),
                                       alpha);
             } else
             {
-              mColorBuffer[i] = qRgb(((1-t)*low.value().red() + t*high.value().red()),
-                                     ((1-t)*low.value().green() + t*high.value().green()),
-                                     ((1-t)*low.value().blue() + t*high.value().blue()));
+              mColorBuffer[i] = qRgb(int( ((1-t)*low.value().red() + t*high.value().red()) ),
+                                     int( ((1-t)*low.value().green() + t*high.value().green()) ),
+                                     int( ((1-t)*low.value().blue() + t*high.value().blue())) );
             }
             break;
           }
@@ -16330,8 +17086,8 @@ void QCPColorGradient::updateColorBuffer()
               const QRgb rgb = QColor::fromHsvF(hue,
                                                 (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),
                                                 (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
-              const float alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF();
-              mColorBuffer[i] = qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha);
+              const double alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF();
+              mColorBuffer[i] = qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha));
             }
             else
             {
@@ -16347,8 +17103,8 @@ void QCPColorGradient::updateColorBuffer()
   } else if (mColorStops.size() == 1)
   {
     const QRgb rgb = mColorStops.constBegin().value().rgb();
-    const float alpha = mColorStops.constBegin().value().alphaF();
-    mColorBuffer.fill(qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha));
+    const double alpha = mColorStops.constBegin().value().alphaF();
+    mColorBuffer.fill(qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha)));
   } else // mColorStops is empty, fill color buffer with black
   {
     mColorBuffer.fill(qRgb(0, 0, 0));
@@ -16358,8 +17114,8 @@ void QCPColorGradient::updateColorBuffer()
 /* end of 'src/colorgradient.cpp' */
 
 
-/* including file 'src/selectiondecorator-bracket.cpp', size 12313           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/selectiondecorator-bracket.cpp' */
+/* modified 2021-03-29T02:30:44, size 12308            */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPSelectionDecoratorBracket
@@ -16501,12 +17257,12 @@ void QCPSelectionDecoratorBracket::drawBracket(QCPPainter *painter, int directio
     }
     case bsHalfEllipse:
     {
-      painter->drawArc(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight, -90*16, -180*16*direction);
+      painter->drawArc(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight), -90*16, -180*16*direction);
       break;
     }
     case bsEllipse:
     {
-      painter->drawEllipse(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight);
+      painter->drawEllipse(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight));
       break;
     }
     case bsPlus:
@@ -16606,7 +17362,7 @@ double QCPSelectionDecoratorBracket::getTangentAngle(const QCPPlottableInterface
     pointsAverage += points[i];
     currentIndex += direction;
   }
-  pointsAverage /= (double)averageCount;
+  pointsAverage /= double(averageCount);
   
   // calculate slope of linear regression through points:
   double numSum = 0;
@@ -16634,18 +17390,18 @@ QPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInte
 {
   QCPAxis *keyAxis = mPlottable->keyAxis();
   QCPAxis *valueAxis = mPlottable->valueAxis();
-  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(0, 0); }
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {0, 0}; }
   
   if (keyAxis->orientation() == Qt::Horizontal)
-    return QPointF(keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)));
+    return {keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex))};
   else
-    return QPointF(valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)));
+    return {valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex))};
 }
 /* end of 'src/selectiondecorator-bracket.cpp' */
 
 
-/* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47584 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200  */
+/* including file 'src/layoutelements/layoutelement-axisrect.cpp' */
+/* modified 2021-03-29T02:30:44, size 47193                       */
 
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -16824,11 +17580,10 @@ QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
 QCPAxisRect::~QCPAxisRect()
 {
   delete mInsetLayout;
-  mInsetLayout = 0;
+  mInsetLayout = nullptr;
   
-  QList<QCPAxis*> axesList = axes();
-  for (int i=0; i<axesList.size(); ++i)
-    removeAxis(axesList.at(i));
+  foreach (QCPAxis *axis, axes())
+    removeAxis(axis);
 }
 
 /*!
@@ -16855,7 +17610,7 @@ QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
   } else
   {
     qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
-    return 0;
+    return nullptr;
   }
 }
 
@@ -16906,7 +17661,7 @@ QList<QCPAxis*> QCPAxisRect::axes() const
   previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership
   of the axis, so you may not delete it afterwards. Further, the \a axis must have been created
   with this axis rect as parent and with the same axis type as specified in \a type. If this is not
-  the case, a debug output is generated, the axis is not added, and the method returns 0.
+  the case, a debug output is generated, the axis is not added, and the method returns \c nullptr.
 
   This method can not be used to move \a axis between axis rects. The same \a axis instance must
   not be added multiple times to the same or different axis rects.
@@ -16928,20 +17683,20 @@ QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis)
     if (newAxis->axisType() != type)
     {
       qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
-      return 0;
+      return nullptr;
     }
     if (newAxis->axisRect() != this)
     {
       qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
-      return 0;
+      return nullptr;
     }
     if (axes().contains(newAxis))
     {
       qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
-      return 0;
+      return nullptr;
     }
   }
-  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
+  if (!mAxes[type].isEmpty()) // multiple axes on one side, add half-bar axis ending to additional axes with offset
   {
     bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
     newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
@@ -17140,10 +17895,10 @@ QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
 {
   // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
   QList<QCPAbstractPlottable*> result;
-  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
+  foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables)
   {
-    if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this || mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
-      result.append(mParentPlot->mPlottables.at(i));
+    if (plottable->keyAxis()->axisRect() == this || plottable->valueAxis()->axisRect() == this)
+      result.append(plottable);
   }
   return result;
 }
@@ -17160,10 +17915,10 @@ QList<QCPGraph*> QCPAxisRect::graphs() const
 {
   // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
   QList<QCPGraph*> result;
-  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
+  foreach (QCPGraph *graph, mParentPlot->mGraphs)
   {
-    if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
-      result.append(mParentPlot->mGraphs.at(i));
+    if (graph->keyAxis()->axisRect() == this || graph->valueAxis()->axisRect() == this)
+      result.append(graph);
   }
   return result;
 }
@@ -17183,21 +17938,20 @@ QList<QCPAbstractItem *> QCPAxisRect::items() const
   // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
   //       and miss those items that have this axis rect as clipAxisRect.
   QList<QCPAbstractItem*> result;
-  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
+  foreach (QCPAbstractItem *item, mParentPlot->mItems)
   {
-    if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
+    if (item->clipAxisRect() == this)
     {
-      result.append(mParentPlot->mItems.at(itemId));
+      result.append(item);
       continue;
     }
-    QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
-    for (int posId=0; posId<positions.size(); ++posId)
+    foreach (QCPItemPosition *position, item->positions())
     {
-      if (positions.at(posId)->axisRect() == this ||
-          positions.at(posId)->keyAxis()->axisRect() == this ||
-          positions.at(posId)->valueAxis()->axisRect() == this)
+      if (position->axisRect() == this ||
+          position->keyAxis()->axisRect() == this ||
+          position->valueAxis()->axisRect() == this)
       {
-        result.append(mParentPlot->mItems.at(itemId));
+        result.append(item);
         break;
       }
     }
@@ -17223,9 +17977,8 @@ void QCPAxisRect::update(UpdatePhase phase)
   {
     case upPreparation:
     {
-      QList<QCPAxis*> allAxes = axes();
-      for (int i=0; i<allAxes.size(); ++i)
-        allAxes.at(i)->setupTickVectors();
+      foreach (QCPAxis *axis, axes())
+        axis->setupTickVectors();
       break;
     }
     case upLayout:
@@ -17353,9 +18106,9 @@ void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
 {
   if (orientation == Qt::Horizontal)
-    return mRangeDragHorzAxis.isEmpty() ? 0 : mRangeDragHorzAxis.first().data();
+    return mRangeDragHorzAxis.isEmpty() ? nullptr : mRangeDragHorzAxis.first().data();
   else
-    return mRangeDragVertAxis.isEmpty() ? 0 : mRangeDragVertAxis.first().data();
+    return mRangeDragVertAxis.isEmpty() ? nullptr : mRangeDragVertAxis.first().data();
 }
 
 /*!
@@ -17367,9 +18120,9 @@ QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
 {
   if (orientation == Qt::Horizontal)
-    return mRangeZoomHorzAxis.isEmpty() ? 0 : mRangeZoomHorzAxis.first().data();
+    return mRangeZoomHorzAxis.isEmpty() ? nullptr : mRangeZoomHorzAxis.first().data();
   else
-    return mRangeZoomVertAxis.isEmpty() ? 0 : mRangeZoomVertAxis.first().data();
+    return mRangeZoomVertAxis.isEmpty() ? nullptr : mRangeZoomVertAxis.first().data();
 }
 
 /*!
@@ -17382,17 +18135,17 @@ QList<QCPAxis*> QCPAxisRect::rangeDragAxes(Qt::Orientation orientation)
   QList<QCPAxis*> result;
   if (orientation == Qt::Horizontal)
   {
-    for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
+    foreach (QPointer<QCPAxis> axis, mRangeDragHorzAxis)
     {
-      if (!mRangeDragHorzAxis.at(i).isNull())
-        result.append(mRangeDragHorzAxis.at(i).data());
+      if (!axis.isNull())
+        result.append(axis.data());
     }
   } else
   {
-    for (int i=0; i<mRangeDragVertAxis.size(); ++i)
+    foreach (QPointer<QCPAxis> axis, mRangeDragVertAxis)
     {
-      if (!mRangeDragVertAxis.at(i).isNull())
-        result.append(mRangeDragVertAxis.at(i).data());
+      if (!axis.isNull())
+        result.append(axis.data());
     }
   }
   return result;
@@ -17408,17 +18161,17 @@ QList<QCPAxis*> QCPAxisRect::rangeZoomAxes(Qt::Orientation orientation)
   QList<QCPAxis*> result;
   if (orientation == Qt::Horizontal)
   {
-    for (int i=0; i<mRangeZoomHorzAxis.size(); ++i)
+    foreach (QPointer<QCPAxis> axis, mRangeZoomHorzAxis)
     {
-      if (!mRangeZoomHorzAxis.at(i).isNull())
-        result.append(mRangeZoomHorzAxis.at(i).data());
+      if (!axis.isNull())
+        result.append(axis.data());
     }
   } else
   {
-    for (int i=0; i<mRangeZoomVertAxis.size(); ++i)
+    foreach (QPointer<QCPAxis> axis, mRangeZoomVertAxis)
     {
-      if (!mRangeZoomVertAxis.at(i).isNull())
-        result.append(mRangeZoomVertAxis.at(i).data());
+      if (!axis.isNull())
+        result.append(axis.data());
     }
   }
   return result;
@@ -17441,9 +18194,9 @@ double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
   default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
   is the left axis (yAxis).
   
-  To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
-  QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
-  Qt::Vertical</tt> as \a orientations.
+  To disable range dragging entirely, pass \c nullptr as \a orientations or remove \ref
+  QCP::iRangeDrag from \ref QCustomPlot::setInteractions. To enable range dragging for both
+  directions, pass <tt>Qt::Horizontal | Qt::Vertical</tt> as \a orientations.
   
   In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
   contains \ref QCP::iRangeDrag to enable the range dragging interaction.
@@ -17461,9 +18214,9 @@ void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
   QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
   axis is the left axis (yAxis).
 
-  To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
-  QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
-  Qt::Vertical</tt> as \a orientations.
+  To disable range zooming entirely, pass \c nullptr as \a orientations or remove \ref
+  QCP::iRangeZoom from \ref QCustomPlot::setInteractions. To enable range zooming for both
+  directions, pass <tt>Qt::Horizontal | Qt::Vertical</tt> as \a orientations.
   
   In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
   contains \ref QCP::iRangeZoom to enable the range zooming interaction.
@@ -17478,7 +18231,8 @@ void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
 /*! \overload
   
   Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging on
-  the QCustomPlot widget. Pass 0 if no axis shall be dragged in the respective orientation.
+  the QCustomPlot widget. Pass \c nullptr if no axis shall be dragged in the respective
+  orientation.
 
   Use the overload taking a list of axes, if multiple axes (more than one per orientation) shall
   react to dragging interactions.
@@ -17547,7 +18301,7 @@ void QCPAxisRect::setRangeDragAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> v
 
 /*!
   Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on
-  the QCustomPlot widget. Pass 0 if no axis shall be zoomed in the respective orientation.
+  the QCustomPlot widget. Pass \c nullptr if no axis shall be zoomed in the respective orientation.
 
   The two axes can be zoomed with different strengths, when different factors are passed to \ref
   setRangeZoomFactor(double horizontalFactor, double verticalFactor).
@@ -17725,7 +18479,7 @@ int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
   
   // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
   const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
-  if (axesList.size() > 0)
+  if (!axesList.isEmpty())
     return axesList.last()->offset() + axesList.last()->calculateMargin();
   else
     return 0;
@@ -17783,11 +18537,11 @@ void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)
     if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
     {
       mDragStartHorzRange.clear();
-      for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
-        mDragStartHorzRange.append(mRangeDragHorzAxis.at(i).isNull() ? QCPRange() : mRangeDragHorzAxis.at(i)->range());
+      foreach (QPointer<QCPAxis> axis, mRangeDragHorzAxis)
+        mDragStartHorzRange.append(axis.isNull() ? QCPRange() : axis->range());
       mDragStartVertRange.clear();
-      for (int i=0; i<mRangeDragVertAxis.size(); ++i)
-        mDragStartVertRange.append(mRangeDragVertAxis.at(i).isNull() ? QCPRange() : mRangeDragVertAxis.at(i)->range());
+      foreach (QPointer<QCPAxis> axis, mRangeDragVertAxis)
+        mDragStartVertRange.append(axis.isNull() ? QCPRange() : axis->range());
     }
   }
 }
@@ -17879,37 +18633,49 @@ void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
   dependent on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
   zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
   
-  Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
-  wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
-  multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
-  exponent of the range zoom factor. This takes care of the wheel direction automatically, by
-  inverting the factor, when the wheel step is negative (f^-1 = 1/f).
+  Note, that event->angleDelta() is usually +/-120 for single rotation steps. However, if the mouse
+  wheel is turned rapidly, many steps may bunch up to one event, so the delta may then be multiples
+  of 120. This is taken into account here, by calculating \a wheelSteps and using it as exponent of
+  the range zoom factor. This takes care of the wheel direction automatically, by inverting the
+  factor, when the wheel step is negative (f^-1 = 1/f).
 */
 void QCPAxisRect::wheelEvent(QWheelEvent *event)
 {
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+  const double delta = event->delta();
+#else
+  const double delta = event->angleDelta().y();
+#endif
+  
+#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
+  const QPointF pos = event->pos();
+#else
+  const QPointF pos = event->position();
+#endif
+  
   // Mouse range zooming interaction:
   if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
   {
     if (mRangeZoom != 0)
     {
       double factor;
-      double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
+      double wheelSteps = delta/120.0; // a single step delta is +/-120 usually
       if (mRangeZoom.testFlag(Qt::Horizontal))
       {
         factor = qPow(mRangeZoomFactorHorz, wheelSteps);
-        for (int i=0; i<mRangeZoomHorzAxis.size(); ++i)
+        foreach (QPointer<QCPAxis> axis, mRangeZoomHorzAxis)
         {
-          if (!mRangeZoomHorzAxis.at(i).isNull())
-            mRangeZoomHorzAxis.at(i)->scaleRange(factor, mRangeZoomHorzAxis.at(i)->pixelToCoord(event->pos().x()));
+          if (!axis.isNull())
+            axis->scaleRange(factor, axis->pixelToCoord(pos.x()));
         }
       }
       if (mRangeZoom.testFlag(Qt::Vertical))
       {
         factor = qPow(mRangeZoomFactorVert, wheelSteps);
-        for (int i=0; i<mRangeZoomVertAxis.size(); ++i)
+        foreach (QPointer<QCPAxis> axis, mRangeZoomVertAxis)
         {
-          if (!mRangeZoomVertAxis.at(i).isNull())
-            mRangeZoomVertAxis.at(i)->scaleRange(factor, mRangeZoomVertAxis.at(i)->pixelToCoord(event->pos().y()));
+          if (!axis.isNull())
+            axis->scaleRange(factor, axis->pixelToCoord(pos.y()));
         }
       }
       mParentPlot->replot();
@@ -17919,8 +18685,8 @@ void QCPAxisRect::wheelEvent(QWheelEvent *event)
 /* end of 'src/layoutelements/layoutelement-axisrect.cpp' */
 
 
-/* including file 'src/layoutelements/layoutelement-legend.cpp', size 31097  */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/layoutelements/layoutelement-legend.cpp' */
+/* modified 2021-03-29T02:30:44, size 31762                     */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPAbstractLegendItem
@@ -18186,9 +18952,9 @@ void QCPPlottableLegendItem::draw(QCPPainter *painter)
   if (!mPlottable) return;
   painter->setFont(getFont());
   painter->setPen(QPen(getTextColor()));
-  QSizeF iconSize = mParentLegend->iconSize();
-  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
-  QRectF iconRect(mRect.topLeft(), iconSize);
+  QSize iconSize = mParentLegend->iconSize();
+  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
+  QRect iconRect(mRect.topLeft(), iconSize);
   int textHeight = qMax(textRect.height(), iconSize.height());  // if text has smaller height than icon, center text vertically in icon height, else align tops
   painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
   // draw icon:
@@ -18216,7 +18982,7 @@ void QCPPlottableLegendItem::draw(QCPPainter *painter)
 */
 QSize QCPPlottableLegendItem::minimumOuterSizeHint() const
 {
-  if (!mPlottable) return QSize();
+  if (!mPlottable) return {};
   QSize result(0, 0);
   QRect textRect;
   QFontMetrics fontMetrics(getFont());
@@ -18259,7 +19025,7 @@ QSize QCPPlottableLegendItem::minimumOuterSizeHint() const
 
   Use the methods \ref setFillOrder and \ref setWrap inherited from \ref QCPLayoutGrid to control
   in which order (column first or row first) the legend is filled up when calling \ref addItem, and
-  at which column or row wrapping occurs.
+  at which column or row wrapping occurs. The default fill order for legends is \ref foRowsFirst.
 
   By default, every QCustomPlot has one legend (\ref QCustomPlot::legend) which is placed in the
   inset layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
@@ -18285,7 +19051,8 @@ QSize QCPPlottableLegendItem::minimumOuterSizeHint() const
   Note that by default, QCustomPlot already contains a legend ready to be used as \ref
   QCustomPlot::legend
 */
-QCPLegend::QCPLegend()
+QCPLegend::QCPLegend() :
+  mIconTextPadding{}
 {
   setFillOrder(QCPLayoutGrid::foRowsFirst);
   setWrap(0);
@@ -18561,7 +19328,8 @@ void QCPLegend::setSelectedTextColor(const QColor &color)
 }
 
 /*!
-  Returns the item with index \a i.
+  Returns the item with index \a i. If non-legend items were added to the legend, and the element
+  at the specified cell index is not a QCPAbstractLegendItem, returns \c nullptr.
 
   Note that the linear index depends on the current fill order (\ref setFillOrder).
 
@@ -18574,7 +19342,7 @@ QCPAbstractLegendItem *QCPLegend::item(int index) const
 
 /*!
   Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
-  If such an item isn't in the legend, returns 0.
+  If such an item isn't in the legend, returns \c nullptr.
   
   \see hasItemWithPlottable
 */
@@ -18588,11 +19356,13 @@ QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable
         return pli;
     }
   }
-  return 0;
+  return nullptr;
 }
 
 /*!
-  Returns the number of items currently in the legend.
+  Returns the number of items currently in the legend. It is identical to the base class
+  QCPLayoutGrid::elementCount(), and unlike the other "item" interface methods of QCPLegend,
+  doesn't only address elements which can be cast to QCPAbstractLegendItem.
 
   Note that if empty cells are in the legend (e.g. by calling methods of the \ref QCPLayoutGrid
   base class which allows creating empty cells), they are included in the returned count.
@@ -18695,8 +19465,12 @@ bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
 */
 void QCPLegend::clearItems()
 {
-  for (int i=itemCount()-1; i>=0; --i)
-    removeItem(i);
+  for (int i=elementCount()-1; i>=0; --i)
+  {
+    if (item(i))
+      removeAt(i); // don't use removeItem() because it would unnecessarily reorder the whole legend for each item
+  }
+  setFillOrder(fillOrder(), true); // get rid of empty cells by reordering once after all items are removed
 }
 
 /*!
@@ -18835,8 +19609,8 @@ void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
 /* end of 'src/layoutelements/layoutelement-legend.cpp' */
 
 
-/* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12761 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200     */
+/* including file 'src/layoutelements/layoutelement-textelement.cpp' */
+/* modified 2021-03-29T02:30:44, size 12925                          */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPTextElement
@@ -18886,7 +19660,7 @@ void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot) :
   QCPLayoutElement(parentPlot),
   mText(),
-  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
+  mTextFlags(Qt::AlignCenter),
   mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
   mTextColor(Qt::black),
   mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
@@ -18911,7 +19685,7 @@ QCPTextElement::QCPTextElement(QCustomPlot *parentPlot) :
 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text) :
   QCPLayoutElement(parentPlot),
   mText(text),
-  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
+  mTextFlags(Qt::AlignCenter),
   mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
   mTextColor(Qt::black),
   mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
@@ -18936,14 +19710,15 @@ QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text) :
 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize) :
   QCPLayoutElement(parentPlot),
   mText(text),
-  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
-  mFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below
+  mTextFlags(Qt::AlignCenter),
+  mFont(QFont(QLatin1String("sans serif"), int(pointSize))), // will be taken from parentPlot if available, see below
   mTextColor(Qt::black),
-  mSelectedFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below
+  mSelectedFont(QFont(QLatin1String("sans serif"), int(pointSize))), // will be taken from parentPlot if available, see below
   mSelectedTextColor(Qt::blue),
   mSelectable(false),
   mSelected(false)
 {
+  mFont.setPointSizeF(pointSize); // set here again as floating point, because constructor above only takes integer
   if (parentPlot)
   {
     mFont = parentPlot->font();
@@ -18963,14 +19738,15 @@ QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, dou
 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize) :
   QCPLayoutElement(parentPlot),
   mText(text),
-  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
-  mFont(QFont(fontFamily, pointSize)),
+  mTextFlags(Qt::AlignCenter),
+  mFont(QFont(fontFamily, int(pointSize))),
   mTextColor(Qt::black),
-  mSelectedFont(QFont(fontFamily, pointSize)),
+  mSelectedFont(QFont(fontFamily, int(pointSize))),
   mSelectedTextColor(Qt::blue),
   mSelectable(false),
   mSelected(false)
 {
+  mFont.setPointSizeF(pointSize); // set here again as floating point, because constructor above only takes integer
   setMargins(QMargins(2, 2, 2, 2));
 }
 
@@ -18983,7 +19759,7 @@ QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, con
 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QFont &font) :
   QCPLayoutElement(parentPlot),
   mText(text),
-  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
+  mTextFlags(Qt::AlignCenter),
   mFont(font),
   mTextColor(Qt::black),
   mSelectedFont(font),
@@ -19111,14 +19887,14 @@ void QCPTextElement::draw(QCPPainter *painter)
 {
   painter->setFont(mainFont());
   painter->setPen(QPen(mainTextColor()));
-  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
+  painter->drawText(mRect, mTextFlags, mText, &mTextBoundingRect);
 }
 
 /* inherits documentation from base class */
 QSize QCPTextElement::minimumOuterSizeHint() const
 {
   QFontMetrics metrics(mFont);
-  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size());
+  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size());
   result.rwidth() += mMargins.left()+mMargins.right();
   result.rheight() += mMargins.top()+mMargins.bottom();
   return result;
@@ -19128,7 +19904,7 @@ QSize QCPTextElement::minimumOuterSizeHint() const
 QSize QCPTextElement::maximumOuterSizeHint() const
 {
   QFontMetrics metrics(mFont);
-  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size());
+  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size());
   result.setWidth(QWIDGETSIZE_MAX);
   result.rheight() += mMargins.top()+mMargins.bottom();
   return result;
@@ -19239,8 +20015,8 @@ QColor QCPTextElement::mainTextColor() const
 /* end of 'src/layoutelements/layoutelement-textelement.cpp' */
 
 
-/* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 25770 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200    */
+/* including file 'src/layoutelements/layoutelement-colorscale.cpp' */
+/* modified 2021-03-29T02:30:44, size 26531                         */
 
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -19333,6 +20109,7 @@ QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :
   QCPLayoutElement(parentPlot),
   mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
   mDataScaleType(QCPAxis::stLinear),
+  mGradient(QCPColorGradient::gpCold),
   mBarWidth(20),
   mAxisRect(new QCPColorScaleAxisRectPrivate(this))
 {
@@ -19407,7 +20184,7 @@ void QCPColorScale::setType(QCPAxis::AxisType type)
     QString labelTransfer;
     QSharedPointer<QCPAxisTicker> tickerTransfer;
     // transfer/revert some settings on old axis if it exists:
-    bool doTransfer = (bool)mColorAxis;
+    bool doTransfer = !mColorAxis.isNull();
     if (doTransfer)
     {
       rangeTransfer = mColorAxis.data()->range();
@@ -19417,7 +20194,7 @@ void QCPColorScale::setType(QCPAxis::AxisType type)
       disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
       disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
     }
-    QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
+    const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
     foreach (QCPAxis::AxisType atype, allAxisTypes)
     {
       mAxisRect.data()->axis(atype)->setTicks(atype == mType);
@@ -19459,13 +20236,22 @@ void QCPColorScale::setDataRange(const QCPRange &dataRange)
 }
 
 /*!
-  Sets the scale type of the color scale, i.e. whether values are linearly associated with colors
+  Sets the scale type of the color scale, i.e. whether values are associated with colors linearly
   or logarithmically.
   
   It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is
   also equivalent to directly accessing the \ref axis and setting its scale type with \ref
   QCPAxis::setScaleType.
   
+  Note that this method controls the coordinate transformation. For logarithmic scales, you will
+  likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting
+  the color scale's \ref axis ticker to an instance of \ref QCPAxisTickerLog :
+  
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-colorscale
+  
+  See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick
+  creation.
+  
   \see setDataRange, setGradient
 */
 void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)
@@ -19538,9 +20324,16 @@ void QCPColorScale::setRangeDrag(bool enabled)
   }
   
   if (enabled)
+  {
     mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
-  else
-    mAxisRect.data()->setRangeDrag(0);
+  } else
+  {
+#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
+    mAxisRect.data()->setRangeDrag(nullptr);
+#else
+    mAxisRect.data()->setRangeDrag({});
+#endif
+  }
 }
 
 /*!
@@ -19558,9 +20351,16 @@ void QCPColorScale::setRangeZoom(bool enabled)
   }
   
   if (enabled)
+  {
     mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
-  else
-    mAxisRect.data()->setRangeZoom(0);
+  } else
+  {
+#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
+    mAxisRect.data()->setRangeDrag(nullptr);
+#else
+    mAxisRect.data()->setRangeZoom({});
+#endif
+  }
 }
 
 /*!
@@ -19592,15 +20392,15 @@ void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
   QCP::SignDomain sign = QCP::sdBoth;
   if (mDataScaleType == QCPAxis::stLogarithmic)
     sign = (mDataRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);
-  for (int i=0; i<maps.size(); ++i)
+  foreach (QCPColorMap *map, maps)
   {
-    if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
+    if (!map->realVisibility() && onlyVisibleMaps)
       continue;
     QCPRange mapRange;
-    if (maps.at(i)->colorScale() == this)
+    if (map->colorScale() == this)
     {
       bool currentFoundRange = true;
-      mapRange = maps.at(i)->data()->dataBounds();
+      mapRange = map->data()->dataBounds();
       if (sign == QCP::sdPositive)
       {
         if (mapRange.lower <= 0 && mapRange.upper > 0)
@@ -19754,7 +20554,7 @@ QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parent
 {
   setParentLayerable(parentColorScale);
   setMinimumMargins(QMargins(0, 0, 0, 0));
-  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
+  const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
   foreach (QCPAxis::AxisType type, allAxisTypes)
   {
     axis(type)->setVisible(true);
@@ -19830,7 +20630,7 @@ void QCPColorScaleAxisRectPrivate::updateGradientImage()
       pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
     mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
     for (int y=1; y<h; ++y)
-      memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
+      memcpy(pixels.at(y), pixels.first(), size_t(n)*sizeof(QRgb));
   } else
   {
     w = rect().width();
@@ -19855,7 +20655,7 @@ void QCPColorScaleAxisRectPrivate::updateGradientImage()
 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
 {
   // axis bases of four axes shall always (de-)selected synchronously:
-  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
+  const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
   foreach (QCPAxis::AxisType type, allAxisTypes)
   {
     if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
@@ -19880,7 +20680,7 @@ void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts
 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
 {
   // synchronize axis base selectability:
-  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
+  const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
   foreach (QCPAxis::AxisType type, allAxisTypes)
   {
     if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
@@ -19899,8 +20699,8 @@ void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectablePart
 /* end of 'src/layoutelements/layoutelement-colorscale.cpp' */
 
 
-/* including file 'src/plottables/plottable-graph.cpp', size 73960           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-graph.cpp' */
+/* modified 2021-03-29T02:30:44, size 74518            */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPGraphData
@@ -20058,7 +20858,10 @@ QCPGraphData::QCPGraphData(double key, double value) :
   To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function.
 */
 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
-  QCPAbstractPlottable1D<QCPGraphData>(keyAxis, valueAxis)
+  QCPAbstractPlottable1D<QCPGraphData>(keyAxis, valueAxis),
+  mLineStyle{},
+  mScatterSkip{},
+  mAdaptiveSampling{}
 {
   // special handling for QCPGraphs to maintain the simple graph interface:
   mParentPlot->registerGraph(this);
@@ -20068,7 +20871,7 @@ QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
   
   setLineStyle(lsLine);
   setScatterSkip(0);
-  setChannelFillGraph(0);
+  setChannelFillGraph(nullptr);
   setAdaptiveSampling(true);
 }
 
@@ -20166,14 +20969,14 @@ void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)
   if (targetGraph == this)
   {
     qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
-    mChannelFillGraph = 0;
+    mChannelFillGraph = nullptr;
     return;
   }
   // prevent setting channel target to a graph not in the plot:
   if (targetGraph && targetGraph->mParentPlot != mParentPlot)
   {
     qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
-    mChannelFillGraph = 0;
+    mChannelFillGraph = nullptr;
     return;
   }
   
@@ -20259,7 +21062,14 @@ void QCPGraph::addData(double key, double value)
   mDataContainer->add(QCPGraphData(key, value));
 }
 
-/* inherits documentation from base class */
+/*!
+  Implements a selectTest specific to this plottable's point geometry.
+
+  If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data
+  point to \a pos.
+  
+  \seebaseclassmethod \ref QCPAbstractPlottable::selectTest
+*/
 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
 {
   if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
@@ -20267,13 +21077,13 @@ double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *d
   if (!mKeyAxis || !mValueAxis)
     return -1;
   
-  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
   {
     QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
     double result = pointDistance(pos, closestDataPoint);
     if (details)
     {
-      int pointIndex = closestDataPoint-mDataContainer->constBegin();
+      int pointIndex = int(closestDataPoint-mDataContainer->constBegin());
       details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
     }
     return result;
@@ -20398,7 +21208,7 @@ void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
 
 /*! \internal
 
-  This method retrieves an optimized set of data points via \ref getOptimizedLineData, an branches
+  This method retrieves an optimized set of data points via \ref getOptimizedLineData, and branches
   out to the line style specific functions such as \ref dataToLines, \ref dataToStepLeftLines, etc.
   according to the line style of the graph.
 
@@ -20763,12 +21573,12 @@ void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const
   if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0) return;
   
   applyFillAntialiasingHint(painter);
-  QVector<QCPDataRange> segments = getNonNanSegments(lines, keyAxis()->orientation());
+  const QVector<QCPDataRange> segments = getNonNanSegments(lines, keyAxis()->orientation());
   if (!mChannelFillGraph)
   {
     // draw base fill under graph, fill goes all the way to the zero-value-line:
-    for (int i=0; i<segments.size(); ++i)
-      painter->drawPolygon(getFillPolygon(lines, segments.at(i)));
+    foreach (QCPDataRange segment, segments)
+      painter->drawPolygon(getFillPolygon(lines, segment));
   } else
   {
     // draw fill between this graph and mChannelFillGraph:
@@ -20795,8 +21605,8 @@ void QCPGraph::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scat
 {
   applyScattersAntialiasingHint(painter);
   style.applyTo(painter, mPen);
-  for (int i=0; i<scatters.size(); ++i)
-    style.drawShape(painter, scatters.at(i).x(), scatters.at(i).y());
+  foreach (const QPointF &scatter, scatters)
+    style.drawShape(painter, scatter.x(), scatter.y());
 }
 
 /*!  \internal
@@ -20856,13 +21666,13 @@ void QCPGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGr
   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
   if (begin == end) return;
   
-  int dataCount = end-begin;
-  int maxCount = std::numeric_limits<int>::max();
+  int dataCount = int(end-begin);
+  int maxCount = (std::numeric_limits<int>::max)();
   if (mAdaptiveSampling)
   {
     double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));
-    if (2*keyPixelSpan+2 < (double)std::numeric_limits<int>::max())
-      maxCount = 2*keyPixelSpan+2;
+    if (2*keyPixelSpan+2 < static_cast<double>((std::numeric_limits<int>::max)()))
+      maxCount = int(2*keyPixelSpan+2);
   }
   
   if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
@@ -20873,7 +21683,7 @@ void QCPGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGr
     QCPGraphDataContainer::const_iterator currentIntervalFirstPoint = it;
     int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction
     int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
-    double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound));
+    double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound));
     double lastIntervalEndKey = currentIntervalStartKey;
     double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
     bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
@@ -20904,7 +21714,7 @@ void QCPGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGr
         minValue = it->value;
         maxValue = it->value;
         currentIntervalFirstPoint = it;
-        currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound));
+        currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound));
         if (keyEpsilonVariable)
           keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
         intervalDataCount = 1;
@@ -20949,19 +21759,19 @@ void QCPGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGr
   
   const int scatterModulo = mScatterSkip+1;
   const bool doScatterSkip = mScatterSkip > 0;
-  int beginIndex = begin-mDataContainer->constBegin();
-  int endIndex = end-mDataContainer->constBegin();
+  int beginIndex = int(begin-mDataContainer->constBegin());
+  int endIndex = int(end-mDataContainer->constBegin());
   while (doScatterSkip && begin != end && beginIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter
   {
     ++beginIndex;
     ++begin;
   }
   if (begin == end) return;
-  int dataCount = end-begin;
-  int maxCount = std::numeric_limits<int>::max();
+  int dataCount = int(end-begin);
+  int maxCount = (std::numeric_limits<int>::max)();
   if (mAdaptiveSampling)
   {
-    int keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));
+    int keyPixelSpan = int(qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)));
     maxCount = 2*keyPixelSpan+2;
   }
   
@@ -20970,7 +21780,7 @@ void QCPGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGr
     double valueMaxRange = valueAxis->range().upper;
     double valueMinRange = valueAxis->range().lower;
     QCPGraphDataContainer::const_iterator it = begin;
-    int itIndex = beginIndex;
+    int itIndex = int(beginIndex);
     double minValue = it->value;
     double maxValue = it->value;
     QCPGraphDataContainer::const_iterator minValueIt = it;
@@ -20978,7 +21788,7 @@ void QCPGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGr
     QCPGraphDataContainer::const_iterator currentIntervalStart = it;
     int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction
     int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
-    double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound));
+    double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound));
     double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
     bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
     int intervalDataCount = 1;
@@ -21035,7 +21845,7 @@ void QCPGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGr
         minValue = it->value;
         maxValue = it->value;
         currentIntervalStart = it;
-        currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound));
+        currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound));
         if (keyEpsilonVariable)
           keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
         intervalDataCount = 1;
@@ -21062,7 +21872,7 @@ void QCPGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGr
       double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
       int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
       QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;
-      int intervalItIndex = intervalIt-mDataContainer->constBegin();
+      int intervalItIndex = int(intervalIt-mDataContainer->constBegin());
       int c = 0;
       while (intervalIt != it)
       {
@@ -21309,7 +22119,7 @@ QPointF QCPGraph::getFillBasePoint(QPointF matchingDataPoint) const
 {
   QCPAxis *keyAxis = mKeyAxis.data();
   QCPAxis *valueAxis = mValueAxis.data();
-  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; }
   
   QPointF result;
   if (valueAxis->scaleType() == QCPAxis::stLinear)
@@ -21583,7 +22393,7 @@ double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer:
     return -1.0;
   
   // calculate minimum distances to graph data points and find closestData iterator:
-  double minDistSqr = std::numeric_limits<double>::max();
+  double minDistSqr = (std::numeric_limits<double>::max)();
   // determine which key range comes into question, taking selection tolerance around pos into account:
   double posKeyMin, posKeyMax, dummy;
   pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);
@@ -21608,7 +22418,7 @@ double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer:
   {
     // line displayed, calculate distance to line segments:
     QVector<QPointF> lineData;
-    getLines(&lineData, QCPDataRange(0, dataCount()));
+    getLines(&lineData, QCPDataRange(0, dataCount())); // don't limit data range further since with sharp data spikes, line segments may be closer to test point than segments with closer key coordinate
     QCPVector2D p(pixelPoint);
     const int step = mLineStyle==lsImpulse ? 2 : 1; // impulse plot differs from other line styles in that the lineData points are only pairwise connected
     for (int i=0; i<lineData.size()-1; i+=step)
@@ -21647,8 +22457,8 @@ int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
 /* end of 'src/plottables/plottable-graph.cpp' */
 
 
-/* including file 'src/plottables/plottable-curve.cpp', size 63527           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-curve.cpp' */
+/* modified 2021-03-29T02:30:44, size 63851            */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPCurveData
@@ -21806,7 +22616,9 @@ QCPCurveData::QCPCurveData(double t, double key, double value) :
   but use QCustomPlot::removePlottable() instead.
 */
 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
-  QCPAbstractPlottable1D<QCPCurveData>(keyAxis, valueAxis)
+  QCPAbstractPlottable1D<QCPCurveData>(keyAxis, valueAxis),
+  mScatterSkip{},
+  mLineStyle{}
 {
   // modify inherited properties from abstract plottable:
   setPen(QPen(Qt::blue, 0));
@@ -22014,7 +22826,14 @@ void QCPCurve::addData(double key, double value)
     mDataContainer->add(QCPCurveData(0.0, key, value));
 }
 
-/* inherits documentation from base class */
+/*!
+  Implements a selectTest specific to this plottable's point geometry.
+
+  If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data
+  point to \a pos.
+  
+  \seebaseclassmethod \ref QCPAbstractPlottable::selectTest
+*/
 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
 {
   if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
@@ -22022,13 +22841,13 @@ double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *d
   if (!mKeyAxis || !mValueAxis)
     return -1;
   
-  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
   {
     QCPCurveDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
     double result = pointDistance(pos, closestDataPoint);
     if (details)
     {
-      int pointIndex = closestDataPoint-mDataContainer->constBegin();
+      int pointIndex = int( closestDataPoint-mDataContainer->constBegin() );
       details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
     }
     return result;
@@ -22178,9 +22997,9 @@ void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &poin
   // draw scatter point symbols:
   applyScattersAntialiasingHint(painter);
   style.applyTo(painter, mPen);
-  for (int i=0; i<points.size(); ++i)
-    if (!qIsNaN(points.at(i).x()) && !qIsNaN(points.at(i).y()))
-      style.drawShape(painter,  points.at(i));
+  foreach (const QPointF &point, points)
+    if (!qIsNaN(point.x()) && !qIsNaN(point.y()))
+      style.drawShape(painter,  point);
 }
 
 /*! \internal
@@ -22329,7 +23148,7 @@ void QCPCurve::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataR
     return;
   const int scatterModulo = mScatterSkip+1;
   const bool doScatterSkip = mScatterSkip > 0;
-  int endIndex = end-mDataContainer->constBegin();
+  int endIndex = int( end-mDataContainer->constBegin() );
   
   QCPRange keyRange = keyAxis->range();
   QCPRange valueRange = valueAxis->range();
@@ -22340,7 +23159,7 @@ void QCPCurve::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataR
   valueRange.upper = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.upper)+scatterWidth*valueAxis->pixelOrientation());
   
   QCPCurveDataContainer::const_iterator it = begin;
-  int itIndex = begin-mDataContainer->constBegin();
+  int itIndex = int( begin-mDataContainer->constBegin() );
   while (doScatterSkip && it != end && itIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter
   {
     ++itIndex;
@@ -22549,9 +23368,9 @@ QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double oth
     }
   }
   if (mKeyAxis->orientation() == Qt::Horizontal)
-    return QPointF(intersectKeyPx, intersectValuePx);
+    return {intersectKeyPx, intersectValuePx};
   else
-    return QPointF(intersectValuePx, intersectKeyPx);
+    return {intersectValuePx, intersectKeyPx};
 }
 
 /*! \internal
@@ -22855,12 +23674,12 @@ bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double
   const double valuePx = mValueAxis->coordToPixel(value);
   const double prevKeyPx = mKeyAxis->coordToPixel(prevKey);
   const double prevValuePx = mValueAxis->coordToPixel(prevValue);
-  if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
+  if (qFuzzyIsNull(keyPx-prevKeyPx)) // line is parallel to value axis
   {
     // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here
     intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMinPx) : QPointF(valueMinPx, keyPx)); // direction will be taken care of at end of method
     intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMaxPx) : QPointF(valueMaxPx, keyPx));
-  } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
+  } else if (qFuzzyIsNull(valuePx-prevValuePx)) // line is parallel to key axis
   {
     // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here
     intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, valuePx) : QPointF(valuePx, keyMinPx)); // direction will be taken care of at end of method
@@ -23064,7 +23883,7 @@ double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer:
   }
   
   // calculate minimum distances to curve data points and find closestData iterator:
-  double minDistSqr = std::numeric_limits<double>::max();
+  double minDistSqr = (std::numeric_limits<double>::max)();
   // iterate over found data points and then choose the one with the shortest distance to pos:
   QCPCurveDataContainer::const_iterator begin = mDataContainer->constBegin();
   QCPCurveDataContainer::const_iterator end = mDataContainer->constEnd();
@@ -23096,8 +23915,8 @@ double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer:
 /* end of 'src/plottables/plottable-curve.cpp' */
 
 
-/* including file 'src/plottables/plottable-bars.cpp', size 43512            */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-bars.cpp' */
+/* modified 2021-03-29T02:30:44, size 43907           */
 
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -23209,7 +24028,7 @@ void QCPBarsGroup::setSpacing(double spacing)
 
 /*!
   Returns the QCPBars instance with the specified \a index in this group. If no such QCPBars
-  exists, returns 0.
+  exists, returns \c nullptr.
 
   \see bars(), size
 */
@@ -23221,7 +24040,7 @@ QCPBars *QCPBarsGroup::bars(int index) const
   } else
   {
     qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
-    return 0;
+    return nullptr;
   }
 }
 
@@ -23232,8 +24051,9 @@ QCPBars *QCPBarsGroup::bars(int index) const
 */
 void QCPBarsGroup::clear()
 {
-  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
-    bars->setBarsGroup(0); // removes itself via removeBars
+  const QList<QCPBars*> oldBars = mBars;
+  foreach (QCPBars *bars, oldBars)
+    bars->setBarsGroup(nullptr); // removes itself from mBars via removeBars
 }
 
 /*!
@@ -23294,7 +24114,7 @@ void QCPBarsGroup::remove(QCPBars *bars)
   }
   
   if (mBars.contains(bars))
-    bars->setBarsGroup(0);
+    bars->setBarsGroup(nullptr);
   else
     qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
 }
@@ -23561,14 +24381,14 @@ QCPBarsData::QCPBarsData(double key, double value) :
 
 /*! \fn QCPBars *QCPBars::barBelow() const
   Returns the bars plottable that is directly below this bars plottable.
-  If there is no such plottable, returns 0.
+  If there is no such plottable, returns \c nullptr.
   
   \see barAbove, moveBelow, moveAbove
 */
 
 /*! \fn QCPBars *QCPBars::barAbove() const
   Returns the bars plottable that is directly above this bars plottable.
-  If there is no such plottable, returns 0.
+  If there is no such plottable, returns \c nullptr.
   
   \see barBelow, moveBelow, moveAbove
 */
@@ -23589,9 +24409,9 @@ QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
   QCPAbstractPlottable1D<QCPBarsData>(keyAxis, valueAxis),
   mWidth(0.75),
   mWidthType(wtPlotCoords),
-  mBarsGroup(0),
+  mBarsGroup(nullptr),
   mBaseValue(0),
-  mStackingGap(0)
+  mStackingGap(1)
 {
   // modify inherited properties from abstract plottable:
   mPen.setColor(Qt::blue);
@@ -23603,7 +24423,7 @@ QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
 
 QCPBars::~QCPBars()
 {
-  setBarsGroup(0);
+  setBarsGroup(nullptr);
   if (mBarBelow || mBarAbove)
     connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
 }
@@ -23673,7 +24493,7 @@ void QCPBars::setWidthType(QCPBars::WidthType widthType)
   Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \ref
   QCPBarsGroup::append.
   
-  To remove this QCPBars from any group, set \a barsGroup to 0.
+  To remove this QCPBars from any group, set \a barsGroup to \c nullptr.
 */
 void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup)
 {
@@ -23765,7 +24585,7 @@ void QCPBars::addData(double key, double value)
   is already between two other bars, the two other bars will be stacked on top of each other after
   the operation.
   
-  To remove this bars plottable from any stacking, set \a bars to 0.
+  To remove this bars plottable from any stacking, set \a bars to \c nullptr.
   
   \see moveBelow, barAbove, barBelow
 */
@@ -23798,7 +24618,7 @@ void QCPBars::moveBelow(QCPBars *bars)
   is already between two other bars, the two other bars will be stacked on top of each other after
   the operation.
   
-  To remove this bars plottable from any stacking, set \a bars to 0.
+  To remove this bars plottable from any stacking, set \a bars to \c nullptr.
   
   \see moveBelow, barBelow, barAbove
 */
@@ -23838,13 +24658,20 @@ QCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable
   for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
   {
     if (rect.intersects(getBarRect(it->key, it->value)))
-      result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
+      result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);
   }
   result.simplify();
   return result;
 }
 
-/* inherits documentation from base class */
+/*!
+  Implements a selectTest specific to this plottable's point geometry.
+
+  If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data
+  point to \a pos.
+  
+  \seebaseclassmethod \ref QCPAbstractPlottable::selectTest
+*/
 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
 {
   Q_UNUSED(details)
@@ -23853,7 +24680,7 @@ double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *de
   if (!mKeyAxis || !mValueAxis)
     return -1;
   
-  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
   {
     // get visible data range:
     QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
@@ -23864,7 +24691,7 @@ double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *de
       {
         if (details)
         {
-          int pointIndex = it-mDataContainer->constBegin();
+          int pointIndex = int(it-mDataContainer->constBegin());
           details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
         }
         return mParentPlot->selectionTolerance()*0.99;
@@ -23927,8 +24754,8 @@ QCPRange QCPBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain,
   QCPBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd();
   if (inKeyRange != QCPRange())
   {
-    itBegin = mDataContainer->findBegin(inKeyRange.lower);
-    itEnd = mDataContainer->findEnd(inKeyRange.upper);
+    itBegin = mDataContainer->findBegin(inKeyRange.lower, false);
+    itEnd = mDataContainer->findEnd(inKeyRange.upper, false);
   }
   for (QCPBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
   {
@@ -23960,19 +24787,19 @@ QPointF QCPBars::dataPixelPosition(int index) const
   {
     QCPAxis *keyAxis = mKeyAxis.data();
     QCPAxis *valueAxis = mValueAxis.data();
-    if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
+    if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; }
     
     const QCPDataContainer<QCPBarsData>::const_iterator it = mDataContainer->constBegin()+index;
     const double valuePixel = valueAxis->coordToPixel(getStackedBaseValue(it->key, it->value >= 0) + it->value);
     const double keyPixel = keyAxis->coordToPixel(it->key) + (mBarsGroup ? mBarsGroup->keyPixelOffset(this, it->key) : 0);
     if (keyAxis->orientation() == Qt::Horizontal)
-      return QPointF(keyPixel, valuePixel);
+      return {keyPixel, valuePixel};
     else
-      return QPointF(valuePixel, keyPixel);
+      return {valuePixel, keyPixel};
   } else
   {
     qDebug() << Q_FUNC_INFO << "Index out of bounds" << index;
-    return QPointF();
+    return {};
   }
 }
 
@@ -24115,7 +24942,7 @@ QRectF QCPBars::getBarRect(double key, double value) const
 {
   QCPAxis *keyAxis = mKeyAxis.data();
   QCPAxis *valueAxis = mValueAxis.data();
-  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); }
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; }
   
   double lowerPixelWidth, upperPixelWidth;
   getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
@@ -24241,22 +25068,22 @@ void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
   {
     // disconnect old bar below upper:
     if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
-      upper->mBarBelow.data()->mBarAbove = 0;
-    upper->mBarBelow = 0;
+      upper->mBarBelow.data()->mBarAbove = nullptr;
+    upper->mBarBelow = nullptr;
   } else if (!upper) // disconnect lower at top
   {
     // disconnect old bar above lower:
     if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
-      lower->mBarAbove.data()->mBarBelow = 0;
-    lower->mBarAbove = 0;
+      lower->mBarAbove.data()->mBarBelow = nullptr;
+    lower->mBarAbove = nullptr;
   } else // connect lower and upper
   {
     // disconnect old bar above lower:
     if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
-      lower->mBarAbove.data()->mBarBelow = 0;
+      lower->mBarAbove.data()->mBarBelow = nullptr;
     // disconnect old bar below upper:
     if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
-      upper->mBarBelow.data()->mBarAbove = 0;
+      upper->mBarBelow.data()->mBarAbove = nullptr;
     lower->mBarAbove = upper;
     upper->mBarBelow = lower;
   }
@@ -24264,8 +25091,8 @@ void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
 /* end of 'src/plottables/plottable-bars.cpp' */
 
 
-/* including file 'src/plottables/plottable-statisticalbox.cpp', size 28622  */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-statisticalbox.cpp' */
+/* modified 2021-03-29T02:30:44, size 28951                     */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPStatisticalBoxData
@@ -24663,13 +25490,20 @@ QCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool only
   for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
   {
     if (rect.intersects(getQuartileBox(it)))
-      result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
+      result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);
   }
   result.simplify();
   return result;
 }
 
-/* inherits documentation from base class */
+/*!
+  Implements a selectTest specific to this plottable's point geometry.
+
+  If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data
+  point to \a pos.
+  
+  \seebaseclassmethod \ref QCPAbstractPlottable::selectTest
+*/
 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
 {
   Q_UNUSED(details)
@@ -24678,13 +25512,13 @@ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QV
   if (!mKeyAxis || !mValueAxis)
     return -1;
   
-  if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
+  if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
   {
     // get visible data range:
     QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
     QCPStatisticalBoxDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
     getVisibleDataBounds(visibleBegin, visibleEnd);
-    double minDistSqr = std::numeric_limits<double>::max();
+    double minDistSqr = (std::numeric_limits<double>::max)();
     for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
     {
       if (getQuartileBox(it).contains(pos)) // quartile box
@@ -24697,10 +25531,11 @@ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QV
         }
       } else // whiskers
       {
-        const QVector<QLineF> whiskerBackbones(getWhiskerBackboneLines(it));
-        for (int i=0; i<whiskerBackbones.size(); ++i)
+        const QVector<QLineF> whiskerBackbones = getWhiskerBackboneLines(it);
+        const QCPVector2D posVec(pos);
+        foreach (const QLineF &backbone, whiskerBackbones)
         {
-          double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(whiskerBackbones.at(i));
+          double currentDistSqr = posVec.distanceSquaredToLine(backbone);
           if (currentDistSqr < minDistSqr)
           {
             minDistSqr = currentDistSqr;
@@ -24711,7 +25546,7 @@ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QV
     }
     if (details)
     {
-      int pointIndex = closestDataPoint-mDataContainer->constBegin();
+      int pointIndex = int(closestDataPoint-mDataContainer->constBegin());
       details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
     }
     return qSqrt(minDistSqr);
@@ -24918,8 +25753,8 @@ QVector<QLineF> QCPStatisticalBox::getWhiskerBarLines(QCPStatisticalBoxDataConta
 /* end of 'src/plottables/plottable-statisticalbox.cpp' */
 
 
-/* including file 'src/plottables/plottable-colormap.cpp', size 47881        */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-colormap.cpp' */
+/* modified 2021-03-29T02:30:44, size 48149               */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPColorMapData
@@ -24981,8 +25816,8 @@ QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &key
   mKeyRange(keyRange),
   mValueRange(valueRange),
   mIsEmpty(true),
-  mData(0),
-  mAlpha(0),
+  mData(nullptr),
+  mAlpha(nullptr),
   mDataModified(true)
 {
   setSize(keySize, valueSize);
@@ -24991,10 +25826,8 @@ QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &key
 
 QCPColorMapData::~QCPColorMapData()
 {
-  if (mData)
-    delete[] mData;
-  if (mAlpha)
-    delete[] mAlpha;
+  delete[] mData;
+  delete[] mAlpha;
 }
 
 /*!
@@ -25004,8 +25837,8 @@ QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) :
   mKeySize(0),
   mValueSize(0),
   mIsEmpty(true),
-  mData(0),
-  mAlpha(0),
+  mData(nullptr),
+  mAlpha(nullptr),
   mDataModified(true)
 {
   *this = other;
@@ -25029,9 +25862,9 @@ QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)
     setRange(other.keyRange(), other.valueRange());
     if (!isEmpty())
     {
-      memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
+      memcpy(mData, other.mData, sizeof(mData[0])*size_t(keySize*valueSize));
       if (mAlpha)
-        memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*keySize*valueSize);
+        memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*size_t(keySize*valueSize));
     }
     mDataBounds = other.mDataBounds;
     mDataModified = true;
@@ -25042,8 +25875,8 @@ QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)
 /* undocumented getter */
 double QCPColorMapData::data(double key, double value)
 {
-  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
-  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
+  int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 );
+  int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 );
   if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
     return mData[valueCell*mKeySize + keyCell];
   else
@@ -25093,24 +25926,23 @@ void QCPColorMapData::setSize(int keySize, int valueSize)
   {
     mKeySize = keySize;
     mValueSize = valueSize;
-    if (mData)
-      delete[] mData;
+    delete[] mData;
     mIsEmpty = mKeySize == 0 || mValueSize == 0;
     if (!mIsEmpty)
     {
 #ifdef __EXCEPTIONS
       try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
 #endif
-      mData = new double[mKeySize*mValueSize];
+      mData = new double[size_t(mKeySize*mValueSize)];
 #ifdef __EXCEPTIONS
-      } catch (...) { mData = 0; }
+      } catch (...) { mData = nullptr; }
 #endif
       if (mData)
         fill(0);
       else
         qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
     } else
-      mData = 0;
+      mData = nullptr;
     
     if (mAlpha) // if we had an alpha map, recreate it with new size
       createAlpha();
@@ -25209,8 +26041,8 @@ void QCPColorMapData::setValueRange(const QCPRange &valueRange)
 */
 void QCPColorMapData::setData(double key, double value, double z)
 {
-  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
-  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
+  int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 );
+  int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 );
   if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
   {
     mData[valueCell*mKeySize + keyCell] = z;
@@ -25325,7 +26157,7 @@ void QCPColorMapData::clearAlpha()
   if (mAlpha)
   {
     delete[] mAlpha;
-    mAlpha = 0;
+    mAlpha = nullptr;
     mDataModified = true;
   }
 }
@@ -25369,8 +26201,8 @@ void QCPColorMapData::fillAlpha(unsigned char alpha)
   
   The retrieved key/value cell indices can then be used for example with \ref setCell.
   
-  If you are only interested in a key or value index, you may pass 0 as \a valueIndex or \a
-  keyIndex.
+  If you are only interested in a key or value index, you may pass \c nullptr as \a valueIndex or
+  \a keyIndex.
   
   \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
   value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
@@ -25382,9 +26214,9 @@ void QCPColorMapData::fillAlpha(unsigned char alpha)
 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
 {
   if (keyIndex)
-    *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
+    *keyIndex = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 );
   if (valueIndex)
-    *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
+    *valueIndex = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 );
 }
 
 /*!
@@ -25392,7 +26224,7 @@ void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *
   instance. The resulting coordinates are returned via the output parameters \a key and \a
   value.
   
-  If you are only interested in a key or value coordinate, you may pass 0 as \a key or \a
+  If you are only interested in a key or value coordinate, you may pass \c nullptr as \a key or \a
   value.
   
   \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
@@ -25405,9 +26237,9 @@ void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *
 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
 {
   if (key)
-    *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
+    *key = keyIndex/double(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
   if (value)
-    *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
+    *value = valueIndex/double(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
 }
 
 /*! \internal
@@ -25432,9 +26264,9 @@ bool QCPColorMapData::createAlpha(bool initializeOpaque)
 #ifdef __EXCEPTIONS
   try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
 #endif
-    mAlpha = new unsigned char[mKeySize*mValueSize];
+    mAlpha = new unsigned char[size_t(mKeySize*mValueSize)];
 #ifdef __EXCEPTIONS
-  } catch (...) { mAlpha = 0; }
+  } catch (...) { mAlpha = nullptr; }
 #endif
   if (mAlpha)
   {
@@ -25485,13 +26317,14 @@ bool QCPColorMapData::createAlpha(bool initializeOpaque)
   
   \section qcpcolormap-appearance Changing the appearance
   
-  The central part of the appearance is the color gradient, which can be specified via \ref
+  Most important to the appearance is the color gradient, which can be specified via \ref
   setGradient. See the documentation of \ref QCPColorGradient for details on configuring a color
   gradient.
   
   The \a data range that is mapped to the colors of the gradient can be specified with \ref
   setDataRange. To make the data range encompass the whole data set minimum to maximum, call \ref
-  rescaleDataRange.
+  rescaleDataRange. If your data may contain NaN values, use \ref QCPColorGradient::setNanHandling
+  to define how they are displayed.
   
   \section qcpcolormap-transparency Transparency
   
@@ -25713,7 +26546,7 @@ void QCPColorMap::setTightBoundary(bool enabled)
   type of \a colorScale. After this call, you may change these properties at either the color map
   or the color scale, and the setting will be applied to both.
   
-  Pass 0 as \a colorScale to disconnect the color scale from this color map again.
+  Pass \c nullptr as \a colorScale to disconnect the color scale from this color map again.
 */
 void QCPColorMap::setColorScale(QCPColorScale *colorScale)
 {
@@ -25804,7 +26637,7 @@ double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant
   if (!mKeyAxis || !mValueAxis)
     return -1;
   
-  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
   {
     double posKey, posValue;
     pixelsToCoords(pos, posKey, posValue);
@@ -25848,7 +26681,7 @@ QCPRange QCPColorMap::getValueRange(bool &foundRange, QCP::SignDomain inSignDoma
     if (mMapData->keyRange().upper < inKeyRange.lower || mMapData->keyRange().lower > inKeyRange.upper)
     {
       foundRange = false;
-      return QCPRange();
+      return {};
     }
   }
   
@@ -25894,8 +26727,8 @@ void QCPColorMap::updateMapImage()
   const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
   const int keySize = mMapData->keySize();
   const int valueSize = mMapData->valueSize();
-  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
-  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
+  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
+  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
   
   // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
   if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
@@ -25996,15 +26829,15 @@ void QCPColorMap::draw(QCPPainter *painter)
   if (keyAxis()->orientation() == Qt::Horizontal)
   {
     if (mMapData->keySize() > 1)
-      halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
+      halfCellWidth = 0.5*imageRect.width()/double(mMapData->keySize()-1);
     if (mMapData->valueSize() > 1)
-      halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
+      halfCellHeight = 0.5*imageRect.height()/double(mMapData->valueSize()-1);
   } else // keyAxis orientation is Qt::Vertical
   {
     if (mMapData->keySize() > 1)
-      halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
+      halfCellHeight = 0.5*imageRect.height()/double(mMapData->keySize()-1);
     if (mMapData->valueSize() > 1)
-      halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
+      halfCellWidth = 0.5*imageRect.width()/double(mMapData->valueSize()-1);
   }
   imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
   const bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
@@ -26053,8 +26886,8 @@ void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
 /* end of 'src/plottables/plottable-colormap.cpp' */
 
 
-/* including file 'src/plottables/plottable-financial.cpp', size 42610       */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-financial.cpp' */
+/* modified 2021-03-29T02:30:44, size 42914                */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPFinancialData
@@ -26453,22 +27286,29 @@ QCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelec
   for (QCPFinancialDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
   {
     if (rect.intersects(selectionHitBox(it)))
-      result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
+      result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);
   }
   result.simplify();
   return result;
 }
 
-/* inherits documentation from base class */
-double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
-{
+/*!
+  Implements a selectTest specific to this plottable's point geometry.
+
+  If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data
+  point to \a pos.
+  
+  \seebaseclassmethod \ref QCPAbstractPlottable::selectTest
+*/
+double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
   Q_UNUSED(details)
   if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
     return -1;
   if (!mKeyAxis || !mValueAxis)
     return -1;
   
-  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
   {
     // get visible data range:
     QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
@@ -26485,7 +27325,7 @@ double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVarian
     }
     if (details)
     {
-      int pointIndex = closestDataPoint-mDataContainer->constBegin();
+      int pointIndex = int(closestDataPoint-mDataContainer->constBegin());
       details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
     }
     return result;
@@ -26852,7 +27692,7 @@ double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataCo
   QCPAxis *valueAxis = mValueAxis.data();
   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
 
-  double minDistSqr = std::numeric_limits<double>::max();
+  double minDistSqr = (std::numeric_limits<double>::max)();
   if (keyAxis->orientation() == Qt::Horizontal)
   {
     for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
@@ -26899,7 +27739,7 @@ double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancia
   QCPAxis *valueAxis = mValueAxis.data();
   if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
 
-  double minDistSqr = std::numeric_limits<double>::max();
+  double minDistSqr = (std::numeric_limits<double>::max)();
   if (keyAxis->orientation() == Qt::Horizontal)
   {
     for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
@@ -26994,7 +27834,7 @@ QRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator i
 {
   QCPAxis *keyAxis = mKeyAxis.data();
   QCPAxis *valueAxis = mValueAxis.data();
-  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); }
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; }
   
   double keyPixel = keyAxis->coordToPixel(it->key);
   double highPixel = valueAxis->coordToPixel(it->high);
@@ -27008,8 +27848,8 @@ QRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator i
 /* end of 'src/plottables/plottable-financial.cpp' */
 
 
-/* including file 'src/plottables/plottable-errorbar.cpp', size 37355        */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-errorbar.cpp' */
+/* modified 2021-03-29T02:30:44, size 37679               */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPErrorBarsData
@@ -27203,13 +28043,13 @@ void QCPErrorBars::setDataPlottable(QCPAbstractPlottable *plottable)
 {
   if (plottable && qobject_cast<QCPErrorBars*>(plottable))
   {
-    mDataPlottable = 0;
+    mDataPlottable = nullptr;
     qDebug() << Q_FUNC_INFO << "can't set another QCPErrorBars instance as data plottable";
     return;
   }
   if (plottable && !plottable->interface1D())
   {
-    mDataPlottable = 0;
+    mDataPlottable = nullptr;
     qDebug() << Q_FUNC_INFO << "passed plottable doesn't implement 1d interface, can't associate with QCPErrorBars";
     return;
   }
@@ -27351,13 +28191,13 @@ QCPRange QCPErrorBars::dataValueRange(int index) const
   {
     const double value = mDataPlottable->interface1D()->dataMainValue(index);
     if (index >= 0 && index < mDataContainer->size() && mErrorType == etValueError)
-      return QCPRange(value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus);
+      return {value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus};
     else
-      return QCPRange(value, value);
+      return {value, value};
   } else
   {
     qDebug() << Q_FUNC_INFO << "no data plottable set";
-    return QCPRange();
+    return {};
   }
 }
 
@@ -27368,7 +28208,7 @@ QPointF QCPErrorBars::dataPixelPosition(int index) const
     return mDataPlottable->interface1D()->dataPixelPosition(index);
   else
     qDebug() << Q_FUNC_INFO << "no data plottable set";
-  return QPointF();
+  return {};
 }
 
 /* inherits documentation from base class */
@@ -27406,11 +28246,11 @@ QCPDataSelection QCPErrorBars::selectTestRect(const QRectF &rect, bool onlySelec
     backbones.clear();
     whiskers.clear();
     getErrorBarLines(it, backbones, whiskers);
-    for (int i=0; i<backbones.size(); ++i)
+    foreach (const QLineF &backbone, backbones)
     {
-      if (rectIntersectsLine(rect, backbones.at(i)))
+      if (rectIntersectsLine(rect, backbone))
       {
-        result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
+        result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);
         break;
       }
     }
@@ -27451,7 +28291,14 @@ int QCPErrorBars::findEnd(double sortKey, bool expandedRange) const
   return 0;
 }
 
-/* inherits documentation from base class */
+/*!
+  Implements a selectTest specific to this plottable's point geometry.
+
+  If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data
+  point to \a pos.
+  
+  \seebaseclassmethod \ref QCPAbstractPlottable::selectTest
+*/
 double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
 {
   if (!mDataPlottable) return -1;
@@ -27461,13 +28308,13 @@ double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVarian
   if (!mKeyAxis || !mValueAxis)
     return -1;
   
-  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))
   {
     QCPErrorBarsDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
     double result = pointDistance(pos, closestDataPoint);
     if (details)
     {
-      int pointIndex = closestDataPoint-mDataContainer->constBegin();
+      int pointIndex = int(closestDataPoint-mDataContainer->constBegin());
       details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
     }
     return result;
@@ -27525,7 +28372,7 @@ void QCPErrorBars::draw(QCPPainter *painter)
     whiskers.clear();
     for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
     {
-      if (!checkPointVisibility || errorBarVisible(it-mDataContainer->constBegin()))
+      if (!checkPointVisibility || errorBarVisible(int(it-mDataContainer->constBegin())))
         getErrorBarLines(it, backbones, whiskers);
     }
     painter->drawLines(backbones);
@@ -27561,7 +28408,7 @@ QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomai
   if (!mDataPlottable)
   {
     foundRange = false;
-    return QCPRange();
+    return {};
   }
   
   QCPRange range;
@@ -27573,7 +28420,7 @@ QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomai
     if (mErrorType == etValueError)
     {
       // error bar doesn't extend in key dimension (except whisker but we ignore that here), so only use data point center
-      const double current = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
+      const double current = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin()));
       if (qIsNaN(current)) continue;
       if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
       {
@@ -27590,7 +28437,7 @@ QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomai
       }
     } else // mErrorType == etKeyError
     {
-      const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
+      const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin()));
       if (qIsNaN(dataKey)) continue;
       // plus error:
       double current = dataKey + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
@@ -27635,7 +28482,7 @@ QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDom
   if (!mDataPlottable)
   {
     foundRange = false;
-    return QCPRange();
+    return {};
   }
   
   QCPRange range;
@@ -27646,20 +28493,20 @@ QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDom
   QCPErrorBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd();
   if (mDataPlottable->interface1D()->sortKeyIsMainKey() && restrictKeyRange)
   {
-    itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower);
-    itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper);
+    itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower, false);
+    itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper, false);
   }
   for (QCPErrorBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
   {
     if (restrictKeyRange)
     {
-      const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
+      const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin()));
       if (dataKey < inKeyRange.lower || dataKey > inKeyRange.upper)
         continue;
     }
     if (mErrorType == etValueError)
     {
-      const double dataValue = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin());
+      const double dataValue = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin()));
       if (qIsNaN(dataValue)) continue;
       // plus error:
       double current = dataValue + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
@@ -27684,7 +28531,7 @@ QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDom
     } else // mErrorType == etKeyError
     {
       // error bar doesn't extend in value dimension (except whisker but we ignore that here), so only use data point center
-      const double current = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin());
+      const double current = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin()));
       if (qIsNaN(current)) continue;
       if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
       {
@@ -27731,7 +28578,7 @@ void QCPErrorBars::getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it
 {
   if (!mDataPlottable) return;
   
-  int index = it-mDataContainer->constBegin();
+  int index = int(it-mDataContainer->constBegin());
   QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);
   if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y()))
     return;
@@ -27870,14 +28717,14 @@ double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataCo
   getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount()));
   
   // calculate minimum distances to error backbones (whiskers are ignored for speed) and find closestData iterator:
-  double minDistSqr = std::numeric_limits<double>::max();
+  double minDistSqr = (std::numeric_limits<double>::max)();
   QVector<QLineF> backbones, whiskers;
   for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
   {
     getErrorBarLines(it, backbones, whiskers);
-    for (int i=0; i<backbones.size(); ++i)
+    foreach (const QLineF &backbone, backbones)
     {
-      const double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(backbones.at(i));
+      const double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(backbone);
       if (currentDistSqr < minDistSqr)
       {
         minDistSqr = currentDistSqr;
@@ -27920,8 +28767,8 @@ void QCPErrorBars::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<
   range.
 
   This method assumes for performance reasons without checking that the key axis, the value axis,
-  and the data plottable (\ref setDataPlottable) are not zero and that \a index is within valid
-  bounds of this \ref QCPErrorBars instance and the bounds of the data plottable.
+  and the data plottable (\ref setDataPlottable) are not \c nullptr and that \a index is within
+  valid bounds of this \ref QCPErrorBars instance and the bounds of the data plottable.
 */
 bool QCPErrorBars::errorBarVisible(int index) const
 {
@@ -27969,8 +28816,8 @@ bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &lin
 /* end of 'src/plottables/plottable-errorbar.cpp' */
 
 
-/* including file 'src/items/item-straightline.cpp', size 7592               */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-straightline.cpp' */
+/* modified 2021-03-29T02:30:44, size 7596          */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemStraightLine
@@ -28042,7 +28889,7 @@ void QCPItemStraightLine::draw(QCPPainter *painter)
   QCPVector2D start(point1->pixelPosition());
   QCPVector2D end(point2->pixelPosition());
   // get visible segment of straight line inside clipRect:
-  double clipPad = mainPen().widthF();
+  int clipPad = qCeil(mainPen().widthF());
   QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
   // paint visible segment, if existent:
   if (!line.isNull())
@@ -28150,8 +28997,8 @@ QPen QCPItemStraightLine::mainPen() const
 /* end of 'src/items/item-straightline.cpp' */
 
 
-/* including file 'src/items/item-line.cpp', size 8498                       */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-line.cpp' */
+/* modified 2021-03-29T02:30:44, size 8525  */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemLine
@@ -28253,8 +29100,8 @@ void QCPItemLine::draw(QCPPainter *painter)
   if (qFuzzyIsNull((startVec-endVec).lengthSquared()))
     return;
   // get visible segment of straight line inside clipRect:
-  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
-  clipPad = qMax(clipPad, (double)mainPen().widthF());
+  int clipPad = int(qMax(mHead.boundingDistance(), mTail.boundingDistance()));
+  clipPad = qMax(clipPad, qCeil(mainPen().widthF()));
   QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
   // paint visible segment, if existent:
   if (!line.isNull())
@@ -28278,10 +29125,10 @@ void QCPItemLine::draw(QCPPainter *painter)
 */
 QLineF QCPItemLine::getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const
 {
-  bool containsStart = rect.contains(start.x(), start.y());
-  bool containsEnd = rect.contains(end.x(), end.y());
+  bool containsStart = rect.contains(qRound(start.x()), qRound(start.y()));
+  bool containsEnd = rect.contains(qRound(end.x()), qRound(end.y()));
   if (containsStart && containsEnd)
-    return QLineF(start.toPointF(), end.toPointF());
+    return {start.toPointF(), end.toPointF()};
   
   QCPVector2D base = start;
   QCPVector2D vec = end-start;
@@ -28381,8 +29228,8 @@ QPen QCPItemLine::mainPen() const
 /* end of 'src/items/item-line.cpp' */
 
 
-/* including file 'src/items/item-curve.cpp', size 7159                      */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-curve.cpp' */
+/* modified 2021-03-29T02:30:44, size 7273   */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemCurve
@@ -28492,9 +29339,12 @@ double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVarian
   QPainterPath cubicPath(startVec);
   cubicPath.cubicTo(startDirVec, endDirVec, endVec);
   
-  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
+  QList<QPolygonF> polygons = cubicPath.toSubpathPolygons();
+  if (polygons.isEmpty())
+    return -1;
+  const QPolygonF polygon = polygons.first();
   QCPVector2D p(pos);
-  double minDistSqr = std::numeric_limits<double>::max();
+  double minDistSqr = (std::numeric_limits<double>::max)();
   for (int i=1; i<polygon.size(); ++i)
   {
     double distSqr = p.distanceSquaredToLine(polygon.at(i-1), polygon.at(i));
@@ -28518,7 +29368,8 @@ void QCPItemCurve::draw(QCPPainter *painter)
   cubicPath.cubicTo(startDirVec.toPointF(), endDirVec.toPointF(), endVec.toPointF());
 
   // paint visible segment, if existent:
-  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
+  const int clipEnlarge = qCeil(mainPen().widthF());
+  QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge);
   QRect cubicRect = cubicPath.controlPointRect().toRect();
   if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
     cubicRect.adjust(0, 0, 1, 1);
@@ -28546,8 +29397,8 @@ QPen QCPItemCurve::mainPen() const
 /* end of 'src/items/item-curve.cpp' */
 
 
-/* including file 'src/items/item-rect.cpp', size 6479                       */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-rect.cpp' */
+/* modified 2021-03-29T02:30:44, size 6472  */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemRect
@@ -28678,7 +29529,7 @@ QPointF QCPItemRect::anchorPixelPosition(int anchorId) const
   }
   
   qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
-  return QPointF();
+  return {};
 }
 
 /*! \internal
@@ -28703,8 +29554,8 @@ QBrush QCPItemRect::mainBrush() const
 /* end of 'src/items/item-rect.cpp' */
 
 
-/* including file 'src/items/item-text.cpp', size 13338                      */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-text.cpp' */
+/* modified 2021-03-29T02:30:44, size 13335 */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemText
@@ -28932,7 +29783,7 @@ void QCPItemText::draw(QCPPainter *painter)
   QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
   textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
   textBoxRect.moveTopLeft(textPos.toPoint());
-  double clipPad = mainPen().widthF();
+  int clipPad = qCeil(mainPen().widthF());
   QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
   if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
   {
@@ -28979,7 +29830,7 @@ QPointF QCPItemText::anchorPixelPosition(int anchorId) const
   }
   
   qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
-  return QPointF();
+  return {};
 }
 
 /*! \internal
@@ -29051,8 +29902,8 @@ QBrush QCPItemText::mainBrush() const
 /* end of 'src/items/item-text.cpp' */
 
 
-/* including file 'src/items/item-ellipse.cpp', size 7863                    */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-ellipse.cpp' */
+/* modified 2021-03-29T02:30:44, size 7881     */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemEllipse
@@ -29176,7 +30027,8 @@ void QCPItemEllipse::draw(QCPPainter *painter)
   if (p1.toPoint() == p2.toPoint())
     return;
   QRectF ellipseRect = QRectF(p1, p2).normalized();
-  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
+  const int clipEnlarge = qCeil(mainPen().widthF());
+  QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge);
   if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
   {
     painter->setPen(mainPen());
@@ -29214,7 +30066,7 @@ QPointF QCPItemEllipse::anchorPixelPosition(int anchorId) const
   }
   
   qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
-  return QPointF();
+  return {};
 }
 
 /*! \internal
@@ -29239,8 +30091,8 @@ QBrush QCPItemEllipse::mainBrush() const
 /* end of 'src/items/item-ellipse.cpp' */
 
 
-/* including file 'src/items/item-pixmap.cpp', size 10615                    */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-pixmap.cpp' */
+/* modified 2021-03-29T02:30:44, size 10622   */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemPixmap
@@ -29351,7 +30203,7 @@ void QCPItemPixmap::draw(QCPPainter *painter)
   bool flipHorz = false;
   bool flipVert = false;
   QRect rect = getFinalRect(&flipHorz, &flipVert);
-  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
+  int clipPad = mainPen().style() == Qt::NoPen ? 0 : qCeil(mainPen().widthF());
   QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
   if (boundingRect.intersects(clipRect()))
   {
@@ -29370,8 +30222,8 @@ void QCPItemPixmap::draw(QCPPainter *painter)
 /* inherits documentation from base class */
 QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const
 {
-  bool flipHorz;
-  bool flipVert;
+  bool flipHorz = false;
+  bool flipVert = false;
   QRect rect = getFinalRect(&flipHorz, &flipVert);
   // we actually want denormal rects (negative width/height) here, so restore
   // the flipped state:
@@ -29387,11 +30239,11 @@ QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const
     case aiRight:       return (rect.topRight()+rect.bottomRight())*0.5;
     case aiBottom:      return (rect.bottomLeft()+rect.bottomRight())*0.5;
     case aiBottomLeft:  return rect.bottomLeft();
-    case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;;
+    case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;
   }
   
   qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
-  return QPointF();
+  return {};
 }
 
 /*! \internal
@@ -29457,7 +30309,7 @@ QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
   QPoint p1 = topLeft->pixelPosition().toPoint();
   QPoint p2 = bottomRight->pixelPosition().toPoint();
   if (p1 == p2)
-    return QRect(p1, QSize(0, 0));
+    return {p1, QSize(0, 0)};
   if (mScaled)
   {
     QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
@@ -29509,8 +30361,8 @@ QPen QCPItemPixmap::mainPen() const
 /* end of 'src/items/item-pixmap.cpp' */
 
 
-/* including file 'src/items/item-tracer.cpp', size 14624                    */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-tracer.cpp' */
+/* modified 2021-03-29T02:30:44, size 14645   */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemTracer
@@ -29557,7 +30409,7 @@ QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :
   position(createPosition(QLatin1String("position"))),
   mSize(6),
   mStyle(tsCrosshair),
-  mGraph(0),
+  mGraph(nullptr),
   mGraphKey(0),
   mInterpolating(false)
 {
@@ -29637,9 +30489,9 @@ void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)
   Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type
   QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph.
   
-  To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed
-  freely like any other item position. This is the state the tracer will assume when its graph gets
-  deleted while still attached to it.
+  To free the tracer from any graph, set \a graph to \c nullptr. The tracer \a position can then be
+  placed freely like any other item position. This is the state the tracer will assume when its
+  graph gets deleted while still attached to it.
   
   \see setGraphKey
 */
@@ -29657,7 +30509,7 @@ void QCPItemTracer::setGraph(QCPGraph *graph)
       qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
   } else
   {
-    mGraph = 0;
+    mGraph = nullptr;
   }
 }
 
@@ -29832,7 +30684,7 @@ void QCPItemTracer::updatePosition()
             {
               // interpolate between iterators around mGraphKey:
               double slope = 0;
-              if (!qFuzzyCompare((double)it->key, (double)prevIt->key))
+              if (!qFuzzyCompare(double(it->key), double(prevIt->key)))
                 slope = (it->value-prevIt->value)/(it->key-prevIt->key);
               position->setCoords(mGraphKey, (mGraphKey-prevIt->key)*slope+prevIt->value);
             } else
@@ -29879,8 +30731,8 @@ QBrush QCPItemTracer::mainBrush() const
 /* end of 'src/items/item-tracer.cpp' */
 
 
-/* including file 'src/items/item-bracket.cpp', size 10687                   */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-bracket.cpp' */
+/* modified 2021-03-29T02:30:44, size 10705    */
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPItemBracket
@@ -29927,195 +30779,4718 @@ QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :
   setSelectedPen(QPen(Qt::blue, 2));
 }
 
-QCPItemBracket::~QCPItemBracket()
+QCPItemBracket::~QCPItemBracket()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the bracket.
+  
+  Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the
+  stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use
+  \ref setLength, which has a similar effect.
+  
+  \see setSelectedPen
+*/
+void QCPItemBracket::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the bracket when selected
+  
+  \see setPen, setSelected
+*/
+void QCPItemBracket::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the \a length in pixels how far the bracket extends in the direction towards the embraced
+  span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)
+  
+  \image html QCPItemBracket-length.png
+  <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
+  bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
+*/
+void QCPItemBracket::setLength(double length)
+{
+  mLength = length;
+}
+
+/*!
+  Sets the style of the bracket, i.e. the shape/visual appearance.
+  
+  \see setPen
+*/
+void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
+{
+  mStyle = style;
+}
+
+/* inherits documentation from base class */
+double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+  
+  QCPVector2D p(pos);
+  QCPVector2D leftVec(left->pixelPosition());
+  QCPVector2D rightVec(right->pixelPosition());
+  if (leftVec.toPoint() == rightVec.toPoint())
+    return -1;
+  
+  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
+  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
+  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
+  
+  switch (mStyle)
+  {
+    case QCPItemBracket::bsSquare:
+    case QCPItemBracket::bsRound:
+    {
+      double a = p.distanceSquaredToLine(centerVec-widthVec, centerVec+widthVec);
+      double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec, centerVec-widthVec);
+      double c = p.distanceSquaredToLine(centerVec+widthVec+lengthVec, centerVec+widthVec);
+      return qSqrt(qMin(qMin(a, b), c));
+    }
+    case QCPItemBracket::bsCurly:
+    case QCPItemBracket::bsCalligraphic:
+    {
+      double a = p.distanceSquaredToLine(centerVec-widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
+      double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec*0.7, centerVec-widthVec*0.75+lengthVec*0.15);
+      double c = p.distanceSquaredToLine(centerVec+widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
+      double d = p.distanceSquaredToLine(centerVec+widthVec+lengthVec*0.7, centerVec+widthVec*0.75+lengthVec*0.15);
+      return qSqrt(qMin(qMin(a, b), qMin(c, d)));
+    }
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+void QCPItemBracket::draw(QCPPainter *painter)
+{
+  QCPVector2D leftVec(left->pixelPosition());
+  QCPVector2D rightVec(right->pixelPosition());
+  if (leftVec.toPoint() == rightVec.toPoint())
+    return;
+  
+  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
+  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
+  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
+
+  QPolygon boundingPoly;
+  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
+               << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
+  const int clipEnlarge = qCeil(mainPen().widthF());
+  QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge);
+  if (clip.intersects(boundingPoly.boundingRect()))
+  {
+    painter->setPen(mainPen());
+    switch (mStyle)
+    {
+      case bsSquare:
+      {
+        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
+        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
+        painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        break;
+      }
+      case bsRound:
+      {
+        painter->setBrush(Qt::NoBrush);
+        QPainterPath path;
+        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
+        path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
+        path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        painter->drawPath(path);
+        break;
+      }
+      case bsCurly:
+      {
+        painter->setBrush(Qt::NoBrush);
+        QPainterPath path;
+        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
+        path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF());
+        path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        painter->drawPath(path);
+        break;
+      }
+      case bsCalligraphic:
+      {
+        painter->setPen(Qt::NoPen);
+        painter->setBrush(QBrush(mainPen().color()));
+        QPainterPath path;
+        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
+        
+        path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
+        path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        
+        path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
+        path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
+        
+        painter->drawPath(path);
+        break;
+      }
+    }
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const
+{
+  QCPVector2D leftVec(left->pixelPosition());
+  QCPVector2D rightVec(right->pixelPosition());
+  if (leftVec.toPoint() == rightVec.toPoint())
+    return leftVec.toPointF();
+  
+  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
+  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
+  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
+  
+  switch (anchorId)
+  {
+    case aiCenter:
+      return centerVec.toPointF();
+  }
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return {};
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the
+  item is not selected and mSelectedPen when it is.
+*/
+QPen QCPItemBracket::mainPen() const
+{
+    return mSelected ? mSelectedPen : mPen;
+}
+/* end of 'src/items/item-bracket.cpp' */
+
+
+/* including file 'src/polar/radialaxis.cpp' */
+/* modified 2021-03-29T02:30:44, size 49415  */
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPPolarAxisRadial
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPPolarAxisRadial
+  \brief The radial axis inside a radial plot
+  
+  \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and
+  functionality to be incomplete, as well as changing public interfaces in the future.
+  
+  Each axis holds an instance of QCPAxisTicker which is used to generate the tick coordinates and
+  tick labels. You can access the currently installed \ref ticker or set a new one (possibly one of
+  the specialized subclasses, or your own subclass) via \ref setTicker. For details, see the
+  documentation of QCPAxisTicker.
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn QSharedPointer<QCPAxisTicker> QCPPolarAxisRadial::ticker() const
+
+  Returns a modifiable shared pointer to the currently installed axis ticker. The axis ticker is
+  responsible for generating the tick positions and tick labels of this axis. You can access the
+  \ref QCPAxisTicker with this method and modify basic properties such as the approximate tick count
+  (\ref QCPAxisTicker::setTickCount).
+
+  You can gain more control over the axis ticks by setting a different \ref QCPAxisTicker subclass, see
+  the documentation there. A new axis ticker can be set with \ref setTicker.
+
+  Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis
+  ticker simply by passing the same shared pointer to multiple axes.
+
+  \see setTicker
+*/
+
+/* end of documentation of inline functions */
+/* start of documentation of signals */
+
+/*! \fn void QCPPolarAxisRadial::rangeChanged(const QCPRange &newRange)
+
+  This signal is emitted when the range of this axis has changed. You can connect it to the \ref
+  setRange slot of another axis to communicate the new range to the other axis, in order for it to
+  be synchronized.
+  
+  You may also manipulate/correct the range with \ref setRange in a slot connected to this signal.
+  This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper
+  range shouldn't go beyond certain values (see \ref QCPRange::bounded). For example, the following
+  slot would limit the x axis to ranges between 0 and 10:
+  \code
+  customPlot->xAxis->setRange(newRange.bounded(0, 10))
+  \endcode
+*/
+
+/*! \fn void QCPPolarAxisRadial::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange)
+  \overload
+  
+  Additionally to the new range, this signal also provides the previous range held by the axis as
+  \a oldRange.
+*/
+
+/*! \fn void QCPPolarAxisRadial::scaleTypeChanged(QCPPolarAxisRadial::ScaleType scaleType);
+  
+  This signal is emitted when the scale type changes, by calls to \ref setScaleType
+*/
+
+/*! \fn void QCPPolarAxisRadial::selectionChanged(QCPPolarAxisRadial::SelectableParts selection)
+  
+  This signal is emitted when the selection state of this axis has changed, either by user interaction
+  or by a direct call to \ref setSelectedParts.
+*/
+
+/*! \fn void QCPPolarAxisRadial::selectableChanged(const QCPPolarAxisRadial::SelectableParts &parts);
+  
+  This signal is emitted when the selectability changes, by calls to \ref setSelectableParts
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs an Axis instance of Type \a type for the axis rect \a parent.
+  
+  Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create
+  them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however,
+  create them manually and then inject them also via \ref QCPAxisRect::addAxis.
+*/
+QCPPolarAxisRadial::QCPPolarAxisRadial(QCPPolarAxisAngular *parent) :
+  QCPLayerable(parent->parentPlot(), QString(), parent),
+  mRangeDrag(true),
+  mRangeZoom(true),
+  mRangeZoomFactor(0.85),
+  // axis base:
+  mAngularAxis(parent),
+  mAngle(45),
+  mAngleReference(arAngularAxis),
+  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
+  mSelectedParts(spNone),
+  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  mSelectedBasePen(QPen(Qt::blue, 2)),
+  // axis label:
+  mLabelPadding(0),
+  mLabel(),
+  mLabelFont(mParentPlot->font()),
+  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
+  mLabelColor(Qt::black),
+  mSelectedLabelColor(Qt::blue),
+  // tick labels:
+  // mTickLabelPadding(0), in label painter
+  mTickLabels(true),
+  // mTickLabelRotation(0), in label painter
+  mTickLabelFont(mParentPlot->font()),
+  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
+  mTickLabelColor(Qt::black),
+  mSelectedTickLabelColor(Qt::blue),
+  mNumberPrecision(6),
+  mNumberFormatChar('g'),
+  mNumberBeautifulPowers(true),
+  mNumberMultiplyCross(false),
+  // ticks and subticks:
+  mTicks(true),
+  mSubTicks(true),
+  mTickLengthIn(5),
+  mTickLengthOut(0),
+  mSubTickLengthIn(2),
+  mSubTickLengthOut(0),
+  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  mSelectedTickPen(QPen(Qt::blue, 2)),
+  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  mSelectedSubTickPen(QPen(Qt::blue, 2)),
+  // scale and range:
+  mRange(0, 5),
+  mRangeReversed(false),
+  mScaleType(stLinear),
+  // internal members:
+  mRadius(1), // non-zero initial value, will be overwritten in ::update() according to inner rect
+  mTicker(new QCPAxisTicker),
+  mLabelPainter(mParentPlot)
+{
+  setParent(parent);
+  setAntialiased(true);
+  
+  setTickLabelPadding(5);
+  setTickLabelRotation(0);
+  setTickLabelMode(lmUpright);
+  mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artTangent);
+  mLabelPainter.setAbbreviateDecimalPowers(false);
+}
+
+QCPPolarAxisRadial::~QCPPolarAxisRadial()
+{
+}
+
+QCPPolarAxisRadial::LabelMode QCPPolarAxisRadial::tickLabelMode() const
+{
+  switch (mLabelPainter.anchorMode())
+  {
+    case QCPLabelPainterPrivate::amSkewedUpright: return lmUpright;
+    case QCPLabelPainterPrivate::amSkewedRotated: return lmRotated;
+    default: qDebug() << Q_FUNC_INFO << "invalid mode for polar axis"; break;
+  }
+  return lmUpright;
+}
+
+/* No documentation as it is a property getter */
+QString QCPPolarAxisRadial::numberFormat() const
+{
+  QString result;
+  result.append(mNumberFormatChar);
+  if (mNumberBeautifulPowers)
+  {
+    result.append(QLatin1Char('b'));
+    if (mNumberMultiplyCross)
+      result.append(QLatin1Char('c'));
+  }
+  return result;
+}
+
+/* No documentation as it is a property getter */
+int QCPPolarAxisRadial::tickLengthIn() const
+{
+  return mTickLengthIn;
+}
+
+/* No documentation as it is a property getter */
+int QCPPolarAxisRadial::tickLengthOut() const
+{
+  return mTickLengthOut;
+}
+
+/* No documentation as it is a property getter */
+int QCPPolarAxisRadial::subTickLengthIn() const
+{
+  return mSubTickLengthIn;
+}
+
+/* No documentation as it is a property getter */
+int QCPPolarAxisRadial::subTickLengthOut() const
+{
+  return mSubTickLengthOut;
+}
+
+/* No documentation as it is a property getter */
+int QCPPolarAxisRadial::labelPadding() const
+{
+  return mLabelPadding;
+}
+
+void QCPPolarAxisRadial::setRangeDrag(bool enabled)
+{
+  mRangeDrag = enabled;
+}
+
+void QCPPolarAxisRadial::setRangeZoom(bool enabled)
+{
+  mRangeZoom = enabled;
+}
+
+void QCPPolarAxisRadial::setRangeZoomFactor(double factor)
+{
+  mRangeZoomFactor = factor;
+}
+
+/*!
+  Sets whether the axis uses a linear scale or a logarithmic scale.
+  
+  Note that this method controls the coordinate transformation. For logarithmic scales, you will
+  likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting
+  the axis ticker to an instance of \ref QCPAxisTickerLog :
+  
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-creation
+  
+  See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick
+  creation.
+  
+  \ref setNumberPrecision
+*/
+void QCPPolarAxisRadial::setScaleType(QCPPolarAxisRadial::ScaleType type)
+{
+  if (mScaleType != type)
+  {
+    mScaleType = type;
+    if (mScaleType == stLogarithmic)
+      setRange(mRange.sanitizedForLogScale());
+    //mCachedMarginValid = false;
+    emit scaleTypeChanged(mScaleType);
+  }
+}
+
+/*!
+  Sets the range of the axis.
+  
+  This slot may be connected with the \ref rangeChanged signal of another axis so this axis
+  is always synchronized with the other axis range, when it changes.
+  
+  To invert the direction of an axis, use \ref setRangeReversed.
+*/
+void QCPPolarAxisRadial::setRange(const QCPRange &range)
+{
+  if (range.lower == mRange.lower && range.upper == mRange.upper)
+    return;
+  
+  if (!QCPRange::validRange(range)) return;
+  QCPRange oldRange = mRange;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = range.sanitizedForLogScale();
+  } else
+  {
+    mRange = range.sanitizedForLinScale();
+  }
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains iSelectAxes.)
+  
+  However, even when \a selectable is set to a value not allowing the selection of a specific part,
+  it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
+  directly.
+  
+  \see SelectablePart, setSelectedParts
+*/
+void QCPPolarAxisRadial::setSelectableParts(const SelectableParts &selectable)
+{
+  if (mSelectableParts != selectable)
+  {
+    mSelectableParts = selectable;
+    emit selectableChanged(mSelectableParts);
+  }
+}
+
+/*!
+  Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part
+  is selected, it uses a different pen/font.
+  
+  The entire selection mechanism for axes is handled automatically when \ref
+  QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you
+  wish to change the selection state manually.
+  
+  This function can change the selection state of a part, independent of the \ref setSelectableParts setting.
+  
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+  
+  \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,
+  setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
+*/
+void QCPPolarAxisRadial::setSelectedParts(const SelectableParts &selected)
+{
+  if (mSelectedParts != selected)
+  {
+    mSelectedParts = selected;
+    emit selectionChanged(mSelectedParts);
+  }
+}
+
+/*!
+  \overload
+  
+  Sets the lower and upper bound of the axis range.
+  
+  To invert the direction of an axis, use \ref setRangeReversed.
+  
+  There is also a slot to set a range, see \ref setRange(const QCPRange &range).
+*/
+void QCPPolarAxisRadial::setRange(double lower, double upper)
+{
+  if (lower == mRange.lower && upper == mRange.upper)
+    return;
+  
+  if (!QCPRange::validRange(lower, upper)) return;
+  QCPRange oldRange = mRange;
+  mRange.lower = lower;
+  mRange.upper = upper;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = mRange.sanitizedForLogScale();
+  } else
+  {
+    mRange = mRange.sanitizedForLinScale();
+  }
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  \overload
+  
+  Sets the range of the axis.
+  
+  The \a position coordinate indicates together with the \a alignment parameter, where the new
+  range will be positioned. \a size defines the size of the new axis range. \a alignment may be
+  Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,
+  or center of the range to be aligned with \a position. Any other values of \a alignment will
+  default to Qt::AlignCenter.
+*/
+void QCPPolarAxisRadial::setRange(double position, double size, Qt::AlignmentFlag alignment)
+{
+  if (alignment == Qt::AlignLeft)
+    setRange(position, position+size);
+  else if (alignment == Qt::AlignRight)
+    setRange(position-size, position);
+  else // alignment == Qt::AlignCenter
+    setRange(position-size/2.0, position+size/2.0);
+}
+
+/*!
+  Sets the lower bound of the axis range. The upper bound is not changed.
+  \see setRange
+*/
+void QCPPolarAxisRadial::setRangeLower(double lower)
+{
+  if (mRange.lower == lower)
+    return;
+  
+  QCPRange oldRange = mRange;
+  mRange.lower = lower;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = mRange.sanitizedForLogScale();
+  } else
+  {
+    mRange = mRange.sanitizedForLinScale();
+  }
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Sets the upper bound of the axis range. The lower bound is not changed.
+  \see setRange
+*/
+void QCPPolarAxisRadial::setRangeUpper(double upper)
+{
+  if (mRange.upper == upper)
+    return;
+  
+  QCPRange oldRange = mRange;
+  mRange.upper = upper;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = mRange.sanitizedForLogScale();
+  } else
+  {
+    mRange = mRange.sanitizedForLinScale();
+  }
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal
+  axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the
+  direction of increasing values is inverted.
+
+  Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part
+  of the \ref setRange interface will still reference the mathematically smaller number than the \a
+  upper part.
+*/
+void QCPPolarAxisRadial::setRangeReversed(bool reversed)
+{
+  mRangeReversed = reversed;
+}
+
+void QCPPolarAxisRadial::setAngle(double degrees)
+{
+  mAngle = degrees;
+}
+
+void QCPPolarAxisRadial::setAngleReference(AngleReference reference)
+{
+  mAngleReference = reference;
+}
+
+/*!
+  The axis ticker is responsible for generating the tick positions and tick labels. See the
+  documentation of QCPAxisTicker for details on how to work with axis tickers.
+  
+  You can change the tick positioning/labeling behaviour of this axis by setting a different
+  QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis
+  ticker, access it via \ref ticker.
+  
+  Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis
+  ticker simply by passing the same shared pointer to multiple axes.
+  
+  \see ticker
+*/
+void QCPPolarAxisRadial::setTicker(QSharedPointer<QCPAxisTicker> ticker)
+{
+  if (ticker)
+    mTicker = ticker;
+  else
+    qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker";
+  // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector
+}
+
+/*!
+  Sets whether tick marks are displayed.
+
+  Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve
+  that, see \ref setTickLabels.
+  
+  \see setSubTicks
+*/
+void QCPPolarAxisRadial::setTicks(bool show)
+{
+  if (mTicks != show)
+  {
+    mTicks = show;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.
+*/
+void QCPPolarAxisRadial::setTickLabels(bool show)
+{
+  if (mTickLabels != show)
+  {
+    mTickLabels = show;
+    //mCachedMarginValid = false;
+    if (!mTickLabels)
+      mTickVectorLabels.clear();
+  }
+}
+
+/*!
+  Sets the distance between the axis base line (including any outward ticks) and the tick labels.
+  \see setLabelPadding, setPadding
+*/
+void QCPPolarAxisRadial::setTickLabelPadding(int padding)
+{
+  mLabelPainter.setPadding(padding);
+}
+
+/*!
+  Sets the font of the tick labels.
+  
+  \see setTickLabels, setTickLabelColor
+*/
+void QCPPolarAxisRadial::setTickLabelFont(const QFont &font)
+{
+  if (font != mTickLabelFont)
+  {
+    mTickLabelFont = font;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the color of the tick labels.
+  
+  \see setTickLabels, setTickLabelFont
+*/
+void QCPPolarAxisRadial::setTickLabelColor(const QColor &color)
+{
+  mTickLabelColor = color;
+}
+
+/*!
+  Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else,
+  the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values
+  from -90 to 90 degrees.
+  
+  If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For
+  other angles, the label is drawn with an offset such that it seems to point toward or away from
+  the tick mark.
+*/
+void QCPPolarAxisRadial::setTickLabelRotation(double degrees)
+{
+  mLabelPainter.setRotation(degrees);
+}
+
+void QCPPolarAxisRadial::setTickLabelMode(LabelMode mode)
+{
+  switch (mode)
+  {
+    case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break;
+    case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break;
+  }
+}
+
+/*!
+  Sets the number format for the numbers in tick labels. This \a formatCode is an extended version
+  of the format code used e.g. by QString::number() and QLocale::toString(). For reference about
+  that, see the "Argument Formats" section in the detailed description of the QString class.
+  
+  \a formatCode is a string of one, two or three characters. The first character is identical to
+  the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed
+  format, 'g'/'G' scientific or fixed, whichever is shorter.
+  
+  The second and third characters are optional and specific to QCustomPlot:\n
+  If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.
+  "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for
+  "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
+  [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot.
+  If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can
+  be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the
+  cross and 183 (0xB7) for the dot.
+  
+  Examples for \a formatCode:
+  \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large,
+  normal scientific format is used
+  \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with
+  beautifully typeset decimal powers and a dot as multiplication sign
+  \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as
+  multiplication sign
+  \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal
+  powers. Format code will be reduced to 'f'.
+  \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format
+  code will not be changed.
+*/
+void QCPPolarAxisRadial::setNumberFormat(const QString &formatCode)
+{
+  if (formatCode.isEmpty())
+  {
+    qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
+    return;
+  }
+  //mCachedMarginValid = false;
+  
+  // interpret first char as number format char:
+  QString allowedFormatChars(QLatin1String("eEfgG"));
+  if (allowedFormatChars.contains(formatCode.at(0)))
+  {
+    mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
+    return;
+  }
+  
+  if (formatCode.length() < 2)
+  {
+    mNumberBeautifulPowers = false;
+    mNumberMultiplyCross = false;
+  } else
+  {
+    // interpret second char as indicator for beautiful decimal powers:
+    if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
+      mNumberBeautifulPowers = true;
+    else
+      qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
+    
+    if (formatCode.length() < 3)
+    {
+      mNumberMultiplyCross = false;
+    } else
+    {
+      // interpret third char as indicator for dot or cross multiplication symbol:
+      if (formatCode.at(2) == QLatin1Char('c'))
+        mNumberMultiplyCross = true;
+      else if (formatCode.at(2) == QLatin1Char('d'))
+        mNumberMultiplyCross = false;
+      else
+        qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
+    }
+  }
+  mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers);
+  mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot);
+}
+
+/*!
+  Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)
+  for details. The effect of precisions are most notably for number Formats starting with 'e', see
+  \ref setNumberFormat
+*/
+void QCPPolarAxisRadial::setNumberPrecision(int precision)
+{
+  if (mNumberPrecision != precision)
+  {
+    mNumberPrecision = precision;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the
+  plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
+  zero, the tick labels and axis label will increase their distance to the axis accordingly, so
+  they won't collide with the ticks.
+  
+  \see setSubTickLength, setTickLengthIn, setTickLengthOut
+*/
+void QCPPolarAxisRadial::setTickLength(int inside, int outside)
+{
+  setTickLengthIn(inside);
+  setTickLengthOut(outside);
+}
+
+/*!
+  Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
+  inside the plot.
+  
+  \see setTickLengthOut, setTickLength, setSubTickLength
+*/
+void QCPPolarAxisRadial::setTickLengthIn(int inside)
+{
+  if (mTickLengthIn != inside)
+  {
+    mTickLengthIn = inside;
+  }
+}
+
+/*!
+  Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach
+  outside the plot. If \a outside is greater than zero, the tick labels and axis label will
+  increase their distance to the axis accordingly, so they won't collide with the ticks.
+  
+  \see setTickLengthIn, setTickLength, setSubTickLength
+*/
+void QCPPolarAxisRadial::setTickLengthOut(int outside)
+{
+  if (mTickLengthOut != outside)
+  {
+    mTickLengthOut = outside;
+    //mCachedMarginValid = false; // only outside tick length can change margin
+  }
+}
+
+/*!
+  Sets whether sub tick marks are displayed.
+  
+  Sub ticks are only potentially visible if (major) ticks are also visible (see \ref setTicks)
+  
+  \see setTicks
+*/
+void QCPPolarAxisRadial::setSubTicks(bool show)
+{
+  if (mSubTicks != show)
+  {
+    mSubTicks = show;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside
+  the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
+  than zero, the tick labels and axis label will increase their distance to the axis accordingly,
+  so they won't collide with the ticks.
+  
+  \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
+*/
+void QCPPolarAxisRadial::setSubTickLength(int inside, int outside)
+{
+  setSubTickLengthIn(inside);
+  setSubTickLengthOut(outside);
+}
+
+/*!
+  Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
+  the plot.
+  
+  \see setSubTickLengthOut, setSubTickLength, setTickLength
+*/
+void QCPPolarAxisRadial::setSubTickLengthIn(int inside)
+{
+  if (mSubTickLengthIn != inside)
+  {
+    mSubTickLengthIn = inside;
+  }
+}
+
+/*!
+  Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach
+  outside the plot. If \a outside is greater than zero, the tick labels will increase their
+  distance to the axis accordingly, so they won't collide with the ticks.
+  
+  \see setSubTickLengthIn, setSubTickLength, setTickLength
+*/
+void QCPPolarAxisRadial::setSubTickLengthOut(int outside)
+{
+  if (mSubTickLengthOut != outside)
+  {
+    mSubTickLengthOut = outside;
+    //mCachedMarginValid = false; // only outside tick length can change margin
+  }
+}
+
+/*!
+  Sets the pen, the axis base line is drawn with.
+  
+  \see setTickPen, setSubTickPen
+*/
+void QCPPolarAxisRadial::setBasePen(const QPen &pen)
+{
+  mBasePen = pen;
+}
+
+/*!
+  Sets the pen, tick marks will be drawn with.
+  
+  \see setTickLength, setBasePen
+*/
+void QCPPolarAxisRadial::setTickPen(const QPen &pen)
+{
+  mTickPen = pen;
+}
+
+/*!
+  Sets the pen, subtick marks will be drawn with.
+  
+  \see setSubTickCount, setSubTickLength, setBasePen
+*/
+void QCPPolarAxisRadial::setSubTickPen(const QPen &pen)
+{
+  mSubTickPen = pen;
+}
+
+/*!
+  Sets the font of the axis label.
+  
+  \see setLabelColor
+*/
+void QCPPolarAxisRadial::setLabelFont(const QFont &font)
+{
+  if (mLabelFont != font)
+  {
+    mLabelFont = font;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the color of the axis label.
+  
+  \see setLabelFont
+*/
+void QCPPolarAxisRadial::setLabelColor(const QColor &color)
+{
+  mLabelColor = color;
+}
+
+/*!
+  Sets the text of the axis label that will be shown below/above or next to the axis, depending on
+  its orientation. To disable axis labels, pass an empty string as \a str.
+*/
+void QCPPolarAxisRadial::setLabel(const QString &str)
+{
+  if (mLabel != str)
+  {
+    mLabel = str;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the distance between the tick labels and the axis label.
+  
+  \see setTickLabelPadding, setPadding
+*/
+void QCPPolarAxisRadial::setLabelPadding(int padding)
+{
+  if (mLabelPadding != padding)
+  {
+    mLabelPadding = padding;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the font that is used for tick labels when they are selected.
+  
+  \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisRadial::setSelectedTickLabelFont(const QFont &font)
+{
+  if (font != mSelectedTickLabelFont)
+  {
+    mSelectedTickLabelFont = font;
+    // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
+  }
+}
+
+/*!
+  Sets the font that is used for the axis label when it is selected.
+  
+  \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisRadial::setSelectedLabelFont(const QFont &font)
+{
+  mSelectedLabelFont = font;
+  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
+}
+
+/*!
+  Sets the color that is used for tick labels when they are selected.
+  
+  \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisRadial::setSelectedTickLabelColor(const QColor &color)
+{
+  if (color != mSelectedTickLabelColor)
+  {
+    mSelectedTickLabelColor = color;
+  }
+}
+
+/*!
+  Sets the color that is used for the axis label when it is selected.
+  
+  \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisRadial::setSelectedLabelColor(const QColor &color)
+{
+  mSelectedLabelColor = color;
+}
+
+/*!
+  Sets the pen that is used to draw the axis base line when selected.
+  
+  \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisRadial::setSelectedBasePen(const QPen &pen)
+{
+  mSelectedBasePen = pen;
+}
+
+/*!
+  Sets the pen that is used to draw the (major) ticks when selected.
+  
+  \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisRadial::setSelectedTickPen(const QPen &pen)
+{
+  mSelectedTickPen = pen;
+}
+
+/*!
+  Sets the pen that is used to draw the subticks when selected.
+  
+  \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisRadial::setSelectedSubTickPen(const QPen &pen)
+{
+  mSelectedSubTickPen = pen;
+}
+
+/*!
+  If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper
+  bounds of the range. The range is simply moved by \a diff.
+  
+  If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This
+  corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff).
+*/
+void QCPPolarAxisRadial::moveRange(double diff)
+{
+  QCPRange oldRange = mRange;
+  if (mScaleType == stLinear)
+  {
+    mRange.lower += diff;
+    mRange.upper += diff;
+  } else // mScaleType == stLogarithmic
+  {
+    mRange.lower *= diff;
+    mRange.upper *= diff;
+  }
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Scales the range of this axis by \a factor around the center of the current axis range. For
+  example, if \a factor is 2.0, then the axis range will double its size, and the point at the axis
+  range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around
+  the center will have moved symmetrically closer).
+
+  If you wish to scale around a different coordinate than the current axis range center, use the
+  overload \ref scaleRange(double factor, double center).
+*/
+void QCPPolarAxisRadial::scaleRange(double factor)
+{
+  scaleRange(factor, range().center());
+}
+
+/*! \overload
+
+  Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a
+  factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at
+  coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates
+  around 1.0 will have moved symmetrically closer to 1.0).
+
+  \see scaleRange(double factor)
+*/
+void QCPPolarAxisRadial::scaleRange(double factor, double center)
+{
+  QCPRange oldRange = mRange;
+  if (mScaleType == stLinear)
+  {
+    QCPRange newRange;
+    newRange.lower = (mRange.lower-center)*factor + center;
+    newRange.upper = (mRange.upper-center)*factor + center;
+    if (QCPRange::validRange(newRange))
+      mRange = newRange.sanitizedForLinScale();
+  } else // mScaleType == stLogarithmic
+  {
+    if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
+    {
+      QCPRange newRange;
+      newRange.lower = qPow(mRange.lower/center, factor)*center;
+      newRange.upper = qPow(mRange.upper/center, factor)*center;
+      if (QCPRange::validRange(newRange))
+        mRange = newRange.sanitizedForLogScale();
+    } else
+      qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
+  }
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Changes the axis range such that all plottables associated with this axis are fully visible in
+  that dimension.
+  
+  \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
+*/
+void QCPPolarAxisRadial::rescale(bool onlyVisiblePlottables)
+{
+  Q_UNUSED(onlyVisiblePlottables)
+  /* TODO
+  QList<QCPAbstractPlottable*> p = plottables();
+  QCPRange newRange;
+  bool haveRange = false;
+  for (int i=0; i<p.size(); ++i)
+  {
+    if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
+      continue;
+    QCPRange plottableRange;
+    bool currentFoundRange;
+    QCP::SignDomain signDomain = QCP::sdBoth;
+    if (mScaleType == stLogarithmic)
+      signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);
+    if (p.at(i)->keyAxis() == this)
+      plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
+    else
+      plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
+    if (currentFoundRange)
+    {
+      if (!haveRange)
+        newRange = plottableRange;
+      else
+        newRange.expand(plottableRange);
+      haveRange = true;
+    }
+  }
+  if (haveRange)
+  {
+    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
+    {
+      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
+      if (mScaleType == stLinear)
+      {
+        newRange.lower = center-mRange.size()/2.0;
+        newRange.upper = center+mRange.size()/2.0;
+      } else // mScaleType == stLogarithmic
+      {
+        newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
+        newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
+      }
+    }
+    setRange(newRange);
+  }
+  */
+}
+
+/*!
+  Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.
+*/
+void QCPPolarAxisRadial::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const
+{
+  QCPVector2D posVector(pixelPos-mCenter);
+  radiusCoord = radiusToCoord(posVector.length());
+  angleCoord = mAngularAxis->angleRadToCoord(posVector.angle());
+}
+
+/*!
+  Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.
+*/
+QPointF QCPPolarAxisRadial::coordToPixel(double angleCoord, double radiusCoord) const
+{
+  const double radiusPixel = coordToRadius(radiusCoord);
+  const double angleRad = mAngularAxis->coordToAngleRad(angleCoord);
+  return QPointF(mCenter.x()+qCos(angleRad)*radiusPixel, mCenter.y()+qSin(angleRad)*radiusPixel);
+}
+
+double QCPPolarAxisRadial::coordToRadius(double coord) const
+{
+  if (mScaleType == stLinear)
+  {
+    if (!mRangeReversed)
+      return (coord-mRange.lower)/mRange.size()*mRadius;
+    else
+      return (mRange.upper-coord)/mRange.size()*mRadius;
+  } else // mScaleType == stLogarithmic
+  {
+    if (coord >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just return outside visible range
+      return !mRangeReversed ? mRadius+200 : mRadius-200;
+    else if (coord <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just return outside visible range
+      return !mRangeReversed ? mRadius-200 :mRadius+200;
+    else
+    {
+      if (!mRangeReversed)
+        return qLn(coord/mRange.lower)/qLn(mRange.upper/mRange.lower)*mRadius;
+      else
+        return qLn(mRange.upper/coord)/qLn(mRange.upper/mRange.lower)*mRadius;
+    }
+  }
+}
+
+double QCPPolarAxisRadial::radiusToCoord(double radius) const
+{
+  if (mScaleType == stLinear)
+  {
+    if (!mRangeReversed)
+      return (radius)/mRadius*mRange.size()+mRange.lower;
+    else
+      return -(radius)/mRadius*mRange.size()+mRange.upper;
+  } else // mScaleType == stLogarithmic
+  {
+    if (!mRangeReversed)
+      return qPow(mRange.upper/mRange.lower, (radius)/mRadius)*mRange.lower;
+    else
+      return qPow(mRange.upper/mRange.lower, (-radius)/mRadius)*mRange.upper;
+  }
+}
+
+
+/*!
+  Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function
+  is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this
+  function does not change the current selection state of the axis.
+  
+  If the axis is not visible (\ref setVisible), this function always returns \ref spNone.
+  
+  \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions
+*/
+QCPPolarAxisRadial::SelectablePart QCPPolarAxisRadial::getPartAt(const QPointF &pos) const
+{
+  Q_UNUSED(pos) // TODO remove later
+  if (!mVisible)
+    return spNone;
+  
+  /*
+    TODO:
+  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
+    return spAxis;
+  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
+    return spTickLabels;
+  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
+    return spAxisLabel;
+  else */
+    return spNone;
+}
+
+/* inherits documentation from base class */
+double QCPPolarAxisRadial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  if (!mParentPlot) return -1;
+  SelectablePart part = getPartAt(pos);
+  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
+    return -1;
+  
+  if (details)
+    details->setValue(part);
+  return mParentPlot->selectionTolerance()*0.99;
+}
+
+/* inherits documentation from base class */
+void QCPPolarAxisRadial::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
+{
+  Q_UNUSED(event)
+  SelectablePart part = details.value<SelectablePart>();
+  if (mSelectableParts.testFlag(part))
+  {
+    SelectableParts selBefore = mSelectedParts;
+    setSelectedParts(additive ? mSelectedParts^part : part);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelectedParts != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPPolarAxisRadial::deselectEvent(bool *selectionStateChanged)
+{
+  SelectableParts selBefore = mSelectedParts;
+  setSelectedParts(mSelectedParts & ~mSelectableParts);
+  if (selectionStateChanged)
+    *selectionStateChanged = mSelectedParts != selBefore;
+}
+
+/*! \internal
+  
+  This mouse event reimplementation provides the functionality to let the user drag individual axes
+  exclusively, by startig the drag on top of the axis.
+
+  For the axis to accept this event and perform the single axis drag, the parent \ref QCPAxisRect
+  must be configured accordingly, i.e. it must allow range dragging in the orientation of this axis
+  (\ref QCPAxisRect::setRangeDrag) and this axis must be a draggable axis (\ref
+  QCPAxisRect::setRangeDragAxes)
+  
+  \seebaseclassmethod
+  
+  \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis
+  rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent.
+*/
+void QCPPolarAxisRadial::mousePressEvent(QMouseEvent *event, const QVariant &details)
+{
+  Q_UNUSED(details)
+  if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag))
+  {
+    event->ignore();
+    return;
+  }
+  
+  if (event->buttons() & Qt::LeftButton)
+  {
+    mDragging = true;
+    // initialize antialiasing backup in case we start dragging:
+    if (mParentPlot->noAntialiasingOnDrag())
+    {
+      mAADragBackup = mParentPlot->antialiasedElements();
+      mNotAADragBackup = mParentPlot->notAntialiasedElements();
+    }
+    // Mouse range dragging interaction:
+    if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
+      mDragStartRange = mRange;
+  }
+}
+
+/*! \internal
+  
+  This mouse event reimplementation provides the functionality to let the user drag individual axes
+  exclusively, by startig the drag on top of the axis.
+  
+  \seebaseclassmethod
+  
+  \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis
+  rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent.
+  
+  \see QCPAxis::mousePressEvent
+*/
+void QCPPolarAxisRadial::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
+{
+  Q_UNUSED(event) // TODO remove later
+  Q_UNUSED(startPos) // TODO remove later
+  if (mDragging)
+  {
+    /* TODO
+    const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y();
+    const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y();
+    if (mScaleType == QCPPolarAxisRadial::stLinear)
+    {
+      const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel);
+      setRange(mDragStartRange.lower+diff, mDragStartRange.upper+diff);
+    } else if (mScaleType == QCPPolarAxisRadial::stLogarithmic)
+    {
+      const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel);
+      setRange(mDragStartRange.lower*diff, mDragStartRange.upper*diff);
+    }
+    */
+    
+    if (mParentPlot->noAntialiasingOnDrag())
+      mParentPlot->setNotAntialiasedElements(QCP::aeAll);
+    mParentPlot->replot(QCustomPlot::rpQueuedReplot);
+  }
+}
+
+/*! \internal
+  
+  This mouse event reimplementation provides the functionality to let the user drag individual axes
+  exclusively, by startig the drag on top of the axis.
+  
+  \seebaseclassmethod
+  
+  \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis
+  rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent.
+  
+  \see QCPAxis::mousePressEvent
+*/
+void QCPPolarAxisRadial::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
+{
+  Q_UNUSED(event)
+  Q_UNUSED(startPos)
+  mDragging = false;
+  if (mParentPlot->noAntialiasingOnDrag())
+  {
+    mParentPlot->setAntialiasedElements(mAADragBackup);
+    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
+  }
+}
+
+/*! \internal
+  
+  This mouse event reimplementation provides the functionality to let the user zoom individual axes
+  exclusively, by performing the wheel event on top of the axis.
+
+  For the axis to accept this event and perform the single axis zoom, the parent \ref QCPAxisRect
+  must be configured accordingly, i.e. it must allow range zooming in the orientation of this axis
+  (\ref QCPAxisRect::setRangeZoom) and this axis must be a zoomable axis (\ref
+  QCPAxisRect::setRangeZoomAxes)
+  
+  \seebaseclassmethod
+  
+  \note The zooming of possibly multiple axes at once by performing the wheel event anywhere in the
+  axis rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::wheelEvent.
+*/
+void QCPPolarAxisRadial::wheelEvent(QWheelEvent *event)
+{
+  // Mouse range zooming interaction:
+  if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom))
+  {
+    event->ignore();
+    return;
+  }
+  
+  // TODO:
+  //const double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
+  //const double factor = qPow(mRangeZoomFactor, wheelSteps);
+  //scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y()));
+  mParentPlot->replot();
+}
+
+void QCPPolarAxisRadial::updateGeometry(const QPointF &center, double radius)
+{
+  mCenter = center;
+  mRadius = radius;
+  if (mRadius < 1) mRadius = 1;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing axis lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+  
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+  
+  \seebaseclassmethod
+  
+  \see setAntialiased
+*/
+void QCPPolarAxisRadial::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
+}
+
+/*! \internal
+  
+  Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance.
+
+  \seebaseclassmethod
+*/
+void QCPPolarAxisRadial::draw(QCPPainter *painter)
+{
+  const double axisAngleRad = (mAngle+(mAngleReference==arAngularAxis ? mAngularAxis->angle() : 0))/180.0*M_PI;
+  const QPointF axisVector(qCos(axisAngleRad), qSin(axisAngleRad)); // semantically should be QCPVector2D, but we save time in loops when we keep it as QPointF
+  const QPointF tickNormal = QCPVector2D(axisVector).perpendicular().toPointF(); // semantically should be QCPVector2D, but we save time in loops when we keep it as QPointF
+  
+  // draw baseline:
+  painter->setPen(getBasePen());
+  painter->drawLine(QLineF(mCenter, mCenter+axisVector*(mRadius-0.5)));
+  
+  // draw subticks:
+  if (!mSubTickVector.isEmpty())
+  {
+    painter->setPen(getSubTickPen());
+    for (int i=0; i<mSubTickVector.size(); ++i)
+    {
+      const QPointF tickPosition = mCenter+axisVector*coordToRadius(mSubTickVector.at(i));
+      painter->drawLine(QLineF(tickPosition-tickNormal*mSubTickLengthIn, tickPosition+tickNormal*mSubTickLengthOut));
+    }
+  }
+  
+  // draw ticks and labels:
+  if (!mTickVector.isEmpty())
+  {
+    mLabelPainter.setAnchorReference(mCenter-axisVector); // subtract (normalized) axisVector, just to prevent degenerate tangents for tick label at exact lower axis range
+    mLabelPainter.setFont(getTickLabelFont());
+    mLabelPainter.setColor(getTickLabelColor());
+    const QPen ticksPen = getTickPen();
+    painter->setPen(ticksPen);
+    for (int i=0; i<mTickVector.size(); ++i)
+    {
+      const double r = coordToRadius(mTickVector.at(i));
+      const QPointF tickPosition = mCenter+axisVector*r;
+      painter->drawLine(QLineF(tickPosition-tickNormal*mTickLengthIn, tickPosition+tickNormal*mTickLengthOut));
+      // possibly draw tick labels:
+      if (!mTickVectorLabels.isEmpty())
+      {
+        if ((!mRangeReversed && (i < mTickVectorLabels.count()-1 || mRadius-r > 10)) ||
+            (mRangeReversed && (i > 0 || mRadius-r > 10))) // skip last label if it's closer than 10 pixels to angular axis
+          mLabelPainter.drawTickLabel(painter, tickPosition+tickNormal*mSubTickLengthOut, mTickVectorLabels.at(i));
+      }
+    }
+  }
+}
+
+/*! \internal
+  
+  Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling
+  QCPAxisTicker::generate on the currently installed ticker.
+  
+  If a change in the label text/count is detected, the cached axis margin is invalidated to make
+  sure the next margin calculation recalculates the label sizes and returns an up-to-date value.
+*/
+void QCPPolarAxisRadial::setupTickVectors()
+{
+  if (!mParentPlot) return;
+  if ((!mTicks && !mTickLabels) || mRange.size() <= 0) return;
+  
+  mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0);
+}
+
+/*! \internal
+  
+  Returns the pen that is used to draw the axis base line. Depending on the selection state, this
+  is either mSelectedBasePen or mBasePen.
+*/
+QPen QCPPolarAxisRadial::getBasePen() const
+{
+  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
+}
+
+/*! \internal
+  
+  Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this
+  is either mSelectedTickPen or mTickPen.
+*/
+QPen QCPPolarAxisRadial::getTickPen() const
+{
+  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
+}
+
+/*! \internal
+  
+  Returns the pen that is used to draw the subticks. Depending on the selection state, this
+  is either mSelectedSubTickPen or mSubTickPen.
+*/
+QPen QCPPolarAxisRadial::getSubTickPen() const
+{
+  return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
+}
+
+/*! \internal
+  
+  Returns the font that is used to draw the tick labels. Depending on the selection state, this
+  is either mSelectedTickLabelFont or mTickLabelFont.
+*/
+QFont QCPPolarAxisRadial::getTickLabelFont() const
+{
+  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
+}
+
+/*! \internal
+  
+  Returns the font that is used to draw the axis label. Depending on the selection state, this
+  is either mSelectedLabelFont or mLabelFont.
+*/
+QFont QCPPolarAxisRadial::getLabelFont() const
+{
+  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
+}
+
+/*! \internal
+  
+  Returns the color that is used to draw the tick labels. Depending on the selection state, this
+  is either mSelectedTickLabelColor or mTickLabelColor.
+*/
+QColor QCPPolarAxisRadial::getTickLabelColor() const
+{
+  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
+}
+
+/*! \internal
+  
+  Returns the color that is used to draw the axis label. Depending on the selection state, this
+  is either mSelectedLabelColor or mLabelColor.
+*/
+QColor QCPPolarAxisRadial::getLabelColor() const
+{
+  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
+}
+
+
+/* inherits documentation from base class */
+QCP::Interaction QCPPolarAxisRadial::selectionCategory() const
+{
+  return QCP::iSelectAxes;
+}
+/* end of 'src/polar/radialaxis.cpp' */
+
+
+/* including file 'src/polar/layoutelement-angularaxis.cpp' */
+/* modified 2021-03-29T02:30:44, size 57266                 */
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPPolarAxisAngular
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPPolarAxisAngular
+  \brief The main container for polar plots, representing the angular axis as a circle
+
+  \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and
+  functionality to be incomplete, as well as changing public interfaces in the future.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QCPLayoutInset *QCPPolarAxisAngular::insetLayout() const
+  
+  Returns the inset layout of this axis rect. It can be used to place other layout elements (or
+  even layouts with multiple other elements) inside/on top of an axis rect.
+  
+  \see QCPLayoutInset
+*/
+
+/*! \fn int QCPPolarAxisAngular::left() const
+  
+  Returns the pixel position of the left border of this axis rect. Margins are not taken into
+  account here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPPolarAxisAngular::right() const
+  
+  Returns the pixel position of the right border of this axis rect. Margins are not taken into
+  account here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPPolarAxisAngular::top() const
+  
+  Returns the pixel position of the top border of this axis rect. Margins are not taken into
+  account here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPPolarAxisAngular::bottom() const
+  
+  Returns the pixel position of the bottom border of this axis rect. Margins are not taken into
+  account here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPPolarAxisAngular::width() const
+  
+  Returns the pixel width of this axis rect. Margins are not taken into account here, so the
+  returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPPolarAxisAngular::height() const
+  
+  Returns the pixel height of this axis rect. Margins are not taken into account here, so the
+  returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QSize QCPPolarAxisAngular::size() const
+  
+  Returns the pixel size of this axis rect. Margins are not taken into account here, so the
+  returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPPolarAxisAngular::topLeft() const
+  
+  Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
+  so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPPolarAxisAngular::topRight() const
+  
+  Returns the top right corner of this axis rect in pixels. Margins are not taken into account
+  here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPPolarAxisAngular::bottomLeft() const
+  
+  Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account
+  here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPPolarAxisAngular::bottomRight() const
+  
+  Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account
+  here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPPolarAxisAngular::center() const
+  
+  Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
+  returned value is with respect to the inner \ref rect.
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates a QCPPolarAxis instance and sets default values. An axis is added for each of the four
+  sides, the top and right axes are set invisible initially.
+*/
+QCPPolarAxisAngular::QCPPolarAxisAngular(QCustomPlot *parentPlot) :
+  QCPLayoutElement(parentPlot),
+  mBackgroundBrush(Qt::NoBrush),
+  mBackgroundScaled(true),
+  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
+  mInsetLayout(new QCPLayoutInset),
+  mRangeDrag(false),
+  mRangeZoom(false),
+  mRangeZoomFactor(0.85),
+  // axis base:
+  mAngle(-90),
+  mAngleRad(mAngle/180.0*M_PI),
+  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
+  mSelectedParts(spNone),
+  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  mSelectedBasePen(QPen(Qt::blue, 2)),
+  // axis label:
+  mLabelPadding(0),
+  mLabel(),
+  mLabelFont(mParentPlot->font()),
+  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
+  mLabelColor(Qt::black),
+  mSelectedLabelColor(Qt::blue),
+  // tick labels:
+  //mTickLabelPadding(0), in label painter
+  mTickLabels(true),
+  //mTickLabelRotation(0), in label painter
+  mTickLabelFont(mParentPlot->font()),
+  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
+  mTickLabelColor(Qt::black),
+  mSelectedTickLabelColor(Qt::blue),
+  mNumberPrecision(6),
+  mNumberFormatChar('g'),
+  mNumberBeautifulPowers(true),
+  mNumberMultiplyCross(false),
+  // ticks and subticks:
+  mTicks(true),
+  mSubTicks(true),
+  mTickLengthIn(5),
+  mTickLengthOut(0),
+  mSubTickLengthIn(2),
+  mSubTickLengthOut(0),
+  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  mSelectedTickPen(QPen(Qt::blue, 2)),
+  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  mSelectedSubTickPen(QPen(Qt::blue, 2)),
+  // scale and range:
+  mRange(0, 360),
+  mRangeReversed(false),
+  // internal members:
+  mRadius(1), // non-zero initial value, will be overwritten in ::update() according to inner rect
+  mGrid(new QCPPolarGrid(this)),
+  mTicker(new QCPAxisTickerFixed),
+  mDragging(false),
+  mLabelPainter(parentPlot)
+{
+  // TODO:
+  //mInsetLayout->initializeParentPlot(mParentPlot);
+  //mInsetLayout->setParentLayerable(this);
+  //mInsetLayout->setParent(this);
+ 
+  if (QCPAxisTickerFixed *fixedTicker = mTicker.dynamicCast<QCPAxisTickerFixed>().data())
+  {
+    fixedTicker->setTickStep(30);
+  }
+  setAntialiased(true);
+  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
+  
+  setTickLabelPadding(5);
+  setTickLabelRotation(0);
+  setTickLabelMode(lmUpright);
+  mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artNormal);
+  mLabelPainter.setAbbreviateDecimalPowers(false);
+  mLabelPainter.setCacheSize(24); // so we can cache up to 15-degree intervals, polar angular axis uses a bit larger cache than normal axes
+  
+  setMinimumSize(50, 50);
+  setMinimumMargins(QMargins(30, 30, 30, 30));
+  
+  addRadialAxis();
+  mGrid->setRadialAxis(radialAxis());
+}
+
+QCPPolarAxisAngular::~QCPPolarAxisAngular()
+{
+  delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
+  mGrid = 0;
+  
+  delete mInsetLayout;
+  mInsetLayout = 0;
+  
+  QList<QCPPolarAxisRadial*> radialAxesList = radialAxes();
+  for (int i=0; i<radialAxesList.size(); ++i)
+    removeRadialAxis(radialAxesList.at(i));
+}
+
+QCPPolarAxisAngular::LabelMode QCPPolarAxisAngular::tickLabelMode() const
+{
+  switch (mLabelPainter.anchorMode())
+  {
+    case QCPLabelPainterPrivate::amSkewedUpright: return lmUpright;
+    case QCPLabelPainterPrivate::amSkewedRotated: return lmRotated;
+    default: qDebug() << Q_FUNC_INFO << "invalid mode for polar axis"; break;
+  }
+  return lmUpright;
+}
+
+/* No documentation as it is a property getter */
+QString QCPPolarAxisAngular::numberFormat() const
+{
+  QString result;
+  result.append(mNumberFormatChar);
+  if (mNumberBeautifulPowers)
+  {
+    result.append(QLatin1Char('b'));
+    if (mLabelPainter.multiplicationSymbol() == QCPLabelPainterPrivate::SymbolCross)
+      result.append(QLatin1Char('c'));
+  }
+  return result;
+}
+
+/*!
+  Returns the number of axes on the axis rect side specified with \a type.
+  
+  \see axis
+*/
+int QCPPolarAxisAngular::radialAxisCount() const
+{
+  return mRadialAxes.size();
+}
+
+/*!
+  Returns the axis with the given \a index on the axis rect side specified with \a type.
+  
+  \see axisCount, axes
+*/
+QCPPolarAxisRadial *QCPPolarAxisAngular::radialAxis(int index) const
+{
+  if (index >= 0 && index < mRadialAxes.size())
+  {
+    return mRadialAxes.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*!
+  Returns all axes on the axis rect sides specified with \a types.
+  
+  \a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
+  multiple sides.
+  
+  \see axis
+*/
+QList<QCPPolarAxisRadial*> QCPPolarAxisAngular::radialAxes() const
+{
+  return mRadialAxes;
+}
+
+
+/*!
+  Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a
+  new QCPAxis instance is created internally. QCustomPlot owns the returned axis, so if you want to
+  remove an axis, use \ref removeAxis instead of deleting it manually.
+
+  You may inject QCPAxis instances (or subclasses of QCPAxis) by setting \a axis to an axis that was
+  previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership
+  of the axis, so you may not delete it afterwards. Further, the \a axis must have been created
+  with this axis rect as parent and with the same axis type as specified in \a type. If this is not
+  the case, a debug output is generated, the axis is not added, and the method returns 0.
+
+  This method can not be used to move \a axis between axis rects. The same \a axis instance must
+  not be added multiple times to the same or different axis rects.
+
+  If an axis rect side already contains one or more axes, the lower and upper endings of the new
+  axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref
+  QCPLineEnding::esHalfBar.
+
+  \see addAxes, setupFullAxesBox
+*/
+QCPPolarAxisRadial *QCPPolarAxisAngular::addRadialAxis(QCPPolarAxisRadial *axis)
+{
+  QCPPolarAxisRadial *newAxis = axis;
+  if (!newAxis)
+  {
+    newAxis = new QCPPolarAxisRadial(this);
+  } else // user provided existing axis instance, do some sanity checks
+  {
+    if (newAxis->angularAxis() != this)
+    {
+      qDebug() << Q_FUNC_INFO << "passed radial axis doesn't have this angular axis as parent angular axis";
+      return 0;
+    }
+    if (radialAxes().contains(newAxis))
+    {
+      qDebug() << Q_FUNC_INFO << "passed axis is already owned by this angular axis";
+      return 0;
+    }
+  }
+  mRadialAxes.append(newAxis);
+  return newAxis;
+}
+
+/*!
+  Removes the specified \a axis from the axis rect and deletes it.
+  
+  Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
+  
+  \see addAxis
+*/
+bool QCPPolarAxisAngular::removeRadialAxis(QCPPolarAxisRadial *radialAxis)
+{
+  if (mRadialAxes.contains(radialAxis))
+  {
+    mRadialAxes.removeOne(radialAxis);
+    delete radialAxis;
+    return true;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Radial axis isn't associated with this angular axis:" << reinterpret_cast<quintptr>(radialAxis);
+    return false;
+  }
+}
+
+QRegion QCPPolarAxisAngular::exactClipRegion() const
+{
+  return QRegion(mCenter.x()-mRadius, mCenter.y()-mRadius, qRound(2*mRadius), qRound(2*mRadius), QRegion::Ellipse);
+}
+
+/*!
+  If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper
+  bounds of the range. The range is simply moved by \a diff.
+  
+  If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This
+  corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff).
+*/
+void QCPPolarAxisAngular::moveRange(double diff)
+{
+  QCPRange oldRange = mRange;
+  mRange.lower += diff;
+  mRange.upper += diff;
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Scales the range of this axis by \a factor around the center of the current axis range. For
+  example, if \a factor is 2.0, then the axis range will double its size, and the point at the axis
+  range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around
+  the center will have moved symmetrically closer).
+
+  If you wish to scale around a different coordinate than the current axis range center, use the
+  overload \ref scaleRange(double factor, double center).
+*/
+void QCPPolarAxisAngular::scaleRange(double factor)
+{
+  scaleRange(factor, range().center());
+}
+
+/*! \overload
+
+  Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a
+  factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at
+  coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates
+  around 1.0 will have moved symmetrically closer to 1.0).
+
+  \see scaleRange(double factor)
+*/
+void QCPPolarAxisAngular::scaleRange(double factor, double center)
+{
+  QCPRange oldRange = mRange;
+  QCPRange newRange;
+  newRange.lower = (mRange.lower-center)*factor + center;
+  newRange.upper = (mRange.upper-center)*factor + center;
+  if (QCPRange::validRange(newRange))
+    mRange = newRange.sanitizedForLinScale();
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Changes the axis range such that all plottables associated with this axis are fully visible in
+  that dimension.
+  
+  \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
+*/
+void QCPPolarAxisAngular::rescale(bool onlyVisiblePlottables)
+{
+  QCPRange newRange;
+  bool haveRange = false;
+  for (int i=0; i<mGraphs.size(); ++i)
+  {
+    if (!mGraphs.at(i)->realVisibility() && onlyVisiblePlottables)
+      continue;
+    QCPRange range;
+    bool currentFoundRange;
+    if (mGraphs.at(i)->keyAxis() == this)
+      range = mGraphs.at(i)->getKeyRange(currentFoundRange, QCP::sdBoth);
+    else
+      range = mGraphs.at(i)->getValueRange(currentFoundRange, QCP::sdBoth);
+    if (currentFoundRange)
+    {
+      if (!haveRange)
+        newRange = range;
+      else
+        newRange.expand(range);
+      haveRange = true;
+    }
+  }
+  if (haveRange)
+  {
+    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
+    {
+      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
+      newRange.lower = center-mRange.size()/2.0;
+      newRange.upper = center+mRange.size()/2.0;
+    }
+    setRange(newRange);
+  }
+}
+
+/*!
+  Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.
+*/
+void QCPPolarAxisAngular::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const
+{
+  if (!mRadialAxes.isEmpty())
+    mRadialAxes.first()->pixelToCoord(pixelPos, angleCoord, radiusCoord);
+  else
+    qDebug() << Q_FUNC_INFO << "no radial axis configured";
+}
+
+/*!
+  Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.
+*/
+QPointF QCPPolarAxisAngular::coordToPixel(double angleCoord, double radiusCoord) const
+{
+  if (!mRadialAxes.isEmpty())
+  {
+    return mRadialAxes.first()->coordToPixel(angleCoord, radiusCoord);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "no radial axis configured";
+    return QPointF();
+  }
+}
+
+/*!
+  Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function
+  is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this
+  function does not change the current selection state of the axis.
+  
+  If the axis is not visible (\ref setVisible), this function always returns \ref spNone.
+  
+  \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions
+*/
+QCPPolarAxisAngular::SelectablePart QCPPolarAxisAngular::getPartAt(const QPointF &pos) const
+{
+  Q_UNUSED(pos) // TODO remove later
+  
+  if (!mVisible)
+    return spNone;
+  
+  /*
+    TODO:
+  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
+    return spAxis;
+  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
+    return spTickLabels;
+  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
+    return spAxisLabel;
+  else */
+    return spNone;
+}
+
+/* inherits documentation from base class */
+double QCPPolarAxisAngular::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  /*
+  if (!mParentPlot) return -1;
+  SelectablePart part = getPartAt(pos);
+  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
+    return -1;
+  
+  if (details)
+    details->setValue(part);
+  return mParentPlot->selectionTolerance()*0.99;
+  */
+  
+  Q_UNUSED(details)
+  
+  if (onlySelectable)
+    return -1;
+  
+  if (QRectF(mOuterRect).contains(pos))
+  {
+    if (mParentPlot)
+      return mParentPlot->selectionTolerance()*0.99;
+    else
+    {
+      qDebug() << Q_FUNC_INFO << "parent plot not defined";
+      return -1;
+    }
+  } else
+    return -1;
+}
+
+/*!
+  This method is called automatically upon replot and doesn't need to be called by users of
+  QCPPolarAxisAngular.
+  
+  Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
+  and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
+  QCPInsetLayout::update function.
+  
+  \seebaseclassmethod
+*/
+void QCPPolarAxisAngular::update(UpdatePhase phase)
+{
+  QCPLayoutElement::update(phase);
+  
+  switch (phase)
+  {
+    case upPreparation:
+    {
+      setupTickVectors();
+      for (int i=0; i<mRadialAxes.size(); ++i)
+        mRadialAxes.at(i)->setupTickVectors();
+      break;
+    }
+    case upLayout:
+    {
+      mCenter = mRect.center();
+      mRadius = 0.5*qMin(qAbs(mRect.width()), qAbs(mRect.height()));
+      if (mRadius < 1) mRadius = 1; // prevent cases where radius might become 0 which causes trouble
+      for (int i=0; i<mRadialAxes.size(); ++i)
+        mRadialAxes.at(i)->updateGeometry(mCenter, mRadius);
+      
+      mInsetLayout->setOuterRect(rect());
+      break;
+    }
+    default: break;
+  }
+  
+  // pass update call on to inset layout (doesn't happen automatically, because QCPPolarAxis doesn't derive from QCPLayout):
+  mInsetLayout->update(phase);
+}
+
+/* inherits documentation from base class */
+QList<QCPLayoutElement*> QCPPolarAxisAngular::elements(bool recursive) const
+{
+  QList<QCPLayoutElement*> result;
+  if (mInsetLayout)
+  {
+    result << mInsetLayout;
+    if (recursive)
+      result << mInsetLayout->elements(recursive);
+  }
+  return result;
+}
+
+bool QCPPolarAxisAngular::removeGraph(QCPPolarGraph *graph)
+{
+  if (!mGraphs.contains(graph))
+  {
+    qDebug() << Q_FUNC_INFO << "graph not in list:" << reinterpret_cast<quintptr>(graph);
+    return false;
+  }
+  
+  // remove plottable from legend:
+  graph->removeFromLegend();
+  // remove plottable:
+  delete graph;
+  mGraphs.removeOne(graph);
+  return true;
+}
+
+/* inherits documentation from base class */
+void QCPPolarAxisAngular::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
+}
+
+/* inherits documentation from base class */
+void QCPPolarAxisAngular::draw(QCPPainter *painter)
+{
+  drawBackground(painter, mCenter, mRadius);
+  
+  // draw baseline circle:
+  painter->setPen(getBasePen());
+  painter->drawEllipse(mCenter, mRadius, mRadius);
+  
+  // draw subticks:
+  if (!mSubTickVector.isEmpty())
+  {
+    painter->setPen(getSubTickPen());
+    for (int i=0; i<mSubTickVector.size(); ++i)
+    {
+      painter->drawLine(mCenter+mSubTickVectorCosSin.at(i)*(mRadius-mSubTickLengthIn),
+                        mCenter+mSubTickVectorCosSin.at(i)*(mRadius+mSubTickLengthOut));
+    }
+  }
+  
+  // draw ticks and labels:
+  if (!mTickVector.isEmpty())
+  {
+    mLabelPainter.setAnchorReference(mCenter);
+    mLabelPainter.setFont(getTickLabelFont());
+    mLabelPainter.setColor(getTickLabelColor());
+    const QPen ticksPen = getTickPen();
+    painter->setPen(ticksPen);
+    for (int i=0; i<mTickVector.size(); ++i)
+    {
+      const QPointF outerTick = mCenter+mTickVectorCosSin.at(i)*(mRadius+mTickLengthOut);
+      painter->drawLine(mCenter+mTickVectorCosSin.at(i)*(mRadius-mTickLengthIn), outerTick);
+      // draw tick labels:
+      if (!mTickVectorLabels.isEmpty())
+      {
+        if (i < mTickVectorLabels.count()-1 || (mTickVectorCosSin.at(i)-mTickVectorCosSin.first()).manhattanLength() > 5/180.0*M_PI) // skip last label if it's closer than approx 5 degrees to first
+          mLabelPainter.drawTickLabel(painter, outerTick, mTickVectorLabels.at(i));
+      }
+    }
+  }
+}
+
+/* inherits documentation from base class */
+QCP::Interaction QCPPolarAxisAngular::selectionCategory() const
+{
+  return QCP::iSelectAxes;
+}
+
+
+/*!
+  Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
+  axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
+  backgrounds are usually drawn below everything else.
+
+  For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
+  enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
+  is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
+  consider using the overloaded version of this function.
+
+  Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
+  setBackground(const QBrush &brush).
+  
+  \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
+*/
+void QCPPolarAxisAngular::setBackground(const QPixmap &pm)
+{
+  mBackgroundPixmap = pm;
+  mScaledBackgroundPixmap = QPixmap();
+}
+
+/*! \overload
+  
+  Sets \a brush as the background brush. The axis rect background will be filled with this brush.
+  Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
+  are usually drawn below everything else.
+
+  The brush will be drawn before (under) any background pixmap, which may be specified with \ref
+  setBackground(const QPixmap &pm).
+
+  To disable drawing of a background brush, set \a brush to Qt::NoBrush.
+  
+  \see setBackground(const QPixmap &pm)
+*/
+void QCPPolarAxisAngular::setBackground(const QBrush &brush)
+{
+  mBackgroundBrush = brush;
+}
+
+/*! \overload
+  
+  Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
+  shall be scaled in one call.
+
+  \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
+*/
+void QCPPolarAxisAngular::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
+{
+  mBackgroundPixmap = pm;
+  mScaledBackgroundPixmap = QPixmap();
+  mBackgroundScaled = scaled;
+  mBackgroundScaledMode = mode;
+}
+
+/*!
+  Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
+  is set to true, you may control whether and how the aspect ratio of the original pixmap is
+  preserved with \ref setBackgroundScaledMode.
+  
+  Note that the scaled version of the original pixmap is buffered, so there is no performance
+  penalty on replots. (Except when the axis rect dimensions are changed continuously.)
+  
+  \see setBackground, setBackgroundScaledMode
+*/
+void QCPPolarAxisAngular::setBackgroundScaled(bool scaled)
+{
+  mBackgroundScaled = scaled;
+}
+
+/*!
+  If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
+  define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
+  \see setBackground, setBackgroundScaled
+*/
+void QCPPolarAxisAngular::setBackgroundScaledMode(Qt::AspectRatioMode mode)
+{
+  mBackgroundScaledMode = mode;
+}
+
+void QCPPolarAxisAngular::setRangeDrag(bool enabled)
+{
+  mRangeDrag = enabled;
+}
+
+void QCPPolarAxisAngular::setRangeZoom(bool enabled)
+{
+  mRangeZoom = enabled;
+}
+
+void QCPPolarAxisAngular::setRangeZoomFactor(double factor)
+{
+  mRangeZoomFactor = factor;
+}
+
+
+
+
+
+
+
+/*!
+  Sets the range of the axis.
+  
+  This slot may be connected with the \ref rangeChanged signal of another axis so this axis
+  is always synchronized with the other axis range, when it changes.
+  
+  To invert the direction of an axis, use \ref setRangeReversed.
+*/
+void QCPPolarAxisAngular::setRange(const QCPRange &range)
+{
+  if (range.lower == mRange.lower && range.upper == mRange.upper)
+    return;
+  
+  if (!QCPRange::validRange(range)) return;
+  QCPRange oldRange = mRange;
+  mRange = range.sanitizedForLinScale();
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains iSelectAxes.)
+  
+  However, even when \a selectable is set to a value not allowing the selection of a specific part,
+  it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
+  directly.
+  
+  \see SelectablePart, setSelectedParts
+*/
+void QCPPolarAxisAngular::setSelectableParts(const SelectableParts &selectable)
+{
+  if (mSelectableParts != selectable)
+  {
+    mSelectableParts = selectable;
+    emit selectableChanged(mSelectableParts);
+  }
+}
+
+/*!
+  Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part
+  is selected, it uses a different pen/font.
+  
+  The entire selection mechanism for axes is handled automatically when \ref
+  QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you
+  wish to change the selection state manually.
+  
+  This function can change the selection state of a part, independent of the \ref setSelectableParts setting.
+  
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+  
+  \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,
+  setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
+*/
+void QCPPolarAxisAngular::setSelectedParts(const SelectableParts &selected)
+{
+  if (mSelectedParts != selected)
+  {
+    mSelectedParts = selected;
+    emit selectionChanged(mSelectedParts);
+  }
+}
+
+/*!
+  \overload
+  
+  Sets the lower and upper bound of the axis range.
+  
+  To invert the direction of an axis, use \ref setRangeReversed.
+  
+  There is also a slot to set a range, see \ref setRange(const QCPRange &range).
+*/
+void QCPPolarAxisAngular::setRange(double lower, double upper)
+{
+  if (lower == mRange.lower && upper == mRange.upper)
+    return;
+  
+  if (!QCPRange::validRange(lower, upper)) return;
+  QCPRange oldRange = mRange;
+  mRange.lower = lower;
+  mRange.upper = upper;
+  mRange = mRange.sanitizedForLinScale();
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  \overload
+  
+  Sets the range of the axis.
+  
+  The \a position coordinate indicates together with the \a alignment parameter, where the new
+  range will be positioned. \a size defines the size of the new axis range. \a alignment may be
+  Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,
+  or center of the range to be aligned with \a position. Any other values of \a alignment will
+  default to Qt::AlignCenter.
+*/
+void QCPPolarAxisAngular::setRange(double position, double size, Qt::AlignmentFlag alignment)
+{
+  if (alignment == Qt::AlignLeft)
+    setRange(position, position+size);
+  else if (alignment == Qt::AlignRight)
+    setRange(position-size, position);
+  else // alignment == Qt::AlignCenter
+    setRange(position-size/2.0, position+size/2.0);
+}
+
+/*!
+  Sets the lower bound of the axis range. The upper bound is not changed.
+  \see setRange
+*/
+void QCPPolarAxisAngular::setRangeLower(double lower)
+{
+  if (mRange.lower == lower)
+    return;
+  
+  QCPRange oldRange = mRange;
+  mRange.lower = lower;
+  mRange = mRange.sanitizedForLinScale();
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Sets the upper bound of the axis range. The lower bound is not changed.
+  \see setRange
+*/
+void QCPPolarAxisAngular::setRangeUpper(double upper)
+{
+  if (mRange.upper == upper)
+    return;
+  
+  QCPRange oldRange = mRange;
+  mRange.upper = upper;
+  mRange = mRange.sanitizedForLinScale();
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal
+  axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the
+  direction of increasing values is inverted.
+
+  Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part
+  of the \ref setRange interface will still reference the mathematically smaller number than the \a
+  upper part.
+*/
+void QCPPolarAxisAngular::setRangeReversed(bool reversed)
+{
+  mRangeReversed = reversed;
+}
+
+void QCPPolarAxisAngular::setAngle(double degrees)
+{
+  mAngle = degrees;
+  mAngleRad = mAngle/180.0*M_PI;
+}
+
+/*!
+  The axis ticker is responsible for generating the tick positions and tick labels. See the
+  documentation of QCPAxisTicker for details on how to work with axis tickers.
+  
+  You can change the tick positioning/labeling behaviour of this axis by setting a different
+  QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis
+  ticker, access it via \ref ticker.
+  
+  Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis
+  ticker simply by passing the same shared pointer to multiple axes.
+  
+  \see ticker
+*/
+void QCPPolarAxisAngular::setTicker(QSharedPointer<QCPAxisTicker> ticker)
+{
+  if (ticker)
+    mTicker = ticker;
+  else
+    qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker";
+  // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector
+}
+
+/*!
+  Sets whether tick marks are displayed.
+
+  Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve
+  that, see \ref setTickLabels.
+  
+  \see setSubTicks
+*/
+void QCPPolarAxisAngular::setTicks(bool show)
+{
+  if (mTicks != show)
+  {
+    mTicks = show;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.
+*/
+void QCPPolarAxisAngular::setTickLabels(bool show)
+{
+  if (mTickLabels != show)
+  {
+    mTickLabels = show;
+    //mCachedMarginValid = false;
+    if (!mTickLabels)
+      mTickVectorLabels.clear();
+  }
+}
+
+/*!
+  Sets the distance between the axis base line (including any outward ticks) and the tick labels.
+  \see setLabelPadding, setPadding
+*/
+void QCPPolarAxisAngular::setTickLabelPadding(int padding)
+{
+  mLabelPainter.setPadding(padding);
+}
+
+/*!
+  Sets the font of the tick labels.
+  
+  \see setTickLabels, setTickLabelColor
+*/
+void QCPPolarAxisAngular::setTickLabelFont(const QFont &font)
+{
+  mTickLabelFont = font;
+}
+
+/*!
+  Sets the color of the tick labels.
+  
+  \see setTickLabels, setTickLabelFont
+*/
+void QCPPolarAxisAngular::setTickLabelColor(const QColor &color)
+{
+  mTickLabelColor = color;
+}
+
+/*!
+  Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else,
+  the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values
+  from -90 to 90 degrees.
+  
+  If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For
+  other angles, the label is drawn with an offset such that it seems to point toward or away from
+  the tick mark.
+*/
+void QCPPolarAxisAngular::setTickLabelRotation(double degrees)
+{
+  mLabelPainter.setRotation(degrees);
+}
+
+void QCPPolarAxisAngular::setTickLabelMode(LabelMode mode)
+{
+  switch (mode)
+  {
+    case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break;
+    case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break;
+  }
+}
+
+/*!
+  Sets the number format for the numbers in tick labels. This \a formatCode is an extended version
+  of the format code used e.g. by QString::number() and QLocale::toString(). For reference about
+  that, see the "Argument Formats" section in the detailed description of the QString class.
+  
+  \a formatCode is a string of one, two or three characters. The first character is identical to
+  the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed
+  format, 'g'/'G' scientific or fixed, whichever is shorter.
+  
+  The second and third characters are optional and specific to QCustomPlot:\n If the first char was
+  'e' or 'g', numbers are/might be displayed in the scientific format, e.g. "5.5e9", which might be
+  visually unappealing in a plot. So when the second char of \a formatCode is set to 'b' (for
+  "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
+  [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot.
+  If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can
+  be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the
+  cross and 183 (0xB7) for the dot.
+  
+  Examples for \a formatCode:
+  \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large,
+  normal scientific format is used
+  \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with
+  beautifully typeset decimal powers and a dot as multiplication sign
+  \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as
+  multiplication sign
+  \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal
+  powers. Format code will be reduced to 'f'.
+  \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format
+  code will not be changed.
+*/
+void QCPPolarAxisAngular::setNumberFormat(const QString &formatCode)
+{
+  if (formatCode.isEmpty())
+  {
+    qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
+    return;
+  }
+  //mCachedMarginValid = false;
+  
+  // interpret first char as number format char:
+  QString allowedFormatChars(QLatin1String("eEfgG"));
+  if (allowedFormatChars.contains(formatCode.at(0)))
+  {
+    mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
+    return;
+  }
+  
+  if (formatCode.length() < 2)
+  {
+    mNumberBeautifulPowers = false;
+    mNumberMultiplyCross = false;
+  } else
+  {
+    // interpret second char as indicator for beautiful decimal powers:
+    if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
+      mNumberBeautifulPowers = true;
+    else
+      qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
+    
+    if (formatCode.length() < 3)
+    {
+      mNumberMultiplyCross = false;
+    } else
+    {
+      // interpret third char as indicator for dot or cross multiplication symbol:
+      if (formatCode.at(2) == QLatin1Char('c'))
+        mNumberMultiplyCross = true;
+      else if (formatCode.at(2) == QLatin1Char('d'))
+        mNumberMultiplyCross = false;
+      else
+        qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
+    }
+  }
+  mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers);
+  mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot);
+}
+
+/*!
+  Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)
+  for details. The effect of precisions are most notably for number Formats starting with 'e', see
+  \ref setNumberFormat
+*/
+void QCPPolarAxisAngular::setNumberPrecision(int precision)
+{
+  if (mNumberPrecision != precision)
+  {
+    mNumberPrecision = precision;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the
+  plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
+  zero, the tick labels and axis label will increase their distance to the axis accordingly, so
+  they won't collide with the ticks.
+  
+  \see setSubTickLength, setTickLengthIn, setTickLengthOut
+*/
+void QCPPolarAxisAngular::setTickLength(int inside, int outside)
+{
+  setTickLengthIn(inside);
+  setTickLengthOut(outside);
+}
+
+/*!
+  Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
+  inside the plot.
+  
+  \see setTickLengthOut, setTickLength, setSubTickLength
+*/
+void QCPPolarAxisAngular::setTickLengthIn(int inside)
+{
+  if (mTickLengthIn != inside)
+  {
+    mTickLengthIn = inside;
+  }
+}
+
+/*!
+  Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach
+  outside the plot. If \a outside is greater than zero, the tick labels and axis label will
+  increase their distance to the axis accordingly, so they won't collide with the ticks.
+  
+  \see setTickLengthIn, setTickLength, setSubTickLength
+*/
+void QCPPolarAxisAngular::setTickLengthOut(int outside)
+{
+  if (mTickLengthOut != outside)
+  {
+    mTickLengthOut = outside;
+    //mCachedMarginValid = false; // only outside tick length can change margin
+  }
+}
+
+/*!
+  Sets whether sub tick marks are displayed.
+  
+  Sub ticks are only potentially visible if (major) ticks are also visible (see \ref setTicks)
+  
+  \see setTicks
+*/
+void QCPPolarAxisAngular::setSubTicks(bool show)
+{
+  if (mSubTicks != show)
+  {
+    mSubTicks = show;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside
+  the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
+  than zero, the tick labels and axis label will increase their distance to the axis accordingly,
+  so they won't collide with the ticks.
+  
+  \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
+*/
+void QCPPolarAxisAngular::setSubTickLength(int inside, int outside)
+{
+  setSubTickLengthIn(inside);
+  setSubTickLengthOut(outside);
+}
+
+/*!
+  Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
+  the plot.
+  
+  \see setSubTickLengthOut, setSubTickLength, setTickLength
+*/
+void QCPPolarAxisAngular::setSubTickLengthIn(int inside)
+{
+  if (mSubTickLengthIn != inside)
+  {
+    mSubTickLengthIn = inside;
+  }
+}
+
+/*!
+  Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach
+  outside the plot. If \a outside is greater than zero, the tick labels will increase their
+  distance to the axis accordingly, so they won't collide with the ticks.
+  
+  \see setSubTickLengthIn, setSubTickLength, setTickLength
+*/
+void QCPPolarAxisAngular::setSubTickLengthOut(int outside)
+{
+  if (mSubTickLengthOut != outside)
+  {
+    mSubTickLengthOut = outside;
+    //mCachedMarginValid = false; // only outside tick length can change margin
+  }
+}
+
+/*!
+  Sets the pen, the axis base line is drawn with.
+  
+  \see setTickPen, setSubTickPen
+*/
+void QCPPolarAxisAngular::setBasePen(const QPen &pen)
+{
+  mBasePen = pen;
+}
+
+/*!
+  Sets the pen, tick marks will be drawn with.
+  
+  \see setTickLength, setBasePen
+*/
+void QCPPolarAxisAngular::setTickPen(const QPen &pen)
+{
+  mTickPen = pen;
+}
+
+/*!
+  Sets the pen, subtick marks will be drawn with.
+  
+  \see setSubTickCount, setSubTickLength, setBasePen
+*/
+void QCPPolarAxisAngular::setSubTickPen(const QPen &pen)
+{
+  mSubTickPen = pen;
+}
+
+/*!
+  Sets the font of the axis label.
+  
+  \see setLabelColor
+*/
+void QCPPolarAxisAngular::setLabelFont(const QFont &font)
+{
+  if (mLabelFont != font)
+  {
+    mLabelFont = font;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the color of the axis label.
+  
+  \see setLabelFont
+*/
+void QCPPolarAxisAngular::setLabelColor(const QColor &color)
+{
+  mLabelColor = color;
+}
+
+/*!
+  Sets the text of the axis label that will be shown below/above or next to the axis, depending on
+  its orientation. To disable axis labels, pass an empty string as \a str.
+*/
+void QCPPolarAxisAngular::setLabel(const QString &str)
+{
+  if (mLabel != str)
+  {
+    mLabel = str;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the distance between the tick labels and the axis label.
+  
+  \see setTickLabelPadding, setPadding
+*/
+void QCPPolarAxisAngular::setLabelPadding(int padding)
+{
+  if (mLabelPadding != padding)
+  {
+    mLabelPadding = padding;
+    //mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the font that is used for tick labels when they are selected.
+  
+  \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisAngular::setSelectedTickLabelFont(const QFont &font)
+{
+  if (font != mSelectedTickLabelFont)
+  {
+    mSelectedTickLabelFont = font;
+    // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
+  }
+}
+
+/*!
+  Sets the font that is used for the axis label when it is selected.
+  
+  \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisAngular::setSelectedLabelFont(const QFont &font)
+{
+  mSelectedLabelFont = font;
+  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
+}
+
+/*!
+  Sets the color that is used for tick labels when they are selected.
+  
+  \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisAngular::setSelectedTickLabelColor(const QColor &color)
+{
+  if (color != mSelectedTickLabelColor)
+  {
+    mSelectedTickLabelColor = color;
+  }
+}
+
+/*!
+  Sets the color that is used for the axis label when it is selected.
+  
+  \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisAngular::setSelectedLabelColor(const QColor &color)
+{
+  mSelectedLabelColor = color;
+}
+
+/*!
+  Sets the pen that is used to draw the axis base line when selected.
+  
+  \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisAngular::setSelectedBasePen(const QPen &pen)
+{
+  mSelectedBasePen = pen;
+}
+
+/*!
+  Sets the pen that is used to draw the (major) ticks when selected.
+  
+  \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisAngular::setSelectedTickPen(const QPen &pen)
+{
+  mSelectedTickPen = pen;
+}
+
+/*!
+  Sets the pen that is used to draw the subticks when selected.
+  
+  \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPPolarAxisAngular::setSelectedSubTickPen(const QPen &pen)
+{
+  mSelectedSubTickPen = pen;
+}
+
+/*! \internal
+  
+  Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
+  pixmap.
+  
+  If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
+  according filling inside the axis rect with the provided \a painter.
+  
+  Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
+  depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
+  the axis rect with the provided \a painter. The scaled version is buffered in
+  mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
+  the axis rect has changed in a way that requires a rescale of the background pixmap (this is
+  dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was
+  set.
+  
+  \see setBackground, setBackgroundScaled, setBackgroundScaledMode
+*/
+void QCPPolarAxisAngular::drawBackground(QCPPainter *painter, const QPointF &center, double radius)
+{
+  // draw background fill (don't use circular clip, looks bad):
+  if (mBackgroundBrush != Qt::NoBrush)
+  {
+    QPainterPath ellipsePath;
+    ellipsePath.addEllipse(center, radius, radius);
+    painter->fillPath(ellipsePath, mBackgroundBrush);
+  }
+  
+  // draw background pixmap (on top of fill, if brush specified):
+  if (!mBackgroundPixmap.isNull())
+  {
+    QRegion clipCircle(center.x()-radius, center.y()-radius, qRound(2*radius), qRound(2*radius), QRegion::Ellipse);
+    QRegion originalClip = painter->clipRegion();
+    painter->setClipRegion(clipCircle);
+    if (mBackgroundScaled)
+    {
+      // check whether mScaledBackground needs to be updated:
+      QSize scaledSize(mBackgroundPixmap.size());
+      scaledSize.scale(mRect.size(), mBackgroundScaledMode);
+      if (mScaledBackgroundPixmap.size() != scaledSize)
+        mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
+      painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
+    } else
+    {
+      painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
+    }
+    painter->setClipRegion(originalClip);
+  }
+}
+
+/*! \internal
+  
+  Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling
+  QCPAxisTicker::generate on the currently installed ticker.
+  
+  If a change in the label text/count is detected, the cached axis margin is invalidated to make
+  sure the next margin calculation recalculates the label sizes and returns an up-to-date value.
+*/
+void QCPPolarAxisAngular::setupTickVectors()
+{
+  if (!mParentPlot) return;
+  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
+  
+  mSubTickVector.clear(); // since we might not pass it to mTicker->generate(), and we don't want old data in there
+  mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0);
+  
+  // fill cos/sin buffers which will be used by draw() and QCPPolarGrid::draw(), so we don't have to calculate it twice:
+  mTickVectorCosSin.resize(mTickVector.size());
+  for (int i=0; i<mTickVector.size(); ++i)
+  {
+    const double theta = coordToAngleRad(mTickVector.at(i));
+    mTickVectorCosSin[i] = QPointF(qCos(theta), qSin(theta));
+  }
+  mSubTickVectorCosSin.resize(mSubTickVector.size());
+  for (int i=0; i<mSubTickVector.size(); ++i)
+  {
+    const double theta = coordToAngleRad(mSubTickVector.at(i));
+    mSubTickVectorCosSin[i] = QPointF(qCos(theta), qSin(theta));
+  }
+}
+
+/*! \internal
+  
+  Returns the pen that is used to draw the axis base line. Depending on the selection state, this
+  is either mSelectedBasePen or mBasePen.
+*/
+QPen QCPPolarAxisAngular::getBasePen() const
+{
+  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
+}
+
+/*! \internal
+  
+  Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this
+  is either mSelectedTickPen or mTickPen.
+*/
+QPen QCPPolarAxisAngular::getTickPen() const
+{
+  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
+}
+
+/*! \internal
+  
+  Returns the pen that is used to draw the subticks. Depending on the selection state, this
+  is either mSelectedSubTickPen or mSubTickPen.
+*/
+QPen QCPPolarAxisAngular::getSubTickPen() const
+{
+  return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
+}
+
+/*! \internal
+  
+  Returns the font that is used to draw the tick labels. Depending on the selection state, this
+  is either mSelectedTickLabelFont or mTickLabelFont.
+*/
+QFont QCPPolarAxisAngular::getTickLabelFont() const
+{
+  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
+}
+
+/*! \internal
+  
+  Returns the font that is used to draw the axis label. Depending on the selection state, this
+  is either mSelectedLabelFont or mLabelFont.
+*/
+QFont QCPPolarAxisAngular::getLabelFont() const
+{
+  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
+}
+
+/*! \internal
+  
+  Returns the color that is used to draw the tick labels. Depending on the selection state, this
+  is either mSelectedTickLabelColor or mTickLabelColor.
+*/
+QColor QCPPolarAxisAngular::getTickLabelColor() const
+{
+  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
+}
+
+/*! \internal
+  
+  Returns the color that is used to draw the axis label. Depending on the selection state, this
+  is either mSelectedLabelColor or mLabelColor.
+*/
+QColor QCPPolarAxisAngular::getLabelColor() const
+{
+  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
+}
+
+/*! \internal
+  
+  Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
+  pressed, the range dragging interaction is initialized (the actual range manipulation happens in
+  the \ref mouseMoveEvent).
+
+  The mDragging flag is set to true and some anchor points are set that are needed to determine the
+  distance the mouse was dragged in the mouse move/release events later.
+  
+  \see mouseMoveEvent, mouseReleaseEvent
+*/
+void QCPPolarAxisAngular::mousePressEvent(QMouseEvent *event, const QVariant &details)
+{
+  Q_UNUSED(details)
+  if (event->buttons() & Qt::LeftButton)
+  {
+    mDragging = true;
+    // initialize antialiasing backup in case we start dragging:
+    if (mParentPlot->noAntialiasingOnDrag())
+    {
+      mAADragBackup = mParentPlot->antialiasedElements();
+      mNotAADragBackup = mParentPlot->notAntialiasedElements();
+    }
+    // Mouse range dragging interaction:
+    if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
+    {
+      mDragAngularStart = range();
+      mDragRadialStart.clear();
+      for (int i=0; i<mRadialAxes.size(); ++i)
+        mDragRadialStart.append(mRadialAxes.at(i)->range());
+    }
+  }
+}
+
+/*! \internal
+  
+  Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
+  preceding \ref mousePressEvent, the range is moved accordingly.
+  
+  \see mousePressEvent, mouseReleaseEvent
+*/
+void QCPPolarAxisAngular::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
+{
+  Q_UNUSED(startPos)
+  bool doReplot = false;
+  // Mouse range dragging interaction:
+  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
+  {
+    if (mRangeDrag)
+    {
+      doReplot = true;
+      double angleCoordStart, radiusCoordStart;
+      double angleCoord, radiusCoord;
+      pixelToCoord(startPos, angleCoordStart, radiusCoordStart);
+      pixelToCoord(event->pos(), angleCoord, radiusCoord);
+      double diff = angleCoordStart - angleCoord;
+      setRange(mDragAngularStart.lower+diff, mDragAngularStart.upper+diff);
+    }
+    
+    for (int i=0; i<mRadialAxes.size(); ++i)
+    {
+      QCPPolarAxisRadial *ax = mRadialAxes.at(i);
+      if (!ax->rangeDrag())
+        continue;
+      doReplot = true;
+      double angleCoordStart, radiusCoordStart;
+      double angleCoord, radiusCoord;
+      ax->pixelToCoord(startPos, angleCoordStart, radiusCoordStart);
+      ax->pixelToCoord(event->pos(), angleCoord, radiusCoord);
+      if (ax->scaleType() == QCPPolarAxisRadial::stLinear)
+      {
+        double diff = radiusCoordStart - radiusCoord;
+        ax->setRange(mDragRadialStart.at(i).lower+diff, mDragRadialStart.at(i).upper+diff);
+      } else if (ax->scaleType() == QCPPolarAxisRadial::stLogarithmic)
+      {
+        if (radiusCoord != 0)
+        {
+          double diff = radiusCoordStart/radiusCoord;
+          ax->setRange(mDragRadialStart.at(i).lower*diff, mDragRadialStart.at(i).upper*diff);
+        }
+      }
+    }
+    
+    if (doReplot) // if either vertical or horizontal drag was enabled, do a replot
+    {
+      if (mParentPlot->noAntialiasingOnDrag())
+        mParentPlot->setNotAntialiasedElements(QCP::aeAll);
+      mParentPlot->replot(QCustomPlot::rpQueuedReplot);
+    }
+  }
+}
+
+/* inherits documentation from base class */
+void QCPPolarAxisAngular::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
+{
+  Q_UNUSED(event)
+  Q_UNUSED(startPos)
+  mDragging = false;
+  if (mParentPlot->noAntialiasingOnDrag())
+  {
+    mParentPlot->setAntialiasedElements(mAADragBackup);
+    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
+  }
+}
+
+/*! \internal
+  
+  Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
+  ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
+  the scaling operation is the current cursor position inside the axis rect. The scaling factor is
+  dependent on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
+  zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
+  
+  Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
+  wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
+  multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
+  exponent of the range zoom factor. This takes care of the wheel direction automatically, by
+  inverting the factor, when the wheel step is negative (f^-1 = 1/f).
+*/
+void QCPPolarAxisAngular::wheelEvent(QWheelEvent *event)
+{
+  bool doReplot = false;
+  // Mouse range zooming interaction:
+  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
+  {
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+    const double delta = event->delta();
+#else
+    const double delta = event->angleDelta().y();
+#endif
+
+#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
+    const QPointF pos = event->pos();
+#else
+    const QPointF pos = event->position();
+#endif
+    const double wheelSteps = delta/120.0; // a single step delta is +/-120 usually
+    if (mRangeZoom)
+    {
+      double angleCoord, radiusCoord;
+      pixelToCoord(pos, angleCoord, radiusCoord);
+      scaleRange(qPow(mRangeZoomFactor, wheelSteps), angleCoord);
+    }
+
+    for (int i=0; i<mRadialAxes.size(); ++i)
+    {
+      QCPPolarAxisRadial *ax = mRadialAxes.at(i);
+      if (!ax->rangeZoom())
+        continue;
+      doReplot = true;
+      double angleCoord, radiusCoord;
+      ax->pixelToCoord(pos, angleCoord, radiusCoord);
+      ax->scaleRange(qPow(ax->rangeZoomFactor(), wheelSteps), radiusCoord);
+    }
+  }
+  if (doReplot)
+    mParentPlot->replot();
+}
+
+bool QCPPolarAxisAngular::registerPolarGraph(QCPPolarGraph *graph)
+{
+  if (mGraphs.contains(graph))
+  {
+    qDebug() << Q_FUNC_INFO << "plottable already added:" << reinterpret_cast<quintptr>(graph);
+    return false;
+  }
+  if (graph->keyAxis() != this)
+  {
+    qDebug() << Q_FUNC_INFO << "plottable not created with this as axis:" << reinterpret_cast<quintptr>(graph);
+    return false;
+  }
+  
+  mGraphs.append(graph);
+  // possibly add plottable to legend:
+  if (mParentPlot->autoAddPlottableToLegend())
+    graph->addToLegend();
+  if (!graph->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
+    graph->setLayer(mParentPlot->currentLayer());
+  return true;
+}
+/* end of 'src/polar/layoutelement-angularaxis.cpp' */
+
+
+/* including file 'src/polar/polargrid.cpp' */
+/* modified 2021-03-29T02:30:44, size 7493  */
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPPolarGrid
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPPolarGrid
+  \brief The grid in both angular and radial dimensions for polar plots
+
+  \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and
+  functionality to be incomplete, as well as changing public interfaces in the future.
+*/
+
+/*!
+  Creates a QCPPolarGrid instance and sets default values.
+  
+  You shouldn't instantiate grids on their own, since every axis brings its own grid.
+*/
+QCPPolarGrid::QCPPolarGrid(QCPPolarAxisAngular *parentAxis) :
+  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
+  mType(gtNone),
+  mSubGridType(gtNone),
+  mAntialiasedSubGrid(true),
+  mAntialiasedZeroLine(true),
+  mParentAxis(parentAxis)
+{
+  // warning: this is called in QCPPolarAxisAngular constructor, so parentAxis members should not be accessed/called
+  setParent(parentAxis);
+  setType(gtAll);
+  setSubGridType(gtNone);
+  
+  setAngularPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
+  setAngularSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
+  
+  setRadialPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
+  setRadialSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
+  setRadialZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
+  
+  setAntialiased(true);
+}
+
+void QCPPolarGrid::setRadialAxis(QCPPolarAxisRadial *axis)
+{
+  mRadialAxis = axis;
+}
+
+void QCPPolarGrid::setType(GridTypes type)
+{
+  mType = type;
+}
+
+void QCPPolarGrid::setSubGridType(GridTypes type)
+{
+  mSubGridType = type;
+}
+
+/*!
+  Sets whether sub grid lines are drawn antialiased.
+*/
+void QCPPolarGrid::setAntialiasedSubGrid(bool enabled)
+{
+  mAntialiasedSubGrid = enabled;
+}
+
+/*!
+  Sets whether zero lines are drawn antialiased.
+*/
+void QCPPolarGrid::setAntialiasedZeroLine(bool enabled)
+{
+  mAntialiasedZeroLine = enabled;
+}
+
+/*!
+  Sets the pen with which (major) grid lines are drawn.
+*/
+void QCPPolarGrid::setAngularPen(const QPen &pen)
+{
+  mAngularPen = pen;
+}
+
+/*!
+  Sets the pen with which sub grid lines are drawn.
+*/
+void QCPPolarGrid::setAngularSubGridPen(const QPen &pen)
+{
+  mAngularSubGridPen = pen;
+}
+
+void QCPPolarGrid::setRadialPen(const QPen &pen)
+{
+  mRadialPen = pen;
+}
+
+void QCPPolarGrid::setRadialSubGridPen(const QPen &pen)
+{
+  mRadialSubGridPen = pen;
+}
+
+void QCPPolarGrid::setRadialZeroLinePen(const QPen &pen)
+{
+  mRadialZeroLinePen = pen;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing the major grid lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+  
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+  
+  \see setAntialiased
+*/
+void QCPPolarGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
+}
+
+/*! \internal
+  
+  Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning
+  over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen).
+*/
+void QCPPolarGrid::draw(QCPPainter *painter)
+{
+  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
+  
+  const QPointF center = mParentAxis->mCenter;
+  const double radius = mParentAxis->mRadius;
+  
+  painter->setBrush(Qt::NoBrush);
+  // draw main angular grid:
+  if (mType.testFlag(gtAngular))
+    drawAngularGrid(painter, center, radius, mParentAxis->mTickVectorCosSin, mAngularPen);
+  // draw main radial grid:
+  if (mType.testFlag(gtRadial) && mRadialAxis)
+    drawRadialGrid(painter, center, mRadialAxis->tickVector(), mRadialPen, mRadialZeroLinePen);
+  
+  applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeGrid);
+  // draw sub angular grid:
+  if (mSubGridType.testFlag(gtAngular))
+    drawAngularGrid(painter, center, radius, mParentAxis->mSubTickVectorCosSin, mAngularSubGridPen);
+  // draw sub radial grid:
+  if (mSubGridType.testFlag(gtRadial) && mRadialAxis)
+    drawRadialGrid(painter, center, mRadialAxis->subTickVector(), mRadialSubGridPen);
+}
+
+void QCPPolarGrid::drawRadialGrid(QCPPainter *painter, const QPointF &center, const QVector<double> &coords, const QPen &pen, const QPen &zeroPen)
+{
+  if (!mRadialAxis) return;
+  if (coords.isEmpty()) return;
+  const bool drawZeroLine = zeroPen != Qt::NoPen;
+  const double zeroLineEpsilon = qAbs(coords.last()-coords.first())*1e-6;
+  
+  painter->setPen(pen);
+  for (int i=0; i<coords.size(); ++i)
+  {
+    const double r = mRadialAxis->coordToRadius(coords.at(i));
+    if (drawZeroLine && qAbs(coords.at(i)) < zeroLineEpsilon)
+    {
+      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
+      painter->setPen(zeroPen);
+      painter->drawEllipse(center, r, r);
+      painter->setPen(pen);
+      applyDefaultAntialiasingHint(painter);
+    } else
+    {
+      painter->drawEllipse(center, r, r);
+    }
+  }
+}
+
+void QCPPolarGrid::drawAngularGrid(QCPPainter *painter, const QPointF &center, double radius, const QVector<QPointF> &ticksCosSin, const QPen &pen)
+{
+  if (ticksCosSin.isEmpty()) return;
+  
+  painter->setPen(pen);
+  for (int i=0; i<ticksCosSin.size(); ++i)
+    painter->drawLine(center, center+ticksCosSin.at(i)*radius);
+}
+/* end of 'src/polar/polargrid.cpp' */
+
+
+/* including file 'src/polar/polargraph.cpp' */
+/* modified 2021-03-29T02:30:44, size 44035  */
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPPolarLegendItem
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPPolarLegendItem
+  \brief A legend item for polar plots
+
+  \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and
+  functionality to be incomplete, as well as changing public interfaces in the future.
+*/
+QCPPolarLegendItem::QCPPolarLegendItem(QCPLegend *parent, QCPPolarGraph *graph) :
+  QCPAbstractLegendItem(parent),
+  mPolarGraph(graph)
+{
+  setAntialiased(false);
+}
+
+void QCPPolarLegendItem::draw(QCPPainter *painter)
+{
+  if (!mPolarGraph) return;
+  painter->setFont(getFont());
+  painter->setPen(QPen(getTextColor()));
+  QSizeF iconSize = mParentLegend->iconSize();
+  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPolarGraph->name());
+  QRectF iconRect(mRect.topLeft(), iconSize);
+  int textHeight = qMax(textRect.height(), iconSize.height());  // if text has smaller height than icon, center text vertically in icon height, else align tops
+  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPolarGraph->name());
+  // draw icon:
+  painter->save();
+  painter->setClipRect(iconRect, Qt::IntersectClip);
+  mPolarGraph->drawLegendIcon(painter, iconRect);
+  painter->restore();
+  // draw icon border:
+  if (getIconBorderPen().style() != Qt::NoPen)
+  {
+    painter->setPen(getIconBorderPen());
+    painter->setBrush(Qt::NoBrush);
+    int halfPen = qCeil(painter->pen().widthF()*0.5)+1;
+    painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen)); // extend default clip rect so thicker pens (especially during selection) are not clipped
+    painter->drawRect(iconRect);
+  }
+}
+
+QSize QCPPolarLegendItem::minimumOuterSizeHint() const
+{
+  if (!mPolarGraph) return QSize();
+  QSize result(0, 0);
+  QRect textRect;
+  QFontMetrics fontMetrics(getFont());
+  QSize iconSize = mParentLegend->iconSize();
+  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPolarGraph->name());
+  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width());
+  result.setHeight(qMax(textRect.height(), iconSize.height()));
+  result.rwidth() += mMargins.left()+mMargins.right();
+  result.rheight() += mMargins.top()+mMargins.bottom();
+  return result;
+}
+
+QPen QCPPolarLegendItem::getIconBorderPen() const
+{
+  return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
+}
+
+QColor QCPPolarLegendItem::getTextColor() const
+{
+  return mSelected ? mSelectedTextColor : mTextColor;
+}
+
+QFont QCPPolarLegendItem::getFont() const
+{
+  return mSelected ? mSelectedFont : mFont;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPPolarGraph
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPPolarGraph
+  \brief A radial graph used to display data in polar plots
+
+  \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and
+  functionality to be incomplete, as well as changing public interfaces in the future.
+*/
+
+/* start of documentation of inline functions */
+
+// TODO
+
+/* end of documentation of inline functions */
+
+/*!
+  Constructs a graph which uses \a keyAxis as its angular and \a valueAxis as its radial axis. \a
+  keyAxis and \a valueAxis must reside in the same QCustomPlot, and the radial axis must be
+  associated with the angular axis. If either of these restrictions is violated, a corresponding
+  message is printed to the debug output (qDebug), the construction is not aborted, though.
+
+  The created QCPPolarGraph is automatically registered with the QCustomPlot instance inferred from
+  \a keyAxis. This QCustomPlot instance takes ownership of the QCPPolarGraph, so do not delete it
+  manually but use QCPPolarAxisAngular::removeGraph() instead.
+
+  To directly create a QCPPolarGraph inside a plot, you shoud use the QCPPolarAxisAngular::addGraph
+  method.
+*/
+QCPPolarGraph::QCPPolarGraph(QCPPolarAxisAngular *keyAxis, QCPPolarAxisRadial *valueAxis) :
+  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis),
+  mDataContainer(new QCPGraphDataContainer),
+  mName(),
+  mAntialiasedFill(true),
+  mAntialiasedScatters(true),
+  mPen(Qt::black),
+  mBrush(Qt::NoBrush),
+  mPeriodic(true),
+  mKeyAxis(keyAxis),
+  mValueAxis(valueAxis),
+  mSelectable(QCP::stWhole)
+  //mSelectionDecorator(0) // TODO
+{
+  if (keyAxis->parentPlot() != valueAxis->parentPlot())
+    qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
+  
+  mKeyAxis->registerPolarGraph(this);
+  
+  //setSelectionDecorator(new QCPSelectionDecorator); // TODO
+  
+  setPen(QPen(Qt::blue, 0));
+  setBrush(Qt::NoBrush);
+  setLineStyle(lsLine);
+}
+
+QCPPolarGraph::~QCPPolarGraph()
+{
+  /* TODO
+  if (mSelectionDecorator)
+  {
+    delete mSelectionDecorator;
+    mSelectionDecorator = 0;
+  }
+  */
+}
+
+/*!
+   The name is the textual representation of this plottable as it is displayed in the legend
+   (\ref QCPLegend). It may contain any UTF-8 characters, including newlines.
+*/
+void QCPPolarGraph::setName(const QString &name)
+{
+  mName = name;
+}
+
+/*!
+  Sets whether fills of this plottable are drawn antialiased or not.
+  
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPPolarGraph::setAntialiasedFill(bool enabled)
+{
+  mAntialiasedFill = enabled;
+}
+
+/*!
+  Sets whether the scatter symbols of this plottable are drawn antialiased or not.
+  
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPPolarGraph::setAntialiasedScatters(bool enabled)
+{
+  mAntialiasedScatters = enabled;
+}
+
+/*!
+  The pen is used to draw basic lines that make up the plottable representation in the
+  plot.
+  
+  For example, the \ref QCPGraph subclass draws its graph lines with this pen.
+
+  \see setBrush
+*/
+void QCPPolarGraph::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  The brush is used to draw basic fills of the plottable representation in the
+  plot. The Fill can be a color, gradient or texture, see the usage of QBrush.
+  
+  For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when
+  it's not set to Qt::NoBrush.
+
+  \see setPen
+*/
+void QCPPolarGraph::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+void QCPPolarGraph::setPeriodic(bool enabled)
+{
+  mPeriodic = enabled;
+}
+
+/*!
+  The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal
+  to the plottable's value axis. This function performs no checks to make sure this is the case.
+  The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the
+  y-axis (QCustomPlot::yAxis) as value axis.
+  
+  Normally, the key and value axes are set in the constructor of the plottable (or \ref
+  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
+
+  \see setValueAxis
+*/
+void QCPPolarGraph::setKeyAxis(QCPPolarAxisAngular *axis)
+{
+  mKeyAxis = axis;
+}
+
+/*!
+  The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is
+  orthogonal to the plottable's key axis. This function performs no checks to make sure this is the
+  case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and
+  the y-axis (QCustomPlot::yAxis) as value axis.
+
+  Normally, the key and value axes are set in the constructor of the plottable (or \ref
+  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
+  
+  \see setKeyAxis
+*/
+void QCPPolarGraph::setValueAxis(QCPPolarAxisRadial *axis)
+{
+  mValueAxis = axis;
+}
+
+/*!
+  Sets whether and to which granularity this plottable can be selected.
+
+  A selection can happen by clicking on the QCustomPlot surface (When \ref
+  QCustomPlot::setInteractions contains \ref QCP::iSelectPlottables), by dragging a selection rect
+  (When \ref QCustomPlot::setSelectionRectMode is \ref QCP::srmSelect), or programmatically by
+  calling \ref setSelection.
+  
+  \see setSelection, QCP::SelectionType
+*/
+void QCPPolarGraph::setSelectable(QCP::SelectionType selectable)
+{
+  if (mSelectable != selectable)
+  {
+    mSelectable = selectable;
+    QCPDataSelection oldSelection = mSelection;
+    mSelection.enforceType(mSelectable);
+    emit selectableChanged(mSelectable);
+    if (mSelection != oldSelection)
+    {
+      emit selectionChanged(selected());
+      emit selectionChanged(mSelection);
+    }
+  }
+}
+
+/*!
+  Sets which data ranges of this plottable are selected. Selected data ranges are drawn differently
+  (e.g. color) in the plot. This can be controlled via the selection decorator (see \ref
+  selectionDecorator).
+  
+  The entire selection mechanism for plottables is handled automatically when \ref
+  QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when
+  you wish to change the selection state programmatically.
+  
+  Using \ref setSelectable you can further specify for each plottable whether and to which
+  granularity it is selectable. If \a selection is not compatible with the current \ref
+  QCP::SelectionType set via \ref setSelectable, the resulting selection will be adjusted
+  accordingly (see \ref QCPDataSelection::enforceType).
+  
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+  
+  \see setSelectable, selectTest
+*/
+void QCPPolarGraph::setSelection(QCPDataSelection selection)
+{
+  selection.enforceType(mSelectable);
+  if (mSelection != selection)
+  {
+    mSelection = selection;
+    emit selectionChanged(selected());
+    emit selectionChanged(mSelection);
+  }
+}
+
+/*! \overload
+  
+  Replaces the current data container with the provided \a data container.
+  
+  Since a QSharedPointer is used, multiple QCPPolarGraphs may share the same data container safely.
+  Modifying the data in the container will then affect all graphs that share the container. Sharing
+  can be achieved by simply exchanging the data containers wrapped in shared pointers:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp QCPPolarGraph-datasharing-1
+  
+  If you do not wish to share containers, but create a copy from an existing container, rather use
+  the \ref QCPDataContainer<DataType>::set method on the graph's data container directly:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp QCPPolarGraph-datasharing-2
+  
+  \see addData
+*/
+void QCPPolarGraph::setData(QSharedPointer<QCPGraphDataContainer> data)
+{
+  mDataContainer = data;
+}
+
+/*! \overload
+  
+  Replaces the current data with the provided points in \a keys and \a values. The provided
+  vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+  
+  If you can guarantee that the passed data points are sorted by \a keys in ascending order, you
+  can set \a alreadySorted to true, to improve performance by saving a sorting run.
+  
+  \see addData
+*/
+void QCPPolarGraph::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
+{
+  mDataContainer->clear();
+  addData(keys, values, alreadySorted);
+}
+
+/*!
+  Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to
+  \ref lsNone and \ref setScatterStyle to the desired scatter style.
+  
+  \see setScatterStyle
+*/
+void QCPPolarGraph::setLineStyle(LineStyle ls)
+{
+  mLineStyle = ls;
+}
+
+/*!
+  Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points
+  are drawn (e.g. for line-only-plots with appropriate line style).
+  
+  \see QCPScatterStyle, setLineStyle
+*/
+void QCPPolarGraph::setScatterStyle(const QCPScatterStyle &style)
+{
+  mScatterStyle = style;
+}
+
+void QCPPolarGraph::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
+{
+  if (keys.size() != values.size())
+    qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
+  const int n = qMin(keys.size(), values.size());
+  QVector<QCPGraphData> tempData(n);
+  QVector<QCPGraphData>::iterator it = tempData.begin();
+  const QVector<QCPGraphData>::iterator itEnd = tempData.end();
+  int i = 0;
+  while (it != itEnd)
+  {
+    it->key = keys[i];
+    it->value = values[i];
+    ++it;
+    ++i;
+  }
+  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
+}
+
+void QCPPolarGraph::addData(double key, double value)
+{
+  mDataContainer->add(QCPGraphData(key, value));
+}
+
+/*!
+  Use this method to set an own QCPSelectionDecorator (subclass) instance. This allows you to
+  customize the visual representation of selected data ranges further than by using the default
+  QCPSelectionDecorator.
+  
+  The plottable takes ownership of the \a decorator.
+  
+  The currently set decorator can be accessed via \ref selectionDecorator.
+*/
+/*
+void QCPPolarGraph::setSelectionDecorator(QCPSelectionDecorator *decorator)
+{
+  if (decorator)
+  {
+    if (decorator->registerWithPlottable(this))
+    {
+      if (mSelectionDecorator) // delete old decorator if necessary
+        delete mSelectionDecorator;
+      mSelectionDecorator = decorator;
+    }
+  } else if (mSelectionDecorator) // just clear decorator
+  {
+    delete mSelectionDecorator;
+    mSelectionDecorator = 0;
+  }
+}
+*/
+
+void QCPPolarGraph::coordsToPixels(double key, double value, double &x, double &y) const
+{
+  if (mValueAxis)
+  {
+    const QPointF point = mValueAxis->coordToPixel(key, value);
+    x = point.x();
+    y = point.y();
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
+  }
+}
+
+const QPointF QCPPolarGraph::coordsToPixels(double key, double value) const
+{
+  if (mValueAxis)
+  {
+    return mValueAxis->coordToPixel(key, value);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
+    return QPointF();
+  }
+}
+
+void QCPPolarGraph::pixelsToCoords(double x, double y, double &key, double &value) const
+{
+  if (mValueAxis)
+  {
+    mValueAxis->pixelToCoord(QPointF(x, y), key, value);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
+  }
+}
+
+void QCPPolarGraph::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
+{
+  if (mValueAxis)
+  {
+    mValueAxis->pixelToCoord(pixelPos, key, value);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "invalid key or value axis";
+  }
+}
+
+void QCPPolarGraph::rescaleAxes(bool onlyEnlarge) const
+{
+  rescaleKeyAxis(onlyEnlarge);
+  rescaleValueAxis(onlyEnlarge);
+}
+
+void QCPPolarGraph::rescaleKeyAxis(bool onlyEnlarge) const
+{
+  QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
+  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
+  
+  bool foundRange;
+  QCPRange newRange = getKeyRange(foundRange, QCP::sdBoth);
+  if (foundRange)
+  {
+    if (onlyEnlarge)
+      newRange.expand(keyAxis->range());
+    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
+    {
+      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
+      newRange.lower = center-keyAxis->range().size()/2.0;
+      newRange.upper = center+keyAxis->range().size()/2.0;
+    }
+    keyAxis->setRange(newRange);
+  }
+}
+
+void QCPPolarGraph::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const
+{
+  QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
+  QCPPolarAxisRadial *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  
+  QCP::SignDomain signDomain = QCP::sdBoth;
+  if (valueAxis->scaleType() == QCPPolarAxisRadial::stLogarithmic)
+    signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
+  
+  bool foundRange;
+  QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange());
+  if (foundRange)
+  {
+    if (onlyEnlarge)
+      newRange.expand(valueAxis->range());
+    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
+    {
+      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
+      if (valueAxis->scaleType() == QCPPolarAxisRadial::stLinear)
+      {
+        newRange.lower = center-valueAxis->range().size()/2.0;
+        newRange.upper = center+valueAxis->range().size()/2.0;
+      } else // scaleType() == stLogarithmic
+      {
+        newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
+        newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
+      }
+    }
+    valueAxis->setRange(newRange);
+  }
+}
+
+bool QCPPolarGraph::addToLegend(QCPLegend *legend)
+{
+  if (!legend)
+  {
+    qDebug() << Q_FUNC_INFO << "passed legend is null";
+    return false;
+  }
+  if (legend->parentPlot() != mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "passed legend isn't in the same QCustomPlot as this plottable";
+    return false;
+  }
+  
+  //if (!legend->hasItemWithPlottable(this)) // TODO
+  //{
+    legend->addItem(new QCPPolarLegendItem(legend, this));
+    return true;
+  //} else
+  //  return false;
+}
+
+bool QCPPolarGraph::addToLegend()
+{
+  if (!mParentPlot || !mParentPlot->legend)
+    return false;
+  else
+    return addToLegend(mParentPlot->legend);
+}
+
+bool QCPPolarGraph::removeFromLegend(QCPLegend *legend) const
+{
+  if (!legend)
+  {
+    qDebug() << Q_FUNC_INFO << "passed legend is null";
+    return false;
+  }
+  
+  
+  QCPPolarLegendItem *removableItem = 0;
+  for (int i=0; i<legend->itemCount(); ++i) // TODO: reduce this to code in QCPAbstractPlottable::removeFromLegend once unified
+  {
+    if (QCPPolarLegendItem *pli = qobject_cast<QCPPolarLegendItem*>(legend->item(i)))
+    {
+      if (pli->polarGraph() == this)
+      {
+        removableItem = pli;
+        break;
+      }
+    }
+  }
+  
+  if (removableItem)
+    return legend->removeItem(removableItem);
+  else
+    return false;
+}
+
+bool QCPPolarGraph::removeFromLegend() const
+{
+  if (!mParentPlot || !mParentPlot->legend)
+    return false;
+  else
+    return removeFromLegend(mParentPlot->legend);
+}
+
+double QCPPolarGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
+    return -1;
+  if (!mKeyAxis || !mValueAxis)
+    return -1;
+  
+  if (mKeyAxis->rect().contains(pos.toPoint()))
+  {
+    QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
+    double result = pointDistance(pos, closestDataPoint);
+    if (details)
+    {
+      int pointIndex = closestDataPoint-mDataContainer->constBegin();
+      details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
+    }
+    return result;
+  } else
+    return -1;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPPolarGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
+{
+  return mDataContainer->keyRange(foundRange, inSignDomain);
+}
+
+/* inherits documentation from base class */
+QCPRange QCPPolarGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
+{
+  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
+}
+
+/* inherits documentation from base class */
+QRect QCPPolarGraph::clipRect() const
+{
+  if (mKeyAxis)
+    return mKeyAxis.data()->rect();
+  else
+    return QRect();
+}
+
+void QCPPolarGraph::draw(QCPPainter *painter)
+{
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
+  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
+  
+  painter->setClipRegion(mKeyAxis->exactClipRegion());
+  
+  QVector<QPointF> lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while iterating over segments
+  
+  // loop over and draw segments of unselected/selected data:
+  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
+  getDataSegments(selectedSegments, unselectedSegments);
+  allSegments << unselectedSegments << selectedSegments;
+  for (int i=0; i<allSegments.size(); ++i)
+  {
+    bool isSelectedSegment = i >= unselectedSegments.size();
+    // get line pixel points appropriate to line style:
+    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)
+    getLines(&lines, lineDataRange);
+    
+    // check data validity if flag set:
+#ifdef QCUSTOMPLOT_CHECK_DATA
+    QCPGraphDataContainer::const_iterator it;
+    for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
+    {
+      if (QCP::isInvalidData(it->key, it->value))
+        qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
+    }
+#endif
+    
+    // draw fill of graph:
+    //if (isSelectedSegment && mSelectionDecorator)
+    //  mSelectionDecorator->applyBrush(painter);
+    //else
+      painter->setBrush(mBrush);
+    painter->setPen(Qt::NoPen);
+    drawFill(painter, &lines);
+    
+    
+    // draw line:
+    if (mLineStyle != lsNone)
+    {
+      //if (isSelectedSegment && mSelectionDecorator)
+      //  mSelectionDecorator->applyPen(painter);
+      //else
+        painter->setPen(mPen);
+      painter->setBrush(Qt::NoBrush);
+      drawLinePlot(painter, lines);
+    }
+    
+    // draw scatters:
+    
+    QCPScatterStyle finalScatterStyle = mScatterStyle;
+    //if (isSelectedSegment && mSelectionDecorator)
+    //  finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle);
+    if (!finalScatterStyle.isNone())
+    {
+      getScatters(&scatters, allSegments.at(i));
+      drawScatterPlot(painter, scatters, finalScatterStyle);
+    }
+  }
+  
+  // draw other selection decoration that isn't just line/scatter pens and brushes:
+  //if (mSelectionDecorator)
+  //  mSelectionDecorator->drawDecoration(painter, selection());
+}
+
+QCP::Interaction QCPPolarGraph::selectionCategory() const
+{
+  return QCP::iSelectPlottables;
+}
+
+void QCPPolarGraph::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
+}
+
+/* inherits documentation from base class */
+void QCPPolarGraph::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
+{
+  Q_UNUSED(event)
+  
+  if (mSelectable != QCP::stNone)
+  {
+    QCPDataSelection newSelection = details.value<QCPDataSelection>();
+    QCPDataSelection selectionBefore = mSelection;
+    if (additive)
+    {
+      if (mSelectable == QCP::stWhole) // in whole selection mode, we toggle to no selection even if currently unselected point was hit
+      {
+        if (selected())
+          setSelection(QCPDataSelection());
+        else
+          setSelection(newSelection);
+      } else // in all other selection modes we toggle selections of homogeneously selected/unselected segments
+      {
+        if (mSelection.contains(newSelection)) // if entire newSelection is already selected, toggle selection
+          setSelection(mSelection-newSelection);
+        else
+          setSelection(mSelection+newSelection);
+      }
+    } else
+      setSelection(newSelection);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelection != selectionBefore;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPPolarGraph::deselectEvent(bool *selectionStateChanged)
+{
+  if (mSelectable != QCP::stNone)
+  {
+    QCPDataSelection selectionBefore = mSelection;
+    setSelection(QCPDataSelection());
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelection != selectionBefore;
+  }
+}
+
+/*!  \internal
+  
+  Draws lines between the points in \a lines, given in pixel coordinates.
+  
+  \see drawScatterPlot, drawImpulsePlot, QCPAbstractPlottable1D::drawPolyline
+*/
+void QCPPolarGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
+{
+  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
+  {
+    applyDefaultAntialiasingHint(painter);
+    drawPolyline(painter, lines);
+  }
+}
+
+/*! \internal
+  
+  Draws the fill of the graph using the specified \a painter, with the currently set brush.
+  
+  Depending on whether a normal fill or a channel fill (\ref setChannelFillGraph) is needed, \ref
+  getFillPolygon or \ref getChannelFillPolygon are used to find the according fill polygons.
+  
+  In order to handle NaN Data points correctly (the fill needs to be split into disjoint areas),
+  this method first determines a list of non-NaN segments with \ref getNonNanSegments, on which to
+  operate. In the channel fill case, \ref getOverlappingSegments is used to consolidate the non-NaN
+  segments of the two involved graphs, before passing the overlapping pairs to \ref
+  getChannelFillPolygon.
+  
+  Pass the points of this graph's line as \a lines, in pixel coordinates.
+
+  \see drawLinePlot, drawImpulsePlot, drawScatterPlot
+*/
+void QCPPolarGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const
+{
+  applyFillAntialiasingHint(painter);
+  if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0)
+    painter->drawPolygon(QPolygonF(*lines));
+}
+
+/*! \internal
+
+  Draws scatter symbols at every point passed in \a scatters, given in pixel coordinates. The
+  scatters will be drawn with \a painter and have the appearance as specified in \a style.
+
+  \see drawLinePlot, drawImpulsePlot
+*/
+void QCPPolarGraph::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const
+{
+  applyScattersAntialiasingHint(painter);
+  style.applyTo(painter, mPen);
+  for (int i=0; i<scatters.size(); ++i)
+    style.drawShape(painter, scatters.at(i).x(), scatters.at(i).y());
+}
+
+void QCPPolarGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
+{
+  // draw fill:
+  if (mBrush.style() != Qt::NoBrush)
+  {
+    applyFillAntialiasingHint(painter);
+    painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
+  }
+  // draw line vertically centered:
+  if (mLineStyle != lsNone)
+  {
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mPen);
+    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
+  }
+  // draw scatter symbol:
+  if (!mScatterStyle.isNone())
+  {
+    applyScattersAntialiasingHint(painter);
+    // scale scatter pixmap if it's too large to fit in legend icon rect:
+    if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
+    {
+      QCPScatterStyle scaledStyle(mScatterStyle);
+      scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
+      scaledStyle.applyTo(painter, mPen);
+      scaledStyle.drawShape(painter, QRectF(rect).center());
+    } else
+    {
+      mScatterStyle.applyTo(painter, mPen);
+      mScatterStyle.drawShape(painter, QRectF(rect).center());
+    }
+  }
+}
+
+void QCPPolarGraph::applyFillAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
+}
+
+void QCPPolarGraph::applyScattersAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
+}
+
+double QCPPolarGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
+{
+  closestData = mDataContainer->constEnd();
+  if (mDataContainer->isEmpty())
+    return -1.0;
+  if (mLineStyle == lsNone && mScatterStyle.isNone())
+    return -1.0;
+  
+  // calculate minimum distances to graph data points and find closestData iterator:
+  double minDistSqr = (std::numeric_limits<double>::max)();
+  // determine which key range comes into question, taking selection tolerance around pos into account:
+  double posKeyMin, posKeyMax, dummy;
+  pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);
+  pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy);
+  if (posKeyMin > posKeyMax)
+    qSwap(posKeyMin, posKeyMax);
+  // iterate over found data points and then choose the one with the shortest distance to pos:
+  QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true);
+  QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true);
+  for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it)
+  {
+    const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
+    if (currentDistSqr < minDistSqr)
+    {
+      minDistSqr = currentDistSqr;
+      closestData = it;
+    }
+  }
+    
+  // calculate distance to graph line if there is one (if so, will probably be smaller than distance to closest data point):
+  if (mLineStyle != lsNone)
+  {
+    // line displayed, calculate distance to line segments:
+    QVector<QPointF> lineData;
+    getLines(&lineData, QCPDataRange(0, dataCount()));
+    QCPVector2D p(pixelPoint);
+    for (int i=0; i<lineData.size()-1; ++i)
+    {
+      const double currentDistSqr = p.distanceSquaredToLine(lineData.at(i), lineData.at(i+1));
+      if (currentDistSqr < minDistSqr)
+        minDistSqr = currentDistSqr;
+    }
+  }
+  
+  return qSqrt(minDistSqr);
+}
+
+int QCPPolarGraph::dataCount() const
+{
+  return mDataContainer->size();
+}
+
+void QCPPolarGraph::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const
+{
+  selectedSegments.clear();
+  unselectedSegments.clear();
+  if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty
+  {
+    if (selected())
+      selectedSegments << QCPDataRange(0, dataCount());
+    else
+      unselectedSegments << QCPDataRange(0, dataCount());
+  } else
+  {
+    QCPDataSelection sel(selection());
+    sel.simplify();
+    selectedSegments = sel.dataRanges();
+    unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges();
+  }
+}
+
+void QCPPolarGraph::drawPolyline(QCPPainter *painter, const QVector<QPointF> &lineData) const
 {
+  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
+  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
+      painter->pen().style() == Qt::SolidLine &&
+      !painter->modes().testFlag(QCPPainter::pmVectorized) &&
+      !painter->modes().testFlag(QCPPainter::pmNoCaching))
+  {
+    int i = 0;
+    bool lastIsNan = false;
+    const int lineDataSize = lineData.size();
+    while (i < lineDataSize && (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()))) // make sure first point is not NaN
+      ++i;
+    ++i; // because drawing works in 1 point retrospect
+    while (i < lineDataSize)
+    {
+      if (!qIsNaN(lineData.at(i).y()) && !qIsNaN(lineData.at(i).x())) // NaNs create a gap in the line
+      {
+        if (!lastIsNan)
+          painter->drawLine(lineData.at(i-1), lineData.at(i));
+        else
+          lastIsNan = false;
+      } else
+        lastIsNan = true;
+      ++i;
+    }
+  } else
+  {
+    int segmentStart = 0;
+    int i = 0;
+    const int lineDataSize = lineData.size();
+    while (i < lineDataSize)
+    {
+      if (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()) || qIsInf(lineData.at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block
+      {
+        painter->drawPolyline(lineData.constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
+        segmentStart = i+1;
+      }
+      ++i;
+    }
+    // draw last segment:
+    painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart);
+  }
 }
 
-/*!
-  Sets the pen that will be used to draw the bracket.
-  
-  Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the
-  stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use
-  \ref setLength, which has a similar effect.
-  
-  \see setSelectedPen
-*/
-void QCPItemBracket::setPen(const QPen &pen)
+void QCPPolarGraph::getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
 {
-  mPen = pen;
+  if (rangeRestriction.isEmpty())
+  {
+    end = mDataContainer->constEnd();
+    begin = end;
+  } else
+  {
+    QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
+    QCPPolarAxisRadial *valueAxis = mValueAxis.data();
+    if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+    // get visible data range:
+    if (mPeriodic)
+    {
+      begin = mDataContainer->constBegin();
+      end = mDataContainer->constEnd();
+    } else
+    {
+      begin = mDataContainer->findBegin(keyAxis->range().lower);
+      end = mDataContainer->findEnd(keyAxis->range().upper);
+    }
+    // limit lower/upperEnd to rangeRestriction:
+    mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction); // this also ensures rangeRestriction outside data bounds doesn't break anything
+  }
 }
 
-/*!
-  Sets the pen that will be used to draw the bracket when selected
-  
-  \see setPen, setSelected
-*/
-void QCPItemBracket::setSelectedPen(const QPen &pen)
-{
-  mSelectedPen = pen;
-}
+/*! \internal
 
-/*!
-  Sets the \a length in pixels how far the bracket extends in the direction towards the embraced
-  span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)
-  
-  \image html QCPItemBracket-length.png
-  <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
-  bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
-*/
-void QCPItemBracket::setLength(double length)
-{
-  mLength = length;
-}
+  This method retrieves an optimized set of data points via \ref getOptimizedLineData, an branches
+  out to the line style specific functions such as \ref dataToLines, \ref dataToStepLeftLines, etc.
+  according to the line style of the graph.
 
-/*!
-  Sets the style of the bracket, i.e. the shape/visual appearance.
-  
-  \see setPen
+  \a lines will be filled with points in pixel coordinates, that can be drawn with the according
+  draw functions like \ref drawLinePlot and \ref drawImpulsePlot. The points returned in \a lines
+  aren't necessarily the original data points. For example, step line styles require additional
+  points to form the steps when drawn. If the line style of the graph is \ref lsNone, the \a
+  lines vector will be empty.
+
+  \a dataRange specifies the beginning and ending data indices that will be taken into account for
+  conversion. In this function, the specified range may exceed the total data bounds without harm:
+  a correspondingly trimmed data range will be used. This takes the burden off the user of this
+  function to check for valid indices in \a dataRange, e.g. when extending ranges coming from \ref
+  getDataSegments.
+
+  \see getScatters
 */
-void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
+void QCPPolarGraph::getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const
 {
-  mStyle = style;
+  if (!lines) return;
+  QCPGraphDataContainer::const_iterator begin, end;
+  getVisibleDataBounds(begin, end, dataRange);
+  if (begin == end)
+  {
+    lines->clear();
+    return;
+  }
+  
+  QVector<QCPGraphData> lineData;
+  if (mLineStyle != lsNone)
+    getOptimizedLineData(&lineData, begin, end);
+
+  switch (mLineStyle)
+  {
+    case lsNone: lines->clear(); break;
+    case lsLine: *lines = dataToLines(lineData); break;
+  }
 }
 
-/* inherits documentation from base class */
-double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+void QCPPolarGraph::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const
 {
-  Q_UNUSED(details)
-  if (onlySelectable && !mSelectable)
-    return -1;
+  QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
+  QCPPolarAxisRadial *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
   
-  QCPVector2D p(pos);
-  QCPVector2D leftVec(left->pixelPosition());
-  QCPVector2D rightVec(right->pixelPosition());
-  if (leftVec.toPoint() == rightVec.toPoint())
-    return -1;
+  if (!scatters) return;
+  QCPGraphDataContainer::const_iterator begin, end;
+  getVisibleDataBounds(begin, end, dataRange);
+  if (begin == end)
+  {
+    scatters->clear();
+    return;
+  }
   
-  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
-  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
-  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
+  QVector<QCPGraphData> data;
+  getOptimizedScatterData(&data, begin, end);
   
-  switch (mStyle)
+  scatters->resize(data.size());
+  for (int i=0; i<data.size(); ++i)
   {
-    case QCPItemBracket::bsSquare:
-    case QCPItemBracket::bsRound:
-    {
-      double a = p.distanceSquaredToLine(centerVec-widthVec, centerVec+widthVec);
-      double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec, centerVec-widthVec);
-      double c = p.distanceSquaredToLine(centerVec+widthVec+lengthVec, centerVec+widthVec);
-      return qSqrt(qMin(qMin(a, b), c));
-    }
-    case QCPItemBracket::bsCurly:
-    case QCPItemBracket::bsCalligraphic:
-    {
-      double a = p.distanceSquaredToLine(centerVec-widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
-      double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec*0.7, centerVec-widthVec*0.75+lengthVec*0.15);
-      double c = p.distanceSquaredToLine(centerVec+widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
-      double d = p.distanceSquaredToLine(centerVec+widthVec+lengthVec*0.7, centerVec+widthVec*0.75+lengthVec*0.15);
-      return qSqrt(qMin(qMin(a, b), qMin(c, d)));
-    }
+    if (!qIsNaN(data.at(i).value))
+      (*scatters)[i] = valueAxis->coordToPixel(data.at(i).key, data.at(i).value);
   }
-  return -1;
 }
 
-/* inherits documentation from base class */
-void QCPItemBracket::draw(QCPPainter *painter)
+void QCPPolarGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
 {
-  QCPVector2D leftVec(left->pixelPosition());
-  QCPVector2D rightVec(right->pixelPosition());
-  if (leftVec.toPoint() == rightVec.toPoint())
-    return;
+  lineData->clear();
   
-  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
-  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
-  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
-
-  QPolygon boundingPoly;
-  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
-               << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
-  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
-  if (clip.intersects(boundingPoly.boundingRect()))
+  // TODO: fix for log axes and thick line style
+  
+  const QCPRange range = mValueAxis->range();
+  bool reversed = mValueAxis->rangeReversed();
+  const double clipMargin = range.size()*0.05; // extra distance from visible circle, so optimized outside lines can cover more angle before having to place a dummy point to prevent tangents
+  const double upperClipValue = range.upper + (reversed ? 0 : range.size()*0.05+clipMargin); // clip slightly outside of actual range to avoid line thicknesses to peek into visible circle
+  const double lowerClipValue = range.lower - (reversed ? range.size()*0.05+clipMargin : 0); // clip slightly outside of actual range to avoid line thicknesses to peek into visible circle
+  const double maxKeySkip = qAsin(qSqrt(clipMargin*(clipMargin+2*range.size()))/(range.size()+clipMargin))/M_PI*mKeyAxis->range().size(); // the maximum angle between two points on outer circle (r=clipValue+clipMargin) before connecting line becomes tangent to inner circle (r=clipValue)
+  double skipBegin = 0;
+  bool belowRange = false;
+  bool aboveRange = false;
+  QCPGraphDataContainer::const_iterator it = begin;
+  while (it != end)
   {
-    painter->setPen(mainPen());
-    switch (mStyle)
+    if (it->value < lowerClipValue)
     {
-      case bsSquare:
+      if (aboveRange) // jumped directly from above to below visible range, draw previous point so entry angle is correct
       {
-        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
-        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
-        painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
-        break;
+        aboveRange = false;
+        if (!reversed) // TODO: with inner radius, we'll need else case here with projected border point
+          lineData->append(*(it-1));
       }
-      case bsRound:
+      if (!belowRange)
       {
-        painter->setBrush(Qt::NoBrush);
-        QPainterPath path;
-        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
-        path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
-        path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
-        painter->drawPath(path);
-        break;
+        skipBegin = it->key;
+        lineData->append(QCPGraphData(it->key, lowerClipValue));
+        belowRange = true;
       }
-      case bsCurly:
+      if (it->key-skipBegin > maxKeySkip) // add dummy point if we're exceeding the maximum skippable angle (to prevent unintentional intersections with visible circle)
       {
-        painter->setBrush(Qt::NoBrush);
-        QPainterPath path;
-        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
-        path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF());
-        path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
-        painter->drawPath(path);
-        break;
+        skipBegin += maxKeySkip;
+        lineData->append(QCPGraphData(skipBegin, lowerClipValue));
       }
-      case bsCalligraphic:
+    } else if (it->value > upperClipValue)
+    {
+      if (belowRange) // jumped directly from below to above visible range, draw previous point so entry angle is correct (if lower means outer, so if reversed axis)
       {
-        painter->setPen(Qt::NoPen);
-        painter->setBrush(QBrush(mainPen().color()));
-        QPainterPath path;
-        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
-        
-        path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
-        path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
-        
-        path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
-        path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
-        
-        painter->drawPath(path);
-        break;
+        belowRange = false;
+        if (reversed)
+          lineData->append(*(it-1));
       }
+      if (!aboveRange)
+      {
+        skipBegin = it->key;
+        lineData->append(QCPGraphData(it->key, upperClipValue));
+        aboveRange = true;
+      }
+      if (it->key-skipBegin > maxKeySkip) // add dummy point if we're exceeding the maximum skippable angle (to prevent unintentional intersections with visible circle)
+      {
+        skipBegin += maxKeySkip;
+        lineData->append(QCPGraphData(skipBegin, upperClipValue));
+      }
+    } else // value within bounds where we don't optimize away points
+    {
+      if (aboveRange)
+      {
+        aboveRange = false;
+        if (!reversed)
+          lineData->append(*(it-1)); // just entered from above, draw previous point so entry angle is correct (if above means outer, so if not reversed axis)
+      }
+      if (belowRange)
+      {
+        belowRange = false;
+        if (reversed)
+          lineData->append(*(it-1)); // just entered from below, draw previous point so entry angle is correct (if below means outer, so if reversed axis)
+      }
+      lineData->append(*it); // inside visible circle, add point normally
     }
+    ++it;
+  }
+  // to make fill not erratic, add last point normally if it was outside visible circle:
+  if (aboveRange)
+  {
+    aboveRange = false;
+    if (!reversed)
+      lineData->append(*(it-1)); // just entered from above, draw previous point so entry angle is correct (if above means outer, so if not reversed axis)
+  }
+  if (belowRange)
+  {
+    belowRange = false;
+    if (reversed)
+      lineData->append(*(it-1)); // just entered from below, draw previous point so entry angle is correct (if below means outer, so if reversed axis)
   }
 }
 
-/* inherits documentation from base class */
-QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const
+void QCPPolarGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const
 {
-  QCPVector2D leftVec(left->pixelPosition());
-  QCPVector2D rightVec(right->pixelPosition());
-  if (leftVec.toPoint() == rightVec.toPoint())
-    return leftVec.toPointF();
-  
-  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
-  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
-  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
+  scatterData->clear();
   
-  switch (anchorId)
+  const QCPRange range = mValueAxis->range();
+  bool reversed = mValueAxis->rangeReversed();
+  const double clipMargin = range.size()*0.05;
+  const double upperClipValue = range.upper + (reversed ? 0 : clipMargin); // clip slightly outside of actual range to avoid scatter size to peek into visible circle
+  const double lowerClipValue = range.lower - (reversed ? clipMargin : 0); // clip slightly outside of actual range to avoid scatter size to peek into visible circle
+  QCPGraphDataContainer::const_iterator it = begin;
+  while (it != end)
   {
-    case aiCenter:
-      return centerVec.toPointF();
+    if (it->value > lowerClipValue && it->value < upperClipValue)
+      scatterData->append(*it);
+    ++it;
   }
-  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
-  return QPointF();
 }
 
 /*! \internal
 
-  Returns the pen that should be used for drawing lines. Returns mPen when the
-  item is not selected and mSelectedPen when it is.
+  Takes raw data points in plot coordinates as \a data, and returns a vector containing pixel
+  coordinate points which are suitable for drawing the line style \ref lsLine.
+  
+  The source of \a data is usually \ref getOptimizedLineData, and this method is called in \a
+  getLines if the line style is set accordingly.
+
+  \see dataToStepLeftLines, dataToStepRightLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot
 */
-QPen QCPItemBracket::mainPen() const
+QVector<QPointF> QCPPolarGraph::dataToLines(const QVector<QCPGraphData> &data) const
 {
-    return mSelected ? mSelectedPen : mPen;
+  QVector<QPointF> result;
+  QCPPolarAxisAngular *keyAxis = mKeyAxis.data();
+  QCPPolarAxisRadial *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
+
+  // transform data points to pixels:
+  result.resize(data.size());
+  for (int i=0; i<data.size(); ++i)
+    result[i] = mValueAxis->coordToPixel(data.at(i).key, data.at(i).value);
+  return result;
 }
-/* end of 'src/items/item-bracket.cpp' */
+/* end of 'src/polar/polargraph.cpp' */
 
 
diff --git a/Modules/Graph/qcustomplot.h b/Core/QCustomPlot/QCustomPlot.h
similarity index 82%
rename from Modules/Graph/qcustomplot.h
rename to Core/QCustomPlot/QCustomPlot.h
index 278fbf82cc5846057886bb01e697458070682c9b..8f0f78b1793881584e669cc10acc583e3b44aead 100644
--- a/Modules/Graph/qcustomplot.h
+++ b/Core/QCustomPlot/QCustomPlot.h
@@ -1,7 +1,7 @@
 /***************************************************************************
 **                                                                        **
 **  QCustomPlot, an easy to use, modern plotting widget for Qt            **
-**  Copyright (C) 2011-2017 Emanuel Eichhammer                            **
+**  Copyright (C) 2011-2021 Emanuel Eichhammer                            **
 **                                                                        **
 **  This program is free software: you can redistribute it and/or modify  **
 **  it under the terms of the GNU General Public License as published by  **
@@ -19,8 +19,8 @@
 ****************************************************************************
 **           Author: Emanuel Eichhammer                                   **
 **  Website/Contact: http://www.qcustomplot.com/                          **
-**             Date: 04.09.17                                             **
-**          Version: 2.0.0                                                **
+**             Date: 29.03.21                                             **
+**          Version: 2.1.0                                                **
 ****************************************************************************/
 
 #ifndef QCUSTOMPLOT_H
@@ -52,6 +52,7 @@
 #include <QtCore/QSharedPointer>
 #include <QtCore/QTimer>
 #include <QtGui/QPainter>
+#include <QtGui/QPainterPath>
 #include <QtGui/QPaintEvent>
 #include <QtGui/QMouseEvent>
 #include <QtGui/QWheelEvent>
@@ -70,7 +71,12 @@
 #include <algorithm>
 #ifdef QCP_OPENGL_FBO
 #  include <QtGui/QOpenGLContext>
-#  include <QtGui/QOpenGLFramebufferObject>
+#  if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#    include <QtGui/QOpenGLFramebufferObject>
+#  else
+#    include <QOpenGLFramebufferObject>
+#    include <QOpenGLPaintDevice>
+#  endif
 #  ifdef QCP_OPENGL_OFFSCREENSURFACE
 #    include <QtGui/QOffscreenSurface>
 #  else
@@ -90,6 +96,12 @@
 #  include <QtWidgets/QWidget>
 #  include <QtPrintSupport/QtPrintSupport>
 #endif
+#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
+#  include <QtCore/QElapsedTimer>
+#endif
+# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+#  include <QtCore/QTimeZone>
+#endif
 
 class QCPPainter;
 class QCustomPlot;
@@ -111,12 +123,21 @@ class QCPSelectionRect;
 class QCPColorMap;
 class QCPColorScale;
 class QCPBars;
+class QCPPolarAxisRadial;
+class QCPPolarAxisAngular;
+class QCPPolarGrid;
+class QCPPolarGraph;
 
-/* including file 'src/global.h', size 16225                                 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/global.h'            */
+/* modified 2021-03-29T02:30:44, size 16981 */
+
+#define QCUSTOMPLOT_VERSION_STR "2.1.0"
+#define QCUSTOMPLOT_VERSION 0x020100
 
 // decl definitions for shared library compilation/usage:
-#if defined(QCUSTOMPLOT_COMPILE_LIBRARY)
+#if defined(QT_STATIC_BUILD)
+#  define QCP_LIB_DECL
+#elif defined(QCUSTOMPLOT_COMPILE_LIBRARY)
 #  define QCP_LIB_DECL Q_DECL_EXPORT
 #elif defined(QCUSTOMPLOT_USE_LIBRARY)
 #  define QCP_LIB_DECL Q_DECL_IMPORT
@@ -248,7 +269,8 @@ Q_DECLARE_FLAGS(PlottingHints, PlottingHint)
   
   \see QCustomPlot::setInteractions
 */
-enum Interaction { iRangeDrag         = 0x001 ///< <tt>0x001</tt> Axis ranges are draggable (see \ref QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeDragAxes)
+enum Interaction { iNone              = 0x000 ///< <tt>0x000</tt> None of the interactions are possible
+                   ,iRangeDrag        = 0x001 ///< <tt>0x001</tt> Axis ranges are draggable (see \ref QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeDragAxes)
                    ,iRangeZoom        = 0x002 ///< <tt>0x002</tt> Axis ranges are zoomable with the mouse wheel (see \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeZoomAxes)
                    ,iMultiSelect      = 0x004 ///< <tt>0x004</tt> The user can select multiple objects by holding the modifier set by \ref QCustomPlot::setMultiSelectModifier while clicking
                    ,iSelectPlottables = 0x008 ///< <tt>0x008</tt> Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable)
@@ -256,6 +278,7 @@ enum Interaction { iRangeDrag         = 0x001 ///< <tt>0x001</tt> Axis ranges ar
                    ,iSelectLegend     = 0x020 ///< <tt>0x020</tt> Legends are selectable (or their child items, see QCPLegend::setSelectableParts)
                    ,iSelectItems      = 0x040 ///< <tt>0x040</tt> Items are selectable (Rectangles, Arrows, Textitems, etc. see \ref QCPAbstractItem)
                    ,iSelectOther      = 0x080 ///< <tt>0x080</tt> All other objects are selectable (e.g. your own derived layerables, other layout elements,...)
+                   ,iSelectPlottablesBeyondAxisRect = 0x100 ///< <tt>0x100</tt> When performing plottable selection/hit tests, this flag extends the sensitive area beyond the axis rect
                  };
 Q_DECLARE_FLAGS(Interactions, Interaction)
 
@@ -376,8 +399,8 @@ Q_DECLARE_METATYPE(QCP::SelectionType)
 /* end of 'src/global.h' */
 
 
-/* including file 'src/vector2d.h', size 4928                                */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/vector2d.h'         */
+/* modified 2021-03-29T02:30:44, size 4988 */
 
 class QCP_LIB_DECL QCPVector2D
 {
@@ -400,7 +423,8 @@ public:
   // non-virtual methods:
   double length() const { return qSqrt(mX*mX+mY*mY); }
   double lengthSquared() const { return mX*mX+mY*mY; }
-  QPoint toPoint() const { return QPoint(mX, mY); }
+  double angle() const { return qAtan2(mY, mX); }
+  QPoint toPoint() const { return QPoint(int(mX), int(mY)); }
   QPointF toPointF() const { return QPointF(mX, mY); }
   
   bool isNull() const { return qIsNull(mX) && qIsNull(mY); }
@@ -450,8 +474,8 @@ inline QDebug operator<< (QDebug d, const QCPVector2D &vec)
 /* end of 'src/vector2d.h' */
 
 
-/* including file 'src/painter.h', size 4035                                 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/painter.h'          */
+/* modified 2021-03-29T02:30:44, size 4035 */
 
 class QCP_LIB_DECL QCPPainter : public QPainter
 {
@@ -509,8 +533,8 @@ Q_DECLARE_METATYPE(QCPPainter::PainterMode)
 /* end of 'src/painter.h' */
 
 
-/* including file 'src/paintbuffer.h', size 4958                             */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/paintbuffer.h'      */
+/* modified 2021-03-29T02:30:44, size 5006 */
 
 class QCP_LIB_DECL QCPAbstractPaintBuffer
 {
@@ -551,7 +575,7 @@ class QCP_LIB_DECL QCPPaintBufferPixmap : public QCPAbstractPaintBuffer
 {
 public:
   explicit QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio);
-  virtual ~QCPPaintBufferPixmap();
+  virtual ~QCPPaintBufferPixmap() Q_DECL_OVERRIDE;
   
   // reimplemented virtual methods:
   virtual QCPPainter *startPainting() Q_DECL_OVERRIDE;
@@ -572,7 +596,7 @@ class QCP_LIB_DECL QCPPaintBufferGlPbuffer : public QCPAbstractPaintBuffer
 {
 public:
   explicit QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples);
-  virtual ~QCPPaintBufferGlPbuffer();
+  virtual ~QCPPaintBufferGlPbuffer() Q_DECL_OVERRIDE;
   
   // reimplemented virtual methods:
   virtual QCPPainter *startPainting() Q_DECL_OVERRIDE;
@@ -595,7 +619,7 @@ class QCP_LIB_DECL QCPPaintBufferGlFbo : public QCPAbstractPaintBuffer
 {
 public:
   explicit QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer<QOpenGLContext> glContext, QWeakPointer<QOpenGLPaintDevice> glPaintDevice);
-  virtual ~QCPPaintBufferGlFbo();
+  virtual ~QCPPaintBufferGlFbo() Q_DECL_OVERRIDE;
   
   // reimplemented virtual methods:
   virtual QCPPainter *startPainting() Q_DECL_OVERRIDE;
@@ -617,8 +641,8 @@ protected:
 /* end of 'src/paintbuffer.h' */
 
 
-/* including file 'src/layer.h', size 6885                                   */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/layer.h'            */
+/* modified 2021-03-29T02:30:44, size 7038 */
 
 class QCP_LIB_DECL QCPLayer : public QObject
 {
@@ -700,7 +724,7 @@ class QCP_LIB_DECL QCPLayerable : public QObject
   Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased)
   /// \endcond
 public:
-  QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0);
+  QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=nullptr);
   virtual ~QCPLayerable();
   
   // getters:
@@ -717,7 +741,7 @@ public:
   void setAntialiased(bool enabled);
   
   // introduced virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const;
 
   // non-property methods:
   bool realVisibility() const;
@@ -766,8 +790,8 @@ private:
 /* end of 'src/layer.h' */
 
 
-/* including file 'src/axis/range.h', size 5280                              */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/range.h'       */
+/* modified 2021-03-29T02:30:44, size 5280 */
 
 class QCP_LIB_DECL QCPRange
 {
@@ -884,8 +908,8 @@ inline const QCPRange operator/(const QCPRange& range, double value)
 /* end of 'src/axis/range.h' */
 
 
-/* including file 'src/selection.h', size 8579                               */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/selection.h'        */
+/* modified 2021-03-29T02:30:44, size 8569 */
 
 class QCP_LIB_DECL QCPDataRange
 {
@@ -1062,8 +1086,8 @@ inline const QCPDataSelection operator-(const QCPDataRange& a, const QCPDataRang
 */
 inline QDebug operator<< (QDebug d, const QCPDataRange &dataRange)
 {
-    d.nospace() << "[" << dataRange.begin() << ".." << dataRange.end()-1 << "]";
-    return d.space();
+  d.nospace() << "QCPDataRange(" << dataRange.begin() << ", " << dataRange.end() << ")";
+  return d;
 }
 
 /*! \relates QCPDataSelection
@@ -1080,7 +1104,7 @@ inline QDebug operator<< (QDebug d, const QCPDataSelection &selection)
       d << selection.dataRange(i);
     }
     d << ")";
-    return d.space();
+    return d;
 }
 
 
@@ -1088,15 +1112,15 @@ inline QDebug operator<< (QDebug d, const QCPDataSelection &selection)
 /* end of 'src/selection.h' */
 
 
-/* including file 'src/selectionrect.h', size 3338                           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/selectionrect.h'    */
+/* modified 2021-03-29T02:30:44, size 3354 */
 
 class QCP_LIB_DECL QCPSelectionRect : public QCPLayerable
 {
   Q_OBJECT
 public:
   explicit QCPSelectionRect(QCustomPlot *parentPlot);
-  virtual ~QCPSelectionRect();
+  virtual ~QCPSelectionRect() Q_DECL_OVERRIDE;
   
   // getters:
   QRect rect() const { return mRect; }
@@ -1142,8 +1166,8 @@ protected:
 /* end of 'src/selectionrect.h' */
 
 
-/* including file 'src/layout.h', size 14224                                 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/layout.h'            */
+/* modified 2021-03-29T02:30:44, size 14279 */
 
 class QCP_LIB_DECL QCPMarginGroup : public QObject
 {
@@ -1213,8 +1237,8 @@ public:
                           };
   Q_ENUMS(SizeConstraintRect)
 
-  explicit QCPLayoutElement(QCustomPlot *parentPlot=0);
-  virtual ~QCPLayoutElement();
+  explicit QCPLayoutElement(QCustomPlot *parentPlot=nullptr);
+  virtual ~QCPLayoutElement() Q_DECL_OVERRIDE;
   
   // getters:
   QCPLayout *layout() const { return mParentLayout; }
@@ -1226,7 +1250,7 @@ public:
   QSize minimumSize() const { return mMinimumSize; }
   QSize maximumSize() const { return mMaximumSize; }
   SizeConstraintRect sizeConstraintRect() const { return mSizeConstraintRect; }
-  QCPMarginGroup *marginGroup(QCP::MarginSide side) const { return mMarginGroups.value(side, (QCPMarginGroup*)0); }
+  QCPMarginGroup *marginGroup(QCP::MarginSide side) const { return mMarginGroups.value(side, nullptr); }
   QHash<QCP::MarginSide, QCPMarginGroup*> marginGroups() const { return mMarginGroups; }
   
   // setters:
@@ -1248,7 +1272,7 @@ public:
   virtual QList<QCPLayoutElement*> elements(bool recursive) const;
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
 protected:
   // property members:
@@ -1347,7 +1371,7 @@ public:
   Q_ENUMS(FillOrder)
   
   explicit QCPLayoutGrid();
-  virtual ~QCPLayoutGrid();
+  virtual ~QCPLayoutGrid() Q_DECL_OVERRIDE;
   
   // getters:
   int rowCount() const { return mElements.size(); }
@@ -1423,7 +1447,7 @@ public:
   Q_ENUMS(InsetPlacement)
   
   explicit QCPLayoutInset();
-  virtual ~QCPLayoutInset();
+  virtual ~QCPLayoutInset() Q_DECL_OVERRIDE;
   
   // getters:
   InsetPlacement insetPlacement(int index) const;
@@ -1442,7 +1466,7 @@ public:
   virtual QCPLayoutElement* takeAt(int index) Q_DECL_OVERRIDE;
   virtual bool take(QCPLayoutElement* element) Q_DECL_OVERRIDE;
   virtual void simplify() Q_DECL_OVERRIDE {}
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   // non-virtual methods:
   void addElement(QCPLayoutElement *element, Qt::Alignment alignment);
@@ -1463,8 +1487,8 @@ Q_DECLARE_METATYPE(QCPLayoutInset::InsetPlacement)
 /* end of 'src/layout.h' */
 
 
-/* including file 'src/lineending.h', size 4426                              */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/lineending.h'       */
+/* modified 2021-03-29T02:30:44, size 4426 */
 
 class QCP_LIB_DECL QCPLineEnding
 {
@@ -1527,8 +1551,148 @@ Q_DECLARE_METATYPE(QCPLineEnding::EndingStyle)
 /* end of 'src/lineending.h' */
 
 
-/* including file 'src/axis/axisticker.h', size 4177                         */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/labelpainter.h' */
+/* modified 2021-03-29T02:30:44, size 7086  */
+
+class QCPLabelPainterPrivate
+{
+  Q_GADGET
+public:
+  /*!
+    TODO
+  */
+  enum AnchorMode { amRectangular    ///< 
+                    ,amSkewedUpright ///<
+                    ,amSkewedRotated ///<
+                   };
+  Q_ENUMS(AnchorMode)
+  
+  /*!
+    TODO
+  */
+  enum AnchorReferenceType { artNormal    ///< 
+                             ,artTangent ///<
+                           };
+  Q_ENUMS(AnchorReferenceType)
+  
+  /*!
+    TODO
+  */
+  enum AnchorSide { asLeft      ///< 
+                    ,asRight    ///< 
+                    ,asTop      ///< 
+                    ,asBottom   ///< 
+                    ,asTopLeft
+                    ,asTopRight
+                    ,asBottomRight
+                    ,asBottomLeft
+                   };
+  Q_ENUMS(AnchorSide)
+  
+  explicit QCPLabelPainterPrivate(QCustomPlot *parentPlot);
+  virtual ~QCPLabelPainterPrivate();
+  
+  // setters:
+  void setAnchorSide(AnchorSide side);
+  void setAnchorMode(AnchorMode mode);
+  void setAnchorReference(const QPointF &pixelPoint);
+  void setAnchorReferenceType(AnchorReferenceType type);
+  void setFont(const QFont &font);
+  void setColor(const QColor &color);
+  void setPadding(int padding);
+  void setRotation(double rotation);
+  void setSubstituteExponent(bool enabled);
+  void setMultiplicationSymbol(QChar symbol);
+  void setAbbreviateDecimalPowers(bool enabled);
+  void setCacheSize(int labelCount);
+  
+  // getters:
+  AnchorMode anchorMode() const { return mAnchorMode; }
+  AnchorSide anchorSide() const { return mAnchorSide; }
+  QPointF anchorReference() const { return mAnchorReference; }
+  AnchorReferenceType anchorReferenceType() const { return mAnchorReferenceType; }
+  QFont font() const { return mFont; }
+  QColor color() const { return mColor; }
+  int padding() const { return mPadding; }
+  double rotation() const { return mRotation; }
+  bool substituteExponent() const { return mSubstituteExponent; }
+  QChar multiplicationSymbol() const { return mMultiplicationSymbol; }
+  bool abbreviateDecimalPowers() const { return mAbbreviateDecimalPowers; }
+  int cacheSize() const;
+  
+  //virtual int size() const;
+  
+  // non-property methods: 
+  void drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text);
+  void clearCache();
+  
+  // constants that may be used with setMultiplicationSymbol:
+  static const QChar SymbolDot;
+  static const QChar SymbolCross;
+  
+protected:
+  struct CachedLabel
+  {
+    QPoint offset;
+    QPixmap pixmap;
+  };
+  struct LabelData
+  {
+    AnchorSide side;
+    double rotation; // angle in degrees
+    QTransform transform; // the transform about the label anchor which is at (0, 0). Does not contain final absolute x/y positioning on the plot/axis
+    QString basePart, expPart, suffixPart;
+    QRect baseBounds, expBounds, suffixBounds;
+    QRect totalBounds; // is in a coordinate system where label top left is at (0, 0)
+    QRect rotatedTotalBounds; // is in a coordinate system where the label anchor is at (0, 0)
+    QFont baseFont, expFont;
+    QColor color;
+  };
+  
+  // property members:
+  AnchorMode mAnchorMode;
+  AnchorSide mAnchorSide;
+  QPointF mAnchorReference;
+  AnchorReferenceType mAnchorReferenceType;
+  QFont mFont;
+  QColor mColor;
+  int mPadding;
+  double mRotation; // this is the rotation applied uniformly to all labels, not the heterogeneous rotation in amCircularRotated mode
+  bool mSubstituteExponent;
+  QChar mMultiplicationSymbol;
+  bool mAbbreviateDecimalPowers;
+  // non-property members:
+  QCustomPlot *mParentPlot;
+  QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters
+  QCache<QString, CachedLabel> mLabelCache;
+  QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;
+  int mLetterCapHeight, mLetterDescent;
+  
+  // introduced virtual methods:
+  virtual void drawLabelMaybeCached(QCPPainter *painter, const QFont &font, const QColor &color, const QPointF &pos, AnchorSide side, double rotation, const QString &text);
+  virtual QByteArray generateLabelParameterHash() const; // TODO: get rid of this in favor of invalidation flag upon setters?
+
+  // non-virtual methods:
+  QPointF getAnchorPos(const QPointF &tickPos);
+  void drawText(QCPPainter *painter, const QPointF &pos, const LabelData &labelData) const;
+  LabelData getTickLabelData(const QFont &font, const QColor &color, double rotation, AnchorSide side, const QString &text) const;
+  void applyAnchorTransform(LabelData &labelData) const;
+  //void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const;
+  CachedLabel *createCachedLabel(const LabelData &labelData) const;
+  QByteArray cacheKey(const QString &text, const QColor &color, double rotation, AnchorSide side) const;
+  AnchorSide skewedAnchorSide(const QPointF &tickPos, double sideExpandHorz, double sideExpandVert) const;
+  AnchorSide rotationCorrectedSide(AnchorSide side, double rotation) const;
+  void analyzeFontMetrics();
+};
+Q_DECLARE_METATYPE(QCPLabelPainterPrivate::AnchorMode)
+Q_DECLARE_METATYPE(QCPLabelPainterPrivate::AnchorSide)
+
+
+/* end of 'src/axis/labelpainter.h' */
+
+
+/* including file 'src/axis/axisticker.h'  */
+/* modified 2021-03-29T02:30:44, size 4230 */
 
 class QCP_LIB_DECL QCPAxisTicker
 {
@@ -1579,8 +1743,12 @@ protected:
   // non-virtual methods:
   void trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const;
   double pickClosest(double target, const QVector<double> &candidates) const;
-  double getMantissa(double input, double *magnitude=0) const;
+  double getMantissa(double input, double *magnitude=nullptr) const;
   double cleanMantissa(double input) const;
+  
+private:
+  Q_DISABLE_COPY(QCPAxisTicker)
+  
 };
 Q_DECLARE_METATYPE(QCPAxisTicker::TickStepStrategy)
 Q_DECLARE_METATYPE(QSharedPointer<QCPAxisTicker>)
@@ -1588,8 +1756,8 @@ Q_DECLARE_METATYPE(QSharedPointer<QCPAxisTicker>)
 /* end of 'src/axis/axisticker.h' */
 
 
-/* including file 'src/axis/axistickerdatetime.h', size 3289                 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickerdatetime.h' */
+/* modified 2021-03-29T02:30:44, size 3600        */
 
 class QCP_LIB_DECL QCPAxisTickerDateTime : public QCPAxisTicker
 {
@@ -1599,23 +1767,31 @@ public:
   // getters:
   QString dateTimeFormat() const { return mDateTimeFormat; }
   Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; }
+# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+  QTimeZone timeZone() const { return mTimeZone; }
+#endif
   
   // setters:
   void setDateTimeFormat(const QString &format);
   void setDateTimeSpec(Qt::TimeSpec spec);
+# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+  void setTimeZone(const QTimeZone &zone);
+# endif
   void setTickOrigin(double origin); // hides base class method but calls baseclass implementation ("using" throws off IDEs and doxygen)
   void setTickOrigin(const QDateTime &origin);
   
   // static methods:
   static QDateTime keyToDateTime(double key);
-  static double dateTimeToKey(const QDateTime dateTime);
-  static double dateTimeToKey(const QDate date);
+  static double dateTimeToKey(const QDateTime &dateTime);
+  static double dateTimeToKey(const QDate &date, Qt::TimeSpec timeSpec=Qt::LocalTime);
   
 protected:
   // property members:
   QString mDateTimeFormat;
   Qt::TimeSpec mDateTimeSpec;
-  
+# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+  QTimeZone mTimeZone;
+# endif
   // non-property members:
   enum DateStrategy {dsNone, dsUniformTimeInDay, dsUniformDayInMonth} mDateStrategy;
   
@@ -1629,8 +1805,8 @@ protected:
 /* end of 'src/axis/axistickerdatetime.h' */
 
 
-/* including file 'src/axis/axistickertime.h', size 3542                     */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickertime.h' */
+/* modified 2021-03-29T02:30:44, size 3542    */
 
 class QCP_LIB_DECL QCPAxisTickerTime : public QCPAxisTicker
 {
@@ -1681,8 +1857,8 @@ Q_DECLARE_METATYPE(QCPAxisTickerTime::TimeUnit)
 /* end of 'src/axis/axistickertime.h' */
 
 
-/* including file 'src/axis/axistickerfixed.h', size 3308                    */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickerfixed.h' */
+/* modified 2021-03-29T02:30:44, size 3308     */
 
 class QCP_LIB_DECL QCPAxisTickerFixed : public QCPAxisTicker
 {
@@ -1723,8 +1899,8 @@ Q_DECLARE_METATYPE(QCPAxisTickerFixed::ScaleStrategy)
 /* end of 'src/axis/axistickerfixed.h' */
 
 
-/* including file 'src/axis/axistickertext.h', size 3085                     */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickertext.h' */
+/* modified 2021-03-29T02:30:44, size 3090    */
 
 class QCP_LIB_DECL QCPAxisTickerText : public QCPAxisTicker
 {
@@ -1737,12 +1913,12 @@ public:
   
   // setters:
   void setTicks(const QMap<double, QString> &ticks);
-  void setTicks(const QVector<double> &positions, const QVector<QString> labels);
+  void setTicks(const QVector<double> &positions, const QVector<QString> &labels);
   void setSubTickCount(int subTicks);
   
   // non-virtual methods:
   void clear();
-  void addTick(double position, QString label);
+  void addTick(double position, const QString &label);
   void addTicks(const QMap<double, QString> &ticks);
   void addTicks(const QVector<double> &positions, const QVector<QString> &labels);
   
@@ -1756,14 +1932,13 @@ protected:
   virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;
   virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE;
   virtual QVector<double> createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE;
-  
 };
 
 /* end of 'src/axis/axistickertext.h' */
 
 
-/* including file 'src/axis/axistickerpi.h', size 3911                       */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickerpi.h' */
+/* modified 2021-03-29T02:30:44, size 3911  */
 
 class QCP_LIB_DECL QCPAxisTickerPi : public QCPAxisTicker
 {
@@ -1821,8 +1996,8 @@ Q_DECLARE_METATYPE(QCPAxisTickerPi::FractionStyle)
 /* end of 'src/axis/axistickerpi.h' */
 
 
-/* including file 'src/axis/axistickerlog.h', size 2663                      */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axistickerlog.h' */
+/* modified 2021-03-29T02:30:44, size 2594   */
 
 class QCP_LIB_DECL QCPAxisTickerLog : public QCPAxisTicker
 {
@@ -1846,7 +2021,6 @@ protected:
   double mLogBaseLnInv;
   
   // reimplemented virtual methods:
-  virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE;
   virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;
   virtual QVector<double> createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE;
 };
@@ -1854,8 +2028,8 @@ protected:
 /* end of 'src/axis/axistickerlog.h' */
 
 
-/* including file 'src/axis/axis.h', size 20634                              */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/axis/axis.h'         */
+/* modified 2021-03-29T02:30:44, size 20913 */
 
 class QCP_LIB_DECL QCPGrid :public QCPLayerable
 {
@@ -2000,7 +2174,7 @@ public:
   Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
   
   explicit QCPAxis(QCPAxisRect *parent, AxisType type);
-  virtual ~QCPAxis();
+  virtual ~QCPAxis() Q_DECL_OVERRIDE;
   
   // getters:
   AxisType axisType() const { return mAxisType; }
@@ -2094,7 +2268,7 @@ public:
   void setUpperEnding(const QCPLineEnding &ending);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   // non-property methods:
   Qt::Orientation orientation() const { return mOrientation; }
@@ -2112,7 +2286,7 @@ public:
   QList<QCPAbstractItem*> items() const;
   
   static AxisType marginSideToAxisType(QCP::MarginSide side);
-  static Qt::Orientation orientation(AxisType type) { return type==atBottom||type==atTop ? Qt::Horizontal : Qt::Vertical; }
+  static Qt::Orientation orientation(AxisType type) { return type==atBottom || type==atTop ? Qt::Horizontal : Qt::Vertical; }
   static AxisType opposite(AxisType type);
   
 signals:
@@ -2183,10 +2357,10 @@ protected:
   virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE;
   virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE;
   // mouse events:
-  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details);
-  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos);
-  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos);
-  virtual void wheelEvent(QWheelEvent *event);
+  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;
+  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;
+  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;
+  virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
   
   // non-virtual methods:
   void setupTickVectors();
@@ -2220,7 +2394,7 @@ public:
   virtual ~QCPAxisPainterPrivate();
   
   virtual void draw(QCPPainter *painter);
-  virtual int size() const;
+  virtual int size();
   void clearCache();
   
   QRect axisSelectionBox() const { return mAxisSelectionBox; }
@@ -2245,7 +2419,7 @@ public:
   QFont tickLabelFont;
   QColor tickLabelColor;
   QRect axisRect, viewportRect;
-  double offset; // directly accessed by QCPAxis setters/getters
+  int offset; // directly accessed by QCPAxis setters/getters
   bool abbreviateDecimalPowers;
   bool reversedEndings;
   
@@ -2282,8 +2456,8 @@ protected:
 /* end of 'src/axis/axis.h' */
 
 
-/* including file 'src/scatterstyle.h', size 7275                            */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/scatterstyle.h'     */
+/* modified 2021-03-29T02:30:44, size 7275 */
 
 class QCP_LIB_DECL QCPScatterStyle
 {
@@ -2389,8 +2563,8 @@ Q_DECLARE_METATYPE(QCPScatterStyle::ScatterShape)
 /* end of 'src/scatterstyle.h' */
 
 
-/* including file 'src/datacontainer.h', size 4596                           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/datacontainer.h'     */
+/* modified 2021-03-29T02:30:44, size 34070 */
 
 /*! \relates QCPDataContainer
   Returns whether the sort key of \a a is less than the sort key of \a b.
@@ -2457,11 +2631,9 @@ protected:
   void performAutoSqueeze();
 };
 
-// include implementation in header since it is a class template:
 
-/* including file 'src/datacontainer.cpp', size 31349                        */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
 
+// include implementation in header since it is a class template:
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPDataContainer
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2758,7 +2930,7 @@ void QCPDataContainer<DataType>::removeBefore(double sortKey)
 {
   QCPDataContainer<DataType>::iterator it = begin();
   QCPDataContainer<DataType>::iterator itEnd = std::lower_bound(begin(), end(), DataType::fromSortKey(sortKey), qcpLessThanSortKey<DataType>);
-  mPreallocSize += itEnd-it; // don't actually delete, just add it to the preallocated block (if it gets too large, squeeze will take care of it)
+  mPreallocSize += int(itEnd-it); // don't actually delete, just add it to the preallocated block (if it gets too large, squeeze will take care of it)
   if (mAutoSqueeze)
     performAutoSqueeze();
 }
@@ -3088,8 +3260,8 @@ QCPRange QCPDataContainer<DataType>::valueRange(bool &foundRange, QCP::SignDomai
   QCPDataContainer<DataType>::const_iterator itEnd = constEnd();
   if (DataType::sortKeyIsMainKey() && restrictKeyRange)
   {
-    itBegin = findBegin(inKeyRange.lower);
-    itEnd = findEnd(inKeyRange.upper);
+    itBegin = findBegin(inKeyRange.lower, false);
+    itEnd = findEnd(inKeyRange.upper, false);
   }
   if (signDomain == QCP::sdBoth) // range may be anywhere
   {
@@ -3162,7 +3334,7 @@ QCPRange QCPDataContainer<DataType>::valueRange(bool &foundRange, QCP::SignDomai
 template <class DataType>
 void QCPDataContainer<DataType>::limitIteratorsToDataRange(const_iterator &begin, const_iterator &end, const QCPDataRange &dataRange) const
 {
-  QCPDataRange iteratorRange(begin-constBegin(), end-constBegin());
+  QCPDataRange iteratorRange(int(begin-constBegin()), int(end-constBegin()));
   iteratorRange = iteratorRange.bounded(dataRange.bounded(this->dataRange()));
   begin = constBegin()+iteratorRange.begin();
   end = constBegin()+iteratorRange.end();
@@ -3228,14 +3400,13 @@ void QCPDataContainer<DataType>::performAutoSqueeze()
   if (shrinkPreAllocation || shrinkPostAllocation)
     squeeze(shrinkPreAllocation, shrinkPostAllocation);
 }
-/* end of 'src/datacontainer.cpp' */
 
 
 /* end of 'src/datacontainer.h' */
 
 
-/* including file 'src/plottable.h', size 8312                               */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottable.h'        */
+/* modified 2021-03-29T02:30:44, size 8461 */
 
 class QCP_LIB_DECL QCPSelectionDecorator
 {
@@ -3301,7 +3472,7 @@ class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable
   /// \endcond
 public:
   QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis);
-  virtual ~QCPAbstractPlottable();
+  virtual ~QCPAbstractPlottable() Q_DECL_OVERRIDE;
   
   // getters:
   QString name() const { return mName; }
@@ -3329,8 +3500,8 @@ public:
   void setSelectionDecorator(QCPSelectionDecorator *decorator);
 
   // introduced virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0;
-  virtual QCPPlottableInterface1D *interface1D() { return 0; }
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE = 0; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables
+  virtual QCPPlottableInterface1D *interface1D() { return nullptr; }
   virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const = 0;
   virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const = 0;
   
@@ -3391,8 +3562,8 @@ private:
 /* end of 'src/plottable.h' */
 
 
-/* including file 'src/item.h', size 9384                                    */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/item.h'             */
+/* modified 2021-03-29T02:30:44, size 9425 */
 
 class QCP_LIB_DECL QCPItemAnchor
 {
@@ -3416,7 +3587,7 @@ protected:
   QSet<QCPItemPosition*> mChildrenX, mChildrenY;
   
   // introduced virtual methods:
-  virtual QCPItemPosition *toQCPItemPosition() { return 0; }
+  virtual QCPItemPosition *toQCPItemPosition() { return nullptr; }
   
   // non-virtual methods:
   void addChildX(QCPItemPosition* pos); // called from pos when this anchor is set as parent
@@ -3454,7 +3625,7 @@ public:
   Q_ENUMS(PositionType)
   
   QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name);
-  virtual ~QCPItemPosition();
+  virtual ~QCPItemPosition() Q_DECL_OVERRIDE;
   
   // getters:
   PositionType type() const { return typeX(); }
@@ -3479,7 +3650,7 @@ public:
   bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);
   bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);
   void setCoords(double key, double value);
-  void setCoords(const QPointF &coords);
+  void setCoords(const QPointF &pos);
   void setAxes(QCPAxis* keyAxis, QCPAxis* valueAxis);
   void setAxisRect(QCPAxisRect *axisRect);
   void setPixelPosition(const QPointF &pixelPosition);
@@ -3513,7 +3684,7 @@ class QCP_LIB_DECL QCPAbstractItem : public QCPLayerable
   /// \endcond
 public:
   explicit QCPAbstractItem(QCustomPlot *parentPlot);
-  virtual ~QCPAbstractItem();
+  virtual ~QCPAbstractItem() Q_DECL_OVERRIDE;
   
   // getters:
   bool clipToAxisRect() const { return mClipToAxisRect; }
@@ -3528,7 +3699,7 @@ public:
   Q_SLOT void setSelected(bool selected);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE = 0;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE = 0;
   
   // non-virtual methods:
   QList<QCPItemPosition*> positions() const { return mPositions; }
@@ -3576,8 +3747,8 @@ private:
 /* end of 'src/item.h' */
 
 
-/* including file 'src/core.h', size 14886                                   */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/core.h'              */
+/* modified 2021-03-29T02:30:44, size 19304 */
 
 class QCP_LIB_DECL QCustomPlot : public QWidget
 {
@@ -3617,8 +3788,8 @@ public:
                        };
   Q_ENUMS(RefreshPriority)
   
-  explicit QCustomPlot(QWidget *parent = 0);
-  virtual ~QCustomPlot();
+  explicit QCustomPlot(QWidget *parent = nullptr);
+  virtual ~QCustomPlot() Q_DECL_OVERRIDE;
   
   // getters:
   QRect viewport() const { return mViewport; }
@@ -3672,13 +3843,15 @@ public:
   int clearPlottables();
   int plottableCount() const;
   QList<QCPAbstractPlottable*> selectedPlottables() const;
-  QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false) const;
+  template<class PlottableType>
+  PlottableType *plottableAt(const QPointF &pos, bool onlySelectable=false, int *dataIndex=nullptr) const;
+  QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false, int *dataIndex=nullptr) const;
   bool hasPlottable(QCPAbstractPlottable *plottable) const;
  
   // specialized interface for QCPGraph:
   QCPGraph *graph(int index) const;
   QCPGraph *graph() const;
-  QCPGraph *addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0);
+  QCPGraph *addGraph(QCPAxis *keyAxis=nullptr, QCPAxis *valueAxis=nullptr);
   bool removeGraph(QCPGraph *graph);
   bool removeGraph(int index);
   int clearGraphs();
@@ -3693,6 +3866,8 @@ public:
   int clearItems();
   int itemCount() const;
   QList<QCPAbstractItem*> selectedItems() const;
+  template<class ItemType>
+  ItemType *itemAt(const QPointF &pos, bool onlySelectable=false) const;
   QCPAbstractItem *itemAt(const QPointF &pos, bool onlySelectable=false) const;
   bool hasItem(QCPAbstractItem *item) const;
   
@@ -3703,7 +3878,7 @@ public:
   bool setCurrentLayer(const QString &name);
   bool setCurrentLayer(QCPLayer *layer);
   int layerCount() const;
-  bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove);
+  bool addLayer(const QString &name, QCPLayer *otherLayer=nullptr, LayerInsertMode insertMode=limAbove);
   bool removeLayer(QCPLayer *layer);
   bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove);
   
@@ -3727,6 +3902,7 @@ public:
   QPixmap toPixmap(int width=0, int height=0, double scale=1.0);
   void toPainter(QCPPainter *painter, int width=0, int height=0);
   Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint);
+  double replotTime(bool average=false) const;
   
   QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
   QCPLegend *legend;
@@ -3749,6 +3925,7 @@ signals:
   
   void selectionChangedByUser();
   void beforeReplot();
+  void afterLayout();
   void afterReplot();
   
 protected:
@@ -3787,6 +3964,7 @@ protected:
   QVariant mMouseSignalLayerableDetails;
   bool mReplotting;
   bool mReplotQueued;
+  double mReplotTime, mReplotTimeAverage;
   int mOpenGlMultisamples;
   QCP::AntialiasedElements mOpenGlAntialiasedElementsBackup;
   bool mOpenGlCacheLabelsBackup;
@@ -3821,8 +3999,8 @@ protected:
   bool registerGraph(QCPGraph *graph);
   bool registerItem(QCPAbstractItem* item);
   void updateLayerIndices() const;
-  QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const;
-  QList<QCPLayerable*> layerableListAt(const QPointF &pos, bool onlySelectable, QList<QVariant> *selectionDetails=0) const;
+  QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=nullptr) const;
+  QList<QCPLayerable*> layerableListAt(const QPointF &pos, bool onlySelectable, QList<QVariant> *selectionDetails=nullptr) const;
   void drawBackground(QCPPainter *painter);
   void setupPaintBuffers();
   QCPAbstractPaintBuffer *createPaintBuffer();
@@ -3841,16 +4019,111 @@ protected:
 Q_DECLARE_METATYPE(QCustomPlot::LayerInsertMode)
 Q_DECLARE_METATYPE(QCustomPlot::RefreshPriority)
 
+
+// implementation of template functions:
+
+/*!
+  Returns the plottable at the pixel position \a pos. The plottable type (a QCPAbstractPlottable
+  subclass) that shall be taken into consideration can be specified via the template parameter.
+
+  Plottables that only consist of single lines (like graphs) have a tolerance band around them, see
+  \ref setSelectionTolerance. If multiple plottables come into consideration, the one closest to \a
+  pos is returned.
+  
+  If \a onlySelectable is true, only plottables that are selectable
+  (QCPAbstractPlottable::setSelectable) are considered.
+  
+  if \a dataIndex is non-null, it is set to the index of the plottable's data point that is closest
+  to \a pos.
+
+  If there is no plottable of the specified type at \a pos, returns \c nullptr.
+  
+  \see itemAt, layoutElementAt
+*/
+template<class PlottableType>
+PlottableType *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable, int *dataIndex) const
+{
+  PlottableType *resultPlottable = 0;
+  QVariant resultDetails;
+  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
+  
+  foreach (QCPAbstractPlottable *plottable, mPlottables)
+  {
+    PlottableType *currentPlottable = qobject_cast<PlottableType*>(plottable);
+    if (!currentPlottable || (onlySelectable && !currentPlottable->selectable())) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractPlottable::selectable
+      continue;
+    if (currentPlottable->clipRect().contains(pos.toPoint())) // only consider clicks where the plottable is actually visible
+    {
+      QVariant details;
+      double currentDistance = currentPlottable->selectTest(pos, false, dataIndex ? &details : nullptr);
+      if (currentDistance >= 0 && currentDistance < resultDistance)
+      {
+        resultPlottable = currentPlottable;
+        resultDetails = details;
+        resultDistance = currentDistance;
+      }
+    }
+  }
+  
+  if (resultPlottable && dataIndex)
+  {
+    QCPDataSelection sel = resultDetails.value<QCPDataSelection>();
+    if (!sel.isEmpty())
+      *dataIndex = sel.dataRange(0).begin();
+  }
+  return resultPlottable;
+}
+
+/*!
+  Returns the item at the pixel position \a pos. The item type (a QCPAbstractItem subclass) that shall be
+  taken into consideration can be specified via the template parameter. Items that only consist of single
+  lines (e.g. \ref QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref
+  setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is returned.
+  
+  If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are
+  considered.
+  
+  If there is no item at \a pos, returns \c nullptr.
+  
+  \see plottableAt, layoutElementAt
+*/
+template<class ItemType>
+ItemType *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
+{
+  ItemType *resultItem = 0;
+  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
+  
+  foreach (QCPAbstractItem *item, mItems)
+  {
+    ItemType *currentItem = qobject_cast<ItemType*>(item);
+    if (!currentItem || (onlySelectable && !currentItem->selectable())) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
+      continue;
+    if (!currentItem->clipToAxisRect() || currentItem->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
+    {
+      double currentDistance = currentItem->selectTest(pos, false);
+      if (currentDistance >= 0 && currentDistance < resultDistance)
+      {
+        resultItem = currentItem;
+        resultDistance = currentDistance;
+      }
+    }
+  }
+  
+  return resultItem;
+}
+
+
+
 /* end of 'src/core.h' */
 
 
-/* including file 'src/plottable1d.h', size 4544                             */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottable1d.h'       */
+/* modified 2021-03-29T02:30:44, size 25638 */
 
 class QCPPlottableInterface1D
 {
 public:
-  virtual ~QCPPlottableInterface1D() {}
+  virtual ~QCPPlottableInterface1D() = default;
   // introduced pure virtual methods:
   virtual int dataCount() const = 0;
   virtual double dataMainKey(int index) const = 0;
@@ -3871,7 +4144,7 @@ class QCPAbstractPlottable1D : public QCPAbstractPlottable, public QCPPlottableI
   
 public:
   QCPAbstractPlottable1D(QCPAxis *keyAxis, QCPAxis *valueAxis);
-  virtual ~QCPAbstractPlottable1D();
+  virtual ~QCPAbstractPlottable1D() Q_DECL_OVERRIDE;
   
   // virtual methods of 1d plottable interface:
   virtual int dataCount() const Q_DECL_OVERRIDE;
@@ -3886,7 +4159,7 @@ public:
   virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE;
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   virtual QCPPlottableInterface1D *interface1D() Q_DECL_OVERRIDE { return this; }
   
 protected:
@@ -3902,11 +4175,9 @@ private:
   
 };
 
-// include implementation in header since it is a class template:
 
-/* including file 'src/plottable1d.cpp', size 22240                          */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
 
+// include implementation in header since it is a class template:
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////// QCPPlottableInterface1D
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -4241,16 +4512,16 @@ QCPDataSelection QCPAbstractPlottable1D<DataType>::selectTestRect(const QRectF &
     if (currentSegmentBegin == -1)
     {
       if (valueRange.contains(it->mainValue()) && keyRange.contains(it->mainKey())) // start segment
-        currentSegmentBegin = it-mDataContainer->constBegin();
+        currentSegmentBegin = int(it-mDataContainer->constBegin());
     } else if (!valueRange.contains(it->mainValue()) || !keyRange.contains(it->mainKey())) // segment just ended
     {
-      result.addDataRange(QCPDataRange(currentSegmentBegin, it-mDataContainer->constBegin()), false);
+      result.addDataRange(QCPDataRange(currentSegmentBegin, int(it-mDataContainer->constBegin())), false);
       currentSegmentBegin = -1;
     }
   }
   // process potential last segment:
   if (currentSegmentBegin != -1)
-    result.addDataRange(QCPDataRange(currentSegmentBegin, end-mDataContainer->constBegin()), false);
+    result.addDataRange(QCPDataRange(currentSegmentBegin, int(end-mDataContainer->constBegin())), false);
   
   result.simplify();
   return result;
@@ -4262,7 +4533,7 @@ QCPDataSelection QCPAbstractPlottable1D<DataType>::selectTestRect(const QRectF &
 template <class DataType>
 int QCPAbstractPlottable1D<DataType>::findBegin(double sortKey, bool expandedRange) const
 {
-  return mDataContainer->findBegin(sortKey, expandedRange)-mDataContainer->constBegin();
+  return int(mDataContainer->findBegin(sortKey, expandedRange)-mDataContainer->constBegin());
 }
 
 /*!
@@ -4271,7 +4542,7 @@ int QCPAbstractPlottable1D<DataType>::findBegin(double sortKey, bool expandedRan
 template <class DataType>
 int QCPAbstractPlottable1D<DataType>::findEnd(double sortKey, bool expandedRange) const
 {
-  return mDataContainer->findEnd(sortKey, expandedRange)-mDataContainer->constBegin();
+  return int(mDataContainer->findEnd(sortKey, expandedRange)-mDataContainer->constBegin());
 }
 
 /*!
@@ -4279,6 +4550,9 @@ int QCPAbstractPlottable1D<DataType>::findEnd(double sortKey, bool expandedRange
   point-like. Most subclasses will want to reimplement this method again, to provide a more
   accurate hit test based on the true data visualization geometry.
 
+  If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data point
+  to \a pos.
+  
   \seebaseclassmethod
 */
 template <class DataType>
@@ -4290,7 +4564,7 @@ double QCPAbstractPlottable1D<DataType>::selectTest(const QPointF &pos, bool onl
     return -1;
   
   QCPDataSelection selectionResult;
-  double minDistSqr = std::numeric_limits<double>::max();
+  double minDistSqr = (std::numeric_limits<double>::max)();
   int minDistIndex = mDataContainer->size();
   
   typename QCPDataContainer<DataType>::const_iterator begin = mDataContainer->constBegin();
@@ -4320,7 +4594,7 @@ double QCPAbstractPlottable1D<DataType>::selectTest(const QPointF &pos, bool onl
       if (currentDistSqr < minDistSqr)
       {
         minDistSqr = currentDistSqr;
-        minDistIndex = it-mDataContainer->constBegin();
+        minDistIndex = int(it-mDataContainer->constBegin());
       }
     }
   }
@@ -4376,6 +4650,18 @@ void QCPAbstractPlottable1D<DataType>::getDataSegments(QList<QCPDataRange> &sele
 template <class DataType>
 void QCPAbstractPlottable1D<DataType>::drawPolyline(QCPPainter *painter, const QVector<QPointF> &lineData) const
 {
+  // if drawing lines in plot (instead of PDF), reduce 1px lines to cosmetic, because at least in
+  // Qt6 drawing of "1px" width lines is much slower even though it has same appearance apart from
+  // High-DPI. In High-DPI cases people must set a pen width slightly larger than 1.0 to get
+  // correct DPI scaling of width, but of course with performance penalty.
+  if (!painter->modes().testFlag(QCPPainter::pmVectorized) &&
+      qFuzzyCompare(painter->pen().widthF(), 1.0))
+  {
+    QPen newPen = painter->pen();
+    newPen.setWidth(0);
+    painter->setPen(newPen);
+  }
+
   // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
   if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
       painter->pen().style() == Qt::SolidLine &&
@@ -4418,14 +4704,13 @@ void QCPAbstractPlottable1D<DataType>::drawPolyline(QCPPainter *painter, const Q
     painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart);
   }
 }
-/* end of 'src/plottable1d.cpp' */
 
 
 /* end of 'src/plottable1d.h' */
 
 
-/* including file 'src/colorgradient.h', size 6243                           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/colorgradient.h'    */
+/* modified 2021-03-29T02:30:44, size 7262 */
 
 class QCP_LIB_DECL QCPColorGradient
 {
@@ -4441,6 +4726,19 @@ public:
                           };
   Q_ENUMS(ColorInterpolation)
   
+  /*!
+    Defines how NaN data points shall appear in the plot.
+    
+    \see setNanHandling, setNanColor
+  */
+  enum NanHandling { nhNone ///< NaN data points are not explicitly handled and shouldn't occur in the data (this gives slight performance improvement)
+                     ,nhLowestColor  ///< NaN data points appear as the lowest color defined in this QCPColorGradient
+                     ,nhHighestColor ///< NaN data points appear as the highest color defined in this QCPColorGradient
+                     ,nhTransparent ///< NaN data points appear transparent
+                     ,nhNanColor ///< NaN data points appear as the color defined with \ref setNanColor
+                   };
+  Q_ENUMS(NanHandling)
+  
   /*!
     Defines the available presets that can be loaded with \ref loadPreset. See the documentation
     there for an image of the presets.
@@ -4469,6 +4767,8 @@ public:
   int levelCount() const { return mLevelCount; }
   QMap<double, QColor> colorStops() const { return mColorStops; }
   ColorInterpolation colorInterpolation() const { return mColorInterpolation; }
+  NanHandling nanHandling() const { return mNanHandling; }
+  QColor nanColor() const { return mNanColor; }
   bool periodic() const { return mPeriodic; }
   
   // setters:
@@ -4476,6 +4776,8 @@ public:
   void setColorStops(const QMap<double, QColor> &colorStops);
   void setColorStopAt(double position, const QColor &color);
   void setColorInterpolation(ColorInterpolation interpolation);
+  void setNanHandling(NanHandling handling);
+  void setNanColor(const QColor &color);
   void setPeriodic(bool enabled);
   
   // non-property methods:
@@ -4491,6 +4793,8 @@ protected:
   int mLevelCount;
   QMap<double, QColor> mColorStops;
   ColorInterpolation mColorInterpolation;
+  NanHandling mNanHandling;
+  QColor mNanColor;
   bool mPeriodic;
   
   // non-property members:
@@ -4502,13 +4806,14 @@ protected:
   void updateColorBuffer();
 };
 Q_DECLARE_METATYPE(QCPColorGradient::ColorInterpolation)
+Q_DECLARE_METATYPE(QCPColorGradient::NanHandling)
 Q_DECLARE_METATYPE(QCPColorGradient::GradientPreset)
 
 /* end of 'src/colorgradient.h' */
 
 
-/* including file 'src/selectiondecorator-bracket.h', size 4442              */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/selectiondecorator-bracket.h' */
+/* modified 2021-03-29T02:30:44, size 4458           */
 
 class QCP_LIB_DECL QCPSelectionDecoratorBracket : public QCPSelectionDecorator
 {
@@ -4530,7 +4835,7 @@ public:
   Q_ENUMS(BracketStyle)
   
   QCPSelectionDecoratorBracket();
-  virtual ~QCPSelectionDecoratorBracket();
+  virtual ~QCPSelectionDecoratorBracket() Q_DECL_OVERRIDE;
   
   // getters:
   QPen bracketPen() const { return mBracketPen; }
@@ -4576,8 +4881,8 @@ Q_DECLARE_METATYPE(QCPSelectionDecoratorBracket::BracketStyle)
 /* end of 'src/selectiondecorator-bracket.h' */
 
 
-/* including file 'src/layoutelements/layoutelement-axisrect.h', size 7507   */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/layoutelements/layoutelement-axisrect.h' */
+/* modified 2021-03-29T02:30:44, size 7529                      */
 
 class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement
 {
@@ -4591,7 +4896,7 @@ class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement
   /// \endcond
 public:
   explicit QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true);
-  virtual ~QCPAxisRect();
+  virtual ~QCPAxisRect() Q_DECL_OVERRIDE;
   
   // getters:
   QPixmap background() const { return mBackgroundPixmap; }
@@ -4628,7 +4933,7 @@ public:
   QCPAxis *axis(QCPAxis::AxisType type, int index=0) const;
   QList<QCPAxis*> axes(QCPAxis::AxisTypes types) const;
   QList<QCPAxis*> axes() const;
-  QCPAxis *addAxis(QCPAxis::AxisType type, QCPAxis *axis=0);
+  QCPAxis *addAxis(QCPAxis::AxisType type, QCPAxis *axis=nullptr);
   QList<QCPAxis*> addAxes(QCPAxis::AxisTypes types);
   bool removeAxis(QCPAxis *axis);
   QCPLayoutInset *insetLayout() const { return mInsetLayout; }
@@ -4702,8 +5007,8 @@ private:
 /* end of 'src/layoutelements/layoutelement-axisrect.h' */
 
 
-/* including file 'src/layoutelements/layoutelement-legend.h', size 10397    */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/layoutelements/layoutelement-legend.h' */
+/* modified 2021-03-29T02:30:44, size 10425                   */
 
 class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement
 {
@@ -4738,7 +5043,7 @@ public:
   Q_SLOT void setSelected(bool selected);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
 signals:
   void selectionChanged(bool selected);
@@ -4827,7 +5132,7 @@ public:
   Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
   
   explicit QCPLegend();
-  virtual ~QCPLegend();
+  virtual ~QCPLegend() Q_DECL_OVERRIDE;
   
   // getters:
   QPen borderPen() const { return mBorderPen; }
@@ -4863,7 +5168,7 @@ public:
   void setSelectedTextColor(const QColor &color);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   // non-virtual methods:
   QCPAbstractLegendItem *item(int index) const;
@@ -4920,8 +5225,8 @@ Q_DECLARE_METATYPE(QCPLegend::SelectablePart)
 /* end of 'src/layoutelements/layoutelement-legend.h' */
 
 
-/* including file 'src/layoutelements/layoutelement-textelement.h', size 5353 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200  */
+/* including file 'src/layoutelements/layoutelement-textelement.h' */
+/* modified 2021-03-29T02:30:44, size 5359                         */
 
 class QCP_LIB_DECL QCPTextElement : public QCPLayoutElement
 {
@@ -4963,7 +5268,7 @@ public:
   Q_SLOT void setSelected(bool selected);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;
   virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;
   virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;
@@ -5007,8 +5312,8 @@ private:
 /* end of 'src/layoutelements/layoutelement-textelement.h' */
 
 
-/* including file 'src/layoutelements/layoutelement-colorscale.h', size 5923 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/layoutelements/layoutelement-colorscale.h' */
+/* modified 2021-03-29T02:30:44, size 5939                        */
 
 
 class QCPColorScaleAxisRectPrivate : public QCPAxisRect
@@ -5050,7 +5355,7 @@ class QCP_LIB_DECL QCPColorScale : public QCPLayoutElement
   /// \endcond
 public:
   explicit QCPColorScale(QCustomPlot *parentPlot);
-  virtual ~QCPColorScale();
+  virtual ~QCPColorScale() Q_DECL_OVERRIDE;
   
   // getters:
   QCPAxis *axis() const { return mColorAxis.data(); }
@@ -5115,8 +5420,8 @@ private:
 /* end of 'src/layoutelements/layoutelement-colorscale.h' */
 
 
-/* including file 'src/plottables/plottable-graph.h', size 9294              */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-graph.h' */
+/* modified 2021-03-29T02:30:44, size 9316           */
 
 class QCP_LIB_DECL QCPGraphData
 {
@@ -5176,7 +5481,7 @@ public:
   Q_ENUMS(LineStyle)
   
   explicit QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis);
-  virtual ~QCPGraph();
+  virtual ~QCPGraph() Q_DECL_OVERRIDE;
   
   // getters:
   QSharedPointer<QCPGraphDataContainer> data() const { return mDataContainer; }
@@ -5200,7 +5505,7 @@ public:
   void addData(double key, double value);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;
   virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;
   
@@ -5239,7 +5544,7 @@ protected:
   bool segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const;
   QPointF getFillBasePoint(QPointF matchingDataPoint) const;
   const QPolygonF getFillPolygon(const QVector<QPointF> *lineData, QCPDataRange segment) const;
-  const QPolygonF getChannelFillPolygon(const QVector<QPointF> *lineData, QCPDataRange thisSegment, const QVector<QPointF> *otherData, QCPDataRange otherSegment) const;
+  const QPolygonF getChannelFillPolygon(const QVector<QPointF> *thisData, QCPDataRange thisSegment, const QVector<QPointF> *otherData, QCPDataRange otherSegment) const;
   int findIndexBelowX(const QVector<QPointF> *data, double x) const;
   int findIndexAboveX(const QVector<QPointF> *data, double x) const;
   int findIndexBelowY(const QVector<QPointF> *data, double y) const;
@@ -5254,8 +5559,8 @@ Q_DECLARE_METATYPE(QCPGraph::LineStyle)
 /* end of 'src/plottables/plottable-graph.h' */
 
 
-/* including file 'src/plottables/plottable-curve.h', size 7409              */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-curve.h' */
+/* modified 2021-03-29T02:30:44, size 7434           */
 
 class QCP_LIB_DECL QCPCurveData
 {
@@ -5309,7 +5614,7 @@ public:
   Q_ENUMS(LineStyle)
   
   explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis);
-  virtual ~QCPCurve();
+  virtual ~QCPCurve() Q_DECL_OVERRIDE;
   
   // getters:
   QSharedPointer<QCPCurveDataContainer> data() const { return mDataContainer; }
@@ -5332,7 +5637,7 @@ public:
   void addData(double key, double value);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;
   virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;
   
@@ -5354,7 +5659,7 @@ protected:
   void getCurveLines(QVector<QPointF> *lines, const QCPDataRange &dataRange, double penWidth) const;
   void getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange, double scatterWidth) const;
   int getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const;
-  QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const;
+  QPointF getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const;
   QVector<QPointF> getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const;
   bool mayTraverse(int prevRegion, int currentRegion) const;
   bool getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const;
@@ -5369,8 +5674,8 @@ Q_DECLARE_METATYPE(QCPCurve::LineStyle)
 /* end of 'src/plottables/plottable-curve.h' */
 
 
-/* including file 'src/plottables/plottable-bars.h', size 8924               */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-bars.h' */
+/* modified 2021-03-29T02:30:44, size 8955          */
 
 class QCP_LIB_DECL QCPBarsGroup : public QObject
 {
@@ -5392,7 +5697,7 @@ public:
                    };
   Q_ENUMS(SpacingType)
   
-  QCPBarsGroup(QCustomPlot *parentPlot);
+  explicit QCPBarsGroup(QCustomPlot *parentPlot);
   virtual ~QCPBarsGroup();
   
   // getters:
@@ -5494,7 +5799,7 @@ public:
   Q_ENUMS(WidthType)
   
   explicit QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis);
-  virtual ~QCPBars();
+  virtual ~QCPBars() Q_DECL_OVERRIDE;
   
   // getters:
   double width() const { return mWidth; }
@@ -5523,7 +5828,7 @@ public:
   
   // reimplemented virtual methods:
   virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE;
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;
   virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;
   virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE;
@@ -5557,8 +5862,8 @@ Q_DECLARE_METATYPE(QCPBars::WidthType)
 /* end of 'src/plottables/plottable-bars.h' */
 
 
-/* including file 'src/plottables/plottable-statisticalbox.h', size 7516     */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-statisticalbox.h' */
+/* modified 2021-03-29T02:30:44, size 7522                    */
 
 class QCP_LIB_DECL QCPStatisticalBoxData
 {
@@ -5641,7 +5946,7 @@ public:
   
   // reimplemented virtual methods:
   virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE;
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;
   virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;
   
@@ -5674,8 +5979,8 @@ protected:
 /* end of 'src/plottables/plottable-statisticalbox.h' */
 
 
-/* including file 'src/plottables/plottable-colormap.h', size 7070           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-colormap.h' */
+/* modified 2021-03-29T02:30:44, size 7092              */
 
 class QCP_LIB_DECL QCPColorMapData
 {
@@ -5747,7 +6052,7 @@ class QCP_LIB_DECL QCPColorMap : public QCPAbstractPlottable
   /// \endcond
 public:
   explicit QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis);
-  virtual ~QCPColorMap();
+  virtual ~QCPColorMap() Q_DECL_OVERRIDE;
   
   // getters:
   QCPColorMapData *data() const { return mMapData; }
@@ -5772,7 +6077,7 @@ public:
   Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18));
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;
   virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;
   
@@ -5810,8 +6115,8 @@ protected:
 /* end of 'src/plottables/plottable-colormap.h' */
 
 
-/* including file 'src/plottables/plottable-financial.h', size 8622          */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-financial.h' */
+/* modified 2021-03-29T02:30:44, size 8644               */
 
 class QCP_LIB_DECL QCPFinancialData
 {
@@ -5881,7 +6186,7 @@ public:
   Q_ENUMS(ChartStyle)
   
   explicit QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis);
-  virtual ~QCPFinancial();
+  virtual ~QCPFinancial() Q_DECL_OVERRIDE;
   
   // getters:
   QSharedPointer<QCPFinancialDataContainer> data() const { return mDataContainer; }
@@ -5912,7 +6217,7 @@ public:
   
   // reimplemented virtual methods:
   virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE;
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;
   virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;
   
@@ -5949,8 +6254,8 @@ Q_DECLARE_METATYPE(QCPFinancial::ChartStyle)
 /* end of 'src/plottables/plottable-financial.h' */
 
 
-/* including file 'src/plottables/plottable-errorbar.h', size 7727           */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/plottables/plottable-errorbar.h' */
+/* modified 2021-03-29T02:30:44, size 7749              */
 
 class QCP_LIB_DECL QCPErrorBarsData
 {
@@ -6005,7 +6310,7 @@ public:
   Q_ENUMS(ErrorType)
   
   explicit QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis);
-  virtual ~QCPErrorBars();
+  virtual ~QCPErrorBars() Q_DECL_OVERRIDE;
   // getters:
   QSharedPointer<QCPErrorBarsDataContainer> data() const { return mDataContainer; }
   QCPAbstractPlottable *dataPlottable() const { return mDataPlottable.data(); }
@@ -6041,7 +6346,7 @@ public:
   virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE;
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   virtual QCPPlottableInterface1D *interface1D() Q_DECL_OVERRIDE { return this; }
   
 protected:
@@ -6074,8 +6379,8 @@ protected:
 /* end of 'src/plottables/plottable-errorbar.h' */
 
 
-/* including file 'src/items/item-straightline.h', size 3117                 */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-straightline.h' */
+/* modified 2021-03-29T02:30:44, size 3137        */
 
 class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem
 {
@@ -6086,7 +6391,7 @@ class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem
   /// \endcond
 public:
   explicit QCPItemStraightLine(QCustomPlot *parentPlot);
-  virtual ~QCPItemStraightLine();
+  virtual ~QCPItemStraightLine() Q_DECL_OVERRIDE;
   
   // getters:
   QPen pen() const { return mPen; }
@@ -6097,7 +6402,7 @@ public:
   void setSelectedPen(const QPen &pen);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   QCPItemPosition * const point1;
   QCPItemPosition * const point2;
@@ -6110,15 +6415,15 @@ protected:
   virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;
   
   // non-virtual methods:
-  QLineF getRectClippedStraightLine(const QCPVector2D &point1, const QCPVector2D &vec, const QRect &rect) const;
+  QLineF getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRect &rect) const;
   QPen mainPen() const;
 };
 
 /* end of 'src/items/item-straightline.h' */
 
 
-/* including file 'src/items/item-line.h', size 3407                         */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-line.h'  */
+/* modified 2021-03-29T02:30:44, size 3429 */
 
 class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem
 {
@@ -6131,7 +6436,7 @@ class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem
   /// \endcond
 public:
   explicit QCPItemLine(QCustomPlot *parentPlot);
-  virtual ~QCPItemLine();
+  virtual ~QCPItemLine() Q_DECL_OVERRIDE;
   
   // getters:
   QPen pen() const { return mPen; }
@@ -6146,7 +6451,7 @@ public:
   void setTail(const QCPLineEnding &tail);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   QCPItemPosition * const start;
   QCPItemPosition * const end;
@@ -6167,8 +6472,8 @@ protected:
 /* end of 'src/items/item-line.h' */
 
 
-/* including file 'src/items/item-curve.h', size 3379                        */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-curve.h' */
+/* modified 2021-03-29T02:30:44, size 3401 */
 
 class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem
 {
@@ -6181,7 +6486,7 @@ class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem
   /// \endcond
 public:
   explicit QCPItemCurve(QCustomPlot *parentPlot);
-  virtual ~QCPItemCurve();
+  virtual ~QCPItemCurve() Q_DECL_OVERRIDE;
   
   // getters:
   QPen pen() const { return mPen; }
@@ -6196,7 +6501,7 @@ public:
   void setTail(const QCPLineEnding &tail);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   QCPItemPosition * const start;
   QCPItemPosition * const startDir;
@@ -6218,8 +6523,8 @@ protected:
 /* end of 'src/items/item-curve.h' */
 
 
-/* including file 'src/items/item-rect.h', size 3688                         */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-rect.h'  */
+/* modified 2021-03-29T02:30:44, size 3710 */
 
 class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem
 {
@@ -6232,7 +6537,7 @@ class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem
   /// \endcond
 public:
   explicit QCPItemRect(QCustomPlot *parentPlot);
-  virtual ~QCPItemRect();
+  virtual ~QCPItemRect() Q_DECL_OVERRIDE;
   
   // getters:
   QPen pen() const { return mPen; }
@@ -6247,7 +6552,7 @@ public:
   void setSelectedBrush(const QBrush &brush);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   QCPItemPosition * const topLeft;
   QCPItemPosition * const bottomRight;
@@ -6277,8 +6582,8 @@ protected:
 /* end of 'src/items/item-rect.h' */
 
 
-/* including file 'src/items/item-text.h', size 5554                         */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-text.h'  */
+/* modified 2021-03-29T02:30:44, size 5576 */
 
 class QCP_LIB_DECL QCPItemText : public QCPAbstractItem
 {
@@ -6300,7 +6605,7 @@ class QCP_LIB_DECL QCPItemText : public QCPAbstractItem
   /// \endcond
 public:
   explicit QCPItemText(QCustomPlot *parentPlot);
-  virtual ~QCPItemText();
+  virtual ~QCPItemText() Q_DECL_OVERRIDE;
   
   // getters:
   QColor color() const { return mColor; }
@@ -6333,7 +6638,7 @@ public:
   void setPadding(const QMargins &padding);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   QCPItemPosition * const position;
   QCPItemAnchor * const topLeft;
@@ -6374,8 +6679,8 @@ protected:
 /* end of 'src/items/item-text.h' */
 
 
-/* including file 'src/items/item-ellipse.h', size 3868                      */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-ellipse.h' */
+/* modified 2021-03-29T02:30:44, size 3890   */
 
 class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem
 {
@@ -6388,7 +6693,7 @@ class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem
   /// \endcond
 public:
   explicit QCPItemEllipse(QCustomPlot *parentPlot);
-  virtual ~QCPItemEllipse();
+  virtual ~QCPItemEllipse() Q_DECL_OVERRIDE;
   
   // getters:
   QPen pen() const { return mPen; }
@@ -6403,7 +6708,7 @@ public:
   void setSelectedBrush(const QBrush &brush);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   QCPItemPosition * const topLeft;
   QCPItemPosition * const bottomRight;
@@ -6436,8 +6741,8 @@ protected:
 /* end of 'src/items/item-ellipse.h' */
 
 
-/* including file 'src/items/item-pixmap.h', size 4373                       */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-pixmap.h' */
+/* modified 2021-03-29T02:30:44, size 4407  */
 
 class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem
 {
@@ -6452,7 +6757,7 @@ class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem
   /// \endcond
 public:
   explicit QCPItemPixmap(QCustomPlot *parentPlot);
-  virtual ~QCPItemPixmap();
+  virtual ~QCPItemPixmap() Q_DECL_OVERRIDE;
   
   // getters:
   QPixmap pixmap() const { return mPixmap; }
@@ -6469,7 +6774,7 @@ public:
   void setSelectedPen(const QPen &pen);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   QCPItemPosition * const topLeft;
   QCPItemPosition * const bottomRight;
@@ -6498,15 +6803,15 @@ protected:
   
   // non-virtual methods:
   void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false);
-  QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const;
+  QRect getFinalRect(bool *flippedHorz=nullptr, bool *flippedVert=nullptr) const;
   QPen mainPen() const;
 };
 
 /* end of 'src/items/item-pixmap.h' */
 
 
-/* including file 'src/items/item-tracer.h', size 4762                       */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-tracer.h' */
+/* modified 2021-03-29T02:30:44, size 4811  */
 
 class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem
 {
@@ -6537,7 +6842,7 @@ public:
   Q_ENUMS(TracerStyle)
 
   explicit QCPItemTracer(QCustomPlot *parentPlot);
-  virtual ~QCPItemTracer();
+  virtual ~QCPItemTracer() Q_DECL_OVERRIDE;
 
   // getters:
   QPen pen() const { return mPen; }
@@ -6562,7 +6867,7 @@ public:
   void setInterpolating(bool enabled);
 
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   // non-virtual methods:
   void updatePosition();
@@ -6591,8 +6896,8 @@ Q_DECLARE_METATYPE(QCPItemTracer::TracerStyle)
 /* end of 'src/items/item-tracer.h' */
 
 
-/* including file 'src/items/item-bracket.h', size 3969                      */
-/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
+/* including file 'src/items/item-bracket.h' */
+/* modified 2021-03-29T02:30:44, size 3991   */
 
 class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem
 {
@@ -6618,7 +6923,7 @@ public:
   Q_ENUMS(BracketStyle)
 
   explicit QCPItemBracket(QCustomPlot *parentPlot);
-  virtual ~QCPItemBracket();
+  virtual ~QCPItemBracket() Q_DECL_OVERRIDE;
   
   // getters:
   QPen pen() const { return mPen; }
@@ -6633,7 +6938,7 @@ public:
   void setStyle(BracketStyle style);
   
   // reimplemented virtual methods:
-  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;
   
   QCPItemPosition * const left;
   QCPItemPosition * const right;
@@ -6658,5 +6963,775 @@ Q_DECLARE_METATYPE(QCPItemBracket::BracketStyle)
 /* end of 'src/items/item-bracket.h' */
 
 
+/* including file 'src/polar/radialaxis.h'  */
+/* modified 2021-03-29T02:30:44, size 12227 */
+
+
+class QCP_LIB_DECL QCPPolarAxisRadial : public QCPLayerable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  
+  /// \endcond
+public:
+  /*!
+    Defines the reference of the angle at which a radial axis is tilted (\ref setAngle).
+  */
+  enum AngleReference { arAbsolute    ///< The axis tilt is given in absolute degrees. The zero is to the right and positive angles are measured counter-clockwise.
+                       ,arAngularAxis ///< The axis tilt is measured in the angular coordinate system given by the parent angular axis.
+                      };
+  Q_ENUMS(AngleReference)
+  /*!
+    Defines the scale of an axis.
+    \see setScaleType
+  */
+  enum ScaleType { stLinear       ///< Linear scaling
+                   ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed axis coordinates (possibly also \ref setTicker to a \ref QCPAxisTickerLog instance).
+                 };
+  Q_ENUMS(ScaleType)
+  /*!
+    Defines the selectable parts of an axis.
+    \see setSelectableParts, setSelectedParts
+  */
+  enum SelectablePart { spNone        = 0      ///< None of the selectable parts
+                        ,spAxis       = 0x001  ///< The axis backbone and tick marks
+                        ,spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole, not individually)
+                        ,spAxisLabel  = 0x004  ///< The axis label
+                      };
+  Q_ENUMS(SelectablePart)
+  Q_FLAGS(SelectableParts)
+  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
+  
+  enum LabelMode { lmUpright   ///< 
+                   ,lmRotated ///< 
+                 };
+  Q_ENUMS(LabelMode)
+  
+  explicit QCPPolarAxisRadial(QCPPolarAxisAngular *parent);
+  virtual ~QCPPolarAxisRadial();
+  
+  // getters:
+  bool rangeDrag() const { return mRangeDrag; }
+  bool rangeZoom() const { return mRangeZoom; }
+  double rangeZoomFactor() const { return mRangeZoomFactor; }
+  
+  QCPPolarAxisAngular *angularAxis() const { return mAngularAxis; }
+  ScaleType scaleType() const { return mScaleType; }
+  const QCPRange range() const { return mRange; }
+  bool rangeReversed() const { return mRangeReversed; }
+  double angle() const { return mAngle; }
+  AngleReference angleReference() const { return mAngleReference; }
+  QSharedPointer<QCPAxisTicker> ticker() const { return mTicker; }
+  bool ticks() const { return mTicks; }
+  bool tickLabels() const { return mTickLabels; }
+  int tickLabelPadding() const { return mLabelPainter.padding(); }
+  QFont tickLabelFont() const { return mTickLabelFont; }
+  QColor tickLabelColor() const { return mTickLabelColor; }
+  double tickLabelRotation() const { return mLabelPainter.rotation(); }
+  LabelMode tickLabelMode() const;
+  QString numberFormat() const;
+  int numberPrecision() const { return mNumberPrecision; }
+  QVector<double> tickVector() const { return mTickVector; }
+  QVector<double> subTickVector() const { return mSubTickVector; }
+  QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
+  int tickLengthIn() const;
+  int tickLengthOut() const;
+  bool subTicks() const { return mSubTicks; }
+  int subTickLengthIn() const;
+  int subTickLengthOut() const;
+  QPen basePen() const { return mBasePen; }
+  QPen tickPen() const { return mTickPen; }
+  QPen subTickPen() const { return mSubTickPen; }
+  QFont labelFont() const { return mLabelFont; }
+  QColor labelColor() const { return mLabelColor; }
+  QString label() const { return mLabel; }
+  int labelPadding() const;
+  SelectableParts selectedParts() const { return mSelectedParts; }
+  SelectableParts selectableParts() const { return mSelectableParts; }
+  QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
+  QFont selectedLabelFont() const { return mSelectedLabelFont; }
+  QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }
+  QColor selectedLabelColor() const { return mSelectedLabelColor; }
+  QPen selectedBasePen() const { return mSelectedBasePen; }
+  QPen selectedTickPen() const { return mSelectedTickPen; }
+  QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
+  
+  // setters:
+  void setRangeDrag(bool enabled);
+  void setRangeZoom(bool enabled);
+  void setRangeZoomFactor(double factor);
+  
+  Q_SLOT void setScaleType(QCPPolarAxisRadial::ScaleType type);
+  Q_SLOT void setRange(const QCPRange &range);
+  void setRange(double lower, double upper);
+  void setRange(double position, double size, Qt::AlignmentFlag alignment);
+  void setRangeLower(double lower);
+  void setRangeUpper(double upper);
+  void setRangeReversed(bool reversed);
+  void setAngle(double degrees);
+  void setAngleReference(AngleReference reference);
+  void setTicker(QSharedPointer<QCPAxisTicker> ticker);
+  void setTicks(bool show);
+  void setTickLabels(bool show);
+  void setTickLabelPadding(int padding);
+  void setTickLabelFont(const QFont &font);
+  void setTickLabelColor(const QColor &color);
+  void setTickLabelRotation(double degrees);
+  void setTickLabelMode(LabelMode mode);
+  void setNumberFormat(const QString &formatCode);
+  void setNumberPrecision(int precision);
+  void setTickLength(int inside, int outside=0);
+  void setTickLengthIn(int inside);
+  void setTickLengthOut(int outside);
+  void setSubTicks(bool show);
+  void setSubTickLength(int inside, int outside=0);
+  void setSubTickLengthIn(int inside);
+  void setSubTickLengthOut(int outside);
+  void setBasePen(const QPen &pen);
+  void setTickPen(const QPen &pen);
+  void setSubTickPen(const QPen &pen);
+  void setLabelFont(const QFont &font);
+  void setLabelColor(const QColor &color);
+  void setLabel(const QString &str);
+  void setLabelPadding(int padding);
+  void setSelectedTickLabelFont(const QFont &font);
+  void setSelectedLabelFont(const QFont &font);
+  void setSelectedTickLabelColor(const QColor &color);
+  void setSelectedLabelColor(const QColor &color);
+  void setSelectedBasePen(const QPen &pen);
+  void setSelectedTickPen(const QPen &pen);
+  void setSelectedSubTickPen(const QPen &pen);
+  Q_SLOT void setSelectableParts(const QCPPolarAxisRadial::SelectableParts &selectableParts);
+  Q_SLOT void setSelectedParts(const QCPPolarAxisRadial::SelectableParts &selectedParts);
+  
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  
+  // non-property methods:
+  void moveRange(double diff);
+  void scaleRange(double factor);
+  void scaleRange(double factor, double center);
+  void rescale(bool onlyVisiblePlottables=false);
+  void pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const;
+  QPointF coordToPixel(double angleCoord, double radiusCoord) const;
+  double coordToRadius(double coord) const;
+  double radiusToCoord(double radius) const;
+  SelectablePart getPartAt(const QPointF &pos) const;
+  
+signals:
+  void rangeChanged(const QCPRange &newRange);
+  void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
+  void scaleTypeChanged(QCPPolarAxisRadial::ScaleType scaleType);
+  void selectionChanged(const QCPPolarAxisRadial::SelectableParts &parts);
+  void selectableChanged(const QCPPolarAxisRadial::SelectableParts &parts);
+
+protected:
+  // property members:
+  bool mRangeDrag;
+  bool mRangeZoom;
+  double mRangeZoomFactor;
+  
+  // axis base:
+  QCPPolarAxisAngular *mAngularAxis;
+  double mAngle;
+  AngleReference mAngleReference;
+  SelectableParts mSelectableParts, mSelectedParts;
+  QPen mBasePen, mSelectedBasePen;
+  // axis label:
+  int mLabelPadding;
+  QString mLabel;
+  QFont mLabelFont, mSelectedLabelFont;
+  QColor mLabelColor, mSelectedLabelColor;
+  // tick labels:
+  //int mTickLabelPadding; in label painter
+  bool mTickLabels;
+  //double mTickLabelRotation; in label painter
+  QFont mTickLabelFont, mSelectedTickLabelFont;
+  QColor mTickLabelColor, mSelectedTickLabelColor;
+  int mNumberPrecision;
+  QLatin1Char mNumberFormatChar;
+  bool mNumberBeautifulPowers;
+  bool mNumberMultiplyCross;
+  // ticks and subticks:
+  bool mTicks;
+  bool mSubTicks;
+  int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut;
+  QPen mTickPen, mSelectedTickPen;
+  QPen mSubTickPen, mSelectedSubTickPen;
+  // scale and range:
+  QCPRange mRange;
+  bool mRangeReversed;
+  ScaleType mScaleType;
+  
+  // non-property members:
+  QPointF mCenter;
+  double mRadius;
+  QSharedPointer<QCPAxisTicker> mTicker;
+  QVector<double> mTickVector;
+  QVector<QString> mTickVectorLabels;
+  QVector<double> mSubTickVector;
+  bool mDragging;
+  QCPRange mDragStartRange;
+  QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;
+  QCPLabelPainterPrivate mLabelPainter;
+  
+  // reimplemented virtual methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;
+  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;
+  virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;
+  // events:
+  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE;
+  virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE;
+  // mouse events:
+  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;
+  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;
+  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;
+  virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
+  
+  // non-virtual methods:
+  void updateGeometry(const QPointF &center, double radius);
+  void setupTickVectors();
+  QPen getBasePen() const;
+  QPen getTickPen() const;
+  QPen getSubTickPen() const;
+  QFont getTickLabelFont() const;
+  QFont getLabelFont() const;
+  QColor getTickLabelColor() const;
+  QColor getLabelColor() const;
+  
+private:
+  Q_DISABLE_COPY(QCPPolarAxisRadial)
+  
+  friend class QCustomPlot;
+  friend class QCPPolarAxisAngular;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarAxisRadial::SelectableParts)
+Q_DECLARE_METATYPE(QCPPolarAxisRadial::AngleReference)
+Q_DECLARE_METATYPE(QCPPolarAxisRadial::ScaleType)
+Q_DECLARE_METATYPE(QCPPolarAxisRadial::SelectablePart)
+
+
+
+/* end of 'src/polar/radialaxis.h' */
+
+
+/* including file 'src/polar/layoutelement-angularaxis.h' */
+/* modified 2021-03-29T02:30:44, size 13461               */
+
+class QCP_LIB_DECL QCPPolarAxisAngular : public QCPLayoutElement
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  
+  /// \endcond
+public:
+  /*!
+    Defines the selectable parts of an axis.
+    \see setSelectableParts, setSelectedParts
+  */
+  enum SelectablePart { spNone        = 0      ///< None of the selectable parts
+                        ,spAxis       = 0x001  ///< The axis backbone and tick marks
+                        ,spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole, not individually)
+                        ,spAxisLabel  = 0x004  ///< The axis label
+                      };
+  Q_ENUMS(SelectablePart)
+  Q_FLAGS(SelectableParts)
+  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
+  
+  /*!
+    TODO
+  */
+  enum LabelMode { lmUpright   ///< 
+                   ,lmRotated ///< 
+                 };
+  Q_ENUMS(LabelMode)
+  
+  explicit QCPPolarAxisAngular(QCustomPlot *parentPlot);
+  virtual ~QCPPolarAxisAngular();
+  
+  // getters:
+  QPixmap background() const { return mBackgroundPixmap; }
+  QBrush backgroundBrush() const { return mBackgroundBrush; }
+  bool backgroundScaled() const { return mBackgroundScaled; }
+  Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }
+  bool rangeDrag() const { return mRangeDrag; }
+  bool rangeZoom() const { return mRangeZoom; }
+  double rangeZoomFactor() const { return mRangeZoomFactor; }
+  
+  const QCPRange range() const { return mRange; }
+  bool rangeReversed() const { return mRangeReversed; }
+  double angle() const { return mAngle; }
+  QSharedPointer<QCPAxisTicker> ticker() const { return mTicker; }
+  bool ticks() const { return mTicks; }
+  bool tickLabels() const { return mTickLabels; }
+  int tickLabelPadding() const { return mLabelPainter.padding(); }
+  QFont tickLabelFont() const { return mTickLabelFont; }
+  QColor tickLabelColor() const { return mTickLabelColor; }
+  double tickLabelRotation() const { return mLabelPainter.rotation(); }
+  LabelMode tickLabelMode() const;
+  QString numberFormat() const;
+  int numberPrecision() const { return mNumberPrecision; }
+  QVector<double> tickVector() const { return mTickVector; }
+  QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
+  int tickLengthIn() const { return mTickLengthIn; }
+  int tickLengthOut() const { return mTickLengthOut; }
+  bool subTicks() const { return mSubTicks; }
+  int subTickLengthIn() const { return mSubTickLengthIn; }
+  int subTickLengthOut() const { return mSubTickLengthOut; }
+  QPen basePen() const { return mBasePen; }
+  QPen tickPen() const { return mTickPen; }
+  QPen subTickPen() const { return mSubTickPen; }
+  QFont labelFont() const { return mLabelFont; }
+  QColor labelColor() const { return mLabelColor; }
+  QString label() const { return mLabel; }
+  int labelPadding() const { return mLabelPadding; }
+  SelectableParts selectedParts() const { return mSelectedParts; }
+  SelectableParts selectableParts() const { return mSelectableParts; }
+  QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
+  QFont selectedLabelFont() const { return mSelectedLabelFont; }
+  QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }
+  QColor selectedLabelColor() const { return mSelectedLabelColor; }
+  QPen selectedBasePen() const { return mSelectedBasePen; }
+  QPen selectedTickPen() const { return mSelectedTickPen; }
+  QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
+  QCPPolarGrid *grid() const { return mGrid; }
+  
+  // setters:
+  void setBackground(const QPixmap &pm);
+  void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);
+  void setBackground(const QBrush &brush);
+  void setBackgroundScaled(bool scaled);
+  void setBackgroundScaledMode(Qt::AspectRatioMode mode);
+  void setRangeDrag(bool enabled);
+  void setRangeZoom(bool enabled);
+  void setRangeZoomFactor(double factor);
+  
+  Q_SLOT void setRange(const QCPRange &range);
+  void setRange(double lower, double upper);
+  void setRange(double position, double size, Qt::AlignmentFlag alignment);
+  void setRangeLower(double lower);
+  void setRangeUpper(double upper);
+  void setRangeReversed(bool reversed);
+  void setAngle(double degrees);
+  void setTicker(QSharedPointer<QCPAxisTicker> ticker);
+  void setTicks(bool show);
+  void setTickLabels(bool show);
+  void setTickLabelPadding(int padding);
+  void setTickLabelFont(const QFont &font);
+  void setTickLabelColor(const QColor &color);
+  void setTickLabelRotation(double degrees);
+  void setTickLabelMode(LabelMode mode);
+  void setNumberFormat(const QString &formatCode);
+  void setNumberPrecision(int precision);
+  void setTickLength(int inside, int outside=0);
+  void setTickLengthIn(int inside);
+  void setTickLengthOut(int outside);
+  void setSubTicks(bool show);
+  void setSubTickLength(int inside, int outside=0);
+  void setSubTickLengthIn(int inside);
+  void setSubTickLengthOut(int outside);
+  void setBasePen(const QPen &pen);
+  void setTickPen(const QPen &pen);
+  void setSubTickPen(const QPen &pen);
+  void setLabelFont(const QFont &font);
+  void setLabelColor(const QColor &color);
+  void setLabel(const QString &str);
+  void setLabelPadding(int padding);
+  void setLabelPosition(Qt::AlignmentFlag position);
+  void setSelectedTickLabelFont(const QFont &font);
+  void setSelectedLabelFont(const QFont &font);
+  void setSelectedTickLabelColor(const QColor &color);
+  void setSelectedLabelColor(const QColor &color);
+  void setSelectedBasePen(const QPen &pen);
+  void setSelectedTickPen(const QPen &pen);
+  void setSelectedSubTickPen(const QPen &pen);
+  Q_SLOT void setSelectableParts(const QCPPolarAxisAngular::SelectableParts &selectableParts);
+  Q_SLOT void setSelectedParts(const QCPPolarAxisAngular::SelectableParts &selectedParts);
+  
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;
+  virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE;
+  virtual QList<QCPLayoutElement*> elements(bool recursive) const Q_DECL_OVERRIDE;
+  
+  // non-property methods:
+  bool removeGraph(QCPPolarGraph *graph);
+  int radialAxisCount() const;
+  QCPPolarAxisRadial *radialAxis(int index=0) const;
+  QList<QCPPolarAxisRadial*> radialAxes() const;
+  QCPPolarAxisRadial *addRadialAxis(QCPPolarAxisRadial *axis=0);
+  bool removeRadialAxis(QCPPolarAxisRadial *axis);
+  QCPLayoutInset *insetLayout() const { return mInsetLayout; }
+  QRegion exactClipRegion() const;
+  
+  void moveRange(double diff);
+  void scaleRange(double factor);
+  void scaleRange(double factor, double center);
+  void rescale(bool onlyVisiblePlottables=false);
+  double coordToAngleRad(double coord) const { return mAngleRad+(coord-mRange.lower)/mRange.size()*(mRangeReversed ? -2.0*M_PI : 2.0*M_PI); } // mention in doc that return doesn't wrap
+  double angleRadToCoord(double angleRad) const { return mRange.lower+(angleRad-mAngleRad)/(mRangeReversed ? -2.0*M_PI : 2.0*M_PI)*mRange.size(); }
+  void pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const;
+  QPointF coordToPixel(double angleCoord, double radiusCoord) const;
+  SelectablePart getPartAt(const QPointF &pos) const;
+  
+  // read-only interface imitating a QRect:
+  int left() const { return mRect.left(); }
+  int right() const { return mRect.right(); }
+  int top() const { return mRect.top(); }
+  int bottom() const { return mRect.bottom(); }
+  int width() const { return mRect.width(); }
+  int height() const { return mRect.height(); }
+  QSize size() const { return mRect.size(); }
+  QPoint topLeft() const { return mRect.topLeft(); }
+  QPoint topRight() const { return mRect.topRight(); }
+  QPoint bottomLeft() const { return mRect.bottomLeft(); }
+  QPoint bottomRight() const { return mRect.bottomRight(); }
+  QPointF center() const { return mCenter; }
+  double radius() const { return mRadius; }
+  
+signals:
+  void rangeChanged(const QCPRange &newRange);
+  void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
+  void selectionChanged(const QCPPolarAxisAngular::SelectableParts &parts);
+  void selectableChanged(const QCPPolarAxisAngular::SelectableParts &parts);
+  
+protected:
+  // property members:
+  QBrush mBackgroundBrush;
+  QPixmap mBackgroundPixmap;
+  QPixmap mScaledBackgroundPixmap;
+  bool mBackgroundScaled;
+  Qt::AspectRatioMode mBackgroundScaledMode;
+  QCPLayoutInset *mInsetLayout;
+  bool mRangeDrag;
+  bool mRangeZoom;
+  double mRangeZoomFactor;
+  
+  // axis base:
+  double mAngle, mAngleRad;
+  SelectableParts mSelectableParts, mSelectedParts;
+  QPen mBasePen, mSelectedBasePen;
+  // axis label:
+  int mLabelPadding;
+  QString mLabel;
+  QFont mLabelFont, mSelectedLabelFont;
+  QColor mLabelColor, mSelectedLabelColor;
+  // tick labels:
+  //int mTickLabelPadding; in label painter
+  bool mTickLabels;
+  //double mTickLabelRotation; in label painter
+  QFont mTickLabelFont, mSelectedTickLabelFont;
+  QColor mTickLabelColor, mSelectedTickLabelColor;
+  int mNumberPrecision;
+  QLatin1Char mNumberFormatChar;
+  bool mNumberBeautifulPowers;
+  bool mNumberMultiplyCross;
+  // ticks and subticks:
+  bool mTicks;
+  bool mSubTicks;
+  int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut;
+  QPen mTickPen, mSelectedTickPen;
+  QPen mSubTickPen, mSelectedSubTickPen;
+  // scale and range:
+  QCPRange mRange;
+  bool mRangeReversed;
+  
+  // non-property members:
+  QPointF mCenter;
+  double mRadius;
+  QList<QCPPolarAxisRadial*> mRadialAxes;
+  QCPPolarGrid *mGrid;
+  QList<QCPPolarGraph*> mGraphs;
+  QSharedPointer<QCPAxisTicker> mTicker;
+  QVector<double> mTickVector;
+  QVector<QString> mTickVectorLabels;
+  QVector<QPointF> mTickVectorCosSin;
+  QVector<double> mSubTickVector;
+  QVector<QPointF> mSubTickVectorCosSin;
+  bool mDragging;
+  QCPRange mDragAngularStart;
+  QList<QCPRange> mDragRadialStart;
+  QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;
+  QCPLabelPainterPrivate mLabelPainter;
+  
+  // reimplemented virtual methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;
+  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;
+  virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;
+  // events:
+  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;
+  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;
+  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;
+  virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
+  
+  // non-virtual methods:
+  bool registerPolarGraph(QCPPolarGraph *graph);
+  void drawBackground(QCPPainter *painter, const QPointF &center, double radius);
+  void setupTickVectors();
+  QPen getBasePen() const;
+  QPen getTickPen() const;
+  QPen getSubTickPen() const;
+  QFont getTickLabelFont() const;
+  QFont getLabelFont() const;
+  QColor getTickLabelColor() const;
+  QColor getLabelColor() const;
+  
+private:
+  Q_DISABLE_COPY(QCPPolarAxisAngular)
+  
+  friend class QCustomPlot;
+  friend class QCPPolarGrid;
+  friend class QCPPolarGraph;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarAxisAngular::SelectableParts)
+Q_DECLARE_METATYPE(QCPPolarAxisAngular::SelectablePart)
+
+/* end of 'src/polar/layoutelement-angularaxis.h' */
+
+
+/* including file 'src/polar/polargrid.h'  */
+/* modified 2021-03-29T02:30:44, size 4506 */
+
+class QCP_LIB_DECL QCPPolarGrid :public QCPLayerable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  
+  /// \endcond
+public:
+  /*!
+    TODO
+  */
+  enum GridType { gtAngular = 0x01 ///< 
+                  ,gtRadial = 0x02 ///< 
+                  ,gtAll    = 0xFF ///< 
+                  ,gtNone   = 0x00 ///< 
+                };
+  Q_ENUMS(GridType)
+  Q_FLAGS(GridTypes)
+  Q_DECLARE_FLAGS(GridTypes, GridType)
+  
+  explicit QCPPolarGrid(QCPPolarAxisAngular *parentAxis);
+  
+  // getters:
+  QCPPolarAxisRadial *radialAxis() const { return mRadialAxis.data(); }
+  GridTypes type() const { return mType; }
+  GridTypes subGridType() const { return mSubGridType; }
+  bool antialiasedSubGrid() const { return mAntialiasedSubGrid; }
+  bool antialiasedZeroLine() const { return mAntialiasedZeroLine; }
+  QPen angularPen() const { return mAngularPen; }
+  QPen angularSubGridPen() const { return mAngularSubGridPen; }
+  QPen radialPen() const { return mRadialPen; }
+  QPen radialSubGridPen() const { return mRadialSubGridPen; }
+  QPen radialZeroLinePen() const { return mRadialZeroLinePen; }
+  
+  // setters:
+  void setRadialAxis(QCPPolarAxisRadial *axis);
+  void setType(GridTypes type);
+  void setSubGridType(GridTypes type);
+  void setAntialiasedSubGrid(bool enabled);
+  void setAntialiasedZeroLine(bool enabled);
+  void setAngularPen(const QPen &pen);
+  void setAngularSubGridPen(const QPen &pen);
+  void setRadialPen(const QPen &pen);
+  void setRadialSubGridPen(const QPen &pen);
+  void setRadialZeroLinePen(const QPen &pen);
+  
+protected:
+  // property members:
+  GridTypes mType;
+  GridTypes mSubGridType;
+  bool mAntialiasedSubGrid, mAntialiasedZeroLine;
+  QPen mAngularPen, mAngularSubGridPen;
+  QPen mRadialPen, mRadialSubGridPen, mRadialZeroLinePen;
+  
+  // non-property members:
+  QCPPolarAxisAngular *mParentAxis;
+  QPointer<QCPPolarAxisRadial> mRadialAxis;
+  
+  // reimplemented virtual methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;
+  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;
+  
+  // non-virtual methods:
+  void drawRadialGrid(QCPPainter *painter, const QPointF &center, const QVector<double> &coords, const QPen &pen, const QPen &zeroPen=Qt::NoPen);
+  void drawAngularGrid(QCPPainter *painter, const QPointF &center, double radius, const QVector<QPointF> &ticksCosSin, const QPen &pen);
+  
+private:
+  Q_DISABLE_COPY(QCPPolarGrid)
+  
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarGrid::GridTypes)
+Q_DECLARE_METATYPE(QCPPolarGrid::GridType)
+
+
+/* end of 'src/polar/polargrid.h' */
+
+
+/* including file 'src/polar/polargraph.h' */
+/* modified 2021-03-29T02:30:44, size 9606 */
+
+
+class QCP_LIB_DECL QCPPolarLegendItem : public QCPAbstractLegendItem
+{
+  Q_OBJECT
+public:
+  QCPPolarLegendItem(QCPLegend *parent, QCPPolarGraph *graph);
+  
+  // getters:
+  QCPPolarGraph *polarGraph() { return mPolarGraph; }
+  
+protected:
+  // property members:
+  QCPPolarGraph *mPolarGraph;
+  
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;
+  virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE;
+  
+  // non-virtual methods:
+  QPen getIconBorderPen() const;
+  QColor getTextColor() const;
+  QFont getFont() const;
+};
+
+
+class QCP_LIB_DECL QCPPolarGraph : public QCPLayerable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  
+  /// \endcond
+public:
+  /*!
+    Defines how the graph's line is represented visually in the plot. The line is drawn with the
+    current pen of the graph (\ref setPen).
+    \see setLineStyle
+  */
+  enum LineStyle { lsNone        ///< data points are not connected with any lines (e.g. data only represented
+                                 ///< with symbols according to the scatter style, see \ref setScatterStyle)
+                   ,lsLine       ///< data points are connected by a straight line
+                 };
+  Q_ENUMS(LineStyle)
+  
+  QCPPolarGraph(QCPPolarAxisAngular *keyAxis, QCPPolarAxisRadial *valueAxis);
+  virtual ~QCPPolarGraph();
+  
+  // getters:
+  QString name() const { return mName; }
+  bool antialiasedFill() const { return mAntialiasedFill; }
+  bool antialiasedScatters() const { return mAntialiasedScatters; }
+  QPen pen() const { return mPen; }
+  QBrush brush() const { return mBrush; }
+  bool periodic() const { return mPeriodic; }
+  QCPPolarAxisAngular *keyAxis() const { return mKeyAxis.data(); }
+  QCPPolarAxisRadial *valueAxis() const { return mValueAxis.data(); }
+  QCP::SelectionType selectable() const { return mSelectable; }
+  bool selected() const { return !mSelection.isEmpty(); }
+  QCPDataSelection selection() const { return mSelection; }
+  //QCPSelectionDecorator *selectionDecorator() const { return mSelectionDecorator; }
+  QSharedPointer<QCPGraphDataContainer> data() const { return mDataContainer; }
+  LineStyle lineStyle() const { return mLineStyle; }
+  QCPScatterStyle scatterStyle() const { return mScatterStyle; }
+  
+  // setters:
+  void setName(const QString &name);
+  void setAntialiasedFill(bool enabled);
+  void setAntialiasedScatters(bool enabled);
+  void setPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setPeriodic(bool enabled);
+  void setKeyAxis(QCPPolarAxisAngular *axis);
+  void setValueAxis(QCPPolarAxisRadial *axis);
+  Q_SLOT void setSelectable(QCP::SelectionType selectable);
+  Q_SLOT void setSelection(QCPDataSelection selection);
+  //void setSelectionDecorator(QCPSelectionDecorator *decorator);
+  void setData(QSharedPointer<QCPGraphDataContainer> data);
+  void setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);
+  void setLineStyle(LineStyle ls);
+  void setScatterStyle(const QCPScatterStyle &style);
+
+  // non-property methods:
+  void addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);
+  void addData(double key, double value);
+  void coordsToPixels(double key, double value, double &x, double &y) const;
+  const QPointF coordsToPixels(double key, double value) const;
+  void pixelsToCoords(double x, double y, double &key, double &value) const;
+  void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const;
+  void rescaleAxes(bool onlyEnlarge=false) const;
+  void rescaleKeyAxis(bool onlyEnlarge=false) const;
+  void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const;
+  bool addToLegend(QCPLegend *legend);
+  bool addToLegend();
+  bool removeFromLegend(QCPLegend *legend) const;
+  bool removeFromLegend() const;
+  
+  // introduced virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables
+  virtual QCPPlottableInterface1D *interface1D() { return 0; } // TODO: return this later, when QCPAbstractPolarPlottable is created
+  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const;
+  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const;
+  
+signals:
+  void selectionChanged(bool selected);
+  void selectionChanged(const QCPDataSelection &selection);
+  void selectableChanged(QCP::SelectionType selectable);
+  
+protected:
+  // property members:
+  QSharedPointer<QCPGraphDataContainer> mDataContainer;
+  LineStyle mLineStyle;
+  QCPScatterStyle mScatterStyle;
+  QString mName;
+  bool mAntialiasedFill, mAntialiasedScatters;
+  QPen mPen;
+  QBrush mBrush;
+  bool mPeriodic;
+  QPointer<QCPPolarAxisAngular> mKeyAxis;
+  QPointer<QCPPolarAxisRadial> mValueAxis;
+  QCP::SelectionType mSelectable;
+  QCPDataSelection mSelection;
+  //QCPSelectionDecorator *mSelectionDecorator;
+  
+  // introduced virtual methods (later reimplemented TODO from QCPAbstractPolarPlottable):
+  virtual QRect clipRect() const;
+  virtual void draw(QCPPainter *painter);
+  virtual QCP::Interaction selectionCategory() const;
+  void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  // events:
+  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
+  virtual void deselectEvent(bool *selectionStateChanged);
+  // virtual drawing helpers:
+  virtual void drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const;
+  virtual void drawFill(QCPPainter *painter, QVector<QPointF> *lines) const;
+  virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const;
+  
+  // introduced virtual methods:
+  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
+  
+  // non-virtual methods:
+  void applyFillAntialiasingHint(QCPPainter *painter) const;
+  void applyScattersAntialiasingHint(QCPPainter *painter) const;
+  double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const;
+  // drawing helpers:
+  virtual int dataCount() const;
+  void getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const;
+  void drawPolyline(QCPPainter *painter, const QVector<QPointF> &lineData) const;
+  void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const;
+  void getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const;
+  void getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const;
+  void getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const;
+  void getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const;
+  QVector<QPointF> dataToLines(const QVector<QCPGraphData> &data) const;
+
+private:
+  Q_DISABLE_COPY(QCPPolarGraph)
+  
+  friend class QCPPolarLegendItem;
+};
+
+/* end of 'src/polar/polargraph.h' */
+
+
 #endif // QCUSTOMPLOT_H
 
diff --git a/Modules/Graph/graphmodule.cpp b/Modules/Graph/graphmodule.cpp
index f8f8feecd6d9695e2aea3ce4b8f6615bd6a5fedf..e92d80cbfb4235d67d923688901f8f2c36ed6cf4 100644
--- a/Modules/Graph/graphmodule.cpp
+++ b/Modules/Graph/graphmodule.cpp
@@ -1,68 +1,81 @@
 #include "graphmodule.h"
-#include "ui_graphmodule.h"
+
+#include <QDebug>
+#include <QTimer>
 
 #include "Components/ContextMenuSeparator/contextmenuseparator.h"
 #include "Components/SubscriptionsPanel/subscriptionspanel.h"
 #include "Core/modulemessagesbroker.h"
-#include <QDebug>
-#include <QTimer>
+#include "ui_graphmodule.h"
 
-GraphModule::GraphModule(QWidget* parent) : DefaultModule(parent), ui(new Ui::GraphModule) {
+GraphModule::GraphModule(QWidget* parent)
+    : DefaultModule(parent), ui(new Ui::GraphModule)
+{
     ui->setupUi(this);
     defaultContextMenuSetup();
     buildCentralGraphView();
     ui->centralLayout->addWidget(&graphCentralView);
-    connect(&updaterTimer, &QTimer::timeout, this, &GraphModule::onUpdateTimerTick);
+    connect(&updaterTimer, &QTimer::timeout, this,
+            &GraphModule::onUpdateTimerTick);
     updaterTimer.setSingleShot(false);
     updaterTimer.start(updatePeriod);
 
-    getCore()->getModuleMessagesBroker()->subscribe({"*"}, this, [this](const ModuleMessage & msg) {
-        onMsgReceived(msg);
-    });
+    getCore()->getModuleMessagesBroker()->subscribe(
+        {"*"}, this, [this](const ModuleMessage& msg) { onMsgReceived(msg); });
 }
 
-GraphModule::~GraphModule() {
+GraphModule::~GraphModule()
+{
     // QCPGraph are destroyed automatically
     delete ui;
 }
 
-void GraphModule::buildCentralGraphView() {
+void GraphModule::buildCentralGraphView()
+{
     setTheme();
 
     graphCentralView.yAxis2->setVisible(true);
     graphCentralView.yAxis2->setTicks(false);
     graphCentralView.yAxis2->setTickLabels(false);
 
-    //graphCentralView.xAxis2->setVisible(true);
-    //textTicker = QSharedPointer<QCPAxisTickerText>(new QCPAxisTickerText());
-    //cPlot.xAxis2->setTicker(textTicker);
-    //graphCentralView.xAxis2->setTickLabels(true);
+    // graphCentralView.xAxis2->setVisible(true);
+    // textTicker = QSharedPointer<QCPAxisTickerText>(new QCPAxisTickerText());
+    // cPlot.xAxis2->setTicker(textTicker);
+    // graphCentralView.xAxis2->setTickLabels(true);
 
-//    QSharedPointer<QCPAxisTickerTime> dateTimeTicker(new QCPAxisTickerTime);
-//    graphCentralView.xAxis->setTicker(dateTimeTicker);
-    //QDateTime rangeLowerBound = QDateTime::currentDateTime().addDays(-2);
+    //    QSharedPointer<QCPAxisTickerTime> dateTimeTicker(new
+    //    QCPAxisTickerTime); graphCentralView.xAxis->setTicker(dateTimeTicker);
+    // QDateTime rangeLowerBound = QDateTime::currentDateTime().addDays(-2);
     //    double now = QDateTime::currentDateTime().toTime_t();
     //    QDateTime rangeUpperBound = QDateTime::currentDateTime().addDays(+2);
-    //    graphCentralView.xAxis->setRange(now, QCPAxisTickerDateTime::(rangeUpperBound));
-//    dateTimeTicker->setTimeFormat(dateFormat);
+    //    graphCentralView.xAxis->setRange(now,
+    //    QCPAxisTickerDateTime::(rangeUpperBound));
+    //    dateTimeTicker->setTimeFormat(dateFormat);
 
     graphCentralView.legend->setVisible(true);
 
-    // make left and bottom axes always transfer their ranges to right and top axes:
-    connect(graphCentralView.xAxis, SIGNAL(rangeChanged(QCPRange)), graphCentralView.xAxis2, SLOT(setRange(QCPRange)));
-    connect(graphCentralView.yAxis, SIGNAL(rangeChanged(QCPRange)), graphCentralView.yAxis2, SLOT(setRange(QCPRange)));
+    // make left and bottom axes always transfer their ranges to right and top
+    // axes:
+    connect(graphCentralView.xAxis, SIGNAL(rangeChanged(QCPRange)),
+            graphCentralView.xAxis2, SLOT(setRange(QCPRange)));
+    connect(graphCentralView.yAxis, SIGNAL(rangeChanged(QCPRange)),
+            graphCentralView.yAxis2, SLOT(setRange(QCPRange)));
 
-    // Allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking:
-    graphCentralView.setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
+    // Allow user to drag axis ranges with mouse, zoom with mouse wheel and
+    // select graphs by clicking:
+    graphCentralView.setInteractions(QCP::iRangeDrag | QCP::iRangeZoom |
+                                     QCP::iSelectPlottables);
 
     // Enable context menu
     graphCentralView.setContextMenuPolicy(Qt::CustomContextMenu);
-    //connect(&graphCentralView,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(onCustomContextMenuRequested(QPoint)));
-    connect(&graphCentralView, &QCustomPlot::customContextMenuRequested, this, &GraphModule::onCustomContextMenuRequested);
-    //connect(customPlot,SIGNAL(selectionChangedByUser()),this,SLOT(selectedGraphChange()));
+    // connect(&graphCentralView,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(onCustomContextMenuRequested(QPoint)));
+    connect(&graphCentralView, &QCustomPlot::customContextMenuRequested, this,
+            &GraphModule::onCustomContextMenuRequested);
+    // connect(customPlot,SIGNAL(selectionChangedByUser()),this,SLOT(selectedGraphChange()));
 }
 
-void GraphModule::setTheme() {
+void GraphModule::setTheme()
+{
     //    QColor accentColor;
     //    QColor themeColor;
     //    QColor themeBackgroundColor;
@@ -94,29 +107,27 @@ void GraphModule::setTheme() {
     //    replotAll();
 }
 
-void GraphModule::onUpdateTimerTick() {
-    replotAll();
-}
+void GraphModule::onUpdateTimerTick() { replotAll(); }
 
-QCPGraph* GraphModule::instantiateNewGraph() {
+QCPGraph* GraphModule::instantiateNewGraph()
+{
     QCPGraph* graph = graphCentralView.addGraph();
-    int r = rand() % 255;
-    int g = rand() % 255;
-    int b = rand() % 255;
+    int r           = rand() % 255;
+    int g           = rand() % 255;
+    int b           = rand() % 255;
     graph->setPen(QPen(QColor(r, g, b), 2));
     graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3));
     graph->setSelectable(QCP::stSingleData);
     return graph;
 }
 
+QWidget* GraphModule::toWidget() { return this; }
 
-QWidget* GraphModule::toWidget() {
-    return this;
-}
-
-XmlObject GraphModule::toXmlObject() {
+XmlObject GraphModule::toXmlObject()
+{
     XmlObject obj(getName(ModuleId::GRAPH));
-    for(int i = 0; i < subsFilters.count(); i++) {
+    for (int i = 0; i < subsFilters.count(); i++)
+    {
         XmlObject subscription("Subscription");
         subscription.addAttribute("Value", subsFilters[i].toString());
         obj.addChild(subscription);
@@ -124,141 +135,177 @@ XmlObject GraphModule::toXmlObject() {
     return obj;
 }
 
-void GraphModule::fromXmlObject(const XmlObject& xmlObject) {
-    if(xmlObject.getObjectName() == getName(ModuleId::GRAPH)) {
-        for(int i = 0; i < xmlObject.childCount(); i++) {
+void GraphModule::fromXmlObject(const XmlObject& xmlObject)
+{
+    if (xmlObject.getObjectName() == getName(ModuleId::GRAPH))
+    {
+        for (int i = 0; i < xmlObject.childCount(); i++)
+        {
             XmlObject child = xmlObject.childAt(i);
-            if(child.getObjectName() == "Subscription") {
-                auto subscription = TopicAndFieldFilter::fromStringUnsafe(child.getAttribute("Value"));
+            if (child.getObjectName() == "Subscription")
+            {
+                auto subscription = TopicAndFieldFilter::fromStringUnsafe(
+                    child.getAttribute("Value"));
                 onSubscriptionAdded(subscription);
             }
         }
     }
 }
 
-void GraphModule::onSubscribeClicked() {
+void GraphModule::onSubscribeClicked()
+{
     SubscriptionsPanel* sPanel = new SubscriptionsPanel(&subsFilters);
     sPanel->setWindowTitle("Graph Subscriptions");
-    connect(sPanel, &SubscriptionsPanel::SubscriptionAdded, this, &GraphModule::onSubscriptionAdded);
-    connect(sPanel, &SubscriptionsPanel::SubscriptionRemoved, this, &GraphModule::onSubscriptionRemoved);
+    connect(sPanel, &SubscriptionsPanel::SubscriptionAdded, this,
+            &GraphModule::onSubscriptionAdded);
+    connect(sPanel, &SubscriptionsPanel::SubscriptionRemoved, this,
+            &GraphModule::onSubscriptionRemoved);
     sPanel->show();
 }
 
-
-void GraphModule::onSubscriptionAdded(const TopicAndFieldFilter& filter) {
+void GraphModule::onSubscriptionAdded(const TopicAndFieldFilter& filter)
+{
 
     QCPGraph* graph = instantiateNewGraph();
     graph->setName(filter.toString());
     subsFilters.append(filter);
     subsGraphs.append(graph);
 
-    //Da rimuovere per performance
+    // Da rimuovere per performance
     graphCentralView.replot();
 }
 
-void GraphModule::onSubscriptionRemoved(const TopicAndFieldFilter& filter) {
+void GraphModule::onSubscriptionRemoved(const TopicAndFieldFilter& filter)
+{
     int i = subsFilters.indexOf(filter);
 
-    if(i != -1) {
+    if (i != -1)
+    {
         subsFilters.removeAt(i);
         graphCentralView.removeGraph(subsGraphs[i]);
         subsGraphs.removeAt(i);
     }
 
-    //Da rimuovere per performance
+    // Da rimuovere per performance
     graphCentralView.replot();
 }
 
-void GraphModule::onMsgReceived(const ModuleMessage& msg) {
-    if(stopPlot)
+void GraphModule::onMsgReceived(const ModuleMessage& msg)
+{
+    if (stopPlot)
         return;
 
     MessageField value;
-    for(int i = 0; i < subsFilters.size(); i++) {
-        if(subsFilters[i].matchMessage(msg, value)) {
+    for (int i = 0; i < subsFilters.size(); i++)
+    {
+        if (subsFilters[i].matchMessage(msg, value))
+        {
             QCPGraph* graph = subsGraphs[i];
 
             double x = 0, y = 0;
             x = msg.getField("timestamp").getUInteger(0) / 1e6;
             y = value.getDouble(0.0);
-            if(graph != nullptr) {
+            if (graph != nullptr)
+            {
                 graph->addData(x, y);
-                //Da rimuovere per performance
-                //replotAll();
+                // Da rimuovere per performance
+                // replotAll();
             }
         }
     }
 }
 
-void GraphModule::setFollowedGraphIndex(bool checked) {
-    if(checked) {
+void GraphModule::setFollowedGraphIndex(bool checked)
+{
+    if (checked)
+    {
         QCPGraph* selectedGraph = getSelectedGraph();
 
-        if(selectedGraph) {
+        if (selectedGraph)
+        {
 
-            for(int i = 0; i < graphCentralView.graphCount(); i++) {
-                if(selectedGraph == graphCentralView.graph(i))
+            for (int i = 0; i < graphCentralView.graphCount(); i++)
+            {
+                if (selectedGraph == graphCentralView.graph(i))
                     followedGraphIndex = i;
             }
-        } else if(graphCentralView.graphCount() > 0) {
+        }
+        else if (graphCentralView.graphCount() > 0)
+        {
             // If no graph is selected, select the first graph
             followedGraphIndex = 0;
         }
-    } else {
+    }
+    else
+    {
         followedGraphIndex = -1;
     }
 }
 
-void GraphModule::onClearClicked() {
-    for(QCPGraph* g : subsGraphs) {
+void GraphModule::onClearClicked()
+{
+    for (QCPGraph* g : subsGraphs)
+    {
         g->data()->clear();
     }
 
     replotAll();
 }
 
-void GraphModule::onStopClicked(bool checked) {
+void GraphModule::onStopClicked(bool checked)
+{
     stopPlot = checked;
 
-    if(stopPlot)
+    if (stopPlot)
         updaterTimer.stop();
-    else {
+    else
+    {
         updaterTimer.start(updatePeriod);
     }
 }
 
-
-QCPGraph* GraphModule::getSelectedGraph() {
+QCPGraph* GraphModule::getSelectedGraph()
+{
     QList<QCPGraph*> selection;
-    //if(graph)
+    // if(graph)
     selection = graphCentralView.selectedGraphs();
-    if(selection.size() > 0) {
+    if (selection.size() > 0)
+    {
         return selection.first();
     }
     return nullptr;
 }
 
-void GraphModule::centerView(const QCPGraph* graph) {
-    if(!graph->data()->isEmpty()) {
-        double lastKey = (graph->data()->constEnd() - 1)->key;
+void GraphModule::centerView(const QCPGraph* graph)
+{
+    if (!graph->data()->isEmpty())
+    {
+        double lastKey   = (graph->data()->constEnd() - 1)->key;
         double lastValue = (graph->data()->constEnd() - 1)->value;
-        double size_x = graphCentralView.xAxis->range().size();
-        double size_y = graphCentralView.yAxis->range().size();
-        graphCentralView.xAxis->setRange(lastKey, size_x, Qt::AlignmentFlag::AlignRight);
-        graphCentralView.yAxis->setRange(lastValue, size_y, Qt::AlignmentFlag::AlignCenter);
+        double size_x    = graphCentralView.xAxis->range().size();
+        double size_y    = graphCentralView.yAxis->range().size();
+        graphCentralView.xAxis->setRange(lastKey, size_x,
+                                         Qt::AlignmentFlag::AlignRight);
+        graphCentralView.yAxis->setRange(lastValue, size_y,
+                                         Qt::AlignmentFlag::AlignCenter);
     }
 }
 
-void GraphModule::replotAll() {
-    if(followedGraphIndex < graphCentralView.graphCount() && followedGraphIndex >= 0) {
+void GraphModule::replotAll()
+{
+    if (followedGraphIndex < graphCentralView.graphCount() &&
+        followedGraphIndex >= 0)
+    {
         centerView(graphCentralView.graph(followedGraphIndex));
     }
     graphCentralView.replot();
 }
 
-void GraphModule::addCustomActionsToMenu() {
+void GraphModule::addCustomActionsToMenu()
+{
     QAction* subscribe = new QAction("Subscribe");
-    connect(subscribe, &QAction::triggered, this, &GraphModule::onSubscribeClicked);
+    connect(subscribe, &QAction::triggered, this,
+            &GraphModule::onSubscribeClicked);
 
     QAction* clear = new QAction("Clear");
     connect(clear, &QAction::triggered, this, &GraphModule::onClearClicked);
@@ -270,14 +317,15 @@ void GraphModule::addCustomActionsToMenu() {
 
     QAction* follow = new QAction("Follow");
     follow->setCheckable(true);
-    if(followedGraphIndex >= 0) {
+    if (followedGraphIndex >= 0)
+    {
         follow->setChecked(true);
     }
-    connect(follow, &QAction::triggered, this, &GraphModule::setFollowedGraphIndex);
+    connect(follow, &QAction::triggered, this,
+            &GraphModule::setFollowedGraphIndex);
 
     addActionToMenu(follow);
     addActionToMenu(subscribe);
     addActionToMenu(clear);
     addActionToMenu(stop);
 }
-
diff --git a/Modules/Graph/graphmodule.h b/Modules/Graph/graphmodule.h
index 5327236f368a4fc4a17295a619ce6f5972ccb899..773e781252d418f4befaf23d8425af6c1bfe57a5 100644
--- a/Modules/Graph/graphmodule.h
+++ b/Modules/Graph/graphmodule.h
@@ -1,25 +1,27 @@
 #ifndef GRAPHMODULE_H
 #define GRAPHMODULE_H
 
-#include <QWidget>
+#include <Core/QCustomPlot/QCustomPlot.h>
+
 #include <QList>
 #include <QTimer>
-#include "qcustomplot.h"
+#include <QWidget>
 
-#include "Core/module.h"
 #include "Core/Message/modulemessage.h"
 #include "Core/Message/topicandfieldfilter.h"
+#include "Core/module.h"
 #include "Modules/DefaultModule/defaultmodule.h"
 
-
-namespace Ui {
+namespace Ui
+{
 class GraphModule;
 }
 
-class GraphModule : public DefaultModule {
+class GraphModule : public DefaultModule
+{
     Q_OBJECT
 
-  public:
+public:
     explicit GraphModule(QWidget* parent = nullptr);
     ~GraphModule();
 
@@ -30,7 +32,7 @@ class GraphModule : public DefaultModule {
     void centerView(const QCPGraph* graph);
     void replotAll();
 
-  public slots:
+public slots:
     void onSubscribeClicked();
     void onSubscriptionAdded(const TopicAndFieldFilter& filter);
     void onSubscriptionRemoved(const TopicAndFieldFilter& filter);
@@ -39,7 +41,7 @@ class GraphModule : public DefaultModule {
     void onClearClicked();
     void onStopClicked(bool checked);
 
-  protected:
+protected:
     void buildCentralGraphView();
     QCPGraph* instantiateNewGraph();
     void addCustomActionsToMenu() override;
@@ -47,7 +49,7 @@ class GraphModule : public DefaultModule {
     void setTheme();
     void onUpdateTimerTick();
 
-  private:
+private:
     Ui::GraphModule* ui;
     QCustomPlot graphCentralView;
 
@@ -55,11 +57,11 @@ class GraphModule : public DefaultModule {
     QList<QCPGraph*> subsGraphs;
 
     int followedGraphIndex = -1;
-    QString dateFormat = "%h:%m:%s (%z)"; //"HH:mm:ss\n(zzz)";
-    //QSharedPointer<QCPAxisTickerDateTime> dateTicker;
+    QString dateFormat     = "%h:%m:%s (%z)";  //"HH:mm:ss\n(zzz)";
+    // QSharedPointer<QCPAxisTickerDateTime> dateTicker;
     QTimer updaterTimer;
-    int updatePeriod = 1000; // [ms]
-    bool stopPlot = false;
+    int updatePeriod = 1000;  // [ms]
+    bool stopPlot    = false;
 };
 
-#endif // GRAPHMODULE_H
+#endif  // GRAPHMODULE_H
diff --git a/SkywardHub.pro b/SkywardHub.pro
index f6e6fcb5b83ca289b0fe35e895bfc7f8900ed07f..fb60c7a1b4b7e824b21ed2a69262ce3529846737 100644
--- a/SkywardHub.pro
+++ b/SkywardHub.pro
@@ -24,6 +24,7 @@ SOURCES += \
     Core/Message/topic.cpp \
     Core/Message/topicandfieldfilter.cpp \
     Core/Message/topicfilter.cpp \
+    Core/QCustomPlot/QCustomPlot.cpp \
     Core/module.cpp \
     Core/moduleeventshandler.cpp \
     Core/modulemessagesbroker.cpp \
@@ -38,7 +39,6 @@ SOURCES += \
     Modules/Empty/emptymodule.cpp \
     Modules/FileStream/filestreammodule.cpp \
     Modules/Graph/graphmodule.cpp \
-    Modules/Graph/qcustomplot.cpp \
     Modules/IncomingMessagesViewer/incomingmessagesviewermodule.cpp \
     Modules/MainWindow/skywardhubmainwindow.cpp \
     Modules/MainWindow/window.cpp \
@@ -80,6 +80,7 @@ HEADERS += \
     Core/Message/topic.h \
     Core/Message/topicandfieldfilter.h \
     Core/Message/topicfilter.h \
+    Core/QCustomPlot/QCustomPlot.h \
     Core/module.h \
     Core/moduleeventshandler.h \
     Core/modulemessagesbroker.h \
@@ -95,7 +96,6 @@ HEADERS += \
     Modules/Empty/emptymodule.h \
     Modules/FileStream/filestreammodule.h \
     Modules/Graph/graphmodule.h \
-    Modules/Graph/qcustomplot.h \
     Modules/IncomingMessagesViewer/incomingmessagesviewermodule.h \
     Modules/MainWindow/skywardhubmainwindow.h \
     Modules/MainWindow/window.h \