diff options
author | Miikka Heikkinen <miikka.heikkinen@digia.com> | 2014-03-12 09:25:51 +0200 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@digia.com> | 2014-03-24 09:34:40 +0200 |
commit | ba812351a1577163a1c9794b667f2b4e3acb9373 (patch) | |
tree | 85b64bb39b8e48641ef27eda504251b2d33586a7 /src | |
parent | b6621d5651e2f8c8ed4ab4a99d15a36823ac897c (diff) |
Introduce value axis formatter
Currently only used for label formatting.
Also some other preparatory changes for logaxis.
Task-number: QTRD-2787
Note: Not to be merged until 1.0 is released
Change-Id: I2d7ab70b9c51677d0edd5b0226fb779c9e346286
Reviewed-by: Tomi Korpipää <tomi.korpipaa@digia.com>
Diffstat (limited to 'src')
18 files changed, 860 insertions, 69 deletions
diff --git a/src/datavisualization/axis/axis.pri b/src/datavisualization/axis/axis.pri index 2c8bf70e..24463d1e 100644 --- a/src/datavisualization/axis/axis.pri +++ b/src/datavisualization/axis/axis.pri @@ -4,9 +4,12 @@ HEADERS += \ $$PWD/qvalue3daxis.h \ $$PWD/qvalue3daxis_p.h \ $$PWD/qcategory3daxis.h \ - $$PWD/qcategory3daxis_p.h + $$PWD/qcategory3daxis_p.h \ + $$PWD/qvalue3daxisformatter.h \ + $$PWD/qvalue3daxisformatter_p.h SOURCES += \ $$PWD/qabstract3daxis.cpp \ $$PWD/qvalue3daxis.cpp \ - $$PWD/qcategory3daxis.cpp + $$PWD/qcategory3daxis.cpp \ + $$PWD/qvalue3daxisformatter.cpp diff --git a/src/datavisualization/axis/qabstract3daxis.cpp b/src/datavisualization/axis/qabstract3daxis.cpp index 3a327caa..e01f980c 100644 --- a/src/datavisualization/axis/qabstract3daxis.cpp +++ b/src/datavisualization/axis/qabstract3daxis.cpp @@ -265,9 +265,7 @@ QAbstract3DAxisPrivate::QAbstract3DAxisPrivate(QAbstract3DAxis *q, QAbstract3DAx m_isDefaultAxis(false), m_min(0.0f), m_max(10.0f), - m_autoAdjust(true), - m_onlyPositiveValues(false), - m_allowMinMaxSame(false) + m_autoAdjust(true) { } @@ -293,14 +291,25 @@ void QAbstract3DAxisPrivate::updateLabels() void QAbstract3DAxisPrivate::setRange(float min, float max) { bool adjusted = false; - if (m_onlyPositiveValues) { - if (min < 0.0f) { - min = 0.0f; - adjusted = true; - } - if (max < 0.0f) { - max = 0.0f; - adjusted = true; + if (!allowNegatives()) { + if (allowZero()) { + if (min < 0.0f) { + min = 0.0f; + adjusted = true; + } + if (max < 0.0f) { + max = 0.0f; + adjusted = true; + } + } else { + if (min <= 0.0f) { + min = 1.0f; + adjusted = true; + } + if (max <= 0.0f) { + max = 1.0f; + adjusted = true; + } } } // If min >= max, we adjust ranges so that @@ -312,8 +321,8 @@ void QAbstract3DAxisPrivate::setRange(float min, float max) m_min = min; minDirty = true; } - if (m_max != max || min > max || (!m_allowMinMaxSame && min == max)) { - if (min > max || (!m_allowMinMaxSame && min == max)) { + if (m_max != max || min > max || (!allowMinMaxSame() && min == max)) { + if (min > max || (!allowMinMaxSame() && min == max)) { m_max = min + 1.0f; adjusted = true; } else { @@ -339,17 +348,25 @@ void QAbstract3DAxisPrivate::setRange(float min, float max) void QAbstract3DAxisPrivate::setMin(float min) { - if (m_onlyPositiveValues) { - if (min < 0.0f) { - min = 0.0f; - qWarning() << "Warning: Tried to set negative minimum for an axis that only supports" - " positive values:" << min; + if (!allowNegatives()) { + if (allowZero()) { + if (min < 0.0f) { + min = 0.0f; + qWarning() << "Warning: Tried to set negative minimum for an axis that only" + "supports positive values and zero:" << min; + } + } else { + if (min <= 0.0f) { + min = 1.0f; + qWarning() << "Warning: Tried to set negative or zero minimum for an axis that only" + "supports positive values:" << min; + } } } if (m_min != min) { bool maxChanged = false; - if (min > m_max || (!m_allowMinMaxSame && min == m_max)) { + if (min > m_max || (!allowMinMaxSame() && min == m_max)) { float oldMax = m_max; m_max = min + 1.0f; qWarning() << "Warning: Tried to set minimum to equal or larger than maximum for" @@ -368,22 +385,34 @@ void QAbstract3DAxisPrivate::setMin(float min) void QAbstract3DAxisPrivate::setMax(float max) { - if (m_onlyPositiveValues) { - if (max < 0.0f) { - max = 0.0f; - qWarning() << "Warning: Tried to set negative maximum for an axis that only supports" - " positive values:" << max; + if (!allowNegatives()) { + if (allowZero()) { + if (max < 0.0f) { + max = 0.0f; + qWarning() << "Warning: Tried to set negative maximum for an axis that only" + "supports positive values and zero:" << max; + } + } else { + if (max <= 0.0f) { + max = 1.0f; + qWarning() << "Warning: Tried to set negative or zero maximum for an axis that only" + "supports positive values:" << max; + } } } if (m_max != max) { bool minChanged = false; - if (m_min > max || (!m_allowMinMaxSame && m_min == max)) { + if (m_min > max || (!allowMinMaxSame() && m_min == max)) { float oldMin = m_min; m_min = max - 1.0f; - if (m_onlyPositiveValues && m_min < 0.0f) { - m_min = 0.0f; - if (!m_allowMinMaxSame && max == 0.0f) { + if (!allowNegatives() && m_min < 0.0f) { + if (allowZero()) { + m_min = 0.0f; + } else { + m_min = max / 2.0f; // Need some positive value smaller than max + } + if (!allowMinMaxSame() && max == 0.0f) { m_min = oldMin; qWarning() << "Unable to set maximum value to zero."; return; diff --git a/src/datavisualization/axis/qabstract3daxis_p.h b/src/datavisualization/axis/qabstract3daxis_p.h index 4eb8de68..80f8f0b2 100644 --- a/src/datavisualization/axis/qabstract3daxis_p.h +++ b/src/datavisualization/axis/qabstract3daxis_p.h @@ -53,6 +53,9 @@ public: protected: virtual void updateLabels(); + virtual bool allowZero() = 0; + virtual bool allowNegatives() = 0; + virtual bool allowMinMaxSame() = 0; QAbstract3DAxis *q_ptr; @@ -64,8 +67,6 @@ protected: float m_min; float m_max; bool m_autoAdjust; - bool m_onlyPositiveValues; - bool m_allowMinMaxSame; friend class QAbstract3DAxis; friend class QValue3DAxis; diff --git a/src/datavisualization/axis/qcategory3daxis.cpp b/src/datavisualization/axis/qcategory3daxis.cpp index c11a65eb..d4152c90 100644 --- a/src/datavisualization/axis/qcategory3daxis.cpp +++ b/src/datavisualization/axis/qcategory3daxis.cpp @@ -121,8 +121,6 @@ QCategory3DAxisPrivate::QCategory3DAxisPrivate(QCategory3DAxis *q) : QAbstract3DAxisPrivate(q, QAbstract3DAxis::AxisTypeCategory), m_labelsExplicitlySet(false) { - m_onlyPositiveValues = true; - m_allowMinMaxSame = true; } QCategory3DAxisPrivate::~QCategory3DAxisPrivate() @@ -142,6 +140,21 @@ void QCategory3DAxisPrivate::setDataLabels(const QStringList &labels) } } +bool QCategory3DAxisPrivate::allowZero() +{ + return true; +} + +bool QCategory3DAxisPrivate::allowNegatives() +{ + return false; +} + +bool QCategory3DAxisPrivate::allowMinMaxSame() +{ + return true; +} + QCategory3DAxis *QCategory3DAxisPrivate::qptr() { return static_cast<QCategory3DAxis *>(q_ptr); diff --git a/src/datavisualization/axis/qcategory3daxis_p.h b/src/datavisualization/axis/qcategory3daxis_p.h index 1ba5ccb0..0f590ece 100644 --- a/src/datavisualization/axis/qcategory3daxis_p.h +++ b/src/datavisualization/axis/qcategory3daxis_p.h @@ -45,6 +45,11 @@ public: void setDataLabels(const QStringList &labels); +protected: + virtual bool allowZero(); + virtual bool allowNegatives(); + virtual bool allowMinMaxSame(); + private: QCategory3DAxis *qptr(); diff --git a/src/datavisualization/axis/qvalue3daxis.cpp b/src/datavisualization/axis/qvalue3daxis.cpp index 0d53291d..e93f440f 100644 --- a/src/datavisualization/axis/qvalue3daxis.cpp +++ b/src/datavisualization/axis/qvalue3daxis.cpp @@ -18,7 +18,7 @@ #include "qvalue3daxis.h" #include "qvalue3daxis_p.h" -#include "utils_p.h" +#include "qvalue3daxisformatter_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -79,6 +79,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION QValue3DAxis::QValue3DAxis(QObject *parent) : QAbstract3DAxis(new QValue3DAxisPrivate(this), parent) { + setFormatter(new QValue3DAxisFormatter); } /*! @@ -88,7 +89,6 @@ QValue3DAxis::~QValue3DAxis() { } - /*! * \property QValue3DAxis::segmentCount * @@ -169,6 +169,31 @@ QString QValue3DAxis::labelFormat() const } /*! + * \property QValue3DAxis::formatter + * + * Defines the axis \a formatter to be used. Any existing formatter is deleted when a new formatter + * is set. + */ +void QValue3DAxis::setFormatter(QValue3DAxisFormatter *formatter) +{ + Q_ASSERT(formatter); + + if (formatter != dptr()->m_formatter) { + delete dptr()->m_formatter; + dptr()->m_formatter = formatter; + formatter->setParent(this); + formatter->d_ptr->setAxis(this); + emit formatterChanged(formatter); + emit dptr()->formatterDirty(); + } +} + +QValue3DAxisFormatter *QValue3DAxis::formatter() const +{ + return dptrc()->m_formatter; +} + +/*! * \internal */ QValue3DAxisPrivate *QValue3DAxis::dptr() @@ -189,7 +214,8 @@ QValue3DAxisPrivate::QValue3DAxisPrivate(QValue3DAxis *q) m_segmentCount(5), m_subSegmentCount(1), m_labelFormat(Utils::defaultLabelFormat()), - m_labelsDirty(true) + m_labelsDirty(true), + m_formatter(0) { } @@ -243,25 +269,30 @@ void QValue3DAxisPrivate::updateLabels() QStringList newLabels; newLabels.reserve(m_segmentCount + 1); - // First label is at axis min, which is an extra segment - float segmentStep = (m_max - m_min) / m_segmentCount; - - QString formatString(m_labelFormat); - Utils::ParamType paramType = Utils::findFormatParamType(formatString); - QByteArray formatArray = formatString.toUtf8(); - - for (int i = 0; i < m_segmentCount; i++) { - float value = m_min + (segmentStep * i); - newLabels.append(Utils::formatLabel(formatArray, paramType, value)); + for (int i = 0; i <= m_segmentCount; i++) { + float value = m_formatter->valueAt(m_formatter->gridPosition(i)); + newLabels.append(m_formatter->stringForValue(value, m_labelFormat)); } - // Ensure max label doesn't suffer from any rounding errors - newLabels.append(Utils::formatLabel(formatArray, paramType, m_max)); - if (m_labels != newLabels) m_labels = newLabels; } +bool QValue3DAxisPrivate::allowZero() +{ + return m_formatter->allowZero(); +} + +bool QValue3DAxisPrivate::allowNegatives() +{ + return m_formatter->allowNegatives(); +} + +bool QValue3DAxisPrivate::allowMinMaxSame() +{ + return false; +} + QValue3DAxis *QValue3DAxisPrivate::qptr() { return static_cast<QValue3DAxis *>(q_ptr); diff --git a/src/datavisualization/axis/qvalue3daxis.h b/src/datavisualization/axis/qvalue3daxis.h index f0af759b..c5982f33 100644 --- a/src/datavisualization/axis/qvalue3daxis.h +++ b/src/datavisualization/axis/qvalue3daxis.h @@ -20,6 +20,7 @@ #define QVALUE3DAXIS_H #include <QtDataVisualization/qabstract3daxis.h> +#include <QtDataVisualization/qvalue3daxisformatter.h> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -31,6 +32,7 @@ class QT_DATAVISUALIZATION_EXPORT QValue3DAxis : public QAbstract3DAxis Q_PROPERTY(int segmentCount READ segmentCount WRITE setSegmentCount NOTIFY segmentCountChanged) Q_PROPERTY(int subSegmentCount READ subSegmentCount WRITE setSubSegmentCount NOTIFY subSegmentCountChanged) Q_PROPERTY(QString labelFormat READ labelFormat WRITE setLabelFormat NOTIFY labelFormatChanged) + Q_PROPERTY(QValue3DAxisFormatter* formatter READ formatter WRITE setFormatter NOTIFY formatterChanged REVISION 1) public: explicit QValue3DAxis(QObject *parent = 0); @@ -45,10 +47,14 @@ public: void setLabelFormat(const QString &format); QString labelFormat() const; + void setFormatter(QValue3DAxisFormatter *formatter); + QValue3DAxisFormatter *formatter() const; + signals: void segmentCountChanged(int count); void subSegmentCountChanged(int count); void labelFormatChanged(const QString &format); + void formatterChanged(QValue3DAxisFormatter *formatter); protected: QValue3DAxisPrivate *dptr(); @@ -59,6 +65,7 @@ private: friend class Bars3DController; friend class Scatter3DController; friend class Surface3DController; + friend class QValue3DAxisFormatter; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/axis/qvalue3daxis_p.h b/src/datavisualization/axis/qvalue3daxis_p.h index 21fd78ab..c198db75 100644 --- a/src/datavisualization/axis/qvalue3daxis_p.h +++ b/src/datavisualization/axis/qvalue3daxis_p.h @@ -46,14 +46,22 @@ public: virtual void setMin(float min); virtual void setMax (float max); +signals: + void formatterDirty(); + protected: void emitLabelsChanged(); virtual void updateLabels(); + virtual bool allowZero(); + virtual bool allowNegatives(); + virtual bool allowMinMaxSame(); + int m_segmentCount; int m_subSegmentCount; QString m_labelFormat; bool m_labelsDirty; + QValue3DAxisFormatter *m_formatter; private: QValue3DAxis *qptr(); diff --git a/src/datavisualization/axis/qvalue3daxisformatter.cpp b/src/datavisualization/axis/qvalue3daxisformatter.cpp new file mode 100644 index 00000000..5f943bd3 --- /dev/null +++ b/src/datavisualization/axis/qvalue3daxisformatter.cpp @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "qvalue3daxisformatter_p.h" +#include "qvalue3daxis_p.h" + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +/*! + * \class QValue3DAxisFormatter + * \inmodule QtDataVisualization + * \brief QValue3DAxisFormatter is base class for value axis formatters. + * \since Qt Data Visualization 1.1 + * + * This class provides formatting rules for a linear QValue3DAxis. Subclass it if you + * want to implement custom value axes. + * + * \sa QLog3DAxisFormatter + */ + +/*! + * \qmltype ValueAxis3DFormatter + * \inqmlmodule QtDataVisualization + * \since QtDataVisualization 1.0 + * \ingroup datavisualization_qml + * \instantiates QValue3DAxisFormatter + * \brief ValueAxis3DFormatter is base type for value axis formatters. + * + * This type provides formatting rules for a linear ValueAxis3D. + * This type is the default type for ValueAxis3D and thus never needs to be explicitly created. + */ + +/*! + * \internal + */ +QValue3DAxisFormatter::QValue3DAxisFormatter(QValue3DAxisFormatterPrivate *d, QObject *parent) : + QObject(parent), + d_ptr(d) +{ +} + +/*! + * Constructs a new QValue3DAxisFormatter instance with optional \a parent. + */ +QValue3DAxisFormatter::QValue3DAxisFormatter(QObject *parent) : + QObject(parent), + d_ptr(new QValue3DAxisFormatterPrivate(this)) +{ +} + +/*! + * Destroys QValue3DAxisFormatter. + */ +QValue3DAxisFormatter::~QValue3DAxisFormatter() +{ +} + +/*! + * \return \c true if negative values are valid values for parent axis. + * The default implementation always returns true. + */ +bool QValue3DAxisFormatter::allowNegatives() const +{ + return true; +} + +/*! + * \return \c true if zero is a valid value for parent axis. + * The default implementation always returns true. + */ +bool QValue3DAxisFormatter::allowZero() const +{ + return true; +} + +/*! + * \return the normalized position along the axis for the grid line indicated by the \a index. + * The total amount of grid lines is equal to segment count plus one. The returned value should be + * between -1.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. + * This value is used to position the segment grid lines when the graph is rendered. + * + * \sa doRecalculate(), gridPositions() + */ +float QValue3DAxisFormatter::gridPosition(int index) +{ + d_ptr->recalculate(); + + Q_ASSERT(d_ptr->m_gridPositions.size() > index); + + return d_ptr->m_gridPositions.at(index); +} + +/*! + * \return the normalized position along the axis for a subsegment grid line of a segment. + * The \a subGridIndex and \a segmentIndex specify the desired subgrid line. + * The returned value should be between -1.0 (for minimum value) and 1.0 (for maximum value), + * inclusive. + * The amount of subgrid lines per segment is equal to subsegment count minus one. + * This value is used to position the subsegment grid lines when the graph is rendered. + * + * \sa doRecalculate(), subGridPositions() + */ +float QValue3DAxisFormatter::subGridPosition(int subGridIndex, int segmentIndex) +{ + d_ptr->recalculate(); + + Q_ASSERT(d_ptr->m_subGridPositions.size() > subGridIndex); + Q_ASSERT(d_ptr->m_subGridPositions.at(0).size() > segmentIndex); + + return d_ptr->m_subGridPositions.at(segmentIndex).at(subGridIndex); +} + +/*! + * \return the normalized position along the axis for given label \a index. + * The total amount of labels is equal to segment count plus one. The label \a index zero + * corresponds to the minimum value of the axis. + * The returned value should be between -1.0 (for minimum value) and 1.0 (for maximum value), + * inclusive. This value is used to position the axis labels when the graph is rendered. + * + * \sa doRecalculate(), labelPositions() + */ +float QValue3DAxisFormatter::labelPosition(int index) +{ + d_ptr->recalculate(); + + Q_ASSERT(d_ptr->m_labelPositions.size() > index); + + return d_ptr->m_labelPositions.at(index); +} + +/*! + * This method can be used to convert a value to a string using formatter specific formatting + * rules supported by the labelFormat property of the parent axis. + * + * \return the formatted label string using \a value and \a format. + * + * \sa doRecalculate(), doStringForValue(), QValue3DAxis::labelFormat + */ +QString QValue3DAxisFormatter::stringForValue(float value, const QString &format) +{ + d_ptr->recalculate(); + return doStringForValue(value, format); +} + +/*! + * \return the normalized position along the axis for the given \a value. + * The returned value should be between -1.0 (for minimum value) and 1.0 (for maximum value), + * inclusive. This value is used to position the items when the graph is rendered. + * + * \sa doRecalculate(), doPositionAt() + */ +float QValue3DAxisFormatter::positionAt(float value) +{ + d_ptr->recalculate(); + return doPositionAt(value); +} + +/*! + * \return the value at the normalized \a position along the axis. + * The \a position value should be between -1.0 (for minimum value) and 1.0 (for maximum value), + * inclusive. + * + * \sa doRecalculate(), doValueAt() + */ +float QValue3DAxisFormatter::valueAt(float position) +{ + d_ptr->recalculate(); + return doValueAt(position); +} + +/*! + * Creates a new empty instance of this formatter. Must be reimplemented in a subclass. + * + * \return the new instance. The renderer uses this method to cache a copy of the + * the formatter. The ownership of the new copy transfers to the caller. + */ +QValue3DAxisFormatter *QValue3DAxisFormatter::createNewInstance() +{ + return new QValue3DAxisFormatter(); +} + +/*! + * Populates the \a copy with the values of this formatter. + * + * \sa doPopulateCopy() + */ +void QValue3DAxisFormatter::populateCopy(QValue3DAxisFormatter ©) +{ + d_ptr->recalculate(); + doPopulateCopy(copy); +} + +/*! + * Marks this formatter dirty, prompting the renderer to make a new copy of its cache on the next + * renderer synchronization. Recalculation is also triggered on next access to the values. + * This method should be called by a subclass whenever the formatter + * is changed in a way that affects the resolved values. + */ +void QValue3DAxisFormatter::markDirty() +{ + d_ptr->m_needsRecalculate = true; + if (d_ptr->m_axis) + emit d_ptr->m_axis->dptr()->formatterDirty(); +} + +/*! + * \return the parent axis. The parent axis must only be accessed in doRecalculate() + * method to maintain thread safety in environments using a threaded renderer. + * + * \sa doRecalculate() + */ +QValue3DAxis *QValue3DAxisFormatter::axis() const +{ + return d_ptr->m_axis; +} + +/*! + * 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 + * the parent axis from inside this function. + * + * This method must be reimplemented in a subclass. + * + * \sa gridPositions(), subGridPositions(), labelPositions(), axis() + */ +void QValue3DAxisFormatter::doRecalculate() +{ + d_ptr->doRecalculate(); +} + +/*! + * Reimplement this method in a subclass to resolve the formatted string for a given \a value + * if the default formatting provided by QValue3DAxis::labelFormat property is not sufficient. + * + * \return the formatted label string using \a value and \a format. + * + * \sa doRecalculate(), stringForValue(), QValue3DAxis::labelFormat + */ +QString QValue3DAxisFormatter::doStringForValue(float value, const QString &format) +{ + return d_ptr->stringForValue(value, format); +} + +/*! + * Reimplement this method if the position cannot be resolved by linear interpolation + * between the parent axis minimum and maximum values. + * + * \return the normalized position along the axis for the given \a value. + * The returned value should be between -1.0 (for minimum value) and 1.0 (for maximum value), + * inclusive, if the value is within the parent axis range. + * + * \sa doRecalculate(), positionAt() + */ +float QValue3DAxisFormatter::doPositionAt(float value) +{ + return d_ptr->positionAt(value); +} + +/*! + * Reimplement this method if the value cannot be resolved by linear interpolation + * between the parent axis minimum and maximum values. + * + * \return the value at the normalized \a position along the axis. + * The \a position value should be between -1.0 (for minimum value) and 1.0 (for maximum value), + * inclusive to obtain values within the parent axis range. + * + * \sa doRecalculate(), positionAt() + */ +float QValue3DAxisFormatter::doValueAt(float position) +{ + return d_ptr->valueAt(position); +} + +/*! + * Copies all relevant values from this formatter to the \a copy. + * When reimplementing this method in a subclass, call the the superclass version at some point. + * The renderer uses this method to cache a copy of the the formatter. + * + * \return the new copy. The ownership of the new copy transfers to the caller. + */ +void QValue3DAxisFormatter::doPopulateCopy(QValue3DAxisFormatter ©) +{ + copy.d_ptr->m_min = d_ptr->m_min; + copy.d_ptr->m_max = d_ptr->m_max; +} + +/*! + * \return Returns a reference to the array of normalized grid positions. + * The array size is equal to the segment count of the parent axis plus one. + * The grid line at the index zero corresponds to the minimum value of the axis. + * + * \sa gridPosition() + */ +QVector<float> &QValue3DAxisFormatter::gridPositions() +{ + return d_ptr->m_gridPositions; +} + +/*! + * \return Returns a reference to the array of normalized subgrid positions. + * The array size is equal to subsegment count of the parent axis minus one. + * + * \sa subGridPosition() + */ +QVector<QVector<float> > &QValue3DAxisFormatter::subGridPositions() +{ + return d_ptr->m_subGridPositions; +} + +/*! + * \return Returns a reference to the array of normalized label positions. + * The array size is equal to the segment count of the parent axis plus one. + * + * \sa labelPosition() + */ +QVector<float> &QValue3DAxisFormatter::labelPositions() +{ + return d_ptr->m_labelPositions; +} + +// QValue3DAxisFormatterPrivate +QValue3DAxisFormatterPrivate::QValue3DAxisFormatterPrivate(QValue3DAxisFormatter *q) + : QObject(0), + q_ptr(q), + m_needsRecalculate(true), + m_min(0.0f), + m_max(0.0f), + m_rangeNormalizer(0.0f), + m_axis(0), + m_preparsedParamType(Utils::ParamTypeUnknown) +{ +} + +QValue3DAxisFormatterPrivate::~QValue3DAxisFormatterPrivate() +{ +} + +void QValue3DAxisFormatterPrivate::recalculate() +{ + // Only recalculate if we need to and have m_axis pointer. If we do not have + // m_axis, either we are not attached to an axis or this is a renderer cache. + if (m_axis && m_needsRecalculate) + q_ptr->doRecalculate(); +} + +void QValue3DAxisFormatterPrivate::doRecalculate() +{ + m_needsRecalculate = false; + + m_gridPositions.clear(); + m_subGridPositions.clear(); + m_labelPositions.clear(); + + int segmentCount = m_axis->segmentCount(); + int subGridCount = m_axis->subSegmentCount() - 1; + m_min = m_axis->min(); + m_max = m_axis->max(); + m_rangeNormalizer = (m_max - m_min) / 2.0f; + + m_gridPositions.resize(segmentCount + 1); + + float segmentStep = 2.0f / float(segmentCount); + float subSegmentStep = 0; + if (subGridCount > 0) { + subSegmentStep = segmentStep / (subGridCount + 1); + m_subGridPositions.resize(segmentCount); + for (int i = 0; i < segmentCount; i++) + m_subGridPositions[i].resize(subGridCount); + } + + m_labelPositions.resize(segmentCount + 1); + + // Calculate positions + + for (int i = 0; i < segmentCount; i++) { + float gridValue = -1.0f + segmentStep * i; + m_gridPositions[i] = gridValue; + m_labelPositions[i] = gridValue; + if (m_subGridPositions.size()) { + for (int j = 0; j < subGridCount; j++) + m_subGridPositions[i][j] = gridValue + subSegmentStep * i; + } + } + + // Ensure max value doesn't suffer from any rounding errors + m_gridPositions[segmentCount] = 1.0f; + m_labelPositions[segmentCount] = 1.0f; +} + +QString QValue3DAxisFormatterPrivate::stringForValue(float value, const QString &format) +{ + if (m_previousLabelFormat.compare(format)) { + // Format string different than the previous one used, reparse it + m_previousLabelFormat = format; + m_preparsedParamType = Utils::findFormatParamType(format); + m_labelFormatArray = format.toUtf8(); + } + + return Utils::formatLabel(m_labelFormatArray, m_preparsedParamType, value); +} + +float QValue3DAxisFormatterPrivate::positionAt(float value) const +{ + return (((value - m_min) / m_rangeNormalizer) - 1.0f); +} + +float QValue3DAxisFormatterPrivate::valueAt(float position) const +{ + return (((position + 1.0f) * m_rangeNormalizer) + m_min); +} + +void QValue3DAxisFormatterPrivate::setAxis(QValue3DAxis *axis) +{ + Q_ASSERT(axis); + + connect(axis, &QValue3DAxis::segmentCountChanged, + this, &QValue3DAxisFormatterPrivate::setNeedsRecalculate); + connect(axis, &QValue3DAxis::subSegmentCountChanged, + this, &QValue3DAxisFormatterPrivate::setNeedsRecalculate); + connect(axis, &QAbstract3DAxis::rangeChanged, + this, &QValue3DAxisFormatterPrivate::setNeedsRecalculate); + + m_axis = axis; +} + +void QValue3DAxisFormatterPrivate::setNeedsRecalculate() +{ + m_needsRecalculate = true; +} + +QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/axis/qvalue3daxisformatter.h b/src/datavisualization/axis/qvalue3daxisformatter.h new file mode 100644 index 00000000..a67b6094 --- /dev/null +++ b/src/datavisualization/axis/qvalue3daxisformatter.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef QVALUE3DAXISFORMATTER_H +#define QVALUE3DAXISFORMATTER_H + +#include <QtDataVisualization/qdatavisualizationglobal.h> +#include <QtCore/QObject> +#include <QtCore/QScopedPointer> + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +class QValue3DAxisFormatterPrivate; +class QValue3DAxis; + +class QT_DATAVISUALIZATION_EXPORT QValue3DAxisFormatter : public QObject +{ + Q_OBJECT + +protected: + explicit QValue3DAxisFormatter(QValue3DAxisFormatterPrivate *d, QObject *parent = 0); +public: + explicit QValue3DAxisFormatter(QObject *parent = 0); + virtual ~QValue3DAxisFormatter(); + + virtual bool allowNegatives() const; + virtual bool allowZero() const; + + // Getters not const as they can trigger recalculate + float gridPosition(int index); + float subGridPosition(int subGridIndex, int segmentIndex); + float labelPosition(int index); + QString stringForValue(float value, const QString &format); + float positionAt(float value); + float valueAt(float position); + + virtual QValue3DAxisFormatter *createNewInstance(); + void populateCopy(QValue3DAxisFormatter ©); + +protected: + void markDirty(); + QValue3DAxis *axis() const; + + virtual void doRecalculate(); + virtual QString doStringForValue(float value, const QString &format); + virtual float doPositionAt(float value); + virtual float doValueAt(float position); + virtual void doPopulateCopy(QValue3DAxisFormatter ©); + + QVector<float> &gridPositions(); + QVector<QVector<float> > &subGridPositions(); + QVector<float> &labelPositions(); + + QScopedPointer<QValue3DAxisFormatterPrivate> d_ptr; + +private: + Q_DISABLE_COPY(QValue3DAxisFormatter) + + friend class Abstract3DController; + friend class QValue3DAxisFormatterPrivate; + friend class QValue3DAxis; +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif diff --git a/src/datavisualization/axis/qvalue3daxisformatter_p.h b/src/datavisualization/axis/qvalue3daxisformatter_p.h new file mode 100644 index 00000000..90b0f49e --- /dev/null +++ b/src/datavisualization/axis/qvalue3daxisformatter_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include "datavisualizationglobal_p.h" +#include "qvalue3daxisformatter.h" +#include "utils_p.h" +#include <QtCore/QVector> + +#ifndef QVALUE3DAXISFORMATTER_P_H +#define QVALUE3DAXISFORMATTER_P_H + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +class QValue3DAxis; + +class QValue3DAxisFormatterPrivate : public QObject +{ + Q_OBJECT + +public: + QValue3DAxisFormatterPrivate(QValue3DAxisFormatter *q); + virtual ~QValue3DAxisFormatterPrivate(); + + void recalculate(); + void doRecalculate(); + + QString stringForValue(float value, const QString &format); + float positionAt(float value) const; + float valueAt(float position) const; + + void setAxis(QValue3DAxis *axis); + +public slots: + void setNeedsRecalculate(); + +protected: + QValue3DAxisFormatter *q_ptr; + + bool m_needsRecalculate; + + float m_min; + float m_max; + float m_rangeNormalizer; + + QVector<float> m_gridPositions; + QVector<QVector<float> > m_subGridPositions; + QVector<float> m_labelPositions; + + QValue3DAxis *m_axis; + + QString m_previousLabelFormat; + QByteArray m_labelFormatArray; + Utils::ParamType m_preparsedParamType; + + friend class QValue3DAxisFormatter; +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 63d66bc4..2a8564a0 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -326,6 +326,31 @@ void Abstract3DController::synchDataToRenderer() } } + if (m_changeTracker.axisXFormatterChanged) { + m_changeTracker.axisXFormatterChanged = false; + if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { + QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); + m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationX, + valueAxisX->formatter()); + } + } + if (m_changeTracker.axisYFormatterChanged) { + m_changeTracker.axisYFormatterChanged = false; + if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) { + QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); + m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationY, + valueAxisY->formatter()); + } + } + if (m_changeTracker.axisZFormatterChanged) { + m_changeTracker.axisZFormatterChanged = false; + if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) { + QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ); + m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationZ, + valueAxisZ->formatter()); + } + } + if (m_isSeriesVisibilityDirty || m_isSeriesVisualsDirty) { m_renderer->updateSeries(m_seriesList, m_isSeriesVisibilityDirty); m_isSeriesVisibilityDirty = false; @@ -888,6 +913,11 @@ void Abstract3DController::handleAxisLabelFormatChanged(const QString &format) handleAxisLabelFormatChangedBySender(sender()); } +void Abstract3DController::handleAxisFormatterDirty() +{ + handleAxisFormatterDirtyBySender(sender()); +} + void Abstract3DController::handleInputViewChanged(QAbstract3DInputHandler::InputView view) { // When in automatic slicing mode, input view change to primary disables slice mode @@ -896,15 +926,12 @@ void Abstract3DController::handleInputViewChanged(QAbstract3DInputHandler::Input setSlicingActive(false); } - m_changeTracker.inputViewChanged = true; emitNeedRender(); } void Abstract3DController::handleInputPositionChanged(const QPoint &position) { Q_UNUSED(position) - - m_changeTracker.inputPositionChanged = true; emitNeedRender(); } @@ -938,6 +965,23 @@ void Abstract3DController::handleAxisLabelFormatChangedBySender(QObject *sender) emitNeedRender(); } +void Abstract3DController::handleAxisFormatterDirtyBySender(QObject *sender) +{ + if (sender == m_axisX) { + m_isDataDirty = true; + m_changeTracker.axisXFormatterChanged = true; + } else if (sender == m_axisY) { + m_isDataDirty = true; + m_changeTracker.axisYFormatterChanged = true; + } else if (sender == m_axisZ) { + m_isDataDirty = true; + m_changeTracker.axisZFormatterChanged = true; + } else { + qWarning() << __FUNCTION__ << "invoked for invalid axis"; + } + emitNeedRender(); +} + void Abstract3DController::handleSeriesVisibilityChangedBySender(QObject *sender) { Q_UNUSED(sender) diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index c0a2fac2..a8cb040a 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -73,8 +73,9 @@ struct Abstract3DChangeBitField { bool axisXLabelFormatChanged : 1; bool axisYLabelFormatChanged : 1; bool axisZLabelFormatChanged : 1; - bool inputViewChanged : 1; - bool inputPositionChanged : 1; + bool axisXFormatterChanged : 1; + bool axisYFormatterChanged : 1; + bool axisZFormatterChanged : 1; Abstract3DChangeBitField() : zoomLevelChanged(true), @@ -102,7 +103,10 @@ struct Abstract3DChangeBitField { axisZSubSegmentCountChanged(true), axisXLabelFormatChanged(true), axisYLabelFormatChanged(true), - axisZLabelFormatChanged(true) + axisZLabelFormatChanged(true), + axisXFormatterChanged(true), + axisYFormatterChanged(true), + axisZFormatterChanged(true) { } }; @@ -233,6 +237,7 @@ public: virtual void handleAxisAutoAdjustRangeChangedInOrientation( QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust) = 0; virtual void handleAxisLabelFormatChangedBySender(QObject *sender); + virtual void handleAxisFormatterDirtyBySender(QObject *sender); virtual void handleSeriesVisibilityChangedBySender(QObject *sender); virtual void handlePendingClick() = 0; @@ -244,6 +249,7 @@ public slots: void handleAxisSubSegmentCountChanged(int count); void handleAxisAutoAdjustRangeChanged(bool autoAdjust); void handleAxisLabelFormatChanged(const QString &format); + void handleAxisFormatterDirty(); void handleInputViewChanged(QAbstract3DInputHandler::InputView view); void handleInputPositionChanged(const QPoint &position); void handleSeriesVisibilityChanged(bool visible); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 3122cf76..48453a82 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -26,6 +26,7 @@ #include "qabstract3dseries_p.h" #include "q3dtheme_p.h" #include "objecthelper_p.h" +#include "qvalue3daxisformatter_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -332,6 +333,18 @@ void Abstract3DRenderer::updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation axisCacheForOrientation(orientation).setLabelFormat(format); } +void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation, + QValue3DAxisFormatter *formatter) +{ + AxisRenderCache &cache = axisCacheForOrientation(orientation); + if (cache.ctrlFormatter() != formatter) { + delete cache.formatter(); + cache.setFormatter(formatter->createNewInstance()); + cache.setCtrlFormatter(formatter); + } + formatter->populateCopy(*(cache.formatter())); +} + void Abstract3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh) { // Default implementation does nothing. diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 4eb8426f..b198f004 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -84,14 +84,23 @@ public: virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality) = 0; virtual void initShaders(const QString &vertexShader, const QString &fragmentShader) = 0; virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader); - virtual void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) = 0; - virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); - virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, const QString &title); - virtual void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, const QStringList &labels); - virtual void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min, float max); + virtual void initBackgroundShaders(const QString &vertexShader, + const QString &fragmentShader) = 0; + virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, + QAbstract3DAxis::AxisType type); + virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, + const QString &title); + virtual void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, + const QStringList &labels); + virtual void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min, + float max); virtual void updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation, int count); - virtual void updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation, int count); - virtual void updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation, const QString &format); + virtual void updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation, + int count); + virtual void updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation, + const QString &format); + virtual void updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation, + QValue3DAxisFormatter *formatter); virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh); void generateBaseColorTexture(const QColor &color, GLuint *texture); diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp index a107dd23..bc0ab703 100644 --- a/src/datavisualization/engine/axisrendercache.cpp +++ b/src/datavisualization/engine/axisrendercache.cpp @@ -30,6 +30,8 @@ AxisRenderCache::AxisRenderCache() m_segmentCount(5), m_subSegmentCount(1), m_font(QFont(QStringLiteral("Arial"))), + m_formatter(0), + m_ctrlFormatter(0), m_drawer(0), m_segmentStep(10.0f), m_subSegmentStep(10.0f) @@ -40,6 +42,8 @@ AxisRenderCache::~AxisRenderCache() { foreach (LabelItem *label, m_labelItems) delete label; + + delete m_formatter; } void AxisRenderCache::setDrawer(Drawer *drawer) diff --git a/src/datavisualization/engine/axisrendercache_p.h b/src/datavisualization/engine/axisrendercache_p.h index e1c51e7c..5b48fbdf 100644 --- a/src/datavisualization/engine/axisrendercache_p.h +++ b/src/datavisualization/engine/axisrendercache_p.h @@ -48,19 +48,23 @@ public: void setType(QAbstract3DAxis::AxisType type); inline QAbstract3DAxis::AxisType type() const { return m_type; } void setTitle(const QString &title); - inline const QString &title() { return m_title; } + inline const QString &title() const { return m_title; } void setLabels(const QStringList &labels); - inline const QStringList &labels() { return m_labels; } + inline const QStringList &labels() const { return m_labels; } void setMin(float min); - inline float min() { return m_min; } + inline float min() const { return m_min; } void setMax(float max); - inline float max() { return m_max; } + inline float max() const { return m_max; } void setSegmentCount(int count); inline int segmentCount() const { return m_segmentCount; } void setSubSegmentCount(int count); inline int subSegmentCount() const { return m_subSegmentCount; } inline void setLabelFormat(const QString &format) { m_labelFormat = format; } - inline const QString &labelFormat() { return m_labelFormat; } + inline const QString &labelFormat() const { return m_labelFormat; } + inline void setFormatter(QValue3DAxisFormatter *formatter) { m_formatter = formatter; } + inline QValue3DAxisFormatter *formatter() const { return m_formatter; } + inline void setCtrlFormatter(const QValue3DAxisFormatter *formatter) { m_ctrlFormatter = formatter; } + inline const QValue3DAxisFormatter *ctrlFormatter() const { return m_ctrlFormatter; } inline LabelItem &titleItem() { return m_titleItem; } inline QList<LabelItem *> &labelItems() { return m_labelItems; } @@ -85,6 +89,8 @@ private: int m_subSegmentCount; QString m_labelFormat; QFont m_font; + QValue3DAxisFormatter *m_formatter; + const QValue3DAxisFormatter *m_ctrlFormatter; // Renderer items Drawer *m_drawer; // Not owned diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp index 91240259..c6c1a9f1 100644 --- a/src/datavisualization/engine/bars3dcontroller.cpp +++ b/src/datavisualization/engine/bars3dcontroller.cpp @@ -28,7 +28,6 @@ #include "q3dtheme_p.h" #include <QtGui/QMatrix4x4> -#include <QtCore/qmath.h> QT_BEGIN_NAMESPACE_DATAVISUALIZATION |