blob: 2d0e203aca3a80101c3c9976a95543ea6405ac0b [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 "qscatterdataproxy_p.h"
#include "qscatter3dseries_p.h"
#include "qabstract3daxis_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \class QScatterDataProxy
* \inmodule QtDataVisualization
* \brief The QScatterDataProxy class is the data proxy for 3D scatter graphs.
* \since QtDataVisualization 1.0
*
* A scatter data proxy handles adding, inserting, changing, and removing data
* items.
*
* QScatterDataProxy takes ownership of all
* QtDataVisualization::QScatterDataArray and QScatterDataItem objects passed to
* it.
*
* \sa {Qt Data Visualization Data Handling}
*/
/*!
* \typedef QScatterDataArray
* \relates QScatterDataProxy
*
* A vector of \l {QScatterDataItem} objects.
*/
/*!
* \qmltype ScatterDataProxy
* \inqmlmodule QtDataVisualization
* \since QtDataVisualization 1.0
* \ingroup datavisualization_qml
* \instantiates QScatterDataProxy
* \inherits AbstractDataProxy
* \brief The data proxy for 3D scatter graphs.
*
* This type handles adding, inserting, changing, and removing data items.
*
* This type is uncreatable, but contains properties that are exposed via subtypes.
*
* \sa ItemModelScatterDataProxy, {Qt Data Visualization Data Handling}
*/
/*!
* \qmlproperty int ScatterDataProxy::itemCount
* The number of items in the array.
*/
/*!
* \qmlproperty Scatter3DSeries ScatterDataProxy::series
*
* The series this proxy is attached to.
*/
/*!
* Constructs QScatterDataProxy with the given \a parent.
*/
QScatterDataProxy::QScatterDataProxy(QObject *parent) :
QAbstractDataProxy(new QScatterDataProxyPrivate(this), parent)
{
}
/*!
* \internal
*/
QScatterDataProxy::QScatterDataProxy(QScatterDataProxyPrivate *d, QObject *parent) :
QAbstractDataProxy(d, parent)
{
}
/*!
* Deletes the scatter data proxy.
*/
QScatterDataProxy::~QScatterDataProxy()
{
}
/*!
* \property QScatterDataProxy::series
*
* \brief The series this proxy is attached to.
*/
QScatter3DSeries *QScatterDataProxy::series() const
{
return static_cast<QScatter3DSeries *>(d_ptr->series());
}
/*!
* Takes ownership of the array \a newArray. Clears the existing array if the
* new array differs from it. If the arrays are the same, this function
* just triggers the arrayReset() signal.
*
* Passing a null array deletes the old array and creates a new empty array.
*/
void QScatterDataProxy::resetArray(QScatterDataArray *newArray)
{
if (dptr()->m_dataArray != newArray)
dptr()->resetArray(newArray);
emit arrayReset();
emit itemCountChanged(itemCount());
}
/*!
* Replaces the item at the position \a index with the item \a item.
*/
void QScatterDataProxy::setItem(int index, const QScatterDataItem &item)
{
dptr()->setItem(index, item);
emit itemsChanged(index, 1);
}
/*!
* Replaces the items starting from the position \a index with the items
* specified by \a items.
*/
void QScatterDataProxy::setItems(int index, const QScatterDataArray &items)
{
dptr()->setItems(index, items);
emit itemsChanged(index, items.size());
}
/*!
* Adds the item \a item to the end of the array.
*
* Returns the index of the added item.
*/
int QScatterDataProxy::addItem(const QScatterDataItem &item)
{
int addIndex = dptr()->addItem(item);
emit itemsAdded(addIndex, 1);
emit itemCountChanged(itemCount());
return addIndex;
}
/*!
* Adds the items specified by \a items to the end of the array.
*
* Returns the index of the first added item.
*/
int QScatterDataProxy::addItems(const QScatterDataArray &items)
{
int addIndex = dptr()->addItems(items);
emit itemsAdded(addIndex, items.size());
emit itemCountChanged(itemCount());
return addIndex;
}
/*!
* Inserts the item \a item to the position \a index. If the index is equal to
* the data array size, the item is added to the array.
*/
void QScatterDataProxy::insertItem(int index, const QScatterDataItem &item)
{
dptr()->insertItem(index, item);
emit itemsInserted(index, 1);
emit itemCountChanged(itemCount());
}
/*!
* Inserts the items specified by \a items to the position \a index. If the
* index is equal to data array size, the items are added to the array.
*/
void QScatterDataProxy::insertItems(int index, const QScatterDataArray &items)
{
dptr()->insertItems(index, items);
emit itemsInserted(index, items.size());
emit itemCountChanged(itemCount());
}
/*!
* Removes the number of items specified by \a removeCount starting at the
* position \a index. Attempting to remove items past the end of
* the array does nothing.
*/
void QScatterDataProxy::removeItems(int index, int removeCount)
{
if (index >= dptr()->m_dataArray->size())
return;
dptr()->removeItems(index, removeCount);
emit itemsRemoved(index, removeCount);
emit itemCountChanged(itemCount());
}
/*!
* \property QScatterDataProxy::itemCount
*
* \brief The number of items in the array.
*/
int QScatterDataProxy::itemCount() const
{
return dptrc()->m_dataArray->size();
}
/*!
* Returns the pointer to the data array.
*/
const QScatterDataArray *QScatterDataProxy::array() const
{
return dptrc()->m_dataArray;
}
/*!
* Returns the pointer to the item at the index \a index. It is guaranteed to be
* valid only until the next call that modifies data.
*/
const QScatterDataItem *QScatterDataProxy::itemAt(int index) const
{
return &dptrc()->m_dataArray->at(index);
}
/*!
* \internal
*/
QScatterDataProxyPrivate *QScatterDataProxy::dptr()
{
return static_cast<QScatterDataProxyPrivate *>(d_ptr.data());
}
/*!
* \internal
*/
const QScatterDataProxyPrivate *QScatterDataProxy::dptrc() const
{
return static_cast<const QScatterDataProxyPrivate *>(d_ptr.data());
}
/*!
* \fn void QScatterDataProxy::arrayReset()
*
* This signal is emitted when the data array is reset.
* If the contents of the whole array are changed without calling resetArray(),
* this signal needs to be emitted to update the graph.
*/
/*!
* \fn void QScatterDataProxy::itemsAdded(int startIndex, int count)
*
* This signal is emitted when the number of items specified by \a count is
* added starting at the position \a startIndex.
* If items are added to the array without calling addItem() or addItems(),
* this signal needs to be emitted to update the graph.
*/
/*!
* \fn void QScatterDataProxy::itemsChanged(int startIndex, int count)
*
* This signal is emitted when the number of items specified by \a count is
* changed starting at the position \a startIndex.
* If items are changed in the array without calling setItem() or setItems(),
* this signal needs to be emitted to update the graph.
*/
/*!
* \fn void QScatterDataProxy::itemsRemoved(int startIndex, int count)
*
* This signal is emitted when the number of rows specified by \a count is
* removed starting at the position \a startIndex.
* The index may be larger than the current array size if items are removed from
* the end. If items are removed from the array without calling removeItems(),
* this signal needs to be emitted to update the graph.
*/
/*!
* \fn void QScatterDataProxy::itemsInserted(int startIndex, int count)
*
* This signal is emitted when the number of items specified by \a count is
* inserted starting at the position \a startIndex.
* If items are inserted into the array without calling insertItem() or
* insertItems(), this signal needs to be emitted to update the graph.
*/
// QScatterDataProxyPrivate
QScatterDataProxyPrivate::QScatterDataProxyPrivate(QScatterDataProxy *q)
: QAbstractDataProxyPrivate(q, QAbstractDataProxy::DataTypeScatter),
m_dataArray(new QScatterDataArray)
{
}
QScatterDataProxyPrivate::~QScatterDataProxyPrivate()
{
m_dataArray->clear();
delete m_dataArray;
}
void QScatterDataProxyPrivate::resetArray(QScatterDataArray *newArray)
{
if (!newArray)
newArray = new QScatterDataArray;
if (newArray != m_dataArray) {
m_dataArray->clear();
delete m_dataArray;
m_dataArray = newArray;
}
}
void QScatterDataProxyPrivate::setItem(int index, const QScatterDataItem &item)
{
Q_ASSERT(index >= 0 && index < m_dataArray->size());
(*m_dataArray)[index] = item;
}
void QScatterDataProxyPrivate::setItems(int index, const QScatterDataArray &items)
{
Q_ASSERT(index >= 0 && (index + items.size()) <= m_dataArray->size());
for (int i = 0; i < items.size(); i++)
(*m_dataArray)[index++] = items[i];
}
int QScatterDataProxyPrivate::addItem(const QScatterDataItem &item)
{
int currentSize = m_dataArray->size();
m_dataArray->append(item);
return currentSize;
}
int QScatterDataProxyPrivate::addItems(const QScatterDataArray &items)
{
int currentSize = m_dataArray->size();
(*m_dataArray) += items;
return currentSize;
}
void QScatterDataProxyPrivate::insertItem(int index, const QScatterDataItem &item)
{
Q_ASSERT(index >= 0 && index <= m_dataArray->size());
m_dataArray->insert(index, item);
}
void QScatterDataProxyPrivate::insertItems(int index, const QScatterDataArray &items)
{
Q_ASSERT(index >= 0 && index <= m_dataArray->size());
for (int i = 0; i < items.size(); i++)
m_dataArray->insert(index++, items.at(i));
}
void QScatterDataProxyPrivate::removeItems(int index, int removeCount)
{
Q_ASSERT(index >= 0);
int maxRemoveCount = m_dataArray->size() - index;
removeCount = qMin(removeCount, maxRemoveCount);
m_dataArray->remove(index, removeCount);
}
void QScatterDataProxyPrivate::limitValues(QVector3D &minValues, QVector3D &maxValues,
QAbstract3DAxis *axisX, QAbstract3DAxis *axisY,
QAbstract3DAxis *axisZ) const
{
if (m_dataArray->isEmpty())
return;
const QVector3D &firstPos = m_dataArray->at(0).position();
float minX = firstPos.x();
float maxX = minX;
float minY = firstPos.y();
float maxY = minY;
float minZ = firstPos.z();
float maxZ = minZ;
if (m_dataArray->size() > 1) {
for (int i = 1; i < m_dataArray->size(); i++) {
const QVector3D &pos = m_dataArray->at(i).position();
float value = pos.x();
if (qIsNaN(value) || qIsInf(value))
continue;
if (isValidValue(minX, value, axisX))
minX = value;
if (maxX < value)
maxX = value;
value = pos.y();
if (qIsNaN(value) || qIsInf(value))
continue;
if (isValidValue(minY, value, axisY))
minY = value;
if (maxY < value)
maxY = value;
value = pos.z();
if (qIsNaN(value) || qIsInf(value))
continue;
if (isValidValue(minZ, value, axisZ))
minZ = value;
if (maxZ < value)
maxZ = value;
}
}
minValues.setX(minX);
minValues.setY(minY);
minValues.setZ(minZ);
maxValues.setX(maxX);
maxValues.setY(maxY);
maxValues.setZ(maxZ);
}
bool QScatterDataProxyPrivate::isValidValue(float axisValue, float value,
QAbstract3DAxis *axis) const
{
return (axisValue > value && (value > 0.0f
|| (value == 0.0f && axis->d_ptr->allowZero())
|| (value < 0.0f && axis->d_ptr->allowNegatives())));
}
void QScatterDataProxyPrivate::setSeries(QAbstract3DSeries *series)
{
QAbstractDataProxyPrivate::setSeries(series);
QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(series);
emit qptr()->seriesChanged(scatterSeries);
}
QScatterDataProxy *QScatterDataProxyPrivate::qptr()
{
return static_cast<QScatterDataProxy *>(q_ptr);
}
QT_END_NAMESPACE_DATAVISUALIZATION