diff options
author | Liang Qi <liang.qi@qt.io> | 2016-09-06 09:22:20 +0200 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2016-09-06 11:22:58 +0300 |
commit | 83d5e7fc85cc07fb180498876d1a346573f747e7 (patch) | |
tree | c451bf6f083a49fe21244c60b2bb9912c28407cb /src | |
parent | 144474348803564eaa7bb4d17bd609d146e74f9f (diff) | |
parent | bf26aa9d15e525fdcf6fd3f59268418533f06790 (diff) |
Merge remote-tracking branch 'origin/5.7' into 5.8
Conflicts:
examples/charts/charts.pro
src/charts/glwidget.cpp
src/chartsqml2/declarativechart.cpp
src/chartsqml2/declarativeopenglrendernode.cpp
tests/auto/auto.pro
Change-Id: If909b4b13844c474bf4898a66ee01ac111d0a248
Diffstat (limited to 'src')
33 files changed, 459 insertions, 310 deletions
diff --git a/src/charts/areachart/areachartitem.cpp b/src/charts/areachart/areachartitem.cpp index d05aa3a3..df3cc8af 100644 --- a/src/charts/areachart/areachartitem.cpp +++ b/src/charts/areachart/areachartitem.cpp @@ -33,6 +33,7 @@ #include <QtCharts/QLineSeries> #include <private/chartpresenter_p.h> #include <private/abstractdomain_p.h> +#include <private/chartdataset_p.h> #include <QtGui/QPainter> #include <QtWidgets/QGraphicsSceneMouseEvent> #include <QtCore/QDebug> @@ -94,10 +95,39 @@ void AreaChartItem::setPresenter(ChartPresenter *presenter) { if (m_upper) m_upper->setPresenter(presenter); - if (m_lower) { + if (m_lower) m_lower->setPresenter(presenter); + ChartItem::setPresenter(presenter); +} + +void AreaChartItem::setUpperSeries(QLineSeries *series) +{ + delete m_upper; + if (series) + m_upper = new AreaBoundItem(this, series); + else + m_upper = 0; + if (m_upper) { + m_upper->setPresenter(presenter()); + fixEdgeSeriesDomain(m_upper); + } else { + updatePath(); + } +} + +void AreaChartItem::setLowerSeries(QLineSeries *series) +{ + delete m_lower; + if (series) + m_lower = new AreaBoundItem(this, series); + else + m_lower = 0; + if (m_lower) { + m_lower->setPresenter(presenter()); + fixEdgeSeriesDomain(m_lower); + } else { + updatePath(); } - ChartItem::setPresenter(presenter); } QRectF AreaChartItem::boundingRect() const @@ -115,31 +145,34 @@ void AreaChartItem::updatePath() QPainterPath path; QRectF rect(QPointF(0,0),domain()->size()); - path = m_upper->path(); - - if (m_lower) { - // Note: Polarcharts always draw area correctly only when both series have equal width or are - // fully displayed. If one series is partally off-chart, the connecting line between - // the series does not attach to the end of the partially hidden series but to the point - // where it intersects the axis line. The problem is especially noticeable when one of the series - // is entirely off-chart, in which case the connecting line connects two ends of the - // visible series. - // This happens because we get the paths from linechart, which omits off-chart segments. - // To properly fix, linechart would need to provide true full path, in right, left, and the rest - // portions to enable proper clipping. However, combining those to single visually unified area - // would be a nightmare, since they would have to be painted separately. - path.connectPath(m_lower->path().toReversed()); - } else { - QPointF first = path.pointAtPercent(0); - QPointF last = path.pointAtPercent(1); - if (presenter()->chartType() == QChart::ChartTypeCartesian) { - path.lineTo(last.x(), rect.bottom()); - path.lineTo(first.x(), rect.bottom()); - } else { // polar - path.lineTo(rect.center()); + if (m_upper) { + path = m_upper->path(); + + if (m_lower) { + // Note: Polarcharts draw area correctly only when both series have equal width or are + // fully displayed. If one series is partally off-chart, the connecting line between + // the series does not attach to the end of the partially hidden series but to the point + // where it intersects the axis line. The problem is especially noticeable when one of + // the series is entirely off-chart, in which case the connecting line connects two + // ends of the visible series. + // This happens because we get the paths from linechart, which omits off-chart segments. + // To properly fix, linechart would need to provide true full path, in right, left, + // and the rest portions to enable proper clipping. However, combining those to single + // visually unified area would be a nightmare, since they would have to be painted + // separately. + path.connectPath(m_lower->path().toReversed()); + } else { + QPointF first = path.pointAtPercent(0); + QPointF last = path.pointAtPercent(1); + if (presenter()->chartType() == QChart::ChartTypeCartesian) { + path.lineTo(last.x(), rect.bottom()); + path.lineTo(first.x(), rect.bottom()); + } else { // polar + path.lineTo(rect.center()); + } } + path.closeSubpath(); } - path.closeSubpath(); // Only zoom in if the bounding rect of the path fits inside int limits. QWidget::update() uses // a region that has to be compatible with QRect. @@ -171,18 +204,26 @@ void AreaChartItem::handleUpdated() void AreaChartItem::handleDomainUpdated() { - if (m_upper) { - AbstractDomain* d = m_upper->domain(); - d->setSize(domain()->size()); - d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY()); - m_upper->handleDomainUpdated(); - } + fixEdgeSeriesDomain(m_upper); + fixEdgeSeriesDomain(m_lower); +} - if (m_lower) { - AbstractDomain* d = m_lower->domain(); - d->setSize(domain()->size()); - d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY()); - m_lower->handleDomainUpdated(); +void AreaChartItem::fixEdgeSeriesDomain(LineChartItem *edgeSeries) +{ + if (edgeSeries) { + AbstractDomain* mainDomain = domain(); + AbstractDomain* edgeDomain = edgeSeries->domain(); + + if (edgeDomain->type() != mainDomain->type()) { + // Change the domain of edge series to the same type as the area series + edgeDomain = dataSet()->createDomain(mainDomain->type()); + edgeSeries->seriesPrivate()->setDomain(edgeDomain); + } + edgeDomain->setSize(mainDomain->size()); + edgeDomain->setRange(mainDomain->minX(), mainDomain->maxX(), mainDomain->minY(), mainDomain->maxY()); + edgeDomain->setReverseX(mainDomain->isReverseX()); + edgeDomain->setReverseY(mainDomain->isReverseY()); + edgeSeries->handleDomainUpdated(); } } @@ -190,6 +231,7 @@ void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt { Q_UNUSED(widget) Q_UNUSED(option) + painter->save(); painter->setPen(m_linePen); painter->setBrush(m_brush); @@ -199,18 +241,15 @@ void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt else painter->setClipRect(clipRect); - reversePainter(painter, clipRect); - painter->drawPath(m_path); if (m_pointsVisible) { painter->setPen(m_pointPen); - painter->drawPoints(m_upper->geometryPoints()); + if (m_upper) + painter->drawPoints(m_upper->geometryPoints()); if (m_lower) painter->drawPoints(m_lower->geometryPoints()); } - reversePainter(painter, clipRect); - // Draw series point label if (m_pointLabelsVisible) { static const QString xPointTag(QLatin1String("@xPoint")); @@ -239,17 +278,9 @@ void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt // Position text in relation to the point int pointLabelWidth = fm.width(pointLabel); QPointF position(m_upper->geometryPoints().at(i)); - if (!seriesPrivate()->reverseXAxis()) - position.setX(position.x() - pointLabelWidth / 2); - else - position.setX(domain()->size().width() - position.x() - pointLabelWidth / 2); - if (!seriesPrivate()->reverseYAxis()) { - position.setY(position.y() - m_series->upperSeries()->pen().width() / 2 - - labelOffset); - } else { - position.setY(domain()->size().height() - position.y() - - m_series->upperSeries()->pen().width() / 2 - labelOffset); - } + position.setX(position.x() - pointLabelWidth / 2); + position.setY(position.y() - m_series->upperSeries()->pen().width() / 2 + - labelOffset); painter->drawText(position, pointLabel); } } @@ -265,17 +296,9 @@ void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt // Position text in relation to the point int pointLabelWidth = fm.width(pointLabel); QPointF position(m_lower->geometryPoints().at(i)); - if (!seriesPrivate()->reverseXAxis()) - position.setX(position.x() - pointLabelWidth / 2); - else - position.setX(domain()->size().width() - position.x() - pointLabelWidth / 2); - if (!seriesPrivate()->reverseYAxis()) { - position.setY(position.y() - m_series->lowerSeries()->pen().width() / 2 - - labelOffset); - } else { - position.setY(domain()->size().height() - position.y() - - m_series->lowerSeries()->pen().width() / 2 - labelOffset); - } + position.setX(position.x() - pointLabelWidth / 2); + position.setY(position.y() - m_series->lowerSeries()->pen().width() / 2 + - labelOffset); painter->drawText(position, pointLabel); } } @@ -286,7 +309,7 @@ void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt void AreaChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - emit pressed(m_upper->domain()->calculateDomainPoint(event->pos())); + emit pressed(domain()->calculateDomainPoint(event->pos())); m_lastMousePos = event->pos(); m_mousePressed = true; ChartItem::mousePressEvent(event); @@ -308,16 +331,16 @@ void AreaChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) void AreaChartItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - emit released(m_upper->domain()->calculateDomainPoint(m_lastMousePos)); + emit released(domain()->calculateDomainPoint(m_lastMousePos)); if (m_mousePressed) - emit clicked(m_upper->domain()->calculateDomainPoint(m_lastMousePos)); + emit clicked(domain()->calculateDomainPoint(m_lastMousePos)); m_mousePressed = false; ChartItem::mouseReleaseEvent(event); } void AreaChartItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { - emit doubleClicked(m_upper->domain()->calculateDomainPoint(m_lastMousePos)); + emit doubleClicked(domain()->calculateDomainPoint(m_lastMousePos)); ChartItem::mouseDoubleClickEvent(event); } diff --git a/src/charts/areachart/areachartitem_p.h b/src/charts/areachart/areachartitem_p.h index fafd8588..be943877 100644 --- a/src/charts/areachart/areachartitem_p.h +++ b/src/charts/areachart/areachartitem_p.h @@ -68,6 +68,9 @@ public: void setPresenter(ChartPresenter *presenter); QAreaSeries *series() const { return m_series; } + void setUpperSeries(QLineSeries *series); + void setLowerSeries(QLineSeries *series); + protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent *event); @@ -87,6 +90,8 @@ public Q_SLOTS: void handleDomainUpdated(); private: + void fixEdgeSeriesDomain(LineChartItem *edgeSeries); + QAreaSeries *m_series; LineChartItem *m_upper; LineChartItem *m_lower; diff --git a/src/charts/areachart/qareaseries.cpp b/src/charts/areachart/qareaseries.cpp index ebff7f0f..739efd71 100644 --- a/src/charts/areachart/qareaseries.cpp +++ b/src/charts/areachart/qareaseries.cpp @@ -406,13 +406,18 @@ QAbstractSeries::SeriesType QAreaSeries::type() const /*! Sets the \a series that is to be used as the area chart upper series. + If the upper series is null, the area chart is not drawn, even if it has a lower series. */ void QAreaSeries::setUpperSeries(QLineSeries *series) { Q_D(QAreaSeries); + if (d->m_upperSeries != series) { - series->d_ptr->setBlockOpenGL(true); + if (series) + series->d_ptr->setBlockOpenGL(true); d->m_upperSeries = series; + if (!d->m_item.isNull()) + static_cast<AreaChartItem *>(d->m_item.data())->setUpperSeries(series); } } @@ -428,8 +433,13 @@ QLineSeries *QAreaSeries::upperSeries() const void QAreaSeries::setLowerSeries(QLineSeries *series) { Q_D(QAreaSeries); - series->d_ptr->setBlockOpenGL(true); - d->m_lowerSeries = series; + if (d->m_lowerSeries != series) { + if (series) + series->d_ptr->setBlockOpenGL(true); + d->m_lowerSeries = series; + if (!d->m_item.isNull()) + static_cast<AreaChartItem *>(d->m_item.data())->setLowerSeries(series); + } } QLineSeries *QAreaSeries::lowerSeries() const diff --git a/src/charts/axis/qabstractaxis.cpp b/src/charts/axis/qabstractaxis.cpp index 10848d9d..bc1697fe 100644 --- a/src/charts/axis/qabstractaxis.cpp +++ b/src/charts/axis/qabstractaxis.cpp @@ -523,7 +523,7 @@ QPen QAbstractAxis::linePen() const void QAbstractAxis::setLinePenColor(QColor color) { QPen p = linePen(); - if (p.color() != color) { + if (p.color() != color || d_ptr->m_axisPen == QChartPrivate::defaultPen()) { p.setColor(color); setLinePen(p); emit colorChanged(color); @@ -618,31 +618,31 @@ QPen QAbstractAxis::minorGridLinePen() const void QAbstractAxis::setGridLineColor(const QColor &color) { QPen pen = gridLinePen(); - if (color != pen.color()) { + if (color != pen.color() || d_ptr->m_gridLinePen == QChartPrivate::defaultPen()) { pen.setColor(color); - d_ptr->m_gridLinePen = pen; + setGridLinePen(pen); emit gridLineColorChanged(color); } } QColor QAbstractAxis::gridLineColor() { - return d_ptr->m_gridLinePen.color(); + return gridLinePen().color(); } void QAbstractAxis::setMinorGridLineColor(const QColor &color) { QPen pen = minorGridLinePen(); - if (color != pen.color()) { + if (color != pen.color() || d_ptr->m_minorGridLinePen == QChartPrivate::defaultPen()) { pen.setColor(color); - d_ptr->m_minorGridLinePen = pen; + setMinorGridLinePen(pen); emit minorGridLineColorChanged(color); } } QColor QAbstractAxis::minorGridLineColor() { - return d_ptr->m_minorGridLinePen.color(); + return minorGridLinePen().color(); } void QAbstractAxis::setLabelsVisible(bool visible) @@ -717,7 +717,7 @@ int QAbstractAxis::labelsAngle() const void QAbstractAxis::setLabelsColor(QColor color) { QBrush b = labelsBrush(); - if (b.color() != color) { + if (b.color() != color || d_ptr->m_labelsBrush == QChartPrivate::defaultBrush()) { b.setColor(color); setLabelsBrush(b); emit labelsColorChanged(color); @@ -860,7 +860,7 @@ QBrush QAbstractAxis::shadesBrush() const void QAbstractAxis::setShadesColor(QColor color) { QBrush b = shadesBrush(); - if (b.color() != color) { + if (b.color() != color || d_ptr->m_shadesBrush == QChartPrivate::defaultBrush()) { b.setColor(color); setShadesBrush(b); emit shadesColorChanged(color); @@ -874,8 +874,8 @@ QColor QAbstractAxis::shadesColor() const void QAbstractAxis::setShadesBorderColor(QColor color) { - QPen p = d_ptr->m_shadesPen; - if (p.color() != color) { + QPen p = shadesPen(); + if (p.color() != color || d_ptr->m_shadesPen == QChartPrivate::defaultPen()) { p.setColor(color); setShadesPen(p); emit shadesColorChanged(color); diff --git a/src/charts/barchart/qbarset.cpp b/src/charts/barchart/qbarset.cpp index f62fc806..c065787f 100644 --- a/src/charts/barchart/qbarset.cpp +++ b/src/charts/barchart/qbarset.cpp @@ -280,12 +280,19 @@ QT_CHARTS_BEGIN_NAMESPACE /*! \qmlproperty QVariantList BarSet::values - The values of the bar set. You can set either a list of reals or a list of points as values. If you set a list of - reals as values, the values are automatically completed to points by using the index of a value as it's - x-coordinate. For example the following sets have equal values: + The values of the bar set. You can set either a list of reals or a list of points as values. + + If you set a list of reals as values, the values directly define the bar set values. + + If you set a list of points as values, the x-coordinate of the point specifies its zero-based + index in the bar set. The size of the bar set is the highest x-coordinate value + 1. + If a point is missing for any x-coordinate between zero and the highest value, + it gets value zero. + + For example the following sets have equal values: \code - myBarSet1.values = [0, 5, 1, 5]; - myBarSet2.values = [Qt.point(0, 0), Qt.point(1, 5), Qt.point(2, 1), Qt.point(3, 5)]; + myBarSet1.values = [5, 0, 1, 5]; + myBarSet2.values = [Qt.point(0, 5), Qt.point(2, 1), Qt.point(3, 5)]; \endcode */ diff --git a/src/charts/boxplotchart/qboxplotseries.cpp b/src/charts/boxplotchart/qboxplotseries.cpp index 9515c60b..26184735 100644 --- a/src/charts/boxplotchart/qboxplotseries.cpp +++ b/src/charts/boxplotchart/qboxplotseries.cpp @@ -535,9 +535,10 @@ void QBoxPlotSeriesPrivate::handleSeriesRemove(QAbstractSeries *series) QBoxPlotSeries *removedSeries = static_cast<QBoxPlotSeries *>(series); - if (q == removedSeries && m_animation) { - m_animation->stopAll(); - QObject::disconnect(m_chart->d_ptr->m_dataset, 0, removedSeries->d_func(), 0); + if (q == removedSeries) { + if (m_animation) + m_animation->stopAll(); + QObject::disconnect(m_chart->d_ptr->m_dataset, 0, this, 0); } // Test if series removed is me, then don't do anything diff --git a/src/charts/chartdataset_p.h b/src/charts/chartdataset_p.h index bc2917bc..4552ad20 100644 --- a/src/charts/chartdataset_p.h +++ b/src/charts/chartdataset_p.h @@ -81,6 +81,8 @@ public: GLXYSeriesDataManager *glXYSeriesDataManager() { return m_glXYSeriesDataManager; } + AbstractDomain* createDomain(AbstractDomain::DomainType type); + Q_SIGNALS: void axisAdded(QAbstractAxis* axis); void axisRemoved(QAbstractAxis* axis); @@ -92,7 +94,6 @@ private: void createAxes(QAbstractAxis::AxisTypes type, Qt::Orientation orientation); QAbstractAxis *createAxis(QAbstractAxis::AxisType type, Qt::Orientation orientation); AbstractDomain::DomainType selectDomain(QList<QAbstractAxis* > axes); - AbstractDomain* createDomain(AbstractDomain::DomainType type); void deleteAllAxes(); void deleteAllSeries(); void findMinMaxForSeries(QList<QAbstractSeries *> series,Qt::Orientations orientation, qreal &min, qreal &max); diff --git a/src/charts/chartitem.cpp b/src/charts/chartitem.cpp index 58a97d01..fda019c9 100644 --- a/src/charts/chartitem.cpp +++ b/src/charts/chartitem.cpp @@ -52,19 +52,6 @@ void ChartItem::handleDomainUpdated() qWarning() << __FUNCTION__<< "Slot not implemented"; } -void ChartItem::reversePainter(QPainter *painter, const QRectF &clipRect) -{ - if (m_series->reverseXAxis()) { - painter->translate(clipRect.width(), 0); - painter->scale(-1, 1); - } - - if (m_series->reverseYAxis()) { - painter->translate(0, clipRect.height()); - painter->scale(1, -1); - } -} - #include "moc_chartitem_p.cpp" QT_CHARTS_END_NAMESPACE diff --git a/src/charts/chartitem_p.h b/src/charts/chartitem_p.h index 266d246b..ad4266d3 100644 --- a/src/charts/chartitem_p.h +++ b/src/charts/chartitem_p.h @@ -55,7 +55,6 @@ public: public Q_SLOTS: virtual void handleDomainUpdated(); - void reversePainter(QPainter *painter, const QRectF &clipRect); QAbstractSeriesPrivate* seriesPrivate() const {return m_series;} protected: diff --git a/src/charts/chartpresenter.cpp b/src/charts/chartpresenter.cpp index f15bc880..a4955141 100644 --- a/src/charts/chartpresenter.cpp +++ b/src/charts/chartpresenter.cpp @@ -147,6 +147,7 @@ void ChartPresenter::handleSeriesRemoved(QAbstractSeries *series) ChartItem *chart = series->d_ptr->m_item.take(); chart->hide(); chart->disconnect(); + series->disconnect(chart); chart->deleteLater(); if (chart->animation()) chart->animation()->stopAndDestroyLater(); diff --git a/src/charts/domain/abstractdomain.cpp b/src/charts/domain/abstractdomain.cpp index fb906c66..9186686e 100644 --- a/src/charts/domain/abstractdomain.cpp +++ b/src/charts/domain/abstractdomain.cpp @@ -45,7 +45,9 @@ AbstractDomain::AbstractDomain(QObject *parent) m_zoomResetMinX(0), m_zoomResetMaxX(0), m_zoomResetMinY(0), - m_zoomResetMaxY(0) + m_zoomResetMaxY(0), + m_reverseX(false), + m_reverseY(false) { } @@ -114,15 +116,6 @@ bool AbstractDomain::isEmpty() const return qFuzzyCompare(spanX(), 0) || qFuzzyCompare(spanY(), 0) || m_size.isEmpty(); } -QPointF AbstractDomain::calculateDomainPoint(const QPointF &point) const -{ - const qreal deltaX = m_size.width() / (m_maxX - m_minX); - const qreal deltaY = m_size.height() / (m_maxY - m_minY); - qreal x = point.x() / deltaX + m_minX; - qreal y = (point.y() - m_size.height()) / (-deltaY) + m_minY; - return QPointF(x, y); -} - // handlers void AbstractDomain::handleVerticalAxisRangeChanged(qreal min, qreal max) @@ -135,6 +128,18 @@ void AbstractDomain::handleHorizontalAxisRangeChanged(qreal min, qreal max) setRangeX(min, max); } +void AbstractDomain::handleReverseXChanged(bool reverse) +{ + m_reverseX = reverse; + emit updated(); +} + +void AbstractDomain::handleReverseYChanged(bool reverse) +{ + m_reverseY = reverse; + emit updated(); +} + void AbstractDomain::blockRangeSignals(bool block) { if (m_signalsBlocked!=block) { @@ -207,11 +212,17 @@ bool AbstractDomain::attachAxis(QAbstractAxis *axis) if (axis->orientation() == Qt::Vertical) { QObject::connect(axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), this, SLOT(handleVerticalAxisRangeChanged(qreal,qreal))); QObject::connect(this, SIGNAL(rangeVerticalChanged(qreal,qreal)), axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); + QObject::connect(axis, &QAbstractAxis::reverseChanged, + this, &AbstractDomain::handleReverseYChanged); + m_reverseY = axis->isReverse(); } if (axis->orientation() == Qt::Horizontal) { QObject::connect(axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), this, SLOT(handleHorizontalAxisRangeChanged(qreal,qreal))); QObject::connect(this, SIGNAL(rangeHorizontalChanged(qreal,qreal)), axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); + QObject::connect(axis, &QAbstractAxis::reverseChanged, + this, &AbstractDomain::handleReverseXChanged); + m_reverseX = axis->isReverse(); } return true; @@ -222,12 +233,16 @@ bool AbstractDomain::detachAxis(QAbstractAxis *axis) if (axis->orientation() == Qt::Vertical) { QObject::disconnect(axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), this, SLOT(handleVerticalAxisRangeChanged(qreal,qreal))); QObject::disconnect(this, SIGNAL(rangeVerticalChanged(qreal,qreal)), axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); - } + QObject::disconnect(axis, &QAbstractAxis::reverseChanged, + this, &AbstractDomain::handleReverseYChanged); + } if (axis->orientation() == Qt::Horizontal) { QObject::disconnect(axis->d_ptr.data(), SIGNAL(rangeChanged(qreal,qreal)), this, SLOT(handleHorizontalAxisRangeChanged(qreal,qreal))); QObject::disconnect(this, SIGNAL(rangeHorizontalChanged(qreal,qreal)), axis->d_ptr.data(), SLOT(handleRangeChanged(qreal,qreal))); - } + QObject::disconnect(axis, &QAbstractAxis::reverseChanged, + this, &AbstractDomain::handleReverseXChanged); + } return true; } @@ -269,6 +284,22 @@ void AbstractDomain::adjustLogDomainRanges(qreal &min, qreal &max) } } +// This function fixes the zoom rect based on axis reversals +QRectF AbstractDomain::fixZoomRect(const QRectF &rect) +{ + QRectF fixRect = rect; + if (m_reverseX || m_reverseY) { + QPointF center = rect.center(); + if (m_reverseX) + center.setX(m_size.width() - center.x()); + if (m_reverseY) + center.setY(m_size.height() - center.y()); + fixRect.moveCenter(QPointF(center.x(), center.y())); + } + + return fixRect; +} + #include "moc_abstractdomain_p.cpp" diff --git a/src/charts/domain/abstractdomain_p.h b/src/charts/domain/abstractdomain_p.h index 1e1782bf..b6f5d7dd 100644 --- a/src/charts/domain/abstractdomain_p.h +++ b/src/charts/domain/abstractdomain_p.h @@ -111,6 +111,12 @@ public: static void looseNiceNumbers(qreal &min, qreal &max, int &ticksCount); static qreal niceNumber(qreal x, bool ceiling); + void setReverseX(bool reverse) { m_reverseX = reverse; } + void setReverseY(bool reverse) { m_reverseY = reverse; } + + bool isReverseX() const { return m_reverseX; } + bool isReverseY() const { return m_reverseY; } + Q_SIGNALS: void updated(); void rangeHorizontalChanged(qreal min, qreal max); @@ -119,9 +125,12 @@ Q_SIGNALS: public Q_SLOTS: void handleVerticalAxisRangeChanged(qreal min,qreal max); void handleHorizontalAxisRangeChanged(qreal min,qreal max); + void handleReverseXChanged(bool reverse); + void handleReverseYChanged(bool reverse); protected: void adjustLogDomainRanges(qreal &min, qreal &max); + QRectF fixZoomRect(const QRectF &rect); qreal m_minX; qreal m_maxX; @@ -134,6 +143,8 @@ protected: qreal m_zoomResetMaxX; qreal m_zoomResetMinY; qreal m_zoomResetMaxY; + bool m_reverseX; + bool m_reverseY; }; QT_CHARTS_END_NAMESPACE diff --git a/src/charts/domain/logxlogydomain.cpp b/src/charts/domain/logxlogydomain.cpp index ab799e68..1651132c 100644 --- a/src/charts/domain/logxlogydomain.cpp +++ b/src/charts/domain/logxlogydomain.cpp @@ -89,15 +89,16 @@ void LogXLogYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) void LogXLogYDomain::zoomIn(const QRectF &rect) { storeZoomReset(); - qreal logLeftX = rect.left() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; - qreal logRightX = rect.right() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; + QRectF fixedRect = fixZoomRect(rect); + qreal logLeftX = fixedRect.left() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; + qreal logRightX = fixedRect.right() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; qreal leftX = qPow(m_logBaseX, logLeftX); qreal rightX = qPow(m_logBaseX, logRightX); qreal minX = leftX < rightX ? leftX : rightX; qreal maxX = leftX > rightX ? leftX : rightX; - qreal logLeftY = m_logRightY - rect.bottom() * (m_logRightY - m_logLeftY) / m_size.height(); - qreal logRightY = m_logRightY - rect.top() * (m_logRightY - m_logLeftY) / m_size.height(); + qreal logLeftY = m_logRightY - fixedRect.bottom() * (m_logRightY - m_logLeftY) / m_size.height(); + qreal logRightY = m_logRightY - fixedRect.top() * (m_logRightY - m_logLeftY) / m_size.height(); qreal leftY = qPow(m_logBaseY, logLeftY); qreal rightY = qPow(m_logBaseY, logRightY); qreal minY = leftY < rightY ? leftY : rightY; @@ -109,8 +110,9 @@ void LogXLogYDomain::zoomIn(const QRectF &rect) void LogXLogYDomain::zoomOut(const QRectF &rect) { storeZoomReset(); - const qreal factorX = m_size.width() / rect.width(); - const qreal factorY = m_size.height() / rect.height(); + QRectF fixedRect = fixZoomRect(rect); + const qreal factorX = m_size.width() / fixedRect.width(); + const qreal factorY = m_size.height() / fixedRect.height(); qreal logLeftX = m_logLeftX + (m_logRightX - m_logLeftX) / 2 * (1 - factorX); qreal logRIghtX = m_logLeftX + (m_logRightX - m_logLeftX) / 2 * (1 + factorX); @@ -131,6 +133,11 @@ void LogXLogYDomain::zoomOut(const QRectF &rect) void LogXLogYDomain::move(qreal dx, qreal dy) { + if (m_reverseX) + dx = -dx; + if (m_reverseY) + dy = -dy; + qreal stepX = dx * qAbs(m_logRightX - m_logLeftX) / m_size.width(); qreal leftX = qPow(m_logBaseX, m_logLeftX + stepX); qreal rightX = qPow(m_logBaseX, m_logRightX + stepX); @@ -153,23 +160,25 @@ QPointF LogXLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) c qreal x(0); qreal y(0); if (point.x() > 0 && point.y() > 0) { - x = (std::log10(point.x()) / std::log10(m_logBaseX)) * deltaX - m_logLeftX * deltaX; - y = (std::log10(point.y()) / std::log10(m_logBaseY)) * -deltaY - m_logLeftY * -deltaY + m_size.height(); + x = ((std::log10(point.x()) / std::log10(m_logBaseX)) - m_logLeftX) * deltaX; + y = ((std::log10(point.y()) / std::log10(m_logBaseY)) - m_logLeftY) * deltaY; ok = true; } else { qWarning() << "Logarithms of zero and negative values are undefined."; ok = false; if (point.x() > 0) - x = (std::log10(point.x()) / std::log10(m_logBaseX)) * deltaX - m_logLeftX * deltaX; + x = ((std::log10(point.x()) / std::log10(m_logBaseX)) - m_logLeftX) * deltaX; else x = 0; - if (point.y() > 0) { - y = (std::log10(point.y()) / std::log10(m_logBaseY)) * -deltaY - m_logLeftY * -deltaY - + m_size.height(); - } else { - y = m_size.height(); - } + if (point.y() > 0) + y = ((std::log10(point.y()) / std::log10(m_logBaseY)) - m_logLeftY) * deltaY; + else + y = 0; } + if (m_reverseX) + x = m_size.width() - x; + if (!m_reverseY) + y = m_size.height() - y; return QPointF(x, y); } @@ -183,8 +192,12 @@ QVector<QPointF> LogXLogYDomain::calculateGeometryPoints(const QVector<QPointF> for (int i = 0; i < vector.count(); ++i) { if (vector[i].x() > 0 && vector[i].y() > 0) { - qreal x = (std::log10(vector[i].x()) / std::log10(m_logBaseX)) * deltaX - m_logLeftX * deltaX; - qreal y = (std::log10(vector[i].y()) / std::log10(m_logBaseY)) * -deltaY - m_logLeftY * -deltaY + m_size.height(); + qreal x = ((std::log10(vector[i].x()) / std::log10(m_logBaseX)) - m_logLeftX) * deltaX; + if (m_reverseX) + x = m_size.width() - x; + qreal y = ((std::log10(vector[i].y()) / std::log10(m_logBaseY)) - m_logLeftY) * deltaY; + if (!m_reverseY) + y = m_size.height() - y; result[i].setX(x); result[i].setY(y); } else { @@ -199,8 +212,10 @@ QPointF LogXLogYDomain::calculateDomainPoint(const QPointF &point) const { const qreal deltaX = m_size.width() / qAbs(m_logRightX - m_logLeftX); const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); - qreal x = qPow(m_logBaseX, m_logLeftX + point.x() / deltaX); - qreal y = qPow(m_logBaseY, m_logLeftY + (m_size.height() - point.y()) / deltaY); + qreal x = m_reverseX ? (m_size.width() - point.x()) : point.x(); + x = qPow(m_logBaseX, m_logLeftX + x / deltaX); + qreal y = m_reverseY ? point.y() : (m_size.height() - point.y()); + y = qPow(m_logBaseY, m_logLeftY + y / deltaY); return QPointF(x, y); } diff --git a/src/charts/domain/logxydomain.cpp b/src/charts/domain/logxydomain.cpp index d8f712d9..b5cef8db 100644 --- a/src/charts/domain/logxydomain.cpp +++ b/src/charts/domain/logxydomain.cpp @@ -81,8 +81,9 @@ void LogXYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) void LogXYDomain::zoomIn(const QRectF &rect) { storeZoomReset(); - qreal logLeftX = rect.left() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; - qreal logRightX = rect.right() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; + QRectF fixedRect = fixZoomRect(rect); + qreal logLeftX = fixedRect.left() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; + qreal logRightX = fixedRect.right() * (m_logRightX - m_logLeftX) / m_size.width() + m_logLeftX; qreal leftX = qPow(m_logBaseX, logLeftX); qreal rightX = qPow(m_logBaseX, logRightX); qreal minX = leftX < rightX ? leftX : rightX; @@ -92,8 +93,8 @@ void LogXYDomain::zoomIn(const QRectF &rect) qreal minY = m_minY; qreal maxY = m_maxY; - minY = maxY - dy * rect.bottom(); - maxY = maxY - dy * rect.top(); + minY = maxY - dy * fixedRect.bottom(); + maxY = maxY - dy * fixedRect.top(); setRange(minX, maxX, minY, maxY); } @@ -101,7 +102,8 @@ void LogXYDomain::zoomIn(const QRectF &rect) void LogXYDomain::zoomOut(const QRectF &rect) { storeZoomReset(); - const qreal factorX = m_size.width() / rect.width(); + QRectF fixedRect = fixZoomRect(rect); + const qreal factorX = m_size.width() / fixedRect.width(); qreal logLeftX = m_logLeftX + (m_logRightX - m_logLeftX) / 2 * (1 - factorX); qreal logRIghtX = m_logLeftX + (m_logRightX - m_logLeftX) / 2 * (1 + factorX); @@ -110,11 +112,11 @@ void LogXYDomain::zoomOut(const QRectF &rect) qreal minX = leftX < rightX ? leftX : rightX; qreal maxX = leftX > rightX ? leftX : rightX; - qreal dy = spanY() / rect.height(); + qreal dy = spanY() / fixedRect.height(); qreal minY = m_minY; qreal maxY = m_maxY; - maxY = minY + dy * rect.bottom(); + maxY = minY + dy * fixedRect.bottom(); minY = maxY - dy * m_size.height(); setRange(minX, maxX, minY, maxY); @@ -122,6 +124,11 @@ void LogXYDomain::zoomOut(const QRectF &rect) void LogXYDomain::move(qreal dx, qreal dy) { + if (m_reverseX) + dx = -dx; + if (m_reverseY) + dy = -dy; + qreal stepX = dx * (m_logRightX - m_logLeftX) / m_size.width(); qreal leftX = qPow(m_logBaseX, m_logLeftX + stepX); qreal rightX = qPow(m_logBaseX, m_logRightX + stepX); @@ -145,9 +152,13 @@ QPointF LogXYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons const qreal deltaY = m_size.height() / (m_maxY - m_minY); qreal x(0); - qreal y = (point.y() - m_minY) * -deltaY + m_size.height(); + qreal y = (point.y() - m_minY) * deltaY; + if (!m_reverseY) + y = m_size.height() - y; if (point.x() > 0) { - x = (std::log10(point.x()) / std::log10(m_logBaseX)) * deltaX - m_logLeftX * deltaX; + x = ((std::log10(point.x()) / std::log10(m_logBaseX)) - m_logLeftX) * deltaX; + if (m_reverseX) + x = m_size.width() - x; ok = true; } else { x = 0; @@ -167,8 +178,12 @@ QVector<QPointF> LogXYDomain::calculateGeometryPoints(const QVector<QPointF> &ve for (int i = 0; i < vector.count(); ++i) { if (vector[i].x() > 0) { - qreal x = (std::log10(vector[i].x()) / std::log10(m_logBaseX)) * deltaX - m_logLeftX * deltaX; - qreal y = (vector[i].y() - m_minY) * -deltaY + m_size.height(); + qreal x = ((std::log10(vector[i].x()) / std::log10(m_logBaseX)) - m_logLeftX) * deltaX; + if (m_reverseX) + x = m_size.width() - x; + qreal y = (vector[i].y() - m_minY) * deltaY; + if (!m_reverseY) + y = m_size.height() - y; result[i].setX(x); result[i].setY(y); } else { @@ -184,8 +199,11 @@ QPointF LogXYDomain::calculateDomainPoint(const QPointF &point) const { const qreal deltaX = m_size.width() / (m_logRightX - m_logLeftX); const qreal deltaY = m_size.height() / (m_maxY - m_minY); - qreal x = qPow(m_logBaseX, m_logLeftX + point.x() / deltaX); - qreal y = (point.y() - m_size.height()) / (-deltaY) + m_minY; + qreal x = m_reverseX ? (m_size.width() - point.x()) : point.x(); + x = qPow(m_logBaseX, m_logLeftX + x / deltaX); + qreal y = m_reverseY ? point.y() : (m_size.height() - point.y()); + y /= deltaY; + y += m_minY; return QPointF(x, y); } diff --git a/src/charts/domain/xlogydomain.cpp b/src/charts/domain/xlogydomain.cpp index 172a9de4..75187197 100644 --- a/src/charts/domain/xlogydomain.cpp +++ b/src/charts/domain/xlogydomain.cpp @@ -81,15 +81,16 @@ void XLogYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) void XLogYDomain::zoomIn(const QRectF &rect) { storeZoomReset(); + QRectF fixedRect = fixZoomRect(rect); qreal dx = spanX() / m_size.width(); qreal maxX = m_maxX; qreal minX = m_minX; - maxX = minX + dx * rect.right(); - minX = minX + dx * rect.left(); + maxX = minX + dx * fixedRect.right(); + minX = minX + dx * fixedRect.left(); - qreal logLeftY = m_logRightY - rect.bottom() * (m_logRightY - m_logLeftY) / m_size.height(); - qreal logRightY = m_logRightY - rect.top() * (m_logRightY - m_logLeftY) / m_size.height(); + qreal logLeftY = m_logRightY - fixedRect.bottom() * (m_logRightY - m_logLeftY) / m_size.height(); + qreal logRightY = m_logRightY - fixedRect.top() * (m_logRightY - m_logLeftY) / m_size.height(); qreal leftY = qPow(m_logBaseY, logLeftY); qreal rightY = qPow(m_logBaseY, logRightY); qreal minY = leftY < rightY ? leftY : rightY; @@ -101,14 +102,15 @@ void XLogYDomain::zoomIn(const QRectF &rect) void XLogYDomain::zoomOut(const QRectF &rect) { storeZoomReset(); - qreal dx = spanX() / rect.width(); + QRectF fixedRect = fixZoomRect(rect); + qreal dx = spanX() / fixedRect.width(); qreal maxX = m_maxX; qreal minX = m_minX; - minX = maxX - dx * rect.right(); + minX = maxX - dx * fixedRect.right(); maxX = minX + dx * m_size.width(); - const qreal factorY = m_size.height() / rect.height(); + const qreal factorY = m_size.height() / fixedRect.height(); qreal newLogMinY = m_logLeftY + (m_logRightY - m_logLeftY) / 2 * (1 - factorY); qreal newLogMaxY = m_logLeftY + (m_logRightY - m_logLeftY) / 2 * (1 + factorY); qreal leftY = qPow(m_logBaseY, newLogMinY); @@ -121,6 +123,11 @@ void XLogYDomain::zoomOut(const QRectF &rect) void XLogYDomain::move(qreal dx, qreal dy) { + if (m_reverseX) + dx = -dx; + if (m_reverseY) + dy = -dy; + qreal x = spanX() / m_size.width(); qreal maxX = m_maxX; qreal minX = m_minX; @@ -145,9 +152,13 @@ QPointF XLogYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) cons const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); qreal x = (point.x() - m_minX) * deltaX; + if (m_reverseX) + x = m_size.width() - x; qreal y(0); if (point.y() > 0) { - y = (std::log10(point.y()) / std::log10(m_logBaseY)) * -deltaY - m_logLeftY * -deltaY + m_size.height(); + y = ((std::log10(point.y()) / std::log10(m_logBaseY)) - m_logLeftY) * deltaY; + if (!m_reverseY) + y = m_size.height() - y; ok = true; } else { y = m_size.height(); @@ -168,7 +179,11 @@ QVector<QPointF> XLogYDomain::calculateGeometryPoints(const QVector<QPointF> &ve for (int i = 0; i < vector.count(); ++i) { if (vector[i].y() > 0) { qreal x = (vector[i].x() - m_minX) * deltaX; - qreal y = (std::log10(vector[i].y()) / std::log10(m_logBaseY)) * -deltaY - m_logLeftY * -deltaY + m_size.height(); + if (m_reverseX) + x = m_size.width() - x; + qreal y = ((std::log10(vector[i].y()) / std::log10(m_logBaseY)) - m_logLeftY) * deltaY; + if (!m_reverseY) + y = m_size.height() - y; result[i].setX(x); result[i].setY(y); } else { @@ -183,8 +198,11 @@ QPointF XLogYDomain::calculateDomainPoint(const QPointF &point) const { const qreal deltaX = m_size.width() / (m_maxX - m_minX); const qreal deltaY = m_size.height() / qAbs(m_logRightY - m_logLeftY); - qreal x = point.x() / deltaX + m_minX; - qreal y = qPow(m_logBaseY, m_logLeftY + (m_size.height() - point.y()) / deltaY); + qreal x = m_reverseX ? (m_size.width() - point.x()) : point.x(); + x /= deltaX; + x += m_minX; + qreal y = m_reverseY ? point.y() : (m_size.height() - point.y()); + y = qPow(m_logBaseY, m_logLeftY + y / deltaY); return QPointF(x, y); } diff --git a/src/charts/domain/xydomain.cpp b/src/charts/domain/xydomain.cpp index da54b4fa..1e79de52 100644 --- a/src/charts/domain/xydomain.cpp +++ b/src/charts/domain/xydomain.cpp @@ -71,6 +71,7 @@ void XYDomain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) void XYDomain::zoomIn(const QRectF &rect) { storeZoomReset(); + QRectF fixedRect = fixZoomRect(rect); qreal dx = spanX() / m_size.width(); qreal dy = spanY() / m_size.height(); @@ -79,10 +80,10 @@ void XYDomain::zoomIn(const QRectF &rect) qreal minY = m_minY; qreal maxY = m_maxY; - maxX = minX + dx * rect.right(); - minX = minX + dx * rect.left(); - minY = maxY - dy * rect.bottom(); - maxY = maxY - dy * rect.top(); + maxX = minX + dx * fixedRect.right(); + minX = minX + dx * fixedRect.left(); + minY = maxY - dy * fixedRect.bottom(); + maxY = maxY - dy * fixedRect.top(); if ((maxX - minX) == spanX()) { minX = m_minX; @@ -99,6 +100,7 @@ void XYDomain::zoomIn(const QRectF &rect) void XYDomain::zoomOut(const QRectF &rect) { storeZoomReset(); + QRectF fixedRect = fixZoomRect(rect); qreal dx = spanX() / rect.width(); qreal dy = spanY() / rect.height(); @@ -107,9 +109,9 @@ void XYDomain::zoomOut(const QRectF &rect) qreal minY = m_minY; qreal maxY = m_maxY; - minX = maxX - dx * rect.right(); + minX = maxX - dx * fixedRect.right(); maxX = minX + dx * m_size.width(); - maxY = minY + dy * rect.bottom(); + maxY = minY + dy * fixedRect.bottom(); minY = maxY - dy * m_size.height(); if ((maxX - minX) == spanX()) { @@ -126,6 +128,11 @@ void XYDomain::zoomOut(const QRectF &rect) void XYDomain::move(qreal dx, qreal dy) { + if (m_reverseX) + dx = -dx; + if (m_reverseY) + dy = -dy; + qreal x = spanX() / m_size.width(); qreal y = spanY() / m_size.height(); @@ -150,7 +157,11 @@ QPointF XYDomain::calculateGeometryPoint(const QPointF &point, bool &ok) const const qreal deltaX = m_size.width() / (m_maxX - m_minX); const qreal deltaY = m_size.height() / (m_maxY - m_minY); qreal x = (point.x() - m_minX) * deltaX; - qreal y = (point.y() - m_minY) * -deltaY + m_size.height(); + if (m_reverseX) + x = m_size.width() - x; + qreal y = (point.y() - m_minY) * deltaY; + if (!m_reverseY) + y = m_size.height() - y; ok = true; return QPointF(x, y); } @@ -165,7 +176,11 @@ QVector<QPointF> XYDomain::calculateGeometryPoints(const QVector<QPointF> &vecto for (int i = 0; i < vector.count(); ++i) { qreal x = (vector[i].x() - m_minX) * deltaX; - qreal y = (vector[i].y() - m_minY) * -deltaY + m_size.height(); + if (m_reverseX) + x = m_size.width() - x; + qreal y = (vector[i].y() - m_minY) * deltaY; + if (!m_reverseY) + y = m_size.height() - y; result[i].setX(x); result[i].setY(y); } @@ -176,8 +191,12 @@ QPointF XYDomain::calculateDomainPoint(const QPointF &point) const { const qreal deltaX = m_size.width() / (m_maxX - m_minX); const qreal deltaY = m_size.height() / (m_maxY - m_minY); - qreal x = point.x() / deltaX + m_minX; - qreal y = (point.y() - m_size.height()) / (-deltaY) + m_minY; + qreal x = m_reverseX ? (m_size.width() - point.x()) : point.x(); + x /= deltaX; + x += m_minX; + qreal y = m_reverseY ? point.y() : (m_size.height() - point.y()); + y /= deltaY; + y += m_minY; return QPointF(x, y); } diff --git a/src/charts/glwidget.cpp b/src/charts/glwidget.cpp index 784f4b3a..ff22050f 100644 --- a/src/charts/glwidget.cpp +++ b/src/charts/glwidget.cpp @@ -334,41 +334,43 @@ void GLWidget::render(bool selection) QOpenGLBuffer *vbo = m_seriesBufferMap.value(i.key()); GLXYSeriesData *data = i.value(); - if (selection) { - m_selectionVector[counter] = i.key(); - m_program->setUniformValue(m_colorUniformLoc, QVector3D((counter & 0xff) / 255.0f, - ((counter & 0xff00) >> 8) / 255.0f, - ((counter & 0xff0000) >> 16) / 255.0f)); - counter++; - } else { - m_program->setUniformValue(m_colorUniformLoc, data->color); - } - m_program->setUniformValue(m_minUniformLoc, data->min); - m_program->setUniformValue(m_deltaUniformLoc, data->delta); - m_program->setUniformValue(m_matrixUniformLoc, data->matrix); - bool dirty = data->dirty; - if (!vbo) { - vbo = new QOpenGLBuffer; - m_seriesBufferMap.insert(i.key(), vbo); - vbo->create(); - dirty = true; - } - vbo->bind(); - if (dirty) { - vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat)); - data->dirty = false; - m_selectionRenderNeeded = true; - } + if (data->visible) { + if (selection) { + m_selectionVector[counter] = i.key(); + m_program->setUniformValue(m_colorUniformLoc, QVector3D((counter & 0xff) / 255.0f, + ((counter & 0xff00) >> 8) / 255.0f, + ((counter & 0xff0000) >> 16) / 255.0f)); + counter++; + } else { + m_program->setUniformValue(m_colorUniformLoc, data->color); + } + m_program->setUniformValue(m_minUniformLoc, data->min); + m_program->setUniformValue(m_deltaUniformLoc, data->delta); + m_program->setUniformValue(m_matrixUniformLoc, data->matrix); + bool dirty = data->dirty; + if (!vbo) { + vbo = new QOpenGLBuffer; + m_seriesBufferMap.insert(i.key(), vbo); + vbo->create(); + dirty = true; + } + vbo->bind(); + if (dirty) { + vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat)); + dirty = false; + m_selectionRenderNeeded = true; + } - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); - if (data->type == QAbstractSeries::SeriesTypeLine) { - glLineWidth(data->width); - glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2); - } else { // Scatter - m_program->setUniformValue(m_pointSizeUniformLoc, data->width); - glDrawArrays(GL_POINTS, 0, data->array.size() / 2); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + if (data->type == QAbstractSeries::SeriesTypeLine) { + glLineWidth(data->width); + glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2); + } else { // Scatter + m_program->setUniformValue(m_pointSizeUniformLoc, data->width); + glDrawArrays(GL_POINTS, 0, data->array.size() / 2); + } + vbo->release(); } - vbo->release(); } m_program->release(); } diff --git a/src/charts/layout/cartesianchartlayout.cpp b/src/charts/layout/cartesianchartlayout.cpp index 7a3d8638..80a852a7 100644 --- a/src/charts/layout/cartesianchartlayout.cpp +++ b/src/charts/layout/cartesianchartlayout.cpp @@ -233,11 +233,11 @@ QRectF CartesianChartLayout::calculateAxisMinimum(const QRectF &minimum, const Q switch (axis->axis()->alignment()) { case Qt::AlignLeft: left.setWidth(left.width() + size.width()); - left.setHeight(qMax(left.height() * 2, size.height())); + left.setHeight(qMax(left.height(), size.height())); break; case Qt::AlignRight: right.setWidth(right.width() + size.width()); - right.setHeight(qMax(right.height() * 2, size.height())); + right.setHeight(qMax(right.height(), size.height())); break; case Qt::AlignTop: top.setWidth(qMax(top.width(), size.width())); diff --git a/src/charts/linechart/linechartitem.cpp b/src/charts/linechart/linechartitem.cpp index afb1284d..03099356 100644 --- a/src/charts/linechart/linechartitem.cpp +++ b/src/charts/linechart/linechartitem.cpp @@ -347,6 +347,7 @@ void LineChartItem::handleUpdated() bool doGeometryUpdate = (m_pointsVisible != m_series->pointsVisible()) || (m_series->pointsVisible() && (m_linePen != m_series->pen())); + bool visibleChanged = m_series->isVisible() != isVisible(); setVisible(m_series->isVisible()); setOpacity(m_series->opacity()); m_pointsVisible = m_series->pointsVisible(); @@ -358,6 +359,8 @@ void LineChartItem::handleUpdated() m_pointLabelsClipping = m_series->pointLabelsClipping(); if (doGeometryUpdate) updateGeometry(); + else if (m_series->useOpenGL() && visibleChanged) + refreshGlChart(); update(); } @@ -392,8 +395,6 @@ void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt painter->setClipRect(clipRect); } - reversePainter(painter, clipRect); - if (m_pointsVisible) { painter->setBrush(m_linePen.color()); painter->drawPath(m_linePath); @@ -409,8 +410,6 @@ void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt } } - reversePainter(painter, clipRect); - if (m_pointLabelsVisible) { if (m_pointLabelsClipping) painter->setClipping(true); diff --git a/src/charts/qabstractseries.cpp b/src/charts/qabstractseries.cpp index 271f7f66..9f2c21b9 100644 --- a/src/charts/qabstractseries.cpp +++ b/src/charts/qabstractseries.cpp @@ -454,40 +454,6 @@ void QAbstractSeriesPrivate::initializeAnimations(QChart::AnimationOptions optio Q_UNUSED(curve); } -bool QAbstractSeriesPrivate::reverseXAxis() -{ - bool reverseXAxis = false; - if (m_axes.size() != 0 && !(m_chart->chartType() == QChart::ChartTypePolar)) { - int i = 0; - while (i < m_axes.size()) { - if (m_axes.at(i)->orientation() == Qt::Horizontal && m_axes.at(i)->isReverse()) { - reverseXAxis = true; - break; - } - i++; - } - } - - return reverseXAxis; -} - -bool QAbstractSeriesPrivate::reverseYAxis() -{ - bool reverseYAxis = false; - if (m_axes.size() != 0 && !(m_chart->chartType() == QChart::ChartTypePolar)) { - int i = 0; - while (i < m_axes.size()) { - if (m_axes.at(i)->orientation() == Qt::Vertical && m_axes.at(i)->isReverse()) { - reverseYAxis = true; - break; - } - i++; - } - } - - return reverseYAxis; -} - // This function can be used to explicitly block OpenGL use from some otherwise supported series, // such as the line series used as edge series of an area series. void QAbstractSeriesPrivate::setBlockOpenGL(bool enable) diff --git a/src/charts/qabstractseries_p.h b/src/charts/qabstractseries_p.h index cbdfb5f8..0d50ea2b 100644 --- a/src/charts/qabstractseries_p.h +++ b/src/charts/qabstractseries_p.h @@ -89,8 +89,6 @@ public: ChartPresenter *presenter() const; QChart* chart() { return m_chart; } - bool reverseXAxis(); - bool reverseYAxis(); void setBlockOpenGL(bool enable); diff --git a/src/charts/scatterchart/scatterchartitem.cpp b/src/charts/scatterchart/scatterchartitem.cpp index cc1ecb60..2defadf5 100644 --- a/src/charts/scatterchart/scatterchartitem.cpp +++ b/src/charts/scatterchart/scatterchartitem.cpp @@ -194,17 +194,10 @@ void ScatterChartItem::updateGeometry() // fake anyway. After remove animation stops, geometry is updated to correct one. m_markerMap[item] = m_series->at(qMin(seriesLastIndex, i)); QPointF position; - if (seriesPrivate()->reverseXAxis()) - position.setX(domain()->size().width() - point.x() - rect.width() / 2); - else - position.setX(point.x() - rect.width() / 2); - if (seriesPrivate()->reverseYAxis()) - position.setY(domain()->size().height() - point.y() - rect.height() / 2); - else - position.setY(point.y() - rect.height() / 2); + position.setX(point.x() - rect.width() / 2); + position.setY(point.y() - rect.height() / 2); item->setPos(position); - if (!m_visible || offGridStatus.at(i)) item->setVisible(false); else @@ -256,15 +249,21 @@ void ScatterChartItem::setBrush(const QBrush &brush) void ScatterChartItem::handleUpdated() { - int count = m_items.childItems().count(); + if (m_series->useOpenGL()) { + if ((m_series->isVisible() != m_visible)) { + m_visible = m_series->isVisible(); + refreshGlChart(); + } + return; + } + int count = m_items.childItems().count(); if (count == 0) return; bool recreate = m_visible != m_series->isVisible() || m_size != m_series->markerSize() || m_shape != m_series->markerShape(); - m_visible = m_series->isVisible(); m_size = m_series->markerSize(); m_shape = m_series->markerShape(); diff --git a/src/charts/splinechart/splinechartitem.cpp b/src/charts/splinechart/splinechartitem.cpp index 4f7e7cb2..0f9c1e24 100644 --- a/src/charts/splinechart/splinechartitem.cpp +++ b/src/charts/splinechart/splinechartitem.cpp @@ -460,8 +460,6 @@ void SplineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *o painter->setClipRect(clipRect); } - reversePainter(painter, clipRect); - painter->drawPath(m_path); if (m_pointsVisible) { @@ -472,8 +470,6 @@ void SplineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *o painter->drawPoints(geometryPoints()); } - reversePainter(painter, clipRect); - if (m_pointLabelsVisible) { if (m_pointLabelsClipping) painter->setClipping(true); diff --git a/src/charts/xychart/glxyseriesdata.cpp b/src/charts/xychart/glxyseriesdata.cpp index 37da55b1..9a6f330a 100644 --- a/src/charts/xychart/glxyseriesdata.cpp +++ b/src/charts/xychart/glxyseriesdata.cpp @@ -50,6 +50,7 @@ void GLXYSeriesDataManager::setPoints(QXYSeries *series, const AbstractDomain *d if (!data) { data = new GLXYSeriesData; data->type = series->type(); + data->visible = series->isVisible(); QColor sc; if (data->type == QAbstractSeries::SeriesTypeScatter) { QScatterSeries *scatter = static_cast<QScatterSeries *>(series); @@ -68,6 +69,8 @@ void GLXYSeriesDataManager::setPoints(QXYSeries *series, const AbstractDomain *d data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF())); connect(series, &QXYSeries::useOpenGLChanged, this, &GLXYSeriesDataManager::handleSeriesOpenGLChange); + connect(series, &QXYSeries::visibleChanged, this, + &GLXYSeriesDataManager::handleSeriesVisibilityChange); m_seriesDataMap.insert(series, data); m_mapDirty = true; } @@ -174,6 +177,18 @@ void GLXYSeriesDataManager::handleSeriesOpenGLChange() removeSeries(series); } +void GLXYSeriesDataManager::handleSeriesVisibilityChange() +{ + QXYSeries *series = qobject_cast<QXYSeries *>(sender()); + if (series) { + GLXYSeriesData *data = m_seriesDataMap.value(series); + if (data) { + data->visible = series->isVisible(); + data->dirty = true; + } + } +} + void GLXYSeriesDataManager::handleScatterColorChange() { QScatterSeries *series = qobject_cast<QScatterSeries *>(sender()); diff --git a/src/charts/xychart/glxyseriesdata_p.h b/src/charts/xychart/glxyseriesdata_p.h index 578ab217..4a22e575 100644 --- a/src/charts/xychart/glxyseriesdata_p.h +++ b/src/charts/xychart/glxyseriesdata_p.h @@ -58,6 +58,7 @@ struct GLXYSeriesData { QAbstractSeries::SeriesType type; QVector2D min; QVector2D delta; + bool visible; QMatrix4x4 matrix; public: GLXYSeriesData &operator=(const GLXYSeriesData &data) { @@ -68,6 +69,7 @@ public: type = data.type; min = data.min; delta = data.delta; + visible = data.visible; matrix = data.matrix; return *this; } @@ -103,6 +105,7 @@ public Q_SLOTS: void cleanup(); void handleSeriesPenChange(); void handleSeriesOpenGLChange(); + void handleSeriesVisibilityChange(); void handleScatterColorChange(); void handleScatterMarkerSizeChange(); diff --git a/src/charts/xychart/qvxymodelmapper.cpp b/src/charts/xychart/qvxymodelmapper.cpp index fb7f63ab..3b68d809 100644 --- a/src/charts/xychart/qvxymodelmapper.cpp +++ b/src/charts/xychart/qvxymodelmapper.cpp @@ -120,7 +120,7 @@ QT_CHARTS_BEGIN_NAMESPACE Minimal and default value is: -1 (count limited by the number of rows in the model) */ /*! - \qmlproperty int VXYModelMapper::columnCount + \qmlproperty int VXYModelMapper::rowCount Defines the number of rows of the model that are mapped as the data for series. The default value is -1 (count limited by the number of rows in the model). */ diff --git a/src/charts/xychart/qxymodelmapper.cpp b/src/charts/xychart/qxymodelmapper.cpp index b6037858..51f4a85a 100644 --- a/src/charts/xychart/qxymodelmapper.cpp +++ b/src/charts/xychart/qxymodelmapper.cpp @@ -32,6 +32,7 @@ #include <QtCharts/QXYSeries> #include <QtCore/QAbstractItemModel> #include <QtCore/QDateTime> +#include <QtCore/QDebug> QT_CHARTS_BEGIN_NAMESPACE @@ -544,15 +545,31 @@ void QXYModelMapperPrivate::initializeXYFromModel() int pointPos = 0; QModelIndex xIndex = xModelIndex(pointPos); QModelIndex yIndex = yModelIndex(pointPos); - while (xIndex.isValid() && yIndex.isValid()) { - QPointF point; - point.setX(valueFromModel(xIndex)); - point.setY(valueFromModel(yIndex)); - m_series->append(point); - pointPos++; - xIndex = xModelIndex(pointPos); - yIndex = yModelIndex(pointPos); + + if (xIndex.isValid() && yIndex.isValid()) { + while (xIndex.isValid() && yIndex.isValid()) { + QPointF point; + point.setX(valueFromModel(xIndex)); + point.setY(valueFromModel(yIndex)); + m_series->append(point); + pointPos++; + xIndex = xModelIndex(pointPos); + yIndex = yModelIndex(pointPos); + // Don't warn about invalid index after the first, those are valid and used to + // determine when we should end looping. + } + } else { + // Invalid index right off the bat means series will be left empty, so output a warning, + // unless model is also empty + int count = m_orientation == Qt::Vertical ? m_model->rowCount() : m_model->columnCount(); + if (count > 0) { + if (!xIndex.isValid()) + qWarning() << __FUNCTION__ << QStringLiteral("Invalid X coordinate index in model mapper."); + else if (!yIndex.isValid()) + qWarning() << __FUNCTION__ << QStringLiteral("Invalid Y coordinate index in model mapper."); + } } + blockSeriesSignals(false); } diff --git a/src/charts/xychart/qxyseries.cpp b/src/charts/xychart/qxyseries.cpp index f3a253cd..fd8562a1 100644 --- a/src/charts/xychart/qxyseries.cpp +++ b/src/charts/xychart/qxyseries.cpp @@ -972,14 +972,8 @@ void QXYSeriesPrivate::drawSeriesPointLabels(QPainter *painter, const QVector<QP // Position text in relation to the point int pointLabelWidth = fm.width(pointLabel); QPointF position(points.at(i)); - if (!reverseXAxis()) - position.setX(position.x() - pointLabelWidth / 2); - else - position.setX(domain()->size().width() - position.x() - pointLabelWidth / 2); - if (!reverseYAxis()) - position.setY(position.y() - labelOffset); - else - position.setY(domain()->size().height() - position.y() - labelOffset); + position.setX(position.x() - pointLabelWidth / 2); + position.setY(position.y() - labelOffset); painter->drawText(position, pointLabel); } diff --git a/src/charts/xychart/xychart.cpp b/src/charts/xychart/xychart.cpp index 1811a76e..4314f6dd 100644 --- a/src/charts/xychart/xychart.cpp +++ b/src/charts/xychart/xychart.cpp @@ -128,6 +128,12 @@ void XYChart::updateGlChart() updateGeometry(); } +// Doesn't update gl geometry, but refreshes the chart +void XYChart::refreshGlChart() +{ + presenter()->updateGLWidget(); +} + //handlers void XYChart::handlePointAdded(int index) diff --git a/src/charts/xychart/xychart_p.h b/src/charts/xychart/xychart_p.h index c0348c18..c5737cca 100644 --- a/src/charts/xychart/xychart_p.h +++ b/src/charts/xychart/xychart_p.h @@ -88,6 +88,7 @@ Q_SIGNALS: protected: virtual void updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index = -1); virtual void updateGlChart(); + virtual void refreshGlChart(); private: inline bool isEmpty(); diff --git a/src/chartsqml2/declarativebarseries.cpp b/src/chartsqml2/declarativebarseries.cpp index 70fbcf36..d335f9d5 100644 --- a/src/chartsqml2/declarativebarseries.cpp +++ b/src/chartsqml2/declarativebarseries.cpp @@ -87,12 +87,12 @@ void DeclarativeBarSet::setValues(QVariantList values) } } - QVector<int> indexValueList; + QVector<qreal> indexValueList; indexValueList.resize(maxValue + 1); for (int i = 0; i < values.count(); i++) { if (values.at(i).canConvert(QVariant::Point)) { - indexValueList.replace(values.at(i).toPoint().x(), values.at(i).toPoint().y()); + indexValueList.replace(values.at(i).toPoint().x(), values.at(i).toPointF().y()); } } diff --git a/src/chartsqml2/declarativechart.cpp b/src/chartsqml2/declarativechart.cpp index 967d9eaa..9a5b2678 100644 --- a/src/chartsqml2/declarativechart.cpp +++ b/src/chartsqml2/declarativechart.cpp @@ -587,13 +587,18 @@ QSGNode *DeclarativeChart::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdateP if (!node) { node = new DeclarativeChartNode(window()); + // Ensure that chart is rendered whenever node is recreated + if (m_sceneImage) + m_sceneImageDirty = true; } const QRectF &bRect = boundingRect(); // Update renderNode data if (node->renderNode()) { if (m_glXYDataManager->dataMap().size() || m_glXYDataManager->mapDirty()) { - const QRectF &plotArea = m_chart->plotArea(); + // Convert plotArea to QRect and back to QRectF to get rid of sub-pixel widths/heights + // which can cause unwanted partial antialising of the graph. + const QRectF plotArea = QRectF(m_chart->plotArea().toRect()); const QSizeF &chartAreaSize = m_chart->size(); // We can't use chart's plot area directly, as chart enforces a minimum size diff --git a/src/chartsqml2/declarativeopenglrendernode.cpp b/src/chartsqml2/declarativeopenglrendernode.cpp index c6c48e66..a0b4ec7f 100644 --- a/src/chartsqml2/declarativeopenglrendernode.cpp +++ b/src/chartsqml2/declarativeopenglrendernode.cpp @@ -290,39 +290,41 @@ void DeclarativeOpenGLRenderNode::renderGL(bool selection) QOpenGLBuffer *vbo = m_seriesBufferMap.value(i.key()); GLXYSeriesData *data = i.value(); - if (selection) { - m_selectionVector[counter] = i.key(); - m_program->setUniformValue(m_colorUniformLoc, QVector3D((counter & 0xff) / 255.0f, - ((counter & 0xff00) >> 8) / 255.0f, - ((counter & 0xff0000) >> 16) / 255.0f)); - counter++; - } else { - m_program->setUniformValue(m_colorUniformLoc, data->color); - } - m_program->setUniformValue(m_minUniformLoc, data->min); - m_program->setUniformValue(m_deltaUniformLoc, data->delta); - m_program->setUniformValue(m_matrixUniformLoc, data->matrix); - - if (!vbo) { - vbo = new QOpenGLBuffer; - m_seriesBufferMap.insert(i.key(), vbo); - vbo->create(); - } - vbo->bind(); - if (data->dirty) { - vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat)); - data->dirty = false; - } + if (data->visible) { + if (selection) { + m_selectionVector[counter] = i.key(); + m_program->setUniformValue(m_colorUniformLoc, QVector3D((counter & 0xff) / 255.0f, + ((counter & 0xff00) >> 8) / 255.0f, + ((counter & 0xff0000) >> 16) / 255.0f)); + counter++; + } else { + m_program->setUniformValue(m_colorUniformLoc, data->color); + } + m_program->setUniformValue(m_minUniformLoc, data->min); + m_program->setUniformValue(m_deltaUniformLoc, data->delta); + m_program->setUniformValue(m_matrixUniformLoc, data->matrix); + + if (!vbo) { + vbo = new QOpenGLBuffer; + m_seriesBufferMap.insert(i.key(), vbo); + vbo->create(); + } + vbo->bind(); + if (data->dirty) { + vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat)); + data->dirty = false; + } - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); - if (data->type == QAbstractSeries::SeriesTypeLine) { - glLineWidth(data->width); - glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2); - } else { // Scatter - m_program->setUniformValue(m_pointSizeUniformLoc, data->width); - glDrawArrays(GL_POINTS, 0, data->array.size() / 2); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + if (data->type == QAbstractSeries::SeriesTypeLine) { + glLineWidth(data->width); + glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2); + } else { // Scatter + m_program->setUniformValue(m_pointSizeUniformLoc, data->width); + glDrawArrays(GL_POINTS, 0, data->array.size() / 2); + } + vbo->release(); } - vbo->release(); } } |