blob: 54b973e80b74a20772a605b10072ffcbdbf6fd9d [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Data Visualization module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qbar3dseries_p.h"
#include "bars3dcontroller_p.h"
#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \class QBar3DSeries
* \inmodule QtDataVisualization
* \brief The QBar3DSeries class represents a data series in a 3D bar graph.
* \since QtDataVisualization 1.0
*
* This class manages the series specific visual elements, as well as the series
* data (via a data proxy).
*
* If no data proxy is set explicitly for the series, the series creates a default
* proxy. Setting another proxy will destroy the existing proxy and all data added to it.
*
* QBar3DSeries supports the following format tags for QAbstract3DSeries::setItemLabelFormat():
* \table
* \row
* \li @rowTitle \li Title from row axis
* \row
* \li @colTitle \li Title from column axis
* \row
* \li @valueTitle \li Title from value axis
* \row
* \li @rowIdx \li Visible row index. Localized using the graph locale.
* \row
* \li @colIdx \li Visible column index. Localized using the graph locale.
* \row
* \li @rowLabel \li Label from row axis
* \row
* \li @colLabel \li Label from column axis
* \row
* \li @valueLabel \li Item value formatted using the format of the value
* axis attached to the graph. For more information,
* see \l{QValue3DAxis::labelFormat}.
* \row
* \li @seriesName \li Name of the series
* \row
* \li %<format spec> \li Item value in the specified format. Formatted
* using the same rules as \l{QValue3DAxis::labelFormat}.
* \endtable
*
* For example:
* \snippet doc_src_qtdatavisualization.cpp 1
*
* \sa {Qt Data Visualization Data Handling}, QAbstract3DGraph::locale
*/
/*!
* \qmltype Bar3DSeries
* \inqmlmodule QtDataVisualization
* \since QtDataVisualization 1.0
* \ingroup datavisualization_qml
* \instantiates QBar3DSeries
* \inherits Abstract3DSeries
* \brief Represents a data series in a 3D bar graph.
*
* This type manages the series specific visual elements, as well as the series
* data (via a data proxy).
*
* For a more complete description, see QBar3DSeries.
*
* \sa {Qt Data Visualization Data Handling}
*/
/*!
* \qmlproperty BarDataProxy Bar3DSeries::dataProxy
*
* The active data proxy. The series assumes ownership of any proxy set to
* it and deletes any previously set proxy when a new one is added. The proxy cannot be null or
* set to another series.
*/
/*!
* \qmlproperty point Bar3DSeries::selectedBar
*
* The bar in the series that is selected.
*
* The position of the selected bar is specified as a row and column in the
* data array of the series.
*
* Only one bar can be selected at a time.
*
* To clear selection from this series, set invalidSelectionPosition as the position.
*
* If this series is added to a graph, the graph can adjust the selection according to user
* interaction or if it becomes invalid. Selecting a bar on another added series will also
* clear the selection.
*
* Removing rows from or inserting rows to the series before the row of the selected bar
* will adjust the selection so that the same bar will stay selected.
*
* \sa {AbstractGraph3D::clearSelection()}{AbstractGraph3D.clearSelection()}
*/
/*!
* \qmlproperty point Bar3DSeries::invalidSelectionPosition
* A constant property providing an invalid position for selection. This
* position is set to the selectedBar property to clear the selection from this
* series.
*
* \sa {AbstractGraph3D::clearSelection()}{AbstractGraph3D.clearSelection()}
*/
/*!
* \qmlproperty real Bar3DSeries::meshAngle
*
* A convenience property for defining the series rotation angle in degrees.
*
* \note When reading this property, it is calculated from the
* \l{Abstract3DSeries::meshRotation}{Abstract3DSeries.meshRotation} value
* using floating point precision and always returns a value from zero to 360 degrees.
*
* \sa {Abstract3DSeries::meshRotation}{Abstract3DSeries.meshRotation}
*/
/*!
* Constructsa bar 3D series with the parent \a parent.
*/
QBar3DSeries::QBar3DSeries(QObject *parent) :
QAbstract3DSeries(new QBar3DSeriesPrivate(this), parent)
{
// Default proxy
dptr()->setDataProxy(new QBarDataProxy);
dptr()->connectSignals();
}
/*!
* Constructs a bar 3D series with the data proxy \a dataProxy and the parent
* \a parent.
*/
QBar3DSeries::QBar3DSeries(QBarDataProxy *dataProxy, QObject *parent) :
QAbstract3DSeries(new QBar3DSeriesPrivate(this), parent)
{
dptr()->setDataProxy(dataProxy);
dptr()->connectSignals();
}
/*!
* Deletes a bar 3D series.
*/
QBar3DSeries::~QBar3DSeries()
{
}
/*!
* \property QBar3DSeries::dataProxy
*
* \brief The active data proxy.
*
* The series assumes ownership of any proxy set to it and deletes any
* previously set proxy when a new one is added. The proxy cannot be null or
* set to another series.
*/
void QBar3DSeries::setDataProxy(QBarDataProxy *proxy)
{
d_ptr->setDataProxy(proxy);
}
QBarDataProxy *QBar3DSeries::dataProxy() const
{
return static_cast<QBarDataProxy *>(d_ptr->dataProxy());
}
/*!
* \property QBar3DSeries::selectedBar
*
* \brief The bar in the series that is selected.
*
*/
/*!
* Selects the bar at the \a position position, specified as a row and column in
* the data array of the series.
*
* Only one bar can be selected at a time.
*
* To clear selection from this series, invalidSelectionPosition() is set as
* \a position.
*
* If this series is added to a graph, the graph can adjust the selection according to user
* interaction or if it becomes invalid. Selecting a bar on another added series will also
* clear the selection.
*
* Removing rows from or inserting rows to the series before the row of the selected bar
* will adjust the selection so that the same bar will stay selected.
*
* \sa QAbstract3DGraph::clearSelection()
*/
void QBar3DSeries::setSelectedBar(const QPoint &position)
{
// Don't do this in private to avoid loops, as that is used for callback from controller.
if (d_ptr->m_controller)
static_cast<Bars3DController *>(d_ptr->m_controller)->setSelectedBar(position, this, true);
else
dptr()->setSelectedBar(position);
}
QPoint QBar3DSeries::selectedBar() const
{
return dptrc()->m_selectedBar;
}
/*!
* Returns an invalid position for selection. This position is set to the
* selectedBar property to clear the selection from this series.
*
* \sa QAbstract3DGraph::clearSelection()
*/
QPoint QBar3DSeries::invalidSelectionPosition()
{
return Bars3DController::invalidSelectionPosition();
}
static inline float quaternionAngle(const QQuaternion &rotation)
{
return qRadiansToDegrees(qAcos(rotation.scalar())) * 2.f;
}
/*!
\property QBar3DSeries::meshAngle
\brief The series rotation angle in degrees.
Setting this property is equivalent to the following call:
\code
setMeshRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, angle))
\endcode
\note When reading this property, it is calculated from the
QAbstract3DSeries::meshRotation value using floating point precision
and always returns a value from zero to 360 degrees.
\sa QAbstract3DSeries::meshRotation
*/
void QBar3DSeries::setMeshAngle(float angle)
{
setMeshRotation(QQuaternion::fromAxisAndAngle(upVector, angle));
}
float QBar3DSeries::meshAngle() const
{
QQuaternion rotation = meshRotation();
if (rotation.isIdentity() || rotation.x() != 0.0f || rotation.z() != 0.0f)
return 0.0f;
else
return quaternionAngle(rotation);
}
/*!
* \internal
*/
QBar3DSeriesPrivate *QBar3DSeries::dptr()
{
return static_cast<QBar3DSeriesPrivate *>(d_ptr.data());
}
/*!
* \internal
*/
const QBar3DSeriesPrivate *QBar3DSeries::dptrc() const
{
return static_cast<const QBar3DSeriesPrivate *>(d_ptr.data());
}
// QBar3DSeriesPrivate
QBar3DSeriesPrivate::QBar3DSeriesPrivate(QBar3DSeries *q)
: QAbstract3DSeriesPrivate(q, QAbstract3DSeries::SeriesTypeBar),
m_selectedBar(Bars3DController::invalidSelectionPosition())
{
m_itemLabelFormat = QStringLiteral("@valueLabel");
m_mesh = QAbstract3DSeries::MeshBevelBar;
}
QBar3DSeriesPrivate::~QBar3DSeriesPrivate()
{
}
QBar3DSeries *QBar3DSeriesPrivate::qptr()
{
return static_cast<QBar3DSeries *>(q_ptr);
}
void QBar3DSeriesPrivate::setDataProxy(QAbstractDataProxy *proxy)
{
Q_ASSERT(proxy->type() == QAbstractDataProxy::DataTypeBar);
QAbstract3DSeriesPrivate::setDataProxy(proxy);
emit qptr()->dataProxyChanged(static_cast<QBarDataProxy *>(proxy));
}
void QBar3DSeriesPrivate::connectControllerAndProxy(Abstract3DController *newController)
{
QBarDataProxy *barDataProxy = static_cast<QBarDataProxy *>(m_dataProxy);
if (m_controller && barDataProxy) {
// Disconnect old controller/old proxy
QObject::disconnect(barDataProxy, 0, m_controller, 0);
QObject::disconnect(q_ptr, 0, m_controller, 0);
}
if (newController && barDataProxy) {
Bars3DController *controller = static_cast<Bars3DController *>(newController);
QObject::connect(barDataProxy, &QBarDataProxy::arrayReset, controller,
&Bars3DController::handleArrayReset);
QObject::connect(barDataProxy, &QBarDataProxy::rowsAdded, controller,
&Bars3DController::handleRowsAdded);
QObject::connect(barDataProxy, &QBarDataProxy::rowsChanged, controller,
&Bars3DController::handleRowsChanged);
QObject::connect(barDataProxy, &QBarDataProxy::rowsRemoved, controller,
&Bars3DController::handleRowsRemoved);
QObject::connect(barDataProxy, &QBarDataProxy::rowsInserted, controller,
&Bars3DController::handleRowsInserted);
QObject::connect(barDataProxy, &QBarDataProxy::itemChanged, controller,
&Bars3DController::handleItemChanged);
QObject::connect(barDataProxy, &QBarDataProxy::rowLabelsChanged, controller,
&Bars3DController::handleDataRowLabelsChanged);
QObject::connect(barDataProxy, &QBarDataProxy::columnLabelsChanged, controller,
&Bars3DController::handleDataColumnLabelsChanged);
QObject::connect(qptr(), &QBar3DSeries::dataProxyChanged, controller,
&Bars3DController::handleArrayReset);
}
}
void QBar3DSeriesPrivate::createItemLabel()
{
static const QString rowIndexTag(QStringLiteral("@rowIdx"));
static const QString rowLabelTag(QStringLiteral("@rowLabel"));
static const QString rowTitleTag(QStringLiteral("@rowTitle"));
static const QString colIndexTag(QStringLiteral("@colIdx"));
static const QString colLabelTag(QStringLiteral("@colLabel"));
static const QString colTitleTag(QStringLiteral("@colTitle"));
static const QString valueTitleTag(QStringLiteral("@valueTitle"));
static const QString valueLabelTag(QStringLiteral("@valueLabel"));
static const QString seriesNameTag(QStringLiteral("@seriesName"));
if (m_selectedBar == QBar3DSeries::invalidSelectionPosition()) {
m_itemLabel = QString();
return;
}
QLocale locale(QLocale::c());
if (m_controller)
locale = m_controller->locale();
QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_controller->axisZ());
QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_controller->axisX());
QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(m_controller->axisY());
qreal selectedBarValue = qreal(qptr()->dataProxy()->itemAt(m_selectedBar)->value());
// Custom format expects printf format specifier. There is no tag for it.
m_itemLabel = valueAxis->formatter()->stringForValue(selectedBarValue, m_itemLabelFormat);
int selBarPosRow = m_selectedBar.x();
int selBarPosCol = m_selectedBar.y();
m_itemLabel.replace(rowIndexTag, locale.toString(selBarPosRow));
if (categoryAxisZ->labels().size() > selBarPosRow)
m_itemLabel.replace(rowLabelTag, categoryAxisZ->labels().at(selBarPosRow));
else
m_itemLabel.replace(rowLabelTag, QString());
m_itemLabel.replace(rowTitleTag, categoryAxisZ->title());
m_itemLabel.replace(colIndexTag, locale.toString(selBarPosCol));
if (categoryAxisX->labels().size() > selBarPosCol)
m_itemLabel.replace(colLabelTag, categoryAxisX->labels().at(selBarPosCol));
else
m_itemLabel.replace(colLabelTag, QString());
m_itemLabel.replace(colTitleTag, categoryAxisX->title());
m_itemLabel.replace(valueTitleTag, valueAxis->title());
if (m_itemLabel.contains(valueLabelTag)) {
QString valueLabelText = valueAxis->formatter()->stringForValue(selectedBarValue,
valueAxis->labelFormat());
m_itemLabel.replace(valueLabelTag, valueLabelText);
}
m_itemLabel.replace(seriesNameTag, m_name);
}
void QBar3DSeriesPrivate::handleMeshRotationChanged(const QQuaternion &rotation)
{
emit qptr()->meshAngleChanged(quaternionAngle(rotation));
}
void QBar3DSeriesPrivate::setSelectedBar(const QPoint &position)
{
if (position != m_selectedBar) {
markItemLabelDirty();
m_selectedBar = position;
emit qptr()->selectedBarChanged(m_selectedBar);
}
}
void QBar3DSeriesPrivate::connectSignals()
{
QObject::connect(q_ptr, &QAbstract3DSeries::meshRotationChanged, this,
&QBar3DSeriesPrivate::handleMeshRotationChanged);
}
QT_END_NAMESPACE_DATAVISUALIZATION