diff --git a/.gitignore b/.gitignore index b14b47d3..a8be0a21 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,7 @@ Desktop_Interface/build_android/package_source/local.properties Desktop_Interface/build_android/package_source/local.properties~ Desktop_Interface/qrc_resources.cpp AVR Code/USB_BULK_TEST/Debug -AVR Code/USB_BULK_TEST/Release \ No newline at end of file +AVR Code/USB_BULK_TEST/Release +Desktop_Interface/debug/Labrador.vc.pdb +Desktop_Interface/debug/Labrador_resource.res +Desktop_Interface/debug/qrc_resources.cpp diff --git a/Desktop_Interface/Labrador.pro b/Desktop_Interface/Labrador.pro index bb8e9ee1..36328ac4 100644 --- a/Desktop_Interface/Labrador.pro +++ b/Desktop_Interface/Labrador.pro @@ -20,6 +20,7 @@ QCP_VER = 1 DEFINES += "QCP_VER=$${QCP_VER}" equals(QCP_VER,"2"){ DEFINES += QCUSTOMPLOT_USE_OPENGL + LIBS += -lOpenGL32 message("Using QCP2 with OpenGL support") } diff --git a/Desktop_Interface/ui_elements/qcp2/qcustomplot.cpp b/Desktop_Interface/ui_elements/qcp2/qcustomplot.cpp index e55d10a3..154576a6 100644 --- a/Desktop_Interface/ui_elements/qcp2/qcustomplot.cpp +++ b/Desktop_Interface/ui_elements/qcp2/qcustomplot.cpp @@ -1,7 +1,7 @@ /*************************************************************************** ** ** ** QCustomPlot, an easy to use, modern plotting widget for Qt ** -** Copyright (C) 2011-2016 Emanuel Eichhammer ** +** Copyright (C) 2011-2017 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: 13.09.16 ** -** Version: 2.0.0-beta ** +** Date: 04.09.17 ** +** Version: 2.0.0 ** ****************************************************************************/ #include "qcustomplot.h" /* including file 'src/vector2d.cpp', size 7340 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPVector2D @@ -260,7 +260,7 @@ QCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector) /* including file 'src/painter.cpp', size 8670 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPPainter @@ -478,7 +478,7 @@ void QCPPainter::makeNonCosmetic() /* including file 'src/paintbuffer.cpp', size 18502 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAbstractPaintBuffer @@ -833,7 +833,6 @@ QCPPaintBufferGlFbo::QCPPaintBufferGlFbo(const QSize &size, double devicePixelRa mGlPaintDevice(glPaintDevice), mGlFrameBuffer(0) { - initializeOpenGLFunctions(); QCPPaintBufferGlFbo::reallocateBuffer(); } @@ -952,7 +951,7 @@ void QCPPaintBufferGlFbo::reallocateBuffer() /* including file 'src/layer.cpp', size 37064 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPLayer @@ -1805,7 +1804,7 @@ void QCPLayerable::wheelEvent(QWheelEvent *event) /* including file 'src/axis/range.cpp', size 12221 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPRange @@ -2126,8 +2125,8 @@ bool QCPRange::validRange(const QCPRange &range) /* end of 'src/axis/range.cpp' */ -/* including file 'src/selection.cpp', size 21898 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/selection.cpp', size 21906 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPDataRange @@ -2434,7 +2433,7 @@ QCPDataSelection &QCPDataSelection::operator+=(const QCPDataRange &other) } /*! - Removes all data point indices that are described by \a other from this data range. + Removes all data point indices that are described by \a other from this data selection. */ QCPDataSelection &QCPDataSelection::operator-=(const QCPDataSelection &other) { @@ -2445,7 +2444,7 @@ QCPDataSelection &QCPDataSelection::operator-=(const QCPDataSelection &other) } /*! - Removes all data point indices that are described by \a other from this data range. + Removes all data point indices that are described by \a other from this data selection. */ QCPDataSelection &QCPDataSelection::operator-=(const QCPDataRange &other) { @@ -2727,7 +2726,7 @@ QCPDataSelection QCPDataSelection::inverse(const QCPDataRange &outerRange) const /* including file 'src/selectionrect.cpp', size 9224 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionRect @@ -2955,8 +2954,8 @@ void QCPSelectionRect::draw(QCPPainter *painter) /* end of 'src/selectionrect.cpp' */ -/* including file 'src/layout.cpp', size 74302 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layout.cpp', size 79064 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPMarginGroup @@ -3146,7 +3145,7 @@ void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element /*! \fn QRect QCPLayoutElement::rect() const - Returns the inner rect of this layout element. The inner rect is the outer rect (\ref + Returns the inner rect of this layout element. The inner rect is the outer rect (\ref outerRect, \ref setOuterRect) shrinked by the margins (\ref setMargins, \ref setAutoMargins). In some cases, the area between outer and inner rect is left blank. In other cases the margin @@ -3155,6 +3154,17 @@ void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element adapt the margins to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if \ref setAutoMargins is enabled) according to the space required by the labels of the axes. + + \see outerRect +*/ + +/*! \fn QRect QCPLayoutElement::outerRect() const + + Returns the outer rect of this layout element. The outer rect is the inner rect expanded by the + margins (\ref setMargins, \ref setAutoMargins). The outer rect is used (and set via \ref + setOuterRect) by the parent \ref QCPLayout to control the size of this layout element. + + \see rect */ /* end documentation of inline functions */ @@ -3167,6 +3177,7 @@ QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) : mParentLayout(0), mMinimumSize(), mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), + mSizeConstraintRect(scrInnerRect), mRect(0, 0, 0, 0), mOuterRect(0, 0, 0, 0), mMargins(0, 0, 0, 0), @@ -3256,13 +3267,16 @@ void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides) } /*! - Sets the minimum size for the inner \ref rect of this layout element. A parent layout tries to - respect the \a size here by changing row/column sizes in the layout accordingly. + Sets the minimum size of this layout element. A parent layout tries to respect the \a size here + by changing row/column sizes in the layout accordingly. If the parent layout size is not sufficient to satisfy all minimum size constraints of its child layout elements, the layout may set a size that is actually smaller than \a size. QCustomPlot propagates the layout's size constraints to the outside by setting its own minimum QWidget size accordingly, so violations of \a size should be exceptions. + + Whether this constraint applies to the inner or the outer rect can be specified with \ref + setSizeConstraintRect (see \ref rect and \ref outerRect). */ void QCPLayoutElement::setMinimumSize(const QSize &size) { @@ -3276,7 +3290,10 @@ void QCPLayoutElement::setMinimumSize(const QSize &size) /*! \overload - Sets the minimum size for the inner \ref rect of this layout element. + Sets the minimum size of this layout element. + + Whether this constraint applies to the inner or the outer rect can be specified with \ref + setSizeConstraintRect (see \ref rect and \ref outerRect). */ void QCPLayoutElement::setMinimumSize(int width, int height) { @@ -3284,8 +3301,11 @@ void QCPLayoutElement::setMinimumSize(int width, int height) } /*! - Sets the maximum size for the inner \ref rect of this layout element. A parent layout tries to - respect the \a size here by changing row/column sizes in the layout accordingly. + Sets the maximum size of this layout element. A parent layout tries to respect the \a size here + by changing row/column sizes in the layout accordingly. + + Whether this constraint applies to the inner or the outer rect can be specified with \ref + setSizeConstraintRect (see \ref rect and \ref outerRect). */ void QCPLayoutElement::setMaximumSize(const QSize &size) { @@ -3299,13 +3319,35 @@ void QCPLayoutElement::setMaximumSize(const QSize &size) /*! \overload - Sets the maximum size for the inner \ref rect of this layout element. + Sets the maximum size of this layout element. + + Whether this constraint applies to the inner or the outer rect can be specified with \ref + setSizeConstraintRect (see \ref rect and \ref outerRect). */ void QCPLayoutElement::setMaximumSize(int width, int height) { setMaximumSize(QSize(width, height)); } +/*! + Sets to which rect of a layout element the size constraints apply. Size constraints can be set + via \ref setMinimumSize and \ref setMaximumSize. + + The outer rect (\ref outerRect) includes the margins (e.g. in the case of a QCPAxisRect the axis + labels), whereas the inner rect (\ref rect) does not. + + \see setMinimumSize, setMaximumSize +*/ +void QCPLayoutElement::setSizeConstraintRect(SizeConstraintRect constraintRect) +{ + if (mSizeConstraintRect != constraintRect) + { + mSizeConstraintRect = constraintRect; + if (mParentLayout) + mParentLayout->sizeConstraintsChanged(); + } +} + /*! Sets the margin \a group of the specified margin \a sides. @@ -3388,27 +3430,41 @@ void QCPLayoutElement::update(UpdatePhase phase) } /*! - Returns the minimum size this layout element (the inner \ref rect) may be compressed to. + Returns the suggested minimum size this layout element (the \ref outerRect) may be compressed to, + if no manual minimum size is set. - if a minimum size (\ref setMinimumSize) was not set manually, parent layouts consult this - function to determine the minimum allowed size of this layout element. (A manual minimum size is - considered set if it is non-zero.) + if a minimum size (\ref setMinimumSize) was not set manually, parent layouts use the returned size + (usually indirectly through \ref QCPLayout::getFinalMinimumOuterSize) to determine the minimum + allowed size of this layout element. + + A manual minimum size is considered set if it is non-zero. + + The default implementation simply returns the sum of the horizontal margins for the width and the + sum of the vertical margins for the height. Reimplementations may use their detailed knowledge + about the layout element's content to provide size hints. */ -QSize QCPLayoutElement::minimumSizeHint() const +QSize QCPLayoutElement::minimumOuterSizeHint() const { - return mMinimumSize; + return QSize(mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom()); } /*! - Returns the maximum size this layout element (the inner \ref rect) may be expanded to. + Returns the suggested maximum size this layout element (the \ref outerRect) may be expanded to, + if no manual maximum size is set. - if a maximum size (\ref setMaximumSize) was not set manually, parent layouts consult this - function to determine the maximum allowed size of this layout element. (A manual maximum size is - considered set if it is smaller than Qt's QWIDGETSIZE_MAX.) + if a maximum size (\ref setMaximumSize) was not set manually, parent layouts use the returned + size (usually indirectly through \ref QCPLayout::getFinalMaximumOuterSize) to determine the + maximum allowed size of this layout element. + + A manual maximum size is considered set if it is smaller than Qt's \c QWIDGETSIZE_MAX. + + The default implementation simply returns \c QWIDGETSIZE_MAX for both width and height, implying + no suggested maximum size. Reimplementations may use their detailed knowledge about the layout + element's content to provide size hints. */ -QSize QCPLayoutElement::maximumSizeHint() const +QSize QCPLayoutElement::maximumOuterSizeHint() const { - return mMaximumSize; + return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); } /*! @@ -3580,12 +3636,13 @@ QCPLayout::QCPLayout() } /*! - First calls the QCPLayoutElement::update base class implementation to update the margins on this - layout. + If \a phase is \ref upLayout, calls \ref updateLayout, which subclasses may reimplement to + reposition and resize their cells. - Then calls \ref updateLayout which subclasses reimplement to reposition and resize their cells. + Finally, the call is propagated down to all child \ref QCPLayoutElement "QCPLayoutElements". - Finally, \ref update is called on all child elements. + For details about this method and the update phases, see the documentation of \ref + QCPLayoutElement::update. */ void QCPLayout::update(UpdatePhase phase) { @@ -3917,6 +3974,56 @@ QVector QCPLayout::getSectionSizes(QVector maxSizes, QVector minS return result; } +/*! \internal + + This is a helper function for the implementation of subclasses. + + It returns the minimum size that should finally be used for the outer rect of the passed layout + element \a el. + + It takes into account whether a manual minimum size is set (\ref + QCPLayoutElement::setMinimumSize), which size constraint is set (\ref + QCPLayoutElement::setSizeConstraintRect), as well as the minimum size hint, if no manual minimum + size was set (\ref QCPLayoutElement::minimumOuterSizeHint). +*/ +QSize QCPLayout::getFinalMinimumOuterSize(const QCPLayoutElement *el) +{ + QSize minOuterHint = el->minimumOuterSizeHint(); + QSize minOuter = el->minimumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset minimum of 0) + if (minOuter.width() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect) + minOuter.rwidth() += el->margins().left() + el->margins().right(); + 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());; +} + +/*! \internal + + This is a helper function for the implementation of subclasses. + + It returns the maximum size that should finally be used for the outer rect of the passed layout + element \a el. + + It takes into account whether a manual maximum size is set (\ref + QCPLayoutElement::setMaximumSize), which size constraint is set (\ref + QCPLayoutElement::setSizeConstraintRect), as well as the maximum size hint, if no manual maximum + size was set (\ref QCPLayoutElement::maximumOuterSizeHint). +*/ +QSize QCPLayout::getFinalMaximumOuterSize(const QCPLayoutElement *el) +{ + QSize maxOuterHint = el->maximumOuterSizeHint(); + QSize maxOuter = el->maximumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset maximum of QWIDGETSIZE_MAX) + if (maxOuter.width() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect) + maxOuter.rwidth() += el->margins().left() + el->margins().right(); + 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()); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPLayoutGrid @@ -4092,8 +4199,9 @@ bool QCPLayoutGrid::hasElement(int row, int column) Sets the stretch \a factor of \a column. Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond - their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref - QCPLayoutElement::setMaximumSize), regardless of the stretch factor. + their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref + QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref + QCPLayoutElement::setSizeConstraintRect.) The default stretch factor of newly created rows/columns is 1. @@ -4115,8 +4223,9 @@ void QCPLayoutGrid::setColumnStretchFactor(int column, double factor) Sets the stretch \a factors of all columns. \a factors must have the size \ref columnCount. Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond - their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref - QCPLayoutElement::setMaximumSize), regardless of the stretch factor. + their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref + QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref + QCPLayoutElement::setSizeConstraintRect.) The default stretch factor of newly created rows/columns is 1. @@ -4143,8 +4252,9 @@ void QCPLayoutGrid::setColumnStretchFactors(const QList &factors) Sets the stretch \a factor of \a row. Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond - their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref - QCPLayoutElement::setMaximumSize), regardless of the stretch factor. + their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref + QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref + QCPLayoutElement::setSizeConstraintRect.) The default stretch factor of newly created rows/columns is 1. @@ -4166,8 +4276,9 @@ void QCPLayoutGrid::setRowStretchFactor(int row, double factor) Sets the stretch \a factors of all rows. \a factors must have the size \ref rowCount. Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond - their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref - QCPLayoutElement::setMaximumSize), regardless of the stretch factor. + their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref + QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref + QCPLayoutElement::setSizeConstraintRect.) The default stretch factor of newly created rows/columns is 1. @@ -4243,7 +4354,7 @@ void QCPLayoutGrid::setWrap(int count) 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 - cells are skipped during build-up of the new cell order, which shifts the succeding element's + cells are skipped during build-up of the new cell order, which shifts the succeeding element's index. The rearranging is performed even if the specified \a order is already the current fill order. Thus this method can be used to re-wrap the current elements. @@ -4342,7 +4453,8 @@ void QCPLayoutGrid::insertRow(int newIndex) /*! Inserts a new column with empty cells at the column index \a newIndex. Valid values for \a - newIndex range from 0 (inserts a row at the left) to \a rowCount (appends a row at the right). + newIndex range from 0 (inserts a column at the left) to \a columnCount (appends a column at the + right). \see insertRow */ @@ -4414,7 +4526,9 @@ void QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const { row = -1; column = -1; - if (columnCount() == 0 || rowCount() == 0) + const int nCols = columnCount(); + const int nRows = rowCount(); + if (nCols == 0 || nRows == 0) return; if (index < 0 || index >= elementCount()) { @@ -4426,14 +4540,14 @@ void QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const { case foRowsFirst: { - column = index / rowCount(); - row = index % rowCount(); + column = index / nRows; + row = index % nRows; break; } case foColumnsFirst: { - row = index / columnCount(); - column = index % columnCount(); + row = index / nCols; + column = index % nCols; break; } } @@ -4599,7 +4713,7 @@ void QCPLayoutGrid::simplify() } /* inherits documentation from base class */ -QSize QCPLayoutGrid::minimumSizeHint() const +QSize QCPLayoutGrid::minimumOuterSizeHint() const { QVector minColWidths, minRowHeights; getMinimumRowColSizes(&minColWidths, &minRowHeights); @@ -4608,13 +4722,15 @@ QSize QCPLayoutGrid::minimumSizeHint() const result.rwidth() += minColWidths.at(i); for (int i=0; i maxColWidths, maxRowHeights; getMaximumRowColSizes(&maxColWidths, &maxRowHeights); @@ -4624,8 +4740,14 @@ QSize QCPLayoutGrid::maximumSizeHint() const result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX)); for (int i=0; i QWIDGETSIZE_MAX) + result.setHeight(QWIDGETSIZE_MAX); + if (result.width() > QWIDGETSIZE_MAX) + result.setWidth(QWIDGETSIZE_MAX); return result; } @@ -4634,8 +4756,9 @@ QSize QCPLayoutGrid::maximumSizeHint() const Places the minimum column widths and row heights into \a minColWidths and \a minRowHeights respectively. - The minimum height of a row is the largest minimum height of any element in that row. The minimum - width of a column is the largest minimum width of any element in that column. + The minimum height of a row is the largest minimum height of any element's outer rect in that + row. The minimum width of a column is the largest minimum width of any element's outer rect in + that column. This is a helper function for \ref updateLayout. @@ -4649,15 +4772,13 @@ void QCPLayoutGrid::getMinimumRowColSizes(QVector *minColWidths, QVectorminimumSizeHint(); - QSize min = mElements.at(row).at(col)->minimumSize(); - QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height()); - if (minColWidths->at(col) < final.width()) - (*minColWidths)[col] = final.width(); - if (minRowHeights->at(row) < final.height()) - (*minRowHeights)[row] = final.height(); + QSize minSize = getFinalMinimumOuterSize(el); + if (minColWidths->at(col) < minSize.width()) + (*minColWidths)[col] = minSize.width(); + if (minRowHeights->at(row) < minSize.height()) + (*minRowHeights)[row] = minSize.height(); } } } @@ -4668,8 +4789,9 @@ void QCPLayoutGrid::getMinimumRowColSizes(QVector *minColWidths, QVector *maxColWidths, QVectormaximumSizeHint(); - QSize max = mElements.at(row).at(col)->maximumSize(); - QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height()); - if (maxColWidths->at(col) > final.width()) - (*maxColWidths)[col] = final.width(); - if (maxRowHeights->at(row) > final.height()) - (*maxRowHeights)[row] = final.height(); + QSize maxSize = getFinalMaximumOuterSize(el); + if (maxColWidths->at(col) > maxSize.width()) + (*maxColWidths)[col] = maxSize.width(); + if (maxRowHeights->at(row) > maxSize.height()) + (*maxRowHeights)[row] = maxSize.height(); } } } @@ -4840,14 +4960,10 @@ void QCPLayoutInset::updateLayout() { for (int i=0; iminimumSizeHint(); - QSize maxSizeHint = mElements.at(i)->maximumSizeHint(); - finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width()); - finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height()); - finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width()); - finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height()); + QSize finalMinSize = getFinalMinimumOuterSize(el); + QSize finalMaxSize = getFinalMaximumOuterSize(el); if (mInsetPlacement.at(i) == ipFree) { insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(), @@ -5009,7 +5125,7 @@ void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) /* including file 'src/lineending.cpp', size 11536 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPLineEnding @@ -5308,7 +5424,7 @@ void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double ang /* including file 'src/axis/axisticker.cpp', size 18664 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTicker @@ -5728,7 +5844,7 @@ double QCPAxisTicker::cleanMantissa(double input) const /* including file 'src/axis/axistickerdatetime.cpp', size 14443 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerDateTime @@ -6025,7 +6141,7 @@ double QCPAxisTickerDateTime::dateTimeToKey(const QDate date) /* including file 'src/axis/axistickertime.cpp', size 11747 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerTime @@ -6274,7 +6390,7 @@ void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit u /* including file 'src/axis/axistickerfixed.cpp', size 5583 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerFixed @@ -6376,7 +6492,7 @@ double QCPAxisTickerFixed::getTickStep(const QCPRange &range) /* including file 'src/axis/axistickertext.cpp', size 8653 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerText @@ -6589,7 +6705,7 @@ QVector QCPAxisTickerText::createTickVector(double tickStep, const QCPRa /* including file 'src/axis/axistickerpi.cpp', size 11170 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerPi @@ -6876,7 +6992,7 @@ QString QCPAxisTickerPi::unicodeSubscript(int number) const /* including file 'src/axis/axistickerlog.cpp', size 7106 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerLog @@ -7016,8 +7132,8 @@ QVector QCPAxisTickerLog::createTickVector(double tickStep, const QCPRan /* end of 'src/axis/axistickerlog.cpp' */ -/* including file 'src/axis/axis.cpp', size 94458 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/axis/axis.cpp', size 99397 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -8486,9 +8602,9 @@ double QCPAxis::coordToPixel(double value) const return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left(); } else // mScaleType == stLogarithmic { - if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range + if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200; - else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range + else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200; else { @@ -8508,9 +8624,9 @@ double QCPAxis::coordToPixel(double value) const return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height(); } else // mScaleType == stLogarithmic { - if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range + if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200; - else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range + else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200; else { @@ -8680,6 +8796,137 @@ void QCPAxis::deselectEvent(bool *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 QCPAxis::mousePressEvent(QMouseEvent *event, const QVariant &details) +{ + Q_UNUSED(details) + if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag) || + !mAxisRect->rangeDrag().testFlag(orientation()) || + !mAxisRect->rangeDragAxes(orientation()).contains(this)) + { + 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 QCPAxis::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) +{ + if (mDragging) + { + const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y(); + const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y(); + if (mScaleType == QCPAxis::stLinear) + { + const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel); + setRange(mDragStartRange.lower+diff, mDragStartRange.upper+diff); + } else if (mScaleType == QCPAxis::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 QCPAxis::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 QCPAxis::wheelEvent(QWheelEvent *event) +{ + // Mouse range zooming interaction: + if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom) || + !mAxisRect->rangeZoom().testFlag(orientation()) || + !mAxisRect->rangeZoomAxes(orientation()).contains(this)) + { + event->ignore(); + return; + } + + const double wheelSteps = event->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())); + mParentPlot->replot(); +} + /*! \internal A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter @@ -9263,11 +9510,14 @@ void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, { cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio()); #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED +# ifdef QCP_DEVICEPIXELRATIO_FLOAT + cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF()); +# else cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio()); +# endif #endif } else cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()); - cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()); cachedLabel->pixmap.fill(Qt::transparent); QCPPainter cachePainter(&cachedLabel->pixmap); cachePainter.setPen(painter->pen()); @@ -9568,8 +9818,8 @@ void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString /* end of 'src/axis/axis.cpp' */ -/* including file 'src/scatterstyle.cpp', size 17420 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/scatterstyle.cpp', size 17450 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPScatterStyle @@ -9751,8 +10001,8 @@ QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) : The custom shape line will be drawn with \a pen and filled with \a brush. The size has a slightly different meaning than for built-in scatter points: The custom path will be drawn scaled by a - factor of \a size/6.0. Since the default \a size is 6, the custom path will appear at a its - natural size by default. To double the size of the path for example, set \a size to 12. + factor of \a size/6.0. Since the default \a size is 6, the custom path will appear in its + original size by default. To for example double the size of the path, set \a size to 12. */ QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) : mSize(size), @@ -9947,10 +10197,11 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const } case ssDiamond: { - painter->drawLine(QLineF(x-w, y, x, y-w)); - painter->drawLine(QLineF( x, y-w, x+w, y)); - painter->drawLine(QLineF(x+w, y, x, y+w)); - painter->drawLine(QLineF( x, y+w, x-w, y)); + QPointF lineArray[4] = {QPointF(x-w, y), + QPointF( x, y-w), + QPointF(x+w, y), + QPointF( x, y+w)}; + painter->drawPolygon(lineArray, 4); break; } case ssStar: @@ -9963,52 +10214,54 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const } case ssTriangle: { - painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w)); - painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w)); - painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w)); + QPointF lineArray[3] = {QPointF(x-w, y+0.755*w), + QPointF(x+w, y+0.755*w), + QPointF( x, y-0.977*w)}; + painter->drawPolygon(lineArray, 3); break; } case ssTriangleInverted: { - painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w)); - painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w)); - painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w)); + QPointF lineArray[3] = {QPointF(x-w, y-0.755*w), + QPointF(x+w, y-0.755*w), + QPointF( x, y+0.977*w)}; + painter->drawPolygon(lineArray, 3); break; } case ssCrossSquare: { - painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95)); - painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w)); - painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95)); + painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w)); break; } case ssPlusSquare: { - painter->drawLine(QLineF(x-w, y, x+w*0.95, y)); - painter->drawLine(QLineF( x, y+w, x, y-w)); - painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + painter->drawLine(QLineF(x-w, y, x+w*0.95, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); break; } case ssCrossCircle: { - painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670)); - painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707)); - painter->drawEllipse(QPointF(x, y), w, w); + painter->drawEllipse(QPointF(x, y), w, w); + painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670)); + painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707)); break; } case ssPlusCircle: { - painter->drawLine(QLineF(x-w, y, x+w, y)); - painter->drawLine(QLineF( x, y+w, x, y-w)); - painter->drawEllipse(QPointF(x, y), w, w); + painter->drawEllipse(QPointF(x, y), w, w); + painter->drawLine(QLineF(x-w, y, x+w, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); break; } case ssPeace: { - painter->drawLine(QLineF(x, y-w, x, y+w)); - painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707)); - painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707)); - painter->drawEllipse(QPointF(x, y), w, w); + painter->drawEllipse(QPointF(x, y), w, w); + painter->drawLine(QLineF(x, y-w, x, y+w)); + painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707)); + painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707)); break; } case ssPixmap: @@ -10039,8 +10292,8 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const //amalgamation: add datacontainer.cpp -/* including file 'src/plottable.cpp', size 38861 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/plottable.cpp', size 38845 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionDecorator @@ -10078,8 +10331,8 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const QCPSelectionDecorator::QCPSelectionDecorator() : mPen(QColor(80, 80, 255), 2.5), mBrush(Qt::NoBrush), - mScatterStyle(QCPScatterStyle::ssNone, QPen(Qt::blue, 2), Qt::NoBrush, 6.0), - mUsedScatterProperties(QCPScatterStyle::spPen), + mScatterStyle(), + mUsedScatterProperties(QCPScatterStyle::spNone), mPlottable(0) { } @@ -10121,6 +10374,8 @@ void QCPSelectionDecorator::setScatterStyle(const QCPScatterStyle &scatterStyle, Use this method to define which properties of the scatter style (set via \ref setScatterStyle) will be used for selected data segments. All properties of the scatter style that are not specified in \a properties will remain as specified in the plottable's original scatter style. + + \see QCPScatterStyle::ScatterProperty */ void QCPSelectionDecorator::setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties) { @@ -11011,7 +11266,7 @@ void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) /* including file 'src/item.cpp', size 49269 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemAnchor @@ -12281,8 +12536,8 @@ QCP::Interaction QCPAbstractItem::selectionCategory() const /* end of 'src/item.cpp' */ -/* including file 'src/core.cpp', size 124243 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/core.cpp', size 125037 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCustomPlot @@ -12616,6 +12871,7 @@ QCustomPlot::QCustomPlot(QWidget *parent) : mOpenGl(false), mMouseHasMoved(false), mMouseEventLayerable(0), + mMouseSignalLayerable(0), mReplotting(false), mReplotQueued(false), mOpenGlMultisamples(16), @@ -12630,7 +12886,11 @@ QCustomPlot::QCustomPlot(QWidget *parent) : currentLocale.setNumberOptions(QLocale::OmitGroupSeparator); setLocale(currentLocale); #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED +# ifdef QCP_DEVICEPIXELRATIO_FLOAT + setBufferDevicePixelRatio(QWidget::devicePixelRatioF()); +# else setBufferDevicePixelRatio(QWidget::devicePixelRatio()); +# endif #endif mOpenGlAntialiasedElementsBackup = mAntialiasedElements; @@ -13019,6 +13279,10 @@ void QCustomPlot::setSelectionRect(QCPSelectionRect *selectionRect) } /*! + \warning This is still an experimental feature and its performance depends on the system that it + runs on. Having multiple QCustomPlot widgets in one application with enabled OpenGL rendering + might cause context conflicts on some systems. + This method allows to enable OpenGL plot rendering, for increased plotting performance of graphically demanding plots (thick lines, translucent fills, etc.). @@ -14433,7 +14697,7 @@ bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double */ QSize QCustomPlot::minimumSizeHint() const { - return mPlotLayout->minimumSizeHint(); + return mPlotLayout->minimumOuterSizeHint(); } /*! \internal @@ -14443,7 +14707,7 @@ QSize QCustomPlot::minimumSizeHint() const */ QSize QCustomPlot::sizeHint() const { - return mPlotLayout->minimumSizeHint(); + return mPlotLayout->minimumOuterSizeHint(); } /*! \internal @@ -14553,12 +14817,18 @@ void QCustomPlot::mousePressEvent(QMouseEvent *event) mSelectionRect->startSelection(event); } else { - // no selection rect interaction, so forward event to layerable under the cursor: + // no selection rect interaction, prepare for click signal emission and forward event to layerable under the cursor: QList details; QList candidates = layerableListAt(mMousePressPos, false, &details); + if (!candidates.isEmpty()) + { + mMouseSignalLayerable = candidates.first(); // candidate for signal emission is always topmost hit layerable (signal emitted in release event) + mMouseSignalLayerableDetails = details.first(); + } + // forward event to topmost candidate which accepts the event: for (int i=0; iaccept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list + event->accept(); // default impl of QCPLayerable's mouse events call ignore() on the event, in that case propagate to next candidate in list candidates.at(i)->mousePressEvent(event, details.at(i)); if (event->isAccepted()) { @@ -14625,20 +14895,21 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) processPointSelection(event); // emit specialized click signals of QCustomPlot instance: - if (QCPAbstractPlottable *ap = qobject_cast(mMouseEventLayerable)) + if (QCPAbstractPlottable *ap = qobject_cast(mMouseSignalLayerable)) { int dataIndex = 0; - if (!mMouseEventLayerableDetails.value().isEmpty()) - dataIndex = mMouseEventLayerableDetails.value().dataRange().begin(); + if (!mMouseSignalLayerableDetails.value().isEmpty()) + dataIndex = mMouseSignalLayerableDetails.value().dataRange().begin(); emit plottableClick(ap, dataIndex, event); - } else if (QCPAxis *ax = qobject_cast(mMouseEventLayerable)) - emit axisClick(ax, mMouseEventLayerableDetails.value(), event); - else if (QCPAbstractItem *ai = qobject_cast(mMouseEventLayerable)) + } else if (QCPAxis *ax = qobject_cast(mMouseSignalLayerable)) + emit axisClick(ax, mMouseSignalLayerableDetails.value(), event); + else if (QCPAbstractItem *ai = qobject_cast(mMouseSignalLayerable)) emit itemClick(ai, event); - else if (QCPLegend *lg = qobject_cast(mMouseEventLayerable)) + else if (QCPLegend *lg = qobject_cast(mMouseSignalLayerable)) emit legendClick(lg, 0, event); - else if (QCPAbstractLegendItem *li = qobject_cast(mMouseEventLayerable)) + else if (QCPAbstractLegendItem *li = qobject_cast(mMouseSignalLayerable)) emit legendClick(li->parentLegend(), li, event); + mMouseSignalLayerable = 0; } if (mSelectionRect && mSelectionRect->isActive()) // Note: if a click was detected above, the selection rect is canceled there @@ -15448,7 +15719,7 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) //amalgamation: add plottable1d.cpp /* including file 'src/colorgradient.cpp', size 24646 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -16088,7 +16359,7 @@ void QCPColorGradient::updateColorBuffer() /* including file 'src/selectiondecorator-bracket.cpp', size 12313 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionDecoratorBracket @@ -16373,8 +16644,8 @@ QPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInte /* end of 'src/selectiondecorator-bracket.cpp' */ -/* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47509 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47584 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -16631,7 +16902,7 @@ QList QCPAxisRect::axes() const 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 sublasses of QCPAxis) by setting \a axis to an axis that was + 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 @@ -16731,6 +17002,8 @@ bool QCPAxisRect::removeAxis(QCPAxis *axis) it.next(); if (it.value().contains(axis)) { + if (it.value().first() == axis && it.value().size() > 1) // if removing first axis, transfer axis offset to the new first axis (which at this point is the second axis, if it exists) + it.value()[1]->setOffset(axis->offset()); mAxes[it.key()].removeOne(axis); if (qobject_cast(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot) parentPlot()->axisRemoved(axis); @@ -17497,7 +17770,6 @@ void QCPAxisRect::layoutChanged() void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details) { Q_UNUSED(details) - mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release) if (event->buttons() & Qt::LeftButton) { mDragging = true; @@ -17545,11 +17817,11 @@ void QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) break; if (ax->mScaleType == QCPAxis::stLinear) { - double diff = ax->pixelToCoord(mDragStart.x()) - ax->pixelToCoord(event->pos().x()); + double diff = ax->pixelToCoord(startPos.x()) - ax->pixelToCoord(event->pos().x()); ax->setRange(mDragStartHorzRange.at(i).lower+diff, mDragStartHorzRange.at(i).upper+diff); } else if (ax->mScaleType == QCPAxis::stLogarithmic) { - double diff = ax->pixelToCoord(mDragStart.x()) / ax->pixelToCoord(event->pos().x()); + double diff = ax->pixelToCoord(startPos.x()) / ax->pixelToCoord(event->pos().x()); ax->setRange(mDragStartHorzRange.at(i).lower*diff, mDragStartHorzRange.at(i).upper*diff); } } @@ -17566,11 +17838,11 @@ void QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) break; if (ax->mScaleType == QCPAxis::stLinear) { - double diff = ax->pixelToCoord(mDragStart.y()) - ax->pixelToCoord(event->pos().y()); + double diff = ax->pixelToCoord(startPos.y()) - ax->pixelToCoord(event->pos().y()); ax->setRange(mDragStartVertRange.at(i).lower+diff, mDragStartVertRange.at(i).upper+diff); } else if (ax->mScaleType == QCPAxis::stLogarithmic) { - double diff = ax->pixelToCoord(mDragStart.y()) / ax->pixelToCoord(event->pos().y()); + double diff = ax->pixelToCoord(startPos.y()) / ax->pixelToCoord(event->pos().y()); ax->setRange(mDragStartVertRange.at(i).lower*diff, mDragStartVertRange.at(i).upper*diff); } } @@ -17580,7 +17852,7 @@ void QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) { if (mParentPlot->noAntialiasingOnDrag()) mParentPlot->setNotAntialiasedElements(QCP::aeAll); - mParentPlot->replot(); + mParentPlot->replot(QCustomPlot::rpQueuedReplot); } } @@ -17647,8 +17919,8 @@ void QCPAxisRect::wheelEvent(QWheelEvent *event) /* end of 'src/layoutelements/layoutelement-axisrect.cpp' */ -/* including file 'src/layoutelements/layoutelement-legend.cpp', size 30933 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layoutelements/layoutelement-legend.cpp', size 31097 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAbstractLegendItem @@ -17850,9 +18122,7 @@ void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged) QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding. The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend - creates/removes legend items of this type in the default implementation. However, these functions - may be reimplemented such that a different kind of legend item (e.g a direct subclass of - QCPAbstractLegendItem) is used for that plottable. + creates/removes legend items of this type. Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout @@ -17908,8 +18178,8 @@ QFont QCPPlottableLegendItem::getFont() const /*! \internal Draws the item with \a painter. The size and position of the drawn legend item is defined by the - parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint - of this legend item. + parent layout (typically a \ref QCPLegend) and the \ref minimumOuterSizeHint and \ref + maximumOuterSizeHint of this legend item. */ void QCPPlottableLegendItem::draw(QCPPainter *painter) { @@ -17944,7 +18214,7 @@ void QCPPlottableLegendItem::draw(QCPPainter *painter) \seebaseclassmethod */ -QSize QCPPlottableLegendItem::minimumSizeHint() const +QSize QCPPlottableLegendItem::minimumOuterSizeHint() const { if (!mPlottable) return QSize(); QSize result(0, 0); @@ -17952,8 +18222,10 @@ QSize QCPPlottableLegendItem::minimumSizeHint() const QFontMetrics fontMetrics(getFont()); QSize iconSize = mParentLegend->iconSize(); textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name()); - result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right()); - result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom()); + 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; } @@ -17967,10 +18239,14 @@ QSize QCPPlottableLegendItem::minimumSizeHint() const A legend is a small box somewhere in the plot which lists plottables with their name and icon. - Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The - respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However, - QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref - itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc. + A legend is populated with legend items by calling \ref QCPAbstractPlottable::addToLegend on the + plottable, for which a legend item shall be created. In the case of the main legend (\ref + QCustomPlot::legend), simply adding plottables to the plot while \ref + QCustomPlot::setAutoAddPlottableToLegend is set to true (the default) creates corresponding + legend items. The legend item associated with a certain plottable can be removed with \ref + QCPAbstractPlottable::removeFromLegend. However, QCPLegend also offers an interface to add and + manipulate legend items directly: \ref item, \ref itemWithPlottable, \ref itemCount, \ref + addItem, \ref removeItem, etc. Since \ref QCPLegend derives from \ref QCPLayoutGrid, it can be placed in any position a \ref QCPLayoutElement may be positioned. The legend items are themselves \ref QCPLayoutElement @@ -18559,8 +18835,8 @@ void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot) /* end of 'src/layoutelements/layoutelement-legend.cpp' */ -/* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12759 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12761 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPTextElement @@ -18839,22 +19115,22 @@ void QCPTextElement::draw(QCPPainter *painter) } /* inherits documentation from base class */ -QSize QCPTextElement::minimumSizeHint() const +QSize QCPTextElement::minimumOuterSizeHint() const { QFontMetrics metrics(mFont); - QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size(); - result.rwidth() += mMargins.left() + mMargins.right(); - result.rheight() += mMargins.top() + mMargins.bottom(); + QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size()); + result.rwidth() += mMargins.left()+mMargins.right(); + result.rheight() += mMargins.top()+mMargins.bottom(); return result; } /* inherits documentation from base class */ -QSize QCPTextElement::maximumSizeHint() const +QSize QCPTextElement::maximumOuterSizeHint() const { QFontMetrics metrics(mFont); - QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size(); - result.rheight() += mMargins.top() + mMargins.bottom(); + QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size()); result.setWidth(QWIDGETSIZE_MAX); + result.rheight() += mMargins.top()+mMargins.bottom(); return result; } @@ -18963,8 +19239,8 @@ QColor QCPTextElement::mainTextColor() const /* end of 'src/layoutelements/layoutelement-textelement.cpp' */ -/* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 25910 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 25770 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19385,12 +19661,12 @@ void QCPColorScale::update(UpdatePhase phase) { if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop) { - setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom()); - setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom()); + setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()); + setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()); } else { - setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX); - setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0); + setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), QWIDGETSIZE_MAX); + setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), 0); } break; } @@ -19623,8 +19899,8 @@ void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectablePart /* end of 'src/layoutelements/layoutelement-colorscale.cpp' */ -/* including file 'src/plottables/plottable-graph.cpp', size 72363 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/plottables/plottable-graph.cpp', size 73960 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPGraphData @@ -20154,6 +20430,9 @@ void QCPGraph::getLines(QVector *lines, const QCPDataRange &dataRange) QVector lineData; if (mLineStyle != lsNone) getOptimizedLineData(&lineData, begin, end); + + if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in lineData (significantly simplifies following processing) + std::reverse(lineData.begin(), lineData.end()); switch (mLineStyle) { @@ -20195,6 +20474,10 @@ void QCPGraph::getScatters(QVector *scatters, const QCPDataRange &dataR QVector data; getOptimizedScatterData(&data, begin, end); + + if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in data (significantly simplifies following processing) + std::reverse(data.begin(), data.end()); + scatters->resize(data.size()); if (keyAxis->orientation() == Qt::Vertical) { @@ -20236,7 +20519,6 @@ QVector QCPGraph::dataToLines(const QVector &data) const QCPAxis *valueAxis = mValueAxis.data(); if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } - result.reserve(data.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill result.resize(data.size()); // transform data points to pixels: @@ -20275,7 +20557,6 @@ QVector QCPGraph::dataToStepLeftLines(const QVector &data QCPAxis *valueAxis = mValueAxis.data(); if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } - result.reserve(data.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill result.resize(data.size()*2); // calculate steps from data and transform to pixel coordinates: @@ -20324,7 +20605,6 @@ QVector QCPGraph::dataToStepRightLines(const QVector &dat QCPAxis *valueAxis = mValueAxis.data(); if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } - result.reserve(data.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill result.resize(data.size()*2); // calculate steps from data and transform to pixel coordinates: @@ -20373,7 +20653,6 @@ QVector QCPGraph::dataToStepCenterLines(const QVector &da QCPAxis *valueAxis = mValueAxis.data(); if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } - result.reserve(data.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill result.resize(data.size()*2); // calculate steps from data and transform to pixel coordinates: @@ -20434,7 +20713,7 @@ QVector QCPGraph::dataToImpulseLines(const QVector &data) QCPAxis *valueAxis = mValueAxis.data(); if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } - result.resize(data.size()*2); // no need to reserve 2 extra points because impulse plot has no fill + result.resize(data.size()*2); // transform data points to pixels: if (keyAxis->orientation() == Qt::Vertical) @@ -20462,18 +20741,19 @@ QVector QCPGraph::dataToImpulseLines(const QVector &data) } /*! \internal - + Draws the fill of the graph using the specified \a painter, with the currently set brush. - \a lines contains the points of the graph line, in pixel coordinates. - - If the fill is a normal fill towards the zero-value-line, only the points in \a lines are - required and two extra points at the zero-value-line, which are added by \ref addFillBasePoints - and removed by \ref removeFillBasePoints after the fill drawing is done. - - On the other hand if the fill is a channel fill between this QCPGraph and another QCPGraph (\a - mChannelFillGraph), the more complex polygon is calculated with the \ref getChannelFillPolygon - function, and then drawn. + 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 */ @@ -20483,16 +20763,24 @@ void QCPGraph::drawFill(QCPPainter *painter, QVector *lines) const if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0) return; applyFillAntialiasingHint(painter); + QVector segments = getNonNanSegments(lines, keyAxis()->orientation()); if (!mChannelFillGraph) { // draw base fill under graph, fill goes all the way to the zero-value-line: - addFillBasePoints(lines); - painter->drawPolygon(QPolygonF(*lines)); - removeFillBasePoints(lines); + for (int i=0; idrawPolygon(getFillPolygon(lines, segments.at(i))); } else { - // draw channel fill between this graph and mChannelFillGraph: - painter->drawPolygon(getChannelFillPolygon(lines)); + // draw fill between this graph and mChannelFillGraph: + QVector otherLines; + mChannelFillGraph->getLines(&otherLines, QCPDataRange(0, mChannelFillGraph->dataCount())); + if (!otherLines.isEmpty()) + { + QVector otherSegments = getNonNanSegments(&otherLines, mChannelFillGraph->keyAxis()->orientation()); + QVector > segmentPairs = getOverlappingSegments(segments, lines, otherSegments, &otherLines); + for (int i=0; idrawPolygon(getChannelFillPolygon(lines, segmentPairs.at(i).first, &otherLines, segmentPairs.at(i).second)); + } } } @@ -20635,13 +20923,8 @@ void QCPGraph::getOptimizedLineData(QVector *lineData, const QCPGr } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output { - QCPGraphDataContainer::const_iterator it = begin; - lineData->reserve(dataCount+2); // +2 for possible fill end points - while (it != end) - { - lineData->append(*it); - ++it; - } + lineData->resize(dataCount); + std::copy(begin, end, lineData->begin()); } } @@ -20857,157 +21140,188 @@ void QCPGraph::getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin } } -/*! \internal +/*! \internal - The line vector generated by e.g. \ref getLines describes only the line that connects the data - points. If the graph needs to be filled, two additional points need to be added at the - value-zero-line in the lower and upper key positions of the graph. This function calculates these - points and adds them to the end of \a lineData. Since the fill is typically drawn before the line - stroke, these added points need to be removed again after the fill is done, with the - removeFillBasePoints function. - - The expanding of \a lines by two points will not cause unnecessary memory reallocations, because - the data vector generation functions (e.g. \ref getLines) reserve two extra points when they - allocate memory for \a lines. + This method goes through the passed points in \a lineData and returns a list of the segments + which don't contain NaN data points. - \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint + \a keyOrientation defines whether the \a x or \a y member of the passed QPointF is used to check + for NaN. If \a keyOrientation is \c Qt::Horizontal, the \a y member is checked, if it is \c + Qt::Vertical, the \a x member is checked. + + \see getOverlappingSegments, drawFill */ -void QCPGraph::addFillBasePoints(QVector *lines) const +QVector QCPGraph::getNonNanSegments(const QVector *lineData, Qt::Orientation keyOrientation) const { - if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } - if (!lines) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; } - if (lines->isEmpty()) return; + QVector result; + const int n = lineData->size(); - // append points that close the polygon fill at the key axis: - if (mKeyAxis.data()->orientation() == Qt::Vertical) + QCPDataRange currentSegment(-1, -1); + int i = 0; + + if (keyOrientation == Qt::Horizontal) { - *lines << upperFillBasePoint(lines->last().y()); - *lines << lowerFillBasePoint(lines->first().y()); + while (i < n) + { + while (i < n && qIsNaN(lineData->at(i).y())) // seek next non-NaN data point + ++i; + if (i == n) + break; + currentSegment.setBegin(i++); + while (i < n && !qIsNaN(lineData->at(i).y())) // seek next NaN data point or end of data + ++i; + currentSegment.setEnd(i++); + result.append(currentSegment); + } + } else // keyOrientation == Qt::Vertical + { + while (i < n) + { + while (i < n && qIsNaN(lineData->at(i).x())) // seek next non-NaN data point + ++i; + if (i == n) + break; + currentSegment.setBegin(i++); + while (i < n && !qIsNaN(lineData->at(i).x())) // seek next NaN data point or end of data + ++i; + currentSegment.setEnd(i++); + result.append(currentSegment); + } + } + return result; +} + +/*! \internal + + This method takes two segment lists (e.g. created by \ref getNonNanSegments) \a thisSegments and + \a otherSegments, and their associated point data \a thisData and \a otherData. + + It returns all pairs of segments (the first from \a thisSegments, the second from \a + otherSegments), which overlap in plot coordinates. + + This method is useful in the case of a channel fill between two graphs, when only those non-NaN + segments which actually overlap in their key coordinate shall be considered for drawing a channel + fill polygon. + + It is assumed that the passed segments in \a thisSegments are ordered ascending by index, and + that the segments don't overlap themselves. The same is assumed for the segments in \a + otherSegments. This is fulfilled when the segments are obtained via \ref getNonNanSegments. + + \see getNonNanSegments, segmentsIntersect, drawFill, getChannelFillPolygon +*/ +QVector > QCPGraph::getOverlappingSegments(QVector thisSegments, const QVector *thisData, QVector otherSegments, const QVector *otherData) const +{ + QVector > result; + if (thisData->isEmpty() || otherData->isEmpty() || thisSegments.isEmpty() || otherSegments.isEmpty()) + return result; + + int thisIndex = 0; + int otherIndex = 0; + const bool verticalKey = mKeyAxis->orientation() == Qt::Vertical; + while (thisIndex < thisSegments.size() && otherIndex < otherSegments.size()) + { + if (thisSegments.at(thisIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow + { + ++thisIndex; + continue; + } + if (otherSegments.at(otherIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow + { + ++otherIndex; + continue; + } + double thisLower, thisUpper, otherLower, otherUpper; + if (!verticalKey) + { + thisLower = thisData->at(thisSegments.at(thisIndex).begin()).x(); + thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).x(); + otherLower = otherData->at(otherSegments.at(otherIndex).begin()).x(); + otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).x(); + } else + { + thisLower = thisData->at(thisSegments.at(thisIndex).begin()).y(); + thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).y(); + otherLower = otherData->at(otherSegments.at(otherIndex).begin()).y(); + otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).y(); + } + + int bPrecedence; + if (segmentsIntersect(thisLower, thisUpper, otherLower, otherUpper, bPrecedence)) + result.append(QPair(thisSegments.at(thisIndex), otherSegments.at(otherIndex))); + + if (bPrecedence <= 0) // otherSegment doesn't reach as far as thisSegment, so continue with next otherSegment, keeping current thisSegment + ++otherIndex; + else // otherSegment reaches further than thisSegment, so continue with next thisSegment, keeping current otherSegment + ++thisIndex; + } + + return result; +} + +/*! \internal + + Returns whether the segments defined by the coordinates (aLower, aUpper) and (bLower, bUpper) + have overlap. + + The output parameter \a bPrecedence indicates whether the \a b segment reaches farther than the + \a a segment or not. If \a bPrecedence returns 1, segment \a b reaches the farthest to higher + coordinates (i.e. bUpper > aUpper). If it returns -1, segment \a a reaches the farthest. Only if + both segment's upper bounds are identical, 0 is returned as \a bPrecedence. + + It is assumed that the lower bounds always have smaller or equal values than the upper bounds. + + \see getOverlappingSegments +*/ +bool QCPGraph::segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const +{ + bPrecedence = 0; + if (aLower > bUpper) + { + bPrecedence = -1; + return false; + } else if (bLower > aUpper) + { + bPrecedence = 1; + return false; } else { - *lines << upperFillBasePoint(lines->last().x()); - *lines << lowerFillBasePoint(lines->first().x()); + if (aUpper > bUpper) + bPrecedence = -1; + else if (aUpper < bUpper) + bPrecedence = 1; + + return true; } } /*! \internal - removes the two points from \a lines that were added by \ref addFillBasePoints. - - \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint -*/ -void QCPGraph::removeFillBasePoints(QVector *lines) const -{ - if (!lines) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; } - if (lines->isEmpty()) return; - - lines->remove(lines->size()-2, 2); -} + Returns the point which closes the fill polygon on the zero-value-line parallel to the key axis. + The logarithmic axis scale case is a bit special, since the zero-value-line in pixel coordinates + is in positive or negative infinity. So this case is handled separately by just closing the fill + polygon on the axis which lies in the direction towards the zero value. -/*! \internal - - called by \ref addFillBasePoints to conveniently assign the point which closes the fill polygon - on the lower side of the zero-value-line parallel to the key axis. The logarithmic axis scale - case is a bit special, since the zero-value-line in pixel coordinates is in positive or negative - infinity. So this case is handled separately by just closing the fill polygon on the axis which - lies in the direction towards the zero value. - - \a lowerKey will be the the key (in pixels) of the returned point. Depending on whether the key - axis is horizontal or vertical, \a lowerKey will end up as the x or y value of the returned - point, respectively. - - \see upperFillBasePoint, addFillBasePoints + \a matchingDataPoint will provide the key (in pixels) of the returned point. Depending on whether + the key axis of this graph is horizontal or vertical, \a matchingDataPoint will provide the x or + y value of the returned point, respectively. */ -QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const +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(); } - QPointF point; + QPointF result; if (valueAxis->scaleType() == QCPAxis::stLinear) { - if (keyAxis->axisType() == QCPAxis::atLeft) + if (keyAxis->orientation() == Qt::Horizontal) { - point.setX(valueAxis->coordToPixel(0)); - point.setY(lowerKey); - } else if (keyAxis->axisType() == QCPAxis::atRight) + result.setX(matchingDataPoint.x()); + result.setY(valueAxis->coordToPixel(0)); + } else // keyAxis->orientation() == Qt::Vertical { - point.setX(valueAxis->coordToPixel(0)); - point.setY(lowerKey); - } else if (keyAxis->axisType() == QCPAxis::atTop) - { - point.setX(lowerKey); - point.setY(valueAxis->coordToPixel(0)); - } else if (keyAxis->axisType() == QCPAxis::atBottom) - { - point.setX(lowerKey); - point.setY(valueAxis->coordToPixel(0)); - } - } else // valueAxis->mScaleType == QCPAxis::stLogarithmic - { - // In logarithmic scaling we can't just draw to value zero so we just fill all the way - // to the axis which is in the direction towards zero - if (keyAxis->orientation() == Qt::Vertical) - { - if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || - (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis - point.setX(keyAxis->axisRect()->right()); - else - point.setX(keyAxis->axisRect()->left()); - point.setY(lowerKey); - } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom) - { - point.setX(lowerKey); - if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || - (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis - point.setY(keyAxis->axisRect()->top()); - else - point.setY(keyAxis->axisRect()->bottom()); - } - } - return point; -} - -/*! \internal - - called by \ref addFillBasePoints to conveniently assign the point which closes the fill - polygon on the upper side of the zero-value-line parallel to the key axis. The logarithmic axis - scale case is a bit special, since the zero-value-line in pixel coordinates is in positive or - negative infinity. So this case is handled separately by just closing the fill polygon on the - axis which lies in the direction towards the zero value. - - \a upperKey will be the the key (in pixels) of the returned point. Depending on whether the key - axis is horizontal or vertical, \a upperKey will end up as the x or y value of the returned - point, respectively. - - \see lowerFillBasePoint, addFillBasePoints -*/ -QPointF QCPGraph::upperFillBasePoint(double upperKey) const -{ - QCPAxis *keyAxis = mKeyAxis.data(); - QCPAxis *valueAxis = mValueAxis.data(); - if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); } - - QPointF point; - if (valueAxis->scaleType() == QCPAxis::stLinear) - { - if (keyAxis->axisType() == QCPAxis::atLeft) - { - point.setX(valueAxis->coordToPixel(0)); - point.setY(upperKey); - } else if (keyAxis->axisType() == QCPAxis::atRight) - { - point.setX(valueAxis->coordToPixel(0)); - point.setY(upperKey); - } else if (keyAxis->axisType() == QCPAxis::atTop) - { - point.setX(upperKey); - point.setY(valueAxis->coordToPixel(0)); - } else if (keyAxis->axisType() == QCPAxis::atBottom) - { - point.setX(upperKey); - point.setY(valueAxis->coordToPixel(0)); + result.setX(valueAxis->coordToPixel(0)); + result.setY(matchingDataPoint.y()); } } else // valueAxis->mScaleType == QCPAxis::stLogarithmic { @@ -21017,36 +21331,71 @@ QPointF QCPGraph::upperFillBasePoint(double upperKey) const { if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis - point.setX(keyAxis->axisRect()->right()); + result.setX(keyAxis->axisRect()->right()); else - point.setX(keyAxis->axisRect()->left()); - point.setY(upperKey); + result.setX(keyAxis->axisRect()->left()); + result.setY(matchingDataPoint.y()); } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom) { - point.setX(upperKey); + result.setX(matchingDataPoint.x()); if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis - point.setY(keyAxis->axisRect()->top()); + result.setY(keyAxis->axisRect()->top()); else - point.setY(keyAxis->axisRect()->bottom()); + result.setY(keyAxis->axisRect()->bottom()); } } - return point; + return result; } /*! \internal - Generates the polygon needed for drawing channel fills between this graph and the graph specified - in \a mChannelFillGraph (see \ref setChannelFillGraph). The data points representing the line of - this graph in pixel coordinates must be passed in \a lines, the corresponding points of the other - graph are generated by calling its \ref getLines method. + Returns the polygon needed for drawing normal fills between this graph and the key axis. + + Pass the graph's data points (in pixel coordinates) as \a lineData, and specify the \a segment + which shall be used for the fill. The collection of \a lineData points described by \a segment + must not contain NaN data points (see \ref getNonNanSegments). + + The returned fill polygon will be closed at the key axis (the zero-value line) for linear value + axes. For logarithmic value axes the polygon will reach just beyond the corresponding axis rect + side (see \ref getFillBasePoint). - This method may return an empty polygon if the key ranges of the two graphs have no overlap of if - they don't have the same orientation (e.g. one key axis vertical, the other horizontal). For - increased performance (due to implicit sharing), it is recommended to keep the returned QPolygonF - const. + For increased performance (due to implicit sharing), keep the returned QPolygonF const. + + \see drawFill, getNonNanSegments */ -const QPolygonF QCPGraph::getChannelFillPolygon(const QVector *lines) const +const QPolygonF QCPGraph::getFillPolygon(const QVector *lineData, QCPDataRange segment) const +{ + if (segment.size() < 2) + return QPolygonF(); + QPolygonF result(segment.size()+2); + + result[0] = getFillBasePoint(lineData->at(segment.begin())); + std::copy(lineData->constBegin()+segment.begin(), lineData->constBegin()+segment.end(), result.begin()+1); + result[result.size()-1] = getFillBasePoint(lineData->at(segment.end()-1)); + + return result; +} + +/*! \internal + + Returns the polygon needed for drawing (partial) channel fills between this graph and the graph + specified by \ref setChannelFillGraph. + + The data points of this graph are passed as pixel coordinates via \a thisData, the data of the + other graph as \a otherData. The returned polygon will be calculated for the specified data + segments \a thisSegment and \a otherSegment, pertaining to the respective \a thisData and \a + otherData, respectively. + + The passed \a thisSegment and \a otherSegment should correspond to the segment pairs returned by + \ref getOverlappingSegments, to make sure only segments that actually have key coordinate overlap + need to be processed here. + + For increased performance due to implicit sharing, keep the returned QPolygonF const. + + \see drawFill, getOverlappingSegments, getNonNanSegments +*/ +const QPolygonF QCPGraph::getChannelFillPolygon(const QVector *thisData, QCPDataRange thisSegment, const QVector *otherData, QCPDataRange otherSegment) const { if (!mChannelFillGraph) return QPolygonF(); @@ -21059,47 +21408,29 @@ const QPolygonF QCPGraph::getChannelFillPolygon(const QVector *lines) c if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation()) return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis) - if (lines->isEmpty()) return QPolygonF(); - QVector otherData; - mChannelFillGraph.data()->getLines(&otherData, QCPDataRange(0, mChannelFillGraph.data()->dataCount())); - if (otherData.isEmpty()) return QPolygonF(); - QVector thisData; - thisData.reserve(lines->size()+otherData.size()); // because we will join both vectors at end of this function - for (int i=0; isize(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve() - thisData << lines->at(i); - + if (thisData->isEmpty()) return QPolygonF(); + QVector thisSegmentData(thisSegment.size()); + QVector otherSegmentData(otherSegment.size()); + std::copy(thisData->constBegin()+thisSegment.begin(), thisData->constBegin()+thisSegment.end(), thisSegmentData.begin()); + std::copy(otherData->constBegin()+otherSegment.begin(), otherData->constBegin()+otherSegment.end(), otherSegmentData.begin()); // pointers to be able to swap them, depending which data range needs cropping: - QVector *staticData = &thisData; - QVector *croppedData = &otherData; + QVector *staticData = &thisSegmentData; + QVector *croppedData = &otherSegmentData; // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType): if (keyAxis->orientation() == Qt::Horizontal) { // x is key - // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys: - if (staticData->first().x() > staticData->last().x()) - { - int size = staticData->size(); - for (int i=0; ifirst().x() > croppedData->last().x()) - { - int size = croppedData->size(); - for (int i=0; ifirst().x() < croppedData->first().x()) // other one must be cropped qSwap(staticData, croppedData); - int lowBound = findIndexBelowX(croppedData, staticData->first().x()); + const int lowBound = findIndexBelowX(croppedData, staticData->first().x()); if (lowBound == -1) return QPolygonF(); // key ranges have no overlap croppedData->remove(0, lowBound); - // set lowest point of cropped data to fit exactly key position of first static data - // point via linear interpolation: + // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation: if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation double slope; - if (croppedData->at(1).x()-croppedData->at(0).x() != 0) + if (!qFuzzyCompare(croppedData->at(1).x(), croppedData->at(0).x())) slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x()); else slope = 0; @@ -21112,11 +21443,10 @@ const QPolygonF QCPGraph::getChannelFillPolygon(const QVector *lines) c int highBound = findIndexAboveX(croppedData, staticData->last().x()); if (highBound == -1) return QPolygonF(); // key ranges have no overlap croppedData->remove(highBound+1, croppedData->size()-(highBound+1)); - // set highest point of cropped data to fit exactly key position of last static data - // point via linear interpolation: + // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation: if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation - int li = croppedData->size()-1; // last index - if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0) + const int li = croppedData->size()-1; // last index + if (!qFuzzyCompare(croppedData->at(li).x(), croppedData->at(li-1).x())) slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x()); else slope = 0; @@ -21125,32 +21455,16 @@ const QPolygonF QCPGraph::getChannelFillPolygon(const QVector *lines) c } else // mKeyAxis->orientation() == Qt::Vertical { // y is key - // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x, - // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate. - // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys: - if (staticData->first().y() < staticData->last().y()) - { - int size = staticData->size(); - for (int i=0; ifirst().y() < croppedData->last().y()) - { - int size = croppedData->size(); - for (int i=0; ifirst().y() > croppedData->first().y()) // other one must be cropped + if (staticData->first().y() < croppedData->first().y()) // other one must be cropped qSwap(staticData, croppedData); - int lowBound = findIndexAboveY(croppedData, staticData->first().y()); + int lowBound = findIndexBelowY(croppedData, staticData->first().y()); if (lowBound == -1) return QPolygonF(); // key ranges have no overlap croppedData->remove(0, lowBound); - // set lowest point of cropped data to fit exactly key position of first static data - // point via linear interpolation: + // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation: if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation double slope; - if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots + if (!qFuzzyCompare(croppedData->at(1).y(), croppedData->at(0).y())) // avoid division by zero in step plots slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y()); else slope = 0; @@ -21158,16 +21472,15 @@ const QPolygonF QCPGraph::getChannelFillPolygon(const QVector *lines) c (*croppedData)[0].setY(staticData->first().y()); // crop upper bound: - if (staticData->last().y() < croppedData->last().y()) // other one must be cropped + if (staticData->last().y() > croppedData->last().y()) // other one must be cropped qSwap(staticData, croppedData); - int highBound = findIndexBelowY(croppedData, staticData->last().y()); + int highBound = findIndexAboveY(croppedData, staticData->last().y()); if (highBound == -1) return QPolygonF(); // key ranges have no overlap croppedData->remove(highBound+1, croppedData->size()-(highBound+1)); - // set highest point of cropped data to fit exactly key position of last static data - // point via linear interpolation: + // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation: if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation int li = croppedData->size()-1; // last index - if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots + if (!qFuzzyCompare(croppedData->at(li).y(), croppedData->at(li-1).y())) // avoid division by zero in step plots slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y()); else slope = 0; @@ -21176,15 +21489,16 @@ const QPolygonF QCPGraph::getChannelFillPolygon(const QVector *lines) c } // return joined: - for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted - thisData << otherData.at(i); - return QPolygonF(thisData); + for (int i=otherSegmentData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted + thisSegmentData << otherSegmentData.at(i); + return QPolygonF(thisSegmentData); } /*! \internal Finds the smallest index of \a data, whose points x value is just above \a x. Assumes x values in - \a data points are ordered ascending, as is the case when plotting with horizontal key axis. + \a data points are ordered ascending, as is ensured by \ref getLines/\ref getScatters if the key + axis is horizontal. Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. */ @@ -21206,7 +21520,8 @@ int QCPGraph::findIndexAboveX(const QVector *data, double x) const /*! \internal Finds the highest index of \a data, whose points x value is just below \a x. Assumes x values in - \a data points are ordered ascending, as is the case when plotting with horizontal key axis. + \a data points are ordered ascending, as is ensured by \ref getLines/\ref getScatters if the key + axis is horizontal. Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. */ @@ -21228,20 +21543,21 @@ int QCPGraph::findIndexBelowX(const QVector *data, double x) const /*! \internal Finds the smallest index of \a data, whose points y value is just above \a y. Assumes y values in - \a data points are ordered descending, as is the case when plotting with vertical key axis. + \a data points are ordered ascending, as is ensured by \ref getLines/\ref getScatters if the key + axis is vertical. Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. */ int QCPGraph::findIndexAboveY(const QVector *data, double y) const { - for (int i=0; isize(); ++i) + for (int i=data->size()-1; i>=0; --i) { if (data->at(i).y() < y) { - if (i>0) - return i-1; + if (isize()-1) + return i+1; else - return 0; + return data->size()-1; } } return -1; @@ -21309,21 +21625,21 @@ double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer: /*! \internal Finds the highest index of \a data, whose points y value is just below \a y. Assumes y values in - \a data points are ordered descending, as is the case when plotting with vertical key axis (since - keys are ordered ascending). + \a data points are ordered ascending, as is ensured by \ref getLines/\ref getScatters if the key + axis is vertical. Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. */ int QCPGraph::findIndexBelowY(const QVector *data, double y) const { - for (int i=data->size()-1; i>=0; --i) + for (int i=0; isize(); ++i) { if (data->at(i).y() > y) { - if (isize()-1) - return i+1; + if (i>0) + return i-1; else - return data->size()-1; + return 0; } } return -1; @@ -21331,8 +21647,8 @@ int QCPGraph::findIndexBelowY(const QVector *data, double y) const /* end of 'src/plottables/plottable-graph.cpp' */ -/* including file 'src/plottables/plottable-curve.cpp', size 60009 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/plottables/plottable-curve.cpp', size 63527 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPCurveData @@ -21498,6 +21814,7 @@ QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) : setScatterStyle(QCPScatterStyle()); setLineStyle(lsLine); + setScatterSkip(0); } QCPCurve::~QCPCurve() @@ -22141,42 +22458,55 @@ int QCPCurve::getRegion(double key, double value, double keyMin, double valueMax */ QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const { - double intersectKey = keyMin; // initial value is just fail-safe - double intersectValue = valueMax; // initial value is just fail-safe + // The intersection point interpolation here is done in pixel coordinates, so we don't need to + // differentiate between different axis scale types. Note that the nomenclature + // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be + // different in pixel coordinates (horz/vert key axes, reversed ranges) + + const double keyMinPx = mKeyAxis->coordToPixel(keyMin); + const double keyMaxPx = mKeyAxis->coordToPixel(keyMax); + const double valueMinPx = mValueAxis->coordToPixel(valueMin); + const double valueMaxPx = mValueAxis->coordToPixel(valueMax); + const double otherValuePx = mValueAxis->coordToPixel(otherValue); + const double valuePx = mValueAxis->coordToPixel(value); + const double otherKeyPx = mKeyAxis->coordToPixel(otherKey); + const double keyPx = mKeyAxis->coordToPixel(key); + double intersectKeyPx = keyMinPx; // initial key just a fail-safe + double intersectValuePx = valueMinPx; // initial value just a fail-safe switch (otherRegion) { case 1: // top and left edge { - intersectValue = valueMax; - intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); - if (intersectKey < keyMin || intersectKey > keyMax) // doesn't intersect, so must intersect other: + intersectValuePx = valueMaxPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed) { - intersectKey = keyMin; - intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + intersectKeyPx = keyMinPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); } break; } case 2: // left edge { - intersectKey = keyMin; - intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + intersectKeyPx = keyMinPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); break; } case 3: // bottom and left edge { - intersectValue = valueMin; - intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); - if (intersectKey < keyMin || intersectKey > keyMax) // doesn't intersect, so must intersect other: + intersectValuePx = valueMinPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed) { - intersectKey = keyMin; - intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + intersectKeyPx = keyMinPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); } break; } case 4: // top edge { - intersectValue = valueMax; - intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); + intersectValuePx = valueMaxPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); break; } case 5: @@ -22185,40 +22515,43 @@ QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double oth } case 6: // bottom edge { - intersectValue = valueMin; - intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); + intersectValuePx = valueMinPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); break; } case 7: // top and right edge { - intersectValue = valueMax; - intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); - if (intersectKey < keyMin || intersectKey > keyMax) // doesn't intersect, so must intersect other: + intersectValuePx = valueMaxPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed) { - intersectKey = keyMax; - intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + intersectKeyPx = keyMaxPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); } break; } case 8: // right edge { - intersectKey = keyMax; - intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + intersectKeyPx = keyMaxPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); break; } case 9: // bottom and right edge { - intersectValue = valueMin; - intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); - if (intersectKey < keyMin || intersectKey > keyMax) // doesn't intersect, so must intersect other: + intersectValuePx = valueMinPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed) { - intersectKey = keyMax; - intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + intersectKeyPx = keyMaxPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); } break; } } - return coordsToPixels(intersectKey, intersectValue); + if (mKeyAxis->orientation() == Qt::Horizontal) + return QPointF(intersectKeyPx, intersectValuePx); + else + return QPointF(intersectValuePx, intersectKeyPx); } /*! \internal @@ -22508,38 +22841,51 @@ bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const */ bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const { - QList intersections; // x of QPointF corresponds to key and y to value + // The intersection point interpolation here is done in pixel coordinates, so we don't need to + // differentiate between different axis scale types. Note that the nomenclature + // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be + // different in pixel coordinates (horz/vert key axes, reversed ranges) + + QList intersections; + const double valueMinPx = mValueAxis->coordToPixel(valueMin); + const double valueMaxPx = mValueAxis->coordToPixel(valueMax); + const double keyMinPx = mKeyAxis->coordToPixel(keyMin); + const double keyMaxPx = mKeyAxis->coordToPixel(keyMax); + const double keyPx = mKeyAxis->coordToPixel(key); + 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 { - // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here - intersections.append(QPointF(key, valueMin)); // direction will be taken care of at end of method - intersections.append(QPointF(key, valueMax)); + // 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 { - // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here - intersections.append(QPointF(keyMin, value)); // direction will be taken care of at end of method - intersections.append(QPointF(keyMax, value)); + // 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 + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, valuePx) : QPointF(valuePx, keyMaxPx)); } else // line is skewed { double gamma; - double keyPerValue = (key-prevKey)/(value-prevValue); + double keyPerValuePx = (keyPx-prevKeyPx)/(valuePx-prevValuePx); // check top of rect: - gamma = prevKey + (valueMax-prevValue)*keyPerValue; - if (gamma >= keyMin && gamma <= keyMax) - intersections.append(QPointF(gamma, valueMax)); + gamma = prevKeyPx + (valueMaxPx-prevValuePx)*keyPerValuePx; + if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMaxPx) : QPointF(valueMaxPx, gamma)); // check bottom of rect: - gamma = prevKey + (valueMin-prevValue)*keyPerValue; - if (gamma >= keyMin && gamma <= keyMax) - intersections.append(QPointF(gamma, valueMin)); - double valuePerKey = 1.0/keyPerValue; + gamma = prevKeyPx + (valueMinPx-prevValuePx)*keyPerValuePx; + if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMinPx) : QPointF(valueMinPx, gamma)); + const double valuePerKeyPx = 1.0/keyPerValuePx; // check left of rect: - gamma = prevValue + (keyMin-prevKey)*valuePerKey; - if (gamma >= valueMin && gamma <= valueMax) - intersections.append(QPointF(keyMin, gamma)); + gamma = prevValuePx + (keyMinPx-prevKeyPx)*valuePerKeyPx; + if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, gamma) : QPointF(gamma, keyMinPx)); // check right of rect: - gamma = prevValue + (keyMax-prevKey)*valuePerKey; - if (gamma >= valueMin && gamma <= valueMax) - intersections.append(QPointF(keyMax, gamma)); + gamma = prevValuePx + (keyMaxPx-prevKeyPx)*valuePerKeyPx; + if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, gamma) : QPointF(gamma, keyMaxPx)); } // handle cases where found points isn't exactly 2: @@ -22570,10 +22916,14 @@ bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double } // possibly re-sort points so optimized point segment has same direction as original segment: - if ((key-prevKey)*(intersections.at(1).x()-intersections.at(0).x()) + (value-prevValue)*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction + double xDelta = keyPx-prevKeyPx; + double yDelta = valuePx-prevValuePx; + if (mKeyAxis->orientation() != Qt::Horizontal) + qSwap(xDelta, yDelta); + if (xDelta*(intersections.at(1).x()-intersections.at(0).x()) + yDelta*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction intersections.move(0, 1); - crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y()); - crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y()); + crossA = intersections.at(0); + crossB = intersections.at(1); return true; } @@ -22747,7 +23097,7 @@ double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer: /* including file 'src/plottables/plottable-bars.cpp', size 43512 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -23915,7 +24265,7 @@ void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) /* including file 'src/plottables/plottable-statisticalbox.cpp', size 28622 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPStatisticalBoxData @@ -24568,8 +24918,8 @@ QVector QCPStatisticalBox::getWhiskerBarLines(QCPStatisticalBoxDataConta /* end of 'src/plottables/plottable-statisticalbox.cpp' */ -/* including file 'src/plottables/plottable-colormap.cpp', size 47531 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/plottables/plottable-colormap.cpp', size 47881 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPColorMapData @@ -25226,6 +25576,7 @@ QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) : QCPAbstractPlottable(keyAxis, valueAxis), mDataScaleType(QCPAxis::stLinear), mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))), + mGradient(QCPColorGradient::gpCold), mInterpolate(true), mTightBoundary(false), mMapImageInvalidated(true) @@ -25552,52 +25903,60 @@ void QCPColorMap::updateMapImage() else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor)) mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), format); - QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage - if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) + if (mMapImage.isNull()) { - // resize undersampled map image to actual key/value cell sizes: - if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize)) - mUndersampledMapImage = QImage(QSize(keySize, valueSize), format); - else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize)) - mUndersampledMapImage = QImage(QSize(valueSize, keySize), format); - localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image - } else if (!mUndersampledMapImage.isNull()) - mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it - - const double *rawData = mMapData->mData; - const unsigned char *rawAlpha = mMapData->mAlpha; - if (keyAxis->orientation() == Qt::Horizontal) + qDebug() << Q_FUNC_INFO << "Couldn't create map image (possibly too large for memory)"; + mMapImage = QImage(QSize(10, 10), format); + mMapImage.fill(Qt::black); + } else { - const int lineCount = valueSize; - const int rowCount = keySize; - for (int line=0; line 1 || valueOversamplingFactor > 1) { - QRgb* pixels = reinterpret_cast(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) - if (rawAlpha) - mGradient.colorize(rawData+line*rowCount, rawAlpha+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic); - else - mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic); - } - } else // keyAxis->orientation() == Qt::Vertical - { - const int lineCount = keySize; - const int rowCount = valueSize; - for (int line=0; line(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) - if (rawAlpha) - mGradient.colorize(rawData+line, rawAlpha+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic); - else - mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic); - } - } - - if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) - { + // resize undersampled map image to actual key/value cell sizes: + if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize)) + mUndersampledMapImage = QImage(QSize(keySize, valueSize), format); + else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize)) + mUndersampledMapImage = QImage(QSize(valueSize, keySize), format); + localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image + } else if (!mUndersampledMapImage.isNull()) + mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it + + const double *rawData = mMapData->mData; + const unsigned char *rawAlpha = mMapData->mAlpha; if (keyAxis->orientation() == Qt::Horizontal) - mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); - else - mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); + { + const int lineCount = valueSize; + const int rowCount = keySize; + for (int line=0; line(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) + if (rawAlpha) + mGradient.colorize(rawData+line*rowCount, rawAlpha+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic); + else + mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic); + } + } else // keyAxis->orientation() == Qt::Vertical + { + const int lineCount = keySize; + const int rowCount = valueSize; + for (int line=0; line(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) + if (rawAlpha) + mGradient.colorize(rawData+line, rawAlpha+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic); + else + mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic); + } + } + + if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) + { + if (keyAxis->orientation() == Qt::Horizontal) + mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); + else + mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); + } } mMapData->mDataModified = false; mMapImageInvalidated = false; @@ -25695,7 +26054,7 @@ void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const /* including file 'src/plottables/plottable-financial.cpp', size 42610 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPFinancialData @@ -26649,8 +27008,8 @@ QRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator i /* end of 'src/plottables/plottable-financial.cpp' */ -/* including file 'src/plottables/plottable-errorbar.cpp', size 37210 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/plottables/plottable-errorbar.cpp', size 37355 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPErrorBarsData @@ -27376,8 +27735,8 @@ void QCPErrorBars::getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index); if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y())) return; - QCPAxis *errorAxis = mErrorType == etValueError ? mValueAxis : mKeyAxis; - QCPAxis *orthoAxis = mErrorType == etValueError ? mKeyAxis : mValueAxis; + QCPAxis *errorAxis = mErrorType == etValueError ? mValueAxis.data() : mKeyAxis.data(); + QCPAxis *orthoAxis = mErrorType == etValueError ? mKeyAxis.data() : mValueAxis.data(); const double centerErrorAxisPixel = errorAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y(); const double centerOrthoAxisPixel = orthoAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y(); const double centerErrorAxisCoord = errorAxis->pixelToCoord(centerErrorAxisPixel); // depending on plottable, this might be different from just mDataPlottable->interface1D()->dataMainKey/Value @@ -27501,6 +27860,11 @@ double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataCo closestData = mDataContainer->constEnd(); if (!mDataPlottable || mDataContainer->isEmpty()) return -1.0; + if (!mKeyAxis || !mValueAxis) + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + return -1.0; + } QCPErrorBarsDataContainer::const_iterator begin, end; getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount())); @@ -27606,7 +27970,7 @@ bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &lin /* including file 'src/items/item-straightline.cpp', size 7592 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemStraightLine @@ -27787,7 +28151,7 @@ QPen QCPItemStraightLine::mainPen() const /* including file 'src/items/item-line.cpp', size 8498 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemLine @@ -28018,7 +28382,7 @@ QPen QCPItemLine::mainPen() const /* including file 'src/items/item-curve.cpp', size 7159 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemCurve @@ -28183,7 +28547,7 @@ QPen QCPItemCurve::mainPen() const /* including file 'src/items/item-rect.cpp', size 6479 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemRect @@ -28340,7 +28704,7 @@ QBrush QCPItemRect::mainBrush() const /* including file 'src/items/item-text.cpp', size 13338 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemText @@ -28688,7 +29052,7 @@ QBrush QCPItemText::mainBrush() const /* including file 'src/items/item-ellipse.cpp', size 7863 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemEllipse @@ -28876,7 +29240,7 @@ QBrush QCPItemEllipse::mainBrush() const /* including file 'src/items/item-pixmap.cpp', size 10615 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemPixmap @@ -29146,7 +29510,7 @@ QPen QCPItemPixmap::mainPen() const /* including file 'src/items/item-tracer.cpp', size 14624 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemTracer @@ -29516,7 +29880,7 @@ QBrush QCPItemTracer::mainBrush() const /* including file 'src/items/item-bracket.cpp', size 10687 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemBracket diff --git a/Desktop_Interface/ui_elements/qcp2/qcustomplot.h b/Desktop_Interface/ui_elements/qcp2/qcustomplot.h index 25316bf8..278fbf82 100644 --- a/Desktop_Interface/ui_elements/qcp2/qcustomplot.h +++ b/Desktop_Interface/ui_elements/qcp2/qcustomplot.h @@ -1,7 +1,7 @@ /*************************************************************************** ** ** ** QCustomPlot, an easy to use, modern plotting widget for Qt ** -** Copyright (C) 2011-2016 Emanuel Eichhammer ** +** Copyright (C) 2011-2017 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: 13.09.16 ** -** Version: 2.0.0-beta ** +** Date: 04.09.17 ** +** Version: 2.0.0 ** ****************************************************************************/ #ifndef QCUSTOMPLOT_H @@ -41,7 +41,10 @@ #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) - #define QCP_DEVICEPIXELRATIO_SUPPORTED +# define QCP_DEVICEPIXELRATIO_SUPPORTED +# if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +# define QCP_DEVICEPIXELRATIO_FLOAT +# endif #endif #include @@ -86,7 +89,6 @@ # include # include # include -# include #endif class QCPPainter; @@ -110,8 +112,8 @@ class QCPColorMap; class QCPColorScale; class QCPBars; -/* including file 'src/global.h', size 16131 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/global.h', size 16225 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ // decl definitions for shared library compilation/usage: #if defined(QCUSTOMPLOT_COMPILE_LIBRARY) @@ -375,7 +377,7 @@ Q_DECLARE_METATYPE(QCP::SelectionType) /* including file 'src/vector2d.h', size 4928 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPVector2D { @@ -449,7 +451,7 @@ inline QDebug operator<< (QDebug d, const QCPVector2D &vec) /* including file 'src/painter.h', size 4035 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPPainter : public QPainter { @@ -508,7 +510,7 @@ Q_DECLARE_METATYPE(QCPPainter::PainterMode) /* including file 'src/paintbuffer.h', size 4958 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAbstractPaintBuffer { @@ -589,7 +591,7 @@ protected: #ifdef QCP_OPENGL_FBO -class QCP_LIB_DECL QCPPaintBufferGlFbo : public QCPAbstractPaintBuffer, protected QOpenGLFunctions +class QCP_LIB_DECL QCPPaintBufferGlFbo : public QCPAbstractPaintBuffer { public: explicit QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer glContext, QWeakPointer glPaintDevice); @@ -616,7 +618,7 @@ protected: /* including file 'src/layer.h', size 6885 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPLayer : public QObject { @@ -765,7 +767,7 @@ private: /* including file 'src/axis/range.h', size 5280 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPRange { @@ -883,7 +885,7 @@ inline const QCPRange operator/(const QCPRange& range, double value) /* including file 'src/selection.h', size 8579 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPDataRange { @@ -1087,7 +1089,7 @@ inline QDebug operator<< (QDebug d, const QCPDataSelection &selection) /* including file 'src/selectionrect.h', size 3338 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPSelectionRect : public QCPLayerable { @@ -1140,8 +1142,8 @@ protected: /* end of 'src/selectionrect.h' */ -/* including file 'src/layout.h', size 13128 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layout.h', size 14224 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPMarginGroup : public QObject { @@ -1185,6 +1187,7 @@ class QCP_LIB_DECL QCPLayoutElement : public QCPLayerable Q_PROPERTY(QMargins minimumMargins READ minimumMargins WRITE setMinimumMargins) Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize) Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize) + Q_PROPERTY(SizeConstraintRect sizeConstraintRect READ sizeConstraintRect WRITE setSizeConstraintRect) /// \endcond public: /*! @@ -1196,6 +1199,19 @@ public: ,upLayout ///< Final phase in which the layout system places the rects of the elements }; Q_ENUMS(UpdatePhase) + + /*! + Defines to which rect of a layout element the size constraints that can be set via \ref + setMinimumSize and \ref setMaximumSize apply. The outer rect (\ref outerRect) includes the + margins (e.g. in the case of a QCPAxisRect the axis labels), whereas the inner rect (\ref rect) + does not. + + \see setSizeConstraintRect + */ + enum SizeConstraintRect { scrInnerRect ///< Minimum/Maximum size constraints apply to inner rect + , scrOuterRect ///< Minimum/Maximum size constraints apply to outer rect, thus include layout element margins + }; + Q_ENUMS(SizeConstraintRect) explicit QCPLayoutElement(QCustomPlot *parentPlot=0); virtual ~QCPLayoutElement(); @@ -1209,6 +1225,7 @@ public: QCP::MarginSides autoMargins() const { return mAutoMargins; } 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); } QHash marginGroups() const { return mMarginGroups; } @@ -1221,12 +1238,13 @@ public: void setMinimumSize(int width, int height); void setMaximumSize(const QSize &size); void setMaximumSize(int width, int height); + void setSizeConstraintRect(SizeConstraintRect constraintRect); void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group); // introduced virtual methods: virtual void update(UpdatePhase phase); - virtual QSize minimumSizeHint() const; - virtual QSize maximumSizeHint() const; + virtual QSize minimumOuterSizeHint() const; + virtual QSize maximumOuterSizeHint() const; virtual QList elements(bool recursive) const; // reimplemented virtual methods: @@ -1236,6 +1254,7 @@ protected: // property members: QCPLayout *mParentLayout; QSize mMinimumSize, mMaximumSize; + SizeConstraintRect mSizeConstraintRect; QRect mRect, mOuterRect; QMargins mMargins, mMinimumMargins; QCP::MarginSides mAutoMargins; @@ -1291,6 +1310,8 @@ protected: void adoptElement(QCPLayoutElement *el); void releaseElement(QCPLayoutElement *el); QVector getSectionSizes(QVector maxSizes, QVector minSizes, QVector stretchFactors, int totalSize) const; + static QSize getFinalMinimumOuterSize(const QCPLayoutElement *el); + static QSize getFinalMaximumOuterSize(const QCPLayoutElement *el); private: Q_DISABLE_COPY(QCPLayout) @@ -1356,8 +1377,8 @@ public: virtual bool take(QCPLayoutElement* element) Q_DECL_OVERRIDE; virtual QList elements(bool recursive) const Q_DECL_OVERRIDE; virtual void simplify() Q_DECL_OVERRIDE; - virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; - virtual QSize maximumSizeHint() const Q_DECL_OVERRIDE; + virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE; + virtual QSize maximumOuterSizeHint() const Q_DECL_OVERRIDE; // non-virtual methods: QCPLayoutElement *element(int row, int column) const; @@ -1443,7 +1464,7 @@ Q_DECLARE_METATYPE(QCPLayoutInset::InsetPlacement) /* including file 'src/lineending.h', size 4426 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPLineEnding { @@ -1507,7 +1528,7 @@ Q_DECLARE_METATYPE(QCPLineEnding::EndingStyle) /* including file 'src/axis/axisticker.h', size 4177 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAxisTicker { @@ -1568,7 +1589,7 @@ Q_DECLARE_METATYPE(QSharedPointer) /* including file 'src/axis/axistickerdatetime.h', size 3289 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAxisTickerDateTime : public QCPAxisTicker { @@ -1608,8 +1629,8 @@ protected: /* end of 'src/axis/axistickerdatetime.h' */ -/* including file 'src/axis/axistickertime.h', size 3288 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/axis/axistickertime.h', size 3542 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAxisTickerTime : public QCPAxisTicker { @@ -1620,11 +1641,11 @@ public: \see setFieldWidth, setTimeFormat */ - enum TimeUnit { tuMilliseconds - ,tuSeconds - ,tuMinutes - ,tuHours - ,tuDays + enum TimeUnit { tuMilliseconds ///< Milliseconds, one thousandth of a second (%%z in \ref setTimeFormat) + ,tuSeconds ///< Seconds (%%s in \ref setTimeFormat) + ,tuMinutes ///< Minutes (%%m in \ref setTimeFormat) + ,tuHours ///< Hours (%%h in \ref setTimeFormat) + ,tuDays ///< Days (%%d in \ref setTimeFormat) }; Q_ENUMS(TimeUnit) @@ -1661,7 +1682,7 @@ Q_DECLARE_METATYPE(QCPAxisTickerTime::TimeUnit) /* including file 'src/axis/axistickerfixed.h', size 3308 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAxisTickerFixed : public QCPAxisTicker { @@ -1703,7 +1724,7 @@ Q_DECLARE_METATYPE(QCPAxisTickerFixed::ScaleStrategy) /* including file 'src/axis/axistickertext.h', size 3085 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAxisTickerText : public QCPAxisTicker { @@ -1742,7 +1763,7 @@ protected: /* including file 'src/axis/axistickerpi.h', size 3911 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAxisTickerPi : public QCPAxisTicker { @@ -1801,7 +1822,7 @@ Q_DECLARE_METATYPE(QCPAxisTickerPi::FractionStyle) /* including file 'src/axis/axistickerlog.h', size 2663 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAxisTickerLog : public QCPAxisTicker { @@ -1833,8 +1854,8 @@ protected: /* end of 'src/axis/axistickerlog.h' */ -/* including file 'src/axis/axis.h', size 20230 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/axis/axis.h', size 20634 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPGrid :public QCPLayerable { @@ -2147,6 +2168,9 @@ protected: QVector mSubTickVector; bool mCachedMarginValid; int mCachedMargin; + bool mDragging; + QCPRange mDragStartRange; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; // introduced virtual methods: virtual int calculateMargin(); @@ -2158,6 +2182,11 @@ protected: // 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); + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos); + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos); + virtual void wheelEvent(QWheelEvent *event); // non-virtual methods: void setupTickVectors(); @@ -2254,7 +2283,7 @@ protected: /* including file 'src/scatterstyle.h', size 7275 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPScatterStyle { @@ -2360,8 +2389,8 @@ Q_DECLARE_METATYPE(QCPScatterStyle::ScatterShape) /* end of 'src/scatterstyle.h' */ -/* including file 'src/datacontainer.h', size 4535 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/datacontainer.h', size 4596 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ /*! \relates QCPDataContainer Returns whether the sort key of \a a is less than the sort key of \a b. @@ -2372,7 +2401,7 @@ template inline bool qcpLessThanSortKey(const DataType &a, const DataType &b) { return a.sortKey() < b.sortKey(); } template -class QCP_LIB_DECL QCPDataContainer +class QCPDataContainer // no QCP_LIB_DECL, template class ends up in header (cpp included below) { public: typedef typename QVector::const_iterator const_iterator; @@ -2430,8 +2459,8 @@ protected: // include implementation in header since it is a class template: -/* including file 'src/datacontainer.cpp', size 31224 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/datacontainer.cpp', size 31349 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPDataContainer @@ -3124,8 +3153,9 @@ QCPRange QCPDataContainer::valueRange(bool &foundRange, QCP::SignDomai /*! Makes sure \a begin and \a end mark a data range that is both within the bounds of this data - container's data, as well as within the specified \a dataRange. - + container's data, as well as within the specified \a dataRange. The initial range described by + the passed iterators \a begin and \a end is never expanded, only contracted if necessary. + This function doesn't require for \a dataRange to be within the bounds of this data container's valid range. */ @@ -3205,7 +3235,7 @@ void QCPDataContainer::performAutoSqueeze() /* including file 'src/plottable.h', size 8312 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPSelectionDecorator { @@ -3361,8 +3391,8 @@ private: /* end of 'src/plottable.h' */ -/* including file 'src/item.h', size 9368 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/item.h', size 9384 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemAnchor { @@ -3439,7 +3469,7 @@ public: QCPAxis *keyAxis() const { return mKeyAxis.data(); } QCPAxis *valueAxis() const { return mValueAxis.data(); } QCPAxisRect *axisRect() const; - virtual QPointF pixelPosition() const; + virtual QPointF pixelPosition() const Q_DECL_OVERRIDE; // setters: void setType(PositionType type); @@ -3546,8 +3576,8 @@ private: /* end of 'src/item.h' */ -/* including file 'src/core.h', size 14797 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/core.h', size 14886 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCustomPlot : public QWidget { @@ -3752,7 +3782,9 @@ protected: QPoint mMousePressPos; bool mMouseHasMoved; QPointer mMouseEventLayerable; + QPointer mMouseSignalLayerable; QVariant mMouseEventLayerableDetails; + QVariant mMouseSignalLayerableDetails; bool mReplotting; bool mReplotQueued; int mOpenGlMultisamples; @@ -3812,12 +3844,13 @@ Q_DECLARE_METATYPE(QCustomPlot::RefreshPriority) /* end of 'src/core.h' */ -/* including file 'src/plottable1d.h', size 4250 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/plottable1d.h', size 4544 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ -class QCP_LIB_DECL QCPPlottableInterface1D +class QCPPlottableInterface1D { public: + virtual ~QCPPlottableInterface1D() {} // introduced pure virtual methods: virtual int dataCount() const = 0; virtual double dataMainKey(int index) const = 0; @@ -3832,7 +3865,7 @@ public: }; template -class QCP_LIB_DECL QCPAbstractPlottable1D : public QCPAbstractPlottable, public QCPPlottableInterface1D +class QCPAbstractPlottable1D : public QCPAbstractPlottable, public QCPPlottableInterface1D // no QCP_LIB_DECL, template class ends up in header (cpp included below) { // No Q_OBJECT macro due to template class @@ -3841,20 +3874,20 @@ public: virtual ~QCPAbstractPlottable1D(); // virtual methods of 1d plottable interface: - virtual int dataCount() const; - virtual double dataMainKey(int index) const; - virtual double dataSortKey(int index) const; - virtual double dataMainValue(int index) const; - virtual QCPRange dataValueRange(int index) const; - virtual QPointF dataPixelPosition(int index) const; - virtual bool sortKeyIsMainKey() const; - virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const; - virtual int findBegin(double sortKey, bool expandedRange=true) const; - virtual int findEnd(double sortKey, bool expandedRange=true) const; + virtual int dataCount() const Q_DECL_OVERRIDE; + virtual double dataMainKey(int index) const Q_DECL_OVERRIDE; + virtual double dataSortKey(int index) const Q_DECL_OVERRIDE; + virtual double dataMainValue(int index) const Q_DECL_OVERRIDE; + virtual QCPRange dataValueRange(int index) const Q_DECL_OVERRIDE; + virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE; + virtual bool sortKeyIsMainKey() const Q_DECL_OVERRIDE; + virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE; + virtual int findBegin(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE; + virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE; - // virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; - virtual QCPPlottableInterface1D *interface1D() { return this; } + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual QCPPlottableInterface1D *interface1D() Q_DECL_OVERRIDE { return this; } protected: // property members: @@ -3872,7 +3905,7 @@ private: // include implementation in header since it is a class template: /* including file 'src/plottable1d.cpp', size 22240 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPPlottableInterface1D @@ -4392,7 +4425,7 @@ void QCPAbstractPlottable1D::drawPolyline(QCPPainter *painter, const Q /* including file 'src/colorgradient.h', size 6243 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPColorGradient { @@ -4474,8 +4507,8 @@ Q_DECLARE_METATYPE(QCPColorGradient::GradientPreset) /* end of 'src/colorgradient.h' */ -/* including file 'src/selectiondecorator-bracket.h', size 4426 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/selectiondecorator-bracket.h', size 4442 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPSelectionDecoratorBracket : public QCPSelectionDecorator { @@ -4521,7 +4554,7 @@ public: virtual void drawBracket(QCPPainter *painter, int direction) const; // virtual methods: - virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection); + virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection) Q_DECL_OVERRIDE; protected: // property members: @@ -4543,8 +4576,8 @@ Q_DECLARE_METATYPE(QCPSelectionDecoratorBracket::BracketStyle) /* end of 'src/selectiondecorator-bracket.h' */ -/* including file 'src/layoutelements/layoutelement-axisrect.h', size 7528 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layoutelements/layoutelement-axisrect.h', size 7507 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement { @@ -4641,7 +4674,6 @@ protected: // non-property members: QList mDragStartHorzRange, mDragStartVertRange; QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; - QPoint mDragStart; bool mDragging; QHash > mAxes; @@ -4670,8 +4702,8 @@ private: /* end of 'src/layoutelements/layoutelement-axisrect.h' */ -/* including file 'src/layoutelements/layoutelement-legend.h', size 10392 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layoutelements/layoutelement-legend.h', size 10397 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement { @@ -4752,7 +4784,7 @@ protected: // reimplemented virtual methods: virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; - virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; + virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE; // non-virtual methods: QPen getIconBorderPen() const; @@ -4888,8 +4920,8 @@ Q_DECLARE_METATYPE(QCPLegend::SelectablePart) /* end of 'src/layoutelements/layoutelement-legend.h' */ -/* including file 'src/layoutelements/layoutelement-textelement.h', size 5343 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layoutelements/layoutelement-textelement.h', size 5353 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPTextElement : public QCPLayoutElement { @@ -4956,8 +4988,8 @@ protected: // reimplemented virtual methods: virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; - virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; - virtual QSize maximumSizeHint() const Q_DECL_OVERRIDE; + virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE; + virtual QSize maximumOuterSizeHint() 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; @@ -4975,8 +5007,8 @@ private: /* end of 'src/layoutelements/layoutelement-textelement.h' */ -/* including file 'src/layoutelements/layoutelement-colorscale.h', size 5907 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/layoutelements/layoutelement-colorscale.h', size 5923 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCPColorScaleAxisRectPrivate : public QCPAxisRect @@ -4995,7 +5027,7 @@ protected: using QCPAxisRect::mouseReleaseEvent; using QCPAxisRect::wheelEvent; using QCPAxisRect::update; - virtual void draw(QCPPainter *painter); + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; void updateGradientImage(); Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts); Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts); @@ -5083,8 +5115,8 @@ private: /* end of 'src/layoutelements/layoutelement-colorscale.h' */ -/* including file 'src/plottables/plottable-graph.h', size 8826 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/plottables/plottable-graph.h', size 9294 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPGraphData { @@ -5202,11 +5234,12 @@ protected: QVector dataToStepRightLines(const QVector &data) const; QVector dataToStepCenterLines(const QVector &data) const; QVector dataToImpulseLines(const QVector &data) const; - void addFillBasePoints(QVector *lines) const; - void removeFillBasePoints(QVector *lines) const; - QPointF lowerFillBasePoint(double lowerKey) const; - QPointF upperFillBasePoint(double upperKey) const; - const QPolygonF getChannelFillPolygon(const QVector *lines) const; + QVector getNonNanSegments(const QVector *lineData, Qt::Orientation keyOrientation) const; + QVector > getOverlappingSegments(QVector thisSegments, const QVector *thisData, QVector otherSegments, const QVector *otherData) const; + bool segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const; + QPointF getFillBasePoint(QPointF matchingDataPoint) const; + const QPolygonF getFillPolygon(const QVector *lineData, QCPDataRange segment) const; + const QPolygonF getChannelFillPolygon(const QVector *lineData, QCPDataRange thisSegment, const QVector *otherData, QCPDataRange otherSegment) const; int findIndexBelowX(const QVector *data, double x) const; int findIndexAboveX(const QVector *data, double x) const; int findIndexBelowY(const QVector *data, double y) const; @@ -5222,7 +5255,7 @@ Q_DECLARE_METATYPE(QCPGraph::LineStyle) /* including file 'src/plottables/plottable-curve.h', size 7409 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPCurveData { @@ -5337,7 +5370,7 @@ Q_DECLARE_METATYPE(QCPCurve::LineStyle) /* including file 'src/plottables/plottable-bars.h', size 8924 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPBarsGroup : public QObject { @@ -5525,7 +5558,7 @@ Q_DECLARE_METATYPE(QCPBars::WidthType) /* including file 'src/plottables/plottable-statisticalbox.h', size 7516 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPStatisticalBoxData { @@ -5642,7 +5675,7 @@ protected: /* including file 'src/plottables/plottable-colormap.h', size 7070 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPColorMapData { @@ -5778,7 +5811,7 @@ protected: /* including file 'src/plottables/plottable-financial.h', size 8622 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPFinancialData { @@ -5916,8 +5949,8 @@ Q_DECLARE_METATYPE(QCPFinancial::ChartStyle) /* end of 'src/plottables/plottable-financial.h' */ -/* including file 'src/plottables/plottable-errorbar.h', size 7567 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* including file 'src/plottables/plottable-errorbar.h', size 7727 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPErrorBarsData { @@ -5996,16 +6029,16 @@ public: void addData(double errorMinus, double errorPlus); // virtual methods of 1d plottable interface: - virtual int dataCount() const; - virtual double dataMainKey(int index) const; - virtual double dataSortKey(int index) const; - virtual double dataMainValue(int index) const; - virtual QCPRange dataValueRange(int index) const; - virtual QPointF dataPixelPosition(int index) const; - virtual bool sortKeyIsMainKey() const; - virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const; - virtual int findBegin(double sortKey, bool expandedRange=true) const; - virtual int findEnd(double sortKey, bool expandedRange=true) const; + virtual int dataCount() const Q_DECL_OVERRIDE; + virtual double dataMainKey(int index) const Q_DECL_OVERRIDE; + virtual double dataSortKey(int index) const Q_DECL_OVERRIDE; + virtual double dataMainValue(int index) const Q_DECL_OVERRIDE; + virtual QCPRange dataValueRange(int index) const Q_DECL_OVERRIDE; + virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE; + virtual bool sortKeyIsMainKey() const Q_DECL_OVERRIDE; + virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE; + virtual int findBegin(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE; + 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; @@ -6042,7 +6075,7 @@ protected: /* including file 'src/items/item-straightline.h', size 3117 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem { @@ -6085,7 +6118,7 @@ protected: /* including file 'src/items/item-line.h', size 3407 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem { @@ -6135,7 +6168,7 @@ protected: /* including file 'src/items/item-curve.h', size 3379 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem { @@ -6186,7 +6219,7 @@ protected: /* including file 'src/items/item-rect.h', size 3688 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem { @@ -6245,7 +6278,7 @@ protected: /* including file 'src/items/item-text.h', size 5554 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemText : public QCPAbstractItem { @@ -6342,7 +6375,7 @@ protected: /* including file 'src/items/item-ellipse.h', size 3868 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem { @@ -6404,7 +6437,7 @@ protected: /* including file 'src/items/item-pixmap.h', size 4373 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem { @@ -6473,7 +6506,7 @@ protected: /* including file 'src/items/item-tracer.h', size 4762 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem { @@ -6559,7 +6592,7 @@ Q_DECLARE_METATYPE(QCPItemTracer::TracerStyle) /* including file 'src/items/item-bracket.h', size 3969 */ -/* commit 633339dadc92cb10c58ef3556b55570685fafb99 2016-09-13 23:54:56 +0200 */ +/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem {