From 1e99ff2c532977d21776f5f363b8171ef147a7d9 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 26 Mar 2014 09:52:28 +0200 Subject: Make axis labels more accurate by using qreals for label values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also refactor axis formatter sub grid array to be one dimensional. There is no need to know which segment each sub grid line belongs to. Change-Id: Ie9813088650fcc0ca844f3c358ea1abae9258367 Reviewed-by: Tomi Korpipää --- .../axis/qlogvalue3daxisformatter.cpp | 25 +++++--- .../axis/qvalue3daxisformatter.cpp | 66 +++++++++++++--------- src/datavisualization/axis/qvalue3daxisformatter.h | 7 ++- .../axis/qvalue3daxisformatter_p.h | 7 ++- src/datavisualization/engine/axisrendercache.cpp | 37 ++++++------ src/datavisualization/engine/bars3drenderer.cpp | 8 +-- src/datavisualization/engine/scatter3drenderer.cpp | 6 +- src/datavisualization/engine/surface3drenderer.cpp | 6 +- src/datavisualization/utils/utils.cpp | 2 +- src/datavisualization/utils/utils_p.h | 2 +- 10 files changed, 94 insertions(+), 72 deletions(-) diff --git a/src/datavisualization/axis/qlogvalue3daxisformatter.cpp b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp index 36f27c02..17ae4476 100644 --- a/src/datavisualization/axis/qlogvalue3daxisformatter.cpp +++ b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp @@ -293,6 +293,11 @@ QLogValue3DAxisFormatterPrivate::~QLogValue3DAxisFormatterPrivate() void QLogValue3DAxisFormatterPrivate::recalculate() { + // When doing position/value mappings, base doesn't matter, so just use natural logarithm + m_logMin = qLn(qreal(m_min)); + m_logMax = qLn(qreal(m_max)); + m_logRangeNormalizer = m_logMax - m_logMin; + if (m_base > 0.0) { // Update parent axis segment counts qreal logMin = qLn(qreal(m_min)) / qLn(m_base); @@ -309,17 +314,19 @@ void QLogValue3DAxisFormatterPrivate::recalculate() m_axis->setSubSegmentCount(subSegmentCount); } - resetPositionArrays(); + resetArrays(); // Calculate segment positions for (int i = 0; i < segmentCount; i++) { - float gridValue = float(qreal(i) / logRangeNormalizer); + float gridValue = float(i) / float(logRangeNormalizer); m_gridPositions[i] = gridValue; m_labelPositions[i] = gridValue; + m_labelValues[i] = qPow(m_base, qreal(i) + logMin); } // Ensure max value doesn't suffer from any rounding errors m_gridPositions[segmentCount] = 1.0f; m_labelPositions[segmentCount] = 1.0f; + m_labelValues[segmentCount] = qreal(m_max); float lastDiff = 1.0f - m_labelPositions.at(segmentCount - 1); float firstDiff = m_labelPositions.at(1) - m_labelPositions.at(0); m_evenSegments = qFuzzyCompare(lastDiff, firstDiff); @@ -327,13 +334,15 @@ void QLogValue3DAxisFormatterPrivate::recalculate() // Grid lines and label positions are the same as the parent class, so call parent impl // first to populate those QValue3DAxisFormatterPrivate::doRecalculate(); + + // Label value array needs to be repopulated + qreal segmentStep = 1.0 / qreal(m_axis->segmentCount()); + for (int i = 0; i < m_labelPositions.size(); i++) + m_labelValues[i] = qExp(segmentStep * qreal(i) * m_logRangeNormalizer + m_logMin); + m_evenSegments = true; } - // When doing position/value mappings, base doesn't matter, so just use natural logarithm - m_logMin = qLn(qreal(m_min)); - m_logMax = qLn(qreal(m_max)); - m_logRangeNormalizer = m_logMax - m_logMin; // Subgrid line positions are logarithmically spaced int subGridCount = m_axis->subSegmentCount() - 1; @@ -355,8 +364,8 @@ void QLogValue3DAxisFormatterPrivate::recalculate() for (int j = 0; j < subGridCount; j++) { float position = m_gridPositions.at(i) + actualSubSegmentSteps.at(j); if (position > 1.0f) - position = 1.0f; - m_subGridPositions[i][j] = position; + position = 1.0f; + m_subGridPositions[i * subGridCount + j] = position; } } } diff --git a/src/datavisualization/axis/qvalue3daxisformatter.cpp b/src/datavisualization/axis/qvalue3daxisformatter.cpp index b1183023..7c7db398 100644 --- a/src/datavisualization/axis/qvalue3daxisformatter.cpp +++ b/src/datavisualization/axis/qvalue3daxisformatter.cpp @@ -104,11 +104,11 @@ QValue3DAxisFormatter *QValue3DAxisFormatter::createNewInstance() const } /*! - * This method populates the label and grid line position arrays, as well as calculates - * any values needed for mapping between value and position. It is allowed to access + * This method populates the label and grid line position arrays and the label value array, as well + * as calculates any values needed for mapping between value and position. It is allowed to access * the parent axis from inside this function. * - * This method must be reimplemented in a subclass. The resetPositionArrays() method must be called + * This method must be reimplemented in a subclass. The resetArrays() method must be called * in the subclass implementation before the position arrays are recalculated. If the subclass * implementation changes any parent axis values, these changes must be done before * the resetPositionArrays() call. @@ -116,7 +116,7 @@ QValue3DAxisFormatter *QValue3DAxisFormatter::createNewInstance() const * See gridPositions(), subGridPositions(), and labelPositions() documentation about the arrays * that need to be populated. * - * \sa gridPositions(), subGridPositions(), labelPositions(), axis(), resetPositionArrays() + * \sa gridPositions(), subGridPositions(), labelPositions(), axis(), resetArrays() */ void QValue3DAxisFormatter::recalculate() { @@ -145,7 +145,7 @@ QString QValue3DAxisFormatter::labelForIndex(int index) const * * \sa recalculate(), labelForIndex(), QValue3DAxis::labelFormat */ -QString QValue3DAxisFormatter::stringForValue(float value, const QString &format) const +QString QValue3DAxisFormatter::stringForValue(qreal value, const QString &format) const { return d_ptr->stringForValue(value, format); } @@ -199,9 +199,9 @@ void QValue3DAxisFormatter::populateCopy(QValue3DAxisFormatter ©) const * * \sa gridPositions(), subGridPositions(), labelPositions(), axis(), recalculate() */ -void QValue3DAxisFormatter::resetPositionArrays() +void QValue3DAxisFormatter::resetArrays() { - d_ptr->resetPositionArrays(); + d_ptr->resetArrays(); } /*! @@ -227,7 +227,7 @@ QValue3DAxis *QValue3DAxisFormatter::axis() const } /*! - * \return a reference to the array of normalized grid positions. + * \return a reference to the array of normalized grid line positions. * The array size is equal to the segment count of the parent axis plus one. * The values should be between 0.0 (for minimum value) and 1.0 (for maximum value), inclusive. * The grid line at the index zero corresponds to the minimum value of the axis. @@ -240,14 +240,14 @@ QVector &QValue3DAxisFormatter::gridPositions() const } /*! - * \return a reference to the array of normalized subgrid positions. + * \return a reference to the array of normalized subgrid line positions. * The array size is equal to segment count of the parent axis times sub-segment count of the parent * axis minus one. * The values should be between 0.0 (for minimum value) and 1.0 (for maximum value), inclusive. * * \sa QValue3DAxis::segmentCount, QValue3DAxis::subSegmentCount */ -QVector > &QValue3DAxisFormatter::subGridPositions() const +QVector &QValue3DAxisFormatter::subGridPositions() const { return d_ptr->m_subGridPositions; } @@ -265,6 +265,18 @@ QVector &QValue3DAxisFormatter::labelPositions() const return d_ptr->m_labelPositions; } +/*! + * \return a reference to the array of values used to generate label strings. + * The array size is equal to the size of the label positions array and + * the indexes correspond to that array as well. + * + * \sa labelPositions() + */ +QVector &QValue3DAxisFormatter::labelValues() const +{ + return d_ptr->m_labelValues; +} + // QValue3DAxisFormatterPrivate QValue3DAxisFormatterPrivate::QValue3DAxisFormatterPrivate(QValue3DAxisFormatter *q) : QObject(0), @@ -298,24 +310,27 @@ void QValue3DAxisFormatterPrivate::recalculate() void QValue3DAxisFormatterPrivate::doRecalculate() { - resetPositionArrays(); + resetArrays(); int segmentCount = m_axis->segmentCount(); int subGridCount = m_axis->subSegmentCount() - 1; - float segmentStep = 1.0f / float(segmentCount); - float subSegmentStep = 0; + // Use qreals for intermediate calculations for better accuracy on label values + qreal segmentStep = 1.0 / qreal(segmentCount); + qreal subSegmentStep = 0; if (subGridCount > 0) - subSegmentStep = segmentStep / float(subGridCount + 1); + subSegmentStep = segmentStep / qreal(subGridCount + 1); // Calculate positions + qreal rangeNormalizer = qreal(m_max - m_min); for (int i = 0; i < segmentCount; i++) { - float gridValue = segmentStep * i; - m_gridPositions[i] = gridValue; - m_labelPositions[i] = gridValue; + qreal gridValue = segmentStep * qreal(i); + m_gridPositions[i] = float(gridValue); + m_labelPositions[i] = float(gridValue); + m_labelValues[i] = gridValue * rangeNormalizer + qreal(m_min); if (m_subGridPositions.size()) { for (int j = 0; j < subGridCount; j++) - m_subGridPositions[i][j] = gridValue + subSegmentStep * (j + 1); + m_subGridPositions[i * subGridCount + j] = gridValue + subSegmentStep * (j + 1); } } @@ -343,10 +358,10 @@ void QValue3DAxisFormatterPrivate::doPopulateCopy(QValue3DAxisFormatterPrivate & QString QValue3DAxisFormatterPrivate::labelForIndex(int index) const { - return q_ptr->stringForValue(q_ptr->valueAt(m_gridPositions.at(index)), m_axis->labelFormat()); + return q_ptr->stringForValue(m_labelValues.at(index), m_axis->labelFormat()); } -QString QValue3DAxisFormatterPrivate::stringForValue(float value, const QString &format) +QString QValue3DAxisFormatterPrivate::stringForValue(qreal value, const QString &format) { if (m_previousLabelFormat.compare(format)) { // Format string different than the previous one used, reparse it @@ -382,24 +397,21 @@ void QValue3DAxisFormatterPrivate::setAxis(QValue3DAxis *axis) m_axis = axis; } -void QValue3DAxisFormatterPrivate::resetPositionArrays() +void QValue3DAxisFormatterPrivate::resetArrays() { m_gridPositions.clear(); m_subGridPositions.clear(); m_labelPositions.clear(); + m_labelValues.clear(); int segmentCount = m_axis->segmentCount(); int subGridCount = m_axis->subSegmentCount() - 1; m_gridPositions.resize(segmentCount + 1); - - if (subGridCount > 0) { - m_subGridPositions.resize(segmentCount); - for (int i = 0; i < segmentCount; i++) - m_subGridPositions[i].resize(subGridCount); - } + m_subGridPositions.resize(segmentCount * subGridCount); m_labelPositions.resize(segmentCount + 1); + m_labelValues.resize(segmentCount + 1); } void QValue3DAxisFormatterPrivate::markDirty(bool labelsChange) diff --git a/src/datavisualization/axis/qvalue3daxisformatter.h b/src/datavisualization/axis/qvalue3daxisformatter.h index 6e0e873a..548135c0 100644 --- a/src/datavisualization/axis/qvalue3daxisformatter.h +++ b/src/datavisualization/axis/qvalue3daxisformatter.h @@ -44,18 +44,19 @@ protected: virtual QValue3DAxisFormatter *createNewInstance() const; virtual void recalculate(); virtual QString labelForIndex(int index) const; - virtual QString stringForValue(float value, const QString &format) const; + virtual QString stringForValue(qreal value, const QString &format) const; virtual float positionAt(float value) const; virtual float valueAt(float position) const; virtual void populateCopy(QValue3DAxisFormatter ©) const; - void resetPositionArrays(); + void resetArrays(); void markDirty(bool labelsChange = false); QValue3DAxis *axis() const; QVector &gridPositions() const; - QVector > &subGridPositions() const; + QVector &subGridPositions() const; QVector &labelPositions() const; + QVector &labelValues() const; QScopedPointer d_ptr; diff --git a/src/datavisualization/axis/qvalue3daxisformatter_p.h b/src/datavisualization/axis/qvalue3daxisformatter_p.h index 74b6f20f..b6749fb3 100644 --- a/src/datavisualization/axis/qvalue3daxisformatter_p.h +++ b/src/datavisualization/axis/qvalue3daxisformatter_p.h @@ -52,12 +52,12 @@ public: void doPopulateCopy(QValue3DAxisFormatterPrivate ©); QString labelForIndex(int index) const; - QString stringForValue(float value, const QString &format); + QString stringForValue(qreal value, const QString &format); float positionAt(float value) const; float valueAt(float position) const; void setAxis(QValue3DAxis *axis); - void resetPositionArrays(); + void resetArrays(); void markDirty(bool labelsChange); public slots: @@ -73,8 +73,9 @@ protected: float m_rangeNormalizer; QVector m_gridPositions; - QVector > m_subGridPositions; + QVector m_subGridPositions; QVector m_labelPositions; + QVector m_labelValues; QValue3DAxis *m_axis; diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp index 1b4fd52d..8e78522a 100644 --- a/src/datavisualization/engine/axisrendercache.cpp +++ b/src/datavisualization/engine/axisrendercache.cpp @@ -119,29 +119,28 @@ void AxisRenderCache::updateAllPositions() // by caching all grid and subgrid positions into a single vector. // If subgrid lines are ever themed separately, this array will probably become obsolete. if (m_formatter) { - int subGridCount = m_subSegmentCount - 1; - int fullSize = m_segmentCount + 1 + (m_segmentCount * subGridCount); + int gridCount = m_formatter->gridPositions().size(); + int subGridCount = m_formatter->subGridPositions().size(); + int labelCount = m_formatter->labelPositions().size(); + int fullSize = gridCount + subGridCount; + m_adjustedGridLinePositions.resize(fullSize); - m_adjustedLabelPositions.resize(m_segmentCount + 1); + m_adjustedLabelPositions.resize(labelCount); int index = 0; - int segment = 0; - for (; segment < m_segmentCount; segment++) { - m_adjustedLabelPositions[segment] = - m_formatter->labelPositions().at(segment) * m_scale + m_translate; + int grid = 0; + int label = 0; + for (; label < labelCount; label++) { + m_adjustedLabelPositions[label] = + m_formatter->labelPositions().at(label) * m_scale + m_translate; + } + for (; grid < gridCount; grid++) { m_adjustedGridLinePositions[index++] = - m_formatter->gridPositions().at(segment) * m_scale + m_translate; - if (subGridCount > 0) { - for (int subGrid = 0; subGrid < subGridCount; subGrid++) { - m_adjustedGridLinePositions[index++] = - m_formatter->subGridPositions().at(segment).at(subGrid) * m_scale + m_translate; - } - } + m_formatter->gridPositions().at(grid) * m_scale + m_translate; + } + for (int subGrid = 0; subGrid < subGridCount; subGrid++) { + m_adjustedGridLinePositions[index++] = + m_formatter->subGridPositions().at(subGrid) * m_scale + m_translate; } - // Last gridline - m_adjustedLabelPositions[segment] = - m_formatter->labelPositions().at(segment) * m_scale + m_translate; - m_adjustedGridLinePositions[index] = - m_formatter->gridPositions().at(segment) * m_scale + m_translate; m_positionsDirty = false; } diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index f0a5e3d4..0cad8522 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -648,7 +648,7 @@ void Bars3DRenderer::drawSlicedScene() // Create label texture if we need it if (item.sliceLabel().isNull() || m_updateLabels) { QString valueLabelText = m_axisCacheY.formatter()->stringForValue( - item.value(), m_axisCacheY.labelFormat()); + qreal(item.value()), m_axisCacheY.labelFormat()); item.setSliceLabel(valueLabelText); m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel()); m_updateLabels = false; @@ -674,7 +674,7 @@ void Bars3DRenderer::drawSlicedScene() // Create label texture if we need it if (item.sliceLabel().isNull() || m_updateLabels) { QString valueLabelText = m_axisCacheY.formatter()->stringForValue( - item.value(), m_axisCacheY.labelFormat()); + qreal(item.value()), m_axisCacheY.labelFormat()); item.setSliceLabel(valueLabelText); m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel()); m_updateLabels = false; @@ -1822,7 +1822,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Custom format expects printf format specifier. There is no tag for it. labelText = m_axisCacheY.formatter()->stringForValue( - selectedBar->value(), + qreal(selectedBar->value()), m_visibleSeriesList[m_visualSelectedBarSeriesIndex].itemLabelFormat()); int selBarPosRow = selectedBar->position().x(); @@ -1843,7 +1843,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) if (labelText.contains(valueLabelTag)) { QString valueLabelText = m_axisCacheY.formatter()->stringForValue( - selectedBar->value(), m_axisCacheY.labelFormat()); + qreal(selectedBar->value()), m_axisCacheY.labelFormat()); labelText.replace(valueLabelTag, valueLabelText); } diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index f85936f9..49595052 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -1463,17 +1463,17 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (labelText.contains(xLabelTag)) { QString valueLabelText = m_axisCacheX.formatter()->stringForValue( - selectedItem->position().x(), m_axisCacheX.labelFormat()); + qreal(selectedItem->position().x()), m_axisCacheX.labelFormat()); labelText.replace(xLabelTag, valueLabelText); } if (labelText.contains(yLabelTag)) { QString valueLabelText = m_axisCacheY.formatter()->stringForValue( - selectedItem->position().y(), m_axisCacheY.labelFormat()); + qreal(selectedItem->position().y()), m_axisCacheY.labelFormat()); labelText.replace(yLabelTag, valueLabelText); } if (labelText.contains(zLabelTag)) { QString valueLabelText = m_axisCacheZ.formatter()->stringForValue( - selectedItem->position().z(), m_axisCacheZ.labelFormat()); + qreal(selectedItem->position().z()), m_axisCacheZ.labelFormat()); labelText.replace(zLabelTag, valueLabelText); } labelText.replace(seriesNameTag, diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 9ce80081..86972fdc 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -2242,17 +2242,17 @@ QString Surface3DRenderer::createSelectionLabel(SurfaceSeriesRenderCache *cache, if (labelText.contains(xLabelTag)) { QString valueLabelText = m_axisCacheX.formatter()->stringForValue( - dataArray.at(row)->at(column).x(), m_axisCacheX.labelFormat()); + qreal(dataArray.at(row)->at(column).x()), m_axisCacheX.labelFormat()); labelText.replace(xLabelTag, valueLabelText); } if (labelText.contains(yLabelTag)) { QString valueLabelText = m_axisCacheY.formatter()->stringForValue( - dataArray.at(row)->at(column).y(), m_axisCacheY.labelFormat()); + qreal(dataArray.at(row)->at(column).y()), m_axisCacheY.labelFormat()); labelText.replace(yLabelTag, valueLabelText); } if (labelText.contains(zLabelTag)) { QString valueLabelText = m_axisCacheZ.formatter()->stringForValue( - dataArray.at(row)->at(column).z(), m_axisCacheZ.labelFormat()); + qreal(dataArray.at(row)->at(column).z()), m_axisCacheZ.labelFormat()); labelText.replace(zLabelTag, valueLabelText); } diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp index be087f91..38fa0b2a 100644 --- a/src/datavisualization/utils/utils.cpp +++ b/src/datavisualization/utils/utils.cpp @@ -187,7 +187,7 @@ Utils::ParamType Utils::findFormatParamType(const QString &format) return ParamTypeUnknown; } -QString Utils::formatLabel(const QByteArray &format, ParamType paramType, float value) +QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal value) { switch (paramType) { case ParamTypeInt: diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h index c87ef19a..d2c23abf 100644 --- a/src/datavisualization/utils/utils_p.h +++ b/src/datavisualization/utils/utils_p.h @@ -67,7 +67,7 @@ public: static QImage getGradientImage(const QLinearGradient &gradient); static ParamType findFormatParamType(const QString &format); - static QString formatLabel(const QByteArray &format, ParamType paramType, float value); + static QString formatLabel(const QByteArray &format, ParamType paramType, qreal value); static QString defaultLabelFormat(); static float wrapValue(float value, float min, float max); -- cgit v1.2.3