diff options
Diffstat (limited to 'src/datavisualization/axis')
-rw-r--r-- | src/datavisualization/axis/axis.pri | 10 | ||||
-rw-r--r-- | src/datavisualization/axis/qabstract3daxis.cpp | 201 | ||||
-rw-r--r-- | src/datavisualization/axis/qabstract3daxis.h | 15 | ||||
-rw-r--r-- | src/datavisualization/axis/qabstract3daxis_p.h | 17 | ||||
-rw-r--r-- | src/datavisualization/axis/qcategory3daxis.cpp | 21 | ||||
-rw-r--r-- | src/datavisualization/axis/qcategory3daxis_p.h | 12 | ||||
-rw-r--r-- | src/datavisualization/axis/qlogvalue3daxisformatter.cpp | 432 | ||||
-rw-r--r-- | src/datavisualization/axis/qlogvalue3daxisformatter.h | 72 | ||||
-rw-r--r-- | src/datavisualization/axis/qlogvalue3daxisformatter_p.h | 71 | ||||
-rw-r--r-- | src/datavisualization/axis/qvalue3daxis.cpp | 112 | ||||
-rw-r--r-- | src/datavisualization/axis/qvalue3daxis.h | 13 | ||||
-rw-r--r-- | src/datavisualization/axis/qvalue3daxis_p.h | 21 | ||||
-rw-r--r-- | src/datavisualization/axis/qvalue3daxisformatter.cpp | 419 | ||||
-rw-r--r-- | src/datavisualization/axis/qvalue3daxisformatter.h | 86 | ||||
-rw-r--r-- | src/datavisualization/axis/qvalue3daxisformatter_p.h | 91 |
15 files changed, 1514 insertions, 79 deletions
diff --git a/src/datavisualization/axis/axis.pri b/src/datavisualization/axis/axis.pri index 2c8bf70e..0173b597 100644 --- a/src/datavisualization/axis/axis.pri +++ b/src/datavisualization/axis/axis.pri @@ -4,9 +4,15 @@ 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 \ + $$PWD/qlogvalue3daxisformatter.h \ + $$PWD/qlogvalue3daxisformatter_p.h SOURCES += \ $$PWD/qabstract3daxis.cpp \ $$PWD/qvalue3daxis.cpp \ - $$PWD/qcategory3daxis.cpp + $$PWD/qcategory3daxis.cpp \ + $$PWD/qvalue3daxisformatter.cpp \ + $$PWD/qlogvalue3daxisformatter.cpp diff --git a/src/datavisualization/axis/qabstract3daxis.cpp b/src/datavisualization/axis/qabstract3daxis.cpp index 3a327caa..9fd5a832 100644 --- a/src/datavisualization/axis/qabstract3daxis.cpp +++ b/src/datavisualization/axis/qabstract3daxis.cpp @@ -16,7 +16,6 @@ ** ****************************************************************************/ -#include "qabstract3daxis.h" #include "qabstract3daxis_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -25,7 +24,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \class QAbstract3DAxis * \inmodule QtDataVisualization * \brief QAbstract3DAxis is base class for axes of a graph. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * You should not need to use this class directly, but one of its subclasses instead. * @@ -42,12 +41,15 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * * This type is uncreatable, but contains properties that are exposed via subtypes. * - * For AbstractAxis3D enums, see \l QAbstract3DAxis::AxisOrientation and \l QAbstract3DAxis::AxisType + * For AbstractAxis3D enums, see \l QAbstract3DAxis::AxisOrientation and + * \l{QAbstract3DAxis::AxisType}. */ /*! * \qmlproperty string AbstractAxis3D::title * Defines the title for the axis. + * + * \sa titleVisible, titleFixed */ /*! @@ -88,6 +90,36 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * If set, the axis will automatically adjust the range so that all data fits in it. */ +/*! + * \qmlproperty real AbstractAxis3D::labelAutoRotation + * + * Defines the maximum \a angle the labels can autorotate when the camera angle changes. + * The \a angle can be between 0 and 90, inclusive. The default value is 0. + * If the value is 0, axis labels do not automatically rotate. + * If the value is greater than zero, labels attempt to orient themselves toward the camera, up to + * the specified angle. + */ + +/*! + * \qmlproperty bool AbstractAxis3D::titleVisible + * + * Defines if the axis title is visible in the primary graph view. The default value is \c{false}. + * + * \sa title, titleFixed + */ + +/*! + * \qmlproperty bool AbstractAxis3D::titleFixed + * + * If \c{true}, axis titles in the primary graph view will be rotated towards the camera similarly + * to the axis labels. + * If \c{false}, axis titles are only rotated around their axis but are not otherwise oriented + * towards the camera. + * This property doesn't have any effect if labelAutoRotation property value is zero. + * Default value is \c{true}. + * + * \sa labelAutoRotation, title, titleVisible + */ /*! * \enum QAbstract3DAxis::AxisOrientation @@ -150,6 +182,8 @@ QAbstract3DAxis::AxisType QAbstract3DAxis::type() const * \property QAbstract3DAxis::title * * Defines the title for the axis. + * + * \sa titleVisible, titleFixed */ void QAbstract3DAxis::setTitle(const QString &title) { @@ -193,6 +227,77 @@ void QAbstract3DAxis::setRange(float min, float max) } /*! + * \property QAbstract3DAxis::labelAutoRotation + * + * Defines the maximum \a angle the labels can autorotate when the camera angle changes. + * The \a angle can be between 0 and 90, inclusive. The default value is 0. + * If the value is 0, axis labels do not automatically rotate. + * If the value is greater than zero, labels attempt to orient themselves toward the camera, up to + * the specified angle. + */ +void QAbstract3DAxis::setLabelAutoRotation(float angle) +{ + if (angle < 0.0f) + angle = 0.0f; + if (angle > 90.0f) + angle = 90.0f; + if (d_ptr->m_labelAutoRotation != angle) { + d_ptr->m_labelAutoRotation = angle; + emit labelAutoRotationChanged(angle); + } +} + +float QAbstract3DAxis::labelAutoRotation() const +{ + return d_ptr->m_labelAutoRotation; +} + +/*! + * \property QAbstract3DAxis::titleVisible + * + * Defines if the axis title is visible in the primary graph view. The default value is \c{false}. + * + * \sa title, titleFixed + */ +void QAbstract3DAxis::setTitleVisible(bool visible) +{ + if (d_ptr->m_titleVisible != visible) { + d_ptr->m_titleVisible = visible; + emit titleVisibilityChanged(visible); + } +} + +bool QAbstract3DAxis::isTitleVisible() const +{ + return d_ptr->m_titleVisible; +} + +/*! + * \property QAbstract3DAxis::titleFixed + * + * If \c{true}, axis titles in the primary graph view will be rotated towards the camera similarly + * to the axis labels. + * If \c{false}, axis titles are only rotated around their axis but are not otherwise oriented + * towards the camera. + * This property doesn't have any effect if labelAutoRotation property value is zero. + * Default value is \c{true}. + * + * \sa labelAutoRotation, title, titleVisible + */ +void QAbstract3DAxis::setTitleFixed(bool fixed) +{ + if (d_ptr->m_titleFixed != fixed) { + d_ptr->m_titleFixed = fixed; + emit titleFixedChanged(fixed); + } +} + +bool QAbstract3DAxis::isTitleFixed() const +{ + return d_ptr->m_titleFixed; +} + +/*! * \property QAbstract3DAxis::min * * Defines the minimum value on the axis. @@ -266,8 +371,9 @@ QAbstract3DAxisPrivate::QAbstract3DAxisPrivate(QAbstract3DAxis *q, QAbstract3DAx m_min(0.0f), m_max(10.0f), m_autoAdjust(true), - m_onlyPositiveValues(false), - m_allowMinMaxSame(false) + m_labelAutoRotation(0.0f), + m_titleVisible(false), + m_titleFixed(true) { } @@ -290,17 +396,28 @@ void QAbstract3DAxisPrivate::updateLabels() // Default implementation does nothing } -void QAbstract3DAxisPrivate::setRange(float min, float max) +void QAbstract3DAxisPrivate::setRange(float min, float max, bool suppressWarnings) { 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 +429,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 { @@ -323,7 +440,7 @@ void QAbstract3DAxisPrivate::setRange(float min, float max) } if (minDirty || maxDirty) { - if (adjusted) { + if (adjusted && !suppressWarnings) { qWarning() << "Warning: Tried to set invalid range for axis." " Range automatically adjusted to a valid one:" << min << "-" << max << "-->" << m_min << "-" << m_max; @@ -339,17 +456,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 +493,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.h b/src/datavisualization/axis/qabstract3daxis.h index 2b2be229..a9f75550 100644 --- a/src/datavisualization/axis/qabstract3daxis.h +++ b/src/datavisualization/axis/qabstract3daxis.h @@ -40,6 +40,9 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DAxis : public QObject Q_PROPERTY(float min READ min WRITE setMin NOTIFY minChanged) Q_PROPERTY(float max READ max WRITE setMax NOTIFY maxChanged) Q_PROPERTY(bool autoAdjustRange READ isAutoAdjustRange WRITE setAutoAdjustRange NOTIFY autoAdjustRangeChanged) + Q_PROPERTY(float labelAutoRotation READ labelAutoRotation WRITE setLabelAutoRotation NOTIFY labelAutoRotationChanged REVISION 1) + Q_PROPERTY(bool titleVisible READ isTitleVisible WRITE setTitleVisible NOTIFY titleVisibilityChanged REVISION 1) + Q_PROPERTY(bool titleFixed READ isTitleFixed WRITE setTitleFixed NOTIFY titleFixedChanged REVISION 1) public: enum AxisOrientation { @@ -81,6 +84,15 @@ public: void setRange(float min, float max); + void setLabelAutoRotation(float angle); + float labelAutoRotation() const; + + void setTitleVisible(bool visible); + bool isTitleVisible() const; + + void setTitleFixed(bool fixed); + bool isTitleFixed() const; + signals: void titleChanged(const QString &newTitle); void labelsChanged(); @@ -89,6 +101,9 @@ signals: void maxChanged(float value); void rangeChanged(float min, float max); void autoAdjustRangeChanged(bool autoAdjust); + Q_REVISION(1) void labelAutoRotationChanged(float angle); + Q_REVISION(1) void titleVisibilityChanged(bool visible); + Q_REVISION(1) void titleFixedChanged(bool fixed); protected: QScopedPointer<QAbstract3DAxisPrivate> d_ptr; diff --git a/src/datavisualization/axis/qabstract3daxis_p.h b/src/datavisualization/axis/qabstract3daxis_p.h index 4eb8de68..7181362f 100644 --- a/src/datavisualization/axis/qabstract3daxis_p.h +++ b/src/datavisualization/axis/qabstract3daxis_p.h @@ -26,13 +26,12 @@ // // We mean it. -#include "datavisualizationglobal_p.h" -#include "qabstract3daxis.h" -#include "abstract3dcontroller_p.h" - #ifndef QABSTRACT3DAXIS_P_H #define QABSTRACT3DAXIS_P_H +#include "datavisualizationglobal_p.h" +#include "qabstract3daxis.h" + QT_BEGIN_NAMESPACE_DATAVISUALIZATION class QAbstract3DAxisPrivate : public QObject @@ -47,12 +46,15 @@ public: inline bool isDefaultAxis() { return m_isDefaultAxis; } inline void setDefaultAxis(bool isDefault) { m_isDefaultAxis = isDefault; } - virtual void setRange(float min, float max); + virtual void setRange(float min, float max, bool suppressWarnings = false); virtual void setMin(float min); virtual void setMax (float max); protected: virtual void updateLabels(); + virtual bool allowZero() = 0; + virtual bool allowNegatives() = 0; + virtual bool allowMinMaxSame() = 0; QAbstract3DAxis *q_ptr; @@ -64,8 +66,9 @@ protected: float m_min; float m_max; bool m_autoAdjust; - bool m_onlyPositiveValues; - bool m_allowMinMaxSame; + float m_labelAutoRotation; + bool m_titleVisible; + bool m_titleFixed; friend class QAbstract3DAxis; friend class QValue3DAxis; diff --git a/src/datavisualization/axis/qcategory3daxis.cpp b/src/datavisualization/axis/qcategory3daxis.cpp index c11a65eb..f2cd0a84 100644 --- a/src/datavisualization/axis/qcategory3daxis.cpp +++ b/src/datavisualization/axis/qcategory3daxis.cpp @@ -16,10 +16,8 @@ ** ****************************************************************************/ -#include "qcategory3daxis.h" #include "qcategory3daxis_p.h" #include "bars3dcontroller_p.h" -#include "qbardataproxy.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -27,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \class QCategory3DAxis * \inmodule QtDataVisualization * \brief The QCategory3DAxis class is used for manipulating an axis of a graph. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * QCategory3DAxis provides an axis that can be given labels. The axis is divided into equal-sized * categories based on the data window size defined by setting the axis range. @@ -121,8 +119,6 @@ QCategory3DAxisPrivate::QCategory3DAxisPrivate(QCategory3DAxis *q) : QAbstract3DAxisPrivate(q, QAbstract3DAxis::AxisTypeCategory), m_labelsExplicitlySet(false) { - m_onlyPositiveValues = true; - m_allowMinMaxSame = true; } QCategory3DAxisPrivate::~QCategory3DAxisPrivate() @@ -142,6 +138,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..27126620 100644 --- a/src/datavisualization/axis/qcategory3daxis_p.h +++ b/src/datavisualization/axis/qcategory3daxis_p.h @@ -26,13 +26,12 @@ // // We mean it. -#include "qcategory3daxis.h" -#include "qabstract3daxis_p.h" -#include "qbardataitem.h" - #ifndef QCATEGORY3DAXIS_P_H #define QCATEGORY3DAXIS_P_H +#include "qcategory3daxis.h" +#include "qabstract3daxis_p.h" + QT_BEGIN_NAMESPACE_DATAVISUALIZATION class QCategory3DAxisPrivate : public QAbstract3DAxisPrivate @@ -45,6 +44,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/qlogvalue3daxisformatter.cpp b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp new file mode 100644 index 00000000..7367e7c5 --- /dev/null +++ b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp @@ -0,0 +1,432 @@ +/**************************************************************************** +** +** 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 "qlogvalue3daxisformatter_p.h" +#include "qvalue3daxis_p.h" +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +/*! + * \class QLogValue3DAxisFormatter + * \inmodule QtDataVisualization + * \brief QLogValue3DAxisFormatter implements logarithmic value axis formatter. + * \since QtDataVisualization 1.1 + * + * This class provides formatting rules for a logarithmic QValue3DAxis. + * + * When a QLogValue3DAxisFormatter is attached to a QValue3DAxis, the axis range + * cannot include negative values or the zero. + * + * \sa QValue3DAxisFormatter + */ + +/*! + * \qmltype LogValueAxis3DFormatter + * \inqmlmodule QtDataVisualization + * \since QtDataVisualization 1.1 + * \ingroup datavisualization_qml + * \instantiates QLogValue3DAxisFormatter + * \brief LogValueAxis3DFormatter implements logarithmic value axis formatter. + * + * This type provides formatting rules for a logarithmic ValueAxis3D. + * When a LogValueAxis3DFormatter is attached to a ValueAxis3D, the axis range + * cannot include negative values or the zero. + */ + +/*! + * \qmlproperty real LogValueAxis3DFormatter::base + * + * The \a base of the logarithm used to map axis values. If the base is non-zero, the parent axis + * segment count will be ignored when the grid line and label positions are calculated. + * If you want the range to be divided into equal segments like normal value axis, set this + * property value to zero. + * + * The \a base has to be zero or positive value and not equal to one. + * Defaults to ten. + * + * \sa ValueAxis3D::segmentCount + */ + +/*! + * \qmlproperty bool LogValueAxis3DFormatter::autoSubGrid + * + * If this property value is set to \c true, the parent axis sub-segment count is ignored + * when calculating sub-grid line positions. The sub-grid positions are generated automatically + * according to the base property value. + * The number of sub-grid lines is set to base value minus one, rounded down. + * This property is ignored when base property value is zero. + * Defaults to \c true. + * + * \sa base, ValueAxis3D::subSegmentCount + */ + +/*! + * \qmlproperty bool LogValueAxis3DFormatter::showEdgeLabels + * + * When the base property value is non-zero, the whole axis range is often not equally divided into + * segments. The first and last segments are often smaller than the other segments. + * In extreme cases this can lead to overlapping labels on the first and last two grid lines. + * By setting this property to \c false, you can suppress showing the minimum and maximum labels + * for the axis in cases where the segments do not exactly fit the axis. + * Defaults to \c true. + * + * \sa base, AbstractAxis3D::labels + */ + +/*! + * \internal + */ +QLogValue3DAxisFormatter::QLogValue3DAxisFormatter(QLogValue3DAxisFormatterPrivate *d, + QObject *parent) : + QValue3DAxisFormatter(d, parent) +{ + setAllowNegatives(false); + setAllowZero(false); +} + +/*! + * Constructs a new QLogValue3DAxisFormatter instance with optional \a parent. + */ +QLogValue3DAxisFormatter::QLogValue3DAxisFormatter(QObject *parent) : + QValue3DAxisFormatter(new QLogValue3DAxisFormatterPrivate(this), parent) +{ + setAllowNegatives(false); + setAllowZero(false); +} + +/*! + * Destroys QLogValue3DAxisFormatter. + */ +QLogValue3DAxisFormatter::~QLogValue3DAxisFormatter() +{ +} + +/*! + * \property QLogValue3DAxisFormatter::base + * + * The \a base of the logarithm used to map axis values. If the base is non-zero, the parent axis + * segment count will be ignored when the grid line and label positions are calculated. + * If you want the range to be divided into equal segments like normal value axis, set this + * property value to zero. + * + * The \a base has to be zero or positive value and not equal to one. + * Defaults to ten. + * + * \sa QValue3DAxis::segmentCount + */ +void QLogValue3DAxisFormatter::setBase(qreal base) +{ + if (base < 0.0f || base == 1.0f) { + qWarning() << "Warning: The logarithm base must be greater than 0 and not equal to 1," + << "attempted:" << base; + return; + } + if (dptr()->m_base != base) { + dptr()->m_base = base; + markDirty(true); + emit baseChanged(base); + } +} + +qreal QLogValue3DAxisFormatter::base() const +{ + return dptrc()->m_base; +} + +/*! + * \property QLogValue3DAxisFormatter::autoSubGrid + * + * If this property value is set to \c true, the parent axis sub-segment count is ignored + * when calculating sub-grid line positions. The sub-grid positions are generated automatically + * according to the base property value. + * The number of sub-grid lines is set to base value minus one, rounded down. + * This property is ignored when base property value is zero. + * Defaults to \c true. + * + * \sa base, QValue3DAxis::subSegmentCount + */ +void QLogValue3DAxisFormatter::setAutoSubGrid(bool enabled) +{ + if (dptr()->m_autoSubGrid != enabled) { + dptr()->m_autoSubGrid = enabled; + markDirty(false); + emit autoSubGridChanged(enabled); + } +} + +bool QLogValue3DAxisFormatter::autoSubGrid() const +{ + return dptrc()->m_autoSubGrid; +} + +/*! + * \property QLogValue3DAxisFormatter::showEdgeLabels + * + * When the base property value is non-zero, the whole axis range is often not equally divided into + * segments. The first and last segments are often smaller than the other segments. + * In extreme cases this can lead to overlapping labels on the first and last two grid lines. + * By setting this property to \c false, you can suppress showing the minimum and maximum labels + * for the axis in cases where the segments do not exactly fit the axis. + * Defaults to \c true. + * + * \sa base, QAbstract3DAxis::labels + */ +void QLogValue3DAxisFormatter::setShowEdgeLabels(bool enabled) +{ + if (dptr()->m_showEdgeLabels != enabled) { + dptr()->m_showEdgeLabels = enabled; + markDirty(true); + emit showEdgeLabelsChanged(enabled); + } +} + +bool QLogValue3DAxisFormatter::showEdgeLabels() const +{ + return dptrc()->m_showEdgeLabels; +} + +/*! + * \internal + */ +QValue3DAxisFormatter *QLogValue3DAxisFormatter::createNewInstance() const +{ + return new QLogValue3DAxisFormatter(); +} + +/*! + * \internal + */ +void QLogValue3DAxisFormatter::recalculate() +{ + dptr()->recalculate(); +} + +/*! + * \internal + */ +float QLogValue3DAxisFormatter::positionAt(float value) const +{ + return dptrc()->positionAt(value); +} + +/*! + * \internal + */ +float QLogValue3DAxisFormatter::valueAt(float position) const +{ + return dptrc()->valueAt(position); +} + +/*! + * \internal + */ +void QLogValue3DAxisFormatter::populateCopy(QValue3DAxisFormatter ©) const +{ + QValue3DAxisFormatter::populateCopy(copy); + dptrc()->populateCopy(copy); +} + +/*! + * \internal + */ +QLogValue3DAxisFormatterPrivate *QLogValue3DAxisFormatter::dptr() +{ + return static_cast<QLogValue3DAxisFormatterPrivate *>(d_ptr.data()); +} + +/*! + * \internal + */ +const QLogValue3DAxisFormatterPrivate *QLogValue3DAxisFormatter::dptrc() const +{ + return static_cast<const QLogValue3DAxisFormatterPrivate *>(d_ptr.data()); +} + +// QLogValue3DAxisFormatterPrivate +QLogValue3DAxisFormatterPrivate::QLogValue3DAxisFormatterPrivate(QLogValue3DAxisFormatter *q) + : QValue3DAxisFormatterPrivate(q), + m_base(10.0), + m_logMin(0.0), + m_logMax(0.0), + m_logRangeNormalizer(0.0), + m_autoSubGrid(true), + m_showEdgeLabels(true), + m_evenMinSegment(true), + m_evenMaxSegment(true) +{ +} + +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; + + int subGridCount = m_axis->subSegmentCount() - 1; + int segmentCount = m_axis->segmentCount(); + QString labelFormat = m_axis->labelFormat(); + qreal segmentStep; + if (m_base > 0.0) { + // Update parent axis segment counts + qreal logMin = qLn(qreal(m_min)) / qLn(m_base); + qreal logMax = qLn(qreal(m_max)) / qLn(m_base); + qreal logRangeNormalizer = logMax - logMin; + + qreal minDiff = qCeil(logMin) - logMin; + qreal maxDiff = logMax - qFloor(logMax); + + m_evenMinSegment = qFuzzyCompare(0.0, minDiff); + m_evenMaxSegment = qFuzzyCompare(0.0, maxDiff); + + segmentCount = qRound(logRangeNormalizer - minDiff - maxDiff); + + if (!m_evenMinSegment) + segmentCount++; + if (!m_evenMaxSegment) + segmentCount++; + + segmentStep = 1.0 / logRangeNormalizer; + + if (m_autoSubGrid) { + subGridCount = qCeil(m_base) - 2; // -2 for subgrid because subsegment count is base - 1 + if (subGridCount < 0) + subGridCount = 0; + } + + m_gridPositions.resize(segmentCount + 1); + m_subGridPositions.resize(segmentCount * subGridCount); + m_labelPositions.resize(segmentCount + 1); + m_labelStrings.clear(); + m_labelStrings.reserve(segmentCount + 1); + + // Calculate segment positions + int index = 0; + if (!m_evenMinSegment) { + m_gridPositions[0] = 0.0f; + m_labelPositions[0] = 0.0f; + if (m_showEdgeLabels) + m_labelStrings << qptr()->stringForValue(qreal(m_min), labelFormat); + else + m_labelStrings << QString(); + index++; + } + for (int i = 0; i < segmentCount; i++) { + float gridValue = float((minDiff + qreal(i)) / qreal(logRangeNormalizer)); + m_gridPositions[index] = gridValue; + m_labelPositions[index] = gridValue; + m_labelStrings << qptr()->stringForValue(qPow(m_base, minDiff + qreal(i) + logMin), + labelFormat); + index++; + } + // Ensure max value doesn't suffer from any rounding errors + m_gridPositions[segmentCount] = 1.0f; + m_labelPositions[segmentCount] = 1.0f; + QString finalLabel; + if (m_showEdgeLabels || m_evenMaxSegment) + finalLabel = qptr()->stringForValue(qreal(m_max), labelFormat); + + if (m_labelStrings.size() > segmentCount) + m_labelStrings.replace(segmentCount, finalLabel); + else + m_labelStrings << finalLabel; + } else { + // Grid lines and label positions are the same as the parent class, so call parent impl + // first to populate those + QValue3DAxisFormatterPrivate::doRecalculate(); + + // Label string list needs to be repopulated + segmentStep = 1.0 / qreal(segmentCount); + + m_labelStrings << qptr()->stringForValue(qreal(m_min), labelFormat); + for (int i = 1; i < m_labelPositions.size() - 1; i++) + m_labelStrings[i] = qptr()->stringForValue(qExp(segmentStep * qreal(i) + * m_logRangeNormalizer + m_logMin), + labelFormat); + m_labelStrings << qptr()->stringForValue(qreal(m_max), labelFormat); + + m_evenMaxSegment = true; + m_evenMinSegment = true; + } + + // Subgrid line positions are logarithmically spaced + if (subGridCount > 0) { + float oneSegmentRange = valueAt(float(segmentStep)) - m_min; + float subSegmentStep = oneSegmentRange / float(subGridCount + 1); + + // Since the logarithm has the same curvature across whole axis range, we can just calculate + // subgrid positions for the first segment and replicate them to other segments. + QVector<float> actualSubSegmentSteps(subGridCount); + + for (int i = 0; i < subGridCount; i++) { + float currentSubPosition = positionAt(m_min + ((i + 1) * subSegmentStep)); + actualSubSegmentSteps[i] = currentSubPosition; + } + + float firstPartialSegmentAdjustment = float(segmentStep) - m_gridPositions.at(1); + for (int i = 0; i < segmentCount; i++) { + for (int j = 0; j < subGridCount; j++) { + float position = m_gridPositions.at(i) + actualSubSegmentSteps.at(j); + if (!m_evenMinSegment && i == 0) + position -= firstPartialSegmentAdjustment; + if (position > 1.0f) + position = 1.0f; + if (position < 0.0f) + position = 0.0f; + m_subGridPositions[i * subGridCount + j] = position; + } + } + } +} + +void QLogValue3DAxisFormatterPrivate::populateCopy(QValue3DAxisFormatter ©) const +{ + QLogValue3DAxisFormatter *logFormatter = static_cast<QLogValue3DAxisFormatter *>(©); + QLogValue3DAxisFormatterPrivate *priv = logFormatter->dptr(); + + priv->m_base = m_base; + priv->m_logMin = m_logMin; + priv->m_logMax = m_logMax; + priv->m_logRangeNormalizer = m_logRangeNormalizer; +} + +float QLogValue3DAxisFormatterPrivate::positionAt(float value) const +{ + qreal logValue = qLn(qreal(value)); + float retval = float((logValue - m_logMin) / m_logRangeNormalizer); + + return retval; +} + +float QLogValue3DAxisFormatterPrivate::valueAt(float position) const +{ + qreal logValue = (qreal(position) * m_logRangeNormalizer) + m_logMin; + return float(qExp(logValue)); +} + +QLogValue3DAxisFormatter *QLogValue3DAxisFormatterPrivate::qptr() +{ + return static_cast<QLogValue3DAxisFormatter *>(q_ptr); +} + +QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/axis/qlogvalue3daxisformatter.h b/src/datavisualization/axis/qlogvalue3daxisformatter.h new file mode 100644 index 00000000..62714a7d --- /dev/null +++ b/src/datavisualization/axis/qlogvalue3daxisformatter.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** 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 QLOGVALUE3DAXISFORMATTER_H +#define QLOGVALUE3DAXISFORMATTER_H + +#include <QtDataVisualization/qvalue3daxisformatter.h> + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +class QLogValue3DAxisFormatterPrivate; + +class QT_DATAVISUALIZATION_EXPORT QLogValue3DAxisFormatter : public QValue3DAxisFormatter +{ + Q_OBJECT + + Q_PROPERTY(qreal base READ base WRITE setBase NOTIFY baseChanged) + Q_PROPERTY(bool autoSubGrid READ autoSubGrid WRITE setAutoSubGrid NOTIFY autoSubGridChanged) + Q_PROPERTY(bool showEdgeLabels READ showEdgeLabels WRITE setShowEdgeLabels NOTIFY showEdgeLabelsChanged) + +protected: + explicit QLogValue3DAxisFormatter(QLogValue3DAxisFormatterPrivate *d, QObject *parent = 0); +public: + explicit QLogValue3DAxisFormatter(QObject *parent = 0); + virtual ~QLogValue3DAxisFormatter(); + + void setBase(qreal base); + qreal base() const; + void setAutoSubGrid(bool enabled); + bool autoSubGrid() const; + void setShowEdgeLabels(bool enabled); + bool showEdgeLabels() const; + +signals: + void baseChanged(qreal base); + void autoSubGridChanged(bool enabled); + void showEdgeLabelsChanged(bool enabled); + +protected: + virtual QValue3DAxisFormatter *createNewInstance() const; + virtual void recalculate(); + virtual float positionAt(float value) const; + virtual float valueAt(float position) const; + virtual void populateCopy(QValue3DAxisFormatter ©) const; + + QLogValue3DAxisFormatterPrivate *dptr(); + const QLogValue3DAxisFormatterPrivate *dptrc() const; + +private: + Q_DISABLE_COPY(QLogValue3DAxisFormatter) + + friend class QLogValue3DAxisFormatterPrivate; +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif diff --git a/src/datavisualization/axis/qlogvalue3daxisformatter_p.h b/src/datavisualization/axis/qlogvalue3daxisformatter_p.h new file mode 100644 index 00000000..df2d41d1 --- /dev/null +++ b/src/datavisualization/axis/qlogvalue3daxisformatter_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** 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. + +#ifndef QLOGVALUE3DAXISFORMATTER_P_H +#define QLOGVALUE3DAXISFORMATTER_P_H + +#include "datavisualizationglobal_p.h" +#include "qlogvalue3daxisformatter.h" +#include "qvalue3daxisformatter_p.h" + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +class QLogValue3DAxisFormatterPrivate : public QValue3DAxisFormatterPrivate +{ + Q_OBJECT + +public: + QLogValue3DAxisFormatterPrivate(QLogValue3DAxisFormatter *q); + virtual ~QLogValue3DAxisFormatterPrivate(); + + void recalculate(); + void populateCopy(QValue3DAxisFormatter ©) const; + + float positionAt(float value) const; + float valueAt(float position) const; + +protected: + QLogValue3DAxisFormatter *qptr(); + + qreal m_base; + qreal m_logMin; + qreal m_logMax; + qreal m_logRangeNormalizer; + bool m_autoSubGrid; + bool m_showEdgeLabels; + +private: + bool m_evenMinSegment; + bool m_evenMaxSegment; + + friend class QLogValue3DAxisFormatter; +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif diff --git a/src/datavisualization/axis/qvalue3daxis.cpp b/src/datavisualization/axis/qvalue3daxis.cpp index 0d53291d..8207174f 100644 --- a/src/datavisualization/axis/qvalue3daxis.cpp +++ b/src/datavisualization/axis/qvalue3daxis.cpp @@ -16,9 +16,8 @@ ** ****************************************************************************/ -#include "qvalue3daxis.h" #include "qvalue3daxis_p.h" -#include "utils_p.h" +#include "qvalue3daxisformatter_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -26,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \class QValue3DAxis * \inmodule QtDataVisualization * \brief The QValue3DAxis class is used for manipulating an axis of a graph. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * QValue3DAxis provides an axis that can be given a range of values and segment and subsegment * counts to divide the range into. @@ -74,11 +73,30 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION */ /*! + * \qmlproperty ValueAxis3DFormatter ValueAxis3D::formatter + * \since QtDataVisualization 1.1 + * + * Defines the axis \a formatter to be used. Any existing formatter is deleted when a new formatter + * is set. + * + */ + +/*! + * \qmlproperty bool ValueAxis3D::reversed + * \since QtDataVisualization 1.1 + * + * If \c{true}, the axis will be rendered in reverse, i.e. the positions of minimum and maximum + * values are swapped when the graph is rendered. This property doesn't affect the actual + * minimum and maximum values of the axis. + */ + +/*! * Constructs QValue3DAxis with the given \a parent. */ QValue3DAxis::QValue3DAxis(QObject *parent) : QAbstract3DAxis(new QValue3DAxisPrivate(this), parent) { + setFormatter(new QValue3DAxisFormatter); } /*! @@ -88,7 +106,6 @@ QValue3DAxis::~QValue3DAxis() { } - /*! * \property QValue3DAxis::segmentCount * @@ -105,7 +122,7 @@ void QValue3DAxis::setSegmentCount(int count) << count << "-> 1"; count = 1; } - if (dptr()->m_segmentCount != count){ + if (dptr()->m_segmentCount != count) { dptr()->m_segmentCount = count; dptr()->emitLabelsChanged(); emit segmentCountChanged(count); @@ -169,6 +186,53 @@ QString QValue3DAxis::labelFormat() const } /*! + * \property QValue3DAxis::formatter + * \since QtDataVisualization 1.1 + * + * 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; +} + +/*! + * \property QValue3DAxis::reversed + * \since QtDataVisualization 1.1 + * + * If \c{true}, the axis will be rendered in reverse, i.e. the positions of minimum and maximum + * values are swapped when the graph is rendered. This property doesn't affect the actual + * minimum and maximum values of the axis. + */ +void QValue3DAxis::setReversed(bool enable) +{ + if (dptr()->m_reversed != enable) { + dptr()->m_reversed = enable; + emit reversedChanged(enable); + } +} + +bool QValue3DAxis::reversed() const +{ + return dptrc()->m_reversed; +} + +/*! * \internal */ QValue3DAxisPrivate *QValue3DAxis::dptr() @@ -189,7 +253,9 @@ QValue3DAxisPrivate::QValue3DAxisPrivate(QValue3DAxis *q) m_segmentCount(5), m_subSegmentCount(1), m_labelFormat(Utils::defaultLabelFormat()), - m_labelsDirty(true) + m_labelsDirty(true), + m_formatter(0), + m_reversed(false) { } @@ -197,11 +263,11 @@ QValue3DAxisPrivate::~QValue3DAxisPrivate() { } -void QValue3DAxisPrivate::setRange(float min, float max) +void QValue3DAxisPrivate::setRange(float min, float max, bool suppressWarnings) { bool dirty = (min != m_min || max != m_max); - QAbstract3DAxisPrivate::setRange(min, max); + QAbstract3DAxisPrivate::setRange(min, max, suppressWarnings); if (dirty) emitLabelsChanged(); @@ -240,26 +306,24 @@ void QValue3DAxisPrivate::updateLabels() m_labelsDirty = false; - 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; + m_formatter->d_ptr->recalculate(); - QString formatString(m_labelFormat); - Utils::ParamType paramType = Utils::findFormatParamType(formatString); - QByteArray formatArray = formatString.toUtf8(); + m_labels = m_formatter->labelStrings(); +} - for (int i = 0; i < m_segmentCount; i++) { - float value = m_min + (segmentStep * i); - newLabels.append(Utils::formatLabel(formatArray, paramType, value)); - } +bool QValue3DAxisPrivate::allowZero() +{ + return m_formatter->allowZero(); +} - // Ensure max label doesn't suffer from any rounding errors - newLabels.append(Utils::formatLabel(formatArray, paramType, m_max)); +bool QValue3DAxisPrivate::allowNegatives() +{ + return m_formatter->allowNegatives(); +} - if (m_labels != newLabels) - m_labels = newLabels; +bool QValue3DAxisPrivate::allowMinMaxSame() +{ + return false; } QValue3DAxis *QValue3DAxisPrivate::qptr() diff --git a/src/datavisualization/axis/qvalue3daxis.h b/src/datavisualization/axis/qvalue3daxis.h index f0af759b..f552269c 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,8 @@ 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) + Q_PROPERTY(bool reversed READ reversed WRITE setReversed NOTIFY reversedChanged REVISION 1) public: explicit QValue3DAxis(QObject *parent = 0); @@ -45,10 +48,18 @@ public: void setLabelFormat(const QString &format); QString labelFormat() const; + void setFormatter(QValue3DAxisFormatter *formatter); + QValue3DAxisFormatter *formatter() const; + + void setReversed(bool enable); + bool reversed() const; + signals: void segmentCountChanged(int count); void subSegmentCountChanged(int count); void labelFormatChanged(const QString &format); + Q_REVISION(1) void formatterChanged(QValue3DAxisFormatter *formatter); + Q_REVISION(1) void reversedChanged(bool enable); protected: QValue3DAxisPrivate *dptr(); @@ -56,9 +67,11 @@ protected: private: Q_DISABLE_COPY(QValue3DAxis) + friend class Abstract3DController; friend class Bars3DController; friend class Scatter3DController; friend class Surface3DController; + friend class QValue3DAxisFormatterPrivate; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/axis/qvalue3daxis_p.h b/src/datavisualization/axis/qvalue3daxis_p.h index 21fd78ab..b7394c0a 100644 --- a/src/datavisualization/axis/qvalue3daxis_p.h +++ b/src/datavisualization/axis/qvalue3daxis_p.h @@ -26,12 +26,12 @@ // // We mean it. -#include "qvalue3daxis.h" -#include "qabstract3daxis_p.h" - #ifndef QVALUE3DAXIS_P_H #define QVALUE3DAXIS_P_H +#include "qvalue3daxis.h" +#include "qabstract3daxis_p.h" + QT_BEGIN_NAMESPACE_DATAVISUALIZATION class QValue3DAxisPrivate : public QAbstract3DAxisPrivate @@ -42,23 +42,34 @@ public: QValue3DAxisPrivate(QValue3DAxis *q); virtual ~QValue3DAxisPrivate(); - virtual void setRange(float min, float max); + virtual void setRange(float min, float max, bool suppressWarnings = false); virtual void setMin(float min); virtual void setMax (float max); -protected: void emitLabelsChanged(); + +signals: + void formatterDirty(); + +protected: 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; + bool m_reversed; private: QValue3DAxis *qptr(); friend class QValue3DAxis; + friend class Abstract3DController; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/axis/qvalue3daxisformatter.cpp b/src/datavisualization/axis/qvalue3daxisformatter.cpp new file mode 100644 index 00000000..56ca3b0f --- /dev/null +++ b/src/datavisualization/axis/qvalue3daxisformatter.cpp @@ -0,0 +1,419 @@ +/**************************************************************************** +** +** 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 QtDataVisualization 1.1 + * + * This class provides formatting rules for a linear QValue3DAxis. Subclass it if you + * want to implement custom value axes. + * + * The base class has no public API beyond constructors and destructors. It is meant to be only + * used internally. However, subclasses may implement public properties as needed. + * + * \sa QLogValue3DAxisFormatter + */ + +/*! + * \qmltype ValueAxis3DFormatter + * \inqmlmodule QtDataVisualization + * \since QtDataVisualization 1.1 + * \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. + * This type has not public functionality. + */ + +/*! + * \internal + */ +QValue3DAxisFormatter::QValue3DAxisFormatter(QValue3DAxisFormatterPrivate *d, QObject *parent) : + QObject(parent), + d_ptr(d) +{ +} + +/*! + * Constructs a new QValue3DAxisFormatter instance with an optional \a parent. + */ +QValue3DAxisFormatter::QValue3DAxisFormatter(QObject *parent) : + QObject(parent), + d_ptr(new QValue3DAxisFormatterPrivate(this)) +{ +} + +/*! + * Destroys QValue3DAxisFormatter. + */ +QValue3DAxisFormatter::~QValue3DAxisFormatter() +{ +} + +/*! + * Allow the parent axis to have negative values if \a allow is true. + */ +void QValue3DAxisFormatter::setAllowNegatives(bool allow) +{ + d_ptr->m_allowNegatives = allow; +} + +/*! + * \return \c true if negative values are valid values for parent axis. + * The default implementation always returns true. + */ +bool QValue3DAxisFormatter::allowNegatives() const +{ + return d_ptr->m_allowNegatives; +} + +/*! + * Allow the parent axis to have zero value if \a allow is true. + */ +void QValue3DAxisFormatter::setAllowZero(bool allow) +{ + d_ptr->m_allowZero = allow; +} + +/*! + * \return \c true if zero is a valid value for parent axis. + * The default implementation always returns true. + */ +bool QValue3DAxisFormatter::allowZero() const +{ + return d_ptr->m_allowZero; +} + +/*! + * 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() const +{ + return new QValue3DAxisFormatter(); +} + +/*! + * This method resizes and populates the label and grid line position arrays and the label strings + * 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 if the default array contents are not suitable. + * + * See gridPositions(), subGridPositions(), labelPositions(), and labelStrings() methods for + * documentation about the arrays that need to be resized and populated. + * + * \sa gridPositions(), subGridPositions(), labelPositions(), labelStrings(), axis() + */ +void QValue3DAxisFormatter::recalculate() +{ + d_ptr->doRecalculate(); +} + +/*! + * This method is used to format a string using the specified value and the specified format. + * Reimplement this method in a subclass to resolve the formatted string for a given \a value + * if the default formatting rules specified for QValue3DAxis::labelFormat property are not + * sufficient. + * + * \return the formatted label string using a \a value and a \a format. + * + * \sa recalculate(), labelStrings(), QValue3DAxis::labelFormat + */ +QString QValue3DAxisFormatter::stringForValue(qreal value, const QString &format) const +{ + 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 0.0 (for minimum value) and 1.0 (for maximum value), + * inclusive, if the value is within the parent axis range. + * + * \sa recalculate(), valueAt() + */ +float QValue3DAxisFormatter::positionAt(float value) const +{ + 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 0.0 (for minimum value) and 1.0 (for maximum value), + * inclusive to obtain values within the parent axis range. + * + * \sa recalculate(), positionAt() + */ +float QValue3DAxisFormatter::valueAt(float position) const +{ + return d_ptr->valueAt(position); +} + +/*! + * Copies all necessary values for resolving positions, values, and strings with this formatter + * 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::populateCopy(QValue3DAxisFormatter ©) const +{ + d_ptr->doPopulateCopy(*(copy.d_ptr.data())); +} + +/*! + * Marks this formatter dirty, prompting the renderer to make a new copy of its cache on the next + * renderer synchronization. This method should be called by a subclass whenever the formatter + * is changed in a way that affects the resolved values. Specify \c true for \a labelsChange + * parameter if the change was such that it requires regenerating the parent axis label strings. + */ +void QValue3DAxisFormatter::markDirty(bool labelsChange) +{ + d_ptr->markDirty(labelsChange); +} + +/*! + * \return the parent axis. The parent axis must only be accessed in recalculate() + * method to maintain thread safety in environments using a threaded renderer. + * + * \sa recalculate() + */ +QValue3DAxis *QValue3DAxisFormatter::axis() const +{ + return d_ptr->m_axis; +} + +/*! + * \return a reference to the array of normalized grid line positions. + * The default array size is equal to the segment count of the parent axis plus one, but + * a subclassed implementation of recalculate method may resize the array differently. + * The values should be between 0.0 (for minimum value) and 1.0 (for maximum value), inclusive. + * + * \sa QValue3DAxis::segmentCount, recalculate() + */ +QVector<float> &QValue3DAxisFormatter::gridPositions() const +{ + return d_ptr->m_gridPositions; +} + +/*! + * \return a reference to the array of normalized subgrid line positions. + * The default array size is equal to segment count of the parent axis times sub-segment count + * of the parent axis minus one, but a subclassed implementation of recalculate method may resize + * the array differently. + * The values should be between 0.0 (for minimum value) and 1.0 (for maximum value), inclusive. + * + * \sa QValue3DAxis::segmentCount, QValue3DAxis::subSegmentCount, recalculate() + */ +QVector<float> &QValue3DAxisFormatter::subGridPositions() const +{ + return d_ptr->m_subGridPositions; +} + +/*! + * \return a reference to the array of normalized label positions. + * The default array size is equal to the segment count of the parent axis plus one, but + * a subclassed implementation of recalculate method may resize the array differently. + * The values should be between 0.0 (for minimum value) and 1.0 (for maximum value), inclusive. + * The default behavior is that the label at the index zero corresponds to the minimum value + * of the axis. + * + * \sa QValue3DAxis::segmentCount, QAbstract3DAxis::labels, recalculate() + */ +QVector<float> &QValue3DAxisFormatter::labelPositions() const +{ + return d_ptr->m_labelPositions; +} + +/*! + * \return a reference to the string list containing formatter label strings. + * The array size must be equal to the size of the label positions array and + * the indexes correspond to that array as well. + * + * \sa labelPositions() + */ +QStringList &QValue3DAxisFormatter::labelStrings() const +{ + return d_ptr->m_labelStrings; +} + +// 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), + m_allowNegatives(true), + m_allowZero(true) +{ +} + +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) { + m_min = m_axis->min(); + m_max = m_axis->max(); + m_rangeNormalizer = (m_max - m_min); + + q_ptr->recalculate(); + m_needsRecalculate = false; + } +} + +void QValue3DAxisFormatterPrivate::doRecalculate() +{ + int segmentCount = m_axis->segmentCount(); + int subGridCount = m_axis->subSegmentCount() - 1; + QString labelFormat = m_axis->labelFormat(); + + m_gridPositions.resize(segmentCount + 1); + m_subGridPositions.resize(segmentCount * subGridCount); + + m_labelPositions.resize(segmentCount + 1); + m_labelStrings.clear(); + m_labelStrings.reserve(segmentCount + 1); + + // 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 / qreal(subGridCount + 1); + + // Calculate positions + qreal rangeNormalizer = qreal(m_max - m_min); + for (int i = 0; i < segmentCount; i++) { + qreal gridValue = segmentStep * qreal(i); + m_gridPositions[i] = float(gridValue); + m_labelPositions[i] = float(gridValue); + m_labelStrings << q_ptr->stringForValue(gridValue * rangeNormalizer + qreal(m_min), + labelFormat); + if (m_subGridPositions.size()) { + for (int j = 0; j < subGridCount; j++) + m_subGridPositions[i * subGridCount + j] = gridValue + subSegmentStep * (j + 1); + } + } + + // Ensure max value doesn't suffer from any rounding errors + m_gridPositions[segmentCount] = 1.0f; + m_labelPositions[segmentCount] = 1.0f; + m_labelStrings << q_ptr->stringForValue(qreal(m_max), labelFormat); +} + +void QValue3DAxisFormatterPrivate::populateCopy(QValue3DAxisFormatter ©) +{ + recalculate(); + q_ptr->populateCopy(copy); +} + +void QValue3DAxisFormatterPrivate::doPopulateCopy(QValue3DAxisFormatterPrivate ©) +{ + copy.m_min = m_min; + copy.m_max = m_max; + copy.m_rangeNormalizer = m_rangeNormalizer; + + copy.m_gridPositions = m_gridPositions; + copy.m_labelPositions = m_labelPositions; + copy.m_subGridPositions = m_subGridPositions; +} + +QString QValue3DAxisFormatterPrivate::stringForValue(qreal 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); +} + +float QValue3DAxisFormatterPrivate::valueAt(float position) const +{ + return ((position * m_rangeNormalizer) + m_min); +} + +void QValue3DAxisFormatterPrivate::setAxis(QValue3DAxis *axis) +{ + Q_ASSERT(axis); + + // These signals are all connected to markDirtyNoLabelChange slot, even though most of them + // do require labels to be regenerated. This is because the label regeneration is triggered + // elsewhere in these cases. + connect(axis, &QValue3DAxis::segmentCountChanged, + this, &QValue3DAxisFormatterPrivate::markDirtyNoLabelChange); + connect(axis, &QValue3DAxis::subSegmentCountChanged, + this, &QValue3DAxisFormatterPrivate::markDirtyNoLabelChange); + connect(axis, &QValue3DAxis::labelFormatChanged, + this, &QValue3DAxisFormatterPrivate::markDirtyNoLabelChange); + connect(axis, &QAbstract3DAxis::rangeChanged, + this, &QValue3DAxisFormatterPrivate::markDirtyNoLabelChange); + + m_axis = axis; +} + +void QValue3DAxisFormatterPrivate::markDirty(bool labelsChange) +{ + m_needsRecalculate = true; + if (m_axis) { + if (labelsChange) + m_axis->dptr()->emitLabelsChanged(); + if (m_axis && m_axis->orientation() != QAbstract3DAxis::AxisOrientationNone) + emit m_axis->dptr()->formatterDirty(); + } +} + +void QValue3DAxisFormatterPrivate::markDirtyNoLabelChange() +{ + markDirty(false); +} + +QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/axis/qvalue3daxisformatter.h b/src/datavisualization/axis/qvalue3daxisformatter.h new file mode 100644 index 00000000..c7b0bac5 --- /dev/null +++ b/src/datavisualization/axis/qvalue3daxisformatter.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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> +#include <QtCore/QVector> +#include <QtCore/QStringList> + +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(); + +protected: + void setAllowNegatives(bool allow); + bool allowNegatives() const; + void setAllowZero(bool allow); + bool allowZero() const; + + virtual QValue3DAxisFormatter *createNewInstance() const; + virtual void recalculate(); + 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 markDirty(bool labelsChange = false); + QValue3DAxis *axis() const; + + QVector<float> &gridPositions() const; + QVector<float> &subGridPositions() const; + QVector<float> &labelPositions() const; + QStringList &labelStrings() const; + + QScopedPointer<QValue3DAxisFormatterPrivate> d_ptr; + +private: + Q_DISABLE_COPY(QValue3DAxisFormatter) + + friend class Abstract3DController; + friend class Abstract3DRenderer; + friend class Bars3DRenderer; + friend class Scatter3DRenderer; + friend class Surface3DRenderer; + friend class SurfaceObject; + friend class QValue3DAxisFormatterPrivate; + friend class QLogValue3DAxisFormatter; + friend class QValue3DAxis; + friend class QValue3DAxisPrivate; + friend class AxisRenderCache; + friend class QBar3DSeriesPrivate; + friend class QScatter3DSeriesPrivate; + friend class QSurface3DSeriesPrivate; +}; + +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..2d1dc920 --- /dev/null +++ b/src/datavisualization/axis/qvalue3daxisformatter_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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. + +#ifndef QVALUE3DAXISFORMATTER_P_H +#define QVALUE3DAXISFORMATTER_P_H + +#include "datavisualizationglobal_p.h" +#include "qvalue3daxisformatter.h" +#include "utils_p.h" + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +class QValue3DAxis; + +class QValue3DAxisFormatterPrivate : public QObject +{ + Q_OBJECT + +public: + QValue3DAxisFormatterPrivate(QValue3DAxisFormatter *q); + virtual ~QValue3DAxisFormatterPrivate(); + + void recalculate(); + void doRecalculate(); + void populateCopy(QValue3DAxisFormatter ©); + void doPopulateCopy(QValue3DAxisFormatterPrivate ©); + + QString stringForValue(qreal value, const QString &format); + float positionAt(float value) const; + float valueAt(float position) const; + + void setAxis(QValue3DAxis *axis); + void markDirty(bool labelsChange); + +public slots: + void markDirtyNoLabelChange(); + +protected: + QValue3DAxisFormatter *q_ptr; + + bool m_needsRecalculate; + + float m_min; + float m_max; + float m_rangeNormalizer; + + QVector<float> m_gridPositions; + QVector<float> m_subGridPositions; + QVector<float> m_labelPositions; + QStringList m_labelStrings; + + QValue3DAxis *m_axis; + + QString m_previousLabelFormat; + QByteArray m_labelFormatArray; + Utils::ParamType m_preparsedParamType; + + bool m_allowNegatives; + bool m_allowZero; + + friend class QValue3DAxisFormatter; +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif |