diff options
author | Tomi Korpipää <tomi.korpipaa@digia.com> | 2013-08-02 11:33:34 +0300 |
---|---|---|
committer | Tomi Korpipää <tomi.korpipaa@digia.com> | 2013-08-05 07:41:02 +0300 |
commit | 48248feb5334e07ab0e81f4400b38e62bf26fb03 (patch) | |
tree | ff2039c310ff2d6afebebe7b60734c61cc1fcd43 /src | |
parent | acb15b2d7822c39b74845c7ea65b51881e91f010 (diff) |
Q3DScatter: First draft
Compiles, but has not been tested yet.
QML -part still completely unimplemented.
Change-Id: Iccf98ea6265d38325168d15f3bef1b609f5a7004
Change-Id: Iccf98ea6265d38325168d15f3bef1b609f5a7004
Reviewed-by: Tomi Korpipää <tomi.korpipaa@digia.com>
Diffstat (limited to 'src')
20 files changed, 4224 insertions, 4 deletions
diff --git a/src/datavis3d/axis/qabstractaxis.h b/src/datavis3d/axis/qabstractaxis.h index a5dd6951..97823381 100644 --- a/src/datavis3d/axis/qabstractaxis.h +++ b/src/datavis3d/axis/qabstractaxis.h @@ -102,6 +102,8 @@ private: friend class Abstract3DController; friend class Bars3dController; friend class Bars3dRenderer; + friend class Scatter3DController; + friend class Scatter3DRenderer; }; QT_DATAVIS3D_END_NAMESPACE diff --git a/src/datavis3d/data/data.pri b/src/datavis3d/data/data.pri index bd922e00..6983f669 100644 --- a/src/datavis3d/data/data.pri +++ b/src/datavis3d/data/data.pri @@ -23,6 +23,11 @@ HEADERS += \ $$PWD/qmapdataitem_p.h \ $$PWD/qmapdataproxy.h \ $$PWD/qmapdataproxy_p.h \ + $$PWD/scatterrenderitem_p.h \ + $$PWD/qscatterdataitem.h \ + $$PWD/qscatterdataitem_p.h \ + $$PWD/qscatterdataproxy.h \ + $$PWD/qscatterdataproxy_p.h \ $$PWD/qitemmodelmapdatamapping.h \ $$PWD/qitemmodelmapdatamapping_p.h \ $$PWD/qitemmodelmapdataproxy.h \ @@ -43,5 +48,8 @@ SOURCES += \ $$PWD/maprenderitem.cpp \ $$PWD/qmapdataitem.cpp \ $$PWD/qmapdataproxy.cpp \ + $$PWD/scatterrenderitem.cpp \ + $$PWD/qscatterdataitem.cpp \ + $$PWD/qscatterdataproxy.cpp \ $$PWD/qitemmodelmapdatamapping.cpp \ $$PWD/qitemmodelmapdataproxy.cpp diff --git a/src/datavis3d/data/qscatterdataitem.cpp b/src/datavis3d/data/qscatterdataitem.cpp new file mode 100644 index 00000000..e7d96551 --- /dev/null +++ b/src/datavis3d/data/qscatterdataitem.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscatterdataitem_p.h" + +QT_DATAVIS3D_BEGIN_NAMESPACE + +/*! + * \class QScatterDataItem + * \inmodule QtDataVis3D + * \brief The QScatterDataItem class provides a container for resolved data to be added to scatter + * graphs. + * \since 1.0.0 + * + * A QScatterDataItem holds data for a single rendered item in a scatter graph. + * Scatter data proxies parse data into QScatterDataItem instances for visualizing. + * + * \sa QScatterDataProxy, {Qt Data Visualization 3D C++ Classes} + */ + +/*! + * Constructs QScatterDataItem. + */ +QScatterDataItem::QScatterDataItem() + : QBarDataItem() +{ +} + +QScatterDataItem::QScatterDataItem(const QScatterDataItem &other) + : QBarDataItem(other) +{ + operator=(other); +} + +/*! + * Destroys QScatterDataItem. + */ +QScatterDataItem::~QScatterDataItem() +{ +} + +QScatterDataItem &QScatterDataItem::operator=(const QScatterDataItem &other) +{ + QBarDataItem::operator =(other); + m_scatterPosition = other.m_scatterPosition; + // m_size = other.m_size; + + return *this; +} + +void QScatterDataItem::setScatterPosition(const QPointF &position) +{ + m_scatterPosition = position; +} + +const QPointF &QScatterDataItem::scatterPosition() const +{ + return m_scatterPosition; +} + +//void QScatterDataItem::setSize(qreal size) +//{ +// m_size = size; +//} + +//const qreal &QScatterDataItem::size() const +//{ +// return m_size; +//} + +void QScatterDataItem::createExtraData() +{ + if (!d_ptr) + d_ptr = new QScatterDataItemPrivate; +} + +QScatterDataItemPrivate::QScatterDataItemPrivate() + : QBarDataItemPrivate() +{ +} + +QScatterDataItemPrivate::~QScatterDataItemPrivate() +{ +} + +QT_DATAVIS3D_END_NAMESPACE diff --git a/src/datavis3d/data/qscatterdataitem.h b/src/datavis3d/data/qscatterdataitem.h new file mode 100644 index 00000000..fdf2462b --- /dev/null +++ b/src/datavis3d/data/qscatterdataitem.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCATTERDATAITEM_H +#define QSCATTERDATAITEM_H + +#include "qdatavis3dnamespace.h" +#include "qbardataitem.h" +#include <QPointF> + +QT_DATAVIS3D_BEGIN_NAMESPACE + +class QScatterDataItemPrivate; + +class QT_DATAVIS3D_EXPORT QScatterDataItem : public QBarDataItem +{ +public: + QScatterDataItem(); + QScatterDataItem(const QScatterDataItem &other); + ~QScatterDataItem(); + + QScatterDataItem &operator=(const QScatterDataItem &other); + + void setScatterPosition(const QPointF &position); + const QPointF &scatterPosition() const; + + // void setSize(qreal size); + // qreal size() const; + +protected: + virtual void createExtraData(); + + QScatterDataItemPrivate *d_ptr; + +private: + QPointF m_scatterPosition; + // qreal m_size; +}; + +QT_DATAVIS3D_END_NAMESPACE + +#endif diff --git a/src/datavis3d/data/qscatterdataitem_p.h b/src/datavis3d/data/qscatterdataitem_p.h new file mode 100644 index 00000000..8db88424 --- /dev/null +++ b/src/datavis3d/data/qscatterdataitem_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVis3D 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 QSCATTERDATAITEM_P_H +#define QSCATTERDATAITEM_P_H + +#include "datavis3dglobal_p.h" +#include "qscatterdataitem.h" +#include "qbardataitem_p.h" + +QT_DATAVIS3D_BEGIN_NAMESPACE + +class QScatterDataItemPrivate : public QBarDataItemPrivate +{ +public: + QScatterDataItemPrivate(); + virtual ~QScatterDataItemPrivate(); + + // TODO stores other data for scatter items besides position + +protected: + friend class QScatterDataItem; +}; + +QT_DATAVIS3D_END_NAMESPACE + +#endif diff --git a/src/datavis3d/data/qscatterdataproxy.cpp b/src/datavis3d/data/qscatterdataproxy.cpp new file mode 100644 index 00000000..685d2e57 --- /dev/null +++ b/src/datavis3d/data/qscatterdataproxy.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscatterdataproxy.h" +#include "qscatterdataproxy_p.h" +#include <QMutexLocker> + +QT_DATAVIS3D_BEGIN_NAMESPACE + +QScatterDataProxy::QScatterDataProxy() : + QAbstractDataProxy(new QScatterDataProxyPrivate(this)) +{ +} + +QScatterDataProxy::QScatterDataProxy(QScatterDataProxyPrivate *d) : + QAbstractDataProxy(d) +{ +} + +QScatterDataProxy::~QScatterDataProxy() +{ +} + +void QScatterDataProxy::resetArray(QScatterDataArray *newArray) +{ + if (dptr()->resetArray(newArray)) + emit arrayReset(); +} + + +// Mutexing data accessors should be done by user, if needed +int QScatterDataProxy::itemCount() +{ + return dptr()->m_dataArray.size(); +} + +const QScatterDataArray &QScatterDataProxy::array() const +{ + return dptrc()->m_dataArray; +} + +const QScatterDataItem *QScatterDataProxy::itemAt(int index) const +{ + return &dptrc()->m_dataArray.at(index); +} + +QScatterDataProxyPrivate *QScatterDataProxy::dptr() +{ + return static_cast<QScatterDataProxyPrivate *>(d_ptr.data()); +} + +const QScatterDataProxyPrivate *QScatterDataProxy::dptrc() const +{ + return static_cast<const QScatterDataProxyPrivate *>(d_ptr.data()); +} + +// QBarDataProxyPrivate + +QScatterDataProxyPrivate::QScatterDataProxyPrivate(QScatterDataProxy *q) + : QAbstractDataProxyPrivate(q, QAbstractDataProxy::DataTypeMap) +{ +} + +QScatterDataProxyPrivate::~QScatterDataProxyPrivate() +{ + m_dataArray.clear(); +} + +bool QScatterDataProxyPrivate::resetArray(QScatterDataArray *newArray) +{ + QMutexLocker locker(&m_mutex); + + if (!m_dataArray.size() && (!newArray || !newArray->size())) + return false; + + m_dataArray.clear(); + + if (newArray) { + m_dataArray = *newArray; + delete newArray; + } + + return true; +} + +// Protected & private functions. Do not mutex as these are used from mutexed functions. + +QPair<GLfloat, GLfloat> QScatterDataProxyPrivate::limitValues() +{ + QMutexLocker locker(&m_mutex); + QPair<GLfloat, GLfloat> limits = qMakePair(100.0f, -100.0f); + for (int i = 0; i < m_dataArray.size(); i++) { + const QScatterDataItem &item = m_dataArray.at(i); + qreal itemValue = item.value(); + if (limits.second < itemValue) + limits.second = itemValue; + if (limits.first > itemValue) + limits.first = itemValue; + } + return limits; +} + +QT_DATAVIS3D_END_NAMESPACE diff --git a/src/datavis3d/data/qscatterdataproxy.h b/src/datavis3d/data/qscatterdataproxy.h new file mode 100644 index 00000000..fe83c7fe --- /dev/null +++ b/src/datavis3d/data/qscatterdataproxy.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCATTERDATAPROXY_H +#define QSCATTERDATAPROXY_H + +#include "qabstractdataproxy.h" +#include "qscatterdataitem.h" + +QT_DATAVIS3D_BEGIN_NAMESPACE + +typedef QVector<QScatterDataItem> QScatterDataArray; + +class QScatterDataProxyPrivate; + +class QT_DATAVIS3D_EXPORT QScatterDataProxy : public QAbstractDataProxy +{ + Q_OBJECT + +public: + explicit QScatterDataProxy(); + explicit QScatterDataProxy(QScatterDataProxyPrivate *d); + virtual ~QScatterDataProxy(); + + // QScatterDataProxy is also optimized to use cases where the only defining characteristics of an individual + // map item are it's value and position. Modifying other data such as color or mesh of individual bar + // requires allocating additional data object for the bar. + + // If data is accessed from same thread that sets it, access doesn't need to be protected with mutex. + int itemCount(); + const QScatterDataArray &array() const; + const QScatterDataItem *itemAt(int index) const; // Index needs to exist or this crashes + + // All array/item manipulation functions are internally protected by data mutex. + + // TODO: Should we remove internal mutexing and require user to mutex also on data manipulations? + // TODO: Upside would be enabling user to mix data setters and getters within same mutex scope. + // TODO: Downside would be signal emissions from inside the mutex scope, which has potential for deadlock. + + // TODO Should data manipulation/access methods be protected rather than public to enforce subclassing use of proxy? + // TODO Leaving them public gives user more options. + + // QScatterDataProxy takes ownership of all QScatterDataArrays and QScatterDataItems passed to it. + + // Clears the existing array and sets it data to new array. + void resetArray(QScatterDataArray *newArray); + + // TODO void setItem(int index, QScatterDataItem *item); + // TODO void setItems(int index, QScatterDataArray *items); + + // TODO int addItem(QScatterDataItem *item); // returns the index of added item + // TODO int addItems(QScatterDataArray *items); // returns the index of added item + + // TODO void insertItem(int index, QScatterDataItem *item); + // TODO void insertItems(int index, QScatterDataArray *items); + + // TODO void removeItems(int index, int removeCount); + +signals: + void arrayReset(); + void itemsAdded(int startIndex, int count); + void itemsChanged(int startIndex, int count); + void itemsRemoved(int startIndex, int count); // Index may be over current array size if removed from end + void itemsInserted(int startIndex, int count); + +protected: + QScatterDataProxyPrivate *dptr(); + const QScatterDataProxyPrivate *dptrc() const; + +private: + Q_DISABLE_COPY(QScatterDataProxy) + + friend class Scatter3DController; +}; + +QT_DATAVIS3D_END_NAMESPACE + +#endif diff --git a/src/datavis3d/data/qscatterdataproxy_p.h b/src/datavis3d/data/qscatterdataproxy_p.h new file mode 100644 index 00000000..821ceff2 --- /dev/null +++ b/src/datavis3d/data/qscatterdataproxy_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVis3D 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 QSCATTERDATAPROXY_P_H +#define QSCATTERDATAPROXY_P_H + +#include "qscatterdataproxy.h" +#include "qabstractdataproxy_p.h" +#include "qscatterdataitem.h" + +QT_DATAVIS3D_BEGIN_NAMESPACE + +class QScatterDataProxyPrivate : public QAbstractDataProxyPrivate +{ + Q_OBJECT +public: + QScatterDataProxyPrivate(QScatterDataProxy *q); + virtual ~QScatterDataProxyPrivate(); + + bool resetArray(QScatterDataArray *newArray); + + QPair<GLfloat, GLfloat> limitValues(); + +private: + QScatterDataArray m_dataArray; + QString m_itemLabelFormat; + +private: + friend class QScatterDataProxy; +}; + +QT_DATAVIS3D_END_NAMESPACE + +#endif // QBARDATAPROXY_P_H diff --git a/src/datavis3d/data/scatterrenderitem.cpp b/src/datavis3d/data/scatterrenderitem.cpp new file mode 100644 index 00000000..2cbfeadb --- /dev/null +++ b/src/datavis3d/data/scatterrenderitem.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scatterrenderitem_p.h" +#include "scatter3drenderer_p.h" +#include "qscatterdataproxy.h" + +QT_DATAVIS3D_BEGIN_NAMESPACE + +ScatterRenderItem::ScatterRenderItem() + : BarRenderItem() +{ +} + +ScatterRenderItem::~ScatterRenderItem() +{ +} + +void ScatterRenderItem::formatLabel() +{ + // TODO The label format specified in proxy should probably have additional custom formatting + // TODO specifiers in addition to standard printf specifiers for placement of item labels + // TODO and selection data (like row/column in bar selection) + + // Format the string on first access + // QString numStr; + // numStr.setNum(m_value); + // // TODO actually format instead of just prepending the value + // m_label.clear(); // Just in case + // m_label.append(m_itemLabel); + // m_label.append(QStringLiteral(" ")); + // m_label.append(numStr); + // m_label.append(m_renderer->dataProxy()->itemLabelFormat()); +} + +QT_DATAVIS3D_END_NAMESPACE diff --git a/src/datavis3d/data/scatterrenderitem_p.h b/src/datavis3d/data/scatterrenderitem_p.h new file mode 100644 index 00000000..2ab840c4 --- /dev/null +++ b/src/datavis3d/data/scatterrenderitem_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVis3D 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 SCATTERRENDERITEM_P_H +#define SCATTERRENDERITEM_P_H + +#include "barrenderitem_p.h" + +QT_DATAVIS3D_BEGIN_NAMESPACE + +class Scatter3DRenderer; + +class ScatterRenderItem : public BarRenderItem +{ +public: + ScatterRenderItem(); + virtual ~ScatterRenderItem(); + + inline const QPointF &scatterPosition() const { return m_scatterPosition; } + inline void setScatterPosition(const QPointF &pos) { m_scatterPosition = pos; } + + // inline void setSize(qreal size); + // inline qreal size() const { return m_size; } + + // TODO should be in abstract, but currently there is no abstract renderer + // TODO change when maps refactored + inline void setRenderer(Scatter3DRenderer *renderer) { m_renderer = renderer; } + +protected: + virtual void formatLabel(); + + Scatter3DRenderer *m_renderer; + QPointF m_scatterPosition; + // qreal m_size; // TODO in case we need a fourth variable that adjusts scatter item size + + friend class QScatterDataItem; +}; + +typedef QVector<ScatterRenderItem> ScatterRenderItemArray; + +QT_DATAVIS3D_END_NAMESPACE + +#endif diff --git a/src/datavis3d/engine/engine.pri b/src/datavis3d/engine/engine.pri index 92f21929..5df036c0 100644 --- a/src/datavis3d/engine/engine.pri +++ b/src/datavis3d/engine/engine.pri @@ -14,7 +14,11 @@ HEADERS += $$PWD/q3dwindow_p.h \ $$PWD/q3dsurface_p.h \ $$PWD/surface3dcontroller_p.h \ $$PWD/surface3drenderer_p.h \ - $$PWD/abstract3dcontroller_p.h + $$PWD/abstract3dcontroller_p.h \ + $$PWD/q3dscatter.h \ + $$PWD/q3dscatter_p.h \ + $$PWD/scatter3dcontroller_p.h \ + $$PWD/scatter3drenderer_p.h SOURCES += $$PWD/q3dwindow.cpp \ $$PWD/q3dbars.cpp \ @@ -28,6 +32,9 @@ SOURCES += $$PWD/q3dwindow.cpp \ $$PWD/q3dsurface.cpp \ $$PWD/surface3drenderer.cpp \ $$PWD/surface3dcontroller.cpp \ - $$PWD/abstract3dcontroller.cpp + $$PWD/abstract3dcontroller.cpp \ + $$PWD/q3dscatter.cpp \ + $$PWD/scatter3dcontroller.cpp \ + $$PWD/scatter3drenderer.cpp RESOURCES += engine/engine.qrc diff --git a/src/datavis3d/engine/q3dscatter.cpp b/src/datavis3d/engine/q3dscatter.cpp new file mode 100644 index 00000000..621454e8 --- /dev/null +++ b/src/datavis3d/engine/q3dscatter.cpp @@ -0,0 +1,519 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dscatter.h" +#include "q3dscatter_p.h" +#include "scatter3dcontroller_p.h" + +#include <QMouseEvent> + +#include <QDebug> + +QT_DATAVIS3D_BEGIN_NAMESPACE + +/*! + * \class Q3DScatter + * \inmodule QtDataVis3D + * \brief The Q3DScatter class provides methods for rendering 3D scatter graphs. + * \since 1.0.0 + * + * This class enables developers to render bar graphs in 3D and to view them by rotating the scene + * freely. Rotation is done by holding down the right mouse button and moving the mouse. Zooming + * is done by mouse wheel. Selection, if enabled, is done by left mouse button. The scene can be + * reset to default camera view by clicking mouse wheel. In touch devices rotation is done + * by tap-and-move, selection by tap-and-hold and zoom by pinch. + * + * Methods are provided for changing bar types, themes, bar selection modes and so on. See the + * methods for more detailed descriptions. + * + * \section1 How to construct a minimal Q3DScatter chart + * + * After constructing Q3DScatter, you need to set up sample space using setupSampleSpace(). Let's + * set the sample space to 5 rows and 5 columns: + * + * \snippet doc_src_q3dscatter_construction.cpp 0 + * + * Now Q3DScatter is ready to receive data to be rendered. Add one row of 5 floats into the data + * set: + * + * \snippet doc_src_q3dscatter_construction.cpp 1 + * + * \note We set the sample space to 5 x 5, but we are inserting only one row of data. This is ok, + * the rest of the rows will just be blank. + * + * Finally you will need to set it visible: + * + * \snippet doc_src_q3dscatter_construction.cpp 2 + * + * The complete code needed to create and display this chart is: + * + * \snippet doc_src_q3dscatter_construction.cpp 3 + * + * And this is what those few lines of code produce: + * + * \image q3dscatter-minimal.png + * + * The scene can be rotated and zoomed into, but no other interaction is included in this minimal + * code example. You can learn more by familiarizing yourself with the examples provided, like + * the \l{Rainfall Example} or the \l{Widget Example}. + * + * \sa Q3DMaps, {Qt Data Visualization 3D C++ Classes} + */ + +/*! + * Constructs a new 3D bar window. + */ +Q3DScatter::Q3DScatter() + : d_ptr(new Q3DScatterPrivate(this, geometry())) +{ + d_ptr->m_shared->initializeOpenGL(); + QObject::connect(d_ptr->m_shared, &Scatter3DController::shadowQualityChanged, this, + &Q3DScatter::handleShadowQualityUpdate); +} + +/*! + * Destroys the 3d bar window. + */ +Q3DScatter::~Q3DScatter() +{ +} + +/*! + * \internal + */ +void Q3DScatter::render() +{ + d_ptr->m_shared->render(); +} + +void Q3DScatter::handleShadowQualityUpdate(ShadowQuality quality) +{ + emit shadowQualityChanged(quality); +} + +#if defined(Q_OS_ANDROID) +/*! + * \internal + */ +void Q3DScatter::mouseDoubleClickEvent(QMouseEvent *event) +{ + d_ptr->m_shared->mouseDoubleClickEvent(event); +} + +/*! + * \internal + */ +void Q3DScatter::touchEvent(QTouchEvent *event) +{ + d_ptr->m_shared->touchEvent(event); +} +#endif + +/*! + * \internal + */ +void Q3DScatter::mousePressEvent(QMouseEvent *event) +{ + d_ptr->m_shared->mousePressEvent(event, event->pos()); +} + +/*! + * \internal + */ +void Q3DScatter::mouseReleaseEvent(QMouseEvent *event) +{ + d_ptr->m_shared->mouseReleaseEvent(event, event->pos()); +} + +/*! + * \internal + */ +void Q3DScatter::mouseMoveEvent(QMouseEvent *event) +{ + d_ptr->m_shared->mouseMoveEvent(event, event->pos()); +} + +/*! + * \internal + */ +void Q3DScatter::wheelEvent(QWheelEvent *event) +{ + d_ptr->m_shared->wheelEvent(event); +} + +/*! + * \internal + */ +void Q3DScatter::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event); + d_ptr->m_shared->setSize(width(), height()); +} + +// TODO: Document +// Size +void Q3DScatter::setWidth(const int width) +{ + d_ptr->m_shared->setWidth(width); + QWindow::setWidth(width); +} + +void Q3DScatter::setHeight(const int height) +{ + d_ptr->m_shared->setHeight(height); + QWindow::setHeight(height); +} + +/*! + * \a thickness Thickness of a bar in x and z axes. + * + * \a spacing Spacing between bars in x and z axes. If relative -flag is true, value of 0.0f + * means the bars are side-to-side and for example 1.0f means there is one thickness in between the + * bars. + * + * \a relative A flag to indicate if spacing is meant to be absolute or relative. \c true by + * default. + * + * Sets bar specifications. Bar thickness is relative, as scene is automatically scaled to fit into + * the view. + */ +void Q3DScatter::setBarSpecs(QSizeF thickness, QSizeF spacing, bool relative) +{ + d_ptr->m_shared->setBarSpecs(thickness, spacing, relative); +} + +/*! + * \a style One of the values in \c BarStyle. \c Bars by default. + * + * \a smooth A flag to set shading to smooth. \c false by default. + * + * Sets the bar type to one of the supplied ones. + * + * \sa setMeshFileName() + */ +void Q3DScatter::setBarType(BarStyle style, bool smooth) +{ + d_ptr->m_shared->setBarType(style, smooth); +} + +/*! + * \a samplesRow How many rows of data there will be. + * + * \a samplesColumn How many items there are per row. + * + * Set up sample space. This must be called to initialize the sample space before adding data to the + * Q3DScatter. + * + * \sa addDataRow(), addDataSet() + */ +void Q3DScatter::setupSampleSpace(int samplesRow, int samplesColumn) +{ + d_ptr->m_shared->setupSampleSpace(samplesRow, samplesColumn); +} + +/*! + * \a preset Move camera to a predefined position from \c CameraPreset. + * + * Moves camera to a predefined position. + */ +void Q3DScatter::setCameraPreset(CameraPreset preset) +{ + d_ptr->m_shared->setCameraPreset(preset); +} + +/*! + * \a horizontal Horizontal angle for camera. + * + * \a vertical Vertical angle for camera. + * + * \a distance Distance from the center. \c 100 by default. + * + * Move camera to a wanted position based on horizontal and veritcal angles. Angles are limited + * to -180...180 in horizontal direction and either -90...90 or 0...90 in vertical, depending + * on data values. Negative vertical angles are allowed only if there are negative bar values. + * Distance is adjustable between 10 and 500. + */ +void Q3DScatter::setCameraPosition(qreal horizontal, qreal vertical, int distance) +{ + d_ptr->m_shared->setCameraPosition(GLfloat(horizontal), GLfloat(vertical), GLint(distance)); +} + +/*! + * \a theme Apply a predefined theme from \c ColorTheme. + * + * Sets a predefined theme. Theme affects bar colors, label colors, text color, background color, + * window color and grid color. Lighting is also adjusted by themes. + */ +void Q3DScatter::setTheme(ColorTheme theme) +{ + d_ptr->m_shared->setColorTheme(theme); +} + +/*! + * \a baseColor The base color of a bar. If all other colors are black, this sets the final color of + * the bar. + * + * \a heightColor This color is added to the bar based on its height. The higher the bar, the more + * prominent this color becomes. Setting this black keeps the color unchanged regardless of height. + * + * \a depthColor This color becomes more prominent the further away from the first row the bar is. + * Setting this black keeps bars the same color regardless of "depth" in the set. + * + * \a uniform A flag to define if color needs to be uniform throughout bar's length, or will the + * colors be applied by height. \c true by default. + * + * Set bar color using your own colors. This overrides colors from theme. + */ +void Q3DScatter::setBarColor(QColor baseColor, QColor heightColor, QColor depthColor, bool uniform) +{ + d_ptr->m_shared->setBarColor(baseColor, heightColor, depthColor, uniform); +} + +/*! + * \property Q3DScatter::selectionMode + * + * \a mode Set bar selection mode from \c SelectionMode. \c ModeBar by default. + * + * Sets bar selection mode to be used. + */ +void Q3DScatter::setSelectionMode(SelectionMode mode) +{ + d_ptr->m_shared->setSelectionMode(mode); +} + +SelectionMode Q3DScatter::selectionMode() +{ + return d_ptr->m_shared->selectionMode(); +} + +/*! + * \property Q3DScatter::windowTitle + * + * \a title QString label to be used as window title. + * + * Sets the window title. The default is application executable name. + */ +void Q3DScatter::setWindowTitle(const QString &title) +{ + setTitle(title); +} + +QString Q3DScatter::windowTitle() +{ + return title(); +} + +/*! + * \a objFileName File name of a mesh object. Object needs to be in Wavefront obj format + * and include vertices, normals and UVs. It also needs to be in triangles. + * + * Override bar type with an object mesh. \sa setBarType() + */ +void Q3DScatter::setMeshFileName(const QString &objFileName) +{ + d_ptr->m_shared->setMeshFileName(objFileName); +} + +/*! + * \property Q3DScatter::fontSize + * + * \a fontsize Size of the font. + * + * Sets font size. + */ +void Q3DScatter::setFontSize(float fontsize) +{ + d_ptr->m_shared->setFontSize(fontsize); +} + +float Q3DScatter::fontSize() +{ + return d_ptr->m_shared->fontSize(); +} + +/*! + * \property Q3DScatter::font + * + * \a font QFont to be used for labels. \c Arial by default. + * + * Sets the font for labels. + */ +void Q3DScatter::setFont(const QFont &font) +{ + d_ptr->m_shared->setFont(font); +} + +QFont Q3DScatter::font() +{ + return d_ptr->m_shared->font(); +} + +/*! + * \property Q3DScatter::labelTransparency + * + * \a transparency Transparency level of labels from \c LabelTransparency. + * \c TransparencyFromTheme by default. + * + * Sets label transparency. + */ +void Q3DScatter::setLabelTransparency(LabelTransparency transparency) +{ + d_ptr->m_shared->setLabelTransparency(transparency); +} + +LabelTransparency Q3DScatter::labelTransparency() +{ + return d_ptr->m_shared->labelTransparency(); +} + +/*! + * \property Q3DScatter::gridVisible + * + * \a visible Flag to enable or disable grid. \c true by default. + * + * Sets grid drawing on or off. + */ +void Q3DScatter::setGridVisible(bool visible) +{ + d_ptr->m_shared->setGridEnabled(visible); +} + +bool Q3DScatter::isGridVisible() +{ + return d_ptr->m_shared->gridEnabled(); +} + +/*! + * \property Q3DScatter::backgroundVisible + * + * \a visible Flag to enable or disable background. \c true by default. + * + * Sets backround rendering on or off. + */ +void Q3DScatter::setBackgroundVisible(bool visible) +{ + d_ptr->m_shared->setBackgroundEnabled(visible); +} + +bool Q3DScatter::isBackgroundVisible() +{ + return d_ptr->m_shared->backgroundEnabled(); +} + +/*! + * \property Q3DScatter::shadowQuality + * + * \a quality Shadow quality from \c ShadowQuality. \c ShadowLow by default. + * + * Sets shadow quality. If setting ShadowQuality of a certain level fails, a level is lowered + * until it is successful and shadowQualityChanged signal is emitted for each time the change is done. + */ +void Q3DScatter::setShadowQuality(ShadowQuality quality) +{ + return d_ptr->m_shared->setShadowQuality(quality); +} + +ShadowQuality Q3DScatter::shadowQuality() +{ + return d_ptr->m_shared->shadowQuality(); +} + +QCategoryAxis *Q3DScatter::rowAxis() +{ + return reinterpret_cast<QCategoryAxis *>(d_ptr->m_shared->axisX()); +} + +QCategoryAxis *Q3DScatter::columnAxis() +{ + return reinterpret_cast<QCategoryAxis *>(d_ptr->m_shared->axisZ()); +} + +void Q3DScatter::setValueAxis(QAbstractAxis *axis) +{ + Q_ASSERT(axis); + + return d_ptr->m_shared->setAxisY(axis); +} + +QAbstractAxis *Q3DScatter::valueAxis() +{ + return d_ptr->m_shared->axisY(); +} + +void Q3DScatter::setDataProxy(QScatterDataProxy *proxy) +{ + d_ptr->m_shared->setDataProxy(proxy); +} + +QScatterDataProxy *Q3DScatter::dataProxy() +{ + return d_ptr->m_shared->dataProxy(); +} + +/*! + * \a tickCount How many ticks will be drawn. \c 5 by default. + * + * \a step How large a step each tick is. + * + * \a minimum Minimum value a bar in data set can have. Setting this correctly is especially + * important if values can be negative, or autoscaling won't work correctly. + * + * Sets tick count and step. Note; tickCount * step should be the maximum possible value of data + * set. + */ +void Q3DScatter::setTickCount(int tickCount, qreal step, qreal minimum) +{ + d_ptr->m_shared->setTickCount(GLint(tickCount), GLfloat(step), GLfloat(minimum)); +} + +Q3DScatterPrivate::Q3DScatterPrivate(Q3DScatter *q, QRect rect) + : q_ptr(q), + m_shared(new Scatter3DController(rect)) +{ +} + +Q3DScatterPrivate::~Q3DScatterPrivate() +{ + qDebug() << "Destroying Q3DScatterPrivate"; + delete m_shared; +} + +QT_DATAVIS3D_END_NAMESPACE diff --git a/src/datavis3d/engine/q3dscatter.h b/src/datavis3d/engine/q3dscatter.h new file mode 100644 index 00000000..13c50415 --- /dev/null +++ b/src/datavis3d/engine/q3dscatter.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DSCATTER_H +#define Q3DSCATTER_H + +#include "qdatavis3dnamespace.h" +#include "q3dwindow.h" +#include <QFont> + +QT_DATAVIS3D_BEGIN_NAMESPACE + +class Q3DScatterPrivate; +class LabelItem; +class QAbstractAxis; +class QCategoryAxis; +class QScatterDataProxy; + +class QT_DATAVIS3D_EXPORT Q3DScatter : public Q3DWindow +{ + Q_OBJECT + Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) + Q_PROPERTY(LabelTransparency labelTransparency READ labelTransparency WRITE setLabelTransparency) + Q_PROPERTY(ShadowQuality shadowQuality READ shadowQuality WRITE setShadowQuality) + Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(float fontSize READ fontSize WRITE setFontSize) + Q_PROPERTY(bool gridVisible READ isGridVisible WRITE setGridVisible) + Q_PROPERTY(bool backgroundVisible READ isBackgroundVisible WRITE setBackgroundVisible) + Q_ENUMS(SelectionMode) + Q_ENUMS(ShadowQuality) + Q_ENUMS(LabelTransparency) + +public: + explicit Q3DScatter(); + ~Q3DScatter(); + + // bar thickness, spacing between bars, and is spacing relative to thickness or absolute + // y -component sets the thickness/spacing of z -direction + // With relative 0.0f means side-to-side, 1.0f = one thickness in between + void setBarSpecs(QSizeF thickness = QSizeF(1.0f, 1.0f), + QSizeF spacing = QSizeF(1.0f, 1.0f), + bool relative = true); + + // bar type; bars (=cubes), pyramids, cones, cylinders, etc. + void setBarType(BarStyle style, bool smooth = false); + + // how many samples per row and column, and names for axes + // TODO: This defines the data window, needs additional parameters startRow, startColumn + void setupSampleSpace(int samplesRow, int samplesColumn); + + // Select preset camera placement + void setCameraPreset(CameraPreset preset); + + // Set camera rotation if you don't want to use the presets (in horizontal (-180...180) and + // vertical (0...90) (or (-90...90) if there are negative values) angles and distance in + // percentage (10...500)) + void setCameraPosition(qreal horizontal, qreal vertical, int distance = 100); + + // Set theme (bar colors, shaders, window color, background colors, light intensity and text + // colors are affected) + void setTheme(ColorTheme theme); + + // Set color if you don't want to use themes. Set uniform to false if you want the (height) + // color to change from bottom to top + void setBarColor(QColor baseColor, QColor heightColor, QColor depthColor, + bool uniform = true); + + // Set tick count and step. Note; tickCount * step should be the maximum possible value of data + // set. Minimum is the absolute minimum possible value a bar can have. This is especially + // important to set if values can be negative. + void setTickCount(int tickCount, qreal step, qreal minimum = 0.0f); + + // override bar type with own mesh + void setMeshFileName(const QString &objFileName); + // TODO: light placement API + + // Change selection mode; single bar, bar and row, bar and column, or all + void setSelectionMode(SelectionMode mode); + SelectionMode selectionMode(); + + // Set window title + void setWindowTitle(const QString &title); + QString windowTitle(); + + // Font size adjustment + void setFontSize(float fontsize); + float fontSize(); + + // Set font + void setFont(const QFont &font); + QFont font(); + + // Label transparency adjustment + void setLabelTransparency(LabelTransparency transparency); + LabelTransparency labelTransparency(); + + // Enable or disable background grid + void setGridVisible(bool visible); + bool isGridVisible(); + + // TODO: Do these need to be public? Where are they called from? + // Size + void setWidth(const int width); + void setHeight(const int height); + + // Enable or disable background mesh + void setBackgroundVisible(bool visible); + bool isBackgroundVisible(); + + // Adjust shadow quality + void setShadowQuality(ShadowQuality quality); + ShadowQuality shadowQuality(); + + // Axes - row & column axes are fixed to category axes, value axis can be + // customized. + QCategoryAxis *rowAxis(); + QCategoryAxis *columnAxis(); + void setValueAxis(QAbstractAxis *axis); + QAbstractAxis *valueAxis(); + + // Sets the data proxy. Assumes ownership of the data proxy. Deletes old proxy. + void setDataProxy(QtDataVis3D::QScatterDataProxy *proxy); + QScatterDataProxy *dataProxy(); + +public slots: + // Used to detect when shadow quality changes autonomously due to e.g. resizing. + void handleShadowQualityUpdate(ShadowQuality quality); + +signals: + // Signals shadow quality changes. + void shadowQualityChanged(ShadowQuality quality); + +protected: + void render(); + +#if defined(Q_OS_ANDROID) + void mouseDoubleClickEvent(QMouseEvent *event); + void touchEvent(QTouchEvent *event); +#endif + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void wheelEvent(QWheelEvent *event); + void resizeEvent(QResizeEvent *event); + +private: + QScopedPointer<Q3DScatterPrivate> d_ptr; + Q_DISABLE_COPY(Q3DScatter) +}; + +QT_DATAVIS3D_END_NAMESPACE + +#endif diff --git a/src/datavis3d/engine/q3dscatter_p.h b/src/datavis3d/engine/q3dscatter_p.h new file mode 100644 index 00000000..45ebd4a7 --- /dev/null +++ b/src/datavis3d/engine/q3dscatter_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVis3D 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 Q3DSCATTER_p_H +#define Q3DSCATTER_p_H + +#include "scatter3dcontroller_p.h" +#include "qdatavis3dnamespace.h" + +QT_DATAVIS3D_BEGIN_NAMESPACE + +class Q3DScatter; + +class Q3DScatterPrivate : public QObject +{ +public: + Q3DScatterPrivate(Q3DScatter *q, QRect rect); + ~Q3DScatterPrivate(); + + Q3DScatter *q_ptr; + Scatter3DController *m_shared; +}; + +QT_DATAVIS3D_END_NAMESPACE + +#endif diff --git a/src/datavis3d/engine/scatter3dcontroller.cpp b/src/datavis3d/engine/scatter3dcontroller.cpp new file mode 100644 index 00000000..50c390b5 --- /dev/null +++ b/src/datavis3d/engine/scatter3dcontroller.cpp @@ -0,0 +1,514 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scatter3dcontroller_p.h" +#include "scatter3drenderer_p.h" +#include "camerahelper_p.h" +#include "utils_p.h" +#include "qabstractaxis_p.h" +#include "qvalueaxis.h" +#include "qcategoryaxis.h" +#include "qscatterdataproxy_p.h" + +#include <QMatrix4x4> +#include <QMouseEvent> +#include <QThread> +#include <QDebug> +#include <qmath.h> + +QT_DATAVIS3D_BEGIN_NAMESPACE + +Scatter3DController::Scatter3DController(QRect boundRect) + : Abstract3DController(boundRect), + m_isInitialized(false), + m_rowCount(0), + m_columnCount(0), + m_mouseState(MouseNone), + m_mousePos(QPoint(0, 0)), + m_selectionMode(ModeBar), + m_isSlicingActivated(false), + m_isBarSpecRelative(true), + m_barThickness(QSizeF(0.75f, 0.75f)), + m_barSpacing(m_barThickness * 3.0f), + m_objFile(QStringLiteral(":/defaultMeshes/bar")), + m_font(QFont(QStringLiteral("Arial"))), + m_isGridEnabled(true), + m_isBackgroundEnabled(true), + m_tickCount(0), + m_tickStep(0), + m_tickMinimum(0.0f), + m_renderer(0), + m_data(0), + m_valuesDirty(false) +{ + // Default axes. Only Y axis can actually be changed by user. + setAxisX(new QCategoryAxis()); + setAxisY(new QValueAxis()); + setAxisZ(new QCategoryAxis()); + + setDataProxy(new QScatterDataProxy); +} + +Scatter3DController::~Scatter3DController() +{ + delete m_data; +} + +void Scatter3DController::initializeOpenGL() +{ + // Initialization is called multiple times when Qt Quick components are used + if (m_isInitialized) + return; + + m_renderer = new Scatter3DRenderer(this); + if (m_axisX) + m_axisX->d_ptr->setDrawer(m_renderer->drawer()); + if (m_axisY) + m_axisY->d_ptr->setDrawer(m_renderer->drawer()); + if (m_axisZ) + m_axisZ->d_ptr->setDrawer(m_renderer->drawer()); + m_isInitialized = true; +} + +void Scatter3DController::render(const GLuint defaultFboHandle) +{ + if (!m_isInitialized) + return; + + // TODO do not give the entire data array, just the data window + // TODO Would it be enough to just mutex cache update in renderer? + // TODO --> Only if there is no need to store m_dataProxy for later, e.g. for string formatting + // TODO Also, m_valuesDirty flag setting needs to be under same mutex + QMutexLocker(m_data->mutex()); + m_renderer->render(m_data, m_valuesDirty, m_cameraHelper, m_axisX->d_ptr->titleItem(), + m_axisY->d_ptr->titleItem(), m_axisZ->d_ptr->titleItem(), defaultFboHandle); + + m_valuesDirty = false; + +} + +QMatrix4x4 Scatter3DController::calculateViewMatrix(int zoom, int viewPortWidth, int viewPortHeight, bool showUnder) +{ + return m_cameraHelper->calculateViewMatrix(m_mousePos, + zoom, + viewPortWidth, + viewPortHeight, + showUnder); +} + +bool Scatter3DController::isSlicingActive() +{ + return m_isSlicingActivated; +} + +void Scatter3DController::setSlicingActive(bool isSlicing) +{ + m_isSlicingActivated = isSlicing; + emit slicingActiveChanged(m_isSlicingActivated); +} + +Scatter3DController::MouseState Scatter3DController::mouseState() +{ + return m_mouseState; +} + + +#if defined(Q_OS_ANDROID) +void Scatter3DController::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (!m_isSlicingActivated) { + m_mouseState = Scatter3DController::MouseOnScene; + // update mouse positions to prevent jumping when releasing or repressing a button + m_mousePos = event->pos(); + } +} + +void Scatter3DController::touchEvent(QTouchEvent *event) +{ + static int prevDistance = 0; + + QList<QTouchEvent::TouchPoint> points; + points = event->touchPoints(); + + if (points.count() == 2) { + m_mouseState = Scatter3DController::MouseOnPinch; + + QPointF distance = points.at(0).pos() - points.at(1).pos(); + int newDistance = distance.manhattanLength(); + int zoomRate = 1; + int zoomLevel = m_zoomLevel; + if (zoomLevel > 100) + zoomRate = 5; + if (newDistance > prevDistance) + zoomLevel += zoomRate; + else + zoomLevel -= zoomRate; + if (zoomLevel > 500) + zoomLevel = 500; + else if (zoomLevel < 10) + zoomLevel = 10; + setZoomLevel(zoomLevel); + prevDistance = newDistance; + //qDebug() << "distance" << distance.manhattanLength(); + } +} +#endif + +void Scatter3DController::mousePressEvent(QMouseEvent *event, const QPoint &mousePos) +{ + QRect mainViewPort = m_renderer->mainViewPort(); + if (Qt::LeftButton == event->button()) { + if (m_isSlicingActivated) { + if (event->pos().x() <= mainViewPort.width() + && event->pos().y() <= mainViewPort.height()) { + m_mouseState = Scatter3DController::MouseOnOverview; + //qDebug() << "Mouse pressed on overview"; + } else { + m_mouseState = Scatter3DController::MouseOnZoom; + //qDebug() << "Mouse pressed on zoom"; + } + } else { +#if !defined(Q_OS_ANDROID) + m_mouseState = Scatter3DController::MouseOnScene; +#else + m_mouseState = Scatter3DController::MouseRotating; +#endif + // update mouse positions to prevent jumping when releasing or repressing a button + m_mousePos = event->pos(); + //qDebug() << "Mouse pressed on scene"; + } + } else if (Qt::MiddleButton == event->button()) { + // reset rotations + m_mousePos = QPoint(0, 0); + } else if (Qt::RightButton == event->button()) { +#if !defined(Q_OS_ANDROID) + m_mouseState = Scatter3DController::MouseRotating; +#else + m_mouseState = Scatter3DController::MouseOnScene; +#endif + // update mouse positions to prevent jumping when releasing or repressing a button + m_mousePos = mousePos; //event->pos(); + } + m_cameraHelper->updateMousePos(m_mousePos); +} + +void Scatter3DController::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos) +{ + Q_UNUSED(event); + if (Scatter3DController::MouseRotating == m_mouseState) { + // update mouse positions to prevent jumping when releasing or repressing a button + m_mousePos = mousePos; //event->pos(); + m_cameraHelper->updateMousePos(mousePos); //event->pos()); + } + m_mouseState = Scatter3DController::MouseNone; +} + +void Scatter3DController::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos) +{ + Q_UNUSED(event); + if (Scatter3DController::MouseRotating == m_mouseState) + m_mousePos = mousePos; //event->pos(); +} + +void Scatter3DController::wheelEvent(QWheelEvent *event) +{ + int zoomLevel = m_zoomLevel; + if (zoomLevel > 100) + zoomLevel += event->angleDelta().y() / 12; + else if (zoomLevel > 50) + zoomLevel += event->angleDelta().y() / 60; + else + zoomLevel += event->angleDelta().y() / 120; + if (zoomLevel > 500) + zoomLevel = 500; + else if (zoomLevel < 10) + zoomLevel = 10; + + setZoomLevel(zoomLevel); +} + +// TODO: abstract renderer should have accessor for Drawer instead +Drawer *Scatter3DController::drawer() +{ + if (m_renderer) + return m_renderer->drawer(); + else + return 0; +} + +void Scatter3DController::setDataProxy(QScatterDataProxy *proxy) +{ + delete m_data; + m_data = proxy; + + QObject::connect(m_data, &QScatterDataProxy::arrayReset, + this, &Scatter3DController::handleArrayReset); + QObject::connect(m_data, &QScatterDataProxy::itemsAdded, + this, &Scatter3DController::handleItemsAdded); + QObject::connect(m_data, &QScatterDataProxy::itemsChanged, + this, &Scatter3DController::handleItemsChanged); + QObject::connect(m_data, &QScatterDataProxy::itemsRemoved, + this, &Scatter3DController::handleItemsRemoved); + QObject::connect(m_data, &QScatterDataProxy::itemsInserted, + this, &Scatter3DController::handleItemsInserted); + + // emit something? Renderer might be interested? +} + +QScatterDataProxy *Scatter3DController::dataProxy() +{ + return m_data; +} + +void Scatter3DController::handleArrayReset() +{ + setSlicingActive(false); + handleLimitChange(); + m_valuesDirty = true; +} + +void Scatter3DController::handleItemsAdded(int startIndex, int count) +{ + Q_UNUSED(startIndex) + Q_UNUSED(count) + // TODO should dirty only affected values? + handleLimitChange(); + m_valuesDirty = true; +} + +void Scatter3DController::handleItemsChanged(int startIndex, int count) +{ + Q_UNUSED(startIndex) + Q_UNUSED(count) + // TODO should dirty only affected values? + handleLimitChange(); + m_valuesDirty = true; +} + +void Scatter3DController::handleItemsRemoved(int startIndex, int count) +{ + Q_UNUSED(startIndex) + Q_UNUSED(count) + // TODO should dirty only affected values? + handleLimitChange(); + m_valuesDirty = true; +} + +void Scatter3DController::handleItemsInserted(int startIndex, int count) +{ + Q_UNUSED(startIndex) + Q_UNUSED(count) + // TODO should dirty only affected values? + handleLimitChange(); + m_valuesDirty = true; +} + +void Scatter3DController::setBarSpecs(QSizeF thickness, QSizeF spacing, bool relative) +{ + m_barThickness = thickness; + m_barSpacing = spacing; + m_isBarSpecRelative = relative; + emit barSpecsChanged(thickness, spacing, relative); +} + +QSizeF Scatter3DController::barThickness() +{ + return m_barThickness; +} + +QSizeF Scatter3DController::barSpacing() +{ + return m_barSpacing; +} + +bool Scatter3DController::isBarSpecRelative() +{ + return m_isBarSpecRelative; +} + +QString Scatter3DController::objFile() +{ + return m_objFile; +} + +void Scatter3DController::setBarType(BarStyle style, bool smooth) +{ + if (style == Bars) { + if (smooth) + m_objFile = QStringLiteral(":/defaultMeshes/barSmooth"); + else + m_objFile = QStringLiteral(":/defaultMeshes/bar"); + } else if (style == Pyramids) { + if (smooth) + m_objFile = QStringLiteral(":/defaultMeshes/pyramidSmooth"); + else + m_objFile = QStringLiteral(":/defaultMeshes/pyramid"); + } else if (style == Cones) { + if (smooth) + m_objFile = QStringLiteral(":/defaultMeshes/coneSmooth"); + else + m_objFile = QStringLiteral(":/defaultMeshes/cone"); + } else if (style == Cylinders) { + if (smooth) + m_objFile = QStringLiteral(":/defaultMeshes/cylinderSmooth"); + else + m_objFile = QStringLiteral(":/defaultMeshes/cylinder"); + } else if (style == BevelBars) { + if (smooth) + m_objFile = QStringLiteral(":/defaultMeshes/bevelbarSmooth"); + else + m_objFile = QStringLiteral(":/defaultMeshes/bevelbar"); + } + + emit objFileChanged(m_objFile); +} + +void Scatter3DController::setMeshFileName(const QString &objFileName) +{ + m_objFile = objFileName; + + emit objFileChanged(m_objFile); +} + +// TODO: This sets data window. Needs more parameters, now assumes window always starts at 0,0. +void Scatter3DController::setupSampleSpace(int rowCount, int columnCount) +{ + // Disable zoom mode if we're in it (causes crash if not, as zoom selection is deleted) + setSlicingActive(false); + + m_rowCount = rowCount; + m_columnCount = columnCount; + + emit sampleSpaceChanged(rowCount, columnCount); +} + + +void Scatter3DController::setSelectionMode(SelectionMode mode) +{ + m_selectionMode = mode; + // Disable zoom if selection mode changes + setSlicingActive(false); + emit selectionModeChanged(m_selectionMode); +} + +QPoint Scatter3DController::mousePosition() +{ + return m_mousePos; +} + +SelectionMode Scatter3DController::selectionMode() +{ + return m_selectionMode; +} + +void Scatter3DController::setFontSize(float fontsize) +{ + m_font.setPointSizeF(fontsize); + emit fontChanged(m_font); +} + +float Scatter3DController::fontSize() +{ + return m_font.pointSizeF(); +} + +void Scatter3DController::setFont(const QFont &font) +{ + m_font = font; + emit fontChanged(m_font); +} + +QFont Scatter3DController::font() +{ + return m_font; +} + +void Scatter3DController::setGridEnabled(bool enable) +{ + m_isGridEnabled = enable; + emit gridEnabledChanged(m_isGridEnabled); +} + +bool Scatter3DController::gridEnabled() +{ + return m_isGridEnabled; +} + +void Scatter3DController::setBackgroundEnabled(bool enable) +{ + m_isBackgroundEnabled = enable; + emit backgroundEnabledChanged(m_isBackgroundEnabled); +} + +bool Scatter3DController::backgroundEnabled() +{ + return m_isBackgroundEnabled; +} + +void Scatter3DController::setTickCount(GLint tickCount, GLfloat step, GLfloat minimum) +{ + m_tickCount = tickCount; + m_tickStep = step; + m_tickMinimum = minimum; + emit tickCountChanged(m_tickCount, m_tickStep, m_tickMinimum); +} + +int Scatter3DController::columnCount() +{ + return m_columnCount; +} + +int Scatter3DController::rowCount() +{ + return m_rowCount; +} + +void Scatter3DController::handleLimitChange() +{ + QPair<GLfloat, GLfloat> limits = m_data->dptr()->limitValues(); + // TODO do something with changed limits? + //m_heightNormalizer = (GLfloat)qMax(qFabs(limits.second), qFabs(limits.first)); + //calculateHeightAdjustment(limits); + + emit limitsChanged(limits); +} + +QT_DATAVIS3D_END_NAMESPACE diff --git a/src/datavis3d/engine/scatter3dcontroller_p.h b/src/datavis3d/engine/scatter3dcontroller_p.h new file mode 100644 index 00000000..7f90671d --- /dev/null +++ b/src/datavis3d/engine/scatter3dcontroller_p.h @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVis3D 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 Q3DSCATTERCONTROLLER_p_H +#define Q3DSCATTERCONTROLLER_p_H + +#include <QtCore/QSize> +#include <QtCore/QObject> +#include <QWindow> + +#include "abstract3dcontroller_p.h" +#include "datavis3dglobal_p.h" + +//#define DISPLAY_RENDER_SPEED + +class QFont; +class QPoint; +class QSizeF; + +QT_DATAVIS3D_BEGIN_NAMESPACE + +class Scatter3DRenderer; +class LabelItem; +class QScatterDataProxy; + +class QT_DATAVIS3D_EXPORT Scatter3DController : public Abstract3DController +{ + Q_OBJECT + +public: + enum SelectionType { + SelectionNone = 0, + SelectionBar, + SelectionRow, + SelectionColumn + }; + +private: + bool m_isInitialized; + + // Data + int m_rowCount; + int m_columnCount; + + // Interaction + MouseState m_mouseState; + QPoint m_mousePos; + SelectionMode m_selectionMode; + bool m_isSlicingActivated; + + bool m_isBarSpecRelative; + QSizeF m_barThickness; + QSizeF m_barSpacing; + + // Look'n'Feel + QString m_objFile; + QFont m_font; + bool m_isGridEnabled; + bool m_isBackgroundEnabled; + GLint m_tickCount; + GLfloat m_tickStep; + GLfloat m_tickMinimum; + + Scatter3DRenderer *m_renderer; + QScatterDataProxy *m_data; + bool m_valuesDirty; + +public: + explicit Scatter3DController(QRect rect); + ~Scatter3DController(); + + void initializeOpenGL(); + void render(const GLuint defaultFboHandle = 0); + + int columnCount(); + int rowCount(); + + MouseState mouseState(); + QPoint mousePosition(); + + bool isSlicingActive(); + void setSlicingActive(bool isSlicing); + + QMatrix4x4 calculateViewMatrix(int zoom, int viewPortWidth, int viewPortHeight, bool showUnder = false); + + // bar thickness, spacing between bars, and is spacing relative to thickness or absolute + // y -component sets the thickness/spacing of z -direction + // With relative 0.0f means side-to-side, 1.0f = one thickness in between + void setBarSpecs(QSizeF thickness = QSizeF(1.0f, 1.0f), + QSizeF spacing = QSizeF(1.0f, 1.0f), + bool relative = true); + QSizeF barThickness(); + QSizeF barSpacing(); + bool isBarSpecRelative(); + + // bar type; bars (=cubes), pyramids, cones, cylinders, etc. + void setBarType(BarStyle style, bool smooth = false); + QString objFile(); + + // override bar type with own mesh + void setMeshFileName(const QString &objFileName); + + // how many samples per row and column, and names for axes + void setupSampleSpace(int samplesRow, int samplesColumn); + + // Set tick count and step. Note; tickCount * step should be the maximum possible value of data + // set. Minimum is the absolute minimum possible value a bar can have. This is especially + // important to set if values can be negative. + void setTickCount(GLint tickCount, GLfloat step, GLfloat minimum = 0.0f); + + // TODO: light placement API + + // Change selection mode; single bar, bar and row, bar and column, or all + void setSelectionMode(SelectionMode mode); + SelectionMode selectionMode(); + + // Font size adjustment + void setFontSize(float fontsize); + float fontSize(); + + // Set font + void setFont(const QFont &font); + QFont font(); + + // Enable or disable background grid + void setGridEnabled(bool enable); + bool gridEnabled(); + + // Enable or disable background mesh + void setBackgroundEnabled(bool enable); + bool backgroundEnabled(); + +#if defined(Q_OS_ANDROID) + void mouseDoubleClickEvent(QMouseEvent *event); + void touchEvent(QTouchEvent *event); +#endif + void mousePressEvent(QMouseEvent *event, const QPoint &mousePos); + void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos); + void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos); + void wheelEvent(QWheelEvent *event); + + // TODO: abstract renderer should have accessor for Drawer instead + virtual Drawer *drawer(); + + // Sets the data proxy. Assumes ownership of the data proxy. Deletes old proxy. + void setDataProxy(QScatterDataProxy *proxy); + QScatterDataProxy *dataProxy(); + +public slots: + void handleArrayReset(); + void handleItemsAdded(int startIndex, int count); + void handleItemsChanged(int startIndex, int count); + void handleItemsRemoved(int startIndex, int count); + void handleItemsInserted(int startIndex, int count); + +signals: + void selectionModeChanged(SelectionMode mode); + void slicingActiveChanged(bool isSlicing); + void limitsChanged(QPair<GLfloat, GLfloat> limits); + void sampleSpaceChanged(int samplesRow, int samplesColumn); + void barSpecsChanged(QSizeF thickness, QSizeF spacing, bool relative); + void objFileChanged(QString fileName); + void fontChanged(QFont font); + void gridEnabledChanged(bool enable); + void backgroundEnabledChanged(bool enable); + void tickCountChanged(GLint tickCount, GLfloat step, GLfloat minimum); + +private: + void handleLimitChange(); + + Q_DISABLE_COPY(Scatter3DController) + + friend class DeclarativeBars; + friend class DeclarativeBarsRenderer; +}; + + +QT_DATAVIS3D_END_NAMESPACE + +#endif diff --git a/src/datavis3d/engine/scatter3drenderer.cpp b/src/datavis3d/engine/scatter3drenderer.cpp new file mode 100644 index 00000000..0847df9c --- /dev/null +++ b/src/datavis3d/engine/scatter3drenderer.cpp @@ -0,0 +1,1649 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scatter3drenderer_p.h" +#include "scatter3dcontroller_p.h" +#include "camerahelper_p.h" +#include "shaderhelper_p.h" +#include "objecthelper_p.h" +#include "texturehelper_p.h" +#include "theme_p.h" +#include "utils_p.h" +#include "drawer_p.h" +#include "qabstractaxis_p.h" +#include "qbardataitem.h" + +#include <QMatrix4x4> +#include <QMouseEvent> +#include <QThread> +#include <qmath.h> +#include <QDebug> +#include <QMutexLocker> + +// Uncommenting this draws the shadow map with wider FOV than scene itself, making the light +// seem to be closer to scene than it actually is. This way shadows look slightly better (to me anyway) +#define USE_WIDER_SHADOWS + +// You can verify that depth buffer drawing works correctly by uncommenting this. +// You should see the scene from where the light is +//#define SHOW_DEPTH_TEXTURE_SCENE + +#ifdef DISPLAY_RENDER_SPEED +#include <QTime> +#endif + +QT_DATAVIS3D_BEGIN_NAMESPACE + +#define DISPLAY_FULL_DATA_ON_SELECTION // Append selection value text with row and column labels + +const GLfloat gridLineWidth = 0.005f; +static QVector3D selectionSkipColor = QVector3D(255, 255, 255); // Selection texture's background color + +Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) + : QObject(controller), + m_controller(controller), + m_hasNegativeValues(false), + m_selectedBar(0), + m_previouslySelectedBar(0), + m_tickCount(0), + m_tickStep(0), + m_xFlipped(false), + m_zFlipped(false), + m_updateLabels(false), + m_barShader(0), + m_depthShader(0), + m_selectionShader(0), + m_backgroundShader(0), + m_labelShader(0), + m_barObj(0), + m_backgroundObj(0), + m_gridLineObj(0), + m_labelObj(0), + m_drawer(new Drawer(m_cachedTheme, m_cachedFont, m_cachedLabelTransparency)), + m_bgrTexture(0), + m_depthTexture(0), + m_selectionTexture(0), + m_depthFrameBuffer(0), + m_selectionFrameBuffer(0), + m_selectionDepthBuffer(0), + m_shadowQualityToShader(33.3f), + m_autoScaleAdjustment(1.0f), + m_heightNormalizer(1.0f), + m_yAdjustment(0.0f), + m_rowWidth(0), + m_columnDepth(0), + m_maxDimension(0), + m_scaleX(0), + m_scaleZ(0), + m_scaleFactor(0), + m_maxSceneSize(40.0), + m_selection(selectionSkipColor), + m_areaSize(QSizeF(1.0f, 1.0f)), + m_hasHeightAdjustmentChanged(true), + m_dataProxy(0), + m_valueUpdateNeeded(false) + #ifdef DISPLAY_RENDER_SPEED + , m_isFirstFrame(true), + m_numFrames(0) + #endif +{ + m_dummyRenderItem.setRenderer(this); + + // Listen to changes in the drawer + QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Scatter3DRenderer::updateTextures); + + // Listen to changes in the controller + QObject::connect(m_controller, &Scatter3DController::themeChanged, this, + &Scatter3DRenderer::updateTheme); + QObject::connect(m_controller, &Scatter3DController::selectionModeChanged, this, + &Scatter3DRenderer::updateSelectionMode); + //QObject::connect(m_controller, &Scatter3DController::limitsChanged, this, + // &Scatter3DRenderer::updateLimits); + QObject::connect(m_controller, &Scatter3DController::barSpecsChanged, this, + &Scatter3DRenderer::updateBarSpecs); + QObject::connect(m_controller, &Scatter3DController::objFileChanged, this, + &Scatter3DRenderer::updateMeshFileName); + QObject::connect(m_controller, &Scatter3DController::boundingRectChanged, this, + &Scatter3DRenderer::updateBoundingRect); + QObject::connect(m_controller, &Scatter3DController::sizeChanged, this, + &Scatter3DRenderer::updateBoundingRect); + QObject::connect(m_controller, &Scatter3DController::positionChanged, this, + &Scatter3DRenderer::updatePosition); + QObject::connect(m_controller, &Scatter3DController::fontChanged, this, + &Scatter3DRenderer::updateFont); + QObject::connect(m_controller, &Scatter3DController::labelTransparencyUpdated, this, + &Scatter3DRenderer::updateLabelTransparency); + QObject::connect(m_controller, &Scatter3DController::gridEnabledChanged, this, + &Scatter3DRenderer::updateGridEnabled); + QObject::connect(m_controller, &Scatter3DController::backgroundEnabledChanged, this, + &Scatter3DRenderer::updateBackgroundEnabled); + QObject::connect(m_controller, &Scatter3DController::shadowQualityChanged, this, + &Scatter3DRenderer::updateShadowQuality); + QObject::connect(m_controller, &Scatter3DController::tickCountChanged, this, + &Scatter3DRenderer::updateTickCount); + QObject::connect(m_controller, &Scatter3DController::zoomLevelChanged, this, + &Scatter3DRenderer::updateZoomLevel); + + updateTheme(m_controller->theme()); + updateSelectionMode(m_controller->selectionMode()); + //updateLimits(m_controller->limits()); + updateZoomLevel(m_controller->zoomLevel()); + updateBarSpecs(m_controller->barThickness(), m_controller->barSpacing(), + m_controller->isBarSpecRelative()); + updateMeshFileName(m_controller->objFile()); + updateFont(m_controller->font()); + updateLabelTransparency(m_controller->labelTransparency()); + updateGridEnabled(m_controller->gridEnabled()); + updateBackgroundEnabled(m_controller->backgroundEnabled()); + + initializeOpenGL(); + + updateBoundingRect(m_controller->boundingRect()); + updateShadowQuality(m_controller->shadowQuality()); + + // TODO: Protect with mutex or redesign how axes create label items? + if (m_controller->axisX()) + m_controller->axisX()->d_ptr->setDrawer(m_drawer); + if (m_controller->axisY()) + m_controller->axisY()->d_ptr->setDrawer(m_drawer); + if (m_controller->axisZ()) + m_controller->axisZ()->d_ptr->setDrawer(m_drawer); +} + +Scatter3DRenderer::~Scatter3DRenderer() +{ + m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer); + m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer); + m_textureHelper->deleteTexture(&m_selectionTexture); + m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer); + m_textureHelper->deleteTexture(&m_bgrTexture); + delete m_barShader; + delete m_depthShader; + delete m_selectionShader; + delete m_backgroundShader; + delete m_barObj; + delete m_backgroundObj; + delete m_gridLineObj; + delete m_textureHelper; + delete m_drawer; +} + +void Scatter3DRenderer::initializeOpenGL() +{ + initializeOpenGLFunctions(); + + m_textureHelper = new TextureHelper(); + m_drawer->initializeOpenGL(); + + // Initialize shaders +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + if (!m_cachedTheme.m_uniformColor) { + initShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); + } else { + initShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } else { + if (!m_cachedTheme.m_uniformColor) { + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); + } else { + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } +#else + if (!m_cachedTheme.m_uniformColor) { + initShaders(QStringLiteral(":/shaders/vertexES2"), + QStringLiteral(":/shaders/fragmentColorOnYES2")); + } else { + initShaders(QStringLiteral(":/shaders/vertexES2"), + QStringLiteral(":/shaders/fragmentES2")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertexES2"), + QStringLiteral(":/shaders/fragmentES2")); +#endif + + initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), + QStringLiteral(":/shaders/fragmentLabel")); + +#if !defined(QT_OPENGL_ES_2) + // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. + initDepthShader(); +#endif + + // Init selection shader + initSelectionShader(); + + // Load default mesh + loadBarMesh(); + + // Load grid line mesh + loadGridLineMesh(); + + // Load label mesh + loadLabelMesh(); + + // Set OpenGL features + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + +#if !defined(QT_OPENGL_ES_2) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); +#endif + + // Set view port + glViewport(m_mainViewPort.x(), m_mainViewPort.y(), + m_mainViewPort.width(), m_mainViewPort.height()); + + // Resize in case we've missed resize events + // Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here + handleResize(); + + // Load background mesh (we need to be initialized first) + loadBackgroundMesh(); +} + +void Scatter3DRenderer::render(QScatterDataProxy *dataProxy, + bool valuesDirty, + CameraHelper *camera, + const LabelItem &xLabel, + const LabelItem &yLabel, + const LabelItem &zLabel, + const GLuint defaultFboHandle) +{ + Q_UNUSED(xLabel); + Q_UNUSED(yLabel); + Q_UNUSED(zLabel); + +#ifdef DISPLAY_RENDER_SPEED + // For speed computation + if (m_isFirstFrame) { + m_lastFrameTime.start(); + m_isFirstFrame = false; + } + + // Measure speed (as milliseconds per frame) + m_numFrames++; + if (m_lastFrameTime.elapsed() >= 1000) { // print only if last measurement was more than 1s ago + qDebug() << qreal(m_lastFrameTime.elapsed()) / qreal(m_numFrames) << "ms/frame (=" << qreal(m_numFrames) << "fps)"; + m_numFrames = 0; + m_lastFrameTime.restart(); + } +#endif + + m_dataProxy = dataProxy; + + // Update cached data window + // TODO Should data changes be notified via signal instead of reading data in render? + // TODO this cache initialization assumes data window starts at 0,0 offset from array + // Update cached values + if (valuesDirty /*|| m_valueUpdateNeeded*/) { + const QScatterDataArray &dataArray = m_dataProxy->array(); + int dataSize = dataArray.size(); + m_renderItemArray.resize(dataSize); + for (int i = 0; i < dataSize ; i++) { + qreal value = dataArray.at(i).value(); + m_renderItemArray[i].setValue(value); + m_renderItemArray[i].setScatterPosition(dataArray.at(i).scatterPosition()); + m_renderItemArray[i].setHeight(value / m_heightNormalizer); + //m_renderItemArray[i].setItemLabel(dataArray.at(i).label()); + calculateTranslation(m_renderItemArray[i]); + m_renderItemArray[i].setRenderer(this); + } + // m_valueUpdateNeeded = false; + } + + if (defaultFboHandle) { + glDepthMask(true); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } + + QVector3D clearColor = Utils::vectorFromColor(m_cachedTheme.m_windowColor); + glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (m_hasHeightAdjustmentChanged) { + // Set initial camera position. Also update if height adjustment has changed. + camera->setDefaultCameraOrientation(QVector3D(0.0f, 0.0f, 6.0f + zComp), + QVector3D(0.0f, -m_yAdjustment, zComp), + QVector3D(0.0f, 1.0f, 0.0f)); + m_hasHeightAdjustmentChanged = false; + } + + // Draw bars scene + drawScene(camera, defaultFboHandle); +} + +void Scatter3DRenderer::drawScene(CameraHelper *camera, + const GLuint defaultFboHandle) +{ + GLfloat backgroundRotation = 0; + + GLfloat barPos = 0; + GLfloat rowPos = 0; + + // Specify viewport + glViewport(m_mainViewPort.x(), m_mainViewPort.y(), + m_mainViewPort.width(), m_mainViewPort.height()); + + // Set up projection matrix + QMatrix4x4 projectionMatrix; + projectionMatrix.perspective(45.0f, (GLfloat)m_mainViewPort.width() + / (GLfloat)m_mainViewPort.height(), 0.1f, 100.0f); + + // Calculate view matrix + QMatrix4x4 viewMatrix = m_controller->calculateViewMatrix( + m_cachedZoomLevel * m_autoScaleAdjustment, + m_mainViewPort.width(), + m_mainViewPort.height()); + + // Calculate label flipping + if (viewMatrix.row(0).x() > 0) + m_zFlipped = false; + else + m_zFlipped = true; + if (viewMatrix.row(0).z() <= 0) + m_xFlipped = false; + else + m_xFlipped = true; + + // Calculate background rotation + if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() <= 0) + backgroundRotation = 270.0f; + else if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() > 0) + backgroundRotation = 180.0f; + else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(0).z() > 0) + backgroundRotation = 90.0f; + else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(0).z() <= 0) + backgroundRotation = 0.0f; + + // Get light position (rotate light with camera, a bit above it (as set in defaultLightPos)) + QVector3D lightPos = camera->calculateLightPosition(defaultLightPos); + + // // Map adjustment direction to model matrix scaling + GLfloat heightMultiplier = 1.0f; + GLfloat widthMultiplier = 1.0f; + GLfloat depthMultiplier = 1.0f; + GLfloat heightScaler = 1.0f; + GLfloat widthScaler = 1.0f; + GLfloat depthScaler = 1.0f; + // switch (m_adjustDirection) { + // case Q3DMaps::AdjustHeight: + // widthMultiplier = 0.0f; + // heightMultiplier = 1.0f; + // depthMultiplier = 0.0f; + // widthScaler = m_barThickness.x() / m_scaleFactor; + // heightScaler = 0.0f; + // depthScaler = m_barThickness.z() / m_scaleFactor; + // break; + // case Q3DMaps::AdjustWidth: + // widthMultiplier = 1.0f; + // heightMultiplier = 0.0f; + // depthMultiplier = 0.0f; + // widthScaler = 0.0f; + // heightScaler = m_barThickness.y() / m_scaleFactor; + // depthScaler = m_barThickness.z() / m_scaleFactor; + // break; + // case Q3DMaps::AdjustDepth: + // widthMultiplier = 0.0f; + // heightMultiplier = 0.0f; + // depthMultiplier = 1.0f; + // widthScaler = m_barThickness.x() / m_scaleFactor; + // heightScaler = m_barThickness.y() / m_scaleFactor; + // depthScaler = 0.0f; + // break; + // case Q3DMaps::AdjustRadius: + // widthMultiplier = 1.0f; + // heightMultiplier = 0.0f; + // depthMultiplier = 1.0f; + // widthScaler = 0.0f; + // heightScaler = m_barThickness.y() / m_scaleFactor; + // depthScaler = 0.0f; + // break; + // case Q3DMaps::AdjustAll: + // widthMultiplier = 1.0f; + // heightMultiplier = 1.0f; + // depthMultiplier = 1.0f; + // widthScaler = 0.0f; + // heightScaler = 0.0f; + // depthScaler = 0.0f; + // break; + // } + + // Introduce regardless of shadow quality to simplify logic + QMatrix4x4 depthViewMatrix; + QMatrix4x4 depthProjectionMatrix; + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + // Render scene into a depth texture for using with shadow mapping + // Bind depth shader + m_depthShader->bind(); + + // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows. + glViewport(m_mainViewPort.x(), m_mainViewPort.y(), + m_mainViewPort.width() * m_cachedShadowQuality, + m_mainViewPort.height() * m_cachedShadowQuality); + + // Enable drawing to framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); + glClear(GL_DEPTH_BUFFER_BIT); + + // Set front face culling to reduce self-shadowing issues + glCullFace(GL_FRONT); + + // Get the depth view matrix + // It may be possible to hack lightPos here if we want to make some tweaks to shadow + depthViewMatrix.lookAt(lightPos, QVector3D(0.0f, -m_yAdjustment, zComp), + QVector3D(0.0f, 1.0f, 0.0f)); + // TODO: Why does depthViewMatrix.column(3).y() goes to zero when we're directly above? That causes the scene to be not drawn from above -> must be fixed + //qDebug() << lightPos << depthViewMatrix << depthViewMatrix.column(3); + // Set the depth projection matrix +#ifdef USE_WIDER_SHADOWS + // Use this for a bit exaggerated shadows + depthProjectionMatrix.perspective(60.0f, (GLfloat)m_mainViewPort.width() + / (GLfloat)m_mainViewPort.height(), 3.0f, 100.0f); +#else + // Use these for normal shadows, with the light further away + depthProjectionMatrix = projectionMatrix; +#endif + // Draw bars to depth buffer + for (int bar = 0; bar < m_renderItemArray.size(); bar++) { + const ScatterRenderItem &item = m_renderItemArray.at(bar); + if (!item.value()) + continue; + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + modelMatrix.translate(item.translation().x(), + heightMultiplier * item.height() + heightScaler - m_yAdjustment, + item.translation().z()); + modelMatrix.scale(QVector3D(widthMultiplier * item.height() + widthScaler, + heightMultiplier * item.height() + heightScaler, + depthMultiplier * item.height() + depthScaler)); + + MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); + + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_depthShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, m_barObj->vertexBuf()); + glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0); + + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_barObj->elementBuf()); + + // Draw the triangles + glDrawElements(GL_TRIANGLES, m_barObj->indexCount(), GL_UNSIGNED_SHORT, (void *)0); + + // Free buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableVertexAttribArray(m_depthShader->posAtt()); + } + + // Disable drawing to framebuffer (= enable drawing to screen) + glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); + + // Reset culling to normal + glCullFace(GL_BACK); + + // Release depth shader + m_depthShader->release(); + + // Revert to original viewport + glViewport(m_mainViewPort.x(), m_mainViewPort.y(), + m_mainViewPort.width(), m_mainViewPort.height()); + +#if 0 // Use this if you want to see what is being drawn to the framebuffer + // You'll also have to comment out GL_COMPARE_R_TO_TEXTURE -line in texturehelper (if using it) + m_labelShader->bind(); + glEnable(GL_TEXTURE_2D); + QMatrix4x4 modelMatrix; + QMatrix4x4 viewmatrix; + viewmatrix.lookAt(QVector3D(0.0f, 0.0f, 2.5f + zComp), + QVector3D(0.0f, 0.0f, zComp), + QVector3D(0.0f, 1.0f, 0.0f)); + modelMatrix.translate(0.0, 0.0, zComp); + QMatrix4x4 MVPMatrix = projectionMatrix * viewmatrix * modelMatrix; + m_labelShader->setUniformValue(m_labelShader->MVP(), MVPMatrix); + m_drawer->drawObject(m_labelShader, m_labelObj, + m_depthTexture); + glDisable(GL_TEXTURE_2D); + m_labelShader->release(); +#endif + } +#endif + +#if 1 + // Skip selection mode drawing if we have no selection mode + if (m_cachedSelectionMode > ModeNone) { + // Bind selection shader + m_selectionShader->bind(); + + // Draw bars to selection buffer + glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); + glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used + glClearColor(selectionSkipColor.x() / 255, selectionSkipColor.y() / 255, + selectionSkipColor.z() / 255, 1.0f); // Set clear color to white (= skipColor) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer + glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled + GLint barIdxRed = 0; + GLint barIdxGreen = 0; + GLint barIdxBlue = 0; + for (int bar = 0; bar < m_renderItemArray.size(); bar++, barIdxRed++) { + const ScatterRenderItem &item = m_renderItemArray.at(bar); + if (!item.value()) + continue; + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + modelMatrix.translate(item.translation().x(), + heightMultiplier * item.height() + heightScaler - m_yAdjustment, + item.translation().z()); + modelMatrix.scale(QVector3D(widthMultiplier * item.height() + widthScaler, + heightMultiplier * item.height() + heightScaler, + depthMultiplier * item.height() + depthScaler)); + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + + if (barIdxRed > 0 && barIdxRed % 256 == 0) { + barIdxRed = 0; + barIdxGreen++; + } + if (barIdxGreen > 0 && barIdxGreen % 256 == 0) { + barIdxGreen = 0; + barIdxBlue++; + } + if (barIdxBlue > 255) + qFatal("Too many objects"); + + QVector3D barColor = QVector3D((GLfloat)barIdxRed / 255.0f, + (GLfloat)barIdxGreen / 255.0f, + (GLfloat)barIdxBlue / 255.0f); + + m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); + m_selectionShader->setUniformValue(m_selectionShader->color(), barColor); + + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_selectionShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, m_barObj->vertexBuf()); + glVertexAttribPointer(m_selectionShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0); + + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_barObj->elementBuf()); + + // Draw the triangles + glDrawElements(GL_TRIANGLES, m_barObj->indexCount(), GL_UNSIGNED_SHORT, (void *)0); + + // Free buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableVertexAttribArray(m_selectionShader->posAtt()); + } + glEnable(GL_DITHER); + + // Read color under cursor + // Read color under cursor + if (Scatter3DController::MouseOnScene == m_controller->mouseState()) + m_selection = Utils::getSelection(m_controller->mousePosition(), + m_cachedBoundingRect.height()); + + QMutexLocker locker(&m_mutex); + if (m_isSelectionPointRequestActive) { + m_isSelectionPointRequestActive = false; + m_selection = Utils::getSelection(m_selectionPointRequest, + m_cachedBoundingRect.height()); + emit selectionUpdated(m_selection); + } + locker.unlock(); + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); + + // Release selection shader + m_selectionShader->release(); + +#if 0 // Use this if you want to see what is being drawn to the framebuffer + m_labelShader->bind(); + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + QMatrix4x4 modelMatrix; + QMatrix4x4 viewmatrix; + viewmatrix.lookAt(QVector3D(0.0f, 0.0f, 2.0f + zComp), + QVector3D(0.0f, 0.0f, zComp), + QVector3D(0.0f, 1.0f, 0.0f)); + modelMatrix.translate(0.0, 0.0, zComp); + QMatrix4x4 MVPMatrix = projectionMatrix * viewmatrix * modelMatrix; + m_labelShader->setUniformValue(m_labelShader->MVP(), MVPMatrix); + m_drawer->drawObject(m_labelShader, m_labelObj, + m_selectionTexture); + glDisable(GL_TEXTURE_2D); + m_labelShader->release(); +#endif + } +#if 1 + // Bind bar shader + m_barShader->bind(); + + // Enable texture + glEnable(GL_TEXTURE_2D); + + // Draw bars + // TODO: Handle zoom by camera transformations + + bool barSelectionFound = false; + for (int bar = 0; bar < m_renderItemArray.size(); bar++) { + ScatterRenderItem &item = m_renderItemArray[bar]; + if (!item.value()) + continue; + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(item.translation().x(), + heightMultiplier * item.height() + heightScaler - m_yAdjustment, + item.translation().z()); + modelMatrix.scale(QVector3D(widthMultiplier * item.height() + widthScaler, + heightMultiplier * item.height() + heightScaler, + depthMultiplier * item.height() + depthScaler)); + itModelMatrix.scale(QVector3D(widthMultiplier * item.height() + widthScaler, + heightMultiplier * item.height() + heightScaler, + depthMultiplier * item.height() + depthScaler)); + +#ifdef SHOW_DEPTH_TEXTURE_SCENE + MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; +#else + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; +#endif + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + QVector3D baseColor = Utils::vectorFromColor(m_cachedTheme.m_baseColor); + QVector3D heightColor = Utils::vectorFromColor(m_cachedTheme.m_heightColor) * item.height(); + + QVector3D barColor = baseColor + heightColor; + + GLfloat lightStrength = m_cachedTheme.m_lightStrength; + if (m_cachedSelectionMode > ModeNone) { + Scatter3DController::SelectionType selectionType = isSelected(bar, m_selection); + switch (selectionType) { + case Scatter3DController::SelectionBar: { + barColor = Utils::vectorFromColor(m_cachedTheme.m_highlightBarColor); + lightStrength = m_cachedTheme.m_highlightLightStrength; + // Insert data to QDataItem. We have no ownership, don't delete the previous one + m_selectedBar = &item; + barSelectionFound = true; + break; + } + case Scatter3DController::SelectionNone: { + // Current bar is not selected, nor on a row or column + // do nothing + break; + } + default: { + // Unsupported selection mode + // do nothing + break; + } + } + } + + if (item.height() != 0) { + // Set shader bindings + m_barShader->setUniformValue(m_barShader->lightP(), lightPos); + m_barShader->setUniformValue(m_barShader->view(), viewMatrix); + m_barShader->setUniformValue(m_barShader->model(), modelMatrix); + m_barShader->setUniformValue(m_barShader->nModel(), + itModelMatrix.inverted().transposed()); + m_barShader->setUniformValue(m_barShader->MVP(), MVPMatrix); + m_barShader->setUniformValue(m_barShader->color(), barColor); + m_barShader->setUniformValue(m_barShader->ambientS(), m_cachedTheme.m_ambientStrength); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + // Set shadow shader bindings + m_barShader->setUniformValue(m_barShader->shadowQ(), m_shadowQualityToShader); + m_barShader->setUniformValue(m_barShader->depth(), depthMVPMatrix); + m_barShader->setUniformValue(m_barShader->lightS(), lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_barShader, m_barObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + m_barShader->setUniformValue(m_barShader->lightS(), lightStrength); + + // Draw the object + m_drawer->drawObject(m_barShader, m_barObj); + } + } + } + + // Release bar shader + m_barShader->release(); + + // Bind background shader + m_backgroundShader->bind(); + + glCullFace(GL_BACK); + + // Draw background + if (m_cachedIsBackgroundEnabled && m_backgroundObj) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(0.0f, 1.0f - m_yAdjustment, zComp); + modelMatrix.scale(QVector3D(m_rowWidth / m_scaleFactor, + 1.0f, + m_columnDepth / m_scaleFactor)); + modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); + itModelMatrix.scale(QVector3D(m_rowWidth / m_scaleFactor, + 1.0f, + m_columnDepth / m_scaleFactor)); + +#ifdef SHOW_DEPTH_TEXTURE_SCENE + MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; +#else + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; +#endif + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme.m_backgroundColor); + + // Set shader bindings + m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); + m_backgroundShader->setUniformValue(m_backgroundShader->view(), viewMatrix); + m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix); + m_backgroundShader->setUniformValue(m_backgroundShader->nModel(), + itModelMatrix.inverted().transposed()); + m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix); + m_backgroundShader->setUniformValue(m_backgroundShader->color(), backgroundColor); + m_backgroundShader->setUniformValue(m_backgroundShader->ambientS(), + m_cachedTheme.m_ambientStrength * 2.0f); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + // Set shadow shader bindings + m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(), + m_shadowQualityToShader); + m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix); + m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), + m_cachedTheme.m_lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_backgroundShader, m_backgroundObj); + } + } + + // Release background shader + m_backgroundShader->release(); + + // Disable textures + glDisable(GL_TEXTURE_2D); + + // Draw grid lines + if (m_cachedIsGridEnabled && m_heightNormalizer) { + // Bind bar shader + m_barShader->bind(); + + // Set unchanging shader bindings + QVector3D barColor = Utils::vectorFromColor(m_cachedTheme.m_gridLine); + m_barShader->setUniformValue(m_barShader->lightP(), lightPos); + m_barShader->setUniformValue(m_barShader->view(), viewMatrix); + m_barShader->setUniformValue(m_barShader->color(), barColor); + m_barShader->setUniformValue(m_barShader->ambientS(), m_cachedTheme.m_ambientStrength); + + // Floor lines: rows + for (GLfloat row = 0.0f; row <= m_cachedRowCount; row++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); + modelMatrix.translate(0.0f, -m_yAdjustment, + (m_columnDepth - rowPos) / m_scaleFactor + zComp); + modelMatrix.scale(QVector3D(m_rowWidth / m_scaleFactor, gridLineWidth, + gridLineWidth)); + itModelMatrix.scale(QVector3D(m_rowWidth / m_scaleFactor, gridLineWidth, + gridLineWidth)); + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + m_barShader->setUniformValue(m_barShader->model(), modelMatrix); + m_barShader->setUniformValue(m_barShader->nModel(), + itModelMatrix.inverted().transposed()); + m_barShader->setUniformValue(m_barShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + // Set shadow shader bindings + m_barShader->setUniformValue(m_barShader->shadowQ(), m_shadowQualityToShader); + m_barShader->setUniformValue(m_barShader->depth(), depthMVPMatrix); + m_barShader->setUniformValue(m_barShader->lightS(), + m_cachedTheme.m_lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_barShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + m_barShader->setUniformValue(m_barShader->lightS(), m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_barShader, m_gridLineObj); + } + } + + // Floor lines: columns + for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + barPos = (bar + 0.5f) * (m_cachedBarSpacing.width()); + modelMatrix.translate((m_rowWidth - barPos) / m_scaleFactor, + -m_yAdjustment, zComp); + modelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, + m_columnDepth / m_scaleFactor)); + itModelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, + m_columnDepth / m_scaleFactor)); + + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + m_barShader->setUniformValue(m_barShader->model(), modelMatrix); + m_barShader->setUniformValue(m_barShader->nModel(), + itModelMatrix.inverted().transposed()); + m_barShader->setUniformValue(m_barShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + // Set shadow shader bindings + m_barShader->setUniformValue(m_barShader->shadowQ(), m_shadowQualityToShader); + m_barShader->setUniformValue(m_barShader->depth(), depthMVPMatrix); + m_barShader->setUniformValue(m_barShader->lightS(), + m_cachedTheme.m_lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_barShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + m_barShader->setUniformValue(m_barShader->lightS(), m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_barShader, m_gridLineObj); + } + } + + // Wall lines: back wall + GLfloat heightStep = m_heightNormalizer / 5.0f; // default to 5 lines + GLfloat startLine; + + if (m_tickCount > 0) + heightStep = m_tickStep; + + if (m_hasNegativeValues) + startLine = -m_heightNormalizer; + else + startLine = heightStep; + + for (GLfloat lineHeight = startLine; lineHeight <= m_heightNormalizer; + lineHeight += heightStep) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + if (m_zFlipped) { + modelMatrix.translate(0.0f, + 2.0f * lineHeight / m_heightNormalizer - m_yAdjustment, + m_columnDepth / m_scaleFactor + zComp); + } else { + modelMatrix.translate(0.0f, + 2.0f * lineHeight / m_heightNormalizer - m_yAdjustment, + -m_columnDepth / m_scaleFactor + zComp); + } + modelMatrix.scale(QVector3D(m_rowWidth / m_scaleFactor, gridLineWidth, + gridLineWidth)); + itModelMatrix.scale(QVector3D(m_rowWidth / m_scaleFactor, gridLineWidth, + gridLineWidth)); + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + m_barShader->setUniformValue(m_barShader->model(), modelMatrix); + m_barShader->setUniformValue(m_barShader->nModel(), + itModelMatrix.inverted().transposed()); + m_barShader->setUniformValue(m_barShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + // Set shadow shader bindings + m_barShader->setUniformValue(m_barShader->shadowQ(), m_shadowQualityToShader); + m_barShader->setUniformValue(m_barShader->depth(), depthMVPMatrix); + m_barShader->setUniformValue(m_barShader->lightS(), + m_cachedTheme.m_lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_barShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + m_barShader->setUniformValue(m_barShader->lightS(), m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_barShader, m_gridLineObj); + } + } + + // Wall lines: side wall + for (GLfloat lineHeight = startLine; lineHeight <= m_heightNormalizer; + lineHeight += heightStep) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + if (m_xFlipped) { + modelMatrix.translate(m_rowWidth / m_scaleFactor, + 2.0f * lineHeight / m_heightNormalizer - m_yAdjustment, + zComp); + } else { + modelMatrix.translate(-m_rowWidth / m_scaleFactor, + 2.0f * lineHeight / m_heightNormalizer - m_yAdjustment, + zComp); + } + modelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, + m_columnDepth / m_scaleFactor)); + itModelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, + m_columnDepth / m_scaleFactor)); + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + m_barShader->setUniformValue(m_barShader->model(), modelMatrix); + m_barShader->setUniformValue(m_barShader->nModel(), + itModelMatrix.inverted().transposed()); + m_barShader->setUniformValue(m_barShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + // Set shadow shader bindings + m_barShader->setUniformValue(m_barShader->shadowQ(), m_shadowQualityToShader); + m_barShader->setUniformValue(m_barShader->depth(), depthMVPMatrix); + m_barShader->setUniformValue(m_barShader->lightS(), + m_cachedTheme.m_lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_barShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + m_barShader->setUniformValue(m_barShader->lightS(), m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_barShader, m_gridLineObj); + } + } + + // Release bar shader + m_barShader->release(); + } + + // Handle zoom activation and label drawing + if (!barSelectionFound) { + // We have no ownership, don't delete. Just NULL the pointer. + m_selectedBar = NULL; + } else { + // Print value of selected bar + m_labelShader->bind(); + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + if (m_cachedLabelTransparency > TransparencyNone) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } +#ifndef DISPLAY_FULL_DATA_ON_SELECTION + // Draw just the value string of the selected bar + if (m_previouslySelectedBar != m_selectedBar || m_updateLabels) { + m_drawer->generateLabelTexture(m_selectedBar); + m_previouslySelectedBar = m_selectedBar; + } + + m_drawer->drawLabel(*m_selectedBar, m_selectedBar->labelItem(), + viewMatrix, projectionMatrix, + QVector3D(0.0f, m_yAdjustment, zComp), + QVector3D(0.0f, 0.0f, 0.0f), m_selectedBar->height(), + m_cachedSelectionMode, m_labelShader, + m_labelObj, m_camera, true); +#else + // Draw the value string followed by row label and column label + LabelItem &labelItem = m_selectedBar->selectionLabel(); + if (m_previouslySelectedBar != m_selectedBar || m_updateLabels || !labelItem.textureId()) { + QString labelText = m_selectedBar->label(); + // TODO More elaborate label? + m_drawer->generateLabelItem(labelItem, labelText); + m_previouslySelectedBar = m_selectedBar; + } + + m_drawer->drawLabel(*m_selectedBar, labelItem, viewMatrix, projectionMatrix, + QVector3D(0.0f, m_yAdjustment, zComp), + QVector3D(0.0f, 0.0f, 0.0f), m_selectedBar->height(), + m_cachedSelectionMode, m_labelShader, + m_labelObj, camera, true, false); +#endif + glDisable(GL_TEXTURE_2D); + if (m_cachedLabelTransparency > TransparencyNone) + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + + // Release label shader + m_labelShader->release(); + + // Reset label update flag; they should have been updated when we get here + m_updateLabels = false; + } + + // Draw axis labels + // TODO: Calculations done temporarily here. When optimizing, move to after data set addition? Keep drawing of the labels here. + // Bind label shader + m_labelShader->bind(); + + glEnable(GL_TEXTURE_2D); + if (m_cachedLabelTransparency > TransparencyNone) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + // Calculate the positions for row and column labels and store them into QDataItems (and QDataRows?) + for (int row = 0; row != m_cachedRowCount; row++) { + if (m_controller->axisX()->d_ptr->labelItems().size() > row) { + // Go through all rows and get position of max+1 or min-1 column, depending on x flip + // We need only positions for them, labels have already been generated at QDataSetPrivate. Just add LabelItems + rowPos = (row + 1) * (m_cachedBarSpacing.height()); + barPos = 0; + GLfloat rotLabelX = -90.0f; + GLfloat rotLabelY = 0.0f; + GLfloat rotLabelZ = 0.0f; + Qt::AlignmentFlag alignment = Qt::AlignRight; + if (m_zFlipped) + rotLabelY = 180.0f; + if (m_xFlipped) { + barPos = (m_cachedColumnCount + 1) * (m_cachedBarSpacing.width()); + alignment = Qt::AlignLeft; + } + QVector3D labelPos = QVector3D((m_rowWidth - barPos) / m_scaleFactor, + -m_yAdjustment + 0.005f, // raise a bit over background to avoid depth "glimmering" + (m_columnDepth - rowPos) / m_scaleFactor + zComp); + + // TODO: Try it; draw the label here + + m_dummyRenderItem.setTranslation(labelPos); + const LabelItem &axisLabelItem = *m_controller->axisX()->d_ptr->labelItems().at(row); + //qDebug() << "labelPos, row" << row + 1 << ":" << labelPos << dataSet->rowLabels().at(row); + + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + QVector3D(0.0f, m_yAdjustment, zComp), + QVector3D(rotLabelX, rotLabelY, rotLabelZ), + 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, camera, true, true, LabelMid, + alignment); + } + + } + for (int bar = 0; bar != m_cachedColumnCount; bar += 1) { + if (m_controller->axisZ()->d_ptr->labelItems().size() > bar) { + // Go through all columns and get position of max+1 or min-1 row, depending on z flip + // We need only positions for them, labels have already been generated at QDataSetPrivate. Just add LabelItems + barPos = (bar + 1) * (m_cachedBarSpacing.width()); + rowPos = 0; + GLfloat rotLabelX = -90.0f; + GLfloat rotLabelY = 90.0f; + GLfloat rotLabelZ = 0.0f; + Qt::AlignmentFlag alignment = Qt::AlignLeft; + if (m_xFlipped) + rotLabelY = -90.0f; + if (m_zFlipped) { + rowPos = (m_cachedRowCount + 1) * (m_cachedBarSpacing.height()); + alignment = Qt::AlignRight; + } + QVector3D labelPos = QVector3D((m_rowWidth - barPos) / m_scaleFactor, + -m_yAdjustment + 0.005f, // raise a bit over background to avoid depth "glimmering" + (m_columnDepth - rowPos) / m_scaleFactor + zComp); + + // TODO: Try it; draw the label here + + m_dummyRenderItem.setTranslation(labelPos); + const LabelItem &axisLabelItem = *m_controller->axisZ()->d_ptr->labelItems().at(bar); + //qDebug() << "labelPos, col" << bar + 1 << ":" << labelPos << dataSet->columnLabels().at(bar); + + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + QVector3D(0.0f, m_yAdjustment, zComp), + QVector3D(rotLabelX, rotLabelY, rotLabelZ), + 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, camera, true, true, LabelMid, + alignment); + } + } + glDisable(GL_TEXTURE_2D); + if (m_cachedLabelTransparency > TransparencyNone) + glDisable(GL_BLEND); + + // Release label shader + m_labelShader->release(); +#endif +#endif +} + +void Scatter3DRenderer::requestSelectionAtPoint(const QPoint &point) +{ + QMutexLocker locker(&m_mutex); + m_selectionPointRequest.setX(point.x()); + m_selectionPointRequest.setY(point.y()); + m_isSelectionPointRequestActive = true; +} + +void Scatter3DRenderer::handleResize() +{ + if (m_cachedBoundingRect.width() == 0 || m_cachedBoundingRect.height() == 0) + return; + qDebug() << "Scatter3DRenderer::resizeEvent " << m_cachedBoundingRect.width() << "x" <<m_cachedBoundingRect.height(); + + // Set view port + m_mainViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); + + // Calculate zoom level based on aspect ratio + GLfloat div; + GLfloat zoomAdjustment; + div = qMin(m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); + zoomAdjustment = defaultRatio * ((m_cachedBoundingRect.width() / div) / (m_cachedBoundingRect.height() / div)); + //qDebug() << "zoom adjustment" << zoomAdjustment; + m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f + + // Re-init selection buffer + initSelectionBuffer(); + +#if !defined(QT_OPENGL_ES_2) + // Re-init depth buffer + updateDepthBuffer(); +#endif +} + +void Scatter3DRenderer::updateBarSpecs(QSizeF thickness, QSizeF spacing, bool relative) +{ + m_cachedBarThickness = thickness; + if (relative) { + m_cachedBarSpacing.setWidth((thickness.width() * 2) * (spacing.width() + 1.0f)); + m_cachedBarSpacing.setHeight((thickness.height() * 2) * (spacing.height() + 1.0f)); + } else { + m_cachedBarSpacing = thickness * 2 + spacing * 2; + } + + // Calculate here and at setting sample space + calculateSceneScalingFactors(); +} + +void Scatter3DRenderer::updateMeshFileName(const QString &objFileName) +{ + m_cachedObjFile = objFileName; + loadBarMesh(); +} + +void Scatter3DRenderer::updateTheme(Theme theme) +{ + m_cachedTheme.setFromTheme(theme); + + m_drawer->setTheme(m_cachedTheme); + // Re-initialize shaders +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + if (!m_cachedTheme.m_uniformColor) { + initShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); + } else { + initShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } + } else { + if (!m_cachedTheme.m_uniformColor) { + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); + } else { + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } + } +#else + if (!m_cachedTheme.m_uniformColor) { + initShaders(QStringLiteral(":/shaders/vertexES2"), + QStringLiteral(":/shaders/fragmentColorOnYES2")); + } else { + initShaders(QStringLiteral(":/shaders/vertexES2"), + QStringLiteral(":/shaders/fragmentES2")); + } +#endif +} + +void Scatter3DRenderer::updateSelectionMode(SelectionMode mode) +{ + m_cachedSelectionMode = mode; +} + +void Scatter3DRenderer::updateFont(const QFont &font) +{ + m_cachedFont = font; + m_drawer->setFont(font); +} + +void Scatter3DRenderer::updateLabelTransparency(LabelTransparency transparency) +{ + m_cachedLabelTransparency = transparency; + m_drawer->setTransparency(transparency); +} + +void Scatter3DRenderer::updateGridEnabled(bool enable) +{ + m_cachedIsGridEnabled = enable; +} + +void Scatter3DRenderer::updateBackgroundEnabled(bool enable) +{ + if (m_cachedIsBackgroundEnabled != enable) { + m_cachedIsBackgroundEnabled = enable; + // Load changed bar type + loadBarMesh(); + } +} + +void Scatter3DRenderer::updateShadowQuality(ShadowQuality quality) +{ + qDebug() << "Scatter3DRenderer::setShadowQuality" << quality; + m_cachedShadowQuality = quality; + switch (quality) { + case ShadowLow: + m_shadowQualityToShader = 33.3f; + break; + case ShadowMedium: + m_shadowQualityToShader = 100.0f; + break; + case ShadowHigh: + m_shadowQualityToShader = 200.0f; + break; + default: + m_shadowQualityToShader = 0.0f; + break; + } +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > ShadowNone) { + // Re-init shaders + if (!m_cachedTheme.m_uniformColor) { + initShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); + } else { + initShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } else { + // Re-init shaders + if (!m_cachedTheme.m_uniformColor) { + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); + } else { + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } + // Re-init depth buffer + updateDepthBuffer(); +#else + if (!m_cachedTheme.m_uniformColor) { + initShaders(QStringLiteral(":/shaders/vertexES2"), + QStringLiteral(":/shaders/fragmentColorOnYES2")); + } else { + initShaders(QStringLiteral(":/shaders/vertexES2"), + QStringLiteral(":/shaders/fragmentES2")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertexES2"), + QStringLiteral(":/shaders/fragmentES2")); +#endif +} + +void Scatter3DRenderer::updateTickCount(GLint tickCount, GLfloat step, GLfloat minimum) +{ + m_tickCount = tickCount; + m_tickStep = step; + if (tickCount > 0 && step > 0) { + m_heightNormalizer = tickCount * step; + calculateHeightAdjustment(QPair<float, float>(minimum, m_heightNormalizer)); + m_valueUpdateNeeded = true; + } +} + +void Scatter3DRenderer::updateBoundingRect(const QRect boundingRect) +{ + m_cachedBoundingRect = boundingRect; + handleResize(); +} + +void Scatter3DRenderer::updatePosition(const QRect boundingRect) +{ + m_cachedBoundingRect = boundingRect; +} + +void Scatter3DRenderer::loadBarMesh() +{ + QString objectFileName = m_cachedObjFile; + if (m_barObj) + delete m_barObj; + // If background is disabled, load full version of bar mesh + if (!m_cachedIsBackgroundEnabled) + objectFileName.append(QStringLiteral("Full")); + m_barObj = new ObjectHelper(objectFileName); + m_barObj->load(); +} + +void Scatter3DRenderer::loadBackgroundMesh() +{ + if (m_backgroundObj) + delete m_backgroundObj; + m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background")); + m_backgroundObj->load(); +} + +void Scatter3DRenderer::loadGridLineMesh() +{ + if (m_gridLineObj) + delete m_gridLineObj; + m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/bar")); + m_gridLineObj->load(); +} + +void Scatter3DRenderer::loadLabelMesh() +{ + if (m_labelObj) + delete m_labelObj; + m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/label")); + m_labelObj->load(); +} + +void Scatter3DRenderer::updateTextures() +{ + // Drawer has changed; this flag needs to be checked when checking if we need to update labels + m_updateLabels = true; +} + +void Scatter3DRenderer::calculateSceneScalingFactors(const QRect &areaRect) +{ + m_areaSize = areaRect.size(); + // Calculate scaling factor so that we can be sure the whole area fits to positive z space + if (zComp > 1.0f) + m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()) / zComp; + else + m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); + //qDebug() << "scaleFactor" << m_scaleFactor; +} + +void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item) +{ + // TODO: We need to calculate y -translation here as well, based on value. + // Origin should be in the center of scene, ie. both positive and negative values are drawn + // above background + + // We need to convert position (which is in coordinates), to translation (which has origin in the center and is scaled) + // -> move pos(center, center) to trans(0, 0) and pos(0, 0) to trans(left, top) + GLfloat xTrans = 2.0f * (item.scatterPosition().x() - (m_areaSize.width() / 2.0f)) + / m_scaleFactor; + GLfloat zTrans = 2.0f * (item.scatterPosition().y() - (m_areaSize.height() / 2.0f)) + / m_scaleFactor; + //qDebug() << "x, y" << item.mapPosition().x() << item.mapPosition().y(); + item.setTranslation(QVector3D(xTrans, 0.0f, zTrans + zComp)); + //qDebug() << item.translation(); +} + +void Scatter3DRenderer::calculateHeightAdjustment(const QPair<GLfloat, GLfloat> &limits) +{ + // TODO: How does origin being in the center of the scene affect this? + // 2.0f = max difference between minimum and maximum value after scaling with m_heightNormalizer + m_yAdjustment = 2.0f - ((limits.second - limits.first) / m_heightNormalizer); + //qDebug() << m_yAdjustment; +} + +Scatter3DController::SelectionType Scatter3DRenderer::isSelected(GLint bar, + const QVector3D &selection) +{ + GLubyte barIdxRed = 0; + GLubyte barIdxGreen = 0; + GLubyte barIdxBlue = 0; + //static QVector3D prevSel = selection; // TODO: For debugging + Scatter3DController::SelectionType isSelectedType = Scatter3DController::SelectionNone; + + if (selection == selectionSkipColor) + return isSelectedType; // skip window + + if (bar <= 255) { + barIdxRed = bar; + } else if (bar <= 65535) { + barIdxGreen = bar / 256; + barIdxRed = bar % 256; + } else { + barIdxBlue = bar / 65535; + barIdxGreen = bar % 65535; + barIdxRed = bar % 256; + } + + QVector3D current = QVector3D(barIdxRed, barIdxGreen, barIdxBlue); + + // TODO: For debugging + //if (selection != prevSel) { + // qDebug() << selection.x() << selection .y() << selection.z(); + // prevSel = selection; + //} + + if (current == selection) + isSelectedType = Scatter3DController::SelectionBar; + + return isSelectedType; +} + +// TODO: Do we need this for autoscaling? +/*void Scatter3DRenderer::updateLimits(QPair<GLfloat, GLfloat> limits) +{ + m_limits.first = limits.first; + m_limits.second = limits.second; + + // Don't auto-adjust height if tick count is set + if (m_tickCount == 0) { + m_heightNormalizer = (GLfloat)qMax(qFabs(limits.second), qFabs(limits.first)); + calculateHeightAdjustment(limits); + m_valueUpdateNeeded = true; + } +} +*/ + +void Scatter3DRenderer::updateZoomLevel(int newZoomLevel) +{ + m_cachedZoomLevel = newZoomLevel; +} + + +QRect Scatter3DRenderer::mainViewPort() +{ + return m_mainViewPort; +} + +void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) +{ + if (m_barShader) + delete m_barShader; + m_barShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_barShader->initialize(); +} + +void Scatter3DRenderer::initSelectionShader() +{ + if (m_selectionShader) + delete m_selectionShader; + m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSelection"), + QStringLiteral(":/shaders/fragmentSelection")); + m_selectionShader->initialize(); +} + +void Scatter3DRenderer::initSelectionBuffer() +{ + if (m_selectionTexture) + m_textureHelper->deleteTexture(&m_selectionTexture); + + m_selectionTexture = m_textureHelper->createSelectionTexture(m_mainViewPort.size(), + m_selectionFrameBuffer, + m_selectionDepthBuffer); +} + +#if !defined(QT_OPENGL_ES_2) +void Scatter3DRenderer::initDepthShader() +{ + if (m_depthShader) + delete m_depthShader; + m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), + QStringLiteral(":/shaders/fragmentDepth")); + m_depthShader->initialize(); +} + +void Scatter3DRenderer::updateDepthBuffer() +{ + if (m_depthTexture) { + m_textureHelper->deleteTexture(&m_depthTexture); + m_depthTexture = 0; + } + + if (m_cachedShadowQuality > ShadowNone) { + m_depthTexture = m_textureHelper->createDepthTexture(m_mainViewPort.size(), + m_depthFrameBuffer, + m_cachedShadowQuality); + if (!m_depthTexture) { + switch (m_cachedShadowQuality) { + case ShadowHigh: + qWarning("Creating high quality shadows failed. Changing to medium quality."); + (void)m_controller->setShadowQuality(ShadowMedium); + break; + case ShadowMedium: + qWarning("Creating medium quality shadows failed. Changing to low quality."); + (void)m_controller->setShadowQuality(ShadowLow); + break; + case ShadowLow: + qWarning("Creating low quality shadows failed. Switching shadows off."); + (void)m_controller->setShadowQuality(ShadowNone); + break; + default: + // You'll never get here + break; + } + } + } +} +#endif + +void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader, + const QString &fragmentShader) +{ + if (m_backgroundShader) + delete m_backgroundShader; + m_backgroundShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_backgroundShader->initialize(); +} + +void Scatter3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) +{ + if (m_labelShader) + delete m_labelShader; + m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_labelShader->initialize(); +} + +QT_DATAVIS3D_END_NAMESPACE diff --git a/src/datavis3d/engine/scatter3drenderer_p.h b/src/datavis3d/engine/scatter3drenderer_p.h new file mode 100644 index 00000000..0524569a --- /dev/null +++ b/src/datavis3d/engine/scatter3drenderer_p.h @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDataVis3D module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVis3D 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 Q3DSCATTERRENDERER_p_H +#define Q3DSCATTERRENDERER_p_H + +#include <QtCore/QSize> +#include <QtCore/QObject> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/QFont> +#include <QTime> +#include <QWindow> +#include <QMutex> + +#include "datavis3dglobal_p.h" +#include "scatter3dcontroller_p.h" +#include "qscatterdataproxy.h" +#include "scatterrenderitem_p.h" + +//#define DISPLAY_RENDER_SPEED + +class QPoint; +class QSizeF; +class QOpenGLShaderProgram; + +QT_DATAVIS3D_BEGIN_NAMESPACE + +class ShaderHelper; +class ObjectHelper; +class TextureHelper; +class Theme; +class Drawer; +class LabelItem; +class CameraHelper; +class QAbstractAxisPrivate; + +class QT_DATAVIS3D_EXPORT Scatter3DRenderer : public QObject, protected QOpenGLFunctions +{ + Q_OBJECT + +private: + // TODO: Filter to the set of attributes to be moved to the model object. + // * All GL rendering only related attribs should be moved out of this public set. + // * All attribs that are modifiable from QML need to e in this set. + + Scatter3DController *m_controller; + + // Mutex for sharing resources between render and main threads. + QMutex m_mutex; + + // Cached state based on emitted signals from the controller + QSizeF m_cachedBarThickness; + QSizeF m_cachedBarSpacing; + QString m_cachedObjFile; + SelectionMode m_cachedSelectionMode; + int m_cachedZoomLevel; + int m_cachedRowCount; + int m_cachedColumnCount; + QRect m_cachedBoundingRect; + Theme m_cachedTheme; + LabelTransparency m_cachedLabelTransparency; + QFont m_cachedFont; + bool m_cachedIsGridEnabled; + bool m_cachedIsBackgroundEnabled; + ShadowQuality m_cachedShadowQuality; + + // Internal state + bool m_hasNegativeValues; + BarRenderItem *m_selectedBar; // points to renderitem array + BarRenderItem *m_previouslySelectedBar; // points to renderitem array + GLint m_tickCount; + GLfloat m_tickStep; + bool m_xFlipped; + bool m_zFlipped; + QRect m_mainViewPort; + bool m_updateLabels; + ShaderHelper *m_barShader; + ShaderHelper *m_depthShader; + ShaderHelper *m_selectionShader; + ShaderHelper *m_backgroundShader; + ShaderHelper *m_labelShader; + ObjectHelper *m_barObj; + ObjectHelper *m_backgroundObj; + ObjectHelper *m_gridLineObj; + ObjectHelper *m_labelObj; + TextureHelper *m_textureHelper; + Drawer *m_drawer; + GLuint m_bgrTexture; + GLuint m_depthTexture; + GLuint m_selectionTexture; + GLuint m_depthFrameBuffer; + GLuint m_selectionFrameBuffer; + GLuint m_selectionDepthBuffer; + GLfloat m_shadowQualityToShader; + GLfloat m_autoScaleAdjustment; + GLfloat m_heightNormalizer; + GLfloat m_yAdjustment; + GLfloat m_rowWidth; + GLfloat m_columnDepth; + GLfloat m_maxDimension; + GLfloat m_scaleX; + GLfloat m_scaleZ; + GLfloat m_scaleFactor; + GLfloat m_maxSceneSize; + QVector3D m_selection; + QSizeF m_areaSize; + + QPoint m_selectionPointRequest; + bool m_isSelectionPointRequestActive; + + bool m_hasHeightAdjustmentChanged; + QPair<GLfloat, GLfloat> m_limits; + ScatterRenderItem m_dummyRenderItem; + QScatterDataProxy *m_dataProxy; // Only valid during render + + ScatterRenderItemArray m_renderItemArray; + bool m_valueUpdateNeeded; + +#ifdef DISPLAY_RENDER_SPEED + bool m_isFirstFrame; + QTime m_lastFrameTime; + GLint m_numFrames; +#endif + +public: + explicit Scatter3DRenderer(Scatter3DController *controller); + ~Scatter3DRenderer(); + + void render(QScatterDataProxy *dataProxy, bool valuesDirty, CameraHelper *camera, + const LabelItem &xLabel, const LabelItem &yLabel, const LabelItem &zLabel, + const GLuint defaultFboHandle = 0); + + QRect mainViewPort(); + // TODO: Not thread-safe, needs rethinking how axes create labels + Drawer *drawer() { return m_drawer; } + +public slots: + void updateBarSpecs(QSizeF thickness = QSizeF(1.0f, 1.0f), + QSizeF spacing = QSizeF(1.0f, 1.0f), + bool relative = true); + void updateTheme(Theme theme); + void updateSelectionMode(SelectionMode newMode); + //void updateLimits(QPair<GLfloat, GLfloat> newLimits); + void updateZoomLevel(int newZoomLevel); + void updateFont(const QFont &font); + void updateLabelTransparency(LabelTransparency transparency); + void updateGridEnabled(bool enable); + void updateBackgroundEnabled(bool enable); + void updateShadowQuality(ShadowQuality quality); + void updateTickCount(GLint tickCount, GLfloat step, GLfloat minimum = 0.0f); + void updateMeshFileName(const QString &objFileName); + void updateBoundingRect(const QRect boundingRect); + void updatePosition(const QRect boundingRect); + + // Requests that upon next render pass the column and row under the given point is inspected for selection. + // Only one request can be queued per render pass at this point. New request will override any pending requests. + // After inspection the selectionUpdated signal is emitted. + void requestSelectionAtPoint(const QPoint &point); + +signals: + void selectionUpdated(QVector3D selection); + +private: + void initializeOpenGL(); + void drawScene(CameraHelper *camera, const GLuint defaultFboHandle); + void handleResize(); + + void loadBarMesh(); + void loadBackgroundMesh(); + void loadGridLineMesh(); + void loadLabelMesh(); + void initShaders(const QString &vertexShader, const QString &fragmentShader); + void initSelectionShader(); + void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); + void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); + void initSelectionBuffer(); +#if !defined(QT_OPENGL_ES_2) + void initDepthShader(); + void updateDepthBuffer(); +#endif + void updateTextures(); + void calculateSceneScalingFactors(const QRect &areaRect = QRect(0, 0, 1, 1)); + void calculateTranslation(ScatterRenderItem &item); + void calculateHeightAdjustment(const QPair<GLfloat, GLfloat> &limits); + Scatter3DController::SelectionType isSelected(GLint bar, const QVector3D &selection); + + + Q_DISABLE_COPY(Scatter3DRenderer) + + friend class ScatterRenderItem; +}; + + +QT_DATAVIS3D_END_NAMESPACE + +#endif diff --git a/src/datavis3d/engine/theme_p.h b/src/datavis3d/engine/theme_p.h index 3de1c9ea..5789c987 100644 --- a/src/datavis3d/engine/theme_p.h +++ b/src/datavis3d/engine/theme_p.h @@ -76,6 +76,7 @@ private: friend class Maps3DController; friend class Surface3dRenderer; friend class Surface3dController; + friend class Scatter3DRenderer; friend class Drawer; ColorTheme m_colorTheme; diff --git a/src/datavis3d/utils/texturehelper_p.h b/src/datavis3d/utils/texturehelper_p.h index edacce24..accc40a0 100644 --- a/src/datavis3d/utils/texturehelper_p.h +++ b/src/datavis3d/utils/texturehelper_p.h @@ -84,10 +84,9 @@ class TextureHelper : protected QOpenGLFunctions QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format); friend class Bars3dRenderer; - friend class Bars3dController; friend class Maps3DController; friend class Surface3dRenderer; - friend class Surface3dController; + friend class Scatter3DRenderer; }; QT_DATAVIS3D_END_NAMESPACE |