summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTomi Korpipää <tomi.korpipaa@digia.com>2013-08-02 11:33:34 +0300
committerTomi Korpipää <tomi.korpipaa@digia.com>2013-08-05 07:41:02 +0300
commit48248feb5334e07ab0e81f4400b38e62bf26fb03 (patch)
treeff2039c310ff2d6afebebe7b60734c61cc1fcd43 /src
parentacb15b2d7822c39b74845c7ea65b51881e91f010 (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')
-rw-r--r--src/datavis3d/axis/qabstractaxis.h2
-rw-r--r--src/datavis3d/data/data.pri8
-rw-r--r--src/datavis3d/data/qscatterdataitem.cpp124
-rw-r--r--src/datavis3d/data/qscatterdataitem.h80
-rw-r--r--src/datavis3d/data/qscatterdataitem_p.h75
-rw-r--r--src/datavis3d/data/qscatterdataproxy.cpp141
-rw-r--r--src/datavis3d/data/qscatterdataproxy.h116
-rw-r--r--src/datavis3d/data/qscatterdataproxy_p.h82
-rw-r--r--src/datavis3d/data/scatterrenderitem.cpp74
-rw-r--r--src/datavis3d/data/scatterrenderitem_p.h91
-rw-r--r--src/datavis3d/engine/engine.pri11
-rw-r--r--src/datavis3d/engine/q3dscatter.cpp519
-rw-r--r--src/datavis3d/engine/q3dscatter.h192
-rw-r--r--src/datavis3d/engine/q3dscatter_p.h74
-rw-r--r--src/datavis3d/engine/scatter3dcontroller.cpp514
-rw-r--r--src/datavis3d/engine/scatter3dcontroller_p.h228
-rw-r--r--src/datavis3d/engine/scatter3drenderer.cpp1649
-rw-r--r--src/datavis3d/engine/scatter3drenderer_p.h244
-rw-r--r--src/datavis3d/engine/theme_p.h1
-rw-r--r--src/datavis3d/utils/texturehelper_p.h3
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