diff options
Diffstat (limited to 'src/datavisualization/engine')
76 files changed, 5704 insertions, 4826 deletions
diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index bfdc375e..9835dbe4 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -27,21 +27,19 @@ #include "qabstractdataproxy_p.h" #include "qabstract3dinputhandler_p.h" #include "qtouch3dinputhandler.h" +#include "qabstract3dseries_p.h" +#include "thememanager_p.h" +#include "q3dscene_p.h" #include <QThread> QT_DATAVISUALIZATION_BEGIN_NAMESPACE -Abstract3DController::Abstract3DController(QRect boundRect, QObject *parent) : +Abstract3DController::Abstract3DController(QRect initialViewport, QObject *parent) : QObject(parent), - m_boundingRect(boundRect.x(), boundRect.y(), boundRect.width(), boundRect.height()), - m_theme(), - m_font(QFont(QStringLiteral("Arial"))), - m_selectionMode(QDataVis::SelectionModeItem), + m_themeManager(new ThemeManager(this)), + m_selectionMode(QDataVis::SelectionItem), m_shadowQuality(QDataVis::ShadowQualityMedium), - m_labelStyle(QDataVis::LabelStyleTransparent), - m_isBackgroundEnabled(true), - m_isGridEnabled(true), m_scene(new Q3DScene()), m_activeInputHandler(0), m_axisX(0), @@ -49,10 +47,14 @@ Abstract3DController::Abstract3DController(QRect boundRect, QObject *parent) : m_axisZ(0), m_renderer(0), m_isDataDirty(true), - m_data(0), + m_isSeriesVisibilityDirty(true), + m_isSeriesVisualsDirty(true), m_renderPending(false) { - m_theme.useTheme(QDataVis::ThemeQt); + // Set initial theme + setTheme(new Q3DTheme(Q3DTheme::ThemeQt)); + + m_scene->d_ptr->setViewport(initialViewport); // Populate the scene m_scene->activeLight()->setPosition(defaultLightPos); @@ -63,8 +65,10 @@ Abstract3DController::Abstract3DController(QRect boundRect, QObject *parent) : inputHandler->d_ptr->m_isDefaultHandler = true; setActiveInputHandler(inputHandler); connect(inputHandler, &QAbstract3DInputHandler::inputStateChanged, this, - &Abstract3DController::emitNeedRender); - connect(m_scene, &Q3DScene::needRender, this, + &Abstract3DController::handleInputStateChanged); + connect(inputHandler, &QAbstract3DInputHandler::positionChanged, this, + &Abstract3DController::handleInputPositionChanged); + connect(m_scene->d_ptr.data(), &Q3DScenePrivate::needRender, this, &Abstract3DController::emitNeedRender); } @@ -76,46 +80,62 @@ Abstract3DController::~Abstract3DController() else delete m_renderer; delete m_scene; + delete m_themeManager; } +/** + * @brief setRenderer Sets the renderer to be used. isInitialized returns true from this point onwards. + * @param renderer Renderer to be used. + */ void Abstract3DController::setRenderer(Abstract3DRenderer *renderer) { m_renderer = renderer; } -void Abstract3DController::synchDataToRenderer() +void Abstract3DController::addSeries(QAbstract3DSeries *series) { - // If we don't have a renderer, don't do anything - if (!m_renderer) - return; - - if (m_changeTracker.boundingRectChanged || m_changeTracker.sizeChanged) { - m_renderer->updateBoundingRect(m_boundingRect); - m_changeTracker.boundingRectChanged = false; - m_changeTracker.sizeChanged = false; + if (series && !m_seriesList.contains(series)) { + int oldSize = m_seriesList.size(); + m_seriesList.append(series); + series->d_ptr->setController(this); + QObject::connect(series, &QAbstract3DSeries::visibilityChanged, + this, &Abstract3DController::handleSeriesVisibilityChanged); + if (series->isVisible()) + handleSeriesVisibilityChangedBySender(series); + series->d_ptr->resetToTheme(*m_themeManager->theme(), oldSize, false); } +} - if (m_changeTracker.positionChanged) { - m_renderer->updatePosition(m_boundingRect); - m_changeTracker.positionChanged = false; +void Abstract3DController::removeSeries(QAbstract3DSeries *series) +{ + if (series && series->d_ptr->m_controller == this) { + m_seriesList.removeAll(series); + QObject::disconnect(series, &QAbstract3DSeries::visibilityChanged, + this, &Abstract3DController::handleSeriesVisibilityChanged); + series->d_ptr->setController(0); + m_isDataDirty = true; + m_isSeriesVisibilityDirty = true; + emitNeedRender(); } +} - m_renderer->updateScene(m_scene); +QList<QAbstract3DSeries *> Abstract3DController::seriesList() +{ + return m_seriesList; +} - if (m_changeTracker.themeChanged) { - m_renderer->updateTheme(m_theme); - m_changeTracker.themeChanged = false; - } +/** + * @brief synchDataToRenderer Called on the render thread while main GUI thread is blocked before rendering. + */ +void Abstract3DController::synchDataToRenderer() +{ + // If we don't have a renderer, don't do anything + if (!m_renderer) + return; - if (m_changeTracker.fontChanged) { - m_renderer->updateFont(m_font); - m_changeTracker.fontChanged = false; - } + m_renderer->updateScene(m_scene); - if (m_changeTracker.labelStyleChanged) { - m_renderer->updateLabelStyle(m_labelStyle); - m_changeTracker.labelStyleChanged = false; - } + m_renderer->updateTheme(m_themeManager->theme()); if (m_changeTracker.shadowQualityChanged) { m_renderer->updateShadowQuality(m_shadowQuality); @@ -127,21 +147,6 @@ void Abstract3DController::synchDataToRenderer() m_changeTracker.selectionModeChanged = false; } - if (m_changeTracker.objFileChanged) { - m_renderer->updateMeshFileName(m_objFile); - m_changeTracker.objFileChanged = false; - } - - if (m_changeTracker.gridEnabledChanged) { - m_renderer->updateGridEnabled(m_isGridEnabled); - m_changeTracker.gridEnabledChanged = false; - } - - if (m_changeTracker.backgroundEnabledChanged) { - m_renderer->updateBackgroundEnabled(m_isBackgroundEnabled); - m_changeTracker.backgroundEnabledChanged = false; - } - if (m_changeTracker.axisXTypeChanged) { m_renderer->updateAxisType(Q3DAbstractAxis::AxisOrientationX, m_axisX->type()); m_changeTracker.axisXTypeChanged = false; @@ -284,6 +289,19 @@ void Abstract3DController::synchDataToRenderer() valueAxisZ->labelFormat()); } } + + if (m_isSeriesVisibilityDirty || m_isSeriesVisualsDirty) { + m_renderer->updateSeries(m_seriesList, m_isSeriesVisibilityDirty); + m_isSeriesVisibilityDirty = false; + m_isSeriesVisualsDirty = false; + } + + if (m_isDataDirty) { + // Series list supplied above in updateSeries() is used to access the data, + // so no data needs to be passed in updateData() + m_renderer->updateData(); + m_isDataDirty = false; + } } void Abstract3DController::render(const GLuint defaultFboHandle) @@ -306,156 +324,126 @@ void Abstract3DController::mouseDoubleClickEvent(QMouseEvent *event) { if (m_activeInputHandler) m_activeInputHandler->mouseDoubleClickEvent(event); - - emitNeedRender(); } void Abstract3DController::touchEvent(QTouchEvent *event) { if (m_activeInputHandler) m_activeInputHandler->touchEvent(event); - - emitNeedRender(); } void Abstract3DController::mousePressEvent(QMouseEvent *event, const QPoint &mousePos) { if (m_activeInputHandler) m_activeInputHandler->mousePressEvent(event, mousePos); - - emitNeedRender(); } void Abstract3DController::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos) { if (m_activeInputHandler) m_activeInputHandler->mouseReleaseEvent(event, mousePos); - - emitNeedRender(); } void Abstract3DController::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos) { if (m_activeInputHandler) m_activeInputHandler->mouseMoveEvent(event, mousePos); - - emitNeedRender(); } void Abstract3DController::wheelEvent(QWheelEvent *event) { if (m_activeInputHandler) m_activeInputHandler->wheelEvent(event); - - emitNeedRender(); } -void Abstract3DController::setSize(const int width, const int height) +void Abstract3DController::handleThemeColorStyleChanged(Q3DTheme::ColorStyle style) { - m_boundingRect.setWidth(width); - m_boundingRect.setHeight(height); - m_scene->setViewportSize(width, height); - - m_changeTracker.boundingRectChanged = true; - emitNeedRender(); -} - -const QSize Abstract3DController::size() -{ - return m_boundingRect.size(); -} - -const QRect Abstract3DController::boundingRect() -{ - return m_boundingRect; -} - -void Abstract3DController::setBoundingRect(const QRect boundingRect) -{ - m_boundingRect = boundingRect; - m_scene->setViewport(boundingRect); - - m_changeTracker.boundingRectChanged = true; - emitNeedRender(); -} - -void Abstract3DController::setWidth(const int width) -{ - m_boundingRect.setWidth(width); - m_scene->setViewportSize(width, m_scene->viewport().height()); - - m_changeTracker.sizeChanged = true; - emitNeedRender(); -} - -int Abstract3DController::width() -{ - return m_boundingRect.width(); -} - -void Abstract3DController::setHeight(const int height) -{ - m_boundingRect.setHeight(height); - m_scene->setViewportSize(m_scene->viewport().width(), height); - - m_changeTracker.sizeChanged = true; - emitNeedRender(); -} - -int Abstract3DController::height() -{ - return m_boundingRect.height(); -} - -void Abstract3DController::setX(const int x) -{ - m_boundingRect.setX(x); - - m_changeTracker.positionChanged = true; - emitNeedRender(); -} - -int Abstract3DController::x() -{ - return m_boundingRect.x(); -} - -void Abstract3DController::setY(const int y) -{ - m_boundingRect.setY(y); - - m_changeTracker.positionChanged = true; - emitNeedRender(); + // Set value for series that have not explicitly set this value + foreach (QAbstract3DSeries *series, m_seriesList) { + if (!series->d_ptr->m_themeTracker.colorStyleOverride) { + series->setColorStyle(style); + series->d_ptr->m_themeTracker.colorStyleOverride = false; + } + } + markSeriesVisualsDirty(); } -int Abstract3DController::y() +void Abstract3DController::handleThemeBaseColorsChanged(const QList<QColor> &colors) { - return m_boundingRect.y(); + int colorIdx = 0; + // Set value for series that have not explicitly set this value + foreach (QAbstract3DSeries *series, m_seriesList) { + if (!series->d_ptr->m_themeTracker.baseColorOverride) { + series->setBaseColor(colors.at(colorIdx)); + series->d_ptr->m_themeTracker.baseColorOverride = false; + } + if (++colorIdx >= colors.size()) + colorIdx = 0; + } + markSeriesVisualsDirty(); } -QRect Abstract3DController::primarySubViewport() const +void Abstract3DController::handleThemeBaseGradientsChanged(const QList<QLinearGradient> &gradients) { - return m_scene->primarySubViewport(); + int gradientIdx = 0; + // Set value for series that have not explicitly set this value + foreach (QAbstract3DSeries *series, m_seriesList) { + if (!series->d_ptr->m_themeTracker.baseGradientOverride) { + series->setBaseGradient(gradients.at(gradientIdx)); + series->d_ptr->m_themeTracker.baseGradientOverride = false; + } + if (++gradientIdx >= gradients.size()) + gradientIdx = 0; + } + markSeriesVisualsDirty(); } -void Abstract3DController::setPrimarySubViewport(const QRect &primarySubViewport) +void Abstract3DController::handleThemeSingleHighlightColorChanged(const QColor &color) { - m_scene->setPrimarySubViewport(primarySubViewport); + // Set value for series that have not explicitly set this value + foreach (QAbstract3DSeries *series, m_seriesList) { + if (!series->d_ptr->m_themeTracker.singleHighlightColorOverride) { + series->setSingleHighlightColor(color); + series->d_ptr->m_themeTracker.singleHighlightColorOverride = false; + } + } + markSeriesVisualsDirty(); } -QRect Abstract3DController::secondarySubViewport() const +void Abstract3DController::handleThemeSingleHighlightGradientChanged(const QLinearGradient &gradient) { - return m_scene->secondarySubViewport(); + // Set value for series that have not explicitly set this value + foreach (QAbstract3DSeries *series, m_seriesList) { + if (!series->d_ptr->m_themeTracker.singleHighlightGradientOverride) { + series->setSingleHighlightGradient(gradient); + series->d_ptr->m_themeTracker.singleHighlightGradientOverride = false; + } + } + markSeriesVisualsDirty(); } -void Abstract3DController::setSecondarySubViewport(const QRect &secondarySubViewport) +void Abstract3DController::handleThemeMultiHighlightColorChanged(const QColor &color) { - m_scene->setSecondarySubViewport(secondarySubViewport); + // Set value for series that have not explicitly set this value + foreach (QAbstract3DSeries *series, m_seriesList) { + if (!series->d_ptr->m_themeTracker.multiHighlightColorOverride) { + series->setMultiHighlightColor(color); + series->d_ptr->m_themeTracker.multiHighlightColorOverride = false; + } + } + markSeriesVisualsDirty(); } -void Abstract3DController::updateDevicePixelRatio(qreal ratio) +void Abstract3DController::handleThemeMultiHighlightGradientChanged(const QLinearGradient &gradient) { - m_scene->setDevicePixelRatio(ratio); + // Set value for series that have not explicitly set this value + foreach (QAbstract3DSeries *series, m_seriesList) { + if (!series->d_ptr->m_themeTracker.multiHighlightGradientOverride) { + series->setMultiHighlightGradient(gradient); + series->d_ptr->m_themeTracker.multiHighlightGradientOverride = false; + } + } + markSeriesVisualsDirty(); } void Abstract3DController::setAxisX(Q3DAbstractAxis *axis) @@ -532,64 +520,6 @@ QList<Q3DAbstractAxis *> Abstract3DController::axes() const return m_axes; } -QAbstractDataProxy *Abstract3DController::activeDataProxy() const -{ - return m_data; -} - -void Abstract3DController::addDataProxy(QAbstractDataProxy *proxy) -{ - Q_ASSERT(proxy); - Abstract3DController *owner = qobject_cast<Abstract3DController *>(proxy->parent()); - if (owner != this) { - Q_ASSERT_X(!owner, "addDataProxy", "Proxy already attached to a graph."); - proxy->setParent(this); - } - if (!m_dataProxies.contains(proxy)) - m_dataProxies.append(proxy); -} - -void Abstract3DController::releaseDataProxy(QAbstractDataProxy *proxy) -{ - if (proxy && m_dataProxies.contains(proxy)) { - // Clear the default status from released default proxies - if (proxy->d_ptr->isDefaultProxy()) - proxy->d_ptr->setDefaultProxy(false); - - // If the proxy is in use, replace it with a temporary one - if (m_data == proxy) - setActiveDataProxy(0); - - m_dataProxies.removeAll(proxy); - proxy->setParent(0); - } -} - -QList<QAbstractDataProxy *> Abstract3DController::dataProxies() const -{ - return m_dataProxies; -} - -void Abstract3DController::setActiveDataProxy(QAbstractDataProxy *proxy) -{ - // If existing proxy is the default proxy, delete it - if (m_data) { - if (m_data->d_ptr->isDefaultProxy()) { - m_dataProxies.removeAll(m_data); - delete m_data; - } else { - // Disconnect the old proxy from use - QObject::disconnect(m_data, 0, this, 0); - } - } - - // Assume ownership and activate - addDataProxy(proxy); - m_data = proxy; - m_isDataDirty = true; - emitNeedRender(); -} - void Abstract3DController::addInputHandler(QAbstract3DInputHandler *inputHandler) { Q_ASSERT(inputHandler); @@ -665,110 +595,55 @@ void Abstract3DController::setZoomLevel(int zoomLevel) emitNeedRender(); } -void Abstract3DController::setObjectColor(const QColor &baseColor, bool uniform) +void Abstract3DController::setTheme(Q3DTheme *theme) { - m_theme.m_baseColor = baseColor; - m_theme.m_uniformColor = uniform; - - m_changeTracker.themeChanged = true; - emitNeedRender(); -} - -QColor Abstract3DController::objectColor() const -{ - return m_theme.m_baseColor; -} - -void Abstract3DController::setTheme(QDataVis::Theme theme) -{ - m_theme.useTheme(theme); - - m_changeTracker.themeChanged = true; - emitNeedRender(); -} - -Theme Abstract3DController::theme() -{ - return m_theme; -} - -void Abstract3DController::setFont(const QFont &font) -{ - m_font = font; - - m_changeTracker.fontChanged = true; - emitNeedRender(); + if (theme != m_themeManager->theme()) { + m_themeManager->setTheme(theme); + m_changeTracker.themeChanged = true; + // Reset all attached series to the new theme + for (int i = 0; i < m_seriesList.size(); i++) + m_seriesList.at(i)->d_ptr->resetToTheme(*theme, i, true); + markSeriesVisualsDirty(); + emit themeChanged(theme); + } } -QFont Abstract3DController::font() +Q3DTheme *Abstract3DController::theme() const { - return m_font; + return m_themeManager->theme(); } -void Abstract3DController::setSelectionMode(QDataVis::SelectionMode mode) +void Abstract3DController::setSelectionMode(QDataVis::SelectionFlags mode) { - m_selectionMode = mode; - m_changeTracker.selectionModeChanged = true; - emitNeedRender(); + if (mode != m_selectionMode) { + m_selectionMode = mode; + m_changeTracker.selectionModeChanged = true; + emit selectionModeChanged(mode); + emitNeedRender(); + } } -QDataVis::SelectionMode Abstract3DController::selectionMode() +QDataVis::SelectionFlags Abstract3DController::selectionMode() const { return m_selectionMode; } void Abstract3DController::setShadowQuality(QDataVis::ShadowQuality quality) { - m_shadowQuality = quality; - - m_changeTracker.shadowQualityChanged = true; - emit shadowQualityChanged(m_shadowQuality); - emitNeedRender(); + if (quality != m_shadowQuality) { + m_shadowQuality = quality; + m_changeTracker.shadowQualityChanged = true; + emit shadowQualityChanged(m_shadowQuality); + emitNeedRender(); + } } -QDataVis::ShadowQuality Abstract3DController::shadowQuality() +QDataVis::ShadowQuality Abstract3DController::shadowQuality() const { return m_shadowQuality; } -void Abstract3DController::setLabelStyle(QDataVis::LabelStyle style) -{ - m_labelStyle = style; - - m_changeTracker.labelStyleChanged = true; - emitNeedRender(); -} - -QDataVis::LabelStyle Abstract3DController::labelStyle() -{ - return m_labelStyle; -} - -void Abstract3DController::setBackgroundEnabled(bool enable) -{ - m_isBackgroundEnabled = enable; - m_changeTracker.backgroundEnabledChanged = true; - emitNeedRender(); -} - -bool Abstract3DController::backgroundEnabled() -{ - return m_isBackgroundEnabled; -} - -void Abstract3DController::setGridEnabled(bool enable) -{ - m_isGridEnabled = enable; - m_changeTracker.gridEnabledChanged = true; - emitNeedRender(); -} - -bool Abstract3DController::gridEnabled() -{ - return m_isGridEnabled; -} - -bool Abstract3DController::isSlicingActive() +bool Abstract3DController::isSlicingActive() const { return m_scene->isSlicingActive(); } @@ -779,37 +654,21 @@ void Abstract3DController::setSlicingActive(bool isSlicing) emitNeedRender(); } -QDataVis::InputState Abstract3DController::inputState() -{ - if (m_activeInputHandler) - return m_activeInputHandler->inputState(); - else - return QDataVis::InputStateNone; -} - -QPoint Abstract3DController::inputPosition() +Q3DScene *Abstract3DController::scene() { - if (m_activeInputHandler) - return m_activeInputHandler->inputPosition(); - else - return QPoint(0,0); + return m_scene; } -void Abstract3DController::setMeshFileName(const QString &fileName) +void Abstract3DController::markDataDirty() { - m_objFile = fileName; - m_changeTracker.objFileChanged = true; + m_isDataDirty = true; emitNeedRender(); } -QString Abstract3DController::meshFileName() -{ - return m_objFile; -} - -Q3DScene *Abstract3DController::scene() +void Abstract3DController::markSeriesVisualsDirty() { - return m_scene; + m_isSeriesVisualsDirty = true; + emitNeedRender(); } void Abstract3DController::handleAxisTitleChanged(const QString &title) @@ -849,7 +708,7 @@ void Abstract3DController::handleAxisLabelsChangedBySender(QObject *sender) emitNeedRender(); } -void Abstract3DController::handleAxisRangeChanged(qreal min, qreal max) +void Abstract3DController::handleAxisRangeChanged(float min, float max) { Q_UNUSED(min) Q_UNUSED(max) @@ -927,6 +786,38 @@ void Abstract3DController::handleAxisLabelFormatChanged(const QString &format) handleAxisLabelFormatChangedBySender(sender()); } +void Abstract3DController::handleInputStateChanged(QAbstract3DInputHandler::InputState state) +{ + // When in automatic slicing mode, input state change to overview disables slice mode + if (m_selectionMode.testFlag(QDataVis::SelectionSlice) + && state == QAbstract3DInputHandler::InputStateOnPrimaryView) { + setSlicingActive(false); + } + + m_changeTracker.inputStateChanged = true; + emitNeedRender(); +} + +void Abstract3DController::handleInputPositionChanged(const QPoint &position) +{ + Q_UNUSED(position) + + m_changeTracker.inputPositionChanged = true; + emitNeedRender(); +} + +void Abstract3DController::handleSeriesVisibilityChanged(bool visible) +{ + Q_UNUSED(visible) + + handleSeriesVisibilityChangedBySender(sender()); +} + +void Abstract3DController::handleRequestShadowQuality(QDataVis::ShadowQuality quality) +{ + setShadowQuality(quality); +} + void Abstract3DController::handleAxisLabelFormatChangedBySender(QObject *sender) { // Label format changing needs to dirty the data so that labels are reset. @@ -945,6 +836,15 @@ void Abstract3DController::handleAxisLabelFormatChangedBySender(QObject *sender) emitNeedRender(); } +void Abstract3DController::handleSeriesVisibilityChangedBySender(QObject *sender) +{ + Q_UNUSED(sender) + + m_isDataDirty = true; + m_isSeriesVisibilityDirty = true; + emitNeedRender(); +} + void Abstract3DController::setAxisHelper(Q3DAbstractAxis::AxisOrientation orientation, Q3DAbstractAxis *axis, Q3DAbstractAxis **axisPtr) { @@ -1024,12 +924,7 @@ Q3DAbstractAxis *Abstract3DController::createDefaultAxis(Q3DAbstractAxis::AxisOr Q3DValueAxis *Abstract3DController::createDefaultValueAxis() { // Default value axis has single segment, empty label format, and auto scaling - // TODO: Grid should be also hidden, but that is not currently controlled by axis Q3DValueAxis *defaultAxis = new Q3DValueAxis; - defaultAxis->setSegmentCount(1); - defaultAxis->setSubSegmentCount(1); - defaultAxis->setAutoAdjustRange(true); - defaultAxis->setLabelFormat(QString()); defaultAxis->d_ptr->setDefaultAxis(true); return defaultAxis; @@ -1038,9 +933,7 @@ Q3DValueAxis *Abstract3DController::createDefaultValueAxis() Q3DCategoryAxis *Abstract3DController::createDefaultCategoryAxis() { // Default category axis has no labels - // TODO: Grid should be also hidden, but that is not currently controlled by axis. Q3DCategoryAxis *defaultAxis = new Q3DCategoryAxis; - defaultAxis->setAutoAdjustRange(true); defaultAxis->d_ptr->setDefaultAxis(true); return defaultAxis; } diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index f17c6c4d..c56b8a72 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -26,19 +26,19 @@ // // We mean it. -#ifndef CONTROLLER3DBASE_H -#define CONTROLLER3DBASE_H +#ifndef ABSTRACT3DCONTROLLER_P_H +#define ABSTRACT3DCONTROLLER_P_H #include "datavisualizationglobal_p.h" -#include "theme_p.h" #include "q3dabstractaxis.h" #include "drawer_p.h" #include "qabstract3dinputhandler.h" #include "qabstractdataproxy.h" -#include "q3dscene.h" +#include "q3dscene_p.h" #include "q3dbox.h" #include <QObject> +#include <QLinearGradient> class QFont; @@ -46,55 +46,45 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE class CameraHelper; class Abstract3DRenderer; +class QAbstract3DSeries; +class ThemeManager; struct Abstract3DChangeBitField { - bool positionChanged : 1; - bool zoomLevelChanged : 1; - bool themeChanged : 1; - bool fontChanged : 1; - bool labelStyleChanged : 1; - bool boundingRectChanged : 1; - bool sizeChanged : 1; - bool shadowQualityChanged : 1; - bool selectionModeChanged : 1; - bool objFileChanged : 1; - bool gridEnabledChanged : 1; - bool backgroundEnabledChanged : 1; - bool axisXTypeChanged : 1; - bool axisYTypeChanged : 1; - bool axisZTypeChanged : 1; - bool axisXTitleChanged : 1; - bool axisYTitleChanged : 1; - bool axisZTitleChanged : 1; - bool axisXLabelsChanged : 1; - bool axisYLabelsChanged : 1; - bool axisZLabelsChanged : 1; - bool axisXRangeChanged : 1; - bool axisYRangeChanged : 1; - bool axisZRangeChanged : 1; - bool axisXSegmentCountChanged : 1; - bool axisYSegmentCountChanged : 1; - bool axisZSegmentCountChanged : 1; - bool axisXSubSegmentCountChanged : 1; - bool axisYSubSegmentCountChanged : 1; - bool axisZSubSegmentCountChanged : 1; - bool axisXLabelFormatChanged : 1; - bool axisYLabelFormatChanged : 1; - bool axisZLabelFormatChanged : 1; + bool zoomLevelChanged : 1; + bool themeChanged : 1; + bool shadowQualityChanged : 1; + bool selectionModeChanged : 1; + bool objFileChanged : 1; + bool axisXTypeChanged : 1; + bool axisYTypeChanged : 1; + bool axisZTypeChanged : 1; + bool axisXTitleChanged : 1; + bool axisYTitleChanged : 1; + bool axisZTitleChanged : 1; + bool axisXLabelsChanged : 1; + bool axisYLabelsChanged : 1; + bool axisZLabelsChanged : 1; + bool axisXRangeChanged : 1; + bool axisYRangeChanged : 1; + bool axisZRangeChanged : 1; + bool axisXSegmentCountChanged : 1; + bool axisYSegmentCountChanged : 1; + bool axisZSegmentCountChanged : 1; + bool axisXSubSegmentCountChanged : 1; + bool axisYSubSegmentCountChanged : 1; + bool axisZSubSegmentCountChanged : 1; + bool axisXLabelFormatChanged : 1; + bool axisYLabelFormatChanged : 1; + bool axisZLabelFormatChanged : 1; + bool inputStateChanged : 1; + bool inputPositionChanged : 1; Abstract3DChangeBitField() : - positionChanged(true), zoomLevelChanged(true), themeChanged(true), - fontChanged(true), - labelStyleChanged(true), - boundingRectChanged(true), - sizeChanged(true), shadowQualityChanged(true), selectionModeChanged(true), objFileChanged(true), - gridEnabledChanged(true), - backgroundEnabledChanged(true), axisXTypeChanged(true), axisYTypeChanged(true), axisZTypeChanged(true), @@ -143,18 +133,11 @@ public: private: Abstract3DChangeBitField m_changeTracker; - QRect m_boundingRect; GLfloat m_horizontalRotation; GLfloat m_verticalRotation; - Theme m_theme; - QFont m_font; - QDataVis::SelectionMode m_selectionMode; + ThemeManager *m_themeManager; + QDataVis::SelectionFlags m_selectionMode; QDataVis::ShadowQuality m_shadowQuality; - QDataVis::LabelStyle m_labelStyle; - bool m_isBackgroundEnabled; - bool m_isGridEnabled; - QString m_objFile; - Q3DScene *m_scene; protected: @@ -169,51 +152,26 @@ protected: QList<Q3DAbstractAxis *> m_axes; // List of all added axes Abstract3DRenderer *m_renderer; bool m_isDataDirty; - - QAbstractDataProxy *m_data; - QList<QAbstractDataProxy *> m_dataProxies; - + bool m_isSeriesVisibilityDirty; + bool m_isSeriesVisualsDirty; bool m_renderPending; - explicit Abstract3DController(QRect boundRect, QObject *parent = 0); + QList<QAbstract3DSeries *> m_seriesList; + + explicit Abstract3DController(QRect initialViewport, QObject *parent = 0); virtual ~Abstract3DController(); public: inline bool isInitialized() { return (m_renderer != 0); } - - /** - * @brief synchDataToRenderer Called on the render thread while main GUI thread is blocked before rendering. - */ virtual void synchDataToRenderer(); - virtual void render(const GLuint defaultFboHandle = 0); - - /** - * @brief setRenderer Sets the renderer to be used. isInitialized returns true from this point onwards. - * @param renderer Renderer to be used. - */ + virtual void initializeOpenGL() = 0; void setRenderer(Abstract3DRenderer *renderer); - // Size - virtual void setSize(const int width, const int height); - virtual const QSize size(); - virtual const QRect boundingRect(); - virtual void setBoundingRect(const QRect boundingRect); - virtual void setWidth(const int width); - virtual int width(); - virtual void setHeight(const int height); - virtual int height(); - virtual void setX(const int x); - virtual int x(); - virtual void setY(const int y); - virtual int y(); - - virtual QRect primarySubViewport() const; - virtual void setPrimarySubViewport(const QRect &primarySubViewport); - - virtual QRect secondarySubViewport() const; - virtual void setSecondarySubViewport(const QRect &secondarySubViewport); + virtual void addSeries(QAbstract3DSeries *series); + virtual void removeSeries(QAbstract3DSeries *series); + QList<QAbstract3DSeries *> seriesList(); virtual void setAxisX(Q3DAbstractAxis *axis); virtual Q3DAbstractAxis *axisX(); @@ -230,63 +188,27 @@ public: virtual void setActiveInputHandler(QAbstract3DInputHandler *inputHandler); virtual QAbstract3DInputHandler *activeInputHandler(); - virtual QAbstractDataProxy *activeDataProxy() const; - virtual void addDataProxy(QAbstractDataProxy *proxy); - virtual void releaseDataProxy(QAbstractDataProxy *proxy); - virtual QList<QAbstractDataProxy *> dataProxies() const; - virtual void setActiveDataProxy(QAbstractDataProxy *proxy); - virtual void updateDevicePixelRatio(qreal ratio); - virtual int zoomLevel(); virtual void setZoomLevel(int zoomLevel); - // Set color if you don't want to use themes. - virtual void setObjectColor(const QColor &baseColor, bool uniform = true); - virtual QColor objectColor() const; - - // Set theme (bar colors, shaders, window color, background colors, light intensity and text - // colors are affected) - virtual void setTheme(QDataVis::Theme theme); - virtual Theme theme(); + virtual void setTheme(Q3DTheme *theme); + virtual Q3DTheme *theme() const; - // Set font - virtual void setFont(const QFont &font); - virtual QFont font(); + virtual void setSelectionMode(QDataVis::SelectionFlags mode); + virtual QDataVis::SelectionFlags selectionMode() const; - // Selection mode - virtual void setSelectionMode(QDataVis::SelectionMode mode); - virtual QDataVis::SelectionMode selectionMode(); - - // Adjust shadow quality virtual void setShadowQuality(QDataVis::ShadowQuality quality); - virtual QDataVis::ShadowQuality shadowQuality(); - - // Label style adjustment - virtual void setLabelStyle(QDataVis::LabelStyle style); - virtual QDataVis::LabelStyle labelStyle(); + virtual QDataVis::ShadowQuality shadowQuality() const; - // Enable or disable background mesh - virtual void setBackgroundEnabled(bool enable); - virtual bool backgroundEnabled(); - - // Enable or disable background grid - virtual void setGridEnabled(bool enable); - virtual bool gridEnabled(); - - // Query input state and position - QDataVis::InputState inputState(); - QPoint inputPosition(); - - // Enable or disable slicing mode - bool isSlicingActive(); + bool isSlicingActive() const; void setSlicingActive(bool isSlicing); + Q3DScene *scene(); - // override bar type with own mesh - virtual void setMeshFileName(const QString &fileName); - virtual QString meshFileName(); + void markDataDirty(); + void markSeriesVisualsDirty(); - Q3DScene *scene(); + void emitNeedRender(); virtual void mouseDoubleClickEvent(QMouseEvent *event); virtual void touchEvent(QTouchEvent *event); @@ -303,32 +225,50 @@ public: virtual void handleAxisAutoAdjustRangeChangedInOrientation( Q3DAbstractAxis::AxisOrientation orientation, bool autoAdjust) = 0; virtual void handleAxisLabelFormatChangedBySender(QObject *sender); + virtual void handleSeriesVisibilityChangedBySender(QObject *sender); public slots: void handleAxisTitleChanged(const QString &title); void handleAxisLabelsChanged(); - void handleAxisRangeChanged(qreal min, qreal max); + void handleAxisRangeChanged(float min, float max); void handleAxisSegmentCountChanged(int count); void handleAxisSubSegmentCountChanged(int count); void handleAxisAutoAdjustRangeChanged(bool autoAdjust); void handleAxisLabelFormatChanged(const QString &format); + void handleInputStateChanged(QAbstract3DInputHandler::InputState state); + void handleInputPositionChanged(const QPoint &position); + void handleSeriesVisibilityChanged(bool visible); + + void handleThemeColorStyleChanged(Q3DTheme::ColorStyle style); + void handleThemeBaseColorsChanged(const QList<QColor> &color); + void handleThemeBaseGradientsChanged(const QList<QLinearGradient> &gradient); + void handleThemeSingleHighlightColorChanged(const QColor &color); + void handleThemeSingleHighlightGradientChanged(const QLinearGradient &gradient); + void handleThemeMultiHighlightColorChanged(const QColor &color); + void handleThemeMultiHighlightGradientChanged(const QLinearGradient &gradient); + + // Renderer callback handlers + void handleRequestShadowQuality(QDataVis::ShadowQuality quality); signals: void shadowQualityChanged(QDataVis::ShadowQuality quality); void activeInputHandlerChanged(QAbstract3DInputHandler *inputHandler); + void themeChanged(Q3DTheme *theme); + void selectionModeChanged(QDataVis::SelectionFlags mode); void needRender(); protected: virtual Q3DAbstractAxis *createDefaultAxis(Q3DAbstractAxis::AxisOrientation orientation); Q3DValueAxis *createDefaultValueAxis(); Q3DCategoryAxis *createDefaultCategoryAxis(); - void emitNeedRender(); private: void setAxisHelper(Q3DAbstractAxis::AxisOrientation orientation, Q3DAbstractAxis *axis, Q3DAbstractAxis **axisPtr); + + friend class Bars3DController; }; QT_DATAVISUALIZATION_END_NAMESPACE -#endif // CONTROLLER3DBASE_H +#endif diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index eef810df..74088b7c 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -23,24 +23,27 @@ #include "q3dscene_p.h" #include "q3dcamera_p.h" #include "q3dlight_p.h" +#include "qabstract3dseries_p.h" +#include "q3dtheme_p.h" +#include "objecthelper_p.h" + +Q_DECLARE_METATYPE(QtDataVisualization::QDataVis::ShadowQuality) QT_DATAVISUALIZATION_BEGIN_NAMESPACE Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) : QObject(0), - m_controller(controller), m_hasNegativeValues(false), - m_cachedTheme(), - m_cachedFont(QFont(QStringLiteral("Arial"))), - m_cachedLabelStyle(QDataVis::LabelStyleFromTheme), - m_drawer(new Drawer(m_cachedTheme, m_cachedFont, m_cachedLabelStyle)), - m_cachedBoundingRect(QRect(0,0,0,0)), + m_cachedTheme(new Q3DTheme()), + m_drawer(new Drawer(m_cachedTheme)), m_cachedShadowQuality(QDataVis::ShadowQualityMedium), m_autoScaleAdjustment(1.0f), - m_cachedSelectionMode(QDataVis::SelectionModeNone), - m_cachedIsGridEnabled(false), - m_cachedIsBackgroundEnabled(false), - m_cachedScene(new Q3DScene()) + m_cachedSelectionMode(QDataVis::SelectionNone), + m_textureHelper(0), + m_cachedScene(new Q3DScene()), + m_selectionDirty(true), + m_selectionState(SelectNone), + m_devicePixelRatio(1.0f) #ifdef DISPLAY_RENDER_SPEED , m_isFirstFrame(true), m_numFrames(0) @@ -48,15 +51,21 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) { QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); - QObject::connect(this, &Abstract3DRenderer::needRender, m_controller, + QObject::connect(this, &Abstract3DRenderer::needRender, controller, &Abstract3DController::needRender, Qt::QueuedConnection); + QObject::connect(this, &Abstract3DRenderer::requestShadowQuality, controller, + &Abstract3DController::handleRequestShadowQuality, Qt::QueuedConnection); } Abstract3DRenderer::~Abstract3DRenderer() { + for (int i = 0; i < m_visibleSeriesList.size(); i++) + m_visibleSeriesList[i].cleanup(m_textureHelper); + delete m_drawer; delete m_textureHelper; delete m_cachedScene; + delete m_cachedTheme; } void Abstract3DRenderer::initializeOpenGL() @@ -93,7 +102,7 @@ void Abstract3DRenderer::render(const GLuint defaultFboHandle) // 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)"; + qDebug() << float(m_lastFrameTime.elapsed()) / float(m_numFrames) << "ms/frame (=" << float(m_numFrames) << "fps)"; m_numFrames = 0; m_lastFrameTime.restart(); } @@ -108,17 +117,17 @@ void Abstract3DRenderer::render(const GLuint defaultFboHandle) glDisable(GL_BLEND); // For QtQuick2 blending is enabled by default, but we don't want it to be } - glViewport(m_cachedScene->viewport().x(), - m_cachedScene->viewport().y(), - m_cachedScene->viewport().width(), - m_cachedScene->viewport().height()); - - QVector3D clearColor = Utils::vectorFromColor(m_cachedTheme.m_windowColor); + // Clear the graph background to the theme color + glViewport(m_viewport.x(), + m_viewport.y(), + m_viewport.width(), + m_viewport.height()); + QVector3D clearColor = Utils::vectorFromColor(m_cachedTheme->windowColor()); glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } -QString Abstract3DRenderer::generateValueLabel(const QString &format, qreal value) +QString Abstract3DRenderer::generateValueLabel(const QString &format, float value) { QString valueLabelFormat = format; Utils::ParamType valueParamType = Utils::findFormatParamType(valueLabelFormat); @@ -126,126 +135,140 @@ QString Abstract3DRenderer::generateValueLabel(const QString &format, qreal valu return Utils::formatLabel(valueFormatArray, valueParamType, value); } -void Abstract3DRenderer::updateDataModel(QAbstractDataProxy *dataProxy) +void Abstract3DRenderer::updateSelectionState(SelectionState state) { - m_cachedItemLabelFormat = dataProxy->itemLabelFormat(); + m_selectionState = state; } -QString Abstract3DRenderer::itemLabelFormat() const +void Abstract3DRenderer::updateInputPosition(const QPoint &position) { - return m_cachedItemLabelFormat; + m_inputPosition = position; } -void Abstract3DRenderer::updateBoundingRect(const QRect &boundingRect) +void Abstract3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader) { - m_cachedBoundingRect = boundingRect; - handleResize(); + // Do nothing by default + Q_UNUSED(vertexShader) + Q_UNUSED(fragmentShader) } -void Abstract3DRenderer::updatePosition(const QRect &boundingRect) +void Abstract3DRenderer::updateTheme(Q3DTheme *theme) { - m_cachedBoundingRect = boundingRect; + // Synchronize the controller theme with renderer + bool changed = theme->d_ptr->sync(*m_cachedTheme->d_ptr); + + if (changed) { + // Update drawer if sync changed something + m_drawer->setTheme(m_cachedTheme); + // Re-initialize shaders + reInitShaders(); + } } -void Abstract3DRenderer::updateTheme(Theme theme) +void Abstract3DRenderer::updateScene(Q3DScene *scene) { - m_cachedTheme.setFromTheme(theme); + m_viewport = scene->d_ptr->glViewport(); + m_secondarySubViewport = scene->d_ptr->glSecondarySubViewport(); - m_drawer->setTheme(m_cachedTheme); - // Re-initialize shaders - handleShadowQualityChange(); -} + if (m_primarySubViewport != scene->d_ptr->glPrimarySubViewport()) { + // Resize of primary subviewport means resizing shadow and selection buffers + m_primarySubViewport = scene->d_ptr->glPrimarySubViewport(); + handleResize(); + } -void Abstract3DRenderer::updateScene(Q3DScene *scene) -{ - // Synchronize the controller scene to that of the renderer, and vice versa. - // Controller scene had priority if both have changed same values. + if (m_devicePixelRatio != scene->devicePixelRatio()) { + m_devicePixelRatio = scene->devicePixelRatio(); + handleResize(); + } + + scene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); + // Set light position (rotate light with activeCamera, a bit above it (as set in defaultLightPos)) + scene->setLightPositionRelativeToCamera(defaultLightPos); + + QPoint logicalPixelPosition = scene->selectionQueryPosition(); + updateInputPosition(QPoint(logicalPixelPosition.x() * m_devicePixelRatio, + logicalPixelPosition.y() * m_devicePixelRatio)); + + if (Q3DScene::invalidSelectionPoint() == logicalPixelPosition) { + updateSelectionState(SelectNone); + } else { + // Selections are one-shot, reset selection active to false before processing + scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint()); + + if (scene->isSlicingActive()) { + if (scene->isPointInPrimarySubView(logicalPixelPosition)) + updateSelectionState(SelectOnOverview); + else if (scene->isPointInSecondarySubView(logicalPixelPosition)) + updateSelectionState(SelectOnSlice); + else + updateSelectionState(SelectNone); + } else { + updateSelectionState(SelectOnScene); + } + } + + // Synchronize the controller scene with renderer scene->d_ptr->sync(*m_cachedScene->d_ptr); - m_cachedScene->d_ptr->sync(*scene->d_ptr); } -void Abstract3DRenderer::handleShadowQualityChange() +void Abstract3DRenderer::reInitShaders() { #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { - if (!m_cachedTheme.m_uniformColor) { - initShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); - } else { - initShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTex")); - } + initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); + 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")); - } + initGradientShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); + 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"), + initGradientShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnYES2")); + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentES2")); + initBackgroundShaders(QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentES2")); #endif } -void Abstract3DRenderer::updateFont(const QFont &font) -{ - m_cachedFont = font; - m_drawer->setFont(font); -} - -void Abstract3DRenderer::updateLabelStyle(QDataVis::LabelStyle style) +void Abstract3DRenderer::handleShadowQualityChange() { - m_cachedLabelStyle = style; - m_drawer->setStyle(style); -} + reInitShaders(); -void Abstract3DRenderer::updateMeshFileName(const QString &objFileName) -{ - if (objFileName != m_cachedObjFile) { - m_cachedObjFile = objFileName; - loadMeshFile(); +#if defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality != QDataVis::ShadowQualityNone) { + emit requestShadowQuality(QDataVis::ShadowQualityNone); + qWarning("Shadows are not yet supported for OpenGL ES2"); + m_cachedShadowQuality = QDataVis::ShadowQualityNone; } +#endif } -void Abstract3DRenderer::updateSelectionMode(QDataVis::SelectionMode mode) +void Abstract3DRenderer::updateSelectionMode(QDataVis::SelectionFlags mode) { m_cachedSelectionMode = mode; -} - -void Abstract3DRenderer::updateGridEnabled(bool enable) -{ - m_cachedIsGridEnabled = enable; -} - -void Abstract3DRenderer::updateBackgroundEnabled(bool enable) -{ - m_cachedIsBackgroundEnabled = enable; + m_selectionDirty = true; } void Abstract3DRenderer::handleResize() { - if (m_cachedBoundingRect.width() == 0 || m_cachedBoundingRect.height() == 0) + if (m_primarySubViewport.width() == 0 || m_primarySubViewport.height() == 0) return; + // 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)); + div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height()); + zoomAdjustment = defaultRatio * ((m_primarySubViewport.width() / div) + / (m_primarySubViewport.height() / div)); m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f // Re-init selection buffer @@ -272,7 +295,7 @@ void Abstract3DRenderer::updateAxisLabels(Q3DAbstractAxis::AxisOrientation orien axisCacheForOrientation(orientation).setLabels(labels); } -void Abstract3DRenderer::updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, qreal min, qreal max) +void Abstract3DRenderer::updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, float min, float max) { AxisRenderCache &cache = axisCacheForOrientation(orientation); cache.setMin(min); @@ -294,6 +317,42 @@ void Abstract3DRenderer::updateAxisLabelFormat(Q3DAbstractAxis::AxisOrientation axisCacheForOrientation(orientation).setLabelFormat(format); } +void Abstract3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh) +{ + // Default implementation does nothing. + Q_UNUSED(fileName) + Q_UNUSED(mesh) +} + +void Abstract3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList, + bool updateVisibility) +{ + int visibleCount = 0; + if (updateVisibility) { + int oldSize = m_visibleSeriesList.size(); + foreach (QAbstract3DSeries *current, seriesList) { + if (current->isVisible()) + visibleCount++; + } + + // Clean up series caches that are about to be permanently deleted. + // Can't just use cache destructor, as resize will call that to all items. + if (visibleCount < oldSize) { + for (int i = visibleCount; i < oldSize; i++) + m_visibleSeriesList[i].cleanup(m_textureHelper); + } + + if (visibleCount != oldSize) + m_visibleSeriesList.resize(visibleCount); + + visibleCount = 0; + } + foreach (QAbstract3DSeries *current, seriesList) { + if (current->isVisible()) + m_visibleSeriesList[visibleCount++].populate(current, this); + } +} + AxisRenderCache &Abstract3DRenderer::axisCacheForOrientation(Q3DAbstractAxis::AxisOrientation orientation) { switch (orientation) { @@ -309,5 +368,56 @@ AxisRenderCache &Abstract3DRenderer::axisCacheForOrientation(Q3DAbstractAxis::Ax } } +void Abstract3DRenderer::lowerShadowQuality() +{ + QDataVis::ShadowQuality newQuality = QDataVis::ShadowQualityNone; + + switch (m_cachedShadowQuality) { + case QDataVis::ShadowQualityHigh: + qWarning("Creating high quality shadows failed. Changing to medium quality."); + newQuality = QDataVis::ShadowQualityMedium; + break; + case QDataVis::ShadowQualityMedium: + qWarning("Creating medium quality shadows failed. Changing to low quality."); + newQuality = QDataVis::ShadowQualityLow; + break; + case QDataVis::ShadowQualityLow: + qWarning("Creating low quality shadows failed. Switching shadows off."); + newQuality = QDataVis::ShadowQualityNone; + break; + case QDataVis::ShadowQualitySoftHigh: + qWarning("Creating soft high quality shadows failed. Changing to soft medium quality."); + newQuality = QDataVis::ShadowQualitySoftMedium; + break; + case QDataVis::ShadowQualitySoftMedium: + qWarning("Creating soft medium quality shadows failed. Changing to soft low quality."); + newQuality = QDataVis::ShadowQualitySoftLow; + break; + case QDataVis::ShadowQualitySoftLow: + qWarning("Creating soft low quality shadows failed. Switching shadows off."); + newQuality = QDataVis::ShadowQualityNone; + break; + default: + // You'll never get here + break; + } + + emit requestShadowQuality(newQuality); + updateShadowQuality(newQuality); +} + +void Abstract3DRenderer::fixGradientAndGenerateTexture(QLinearGradient *gradient, GLuint *gradientTexture) +{ + // Readjust start/stop to match gradient texture size + gradient->setStart(qreal(gradientTextureWidth), qreal(gradientTextureHeight)); + gradient->setFinalStop(0.0, 0.0); + + if (*gradientTexture) { + m_textureHelper->deleteTexture(gradientTexture); + *gradientTexture = 0; + } + + *gradientTexture = m_textureHelper->createGradientTexture(*gradient); +} QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 1c61ac07..17a55ac9 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -37,6 +37,7 @@ #include "abstract3dcontroller_p.h" #include "axisrendercache_p.h" #include "qabstractdataproxy.h" +#include "seriesrendercache_p.h" //#define DISPLAY_RENDER_SPEED @@ -50,90 +51,98 @@ class Abstract3DRenderer : public QObject, protected QOpenGLFunctions { Q_OBJECT -private: - Abstract3DController *m_controller; - protected: - bool m_hasNegativeValues; - Theme m_cachedTheme; - QFont m_cachedFont; - QDataVis::LabelStyle m_cachedLabelStyle; - Drawer *m_drawer; - QRect m_cachedBoundingRect; - QDataVis::ShadowQuality m_cachedShadowQuality; - GLfloat m_autoScaleAdjustment; - - QString m_cachedItemLabelFormat; - QString m_cachedObjFile; - QDataVis::SelectionMode m_cachedSelectionMode; - bool m_cachedIsGridEnabled; - bool m_cachedIsBackgroundEnabled; - - AxisRenderCache m_axisCacheX; - AxisRenderCache m_axisCacheY; - AxisRenderCache m_axisCacheZ; - TextureHelper *m_textureHelper; - Q3DBox m_boundingBox; + enum SelectionState { + SelectNone = 0, + SelectOnScene, + SelectOnOverview, + SelectOnSlice + }; - Q3DScene *m_cachedScene; - -#ifdef DISPLAY_RENDER_SPEED - bool m_isFirstFrame; - QTime m_lastFrameTime; - GLint m_numFrames; -#endif - - QString generateValueLabel(const QString &format, qreal value); + QString generateValueLabel(const QString &format, float value); public: virtual ~Abstract3DRenderer(); - void updateDataModel(QAbstractDataProxy *dataProxy); + virtual void updateData() = 0; + virtual void updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility); virtual void render(GLuint defaultFboHandle); - virtual void updateBoundingRect(const QRect &boundingRect); - virtual void updatePosition(const QRect &boundingRect); - - virtual void updateTheme(Theme theme); - virtual void updateFont(const QFont &font); - virtual void updateLabelStyle(QDataVis::LabelStyle style); - virtual void updateSelectionMode(QDataVis::SelectionMode newMode); - virtual void updateGridEnabled(bool enable); - virtual void updateBackgroundEnabled(bool enable); - virtual void updateMeshFileName(const QString &objFileName); + virtual void updateTheme(Q3DTheme *theme); + virtual void updateSelectionMode(QDataVis::SelectionFlags newMode); virtual void updateScene(Q3DScene *scene); - virtual QString itemLabelFormat() const; virtual void updateTextures() = 0; virtual void initSelectionBuffer() = 0; + virtual void updateSelectionState(SelectionState state); + virtual void updateInputPosition(const QPoint &position); #if !defined(QT_OPENGL_ES_2) virtual void updateDepthBuffer() = 0; #endif virtual void updateShadowQuality(QDataVis::ShadowQuality quality) = 0; virtual void initShaders(const QString &vertexShader, const QString &fragmentShader) = 0; + virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader); virtual void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) = 0; virtual void updateAxisType(Q3DAbstractAxis::AxisOrientation orientation, Q3DAbstractAxis::AxisType type); virtual void updateAxisTitle(Q3DAbstractAxis::AxisOrientation orientation, const QString &title); virtual void updateAxisLabels(Q3DAbstractAxis::AxisOrientation orientation, const QStringList &labels); - virtual void updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, qreal min, qreal max); + virtual void updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, float min, float max); virtual void updateAxisSegmentCount(Q3DAbstractAxis::AxisOrientation orientation, int count); virtual void updateAxisSubSegmentCount(Q3DAbstractAxis::AxisOrientation orientation, int count); virtual void updateAxisLabelFormat(Q3DAbstractAxis::AxisOrientation orientation, const QString &format); + virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh); + void fixGradientAndGenerateTexture(QLinearGradient *gradient, GLuint *gradientTexture); + signals: void needRender(); // Emit this if something in renderer causes need for another render pass. + void requestShadowQuality(QDataVis::ShadowQuality quality); // For automatic quality adjustments protected: Abstract3DRenderer(Abstract3DController *controller); virtual void initializeOpenGL(); + void reInitShaders(); virtual void handleShadowQualityChange(); virtual void handleResize(); - virtual void loadMeshFile() = 0; AxisRenderCache &axisCacheForOrientation(Q3DAbstractAxis::AxisOrientation orientation); + + virtual void lowerShadowQuality(); + + void fixGradient(QLinearGradient *gradient, GLuint *gradientTexture); + + bool m_hasNegativeValues; + Q3DTheme *m_cachedTheme; + Drawer *m_drawer; + QRect m_viewport; + QDataVis::ShadowQuality m_cachedShadowQuality; + GLfloat m_autoScaleAdjustment; + + QDataVis::SelectionFlags m_cachedSelectionMode; + + AxisRenderCache m_axisCacheX; + AxisRenderCache m_axisCacheY; + AxisRenderCache m_axisCacheZ; + TextureHelper *m_textureHelper; + Q3DBox m_boundingBox; + + Q3DScene *m_cachedScene; + bool m_selectionDirty; + SelectionState m_selectionState; + QPoint m_inputPosition; + QVector<SeriesRenderCache> m_visibleSeriesList; + QRect m_primarySubViewport; + QRect m_secondarySubViewport; + float m_devicePixelRatio; + +#ifdef DISPLAY_RENDER_SPEED + bool m_isFirstFrame; + QTime m_lastFrameTime; + GLint m_numFrames; +#endif }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp index 55ac0765..f7960b2b 100644 --- a/src/datavisualization/engine/axisrendercache.cpp +++ b/src/datavisualization/engine/axisrendercache.cpp @@ -24,8 +24,8 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE AxisRenderCache::AxisRenderCache() : m_type(Q3DAbstractAxis::AxisTypeNone), - m_min(0.0), - m_max(10.0), + m_min(0.0f), + m_max(10.0f), m_segmentCount(5), m_subSegmentCount(1), m_font(QFont(QStringLiteral("Arial"))), @@ -58,8 +58,8 @@ void AxisRenderCache::setType(Q3DAbstractAxis::AxisType type) // If type is set, it means completely new axis instance, so clear all old data m_labels.clear(); m_title.clear(); - m_min = 0.0; - m_max = 10.0; + m_min = 0.0f; + m_max = 10.0f; m_segmentCount = 5; m_subSegmentCount = 1; m_labelFormat.clear(); @@ -109,13 +109,13 @@ void AxisRenderCache::setLabels(const QStringList &labels) } } -void AxisRenderCache::setMin(qreal min) +void AxisRenderCache::setMin(float min) { m_min = min; updateSegmentStep(); } -void AxisRenderCache::setMax(qreal max) +void AxisRenderCache::setMax(float max) { m_max = max; updateSegmentStep(); diff --git a/src/datavisualization/engine/axisrendercache_p.h b/src/datavisualization/engine/axisrendercache_p.h index 0bb1cf92..cddc7839 100644 --- a/src/datavisualization/engine/axisrendercache_p.h +++ b/src/datavisualization/engine/axisrendercache_p.h @@ -51,10 +51,10 @@ public: inline const QString &title() { return m_title; } void setLabels(const QStringList &labels); inline const QStringList &labels() { return m_labels; } - void setMin(qreal min); - inline qreal min() { return m_min; } - void setMax(qreal max); - inline qreal max() { return m_max; } + void setMin(float min); + inline float min() { return m_min; } + void setMax(float max); + inline float max() { return m_max; } void setSegmentCount(int count); inline int segmentCount() const { return m_segmentCount; } void setSubSegmentCount(int count); @@ -79,8 +79,8 @@ private: Q3DAbstractAxis::AxisType m_type; QString m_title; QStringList m_labels; - qreal m_min; - qreal m_max; + float m_min; + float m_max; int m_segmentCount; int m_subSegmentCount; QString m_labelFormat; diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp index 2eea6c74..5232a566 100644 --- a/src/datavisualization/engine/bars3dcontroller.cpp +++ b/src/datavisualization/engine/bars3dcontroller.cpp @@ -23,6 +23,9 @@ #include "q3dvalueaxis_p.h" #include "q3dcategoryaxis_p.h" #include "qbardataproxy_p.h" +#include "qbar3dseries_p.h" +#include "thememanager_p.h" +#include "q3dtheme_p.h" #include <QMatrix4x4> #include <qmath.h> @@ -31,17 +34,13 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE Bars3DController::Bars3DController(QRect boundRect) : Abstract3DController(boundRect), - m_selectedBarPos(noSelectionPoint()), + m_selectedBar(invalidSelectionPosition()), + m_selectedBarSeries(0), m_isBarSpecRelative(true), m_barThicknessRatio(1.0f), m_barSpacing(QSizeF(1.0, 1.0)), m_renderer(0) { - // Default bar type; specific to bars - setBarType(QDataVis::MeshStyleBevelBars, false); - - setActiveDataProxy(0); - // Setting a null axis creates a new default axis according to orientation and graph type. // Note: these cannot be set in the Abstract3DController constructor, as they will call virtual // functions implemented by subclasses. @@ -65,13 +64,20 @@ void Bars3DController::initializeOpenGL() setRenderer(m_renderer); synchDataToRenderer(); - QObject::connect(m_renderer, &Bars3DRenderer::selectedBarPosChanged, this, - &Bars3DController::handleSelectedBarPosChanged, Qt::QueuedConnection); + QObject::connect(m_renderer, &Bars3DRenderer::barClicked, this, + &Bars3DController::handleBarClicked, Qt::QueuedConnection); emitNeedRender(); } void Bars3DController::synchDataToRenderer() { + // Background change requires reloading the meshes in bar graphs, so dirty the series visuals + if (m_themeManager->theme()->d_ptr->m_dirtyBits.backgroundEnabledDirty) { + m_isSeriesVisualsDirty = true; + foreach (QAbstract3DSeries *series, m_seriesList) + series->d_ptr->m_changeTracker.meshChanged = true; + } + Abstract3DController::synchDataToRenderer(); if (!isInitialized()) @@ -83,67 +89,19 @@ void Bars3DController::synchDataToRenderer() m_changeTracker.barSpecsChanged = false; } - if (m_changeTracker.selectedBarPosChanged) { - m_renderer->updateSelectedBarPos(m_selectedBarPos); - m_changeTracker.selectedBarPosChanged = false; - } - - if (m_isDataDirty) { - m_renderer->updateDataModel(static_cast<QBarDataProxy *>(m_data)); - m_isDataDirty = false; + // Needs to be done after data is set, as it needs to know the visual array. + if (m_changeTracker.selectedBarChanged) { + m_renderer->updateSelectedBar(m_selectedBar, m_selectedBarSeries); + m_changeTracker.selectedBarChanged = false; } } -void Bars3DController::setActiveDataProxy(QAbstractDataProxy *proxy) -{ - // Setting null proxy indicates default proxy - if (!proxy) { - proxy = new QBarDataProxy; - proxy->d_ptr->setDefaultProxy(true); - } - - Q_ASSERT(proxy->type() == QAbstractDataProxy::DataTypeBar); - - Abstract3DController::setActiveDataProxy(proxy); - - QBarDataProxy *barDataProxy = static_cast<QBarDataProxy *>(m_data); - - QObject::connect(barDataProxy, &QBarDataProxy::arrayReset, this, - &Bars3DController::handleArrayReset); - QObject::connect(barDataProxy, &QBarDataProxy::rowsAdded, this, - &Bars3DController::handleRowsAdded); - QObject::connect(barDataProxy, &QBarDataProxy::rowsChanged, this, - &Bars3DController::handleRowsChanged); - QObject::connect(barDataProxy, &QBarDataProxy::rowsRemoved, this, - &Bars3DController::handleRowsRemoved); - QObject::connect(barDataProxy, &QBarDataProxy::rowsInserted, this, - &Bars3DController::handleRowsInserted); - QObject::connect(barDataProxy, &QBarDataProxy::itemChanged, this, - &Bars3DController::handleItemChanged); - QObject::connect(barDataProxy, &QBarDataProxy::rowLabelsChanged, this, - &Bars3DController::handleDataRowLabelsChanged); - QObject::connect(barDataProxy, &QBarDataProxy::columnLabelsChanged, this, - &Bars3DController::handleDataColumnLabelsChanged); - - scene()->setSlicingActive(false); - adjustAxisRanges(); - - // Always clear selection on proxy change - setSelectedBarPos(noSelectionPoint()); - - handleDataRowLabelsChanged(); - handleDataColumnLabelsChanged(); - m_isDataDirty = true; - emitNeedRender(); -} - void Bars3DController::handleArrayReset() { - scene()->setSlicingActive(false); adjustAxisRanges(); m_isDataDirty = true; // Clear selection unless still valid - setSelectedBarPos(m_selectedBarPos); + setSelectedBar(m_selectedBar, m_selectedBarSeries); emitNeedRender(); } @@ -151,8 +109,6 @@ void Bars3DController::handleRowsAdded(int startIndex, int count) { Q_UNUSED(startIndex) Q_UNUSED(count) - // TODO should update slice instead of deactivating? - scene()->setSlicingActive(false); adjustAxisRanges(); m_isDataDirty = true; emitNeedRender(); @@ -162,8 +118,6 @@ void Bars3DController::handleRowsChanged(int startIndex, int count) { Q_UNUSED(startIndex) Q_UNUSED(count) - // TODO should update slice instead of deactivating? - scene()->setSlicingActive(false); adjustAxisRanges(); m_isDataDirty = true; emitNeedRender(); @@ -173,13 +127,11 @@ void Bars3DController::handleRowsRemoved(int startIndex, int count) { Q_UNUSED(startIndex) Q_UNUSED(count) - // TODO should update slice instead of deactivating? - scene()->setSlicingActive(false); adjustAxisRanges(); m_isDataDirty = true; // Clear selection unless still valid - setSelectedBarPos(m_selectedBarPos); + setSelectedBar(m_selectedBar, m_selectedBarSeries); emitNeedRender(); } @@ -188,8 +140,6 @@ void Bars3DController::handleRowsInserted(int startIndex, int count) { Q_UNUSED(startIndex) Q_UNUSED(count) - // TODO should update slice instead of deactivating? - scene()->setSlicingActive(false); adjustAxisRanges(); m_isDataDirty = true; emitNeedRender(); @@ -199,8 +149,6 @@ void Bars3DController::handleItemChanged(int rowIndex, int columnIndex) { Q_UNUSED(rowIndex) Q_UNUSED(columnIndex) - // TODO should update slice instead of deactivating? - scene()->setSlicingActive(false); adjustAxisRanges(); m_isDataDirty = true; emitNeedRender(); @@ -208,36 +156,39 @@ void Bars3DController::handleItemChanged(int rowIndex, int columnIndex) void Bars3DController::handleDataRowLabelsChanged() { - if (m_axisX && m_data) { + QBar3DSeries *firstSeries = 0; + if (m_seriesList.size()) + firstSeries = static_cast<QBar3DSeries *>(m_seriesList.at(0)); + if (m_axisZ && firstSeries && firstSeries->dataProxy()) { // Grab a sublist equal to data window (no need to have more labels in axis) - int min = int(m_axisX->min()); - int count = int(m_axisX->max()) - min + 1; - QStringList subList = static_cast<QBarDataProxy *>(m_data)->rowLabels().mid(min, count); - static_cast<Q3DCategoryAxis *>(m_axisX)->dptr()->setDataLabels(subList); + int min = int(m_axisZ->min()); + int count = int(m_axisZ->max()) - min + 1; + QStringList subList = firstSeries->dataProxy()->rowLabels().mid(min, count); + static_cast<Q3DCategoryAxis *>(m_axisZ)->dptr()->setDataLabels(subList); } } void Bars3DController::handleDataColumnLabelsChanged() { - if (m_axisZ && m_data) { + QBar3DSeries *firstSeries = 0; + if (m_seriesList.size()) + firstSeries = static_cast<QBar3DSeries *>(m_seriesList.at(0)); + if (m_axisX && firstSeries && firstSeries->dataProxy()) { // Grab a sublist equal to data window (no need to have more labels in axis) - int min = int(m_axisZ->min()); - int count = int(m_axisZ->max()) - min + 1; - QStringList subList = static_cast<QBarDataProxy *>(m_data)->columnLabels().mid(min, count); - static_cast<Q3DCategoryAxis *>(m_axisZ)->dptr()->setDataLabels(subList); + int min = int(m_axisX->min()); + int count = int(m_axisX->max()) - min + 1; + QStringList subList = static_cast<QBarDataProxy *>(firstSeries->dataProxy()) + ->columnLabels().mid(min, count); + static_cast<Q3DCategoryAxis *>(m_axisX)->dptr()->setDataLabels(subList); } } -void Bars3DController::handleSelectedBarPosChanged(const QPoint &position) +void Bars3DController::handleBarClicked(const QPoint &position, QBar3DSeries *series) { - QPoint pos = position; - if (pos == QPoint(255, 255)) - pos = noSelectionPoint(); - if (pos != m_selectedBarPos) { - m_selectedBarPos = pos; - emit selectedBarPosChanged(pos); - emitNeedRender(); - } + setSelectedBar(position, series); + + // TODO: pass clicked to parent. (QTRD-2517) + // TODO: Also hover needed? (QTRD-2131) } void Bars3DController::handleAxisAutoAdjustRangeChangedInOrientation( @@ -248,41 +199,97 @@ void Bars3DController::handleAxisAutoAdjustRangeChangedInOrientation( adjustAxisRanges(); } -QPoint Bars3DController::noSelectionPoint() +void Bars3DController::handleSeriesVisibilityChangedBySender(QObject *sender) +{ + Abstract3DController::handleSeriesVisibilityChangedBySender(sender); + + // Visibility changes may require disabling/enabling slicing, + // so just reset selection to ensure everything is still valid. + setSelectedBar(m_selectedBar, m_selectedBarSeries); +} + +QPoint Bars3DController::invalidSelectionPosition() { - static QPoint noSelectionPos(-1, -1); - return noSelectionPos; + static QPoint invalidSelectionPos(-1, -1); + return invalidSelectionPos; } void Bars3DController::setAxisX(Q3DAbstractAxis *axis) { Abstract3DController::setAxisX(axis); - handleDataRowLabelsChanged(); + handleDataColumnLabelsChanged(); } void Bars3DController::setAxisZ(Q3DAbstractAxis *axis) { Abstract3DController::setAxisZ(axis); - handleDataColumnLabelsChanged(); + handleDataRowLabelsChanged(); +} + +void Bars3DController::addSeries(QAbstract3DSeries *series) +{ + Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesTypeBar); + + bool firstAdded = !m_seriesList.size(); + + Abstract3DController::addSeries(series); + + if (firstAdded) { + adjustAxisRanges(); + + handleDataRowLabelsChanged(); + handleDataColumnLabelsChanged(); + } + + QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(series); + if (barSeries->selectedBar() != invalidSelectionPosition()) + setSelectedBar(barSeries->selectedBar(), barSeries); +} + +void Bars3DController::removeSeries(QAbstract3DSeries *series) +{ + bool firstRemoved = (m_seriesList.size() && m_seriesList.at(0) == series); + + Abstract3DController::removeSeries(series); + + if (m_selectedBarSeries == series) + setSelectedBar(invalidSelectionPosition(), 0); + + if (firstRemoved) { + adjustAxisRanges(); + + handleDataRowLabelsChanged(); + handleDataColumnLabelsChanged(); + } +} + +QList<QBar3DSeries *> Bars3DController::barSeriesList() +{ + QList<QAbstract3DSeries *> abstractSeriesList = seriesList(); + QList<QBar3DSeries *> barSeriesList; + foreach (QAbstract3DSeries *abstractSeries, abstractSeriesList) { + QBar3DSeries *barSeries = qobject_cast<QBar3DSeries *>(abstractSeries); + if (barSeries) + barSeriesList.append(barSeries); + } + + return barSeriesList; } void Bars3DController::handleAxisRangeChangedBySender(QObject *sender) { // Data window changed if (sender == m_axisX || sender == m_axisZ) { - // Disable zoom mode if we're in it (causes crash if not, as zoom selection is deleted) - scene()->setSlicingActive(false); - - // Clear selection unless still valid - setSelectedBarPos(m_selectedBarPos); - if (sender == m_axisX) - handleDataRowLabelsChanged(); - if (sender == m_axisZ) handleDataColumnLabelsChanged(); + if (sender == m_axisZ) + handleDataRowLabelsChanged(); } Abstract3DController::handleAxisRangeChangedBySender(sender); + + // Update selected bar - may be moved offscreen + setSelectedBar(m_selectedBar, m_selectedBarSeries); } void Bars3DController::setBarSpecs(GLfloat thicknessRatio, const QSizeF &spacing, bool relative) @@ -310,109 +317,182 @@ bool Bars3DController::isBarSpecRelative() return m_isBarSpecRelative; } -void Bars3DController::setBarType(QDataVis::MeshStyle style, bool smooth) -{ - QString objFile; - if (style == QDataVis::MeshStyleBars) - objFile = QStringLiteral(":/defaultMeshes/bar"); - else if (style == QDataVis::MeshStylePyramids) - objFile = QStringLiteral(":/defaultMeshes/pyramid"); - else if (style == QDataVis::MeshStyleCones) - objFile = QStringLiteral(":/defaultMeshes/cone"); - else if (style == QDataVis::MeshStyleCylinders) - objFile = QStringLiteral(":/defaultMeshes/cylinder"); - else if (style == QDataVis::MeshStyleBevelBars) - objFile = QStringLiteral(":/defaultMeshes/bevelbar"); - - if (smooth) - objFile += QStringLiteral("Smooth"); - - Abstract3DController::setMeshFileName(objFile); -} - -void Bars3DController::setSelectionMode(QDataVis::SelectionMode mode) +void Bars3DController::setSelectionMode(QDataVis::SelectionFlags mode) { - // Disable zoom if selection mode changes - scene()->setSlicingActive(false); - Abstract3DController::setSelectionMode(mode); + if (mode.testFlag(QDataVis::SelectionSlice) + && (mode.testFlag(QDataVis::SelectionRow) == mode.testFlag(QDataVis::SelectionColumn))) { + qWarning("Must specify one of either row or column selection mode in conjunction with slicing mode."); + } else { + QDataVis::SelectionFlags oldMode = selectionMode(); + + Abstract3DController::setSelectionMode(mode); + + if (mode != oldMode) { + // Refresh selection upon mode change to ensure slicing is correctly updated + // according to series the visibility. + setSelectedBar(m_selectedBar, m_selectedBarSeries); + + // Special case: Always deactivate slicing when changing away from slice + // automanagement, as this can't be handled in setSelectedBar. + if (!mode.testFlag(QDataVis::SelectionSlice) + && oldMode.testFlag(QDataVis::SelectionSlice)) { + scene()->setSlicingActive(false); + } + } + } } -void Bars3DController::setSelectedBarPos(const QPoint &position) +void Bars3DController::setSelectedBar(const QPoint &position, QBar3DSeries *series) { - // If the selection is outside data window or targets non-existent - // bar, clear selection instead. + // If the selection targets non-existent bar, clear selection instead. QPoint pos = position; - if (pos != noSelectionPoint()) { - int minRow = int(m_axisX->min()); - int maxRow = int(m_axisX->max()); - int minCol = int(m_axisZ->min()); - int maxCol = int(m_axisZ->max()); + // Series may already have been removed, so check it before setting the selection. + if (!m_seriesList.contains(series)) + series = 0; + + adjustSelectionPosition(pos, series); - if (pos.x() < minRow || pos.x() > maxRow || pos.y() < minCol || pos.y() > maxCol - || pos.x() + minRow >= static_cast<QBarDataProxy *>(m_data)->rowCount() - || pos.y() + minCol >= static_cast<QBarDataProxy *>(m_data)->rowAt(pos.x())->size()) { - pos = noSelectionPoint(); + if (selectionMode().testFlag(QDataVis::SelectionSlice)) { + // If the selected bar is outside data window, or there is no visible selected bar, disable slicing + if (pos.x() < m_axisZ->min() || pos.x() > m_axisZ->max() + || pos.y() < m_axisX->min() || pos.y() > m_axisX->max() + || !series->isVisible()) { + scene()->setSlicingActive(false); + } else { + scene()->setSlicingActive(true); } + emitNeedRender(); } - if (pos != m_selectedBarPos) { - m_selectedBarPos = pos; - m_changeTracker.selectedBarPosChanged = true; - emit selectedBarPosChanged(pos); + if (pos != m_selectedBar || series != m_selectedBarSeries) { + m_selectedBar = pos; + m_selectedBarSeries = series; + m_changeTracker.selectedBarChanged = true; + + // Clear selection from other series and finally set new selection to the specified series + foreach (QAbstract3DSeries *otherSeries, m_seriesList) { + QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(otherSeries); + if (barSeries != m_selectedBarSeries) + barSeries->dptr()->setSelectedBar(invalidSelectionPosition()); + } + if (m_selectedBarSeries) + m_selectedBarSeries->dptr()->setSelectedBar(m_selectedBar); + emitNeedRender(); } } -QPoint Bars3DController::selectedBarPos() const -{ - return m_selectedBarPos; -} - void Bars3DController::adjustAxisRanges() { - const QBarDataProxy *proxy = static_cast<QBarDataProxy *>(m_data); - const QBarDataArray *array = proxy->array(); - + Q3DCategoryAxis *categoryAxisZ = static_cast<Q3DCategoryAxis *>(m_axisZ); Q3DCategoryAxis *categoryAxisX = static_cast<Q3DCategoryAxis *>(m_axisX); - if (categoryAxisX && categoryAxisX->isAutoAdjustRange() && proxy) { - int rowCount = proxy->rowCount(); - if (rowCount) - rowCount--; - categoryAxisX->dptr()->setRange(0.0, qreal(rowCount)); - } + Q3DValueAxis *valueAxis = static_cast<Q3DValueAxis *>(m_axisY); - Q3DCategoryAxis *categoryAxisZ = static_cast<Q3DCategoryAxis *>(m_axisZ); - if (categoryAxisZ && categoryAxisZ->isAutoAdjustRange() && proxy) { - int columnCount = 0; - for (int i = 0; i < array->size(); i++) { - if (columnCount < array->at(i)->size()) - columnCount = array->at(i)->size(); + bool adjustZ = (categoryAxisZ && categoryAxisZ->isAutoAdjustRange()); + bool adjustX = (categoryAxisX && categoryAxisX->isAutoAdjustRange()); + bool adjustY = (valueAxis && categoryAxisX && categoryAxisZ && valueAxis->isAutoAdjustRange()); + + if (adjustZ || adjustX || adjustY) { + int maxRowCount = 1; + int maxColumnCount = 1; + float minValue = 0.0f; + float maxValue = 0.0f; + + // First figure out row and column counts + int seriesCount = m_seriesList.size(); + if (adjustZ || adjustX) { + for (int series = 0; series < seriesCount; series++) { + const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(series)); + if (barSeries->isVisible()) { + const QBarDataProxy *proxy = barSeries->dataProxy(); + + if (adjustZ && proxy) { + int rowCount = proxy->rowCount(); + if (rowCount) + rowCount--; + + maxRowCount = qMax(maxRowCount, rowCount); + } + + if (adjustX && proxy) { + const QBarDataArray *array = proxy->array(); + int columnCount = 0; + for (int i = 0; i < array->size(); i++) { + if (columnCount < array->at(i)->size()) + columnCount = array->at(i)->size(); + } + if (columnCount) + columnCount--; + + maxColumnCount = qMax(maxColumnCount, columnCount); + } + } + } + // Call private implementations of setRange to avoid unsetting auto adjust flag + if (adjustZ) + categoryAxisZ->dptr()->setRange(0.0f, float(maxRowCount)); + if (adjustX) + categoryAxisX->dptr()->setRange(0.0f, float(maxColumnCount)); } - if (columnCount) - columnCount--; - categoryAxisZ->dptr()->setRange(0.0, qreal(columnCount)); - } - Q3DValueAxis *valueAxis = static_cast<Q3DValueAxis *>(m_axisY); - if (valueAxis && categoryAxisX && categoryAxisZ && valueAxis->isAutoAdjustRange() && proxy) { - QPair<GLfloat, GLfloat> limits = proxy->dptrc()->limitValues(categoryAxisX->min(), - categoryAxisX->max(), - categoryAxisZ->min(), - categoryAxisZ->max()); - if (limits.first < 0) { - // TODO: Currently we only support symmetric y-axis for bar graph if there are negative values - qreal maxAbs = qMax(qFabs(limits.first), qFabs(limits.second)); - // Call private implementation to avoid unsetting auto adjust flag - valueAxis->dptr()->setRange(-maxAbs, maxAbs); - } else if (limits.second == 0.0) { - valueAxis->dptr()->setRange(0.0, 1.0); // Only zero value values in data set, set range to something. - } else { - valueAxis->dptr()->setRange(0.0, limits.second); + // Now that we know the row and column ranges, figure out the value axis range + if (adjustY) { + for (int series = 0; series < seriesCount; series++) { + const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(series)); + if (barSeries->isVisible()) { + const QBarDataProxy *proxy = barSeries->dataProxy(); + if (adjustY && proxy) { + QPair<GLfloat, GLfloat> limits = proxy->dptrc()->limitValues(categoryAxisZ->min(), + categoryAxisZ->max(), + categoryAxisX->min(), + categoryAxisX->max()); + if (!series) { + // First series initializes the values + minValue = limits.first; + maxValue = limits.second; + } else { + minValue = qMin(minValue, limits.first); + maxValue = qMax(maxValue, limits.second); + } + } + } + } + + if (maxValue < 0.0f) + maxValue = 0.0f; + if (minValue > 0.0f) + minValue = 0.0f; + if (minValue == 0.0f && maxValue == 0.0f) { + // Only zero value values in data set, set range to something. + minValue = 0.0f; + maxValue = 1.0f; + } + valueAxis->dptr()->setRange(minValue, maxValue); } } } +// Invalidate selection position if outside data for the series +void Bars3DController::adjustSelectionPosition(QPoint &pos, const QBar3DSeries *series) +{ + const QBarDataProxy *proxy = 0; + if (series) + proxy = series->dataProxy(); + + if (!proxy) + pos = invalidSelectionPosition(); + + if (pos != invalidSelectionPosition()) { + int maxRow = proxy->rowCount() - 1; + int maxCol = (pos.x() <= maxRow && pos.x() >= 0 && proxy->rowAt(pos.x())) + ? proxy->rowAt(pos.x())->size() - 1 : -1; + + if (pos.x() < 0 || pos.x() > maxRow || pos.y() < 0 || pos.y() > maxCol) + pos = invalidSelectionPosition(); + } +} + Q3DAbstractAxis *Bars3DController::createDefaultAxis(Q3DAbstractAxis::AxisOrientation orientation) { Q3DAbstractAxis *defaultAxis = 0; diff --git a/src/datavisualization/engine/bars3dcontroller_p.h b/src/datavisualization/engine/bars3dcontroller_p.h index 8398dd81..54385571 100644 --- a/src/datavisualization/engine/bars3dcontroller_p.h +++ b/src/datavisualization/engine/bars3dcontroller_p.h @@ -37,17 +37,17 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE class Bars3DRenderer; -class QBarDataProxy; +class QBar3DSeries; struct Bars3DChangeBitField { bool slicingActiveChanged : 1; bool barSpecsChanged : 1; - bool selectedBarPosChanged : 1; + bool selectedBarChanged : 1; Bars3DChangeBitField() : slicingActiveChanged(true), barSpecsChanged(true), - selectedBarPosChanged(true) + selectedBarChanged(true) { } }; @@ -60,7 +60,9 @@ private: Bars3DChangeBitField m_changeTracker; // Interaction - QPoint m_selectedBarPos; // Points to row & column in data window. + QPoint m_selectedBar; // Points to row & column in data window. + QBar3DSeries *m_selectedBarSeries; // Points to the series for which the bar is selected in + // single series selection cases. // Look'n'feel bool m_isBarSpecRelative; @@ -74,7 +76,7 @@ public: explicit Bars3DController(QRect rect); ~Bars3DController(); - void initializeOpenGL(); + virtual void initializeOpenGL(); virtual void synchDataToRenderer(); // bar thickness, spacing between bars, and is spacing relative to thickness or absolute @@ -87,24 +89,21 @@ public: QSizeF barSpacing(); bool isBarSpecRelative(); - // bar type; bars (=cubes), pyramids, cones, cylinders, etc. - void setBarType(QDataVis::MeshStyle style, bool smooth = false); - - // Change selection mode; single bar, bar and row, bar and column, or all - void setSelectionMode(QDataVis::SelectionMode mode); - - void setSelectedBarPos(const QPoint &position); - QPoint selectedBarPos() const; - - virtual void setActiveDataProxy(QAbstractDataProxy *proxy); + void setSelectionMode(QDataVis::SelectionFlags mode); + void setSelectedBar(const QPoint &position, QBar3DSeries *series); virtual void handleAxisAutoAdjustRangeChangedInOrientation(Q3DAbstractAxis::AxisOrientation orientation, bool autoAdjust); + virtual void handleSeriesVisibilityChangedBySender(QObject *sender); - static QPoint noSelectionPoint(); + static QPoint invalidSelectionPosition(); virtual void setAxisX(Q3DAbstractAxis *axis); virtual void setAxisZ(Q3DAbstractAxis *axis); + virtual void addSeries(QAbstract3DSeries *series); + virtual void removeSeries(QAbstract3DSeries *series); + virtual QList<QBar3DSeries *> barSeriesList(); + virtual void handleAxisRangeChangedBySender(QObject *sender); public slots: @@ -117,16 +116,15 @@ public slots: void handleDataRowLabelsChanged(); void handleDataColumnLabelsChanged(); - void handleSelectedBarPosChanged(const QPoint &position); - -signals: - void selectedBarPosChanged(QPoint position); + // Renderer callback handlers + void handleBarClicked(const QPoint &position, QBar3DSeries *series); protected: virtual Q3DAbstractAxis *createDefaultAxis(Q3DAbstractAxis::AxisOrientation orientation); private: void adjustAxisRanges(); + void adjustSelectionPosition(QPoint &pos, const QBar3DSeries *series); Q_DISABLE_COPY(Bars3DController) diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index c7c2f64c..ecdac21f 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -22,11 +22,11 @@ #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 "qbardataitem.h" #include "q3dlight.h" +#include "qbar3dseries_p.h" #include <QMatrix4x4> #include <QMouseEvent> @@ -42,17 +42,15 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE const GLfloat labelMargin = 0.05f; const GLfloat gridLineWidth = 0.005f; -static QVector3D selectionSkipColor = QVector3D(255, 255, 255); // Selection texture's background color -const int smallerVPSize = 5; + +const bool sliceGridLabels = true; // TODO: Make this user controllable (QTRD-2546) Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) : Abstract3DRenderer(controller), - m_controller(controller), m_cachedIsSlicingActivated(false), m_cachedRowCount(0), m_cachedColumnCount(0), m_selectedBar(0), - m_sliceSelection(0), m_sliceCache(0), m_sliceTitleItem(0), m_xFlipped(false), @@ -60,11 +58,11 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_yFlipped(false), m_updateLabels(false), m_barShader(0), + m_barGradientShader(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), @@ -77,7 +75,7 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_shadowQualityToShader(100.0f), m_shadowQualityMultiplier(3), m_heightNormalizer(1.0f), - m_yAdjustment(0.0f), + m_negativeBackgroundAdjustment(0.0f), m_rowWidth(0), m_columnDepth(0), m_maxDimension(0), @@ -85,9 +83,15 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_scaleZ(0), m_scaleFactor(0), m_maxSceneSize(40.0f), - m_selection(selectionSkipColor), - m_previousSelection(selectionSkipColor), - m_hasHeightAdjustmentChanged(true) + m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()), + m_visualSelectedBarSeriesIndex(-1), + m_hasHeightAdjustmentChanged(true), + m_selectedBarPos(Bars3DController::invalidSelectionPosition()), + m_selectedBarSeries(0), + m_noZeroInRange(false), + m_seriesScale(0.0f), + m_seriesStep(0.0f), + m_seriesStart(0.0f) { initializeOpenGLFunctions(); initializeOpenGL(); @@ -100,15 +104,12 @@ Bars3DRenderer::~Bars3DRenderer() m_textureHelper->deleteTexture(&m_selectionTexture); m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer); m_textureHelper->deleteTexture(&m_bgrTexture); - if (m_sliceSelection) { - m_sliceSelection->clear(); // Slice doesn't own its items - delete m_sliceSelection; - } + delete m_barShader; + delete m_barGradientShader; delete m_depthShader; delete m_selectionShader; delete m_backgroundShader; - delete m_barObj; delete m_backgroundObj; delete m_gridLineObj; delete m_labelObj; @@ -120,8 +121,6 @@ void Bars3DRenderer::initializeOpenGL() Abstract3DRenderer::initializeOpenGL(); // Initialize shaders - handleShadowQualityChange(); - initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), QStringLiteral(":/shaders/fragmentLabel")); @@ -139,34 +138,35 @@ void Bars3DRenderer::initializeOpenGL() // Load label mesh loadLabelMesh(); - // Set view port - glViewport(m_sliceViewPort.x(), m_sliceViewPort.y(), - m_sliceViewPort.width(), m_sliceViewPort.height()); - // Load background mesh (we need to be initialized first) loadBackgroundMesh(); } -void Bars3DRenderer::updateDataModel(QBarDataProxy *dataProxy) +void Bars3DRenderer::updateData() { - int minRow = m_axisCacheX.min(); - int maxRow = m_axisCacheX.max(); - int minCol = m_axisCacheZ.min(); - int maxCol = m_axisCacheZ.max(); + int seriesCount = m_visibleSeriesList.size(); + int minRow = m_axisCacheZ.min(); + int maxRow = m_axisCacheZ.max(); + int minCol = m_axisCacheX.min(); + int maxCol = m_axisCacheX.max(); int newRows = maxRow - minRow + 1; int newColumns = maxCol - minCol + 1; - if (newRows != m_renderItemArray.size() || newColumns != m_renderItemArray.at(0).size()) { - // Destroy old render items and reallocate new array - m_renderItemArray.clear(); - m_renderItemArray.resize(newRows); - for (int i = 0; i < newRows; i++) - m_renderItemArray[i].resize(newColumns); + int updateSize = 0; + int dataRowCount = 0; + int maxDataRowCount = 0; + + if (m_renderingArrays.size() != seriesCount) { + m_renderingArrays.resize(seriesCount); + m_seriesScale = 1.0f / float(seriesCount); + m_seriesStep = 1.0f / float(seriesCount); + m_seriesStart = -((float(seriesCount) - 1.0f) / 2.0f) * m_seriesStep; + } + if (m_cachedRowCount != newRows || m_cachedColumnCount != newColumns) { // Force update for selection related items m_sliceCache = 0; m_sliceTitleItem = 0; - if (m_sliceSelection) - m_sliceSelection->clear(); + m_sliceSelection.clear(); m_cachedColumnCount = newColumns; m_cachedRowCount = newRows; @@ -178,62 +178,81 @@ void Bars3DRenderer::updateDataModel(QBarDataProxy *dataProxy) calculateSceneScalingFactors(); } - // Update cached data window - int dataRowCount = dataProxy->rowCount(); - int dataRowIndex = minRow; - int updateSize = 0; - for (int i = 0; i < newRows; i++) { - int j = 0; - if (dataRowIndex < dataRowCount) { - const QBarDataRow *dataRow = dataProxy->rowAt(dataRowIndex); - updateSize = qMin((dataRow->size() - minCol), m_renderItemArray[i].size()); - if (dataRow) { - int dataColIndex = minCol; - for (; j < updateSize ; j++) { - qreal value = dataRow->at(dataColIndex).value(); - m_renderItemArray[i][j].setValue(value); - m_renderItemArray[i][j].setHeight(GLfloat(value) / m_heightNormalizer); - dataColIndex++; + for (int series = 0; series < seriesCount; series++) { + if (newRows != m_renderingArrays.at(series).size() + || newColumns != m_renderingArrays.at(series).at(0).size()) { + // Destroy old render items and reallocate new array + m_renderingArrays[series].resize(newRows); + for (int i = 0; i < newRows; i++) + m_renderingArrays[series][i].resize(newColumns); + } + + // Update cached data window + QBarDataProxy *dataProxy = + static_cast<QBar3DSeries *>(m_visibleSeriesList.at(series).series())->dataProxy(); + dataRowCount = dataProxy->rowCount(); + if (maxDataRowCount < dataRowCount) + maxDataRowCount = qMin(dataRowCount, newRows); + int dataRowIndex = minRow; + GLfloat heightValue = 0.0f; + for (int i = 0; i < newRows; i++) { + int j = 0; + if (dataRowIndex < dataRowCount) { + const QBarDataRow *dataRow = dataProxy->rowAt(dataRowIndex); + updateSize = qMin((dataRow->size() - minCol), + m_renderingArrays.at(series).at(i).size()); + if (dataRow) { + int dataColIndex = minCol; + for (; j < updateSize ; j++) { + float value = dataRow->at(dataColIndex).value(); + if (!m_noZeroInRange) { + heightValue = GLfloat(value); + } else { + // Adjust height to range + if (!m_hasNegativeValues) { + heightValue = value - m_axisCacheY.min(); + if (heightValue < 0.0f) + heightValue = 0.0f; + } else if (m_axisCacheY.max() < 0.0f) { + heightValue = value - m_axisCacheY.max(); + if (heightValue > 0.0f) + heightValue = 0.0f; + } + } + m_renderingArrays[series][i][j].setValue(value); + m_renderingArrays[series][i][j].setHeight(heightValue / m_heightNormalizer); + dataColIndex++; + } } } + for (; j < m_renderingArrays.at(series).at(i).size(); j++) { + m_renderingArrays[series][i][j].setValue(0.0f); + m_renderingArrays[series][i][j].setHeight(0.0f); + } + dataRowIndex++; } - for (; j < m_renderItemArray[i].size(); j++) { - m_renderItemArray[i][j].setValue(0.0); - m_renderItemArray[i][j].setHeight(0.0f); - } - dataRowIndex++; } - m_renderColumns = updateSize; - m_renderRows = qMin((dataRowCount - minRow), m_renderItemArray.size()); - - Abstract3DRenderer::updateDataModel(dataProxy); + // Reset selected bar to update selection + updateSelectedBar(m_selectedBarPos, m_selectedBarSeries); } void Bars3DRenderer::updateScene(Q3DScene *scene) { - // TODO: Move these to more suitable place e.g. controller should be controlling the viewports. - scene->setSecondarySubViewport(m_sliceViewPort); - scene->setPrimarySubViewport(m_mainViewPort); - // TODO: See QTRD-2374 if (m_hasNegativeValues) scene->activeCamera()->setMinYRotation(-90.0); else - scene->activeCamera()->setMinYRotation(0.0); + scene->activeCamera()->setMinYRotation(0.0f); if (m_hasHeightAdjustmentChanged) { // Set initial camera position. Also update if height adjustment has changed. scene->activeCamera()->setBaseOrientation(cameraDistanceVector, - QVector3D(0.0f, -m_yAdjustment, 0.0f), + zeroVector, upVector); m_hasHeightAdjustmentChanged = false; } - scene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); - // Set light position (rotate light with camera, a bit above it (as set in defaultLightPos)) - scene->setLightPositionRelativeToCamera(defaultLightPos); - Abstract3DRenderer::updateScene(scene); updateSlicingActive(scene->isSlicingActive()); @@ -241,48 +260,29 @@ void Bars3DRenderer::updateScene(Q3DScene *scene) void Bars3DRenderer::render(GLuint defaultFboHandle) { - bool slicingChanged = m_cachedIsSlicingActivated != m_cachedScene->isSlicingActive(); - // Handle GL state setup for FBO buffers and clearing of the render surface Abstract3DRenderer::render(defaultFboHandle); - // Draw bars scene drawScene(defaultFboHandle); - - // If slice selection is on, draw the sliced scene if (m_cachedIsSlicingActivated) - drawSlicedScene(m_axisCacheX.titleItem(), m_axisCacheY.titleItem(), m_axisCacheZ.titleItem()); - - // If slicing has been activated by this render pass, we need another render - // Also trigger another render always when slicing changes in general to ensure - // final draw is correct. - if (m_cachedIsSlicingActivated != m_cachedScene->isSlicingActive() || slicingChanged) - emit needRender(); + drawSlicedScene(); } -void Bars3DRenderer::drawSlicedScene(const LabelItem &xLabel, - const LabelItem &yLabel, - const LabelItem &zLabel) +void Bars3DRenderer::drawSlicedScene() { GLfloat barPosX = 0; - GLint startBar = 0; - GLint stopBar = m_sliceSelection->size(); - GLint stepBar = 1; QVector3D lightPos; - GLfloat negativesComp = 1.0f; - - // Compensate bar scaling a bit to avoid drawing on axis titles when we have negative values - if (m_hasNegativeValues) - negativesComp = 0.67f; // Specify viewport - glViewport(m_sliceViewPort.x(), m_sliceViewPort.y(), - m_sliceViewPort.width(), m_sliceViewPort.height()); + glViewport(m_secondarySubViewport.x(), + m_secondarySubViewport.y(), + m_secondarySubViewport.width(), + m_secondarySubViewport.height()); // Set up projection matrix QMatrix4x4 projectionMatrix; - projectionMatrix.perspective(40.0f, (GLfloat)m_sliceViewPort.width() - / (GLfloat)m_sliceViewPort.height(), 0.1f, 10.0f); + projectionMatrix.perspective(40.0f, (GLfloat)m_secondarySubViewport.width() + / (GLfloat)m_secondarySubViewport.height(), 0.1f, 100.0f); // Set view matrix QMatrix4x4 viewMatrix; @@ -293,89 +293,276 @@ void Bars3DRenderer::drawSlicedScene(const LabelItem &xLabel, viewMatrix.lookAt(QVector3D(0.0f, 0.0f, camZPosSliced), zeroVector, upVector); // Set light position - lightPos = QVector3D(0.0f, -m_yAdjustment, camZPosSliced * 2.0f); + lightPos = QVector3D(0.0f, 0.0f, camZPosSliced * 2.0f); - // Bind bar shader - m_barShader->bind(); + const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); + + // Draw the selected row / column + GLfloat barPosYAdjustment = -0.8f; // Positives only -> translate to -1.0 + 0.2 for row/column labels + if (m_hasNegativeValues) { + if (m_noZeroInRange) + barPosYAdjustment = 1.2f; // Negatives only -> translate to 1.0 + 0.2 for row/column labels + else + barPosYAdjustment = 0.2f; // Both -> translate to 0.0 + 0.2 for row/column labels + } + QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; + bool rowMode = m_cachedSelectionMode.testFlag(QDataVis::SelectionRow); + bool itemMode = m_cachedSelectionMode.testFlag(QDataVis::SelectionItem); + + // Draw grid lines + if (m_cachedTheme->isGridEnabled()) { + glDisable(GL_DEPTH_TEST); + ShaderHelper *lineShader = m_backgroundShader; + // Bind line shader + lineShader->bind(); + + // Set unchanging shader bindings + QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); + lineShader->setUniformValue(lineShader->lightP(), lightPos); + lineShader->setUniformValue(lineShader->view(), viewMatrix); + lineShader->setUniformValue(lineShader->color(), lineColor); + lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength() * 2.0f); + lineShader->setUniformValue(lineShader->lightS(), 0.25f); + + GLfloat scaleFactor = 0.0f; + if (rowMode) + scaleFactor = (1.1f * m_rowWidth) / m_scaleFactor; + else + scaleFactor = (1.1f * m_columnDepth) / m_scaleFactor; + + GLfloat startLine = 0.0f; + if (m_noZeroInRange) + startLine = 2.0f * (m_axisCacheY.min() - m_axisCacheY.max()) / m_heightNormalizer; + else + startLine = 2.0f * m_axisCacheY.min() / m_heightNormalizer; + + GLfloat gridStep = (2.0f * m_axisCacheY.subSegmentStep()) / m_heightNormalizer; + GLfloat gridPos = startLine + barPosYAdjustment; + int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); + + // Horizontal lines + if (m_axisCacheY.segmentCount() > 0) { + QVector3D gridLineScale(scaleFactor, gridLineWidth, gridLineWidth); + bool noZero = true; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + modelMatrix.translate(0.0f, gridPos, 0.0f); + modelMatrix.scale(gridLineScale); + itModelMatrix = modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + + if (gridPos == barPosYAdjustment) + noZero = false; + + gridPos += gridStep; + } + // Draw a line at zero, if none exists + if (!m_noZeroInRange && noZero) { + QMatrix4x4 modelMatrix; + modelMatrix.translate(0.0f, barPosYAdjustment, 0.0f); + modelMatrix.scale(gridLineScale); + itModelMatrix = modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + lineShader->setUniformValue(lineShader->color(), + Utils::vectorFromColor(m_cachedTheme->backgroundColor())); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } + } + + // TODO: Make user controllable (QTRD-2546) + if (sliceGridLabels) { + // Bind label shader + m_labelShader->bind(); + glEnable(GL_TEXTURE_2D); + glCullFace(GL_BACK); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Draw grid labels + int labelNbr = 0; + int labelCount = m_axisCacheY.labels().size(); + gridStep = (2.0f * m_axisCacheY.segmentStep()) / m_heightNormalizer; + gridPos = startLine + barPosYAdjustment; + QVector3D backLabelRotation(0.0f, 0.0f, 0.0f); + QVector3D labelTrans = QVector3D(scaleFactor + labelMargin, 0.0f, 0.0f); + + for (int i = 0; i < labelCount; i++) { + if (m_axisCacheY.labelItems().size() > labelNbr) { + const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); + labelTrans.setY(gridPos); + m_dummyBarRenderItem.setTranslation(labelTrans); + m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, + projectionMatrix, zeroVector, backLabelRotation, 0, + m_cachedSelectionMode, m_labelShader, m_labelObj, + activeCamera, true, true, Drawer::LabelMid, Qt::AlignRight); + } + labelNbr++; + gridPos += gridStep; + } + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + } + } + + // Draw bars + QVector3D modelMatrixScaler(m_scaleX, 0.0f, m_scaleZ); + if (rowMode) + modelMatrixScaler.setX(m_scaleX * m_seriesScale); + else + modelMatrixScaler.setZ(m_scaleZ * m_seriesScale); // Set common bar shader bindings + m_barShader->bind(); m_barShader->setUniformValue(m_barShader->lightP(), lightPos); m_barShader->setUniformValue(m_barShader->view(), viewMatrix); m_barShader->setUniformValue(m_barShader->lightS(), 0.5f); m_barShader->setUniformValue(m_barShader->ambientS(), - m_cachedTheme.m_ambientStrength * 2.0f); - - // Draw the selected row / column - // We need some room for labels underneath; add +0.2f - GLfloat barPosYAdjustment = m_yAdjustment / 2.0f - 0.2f; - QVector3D modelMatrixScaler(m_scaleX, 0.0f, m_scaleZ); - QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; - QVector3D barHighlightColor(Utils::vectorFromColor(m_cachedTheme.m_highlightBarColor)); - QVector3D rowHighlightColor(Utils::vectorFromColor(m_cachedTheme.m_highlightRowColor)); - QVector3D columnHighlightColor(Utils::vectorFromColor(m_cachedTheme.m_highlightColumnColor)); - for (int bar = startBar; bar != stopBar; bar += stepBar) { - BarRenderItem *item = m_sliceSelection->at(bar); + m_cachedTheme->ambientLightStrength() * 2.0f); + m_barGradientShader->bind(); + m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos); + m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix); + m_barGradientShader->setUniformValue(m_barGradientShader->lightS(), 0.5f); + m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(), + m_cachedTheme->ambientLightStrength() * 2.0f); + m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f); + + // Default to uniform shader + ShaderHelper *barShader = m_barShader; + barShader->bind(); + + int currentSeriesIndex = -1; + Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform; + Q3DTheme::ColorStyle colorStyle = Q3DTheme::ColorStyleUniform; + ObjectHelper *barObj = 0; + QVector3D barHighlightColor; + QVector3D rowHighlightColor; + GLuint barGradientTexture = 0; + GLuint rowGradientTexture = 0; + const SeriesRenderCache *currentSeries = 0; + bool colorStyleIsUniform = true; + + int sliceItemCount = m_sliceSelection.size(); + for (int bar = 0; bar < sliceItemCount; bar++) { + BarRenderItem *item = m_sliceSelection.at(bar); if (!item) continue; + if (item->seriesIndex() != currentSeriesIndex) { + currentSeriesIndex = item->seriesIndex(); + currentSeries = &(m_visibleSeriesList.at(currentSeriesIndex)); + barObj = currentSeries->object(); + colorStyle = currentSeries->colorStyle(); + colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform); + if (colorStyleIsUniform) { + barHighlightColor = currentSeries->singleHighlightColor(); + rowHighlightColor = currentSeries->multiHighlightColor(); + } else { + barGradientTexture = currentSeries->singleHighlightGradientTexture(); + rowGradientTexture = currentSeries->multiHighlightGradientTexture(); + } + + // Rebind shader if it has changed + if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) { + if (colorStyleIsUniform) + barShader = m_barShader; + else + barShader = m_barGradientShader; + barShader->bind(); + + } + + if (!colorStyleIsUniform && (previousColorStyle != colorStyle) + && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) { + m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f); + } + + previousColorStyle = colorStyle; + } + if (item->height() < 0) glCullFace(GL_FRONT); else glCullFace(GL_BACK); - QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; + QMatrix4x4 modelMatrix; QMatrix4x4 itModelMatrix; + GLfloat barRotation = 0.0f; + GLfloat barPosY = item->translation().y() + barPosYAdjustment; - GLfloat barPosY = negativesComp * item->translation().y() - barPosYAdjustment; - if (QDataVis::SelectionModeSliceRow == m_cachedSelectionMode) + if (rowMode) { barPosX = item->translation().x(); - else + } else { barPosX = -(item->translation().z()); // flip z; frontmost bar to the left + barRotation = 90.0f; + } + modelMatrix.translate(barPosX, barPosY, 0.0f); - modelMatrixScaler.setY(negativesComp * item->height()); + modelMatrixScaler.setY(item->height()); + modelMatrix.rotate(barRotation, 0.0f, 1.0f, 0.0f); + itModelMatrix.rotate(barRotation, 0.0f, 1.0f, 0.0f); modelMatrix.scale(modelMatrixScaler); itModelMatrix.scale(modelMatrixScaler); MVPMatrix = projectionViewMatrix * modelMatrix; -#if 0 - QVector3D baseColor; - if (m_selection.x() == item->position().x() && m_selection.y() == item->position().y()) - baseColor = barHighlightColor; - else if (QDataVis::SelectionModeSliceRow == m_cachedSelectionMode) - baseColor = rowHighlightColor; - else - baseColor = columnHighlightColor; - - QVector3D heightColor = Utils::vectorFromColor(m_cachedTheme.m_heightColor) * item->height(); - QVector3D barColor = baseColor + heightColor; -#else QVector3D barColor; - if (m_selection.x() == item->position().x() && m_selection.y() == item->position().y()) - barColor = barHighlightColor; - else if (QDataVis::SelectionModeSliceRow == m_cachedSelectionMode) - barColor = rowHighlightColor; - else - barColor = columnHighlightColor; -#endif + GLuint gradientTexture = 0; + + if (itemMode && m_visualSelectedBarPos.x() == item->position().x() + && m_visualSelectedBarPos.y() == item->position().y()) { + if (colorStyleIsUniform) + barColor = barHighlightColor; + else + gradientTexture = barGradientTexture; + } else { + if (colorStyleIsUniform) + barColor = rowHighlightColor; + else + gradientTexture = rowGradientTexture; + } if (item->height() != 0) { // Set 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); - m_barShader->setUniformValue(m_barShader->color(), barColor); + barShader->setUniformValue(barShader->model(), modelMatrix); + barShader->setUniformValue(barShader->nModel(), + itModelMatrix.inverted().transposed()); + barShader->setUniformValue(barShader->MVP(), MVPMatrix); + if (colorStyleIsUniform) { + barShader->setUniformValue(barShader->color(), barColor); + } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { + barShader->setUniformValue(barShader->gradientHeight(), + (qAbs(item->height()) / m_gradientFraction)); + } // Draw the object - m_drawer->drawObject(m_barShader, m_barObj); + m_drawer->drawObject(barShader, + barObj, + gradientTexture); } } - // Release bar shader - m_barShader->release(); - // Draw labels m_labelShader->bind(); glDisable(GL_DEPTH_TEST); @@ -384,69 +571,106 @@ void Bars3DRenderer::drawSlicedScene(const LabelItem &xLabel, glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // Draw labels for axes BarRenderItem *dummyItem(0); const LabelItem &sliceSelectionLabel = *m_sliceTitleItem; QVector3D positionComp(0.0f, m_autoScaleAdjustment, 0.0f); - const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); - if (QDataVis::SelectionModeSliceRow == m_cachedSelectionMode) { + + // Draw labels for bars + QVector3D valuePositionComp = zeroVector; + if (!m_hasNegativeValues) + valuePositionComp.setY(2.0f); + else if (m_noZeroInRange) + valuePositionComp.setY(-2.0f); + QVector3D sliceValueRotation(0.0f, 0.0f, 90.0f); + QVector3D sliceLabelRotation(0.0f, 0.0f, -45.0f); + + for (int col = 0; col < sliceItemCount; col++) { + BarRenderItem *item = m_sliceSelection.at(col); + + // TODO: Make user controllable (QTRD-2546) + if (!sliceGridLabels) { + // Draw values + if (item->height() != 0.0f || (!m_noZeroInRange && item->value() == 0.0f)) { + // Create label texture if we need it + if (item->sliceLabel().isNull()) { + item->setSliceLabel(generateValueLabel(m_axisCacheY.labelFormat(), item->value())); + m_drawer->generateLabelItem(item->sliceLabelItem(), item->sliceLabel()); + } + m_drawer->drawLabel(*item, item->sliceLabelItem(), viewMatrix, projectionMatrix, + valuePositionComp, sliceValueRotation, item->height(), + m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, + false, false, Drawer::LabelOver, Qt::AlignTop, true); + } + } else { + // Only draw value for selected item when grid labels are on + // TODO: Maybe use selection label instead of value? Should it be user controllable + // as well? (QTRD-2546) + if (itemMode && m_visualSelectedBarPos.x() == item->position().x() + && m_visualSelectedBarPos.y() == item->position().y() + && item->seriesIndex() == m_visualSelectedBarSeriesIndex) { + // Create label texture if we need it + if (item->sliceLabel().isNull()) { + item->setSliceLabel(generateValueLabel(m_axisCacheY.labelFormat(), item->value())); + m_drawer->generateLabelItem(item->sliceLabelItem(), item->sliceLabel()); + } + m_drawer->drawLabel(*item, item->sliceLabelItem(), viewMatrix, projectionMatrix, + valuePositionComp, sliceValueRotation, item->height(), + m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, + false, false, Drawer::LabelOver, Qt::AlignTop, true); + } + } + } + + int lastLabel = m_sliceCache->labelItems().size() - 1; + + for (int labelNo = 0; labelNo <= lastLabel; labelNo++) { + // Get labels from first series only + BarRenderItem *item = m_sliceSelection.at(labelNo); + // TODO: Make user controllable (QTRD-2546) + // Draw labels + m_drawer->drawLabel(*item, *m_sliceCache->labelItems().at(labelNo), viewMatrix, + projectionMatrix, positionComp, sliceLabelRotation, + item->height(), m_cachedSelectionMode, m_labelShader, + m_labelObj, activeCamera, false, false, Drawer::LabelBelow, + Qt::AlignCenter, true); + } + + // TODO: Make user controllable (QTRD-2546) + // Draw labels for axes + if (rowMode) { if (m_sliceTitleItem) { m_drawer->drawLabel(*dummyItem, sliceSelectionLabel, viewMatrix, projectionMatrix, positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, Drawer::LabelTop); + m_labelObj, activeCamera, false, false, Drawer::LabelTop, + Qt::AlignCenter, true); } - m_drawer->drawLabel(*dummyItem, zLabel, viewMatrix, projectionMatrix, + m_drawer->drawLabel(*dummyItem, m_axisCacheX.titleItem(), viewMatrix, projectionMatrix, positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, Drawer::LabelBottom); + m_labelObj, activeCamera, false, false, Drawer::LabelBottom, + Qt::AlignCenter, true); } else { - m_drawer->drawLabel(*dummyItem, xLabel, viewMatrix, projectionMatrix, + m_drawer->drawLabel(*dummyItem, m_axisCacheZ.titleItem(), viewMatrix, projectionMatrix, positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, Drawer::LabelBottom); + m_labelObj, activeCamera, false, false, Drawer::LabelBottom, + Qt::AlignCenter, true); if (m_sliceTitleItem) { m_drawer->drawLabel(*dummyItem, sliceSelectionLabel, viewMatrix, projectionMatrix, positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, Drawer::LabelTop); + m_labelObj, activeCamera, false, false, Drawer::LabelTop, + Qt::AlignCenter, true); } } - m_drawer->drawLabel(*dummyItem, yLabel, viewMatrix, projectionMatrix, + m_drawer->drawLabel(*dummyItem, m_axisCacheY.titleItem(), viewMatrix, projectionMatrix, positionComp, QVector3D(0.0f, 0.0f, 90.0f), 0, m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, - false, false, Drawer::LabelLeft); - - // Draw labels for bars - QVector3D valuePositionComp(0.0f, m_yAdjustment, 0.0f); - QVector3D negativesRotation(0.0f, 0.0f, 90.0f); - QVector3D sliceLabelRotation(0.0f, 0.0f, -45.0f); - GLfloat negativesCompPow2 = negativesComp * negativesComp; - for (int col = 0; col < stopBar; col++) { - BarRenderItem *item = m_sliceSelection->at(col); - // Draw values - if (!m_hasNegativeValues) { - m_drawer->drawLabel(*item, item->sliceLabelItem(), viewMatrix, projectionMatrix, - valuePositionComp, negativesRotation, item->height(), - m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, - false, false, Drawer::LabelOver, Qt::AlignTop); - } else { - m_drawer->drawLabel(*item, item->sliceLabelItem(), viewMatrix, projectionMatrix, - valuePositionComp, zeroVector, negativesCompPow2 * item->height(), - m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera); - } - - // Draw labels - if (m_sliceCache->labelItems().size() > col) { - m_drawer->drawLabel(*item, *m_sliceCache->labelItems().at(col), viewMatrix, - projectionMatrix, positionComp, sliceLabelRotation, - item->height(), m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, Drawer::LabelBelow); - } - } + false, false, Drawer::LabelLeft, Qt::AlignCenter, true); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); - // Release label shader - m_labelShader->release(); + // Release shader + glUseProgram(0); } void Bars3DRenderer::drawScene(GLuint defaultFboHandle) @@ -464,15 +688,18 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) GLfloat colPos = 0; GLfloat rowPos = 0; + int seriesCount = m_visibleSeriesList.size(); + const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); - // Specify viewport - glViewport(m_mainViewPort.x(), m_mainViewPort.y(), - m_mainViewPort.width(), m_mainViewPort.height()); + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); // Set up projection matrix QMatrix4x4 projectionMatrix; - GLfloat viewPortRatio = (GLfloat)m_mainViewPort.width() / (GLfloat)m_mainViewPort.height(); + GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width() / (GLfloat)m_primarySubViewport.height(); projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f); // Get the view matrix @@ -531,6 +758,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; + bool rowMode = m_cachedSelectionMode.testFlag(QDataVis::SelectionRow); + #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { // Render scene into a depth texture for using with shadow mapping @@ -544,15 +773,14 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows. // Depth viewport must always start from 0, 0, as it is rendered into a texture, not screen glViewport(0, 0, - m_mainViewPort.width() * m_shadowQualityMultiplier, - m_mainViewPort.height() * m_shadowQualityMultiplier); + m_primarySubViewport.width() * m_shadowQualityMultiplier, + m_primarySubViewport.height() * m_shadowQualityMultiplier); // Get the depth view matrix // It may be possible to hack lightPos here if we want to make some tweaks to shadow QVector3D depthLightPos = activeCamera->calculatePositionRelativeToCamera( zeroVector, 0.0f, 3.5f / m_autoScaleAdjustment); - depthViewMatrix.lookAt(depthLightPos, QVector3D(0.0f, -m_yAdjustment, 0.0f), - upVector); + depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector); // 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 @@ -563,73 +791,73 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; // Draw bars to depth buffer - QVector3D shadowScaler(m_scaleX * 0.9f, 0.0f, m_scaleZ * 0.9f); - for (int row = startRow; row != stopRow; row += stepRow) { - for (int bar = startBar; bar != stopBar; bar += stepBar) { - const BarRenderItem &item = m_renderItemArray.at(row).at(bar); - if (!item.value()) - continue; - - GLfloat shadowOffset = 0.0f; - - // Set front face culling for negative valued bars and back face culling for - // positive valued bars to remove peter-panning issues - if (item.height() > 0) { - glCullFace(GL_BACK); - if (m_yFlipped) - shadowOffset = 0.015f; - } else { - glCullFace(GL_FRONT); - if (!m_yFlipped) - shadowOffset = -0.015f; - } + QVector3D shadowScaler(m_scaleX * m_seriesScale * 0.9f, 0.0f, m_scaleZ * 0.9f); + float seriesPos = m_seriesStart; + for (int series = 0; series < seriesCount; series++) { + ObjectHelper *barObj = m_visibleSeriesList.at(series).object(); + for (int row = startRow; row != stopRow; row += stepRow) { + for (int bar = startBar; bar != stopBar; bar += stepBar) { + GLfloat shadowOffset = 0.0f; + const BarRenderItem &item = m_renderingArrays.at(series).at(row).at(bar); + if (!item.value()) + continue; + // Set front face culling for negative valued bars and back face culling for + // positive valued bars to remove peter-panning issues + if (item.height() > 0) { + glCullFace(GL_BACK); + if (m_yFlipped) + shadowOffset = 0.015f; + } else { + glCullFace(GL_FRONT); + if (!m_yFlipped) + shadowOffset = -0.015f; + } - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; - colPos = (bar + 0.5f) * (m_cachedBarSpacing.width()); - rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); + colPos = (bar + 0.5f + seriesPos) * (m_cachedBarSpacing.width()); + rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); - // Draw shadows for bars "on the other side" a bit off ground to avoid seeing - // shadows through the ground - modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, - item.height() - m_yAdjustment + shadowOffset, - (m_columnDepth - rowPos) / m_scaleFactor); - // Scale the bars down in X and Z to reduce self-shadowing issues - shadowScaler.setY(item.height()); - modelMatrix.scale(shadowScaler); + // Draw shadows for bars "on the other side" a bit off ground to avoid seeing + // shadows through the ground + modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, + item.height() + shadowOffset, + (m_columnDepth - rowPos) / m_scaleFactor); + // Scale the bars down in X and Z to reduce self-shadowing issues + shadowScaler.setY(item.height()); + modelMatrix.scale(shadowScaler); - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); + 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); + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_depthShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, barObj->vertexBuf()); + glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, + (void *)0); - // Index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_barObj->elementBuf()); + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf()); - // Draw the triangles - glDrawElements(GL_TRIANGLES, m_barObj->indexCount(), GL_UNSIGNED_SHORT, - (void *)0); + // Draw the triangles + glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_SHORT, + (void *)0); - // Free buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + // Free buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); - glDisableVertexAttribArray(m_depthShader->posAtt()); + glDisableVertexAttribArray(m_depthShader->posAtt()); + } } + seriesPos += m_seriesStep; } // Disable drawing to depth framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); - // Release depth shader - m_depthShader->release(); - #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(); @@ -642,329 +870,419 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_labelShader->setUniformValue(m_labelShader->MVP(), MVPMatrix); m_drawer->drawObject(m_labelShader, m_labelObj, m_depthTexture); glDisable(GL_TEXTURE_2D); - m_labelShader->release(); #endif // Reset culling to normal glCullFace(GL_BACK); // Revert to original viewport - glViewport(m_mainViewPort.x(), m_mainViewPort.y(), - m_mainViewPort.width(), m_mainViewPort.height()); + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); } #endif + // TODO: Selection must be enabled currently to support clicked signal. (QTRD-2517) // Skip selection mode drawing if we're slicing or have no selection mode - if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QDataVis::SelectionModeNone) { + if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QDataVis::SelectionNone + && m_selectionState == SelectOnScene && seriesCount > 0) { // Bind selection shader m_selectionShader->bind(); // Draw bars to selection buffer glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); + glViewport(0, 0, + m_primarySubViewport.width(), + m_primarySubViewport.height()); + glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Set clear color to white (= selectionSkipColor) 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 - for (int row = startRow; row != stopRow; row += stepRow) { - for (int bar = startBar; bar != stopBar; bar += stepBar) { - const BarRenderItem &item = m_renderItemArray.at(row).at(bar); - if (!item.value()) - continue; - - if (item.height() < 0) - glCullFace(GL_FRONT); - else - glCullFace(GL_BACK); - - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - - colPos = (bar + 0.5f) * (m_cachedBarSpacing.width()); - rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); - - modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, - item.height() - m_yAdjustment, - (m_columnDepth - rowPos) / m_scaleFactor); - modelMatrix.scale(QVector3D(m_scaleX, item.height(), m_scaleZ)); - - MVPMatrix = projectionViewMatrix * modelMatrix; - - //#if !defined(QT_OPENGL_ES_2) - // QVector3D barColor = QVector3D((GLdouble)row / 32767.0, - // (GLdouble)bar / 32767.0, - // 0.0); - //#else - QVector3D barColor = QVector3D((GLdouble)row / 255.0, - (GLdouble)bar / 255.0, - 0.0); - //#endif - - 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()); + float seriesPos = m_seriesStart; + for (int series = 0; series < seriesCount; series++) { + ObjectHelper *barObj = m_visibleSeriesList.at(series).object(); + for (int row = startRow; row != stopRow; row += stepRow) { + for (int bar = startBar; bar != stopBar; bar += stepBar) { + const BarRenderItem &item = m_renderingArrays.at(series).at(row).at(bar); + if (!item.value()) + continue; + + if (item.height() < 0) + glCullFace(GL_FRONT); + else + glCullFace(GL_BACK); + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + colPos = (bar + 0.5f + seriesPos) * (m_cachedBarSpacing.width()); + rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); + + modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, + item.height(), + (m_columnDepth - rowPos) / m_scaleFactor); + modelMatrix.scale(QVector3D(m_scaleX * m_seriesScale, + item.height(), + m_scaleZ)); + + MVPMatrix = projectionViewMatrix * modelMatrix; + + //#if !defined(QT_OPENGL_ES_2) + // QVector3D barColor = QVector3D(GLfloat(row) / 32767.0f, + // GLfloat(bar) / 32767.0f, + // 0.0f); + //#else + QVector3D barColor = QVector3D(GLfloat(row) / 255.0f, + GLfloat(bar) / 255.0f, + GLfloat(series) / 255.0f); + //#endif + + 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, barObj->vertexBuf()); + glVertexAttribPointer(m_selectionShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, + (void *)0); + + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf()); + + // Draw the triangles + glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_SHORT, + (void *)0); + + // Free buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableVertexAttribArray(m_selectionShader->posAtt()); + } } + seriesPos += m_seriesStep; } glEnable(GL_DITHER); // Read color under cursor - if (QDataVis::InputStateOnScene == m_controller->inputState()) { - m_selection = Utils::getSelection(m_controller->inputPosition(), - m_cachedBoundingRect.height()); - } + QVector3D clickedColor = Utils::getSelection(m_inputPosition, + m_viewport.height()); + emit barClicked(selectionColorToArrayPosition(clickedColor), selectionColorToSeries(clickedColor)); + // Revert to original render target and viewport 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 - glCullFace(GL_BACK); - m_labelShader->bind(); - glDisable(GL_DEPTH_TEST); - glEnable(GL_TEXTURE_2D); - QMatrix4x4 modelMatrix; - QMatrix4x4 viewmatrix; - viewmatrix.lookAt(QVector3D(0.0f, 0.0f, 2.0f), zeroVector, upVector); - QMatrix4x4 MVPMatrix = projectionViewMatrix * 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 + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); } // Enable texturing glEnable(GL_TEXTURE_2D); - // Bind bar shader - m_barShader->bind(); + ShaderHelper *barShader = 0; + GLuint gradientTexture = 0; + Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform; + bool haveUniformColorSeries = false; + bool haveGradientSeries = false; - // Set common bar shader bindings - m_barShader->setUniformValue(m_barShader->lightP(), lightPos); - m_barShader->setUniformValue(m_barShader->view(), viewMatrix); - m_barShader->setUniformValue(m_barShader->ambientS(), - m_cachedTheme.m_ambientStrength); - - bool selectionDirty = (m_selection != m_previousSelection - || (m_selection != selectionSkipColor - && QDataVis::InputStateOnScene == m_controller->inputState() - && !m_cachedIsSlicingActivated)); - if (selectionDirty) { - m_previousSelection = m_selection; - if (m_sliceSelection) { - if (!m_cachedIsSlicingActivated) { - m_sliceCache = 0; - m_sliceTitleItem = 0; - } - if (m_sliceSelection->size()) { - // Slice doesn't own its items, no need to delete them - just clear - m_sliceSelection->clear(); - } - } + for (int i = 0; i < seriesCount; i++) { + if (m_visibleSeriesList.at(i).colorStyle() == Q3DTheme::ColorStyleUniform) + haveUniformColorSeries = true; + else + haveGradientSeries = true; + } + + // Set unchanging shader bindings + if (haveGradientSeries) { + m_barGradientShader->bind(); + m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos); + m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix); + m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(), + m_cachedTheme->ambientLightStrength()); + m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f); + } + + if (haveUniformColorSeries) { + m_barShader->bind(); + m_barShader->setUniformValue(m_barShader->lightP(), lightPos); + m_barShader->setUniformValue(m_barShader->view(), viewMatrix); + m_barShader->setUniformValue(m_barShader->ambientS(), + m_cachedTheme->ambientLightStrength()); + barShader = m_barShader; + } else { + barShader = m_barGradientShader; + previousColorStyle = Q3DTheme::ColorStyleRangeGradient; + } + + if (m_selectionDirty && m_cachedIsSlicingActivated) { + // Slice doesn't own its items, no need to delete them - just clear + m_sliceSelection.clear(); + int reserveAmount; + if (rowMode) + reserveAmount = m_cachedColumnCount; + else + reserveAmount = m_cachedRowCount; + if (m_cachedSelectionMode.testFlag(QDataVis::SelectionMultiSeries)) + reserveAmount *= m_visibleSeriesList.size(); + m_sliceSelection.resize(reserveAmount); + + // Set slice cache, i.e. axis cache from where slice labels are taken + if (rowMode) + m_sliceCache = &m_axisCacheX; + else + m_sliceCache = &m_axisCacheZ; + m_sliceTitleItem = 0; } // Draw bars - QVector3D barHighlightColor(Utils::vectorFromColor(m_cachedTheme.m_highlightBarColor)); - QVector3D rowHighlightColor(Utils::vectorFromColor(m_cachedTheme.m_highlightRowColor)); - QVector3D columnHighlightColor(Utils::vectorFromColor(m_cachedTheme.m_highlightColumnColor)); - QVector3D baseColor(Utils::vectorFromColor(m_cachedTheme.m_baseColor)); - GLfloat adjustedLightStrength = m_cachedTheme.m_lightStrength / 10.0f; - GLfloat adjustedHighlightStrength = m_cachedTheme.m_highlightLightStrength / 10.0f; + GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; + GLfloat adjustedHighlightStrength = m_cachedTheme->highlightLightStrength() / 10.0f; bool barSelectionFound = false; BarRenderItem *selectedBar(0); - QVector3D modelScaler(m_scaleX, 0.0f, m_scaleZ); - for (int row = startRow; row != stopRow; row += stepRow) { - for (int bar = startBar; bar != stopBar; bar += stepBar) { - BarRenderItem &item = m_renderItemArray[row][bar]; - if (item.height() < 0) - glCullFace(GL_FRONT); + QVector3D baseColor; + QVector3D barColor; + QVector3D modelScaler(m_scaleX * m_seriesScale, 0.0f, m_scaleZ); + bool somethingSelected = (m_visualSelectedBarPos != Bars3DController::invalidSelectionPosition()); + float seriesPos = m_seriesStart; + for (int series = 0; series < seriesCount; series++) { + const SeriesRenderCache ¤tSeries = m_visibleSeriesList.at(series); + ObjectHelper *barObj = currentSeries.object(); + Q3DTheme::ColorStyle colorStyle = currentSeries.colorStyle(); + bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform); + + // Rebind shader if it has changed + if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) { + if (colorStyleIsUniform) + barShader = m_barShader; else - glCullFace(GL_BACK); + barShader = m_barGradientShader; + barShader->bind(); + } - QMatrix4x4 modelMatrix; - QMatrix4x4 itModelMatrix; - QMatrix4x4 MVPMatrix; + if (colorStyleIsUniform) { + baseColor = currentSeries.baseColor(); + } else if ((previousColorStyle != colorStyle) + && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) { + m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f); + } - colPos = (bar + 0.5f) * (m_cachedBarSpacing.width()); - rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); + // Always use base color when no selection mode + if (m_cachedSelectionMode == QDataVis::SelectionNone) { + if (colorStyleIsUniform) + barColor = baseColor; + else + gradientTexture = currentSeries.baseGradientTexture(); + } - modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, - item.height() - m_yAdjustment, - (m_columnDepth - rowPos) / m_scaleFactor); - modelScaler.setY(item.height()); - modelMatrix.scale(modelScaler); - itModelMatrix.scale(modelScaler); -#ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; -#else - MVPMatrix = projectionViewMatrix * modelMatrix; -#endif + previousColorStyle = colorStyle; + int sliceSeriesAdjust = 0; + if (m_selectionDirty && m_cachedIsSlicingActivated) { + int seriesMultiplier = 0; + if (m_cachedSelectionMode.testFlag(QDataVis::SelectionMultiSeries)) + seriesMultiplier = series; + if (rowMode) + sliceSeriesAdjust = seriesMultiplier * m_cachedColumnCount; + else + sliceSeriesAdjust = seriesMultiplier * m_cachedRowCount; + } + + for (int row = startRow; row != stopRow; row += stepRow) { + for (int bar = startBar; bar != stopBar; bar += stepBar) { + BarRenderItem &item = m_renderingArrays[series][row][bar]; + + if (item.height() < 0) + glCullFace(GL_FRONT); + else + glCullFace(GL_BACK); -#if 0 - QVector3D heightColor = Utils::vectorFromColor(m_cachedTheme.m_heightColor) - * item.height(); - QVector3D depthColor = Utils::vectorFromColor(m_cachedTheme.m_depthColor) - * (float(row) / GLfloat(m_cachedRowCount)); + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + QMatrix4x4 MVPMatrix; + + colPos = (bar + 0.5f + seriesPos) * (m_cachedBarSpacing.width()); + rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); - QVector3D barColor = baseColor + heightColor + depthColor; + modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, + item.height(), + (m_columnDepth - rowPos) / m_scaleFactor); + modelScaler.setY(item.height()); + modelMatrix.scale(modelScaler); + itModelMatrix.scale(modelScaler); +#ifdef SHOW_DEPTH_TEXTURE_SCENE + MVPMatrix = depthProjectionViewMatrix * modelMatrix; #else - QVector3D barColor = baseColor; + MVPMatrix = projectionViewMatrix * modelMatrix; #endif - - GLfloat lightStrength = m_cachedTheme.m_lightStrength; - GLfloat shadowLightStrength = adjustedLightStrength; - - if (m_cachedSelectionMode > QDataVis::SelectionModeNone) { - Bars3DController::SelectionType selectionType = isSelected(row, bar); - - switch (selectionType) { - case Bars3DController::SelectionItem: { - barColor = barHighlightColor; - lightStrength = m_cachedTheme.m_highlightLightStrength; - shadowLightStrength = adjustedHighlightStrength; - // Insert position data into render item. We have no ownership, don't delete the previous one - if (!m_cachedIsSlicingActivated) { - selectedBar = &item; - selectedBar->setPosition(QPoint(row, bar)); - item.setTranslation(modelMatrix.column(3).toVector3D()); - barSelectionFound = true; + GLfloat lightStrength = m_cachedTheme->lightStrength(); + GLfloat shadowLightStrength = adjustedLightStrength; + + if (m_cachedSelectionMode > QDataVis::SelectionNone) { + Bars3DController::SelectionType selectionType = Bars3DController::SelectionNone; + if (somethingSelected) + selectionType = isSelected(row, bar, series); + + switch (selectionType) { + case Bars3DController::SelectionItem: { + if (colorStyleIsUniform) + barColor = currentSeries.singleHighlightColor(); + else + gradientTexture = currentSeries.singleHighlightGradientTexture(); + + lightStrength = m_cachedTheme->highlightLightStrength(); + shadowLightStrength = adjustedHighlightStrength; + // Insert position data into render item. We have no ownership, don't delete the previous one + if (!m_cachedIsSlicingActivated && m_visualSelectedBarSeriesIndex == series) { + selectedBar = &item; + selectedBar->setPosition(QPoint(row, bar)); + item.setTranslation(modelMatrix.column(3).toVector3D()); + barSelectionFound = true; + } + if (m_selectionDirty && m_cachedIsSlicingActivated) { + QVector3D translation = modelMatrix.column(3).toVector3D(); + if (m_cachedSelectionMode & QDataVis::SelectionColumn + && seriesCount > 1) { + translation.setZ((m_columnDepth - ((row + 0.5f + seriesPos) + * (m_cachedBarSpacing.height()))) + / m_scaleFactor); + } + item.setTranslation(translation); + item.setPosition(QPoint(row, bar)); + item.setSeriesIndex(series); + if (rowMode) + m_sliceSelection[sliceSeriesAdjust + bar] = &item; + else + m_sliceSelection[sliceSeriesAdjust + row] = &item; + } + break; } - if (selectionDirty && m_cachedSelectionMode >= QDataVis::SelectionModeSliceRow) { - item.setTranslation(modelMatrix.column(3).toVector3D()); - item.setPosition(QPoint(row, bar)); - m_sliceSelection->append(&item); - barSelectionFound = true; - if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { - if (m_axisCacheX.labelItems().size() > row) - m_sliceTitleItem = m_axisCacheX.labelItems().at(row); - if (!m_sliceCache) { - // m_sliceCache is the axis for labels, while title comes from different axis. - m_sliceCache = &m_axisCacheZ; + case Bars3DController::SelectionRow: { + // Current bar is on the same row as the selected bar + if (colorStyleIsUniform) + barColor = currentSeries.multiHighlightColor(); + else + gradientTexture = currentSeries.multiHighlightGradientTexture(); + + lightStrength = m_cachedTheme->highlightLightStrength(); + shadowLightStrength = adjustedHighlightStrength; + if (m_cachedIsSlicingActivated) { + item.setTranslation(modelMatrix.column(3).toVector3D()); + item.setPosition(QPoint(row, bar)); + if (m_selectionDirty) { + item.setSeriesIndex(series); + if (!m_sliceTitleItem && m_axisCacheZ.labelItems().size() > row) + m_sliceTitleItem = m_axisCacheZ.labelItems().at(row); + m_sliceSelection[sliceSeriesAdjust + bar] = &item; } - } else if (m_cachedSelectionMode == QDataVis::SelectionModeSliceColumn) { - if (m_axisCacheZ.labelItems().size() > bar) - m_sliceTitleItem = m_axisCacheZ.labelItems().at(bar); - if (!m_sliceCache) { - // m_sliceCache is the axis for labels, while title comes from different axis. - m_sliceCache = &m_axisCacheX; + } + break; + } + case Bars3DController::SelectionColumn: { + // Current bar is on the same column as the selected bar + if (colorStyleIsUniform) + barColor = currentSeries.multiHighlightColor(); + else + gradientTexture = currentSeries.multiHighlightGradientTexture(); + + lightStrength = m_cachedTheme->highlightLightStrength(); + shadowLightStrength = adjustedHighlightStrength; + if (m_cachedIsSlicingActivated) { + QVector3D translation = modelMatrix.column(3).toVector3D(); + if (seriesCount > 1) { + translation.setZ((m_columnDepth - ((row + 0.5f + seriesPos) + * (m_cachedBarSpacing.height()))) + / m_scaleFactor); + } + item.setTranslation(translation); + item.setPosition(QPoint(row, bar)); + if (m_selectionDirty) { + item.setSeriesIndex(series); + if (!m_sliceTitleItem && m_axisCacheX.labelItems().size() > bar) + m_sliceTitleItem = m_axisCacheX.labelItems().at(bar); + m_sliceSelection[sliceSeriesAdjust + row] = &item; } } + break; } - break; - } - case Bars3DController::SelectionRow: { - // Current bar is on the same row as the selected bar - barColor = rowHighlightColor; - lightStrength = m_cachedTheme.m_highlightLightStrength; - shadowLightStrength = adjustedHighlightStrength; - if (QDataVis::SelectionModeSliceRow == m_cachedSelectionMode) { - item.setTranslation(modelMatrix.column(3).toVector3D()); - item.setPosition(QPoint(row, bar)); - if (selectionDirty && bar < m_renderColumns) - m_sliceSelection->append(&item); + case Bars3DController::SelectionNone: { + // Current bar is not selected, nor on a row or column + if (colorStyleIsUniform) + barColor = baseColor; + else + gradientTexture = currentSeries.baseGradientTexture(); + break; } - break; - } - case Bars3DController::SelectionColumn: { - // Current bar is on the same column as the selected bar - barColor = columnHighlightColor; - lightStrength = m_cachedTheme.m_highlightLightStrength; - shadowLightStrength = adjustedHighlightStrength; - if (QDataVis::SelectionModeSliceColumn == m_cachedSelectionMode) { - item.setTranslation(modelMatrix.column(3).toVector3D()); - item.setPosition(QPoint(row, bar)); - if (selectionDirty && row < m_renderRows) - m_sliceSelection->append(&item); } - break; - } - case Bars3DController::SelectionNone: { - // Current bar is not selected, nor on a row or column - // do nothing - break; - } } - } - // Skip drawing of 0 -height bars - if (item.height() != 0) { - // Set shader bindings - m_barShader->setUniformValue(m_barShader->model(), modelMatrix); - m_barShader->setUniformValue(m_barShader->nModel(), - itModelMatrix.transposed().inverted()); - m_barShader->setUniformValue(m_barShader->MVP(), MVPMatrix); - m_barShader->setUniformValue(m_barShader->color(), barColor); + // Skip drawing of 0-height bars + if (item.height() != 0) { + // Set shader bindings + barShader->setUniformValue(barShader->model(), modelMatrix); + barShader->setUniformValue(barShader->nModel(), + itModelMatrix.transposed().inverted()); + barShader->setUniformValue(barShader->MVP(), MVPMatrix); + if (colorStyleIsUniform) { + barShader->setUniformValue(barShader->color(), barColor); + } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { + barShader->setUniformValue(barShader->gradientHeight(), + qAbs(item.height()) / m_gradientFraction); + } #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_barShader->setUniformValue(m_barShader->shadowQ(), m_shadowQualityToShader); - m_barShader->setUniformValue(m_barShader->depth(), depthMVPMatrix); - m_barShader->setUniformValue(m_barShader->lightS(), shadowLightStrength); - - // Draw the object - m_drawer->drawObject(m_barShader, m_barObj, 0, m_depthTexture); - } else + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + barShader->setUniformValue(barShader->shadowQ(), m_shadowQualityToShader); + barShader->setUniformValue(barShader->depth(), depthMVPMatrix); + barShader->setUniformValue(barShader->lightS(), shadowLightStrength); + + // Draw the object + m_drawer->drawObject(barShader, barObj, gradientTexture, m_depthTexture); + } else #endif - { - // Set shadowless shader bindings - m_barShader->setUniformValue(m_barShader->lightS(), lightStrength); + { + // Set shadowless shader bindings + barShader->setUniformValue(barShader->lightS(), lightStrength); - // Draw the object - m_drawer->drawObject(m_barShader, m_barObj); + // Draw the object + m_drawer->drawObject(barShader, barObj, gradientTexture); + } } } } + seriesPos += m_seriesStep; } - if (selectionDirty) - emit selectedBarPosChanged(QPoint(int(m_selection.x()), int(m_selection.y()))); - - // Release bar shader - m_barShader->release(); - // Bind background shader m_backgroundShader->bind(); - if (m_hasNegativeValues) - glDisable(GL_CULL_FACE); - else - glCullFace(GL_BACK); + // Reset culling + glCullFace(GL_BACK); // Draw background GLfloat rowScaleFactor = m_rowWidth / m_scaleFactor; GLfloat columnScaleFactor = m_columnDepth / m_scaleFactor; - if (m_cachedIsBackgroundEnabled && m_backgroundObj) { + if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; QVector3D backgroundScaler(rowScaleFactor, 1.0f, columnScaleFactor); - modelMatrix.translate(0.0f, 1.0f - m_yAdjustment, 0.0f); + if (m_hasNegativeValues) { + backgroundScaler.setY(0.5f); + modelMatrix.translate(0.0f, m_negativeBackgroundAdjustment, 0.0f); + } else { + modelMatrix.translate(0.0f, 1.0f, 0.0f); + } modelMatrix.scale(backgroundScaler); - modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); itModelMatrix.scale(backgroundScaler); + modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); #ifdef SHOW_DEPTH_TEXTURE_SCENE @@ -972,7 +1290,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) #else MVPMatrix = projectionViewMatrix * modelMatrix; #endif - QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme.m_backgroundColor); + QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); // Set shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); @@ -983,7 +1301,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) 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); + m_cachedTheme->ambientLightStrength() * 2.0f); #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { @@ -1002,53 +1320,96 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) { // Set shadowless shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), - m_cachedTheme.m_lightStrength); + m_cachedTheme->lightStrength()); // Draw the object m_drawer->drawObject(m_backgroundShader, m_backgroundObj); } - } - // Release background shader - m_backgroundShader->release(); + // Draw floor for graph with negatives + if (m_hasNegativeValues) { + modelMatrix = QMatrix4x4(); + itModelMatrix = QMatrix4x4(); - // Disable textures - glDisable(GL_TEXTURE_2D); + modelMatrix.scale(backgroundScaler); - // Reset culling - if (m_hasNegativeValues) { - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); + if (m_yFlipped) + modelMatrix.rotate(90.0f, 1.0f, 0.0f, 0.0f); + else + modelMatrix.rotate(-90.0f, 1.0f, 0.0f, 0.0f); + + itModelMatrix = modelMatrix; + +#ifdef SHOW_DEPTH_TEXTURE_SCENE + MVPMatrix = depthProjectionViewMatrix * modelMatrix; +#else + MVPMatrix = projectionViewMatrix * modelMatrix; +#endif + // Set changed shader bindings + m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix); + m_backgroundShader->setUniformValue(m_backgroundShader->nModel(), + itModelMatrix.inverted().transposed()); + m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(m_backgroundShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Draw the object + m_drawer->drawObject(m_backgroundShader, m_gridLineObj); + } + } } + // Disable textures + glDisable(GL_TEXTURE_2D); + // Draw grid lines - if (m_cachedIsGridEnabled && m_heightNormalizer) { + if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { ShaderHelper *lineShader = m_backgroundShader; + QQuaternion lineRotation = QQuaternion(); // Bind bar shader lineShader->bind(); // Set unchanging shader bindings - QVector3D barColor = Utils::vectorFromColor(m_cachedTheme.m_gridLine); + QVector3D barColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); lineShader->setUniformValue(lineShader->lightP(), lightPos); lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), barColor); - lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme.m_ambientStrength); + lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength()); #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { // Set shadowed shader bindings lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); lineShader->setUniformValue(lineShader->lightS(), - m_cachedTheme.m_lightStrength / 20.0f); + m_cachedTheme->lightStrength() / 20.0f); } else #endif { // Set shadowless shader bindings - lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme.m_lightStrength / 2.5f); + lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 2.5f); } + GLfloat yFloorLinePosition = 0.0f; + if (m_yFlipped) + yFloorLinePosition -= gridLineOffset; + else + yFloorLinePosition += gridLineOffset; + QVector3D gridLineScaler(rowScaleFactor, gridLineWidth, gridLineWidth); + if (m_yFlipped) + lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f); + else + lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); + // Floor lines: rows for (GLfloat row = 0.0f; row <= m_cachedRowCount; row++) { QMatrix4x4 modelMatrix; @@ -1056,15 +1417,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; rowPos = row * m_cachedBarSpacing.height(); - modelMatrix.translate(0.0f, -m_yAdjustment, + modelMatrix.translate(0.0f, yFloorLinePosition, (m_columnDepth - rowPos) / m_scaleFactor); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); - // If we're viewing from below, grid line object must be flipped - if (m_yFlipped) { - modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - itModelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - } + modelMatrix.rotate(lineRotation); + itModelMatrix.rotate(lineRotation); MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1098,15 +1456,11 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) colPos = bar * m_cachedBarSpacing.width(); modelMatrix.translate((m_rowWidth - colPos) / m_scaleFactor, - -m_yAdjustment, 0.0f); + yFloorLinePosition, 0.0f); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); - - // If we're viewing from below, grid line object must be flipped - if (m_yFlipped) { - modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - itModelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - } + modelMatrix.rotate(lineRotation); + itModelMatrix.rotate(lineRotation); MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1135,28 +1489,35 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Wall lines: back wall GLfloat heightStep = m_axisCacheY.subSegmentStep(); GLfloat startLine = 0.0f; + int segmentCount = m_axisCacheY.segmentCount() * m_axisCacheY.subSegmentCount(); - if (m_hasNegativeValues) - startLine = -m_heightNormalizer; + GLfloat zWallLinePosition = -columnScaleFactor + gridLineOffset; + if (m_zFlipped) + zWallLinePosition = -zWallLinePosition; + if (m_hasNegativeValues) { + if (m_noZeroInRange) + startLine = m_axisCacheY.min() - m_axisCacheY.max(); + else + startLine = m_axisCacheY.min(); + } + + GLfloat lineHeight = startLine; gridLineScaler = QVector3D(rowScaleFactor, gridLineWidth, gridLineWidth); - for (GLfloat lineHeight = startLine; lineHeight <= m_heightNormalizer; - lineHeight += heightStep) { + for (int segment = 0; segment <= segmentCount; segment++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - if (m_zFlipped) { - modelMatrix.translate(0.0f, - 2.0f * lineHeight / m_heightNormalizer - m_yAdjustment, - columnScaleFactor); - } else { - modelMatrix.translate(0.0f, - 2.0f * lineHeight / m_heightNormalizer - m_yAdjustment, - -columnScaleFactor); - } + modelMatrix.translate(0.0f, + 2.0f * lineHeight / m_heightNormalizer, + zWallLinePosition); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); + if (m_zFlipped) { + modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1179,27 +1540,33 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } + lineHeight += heightStep; } // Wall lines: side wall + GLfloat xWallLinePosition = -rowScaleFactor + gridLineOffset; + if (m_xFlipped) + xWallLinePosition = -xWallLinePosition; + + if (m_xFlipped) + lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f); + else + lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + + lineHeight = startLine; gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor); - for (GLfloat lineHeight = startLine; lineHeight <= m_heightNormalizer; - lineHeight += heightStep) { + for (int segment = 0; segment <= segmentCount; segment++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - if (m_xFlipped) { - modelMatrix.translate(rowScaleFactor, - 2.0f * lineHeight / m_heightNormalizer - m_yAdjustment, - 0.0f); - } else { - modelMatrix.translate(-rowScaleFactor, - 2.0f * lineHeight / m_heightNormalizer - m_yAdjustment, - 0.0f); - } + modelMatrix.translate(xWallLinePosition, + 2.0f * lineHeight / m_heightNormalizer, + 0.0f); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); + modelMatrix.rotate(lineRotation); + itModelMatrix.rotate(lineRotation); MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1222,27 +1589,25 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } + lineHeight += heightStep; } } - // Release bar shader - lineShader->release(); } - // 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); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_POLYGON_OFFSET_FILL); // Calculate the positions for row and column labels and store them - GLfloat labelYAdjustment = -m_yAdjustment + 0.005f; + GLfloat labelYAdjustment = 0.005f; GLfloat scaledRowWidth = rowScaleFactor; GLfloat scaledColumnDepth = columnScaleFactor; GLfloat colPosValue = scaledRowWidth + labelMargin; GLfloat rowPosValue = scaledColumnDepth + labelMargin; - QVector3D positionComp(0.0f, m_yAdjustment, 0.0f); QVector3D labelRotation(-90.0f, 0.0f, 0.0f); if (m_zFlipped) labelRotation.setY(180.0f); @@ -1253,9 +1618,10 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) labelRotation.setY(180.0f); labelRotation.setZ(180.0f); } + Qt::AlignmentFlag alignment = m_xFlipped ? Qt::AlignLeft : Qt::AlignRight; for (int row = 0; row != m_cachedRowCount; row++) { - if (m_axisCacheX.labelItems().size() > row) { + if (m_axisCacheZ.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 + 0.5f) * m_cachedBarSpacing.height(); @@ -1264,18 +1630,19 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) else colPos = colPosValue; + glPolygonOffset(GLfloat(row) / -10.0f, 1.0f); + QVector3D labelPos = QVector3D(colPos, labelYAdjustment, // raise a bit over background to avoid depth "glimmering" (m_columnDepth - rowPos) / m_scaleFactor); m_dummyBarRenderItem.setTranslation(labelPos); - const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(row); - //qDebug() << "labelPos, row" << row + 1 << ":" << labelPos << m_axisCacheX.labels().at(row); + const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(row); m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - positionComp, labelRotation, 0, m_cachedSelectionMode, + zeroVector, labelRotation, 0, m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, - true, true, Drawer::LabelMid, alignment); + true, true, Drawer::LabelMid, alignment, m_cachedIsSlicingActivated); } } labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); @@ -1290,8 +1657,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } alignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft; - for (int column = 0; column != m_cachedColumnCount; column += 1) { - if (m_axisCacheZ.labelItems().size() > column) { + for (int column = 0; column != m_cachedColumnCount; column++) { + if (m_axisCacheX.labelItems().size() > column) { // 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 colPos = (column + 0.5f) * m_cachedBarSpacing.width(); @@ -1300,15 +1667,17 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) else rowPos = rowPosValue; + glPolygonOffset(GLfloat(column) / -10.0f, 1.0f); + QVector3D labelPos = QVector3D((colPos - m_rowWidth) / m_scaleFactor, labelYAdjustment, // raise a bit over background to avoid depth "glimmering" rowPos); m_dummyBarRenderItem.setTranslation(labelPos); - const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(column); + const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(column); m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - positionComp, labelRotation, 0, m_cachedSelectionMode, + zeroVector, labelRotation, 0, m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, true, true, Drawer::LabelMid, alignment); } @@ -1319,8 +1688,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) GLfloat heightStep = m_axisCacheY.segmentStep(); GLfloat startLine = 0.0f; int labelCount = m_axisCacheY.labels().size(); - if (m_hasNegativeValues) - startLine = -m_heightNormalizer; + if (m_hasNegativeValues) { + if (m_noZeroInRange) + startLine = m_axisCacheY.min() - m_axisCacheY.max(); + else + startLine = m_axisCacheY.min(); + } GLfloat labelPos = startLine; GLfloat labelMarginXTrans = labelMargin; GLfloat labelMarginZTrans = labelMargin; @@ -1349,50 +1722,34 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) for (int i = 0; i < labelCount; i++) { if (m_axisCacheY.labelItems().size() > labelNbr) { - backLabelTrans.setY(2.0f * labelPos / m_heightNormalizer - m_yAdjustment); + backLabelTrans.setY(2.0f * labelPos / m_heightNormalizer); sideLabelTrans.setY(backLabelTrans.y()); + glPolygonOffset(GLfloat(i) / -10.0f, 1.0f); + const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); // Back wall m_dummyBarRenderItem.setTranslation(backLabelTrans); m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - positionComp, backLabelRotation, 0, m_cachedSelectionMode, + zeroVector, backLabelRotation, 0, m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, true, true, Drawer::LabelMid, backAlignment); // Side wall m_dummyBarRenderItem.setTranslation(sideLabelTrans); m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - positionComp, sideLabelRotation, 0, m_cachedSelectionMode, + zeroVector, sideLabelRotation, 0, m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, true, true, Drawer::LabelMid, sideAlignment); } labelNbr++; labelPos += heightStep; } + glDisable(GL_POLYGON_OFFSET_FILL); - // Handle slice activation and selection label drawing - if (!barSelectionFound) { - // We have no ownership, don't delete. Just NULL the pointer. - m_selectedBar = NULL; - if (m_cachedIsSlicingActivated - && (m_selection == selectionSkipColor - || QDataVis::InputStateOnOverview == m_controller->inputState())) { - m_cachedScene->setSlicingActive(false); - } - } else if (m_cachedSelectionMode >= QDataVis::SelectionModeSliceRow && selectionDirty) { - // Activate slice mode - m_cachedScene->setSlicingActive(true); - - // Create label textures - for (int col = 0; col < m_sliceSelection->size(); col++) { - BarRenderItem *item = m_sliceSelection->at(col); - if (item->sliceLabel().isNull()) - item->setSliceLabel(generateValueLabel(m_axisCacheY.labelFormat(), item->value())); - m_drawer->generateLabelItem(item->sliceLabelItem(), item->sliceLabel()); - } - } else { + // Handle selected bar label generation + if (barSelectionFound) { // Print value of selected bar glDisable(GL_DEPTH_TEST); // Draw the selection label @@ -1410,22 +1767,24 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) static const QString valueLabelTag(QStringLiteral("@valueLabel")); // Custom format expects printf format specifier. There is no tag for it. - labelText = generateValueLabel(itemLabelFormat(), selectedBar->value()); - - int selBarPosX = selectedBar->position().x(); - int selBarPosY = selectedBar->position().y(); - labelText.replace(rowIndexTag, QString::number(selBarPosX)); - if (m_axisCacheX.labels().size() > selBarPosX) - labelText.replace(rowLabelTag, m_axisCacheX.labels().at(selBarPosX)); + labelText = generateValueLabel( + m_visibleSeriesList[m_visualSelectedBarSeriesIndex].itemLabelFormat(), + selectedBar->value()); + + int selBarPosRow = selectedBar->position().x(); + int selBarPosCol = selectedBar->position().y(); + labelText.replace(rowIndexTag, QString::number(selBarPosRow)); + if (m_axisCacheZ.labels().size() > selBarPosRow) + labelText.replace(rowLabelTag, m_axisCacheZ.labels().at(selBarPosRow)); else labelText.replace(rowLabelTag, QString()); - labelText.replace(rowTitleTag, m_axisCacheX.title()); - labelText.replace(colIndexTag, QString::number(selBarPosY)); - if (m_axisCacheZ.labels().size() > selBarPosY) - labelText.replace(colLabelTag, m_axisCacheZ.labels().at(selBarPosY)); + labelText.replace(rowTitleTag, m_axisCacheZ.title()); + labelText.replace(colIndexTag, QString::number(selBarPosCol)); + if (m_axisCacheX.labels().size() > selBarPosCol) + labelText.replace(colLabelTag, m_axisCacheX.labels().at(selBarPosCol)); else labelText.replace(colLabelTag, QString()); - labelText.replace(colTitleTag, m_axisCacheZ.title()); + labelText.replace(colTitleTag, m_axisCacheX.title()); labelText.replace(valueTitleTag, m_axisCacheY.title()); if (labelText.contains(valueLabelTag)) { @@ -1443,7 +1802,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix, - positionComp, zeroVector, selectedBar->height(), + zeroVector, zeroVector, selectedBar->height(), m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, true, false); @@ -1451,23 +1810,17 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_updateLabels = false; glEnable(GL_DEPTH_TEST); + } else { + m_selectedBar = 0; } glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); - // Release label shader - m_labelShader->release(); -} + // Release shader + glUseProgram(0); -void Bars3DRenderer::handleResize() -{ - if (m_cachedBoundingRect.width() == 0 || m_cachedBoundingRect.height() == 0) - return; - - setViewPorts(); - - Abstract3DRenderer::handleResize(); + m_selectionDirty = false; } void Bars3DRenderer::updateBarSpecs(GLfloat thicknessRatio, const QSizeF &spacing, bool relative) @@ -1477,17 +1830,24 @@ void Bars3DRenderer::updateBarSpecs(GLfloat thicknessRatio, const QSizeF &spacin m_cachedBarThickness.setHeight(1.0f / thicknessRatio); if (relative) { - m_cachedBarSpacing.setWidth((m_cachedBarThickness.width() * 2) * (spacing.width() + 1.0f)); - m_cachedBarSpacing.setHeight((m_cachedBarThickness.height() * 2) * (spacing.height() + 1.0f)); + m_cachedBarSpacing.setWidth((m_cachedBarThickness.width() * 2) + * (spacing.width() + 1.0f)); + m_cachedBarSpacing.setHeight((m_cachedBarThickness.height() * 2) + * (spacing.height() + 1.0f)); } else { m_cachedBarSpacing = m_cachedBarThickness * 2 + spacing * 2; } + // Slice mode doesn't update correctly without this + if (m_cachedIsSlicingActivated) + m_selectionDirty = true; + // Calculate here and at setting sample space calculateSceneScalingFactors(); } -void Bars3DRenderer::updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, qreal min, qreal max) +void Bars3DRenderer::updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, float min, + float max) { Abstract3DRenderer::updateAxisRange(orientation, min, max); @@ -1505,44 +1865,40 @@ void Bars3DRenderer::updateAxisRange(Q3DAbstractAxis::AxisOrientation orientatio loadBackgroundMesh(); emit needRender(); } - - // TODO Currently bargraph only supports zero centered or zero minimum ranges - if (min > 0.0 || (min != 0.0 && (qFabs(min) != qFabs(max)))) { - qWarning() << __FUNCTION__ << "Bar graph currently properly supports only " - "zero-centered and zero minimum ranges for Y-axis."; - } } } -void Bars3DRenderer::updateSelectionMode(QDataVis::SelectionMode mode) +void Bars3DRenderer::updateSelectedBar(const QPoint &position, const QBar3DSeries *series) { - Abstract3DRenderer::updateSelectionMode(mode); + m_selectedBarPos = position; + m_selectedBarSeries = series; + m_selectionDirty = true; + m_visualSelectedBarSeriesIndex = -1; - // Create zoom selection if there isn't one - if (mode >= QDataVis::SelectionModeSliceRow && !m_sliceSelection) { - m_sliceSelection = new QList<BarRenderItem *>; - if (mode == QDataVis::SelectionModeSliceRow) - m_sliceSelection->reserve(m_cachedRowCount); - else - m_sliceSelection->reserve(m_cachedColumnCount); + if (m_renderingArrays.isEmpty()) { + m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition(); + return; } -} -void Bars3DRenderer::updateBackgroundEnabled(bool enable) -{ - if (enable != m_cachedIsBackgroundEnabled) { - Abstract3DRenderer::updateBackgroundEnabled(enable); - loadMeshFile(); // Load changed bar type + int adjustedZ = m_selectedBarPos.x() - int(m_axisCacheZ.min()); + int adjustedX = m_selectedBarPos.y() - int(m_axisCacheX.min()); + int maxZ = m_renderingArrays.at(0).size() - 1; + int maxX = maxZ >= 0 ? m_renderingArrays.at(0).at(0).size() - 1 : -1; + + for (int i = 0; i < m_visibleSeriesList.size(); i++) { + if (m_visibleSeriesList.at(i).series() == series) { + m_visualSelectedBarSeriesIndex = i; + break; + } } -} -void Bars3DRenderer::updateSelectedBarPos(const QPoint &position) -{ - if (position == Bars3DController::noSelectionPoint()) - m_selection = selectionSkipColor; - else - m_selection = QVector3D(position.x(), position.y(), 0); - emit needRender(); + if (m_selectedBarPos == Bars3DController::invalidSelectionPosition() + || adjustedZ < 0 || adjustedZ > maxZ + || adjustedX < 0 || adjustedX > maxX) { + m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition(); + } else { + m_visualSelectedBarPos = QPoint(adjustedZ, adjustedX); + } } void Bars3DRenderer::updateShadowQuality(QDataVis::ShadowQuality quality) @@ -1587,18 +1943,6 @@ void Bars3DRenderer::updateShadowQuality(QDataVis::ShadowQuality quality) #endif } -void Bars3DRenderer::loadMeshFile() -{ - 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 Bars3DRenderer::loadBackgroundMesh() { if (m_backgroundObj) @@ -1614,7 +1958,7 @@ void Bars3DRenderer::loadGridLineMesh() { if (m_gridLineObj) delete m_gridLineObj; - m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/bar")); + m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); m_gridLineObj->load(); } @@ -1622,7 +1966,7 @@ void Bars3DRenderer::loadLabelMesh() { if (m_labelObj) delete m_labelObj; - m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/label")); + m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); m_labelObj->load(); } @@ -1632,6 +1976,16 @@ void Bars3DRenderer::updateTextures() m_updateLabels = true; } +void Bars3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh) +{ + if (!m_cachedTheme->isBackgroundEnabled()) { + // Load full version of meshes that have it available + // Note: Minimal and Point not supported in bar charts + if (mesh != QAbstract3DSeries::MeshSphere) + fileName.append(QStringLiteral("Full")); + } +} + void Bars3DRenderer::calculateSceneScalingFactors() { // Calculate scene scaling and translation factors @@ -1642,61 +1996,81 @@ void Bars3DRenderer::calculateSceneScalingFactors() (m_cachedRowCount * (m_maxDimension / m_maxSceneSize))); m_scaleX = m_cachedBarThickness.width() / m_scaleFactor; m_scaleZ = m_cachedBarThickness.height() / m_scaleFactor; - //qDebug() << "m_scaleX" << m_scaleX << "m_scaleFactor" << m_scaleFactor; - //qDebug() << "m_scaleZ" << m_scaleZ << "m_scaleFactor" << m_scaleFactor; - //qDebug() << "m_rowWidth:" << m_rowWidth << "m_columnDepth:" << m_columnDepth << "m_maxDimension:" << m_maxDimension; } void Bars3DRenderer::calculateHeightAdjustment() { - m_heightNormalizer = (GLfloat)qMax(qFabs(m_axisCacheY.min()), qFabs(m_axisCacheY.max())); + GLfloat newAdjustment = 0.0f; + GLfloat maxAbs = qFabs(m_axisCacheY.max()); - // 2.0f = max difference between minimum and maximum value after scaling with m_heightNormalizer - GLfloat newAdjustment = 2.0f - ((m_heightNormalizer - m_axisCacheY.min()) / m_heightNormalizer); - if (newAdjustment != m_yAdjustment) { + if (m_axisCacheY.max() < 0.0f) { + m_heightNormalizer = GLfloat(qFabs(m_axisCacheY.min()) - qFabs(m_axisCacheY.max())); + maxAbs = qFabs(m_axisCacheY.max()) - qFabs(m_axisCacheY.min()); + } else { + m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()); + } + + // Height fractions are used in gradient calculations and are therefore doubled + if (m_axisCacheY.max() < 0.0f || m_axisCacheY.min() > 0.0f) { + m_noZeroInRange = true; + m_gradientFraction = 2.0f; + } else { + m_noZeroInRange = false; + GLfloat minAbs = qFabs(m_axisCacheY.min()); + m_gradientFraction = qMax(minAbs, maxAbs) / m_heightNormalizer * 2.0f; + } + + // Calculate translation adjustment for negative background + newAdjustment = qBound(0.0f, (maxAbs / m_heightNormalizer), 1.0f) * 2.0f - 0.5f; + + if (newAdjustment != m_negativeBackgroundAdjustment) { m_hasHeightAdjustmentChanged = true; - m_yAdjustment = newAdjustment; + m_negativeBackgroundAdjustment = newAdjustment; } - //qDebug() << m_yAdjustment; } -Bars3DController::SelectionType Bars3DRenderer::isSelected(GLint row, GLint bar) +Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar, int seriesIndex) { - //static QVector3D prevSel = m_selection; // TODO: For debugging Bars3DController::SelectionType isSelectedType = Bars3DController::SelectionNone; - if (m_selection == selectionSkipColor) - return isSelectedType; // skip window - - //#if !defined(QT_OPENGL_ES_2) - // QVector3D current = QVector3D((GLuint)row, (GLuint)bar, 0); - //#else - QVector3D current = QVector3D((GLubyte)row, (GLubyte)bar, 0); - //#endif - - // TODO: For debugging - //if (selection != prevSel) { - // qDebug() << "current" << current.x() << current .y() << current.z(); - // qDebug() << "selection" << selection.x() << selection .y() << selection.z(); - // prevSel = selection; - //} - if (current == m_selection) { - isSelectedType = Bars3DController::SelectionItem; - } - else if (current.y() == m_selection.y() - && (m_cachedSelectionMode == QDataVis::SelectionModeItemAndColumn - || m_cachedSelectionMode == QDataVis::SelectionModeItemRowAndColumn - || m_cachedSelectionMode == QDataVis::SelectionModeSliceColumn)) { - isSelectedType = Bars3DController::SelectionColumn; - } - else if (current.x() == m_selection.x() - && (m_cachedSelectionMode == QDataVis::SelectionModeItemAndRow - || m_cachedSelectionMode == QDataVis::SelectionModeItemRowAndColumn - || m_cachedSelectionMode == QDataVis::SelectionModeSliceRow)) { - isSelectedType = Bars3DController::SelectionRow; + + if ((m_cachedSelectionMode.testFlag(QDataVis::SelectionMultiSeries) + && m_visualSelectedBarSeriesIndex >= 0) + || seriesIndex == m_visualSelectedBarSeriesIndex) { + if (row == m_visualSelectedBarPos.x() && bar == m_visualSelectedBarPos.y() + && (m_cachedSelectionMode.testFlag(QDataVis::SelectionItem))) { + isSelectedType = Bars3DController::SelectionItem; + } else if (row == m_visualSelectedBarPos.x() + && (m_cachedSelectionMode.testFlag(QDataVis::SelectionRow))) { + isSelectedType = Bars3DController::SelectionRow; + } else if (bar == m_visualSelectedBarPos.y() + && (m_cachedSelectionMode.testFlag(QDataVis::SelectionColumn))) { + isSelectedType = Bars3DController::SelectionColumn; + } } + return isSelectedType; } +QPoint Bars3DRenderer::selectionColorToArrayPosition(const QVector3D &selectionColor) +{ + QPoint position; + if (selectionColor == selectionSkipColor) { + position = Bars3DController::invalidSelectionPosition(); + } else { + position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())), + int(selectionColor.y()) + int(m_axisCacheX.min())); + } + return position; +} + +QBar3DSeries *Bars3DRenderer::selectionColorToSeries(const QVector3D &selectionColor) +{ + if (selectionColor == selectionSkipColor) + return 0; + else + return static_cast<QBar3DSeries *>(m_visibleSeriesList.at(int(selectionColor.z())).series()); +} + void Bars3DRenderer::updateSlicingActive(bool isSlicing) { if (isSlicing == m_cachedIsSlicingActivated) @@ -1704,35 +2078,14 @@ void Bars3DRenderer::updateSlicingActive(bool isSlicing) m_cachedIsSlicingActivated = isSlicing; - setViewPorts(); - if (!m_cachedIsSlicingActivated) initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize #if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well #endif -} -void Bars3DRenderer::setViewPorts() -{ - // Update view ports - if (m_cachedIsSlicingActivated) { - m_mainViewPort = QRect(0, - m_cachedBoundingRect.height() - - (m_cachedBoundingRect.height() / smallerVPSize), - m_cachedBoundingRect.width() / smallerVPSize, - m_cachedBoundingRect.height() / smallerVPSize); - m_sliceViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); - } else { - m_mainViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); - m_sliceViewPort = QRect(0, 0, 0, 0); - } -} - -QRect Bars3DRenderer::mainViewPort() -{ - return m_mainViewPort; + m_selectionDirty = true; } void Bars3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) @@ -1743,12 +2096,20 @@ void Bars3DRenderer::initShaders(const QString &vertexShader, const QString &fra m_barShader->initialize(); } +void Bars3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader) +{ + if (m_barGradientShader) + delete m_barGradientShader; + m_barGradientShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_barGradientShader->initialize(); +} + void Bars3DRenderer::initSelectionShader() { if (m_selectionShader) delete m_selectionShader; - m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSelection"), - QStringLiteral(":/shaders/fragmentSelection")); + m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"), + QStringLiteral(":/shaders/fragmentPlainColor")); m_selectionShader->initialize(); } @@ -1759,10 +2120,10 @@ void Bars3DRenderer::initSelectionBuffer() m_selectionTexture = 0; } - if (m_cachedIsSlicingActivated || m_mainViewPort.size().isEmpty()) + if (m_cachedIsSlicingActivated || m_primarySubViewport.size().isEmpty()) return; - m_selectionTexture = m_textureHelper->createSelectionTexture(m_mainViewPort.size(), + m_selectionTexture = m_textureHelper->createSelectionTexture(m_primarySubViewport.size(), m_selectionFrameBuffer, m_selectionDepthBuffer); } @@ -1784,50 +2145,15 @@ void Bars3DRenderer::updateDepthBuffer() m_depthTexture = 0; } - if (m_mainViewPort.size().isEmpty()) + if (m_primarySubViewport.size().isEmpty()) return; if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { - m_depthTexture = m_textureHelper->createDepthTexture(m_mainViewPort.size(), - m_depthFrameBuffer, - m_shadowQualityMultiplier); - if (!m_depthTexture) { - switch (m_cachedShadowQuality) { - case QDataVis::ShadowQualityHigh: - qWarning("Creating high quality shadows failed. Changing to medium quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityMedium); - updateShadowQuality(QDataVis::ShadowQualityMedium); - break; - case QDataVis::ShadowQualityMedium: - qWarning("Creating medium quality shadows failed. Changing to low quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityLow); - updateShadowQuality(QDataVis::ShadowQualityLow); - break; - case QDataVis::ShadowQualityLow: - qWarning("Creating low quality shadows failed. Switching shadows off."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityNone); - updateShadowQuality(QDataVis::ShadowQualityNone); - break; - case QDataVis::ShadowQualitySoftHigh: - qWarning("Creating soft high quality shadows failed. Changing to soft medium quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualitySoftMedium); - updateShadowQuality(QDataVis::ShadowQualitySoftMedium); - break; - case QDataVis::ShadowQualitySoftMedium: - qWarning("Creating soft medium quality shadows failed. Changing to soft low quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualitySoftLow); - updateShadowQuality(QDataVis::ShadowQualitySoftLow); - break; - case QDataVis::ShadowQualitySoftLow: - qWarning("Creating soft low quality shadows failed. Switching shadows off."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityNone); - updateShadowQuality(QDataVis::ShadowQualityNone); - break; - default: - // You'll never get here - break; - } - } + m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + if (!m_depthTexture) + lowerShadowQuality(); } } #endif diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index dd80902a..16e7a476 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -51,9 +51,6 @@ class QT_DATAVISUALIZATION_EXPORT Bars3DRenderer : public Abstract3DRenderer Q_OBJECT private: - // TODO: Filter to the set of attributes to be moved to the model object. - Bars3DController *m_controller; - // Cached state based on emitted signals from the controller QSizeF m_cachedBarThickness; QSizeF m_cachedBarSpacing; @@ -63,21 +60,19 @@ private: // Internal state BarRenderItem *m_selectedBar; // points to renderitem array - QList<BarRenderItem *> *m_sliceSelection; + QVector<BarRenderItem *> m_sliceSelection; AxisRenderCache *m_sliceCache; // not owned const LabelItem *m_sliceTitleItem; // not owned bool m_xFlipped; bool m_zFlipped; bool m_yFlipped; - QRect m_mainViewPort; - QRect m_sliceViewPort; bool m_updateLabels; ShaderHelper *m_barShader; + ShaderHelper *m_barGradientShader; ShaderHelper *m_depthShader; ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; ShaderHelper *m_labelShader; - ObjectHelper *m_barObj; ObjectHelper *m_backgroundObj; ObjectHelper *m_gridLineObj; ObjectHelper *m_labelObj; @@ -90,7 +85,8 @@ private: GLfloat m_shadowQualityToShader; GLint m_shadowQualityMultiplier; GLfloat m_heightNormalizer; - GLfloat m_yAdjustment; + GLfloat m_gradientFraction; + GLfloat m_negativeBackgroundAdjustment; GLfloat m_rowWidth; GLfloat m_columnDepth; GLfloat m_maxDimension; @@ -98,55 +94,52 @@ private: GLfloat m_scaleZ; GLfloat m_scaleFactor; GLfloat m_maxSceneSize; - QVector3D m_selection; - QVector3D m_previousSelection; - int m_renderRows; - int m_renderColumns; - + QPoint m_visualSelectedBarPos; + int m_visualSelectedBarSeriesIndex; bool m_hasHeightAdjustmentChanged; + QPoint m_selectedBarPos; + const QBar3DSeries *m_selectedBarSeries; BarRenderItem m_dummyBarRenderItem; - - BarRenderItemArray m_renderItemArray; + QVector<BarRenderItemArray> m_renderingArrays; + bool m_noZeroInRange; + float m_seriesScale; + float m_seriesStep; + float m_seriesStart; public: explicit Bars3DRenderer(Bars3DController *controller); ~Bars3DRenderer(); - void updateDataModel(QBarDataProxy *dataProxy); + void updateData(); void updateScene(Q3DScene *scene); void render(GLuint defaultFboHandle = 0); - QRect mainViewPort(); - protected: virtual void initializeOpenGL(); - virtual void loadMeshFile(); public slots: void updateBarSpecs(GLfloat thicknessRatio = 1.0f, const QSizeF &spacing = QSizeF(1.0, 1.0), bool relative = true); - void updateSelectionMode(QDataVis::SelectionMode newMode); void updateSlicingActive(bool isSlicing); - void updateBackgroundEnabled(bool enable); - void updateSelectedBarPos(const QPoint &position); + void updateSelectedBar(const QPoint &position, const QBar3DSeries *series); // Overloaded from abstract renderer - virtual void updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, qreal min, qreal max); + virtual void updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, float min, float max); signals: - void selectedBarPosChanged(QPoint position); + void barClicked(QPoint position, QBar3DSeries *series); private: virtual void initShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader); virtual void updateShadowQuality(QDataVis::ShadowQuality quality); virtual void updateTextures(); + virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh); - void drawSlicedScene(const LabelItem &xLabel, const LabelItem &yLabel, const LabelItem &zLabel); + void drawSlicedScene(); void drawScene(GLuint defaultFboHandle); - void handleResize(); - void setViewPorts(); void loadBackgroundMesh(); void loadGridLineMesh(); void loadLabelMesh(); @@ -160,7 +153,9 @@ private: #endif void calculateSceneScalingFactors(); void calculateHeightAdjustment(); - Abstract3DController::SelectionType isSelected(GLint row, GLint bar); + Abstract3DController::SelectionType isSelected(int row, int bar, int seriesIndex); + QPoint selectionColorToArrayPosition(const QVector3D &selectionColor); + QBar3DSeries *selectionColorToSeries(const QVector3D &selectionColor); Q_DISABLE_COPY(Bars3DRenderer) diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp index cf390b9d..14f0b315 100644 --- a/src/datavisualization/engine/drawer.cpp +++ b/src/datavisualization/engine/drawer.cpp @@ -41,17 +41,22 @@ StaticLibInitializer staticLibInitializer; QT_DATAVISUALIZATION_BEGIN_NAMESPACE -Drawer::Drawer(const Theme &theme, const QFont &font, QDataVis::LabelStyle style) +// Vertex array buffer for point +const GLfloat point_data[] = {0.0f, 0.0f, 0.0f}; + +Drawer::Drawer(Q3DTheme *theme) : m_theme(theme), - m_font(font), - m_style(style), - m_textureHelper(0) + m_font(theme->font()), + m_labelBackground(theme->isLabelBackgroundEnabled()), + m_textureHelper(0), + m_pointbuffer(0) { } Drawer::~Drawer() { delete m_textureHelper; + glDeleteBuffers(1, &m_pointbuffer); } void Drawer::initializeOpenGL() @@ -62,19 +67,23 @@ void Drawer::initializeOpenGL() } } -void Drawer::setTheme(const Theme &theme) +void Drawer::setTheme(Q3DTheme *theme) { m_theme = theme; + m_font = m_theme->font(); + m_labelBackground = m_theme->isLabelBackgroundEnabled(); emit drawerChanged(); } -Theme Drawer::theme() const +Q3DTheme *Drawer::theme() const { return m_theme; } void Drawer::setFont(const QFont &font) { + // We need to be able to override theme's font for drawer + // TODO: (or do we?) m_font = font; emit drawerChanged(); } @@ -84,9 +93,11 @@ QFont Drawer::font() const return m_font; } -void Drawer::setStyle(QDataVis::LabelStyle style) +void Drawer::setLabelBackground(bool enabled) { - m_style = style; + // We need to be able to override theme's label background for drawer + // TODO: (or do we?) + m_labelBackground = enabled; emit drawerChanged(); } @@ -137,10 +148,14 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui glDisableVertexAttribArray(shader->posAtt()); // Release textures - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); + if (depthTextureId) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + } + if (textureId) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + } } void Drawer::drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object) @@ -163,14 +178,36 @@ void Drawer::drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object) glDisableVertexAttribArray(shader->posAtt()); } +void Drawer::drawPoint(ShaderHelper *shader) +{ + // Generate vertex buffer for point if it does not exist + if (!m_pointbuffer) { + glGenBuffers(1, &m_pointbuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(point_data), point_data, GL_STATIC_DRAW); + } + + // 1st attribute buffer : vertices + glEnableVertexAttribArray(shader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); + glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + // Draw the point + glDrawArrays(GL_POINTS, 0, 1); + + // Free buffers + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableVertexAttribArray(shader->posAtt()); +} + void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem, const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix, const QVector3D &positionComp, const QVector3D &rotation, - GLfloat itemHeight, QDataVis::SelectionMode mode, + GLfloat itemHeight, QDataVis::SelectionFlags mode, ShaderHelper *shader, ObjectHelper *object, - const Q3DCamera *camera, - bool useDepth, bool rotateAlong, - LabelPosition position, Qt::AlignmentFlag alignment) + const Q3DCamera *camera, bool useDepth, bool rotateAlong, + LabelPosition position, Qt::AlignmentFlag alignment, bool isSlicing) { // Draw label if (!labelItem.textureId()) @@ -277,7 +314,7 @@ void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelIte xPosition = item.translation().x(); if (useDepth) zPosition = item.translation().z(); - else if (QDataVis::SelectionModeSliceColumn == mode) + else if (mode.testFlag(QDataVis::SelectionColumn) && isSlicing) xPosition = -(item.translation().z()) + positionComp.z(); // flip first to left } @@ -285,21 +322,17 @@ void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelIte modelMatrix.translate(xPosition + xAlignment, yPosition + yAlignment, zPosition + zAlignment); // Rotate - // TODO: We should convert rotations to use quaternions to avoid rotation order problems - //QQuaternion rotQuatX = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, rotation.x()); - //QQuaternion rotQuatY = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, rotation.y()); - //QQuaternion rotQuatZ = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, rotation.z()); - //QQuaternion rotQuaternion = rotQuatX + rotQuatY + rotQuatZ; - //modelMatrix.rotate(rotQuaternion); - modelMatrix.rotate(rotation.y(), 0.0f, 1.0f, 0.0f); - modelMatrix.rotate(rotation.z(), 0.0f, 0.0f, 1.0f); - modelMatrix.rotate(rotation.x(), 1.0f, 0.0f, 0.0f); + QQuaternion rotQuatX = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, rotation.x()); + QQuaternion rotQuatY = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, rotation.y()); + QQuaternion rotQuatZ = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, rotation.z()); + QQuaternion rotQuaternion = rotQuatY * rotQuatZ * rotQuatX; + modelMatrix.rotate(rotQuaternion); if (useDepth && !rotateAlong) { - qreal yComp = qreal(qRadiansToDegrees(qTan(positionComp.y() / cameraDistance))); + float yComp = float(qRadiansToDegrees(qTan(positionComp.y() / cameraDistance))); // Apply negative camera rotations to keep labels facing camera - qreal camRotationX = camera->xRotation(); - qreal camRotationY = camera->yRotation(); + float camRotationX = camera->xRotation(); + float camRotationY = camera->yRotation(); modelMatrix.rotate(-camRotationX, 0.0f, 1.0f, 0.0f); modelMatrix.rotate(-camRotationY - yComp, 1.0f, 0.0f, 0.0f); } @@ -335,10 +368,10 @@ void Drawer::generateLabelItem(LabelItem &item, const QString &text, int widestL // Print label into a QImage using QPainter QImage label = Utils::printTextToImage(m_font, text, - m_theme.m_textBackgroundColor, - m_theme.m_textColor, - m_style, - m_theme.m_labelBorders, + m_theme->labelBackgroundColor(), + m_theme->labelTextColor(), + m_labelBackground, + m_theme->isLabelBorderEnabled(), widestLabel); // Set label size diff --git a/src/datavisualization/engine/drawer_p.h b/src/datavisualization/engine/drawer_p.h index 89a4ce8c..8bc62209 100644 --- a/src/datavisualization/engine/drawer_p.h +++ b/src/datavisualization/engine/drawer_p.h @@ -31,7 +31,7 @@ #include "datavisualizationglobal_p.h" #include "q3dbars.h" -#include "theme_p.h" +#include "q3dtheme.h" #include "labelitem_p.h" #include "abstractrenderitem_p.h" #include <QFont> @@ -63,28 +63,28 @@ public: }; public: - explicit Drawer(const Theme &theme, const QFont &font, QDataVis::LabelStyle style); + explicit Drawer(Q3DTheme *theme); ~Drawer(); void initializeOpenGL(); - void setTheme(const Theme &theme); - Theme theme() const; + void setTheme(Q3DTheme *theme); + Q3DTheme *theme() const; void setFont(const QFont &font); QFont font() const; - void setStyle(QDataVis::LabelStyle style); + void setLabelBackground(bool enabled); void drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId = 0, GLuint depthTextureId = 0); void drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object); + void drawPoint(ShaderHelper *shader); void drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem, const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix, const QVector3D &positionComp, const QVector3D &rotation, GLfloat itemHeight, - QDataVis::SelectionMode mode, ShaderHelper *shader, ObjectHelper *object, - const Q3DCamera *camera, - bool useDepth = false, bool rotateAlong = false, + QDataVis::SelectionFlags mode, ShaderHelper *shader, ObjectHelper *object, + const Q3DCamera *camera, bool useDepth = false, bool rotateAlong = false, LabelPosition position = LabelOver, - Qt::AlignmentFlag alignment = Qt::AlignCenter); + Qt::AlignmentFlag alignment = Qt::AlignCenter, bool isSlicing = false); void generateSelectionLabelTexture(AbstractRenderItem *item); void generateLabelItem(LabelItem &item, const QString &text, int widestLabel = 0); @@ -93,10 +93,11 @@ Q_SIGNALS: void drawerChanged(); private: - Theme m_theme; + Q3DTheme *m_theme; QFont m_font; - QDataVis::LabelStyle m_style; + bool m_labelBackground; TextureHelper *m_textureHelper; + GLuint m_pointbuffer; }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/engine.pri b/src/datavisualization/engine/engine.pri index 4c905fe9..899979e1 100644 --- a/src/datavisualization/engine/engine.pri +++ b/src/datavisualization/engine/engine.pri @@ -2,7 +2,6 @@ HEADERS += $$PWD/q3dwindow_p.h \ $$PWD/q3dwindow.h \ $$PWD/q3dbars.h \ $$PWD/q3dbars_p.h \ - $$PWD/theme_p.h \ $$PWD/drawer_p.h \ $$PWD/bars3dcontroller_p.h \ $$PWD/bars3drenderer_p.h \ @@ -16,6 +15,7 @@ HEADERS += $$PWD/q3dwindow_p.h \ $$PWD/scatter3dcontroller_p.h \ $$PWD/scatter3drenderer_p.h \ $$PWD/axisrendercache_p.h \ + $$PWD/seriesrendercache_p.h \ $$PWD/abstract3drenderer_p.h \ $$PWD/selectionpointer_p.h \ $$PWD/q3dcamera.h \ @@ -30,7 +30,6 @@ HEADERS += $$PWD/q3dwindow_p.h \ SOURCES += $$PWD/q3dwindow.cpp \ $$PWD/q3dbars.cpp \ - $$PWD/theme.cpp \ $$PWD/drawer.cpp \ $$PWD/bars3dcontroller.cpp \ $$PWD/bars3drenderer.cpp \ @@ -42,6 +41,7 @@ SOURCES += $$PWD/q3dwindow.cpp \ $$PWD/scatter3dcontroller.cpp \ $$PWD/scatter3drenderer.cpp \ $$PWD/axisrendercache.cpp \ + $$PWD/seriesrendercache.cpp \ $$PWD/abstract3drenderer.cpp \ $$PWD/selectionpointer.cpp \ $$PWD/q3dcamera.cpp \ diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index 7420ae51..8fcaecec 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -8,14 +8,12 @@ <file alias="barSmooth">meshes/cubeSmooth.obj</file> <file alias="cylinder">meshes/cylinderFlat.obj</file> <file alias="cylinderSmooth">meshes/cylinderSmooth.obj</file> - <file alias="background">meshes/backgroudFlat.obj</file> - <file alias="backgroundSmooth">meshes/backgroudSmooth.obj</file> - <file alias="label">meshes/plane.obj</file> + <file alias="background">meshes/background.obj</file> + <file alias="plane">meshes/plane.obj</file> <file alias="sphere">meshes/sphere.obj</file> <file alias="sphereSmooth">meshes/sphereSmooth.obj</file> <file alias="bevelbar">meshes/barFlat.obj</file> <file alias="bevelbarSmooth">meshes/barSmooth.obj</file> - <file alias="negativeBackground">meshes/backgroudNegatives.obj</file> <file alias="coneFull">meshes/coneFilledFlat.obj</file> <file alias="coneSmoothFull">meshes/coneFilledSmooth.obj</file> <file alias="cylinderFull">meshes/cylinderFilledFlat.obj</file> @@ -26,18 +24,16 @@ <file alias="bevelbarSmoothFull">meshes/barFilledSmooth.obj</file> <file alias="barFull">meshes/cubeFilledFlat.obj</file> <file alias="barSmoothFull">meshes/cubeFilledSmooth.obj</file> - <file alias="dotSmooth">meshes/scatterdot.obj</file> - <file alias="dot">meshes/scatterdotFlat.obj</file> + <file alias="negativeBackground">meshes/backgroundNegatives.obj</file> + <file alias="minimal">meshes/minimalFlat.obj</file> + <file alias="minimalSmooth">meshes/minimalSmooth.obj</file> </qresource> <qresource prefix="/shaders"> <file alias="fragment">shaders/default.frag</file> <file alias="vertex">shaders/default.vert</file> - <file alias="fragmentAmbient">shaders/ambient.frag</file> <file alias="fragmentColorOnY">shaders/colorOnY.frag</file> - <file alias="fragmentSelection">shaders/selection.frag</file> - <file alias="vertexSelection">shaders/selection.vert</file> - <file alias="fragmentTexture">shaders/texture.frag</file> - <file alias="vertexTexture">shaders/texture.vert</file> + <file alias="fragmentPlainColor">shaders/plainColor.frag</file> + <file alias="vertexPlainColor">shaders/plainColor.vert</file> <file alias="fragmentLabel">shaders/label.frag</file> <file alias="vertexLabel">shaders/label.vert</file> <file alias="fragmentDepth">shaders/depth.frag</file> @@ -48,15 +44,13 @@ <file alias="fragmentShadowNoTexColorOnY">shaders/shadowNoTexColorOnY.frag</file> <file alias="fragmentColorOnYES2">shaders/colorOnY_ES2.frag</file> <file alias="fragmentES2">shaders/default_ES2.frag</file> - <file alias="vertexES2">shaders/default_ES2.vert</file> - <file alias="fragmentTextureES2">shaders/texture_ES2.frag</file> <file alias="fragmentSurface">shaders/surface.frag</file> - <file alias="vertexSurface">shaders/surface.vert</file> - <file alias="fragmentSurfaceGrid">shaders/surfaceGrid.frag</file> - <file alias="vertexSurfaceGrid">shaders/surfaceGrid.vert</file> <file alias="vertexSurfaceFlat">shaders/surfaceFlat.vert</file> <file alias="fragmentSurfaceFlat">shaders/surfaceFlat.frag</file> <file alias="fragmentSurfaceES2">shaders/surface_ES2.frag</file> + <file alias="vertexPointES2">shaders/point_ES2.vert</file> + <file alias="fragmentSurfaceShadowNoTex">shaders/surfaceShadowNoTex.frag</file> + <file alias="fragmentSurfaceShadowFlat">shaders/surfaceShadowFlat.frag</file> + <file alias="vertexSurfaceShadowFlat">shaders/surfaceShadowFlat.vert</file> </qresource> - <qresource prefix="/textures"/> </RCC> diff --git a/src/datavisualization/engine/meshes/backgroudNegatives.obj b/src/datavisualization/engine/meshes/backgroudNegatives.obj deleted file mode 100644 index 4153bfd4..00000000 --- a/src/datavisualization/engine/meshes/backgroudNegatives.obj +++ /dev/null @@ -1,33 +0,0 @@ -# Blender v2.66 (sub 0) OBJ File: 'backgroudNegatives.blend' -# www.blender.org -o Cube -v 1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 -1.000000 -v 1.000000 1.000000 1.000000 -v -1.000000 1.000000 1.000000 -v -1.000000 1.000000 -1.000000 -v 1.000000 -3.000000 1.000000 -v -1.000000 -3.000000 1.000000 -v -1.000000 -3.000000 -1.000000 -vt 0.000100 0.666700 -vt 0.333300 0.666700 -vt 0.333300 0.999900 -vt 0.000100 0.000100 -vt 0.333300 0.000100 -vt 0.333300 0.666500 -vt 0.666500 0.000100 -vt 0.666500 0.666500 -vt 0.000100 0.999900 -vt 0.000100 0.666500 -vn -0.000000 1.000000 0.000000 -vn 0.000000 -0.000000 -1.000000 -vn 1.000000 0.000000 0.000000 -s off -f 1/1/1 4/2/1 3/3/1 -f 8/4/2 9/5/2 6/6/2 -f 9/5/3 10/7/3 7/8/3 -f 2/9/1 1/1/1 3/3/1 -f 5/10/2 8/4/2 6/6/2 -f 6/6/3 9/5/3 7/8/3 diff --git a/src/datavisualization/engine/meshes/backgroudSmooth.obj b/src/datavisualization/engine/meshes/backgroudSmooth.obj deleted file mode 100644 index 27d06aea..00000000 --- a/src/datavisualization/engine/meshes/backgroudSmooth.obj +++ /dev/null @@ -1,36 +0,0 @@ -# Blender v2.66 (sub 0) OBJ File: 'backgroud.blend' -# www.blender.org -o Cube -v 1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 -1.000000 -v 1.000000 1.000000 1.000000 -v -1.000000 1.000000 1.000000 -v -1.000000 1.000000 -1.000000 -vt 0.000000 0.501529 -vt 0.001529 1.000000 -vt 0.500000 0.998471 -vt 0.501529 0.500000 -vt 1.000000 0.498471 -vt 0.998471 0.000000 -vt 0.500000 0.498471 -vt 0.498471 0.000000 -vt 0.000000 0.001529 -vt 0.498471 0.500000 -vt 0.500000 0.001529 -vt 0.001529 0.500000 -vn 0.000000 1.000000 0.000000 -vn 0.707083 0.707083 0.000000 -vn 0.577349 0.577349 -0.577349 -vn 0.000000 0.707083 -0.707083 -vn 0.707083 0.000000 -0.707083 -vn 1.000000 0.000000 0.000000 -vn 0.000000 0.000000 -1.000000 -s 1 -f 1/1/1 4/2/2 3/3/3 -f 2/4/4 3/5/3 6/6/5 -f 3/7/3 4/8/2 7/9/6 -f 2/10/4 1/1/1 3/3/3 -f 5/11/7 2/4/4 6/6/5 -f 6/12/5 3/7/3 7/9/6 diff --git a/src/datavisualization/engine/meshes/backgroudFlat.obj b/src/datavisualization/engine/meshes/background.obj index 5eeb1f26..5eeb1f26 100644 --- a/src/datavisualization/engine/meshes/backgroudFlat.obj +++ b/src/datavisualization/engine/meshes/background.obj diff --git a/src/datavisualization/engine/meshes/backgroundNegatives.obj b/src/datavisualization/engine/meshes/backgroundNegatives.obj new file mode 100644 index 00000000..76c7c1d8 --- /dev/null +++ b/src/datavisualization/engine/meshes/backgroundNegatives.obj @@ -0,0 +1,22 @@ +# Blender v2.66 (sub 0) OBJ File: 'backgroudNegativesWall.blend' +# www.blender.org +o Cube +v 0.999999 1.000000 1.000001 +v -1.000000 1.000000 1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -3.000000 1.000000 +v -1.000000 -3.000000 1.000000 +v -1.000000 -3.000000 -1.000000 +vt 0.000100 0.000100 +vt 0.500000 0.000100 +vt 0.500000 0.999900 +vt 0.999900 0.000100 +vt 0.999900 0.999900 +vt 0.000100 0.999900 +vn 0.000000 -0.000000 -1.000000 +vn 1.000000 0.000000 0.000000 +s off +f 4/1/1 5/2/1 2/3/1 +f 5/2/2 6/4/2 3/5/2 +f 1/6/1 4/1/1 2/3/1 +f 2/3/2 5/2/2 3/5/2 diff --git a/src/datavisualization/engine/meshes/minimalFlat.obj b/src/datavisualization/engine/meshes/minimalFlat.obj new file mode 100644 index 00000000..9ab60a21 --- /dev/null +++ b/src/datavisualization/engine/meshes/minimalFlat.obj @@ -0,0 +1,27 @@ +# Blender v2.66 (sub 0) OBJ File: 'scatterdot.blend' +# www.blender.org +o Cone +v 0.000000 -1.000000 -1.414000 +v 1.224560 -1.000000 0.707000 +v 0.000000 1.000000 0.000000 +v -1.224560 -1.000000 0.707000 +vt 0.499966 0.866025 +vt 0.000000 1.153999 +vt 0.000590 0.577029 +vt 0.999376 0.000000 +vt 1.000000 0.577029 +vt 0.500000 0.288996 +vt 1.000000 0.866025 +vt 0.500000 1.154058 +vt 0.500624 0.577029 +vt 0.000000 0.577029 +vt 0.000624 0.000000 +vn -0.000000 -1.000000 0.000000 +vn 0.816510 0.333289 -0.471412 +vn -0.000000 0.333289 0.942825 +vn -0.816510 0.333289 -0.471413 +s off +f 4/1/1 1/2/1 2/3/1 +f 1/4/2 3/5/2 2/6/2 +f 2/7/3 3/8/3 4/9/3 +f 4/6/4 3/10/4 1/11/4 diff --git a/src/datavisualization/engine/meshes/minimalSmooth.obj b/src/datavisualization/engine/meshes/minimalSmooth.obj new file mode 100644 index 00000000..dd2e77d3 --- /dev/null +++ b/src/datavisualization/engine/meshes/minimalSmooth.obj @@ -0,0 +1,27 @@ +# Blender v2.66 (sub 0) OBJ File: 'scatterdot.blend' +# www.blender.org +o Cone +v 0.000000 -1.000000 -1.414000 +v 1.224560 -1.000000 0.707000 +v 0.000000 1.000000 0.000000 +v -1.224560 -1.000000 0.707000 +vt 0.499966 0.866025 +vt 0.000000 1.153999 +vt 0.000590 0.577029 +vt 0.999376 0.000000 +vt 1.000000 0.577029 +vt 0.500000 0.288996 +vt 1.000000 0.866025 +vt 0.500000 1.154058 +vt 0.500624 0.577029 +vt 0.000000 0.577029 +vt 0.000624 0.000000 +vn -0.816462 -0.333354 0.471389 +vn 0.000000 -0.333354 -0.942778 +vn 0.816462 -0.333354 0.471389 +vn 0.000000 1.000000 0.000000 +s 1 +f 4/1/1 1/2/2 2/3/3 +f 1/4/2 3/5/4 2/6/3 +f 2/7/3 3/8/4 4/9/1 +f 4/6/1 3/10/4 1/11/2 diff --git a/src/datavisualization/engine/meshes/scatterdot.obj b/src/datavisualization/engine/meshes/scatterdot.obj deleted file mode 100644 index d994a80f..00000000 --- a/src/datavisualization/engine/meshes/scatterdot.obj +++ /dev/null @@ -1,28 +0,0 @@ -# Blender v2.66 (sub 0) OBJ File: 'scatterdot.blend' -# www.blender.org -o Cone -v 0.000000 -0.500000 -1.000000 -v 0.866025 -0.500000 0.500000 -v 0.000000 0.500000 0.000000 -v -0.866025 -0.500000 0.500000 -vt 0.999727 0.000000 -vt 1.000000 0.492691 -vt 0.522886 0.369782 -vt 0.477114 0.973202 -vt 0.000000 1.096111 -vt 0.000273 0.603420 -vt 0.523159 0.985382 -vt 0.522886 0.492691 -vt 1.000000 0.615600 -vt 0.000000 0.603420 -vt 0.000617 0.000000 -vt 0.522886 0.302245 -vn -0.833033 -0.273293 0.480941 -vn 0.000000 0.999969 0.000000 -vn 0.000000 -0.273293 -0.961913 -vn 0.833033 -0.273293 0.480941 -s 1 -f 4/1/1 3/2/2 1/3/3 -f 1/4/3 3/5/2 2/6/4 -f 2/7/4 3/8/2 4/9/1 -f 1/10/3 2/11/4 4/12/1 diff --git a/src/datavisualization/engine/meshes/scatterdotFlat.obj b/src/datavisualization/engine/meshes/scatterdotFlat.obj deleted file mode 100644 index 4052738d..00000000 --- a/src/datavisualization/engine/meshes/scatterdotFlat.obj +++ /dev/null @@ -1,28 +0,0 @@ -# Blender v2.66 (sub 0) OBJ File: 'scatterdot.blend' -# www.blender.org -o Cone -v 0.000000 -0.500000 -1.000000 -v 0.866025 -0.500000 0.500000 -v 0.000000 0.500000 0.000000 -v -0.866025 -0.500000 0.500000 -vt 0.999727 0.000000 -vt 1.000000 0.492691 -vt 0.522886 0.369782 -vt 0.477114 0.973202 -vt 0.000000 1.096111 -vt 0.000273 0.603420 -vt 0.523159 0.985382 -vt 0.522886 0.492691 -vt 1.000000 0.615600 -vt 0.000000 0.603420 -vt 0.000617 0.000000 -vt 0.522886 0.302245 -vn -0.774597 0.447214 -0.447214 -vn 0.774597 0.447214 -0.447214 -vn -0.000000 0.447214 0.894427 -vn 0.000000 -1.000000 -0.000000 -s off -f 4/1/1 3/2/1 1/3/1 -f 1/4/2 3/5/2 2/6/2 -f 2/7/3 3/8/3 4/9/3 -f 1/10/4 2/11/4 4/12/4 diff --git a/src/datavisualization/engine/q3dbars.cpp b/src/datavisualization/engine/q3dbars.cpp index d6b48e03..d4e97519 100644 --- a/src/datavisualization/engine/q3dbars.cpp +++ b/src/datavisualization/engine/q3dbars.cpp @@ -21,12 +21,11 @@ #include "bars3dcontroller_p.h" #include "q3dvalueaxis.h" #include "q3dcategoryaxis.h" -#include "qbardataproxy.h" #include "q3dcamera.h" +#include "qbar3dseries_p.h" #include <QMouseEvent> -#include <QDebug> QT_DATAVISUALIZATION_BEGIN_NAMESPACE @@ -46,28 +45,30 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * These default axes can be modified via axis accessors, but as soon any axis is set explicitly * for the orientation, the default axis for that orientation is destroyed. * - * Data proxies work similarly: If no data proxy is set explicitly, Q3DBars creates a default - * proxy. If any other proxy is set as active data proxy later, the default proxy and all data - * added to it is destroyed. + * Q3DBars supports more than one series visible at the same time. It is not necessary for all series + * to have the same amount of rows and columns. + * Row and column labels are taken from the first added series, unless explicitly defined to + * row and column axes. * - * Methods are provided for changing bar types, themes, bar selection modes and so on. See the + * Methods are provided for changing themes, bar selection modes and so on. See the * methods for more detailed descriptions. * * \section1 How to construct a minimal Q3DBars graph * - * First, construct an instance of Q3DBars: + * First, construct an instance of Q3DBars. Since we are running the graph as top level window + * in this example, we need to clear the \c Qt::FramelessWindowHint flag, which gets set by + * default: * * \snippet doc_src_q3dbars_construction.cpp 4 * * After constructing Q3DBars, you can set the data window by changing the range on the row and * column axes. It is not mandatory, as data window will default to showing all of the data in - * the data proxy. If the amount of data is large, it is usually preferable to show just a + * the series. If the amount of data is large, it is usually preferable to show just a * portion of it. For the example, let's set the data window to show first five rows and columns: * * \snippet doc_src_q3dbars_construction.cpp 0 * - * Now Q3DBars is ready to receive data to be rendered. Add one row of 5 qreals into the data - * set: + * Now Q3DBars is ready to receive data to be rendered. Create a series with one row of 5 values: * * \snippet doc_src_q3dbars_construction.cpp 1 * @@ -95,99 +96,119 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE */ /*! - * Constructs a new 3D bar window. + * Constructs a new 3D bar graph with optional \a parent window. */ -Q3DBars::Q3DBars() - : d_ptr(new Q3DBarsPrivate(this, geometry())) +Q3DBars::Q3DBars(QWindow *parent) + : Q3DWindow(new Q3DBarsPrivate(this), parent) { - setVisualController(d_ptr->m_shared); - d_ptr->m_shared->initializeOpenGL(); - QObject::connect(d_ptr->m_shared, &Bars3DController::selectedBarPosChanged, this, - &Q3DBars::selectedBarPosChanged); - QObject::connect(d_ptr->m_shared, &Abstract3DController::needRender, this, - &Q3DWindow::renderLater); + dptr()->m_shared = new Bars3DController(geometry()); + d_ptr->setVisualController(dptr()->m_shared); + dptr()->m_shared->initializeOpenGL(); + QObject::connect(dptr()->m_shared, &Abstract3DController::selectionModeChanged, this, + &Q3DBars::selectionModeChanged); + QObject::connect(dptr()->m_shared, &Abstract3DController::shadowQualityChanged, this, + &Q3DBars::shadowQualityChanged); + QObject::connect(dptr()->m_shared, &Abstract3DController::themeChanged, this, + &Q3DBars::themeChanged); + QObject::connect(dptr()->m_shared, &Abstract3DController::needRender, d_ptr.data(), + &Q3DWindowPrivate::renderLater); + QObject::connect(dptr()->m_shared, &Abstract3DController::shadowQualityChanged, dptr(), + &Q3DBarsPrivate::handleShadowQualityUpdate); } /*! - * Destroys the 3D bar window. + * Destroys the 3D bar graph. */ Q3DBars::~Q3DBars() { } /*! - * \internal + * Adds the \a series to the graph. A graph can contain multiple series, but only one set of axes, + * so the rows and columns of all series must match for the visualized data to be meaningful. + * If the graph has multiple visible series, only the first one added will + * generate the row or column labels on the axes in cases where the labels are not explicitly set + * to the axes. If the newly added series has specified a selected bar, it will be highlighted and + * any existing selection will be cleared. Only one added series can have an active selection. */ -void Q3DBars::mouseDoubleClickEvent(QMouseEvent *event) +void Q3DBars::addSeries(QBar3DSeries *series) { - d_ptr->m_shared->mouseDoubleClickEvent(event); + dptr()->m_shared->addSeries(series); } /*! - * \internal + * Removes the \a series from the graph. */ -void Q3DBars::touchEvent(QTouchEvent *event) +void Q3DBars::removeSeries(QBar3DSeries *series) { - d_ptr->m_shared->touchEvent(event); + dptr()->m_shared->removeSeries(series); } /*! - * \internal + * \return list of series added to this graph. */ -void Q3DBars::mousePressEvent(QMouseEvent *event) +QList<QBar3DSeries *> Q3DBars::seriesList() { - d_ptr->m_shared->mousePressEvent(event, event->pos()); + return dptr()->m_shared->barSeriesList(); } /*! * \internal */ -void Q3DBars::mouseReleaseEvent(QMouseEvent *event) +void Q3DBars::mouseDoubleClickEvent(QMouseEvent *event) { - d_ptr->m_shared->mouseReleaseEvent(event, event->pos()); + dptr()->m_shared->mouseDoubleClickEvent(event); } /*! * \internal */ -void Q3DBars::mouseMoveEvent(QMouseEvent *event) +void Q3DBars::touchEvent(QTouchEvent *event) { - d_ptr->m_shared->mouseMoveEvent(event, event->pos()); + dptr()->m_shared->touchEvent(event); } /*! * \internal */ -void Q3DBars::wheelEvent(QWheelEvent *event) +void Q3DBars::mousePressEvent(QMouseEvent *event) { - d_ptr->m_shared->wheelEvent(event); + dptr()->m_shared->mousePressEvent(event, event->pos()); } /*! * \internal */ -void Q3DBars::resizeEvent(QResizeEvent *event) +void Q3DBars::mouseReleaseEvent(QMouseEvent *event) { - Q_UNUSED(event); - d_ptr->m_shared->setSize(width(), height()); + dptr()->m_shared->mouseReleaseEvent(event, event->pos()); } /*! - * Sets window \a width. + * \internal */ -void Q3DBars::setWidth(const int width) +void Q3DBars::mouseMoveEvent(QMouseEvent *event) { - d_ptr->m_shared->setWidth(width); - QWindow::setWidth(width); + dptr()->m_shared->mouseMoveEvent(event, event->pos()); } /*! - * Sets window \a height. + * \internal */ -void Q3DBars::setHeight(const int height) +void Q3DBars::wheelEvent(QWheelEvent *event) +{ + dptr()->m_shared->wheelEvent(event); +} + + +Q3DBarsPrivate *Q3DBars::dptr() { - d_ptr->m_shared->setHeight(height); - QWindow::setHeight(height); + return static_cast<Q3DBarsPrivate *>(d_ptr.data()); +} + +const Q3DBarsPrivate *Q3DBars::dptrc() const +{ + return static_cast<const Q3DBarsPrivate *>(d_ptr.data()); } /*! @@ -196,14 +217,17 @@ void Q3DBars::setHeight(const int height) * Bar thickness ratio between X and Z dimensions. 1.0 means bars are as wide as they are deep, 0.5 * makes them twice as deep as they are wide. It is preset to \c 1.0 by default. */ -void Q3DBars::setBarThickness(qreal thicknessRatio) +void Q3DBars::setBarThickness(float thicknessRatio) { - d_ptr->m_shared->setBarSpecs(GLfloat(thicknessRatio), barSpacing(), isBarSpacingRelative()); + if (thicknessRatio != barThickness()) { + dptr()->m_shared->setBarSpecs(GLfloat(thicknessRatio), barSpacing(), isBarSpacingRelative()); + emit barThicknessChanged(thicknessRatio); + } } -qreal Q3DBars::barThickness() +float Q3DBars::barThickness() { - return d_ptr->m_shared->barThickness(); + return dptr()->m_shared->barThickness(); } /*! @@ -216,12 +240,15 @@ qreal Q3DBars::barThickness() */ void Q3DBars::setBarSpacing(QSizeF spacing) { - d_ptr->m_shared->setBarSpecs(GLfloat(barThickness()), spacing, isBarSpacingRelative()); + if (spacing != barSpacing()) { + dptr()->m_shared->setBarSpecs(GLfloat(barThickness()), spacing, isBarSpacingRelative()); + emit barSpacingChanged(spacing); + } } QSizeF Q3DBars::barSpacing() { - return d_ptr->m_shared->barSpacing(); + return dptr()->m_shared->barSpacing(); } /*! @@ -233,112 +260,48 @@ QSizeF Q3DBars::barSpacing() */ void Q3DBars::setBarSpacingRelative(bool relative) { - d_ptr->m_shared->setBarSpecs(GLfloat(barThickness()), barSpacing(), relative); + if (relative != isBarSpacingRelative()) { + dptr()->m_shared->setBarSpecs(GLfloat(barThickness()), barSpacing(), relative); + emit barSpacingRelativeChanged(relative); + } } bool Q3DBars::isBarSpacingRelative() { - return d_ptr->m_shared->isBarSpecRelative(); + return dptr()->m_shared->isBarSpecRelative(); } /*! - * Sets the bar \a style to one of the values in \c QDataVis::MeshStyle. It is preset to - * \c QDataVis::MeshStyleBars by default. A \a smooth flag can be used to set shading to smooth. - * It is \c false by default. + * \property Q3DBars::theme * - * \sa setMeshFileName() + * A \a theme to be used for the graph. Ownership of the \a theme is transferred. Previous theme + * is deleted when a new one is set. Properties of the \a theme can be modified even after setting + * it, and the modifications take effect immediately. */ -void Q3DBars::setBarType(QDataVis::MeshStyle style, bool smooth) +void Q3DBars::setTheme(Q3DTheme *theme) { - d_ptr->m_shared->setBarType(style, smooth); + dptr()->m_shared->setTheme(theme); } -/*! - * Sets a predefined \a theme from \c QDataVis::Theme. It is preset to \c QDataVis::ThemeQt by - * default. Theme affects bar colors, label colors, text color, background color, window color and - * grid color. Lighting is also adjusted by themes. - * - * \sa setBarColor() - * - * \preliminary - */ -void Q3DBars::setTheme(QDataVis::Theme theme) +Q3DTheme *Q3DBars::theme() const { - d_ptr->m_shared->setTheme(theme); -} - -/*! - * Set bar color using your own color. \a baseColor sets the base color of a bar. The \a uniform - * -flag is used to define if color needs to be uniform throughout bar's length, or will the colors - * be applied by height, starting with dark at the bottom. It is \c true by default. - * - * Calling this method overrides colors from theme. - * - * \sa setTheme() - * - * \preliminary - */ -void Q3DBars::setBarColor(const QColor &baseColor, bool uniform) -{ - d_ptr->m_shared->setObjectColor(baseColor, uniform); -} - -/*! - * \return bar color in use. - */ -QColor Q3DBars::barColor() const -{ - return d_ptr->m_shared->objectColor(); + return dptrc()->m_shared->theme(); } /*! * \property Q3DBars::selectionMode * - * Sets bar selection \a mode to one of \c QDataVis::SelectionMode. It is preset to - * \c QDataVis::SelectionModeItem by default. + * Sets bar selection \a mode to a combination of \c QDataVis::SelectionFlags. It is preset to + * \c QDataVis::SelectionItem by default. */ -void Q3DBars::setSelectionMode(QDataVis::SelectionMode mode) +void Q3DBars::setSelectionMode(QDataVis::SelectionFlags mode) { - d_ptr->m_shared->setSelectionMode(mode); + dptr()->m_shared->setSelectionMode(mode); } -QDataVis::SelectionMode Q3DBars::selectionMode() const +QDataVis::SelectionFlags Q3DBars::selectionMode() const { - return d_ptr->m_shared->selectionMode(); -} - -/*! - * \property Q3DBars::meshFileName - * - * Override bar type with a mesh object located in \a objFileName. - * \note Object needs to be in Wavefront obj format and include vertices, normals and UVs. - * It also needs to be in triangles. - * - * \sa setBarType() - */ -void Q3DBars::setMeshFileName(const QString &objFileName) -{ - d_ptr->m_shared->setMeshFileName(objFileName); -} - -QString Q3DBars::meshFileName() const -{ - return d_ptr->m_shared->meshFileName(); -} - -/*! - * \property Q3DBars::font - * - * Sets the \a font for labels. It is preset to \c Arial by default. - */ -void Q3DBars::setFont(const QFont &font) -{ - d_ptr->m_shared->setFont(font); -} - -QFont Q3DBars::font() const -{ - return d_ptr->m_shared->font(); + return dptrc()->m_shared->selectionMode(); } /*! @@ -348,70 +311,7 @@ QFont Q3DBars::font() const */ Q3DScene *Q3DBars::scene() const { - return d_ptr->m_shared->scene(); -} - -/*! - * \property Q3DBars::labelStyle - * - * Sets label \a style to one of \c QDataVis::LabelStyle. It is preset to - * \c QDataVis::LabelStyleFromTheme by default. - */ -void Q3DBars::setLabelStyle(QDataVis::LabelStyle style) -{ - d_ptr->m_shared->setLabelStyle(style); -} - -QDataVis::LabelStyle Q3DBars::labelStyle() const -{ - return d_ptr->m_shared->labelStyle(); -} - -/*! - * \property Q3DBars::gridVisible - * - * Sets grid visibility to \a visible. It is preset to \c true by default. - */ -void Q3DBars::setGridVisible(bool visible) -{ - d_ptr->m_shared->setGridEnabled(visible); -} - -bool Q3DBars::isGridVisible() const -{ - return d_ptr->m_shared->gridEnabled(); -} - -/*! - * \property Q3DBars::backgroundVisible - * - * Sets background visibility to \a visible. It is preset to \c true by default. - */ -void Q3DBars::setBackgroundVisible(bool visible) -{ - d_ptr->m_shared->setBackgroundEnabled(visible); -} - -bool Q3DBars::isBackgroundVisible() const -{ - return d_ptr->m_shared->backgroundEnabled(); -} - -/*! - * \property Q3DBars::selectedBarPos - * - * Selects a bar in a \a position. The position is the position in data window. - * Only one bar can be selected at a time. - * To clear selection, specify an illegal \a position, e.g. (-1, -1). - */ -void Q3DBars::setSelectedBarPos(const QPoint &position) -{ - d_ptr->m_shared->setSelectedBarPos(position); -} - -QPoint Q3DBars::selectedBarPos() const -{ - return d_ptr->m_shared->selectedBarPos(); + return dptrc()->m_shared->scene(); } /*! @@ -426,12 +326,12 @@ QPoint Q3DBars::selectedBarPos() const */ void Q3DBars::setShadowQuality(QDataVis::ShadowQuality quality) { - d_ptr->m_shared->setShadowQuality(quality); + dptr()->m_shared->setShadowQuality(quality); } QDataVis::ShadowQuality Q3DBars::shadowQuality() const { - return d_ptr->m_shared->shadowQuality(); + return dptrc()->m_shared->shadowQuality(); } /*! @@ -445,7 +345,7 @@ QDataVis::ShadowQuality Q3DBars::shadowQuality() const */ void Q3DBars::setRowAxis(Q3DCategoryAxis *axis) { - d_ptr->m_shared->setAxisX(axis); + dptr()->m_shared->setAxisZ(axis); } /*! @@ -453,7 +353,7 @@ void Q3DBars::setRowAxis(Q3DCategoryAxis *axis) */ Q3DCategoryAxis *Q3DBars::rowAxis() const { - return static_cast<Q3DCategoryAxis *>(d_ptr->m_shared->axisX()); + return static_cast<Q3DCategoryAxis *>(dptrc()->m_shared->axisZ()); } /*! @@ -467,7 +367,7 @@ Q3DCategoryAxis *Q3DBars::rowAxis() const */ void Q3DBars::setColumnAxis(Q3DCategoryAxis *axis) { - d_ptr->m_shared->setAxisZ(axis); + dptr()->m_shared->setAxisX(axis); } /*! @@ -475,7 +375,7 @@ void Q3DBars::setColumnAxis(Q3DCategoryAxis *axis) */ Q3DCategoryAxis *Q3DBars::columnAxis() const { - return static_cast<Q3DCategoryAxis *>(d_ptr->m_shared->axisZ()); + return static_cast<Q3DCategoryAxis *>(dptrc()->m_shared->axisX()); } /*! @@ -490,7 +390,7 @@ Q3DCategoryAxis *Q3DBars::columnAxis() const */ void Q3DBars::setValueAxis(Q3DValueAxis *axis) { - d_ptr->m_shared->setAxisY(axis); + dptr()->m_shared->setAxisY(axis); } /*! @@ -498,7 +398,7 @@ void Q3DBars::setValueAxis(Q3DValueAxis *axis) */ Q3DValueAxis *Q3DBars::valueAxis() const { - return static_cast<Q3DValueAxis *>(d_ptr->m_shared->axisY()); + return static_cast<Q3DValueAxis *>(dptrc()->m_shared->axisY()); } /*! @@ -510,7 +410,7 @@ Q3DValueAxis *Q3DBars::valueAxis() const */ void Q3DBars::addAxis(Q3DAbstractAxis *axis) { - d_ptr->m_shared->addAxis(axis); + dptr()->m_shared->addAxis(axis); } /*! @@ -523,7 +423,7 @@ void Q3DBars::addAxis(Q3DAbstractAxis *axis) */ void Q3DBars::releaseAxis(Q3DAbstractAxis *axis) { - d_ptr->m_shared->releaseAxis(axis); + dptr()->m_shared->releaseAxis(axis); } /*! @@ -533,77 +433,12 @@ void Q3DBars::releaseAxis(Q3DAbstractAxis *axis) */ QList<Q3DAbstractAxis *> Q3DBars::axes() const { - return d_ptr->m_shared->axes(); -} - -/*! - * Sets the active data \a proxy. Implicitly calls addDataProxy() to transfer ownership of - * the \a proxy to this graph. - * - * If the \a proxy is null, a temporary default proxy is created and activated. - * This temporary proxy is destroyed if another \a proxy is set explicitly active via this method. - * - * \sa addDataProxy(), releaseDataProxy() - */ -void Q3DBars::setActiveDataProxy(QBarDataProxy *proxy) -{ - d_ptr->m_shared->setActiveDataProxy(proxy); + return dptrc()->m_shared->axes(); } -/*! - * \return active data proxy. - */ -QBarDataProxy *Q3DBars::activeDataProxy() const +Q3DBarsPrivate::Q3DBarsPrivate(Q3DBars *q) + : Q3DWindowPrivate(q) { - return static_cast<QBarDataProxy *>(d_ptr->m_shared->activeDataProxy()); -} - -/*! - * Adds data \a proxy to the graph. The proxies added via addDataProxy are not yet taken to use, - * addDataProxy is simply used to give the ownership of the data \a proxy to the graph. - * The \a proxy must not be null or added to another graph. - * - * \sa releaseDataProxy(), setActiveDataProxy() - */ -void Q3DBars::addDataProxy(QBarDataProxy *proxy) -{ - d_ptr->m_shared->addDataProxy(proxy); -} - -/*! - * Releases the ownership of the data \a proxy back to the caller, if it is added to this graph. - * If the released \a proxy is in use, a new empty default proxy is created and taken to use. - * - * If the default \a proxy is released and added back later, it behaves as any other proxy would. - * - * \sa addDataProxy(), setActiveDataProxy() - */ -void Q3DBars::releaseDataProxy(QBarDataProxy *proxy) -{ - d_ptr->m_shared->releaseDataProxy(proxy); -} - -/*! - * \return list of all added data proxies. - * - * \sa addDataProxy() - */ -QList<QBarDataProxy *> Q3DBars::dataProxies() const -{ - QList<QBarDataProxy *> retList; - QList<QAbstractDataProxy *> abstractList = d_ptr->m_shared->dataProxies(); - foreach (QAbstractDataProxy *proxy, abstractList) - retList.append(static_cast<QBarDataProxy *>(proxy)); - - return retList; -} - -Q3DBarsPrivate::Q3DBarsPrivate(Q3DBars *q, QRect rect) - : q_ptr(q), - m_shared(new Bars3DController(rect)) -{ - QObject::connect(m_shared, &Abstract3DController::shadowQualityChanged, this, - &Q3DBarsPrivate::handleShadowQualityUpdate); } Q3DBarsPrivate::~Q3DBarsPrivate() @@ -613,7 +448,12 @@ Q3DBarsPrivate::~Q3DBarsPrivate() void Q3DBarsPrivate::handleShadowQualityUpdate(QDataVis::ShadowQuality quality) { - emit q_ptr->shadowQualityChanged(quality); + emit qptr()->shadowQualityChanged(quality); +} + +Q3DBars *Q3DBarsPrivate::qptr() +{ + return static_cast<Q3DBars *>(q_ptr); } QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/q3dbars.h b/src/datavisualization/engine/q3dbars.h index d0ddf3fb..add3b11b 100644 --- a/src/datavisualization/engine/q3dbars.h +++ b/src/datavisualization/engine/q3dbars.h @@ -21,7 +21,9 @@ #include <QtDataVisualization/qdatavisualizationenums.h> #include <QtDataVisualization/q3dwindow.h> +#include <QtDataVisualization/q3dtheme.h> #include <QFont> +#include <QLinearGradient> QT_DATAVISUALIZATION_BEGIN_NAMESPACE @@ -29,40 +31,33 @@ class Q3DBarsPrivate; class Q3DAbstractAxis; class Q3DCategoryAxis; class Q3DValueAxis; -class QBarDataProxy; class Q3DScene; +class QBar3DSeries; class QT_DATAVISUALIZATION_EXPORT Q3DBars : public Q3DWindow { Q_OBJECT - Q_PROPERTY(QtDataVisualization::QDataVis::SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) - Q_PROPERTY(QtDataVisualization::QDataVis::LabelStyle labelStyle READ labelStyle WRITE setLabelStyle) + Q_PROPERTY(QtDataVisualization::QDataVis::SelectionFlags selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged) Q_PROPERTY(QtDataVisualization::QDataVis::ShadowQuality shadowQuality READ shadowQuality WRITE setShadowQuality NOTIFY shadowQualityChanged) - Q_PROPERTY(qreal barThickness READ barThickness WRITE setBarThickness) - Q_PROPERTY(QSizeF barSpacing READ barSpacing WRITE setBarSpacing) - Q_PROPERTY(bool barSpacingRelative READ isBarSpacingRelative WRITE setBarSpacingRelative) - Q_PROPERTY(QString meshFileName READ meshFileName WRITE setMeshFileName) - Q_PROPERTY(QFont font READ font WRITE setFont) - Q_PROPERTY(bool gridVisible READ isGridVisible WRITE setGridVisible) - Q_PROPERTY(bool backgroundVisible READ isBackgroundVisible WRITE setBackgroundVisible) - Q_PROPERTY(QPoint selectedBarPos READ selectedBarPos WRITE setSelectedBarPos NOTIFY selectedBarPosChanged) + Q_PROPERTY(float barThickness READ barThickness WRITE setBarThickness NOTIFY barThicknessChanged) + Q_PROPERTY(QSizeF barSpacing READ barSpacing WRITE setBarSpacing NOTIFY barSpacingChanged) + Q_PROPERTY(bool barSpacingRelative READ isBarSpacingRelative WRITE setBarSpacingRelative NOTIFY barSpacingRelativeChanged) + Q_PROPERTY(Q3DTheme* theme READ theme WRITE setTheme NOTIFY themeChanged) Q_PROPERTY(Q3DScene* scene READ scene) - Q_ENUMS(QtDataVisualization::QDataVis::SelectionMode) - Q_ENUMS(QtDataVisualization::QDataVis::ShadowQuality) - Q_ENUMS(QtDataVisualization::QDataVis::LabelStyle) - Q_ENUMS(QtDataVisualization::QDataVis::CameraPreset) - public: - explicit Q3DBars(); - ~Q3DBars(); + explicit Q3DBars(QWindow *parent = 0); + virtual ~Q3DBars(); - void setBarType(QDataVis::MeshStyle style, bool smooth = false); + void addSeries(QBar3DSeries *series); + void removeSeries(QBar3DSeries *series); + QList<QBar3DSeries *> seriesList(); - void setTheme(QDataVis::Theme theme); + void setTheme(Q3DTheme *theme); + Q3DTheme *theme() const; - void setBarThickness(qreal thicknessRatio); - qreal barThickness(); + void setBarThickness(float thicknessRatio); + float barThickness(); void setBarSpacing(QSizeF spacing); QSizeF barSpacing(); @@ -70,35 +65,11 @@ public: void setBarSpacingRelative(bool relative); bool isBarSpacingRelative(); - void setBarColor(const QColor &baseColor, bool uniform = true); - QColor barColor() const; - - void setMeshFileName(const QString &objFileName); - QString meshFileName() const; - - void setSelectionMode(QDataVis::SelectionMode mode); - QDataVis::SelectionMode selectionMode() const; - - void setFont(const QFont &font); - QFont font() const; + void setSelectionMode(QDataVis::SelectionFlags mode); + QDataVis::SelectionFlags selectionMode() const; Q3DScene *scene() const; - void setLabelStyle(QDataVis::LabelStyle style); - QDataVis::LabelStyle labelStyle() const; - - void setGridVisible(bool visible); - bool isGridVisible() const; - - void setWidth(const int width); - void setHeight(const int height); - - void setBackgroundVisible(bool visible); - bool isBackgroundVisible() const; - - void setSelectedBarPos(const QPoint &position); - QPoint selectedBarPos() const; - void setShadowQuality(QDataVis::ShadowQuality quality); QDataVis::ShadowQuality shadowQuality() const; @@ -112,15 +83,13 @@ public: void releaseAxis(Q3DAbstractAxis *axis); QList<Q3DAbstractAxis *> axes() const; - void setActiveDataProxy(QBarDataProxy *proxy); - QBarDataProxy *activeDataProxy() const; - void addDataProxy(QBarDataProxy *proxy); - void releaseDataProxy(QBarDataProxy *proxy); - QList<QBarDataProxy *> dataProxies() const; - signals: + void selectionModeChanged(QDataVis::SelectionFlags mode); void shadowQualityChanged(QDataVis::ShadowQuality quality); - void selectedBarPosChanged(QPoint position); + void barThicknessChanged(float thicknessRatio); + void barSpacingChanged(QSizeF spacing); + void barSpacingRelativeChanged(bool relative); + void themeChanged(Q3DTheme *theme); protected: @@ -130,10 +99,10 @@ protected: void mouseReleaseEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void wheelEvent(QWheelEvent *event); - void resizeEvent(QResizeEvent *event); private: - QScopedPointer<Q3DBarsPrivate> d_ptr; + Q3DBarsPrivate *dptr(); + const Q3DBarsPrivate *dptrc() const; Q_DISABLE_COPY(Q3DBars) }; diff --git a/src/datavisualization/engine/q3dbars_p.h b/src/datavisualization/engine/q3dbars_p.h index 653db606..9a059d8d 100644 --- a/src/datavisualization/engine/q3dbars_p.h +++ b/src/datavisualization/engine/q3dbars_p.h @@ -26,26 +26,29 @@ // // We mean it. -#ifndef Q3DBARS_p_H -#define Q3DBARS_p_H +#ifndef Q3DBARS_P_H +#define Q3DBARS_P_H #include "bars3dcontroller_p.h" #include "qdatavisualizationenums.h" +#include "q3dwindow_p.h" QT_DATAVISUALIZATION_BEGIN_NAMESPACE class Q3DBars; -class Q3DBarsPrivate : public QObject +class Q3DBarsPrivate : public Q3DWindowPrivate { + Q_OBJECT public: - Q3DBarsPrivate(Q3DBars *q, QRect rect); + Q3DBarsPrivate(Q3DBars *q); ~Q3DBarsPrivate(); // Used to detect when shadow quality changes autonomously due to e.g. resizing. void handleShadowQualityUpdate(QDataVis::ShadowQuality quality); - Q3DBars *q_ptr; + Q3DBars *qptr(); + Bars3DController *m_shared; }; diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp index fe452739..6378051c 100644 --- a/src/datavisualization/engine/q3dcamera.cpp +++ b/src/datavisualization/engine/q3dcamera.cpp @@ -29,19 +29,192 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE /*! - \class Q3DCamera - \inmodule QtDataVisualization - \brief Representation of a camera in 3D space. - \since Qt Data Visualization 1.0 + * \class Q3DCamera + * \inmodule QtDataVisualization + * \brief Representation of a camera in 3D space. + * \since Qt Data Visualization 1.0 + * + * Q3DCamera represents a basic orbit around centerpoint 3D camera that is used when rendering the + * data visualization. The class offers simple methods for setting the orbit point in rotations, + * but allows also setting the 4x4 view matrix directly in case a more customized camera behavior is + * needed. + */ + +/*! + * \enum Q3DCamera::CameraPreset + * + * Predefined positions for camera. + * + * \value CameraPresetNone + * Used to indicate a preset has not been set, or the scene has been rotated freely. + * \value CameraPresetFrontLow + * \value CameraPresetFront + * \value CameraPresetFrontHigh + * \value CameraPresetLeftLow + * \value CameraPresetLeft + * \value CameraPresetLeftHigh + * \value CameraPresetRightLow + * \value CameraPresetRight + * \value CameraPresetRightHigh + * \value CameraPresetBehindLow + * \value CameraPresetBehind + * \value CameraPresetBehindHigh + * \value CameraPresetIsometricLeft + * \value CameraPresetIsometricLeftHigh + * \value CameraPresetIsometricRight + * \value CameraPresetIsometricRightHigh + * \value CameraPresetDirectlyAbove + * \value CameraPresetDirectlyAboveCW45 + * \value CameraPresetDirectlyAboveCCW45 + * \value CameraPresetFrontBelow + * In Q3DBars from CameraPresetFrontBelow onward these only work for graphs including negative + * values. They act as Preset...Low for positive-only values. + * \value CameraPresetLeftBelow + * \value CameraPresetRightBelow + * \value CameraPresetBehindBelow + * \value CameraPresetDirectlyBelow + * Acts as CameraPresetFrontLow for positive-only bars. + */ + +/*! + * \qmltype Camera3D + * \inqmlmodule QtDataVisualization + * \since QtDataVisualization 1.0 + * \ingroup datavisualization_qml + * \instantiates Q3DCamera + * \brief Representation of a camera in 3D space. + * + * Camera3D represents a basic orbit around centerpoint 3D camera that is used when rendering the + * data visualization. The class offers simple methods for setting the orbit point in rotations, + * but allows also setting the 4x4 view matrix directly in case a more customized camera behavior is + * needed. + */ + +/*! + * \qmlproperty float Camera3D::xRotation + * + * This property contains the X-rotation angle of the camera around the target point in degrees + * starting from the current base position set by the setBaseOrientation method. + */ - Q3DCamera represents a basic orbit around centerpoint 3D camera that is used when rendering the data visualization. - The class offers simple methods for setting the orbit point in rotations, but allows also setting the 4x4 viewmatrix - directly in case a more customized camera behavior is needed. -*/ +/*! + * \qmlproperty float Camera3D::yRotation + * + * This property contains the Y-rotation angle of the camera around the target point in degrees + * starting from the current base position set by the setBaseOrientation method. + */ /*! - * Constructs a new 3D camera with position set to origo, up direction facing towards the Y-axis and looking at origo by default. An - * optional \a parent parameter can be given and is then passed to QObject constructor. + * \qmlproperty float Camera3D::minXRotation + * + * This property contains the current minimum X-rotation for the camera. + * The full circle range is \c{[-180, 180]} and the minimum value is limited to \c -180. + * Also the value can't be higher than the maximum, and is adjusted if necessary. + * + * \sa wrapXRotation, maxXRotation + */ + +/*! + * \qmlproperty float Camera3D::minYRotation + * + * This property contains the current minimum Y-rotation for the camera. + * The full Y angle range is \c{[-90, 90]} and the minimum value is limited to \c -90. + * Also the value can't be higher than the maximum, and is adjusted if necessary. + * + * \sa wrapYRotation, maxYRotation + */ + +/*! + * \qmlproperty float Camera3D::maxXRotation + * + * This property contains the current maximum X-rotation for the camera. + * The full circle range is \c{[-180, 180]} and the maximum value is limited to \c 180. + * Also the value can't be lower than the minimum, and is adjusted if necessary. + * + * \sa wrapXRotation, minXRotation + */ + +/*! + * \qmlproperty float Camera3D::maxYRotation + * + * This property contains the current maximum Y-rotation for the camera. + * The full Y angle range is \c{[-90, 90]} and the maximum value is limited to \c 90. + * Also the value can't be lower than the minimum, and is adjusted if necessary. + * + * \sa wrapYRotation, minYRotation + */ + +/*! + * \qmlmethod void Camera3D::setBaseOrientation(vector3d basePosition, vector3d target, vector3d baseUp) + * + * Sets the base values for the camera that are used when calculating the camera position using the + * rotation values. The base position of the camera is defined by \a basePosition, expectation is + * that the x and y values are 0. Look at target point is defined by \a target and the camera + * rotates around it. Up direction for the camera is defined by \a baseUp, normally this is a + * vector with only y value set to 1. + */ + +/*! + * \qmlproperty matrix4x4 Camera3D::viewMatrix + * + * This property contains the view matrix used in the 3D calculations. When the default orbiting + * camera behavior is sufficient, there is no need to touch this property. If the default + * behavior is insufficient, the view matrix can be set directly. + * \note When setting the view matrix directly remember to set viewMatrixAutoUpdateEnabled + * property to \c false. + */ + +/*! + * \qmlproperty bool Camera3D::viewMatrixAutoUpdateEnabled + * + * This property determines if view matrix is automatically updated each render cycle using the + * current base orientation and rotations. If set to \c false, no automatic recalculation is done and + * the view matrix can be set using the viewMatrix property. + */ + +/*! + * \qmlproperty Camera3D.CameraPreset Camera3D::cameraPreset + * + * This property contains the currently active camera preset, which is one of + * \l{Q3DCamera::CameraPreset}{Camera3D.CameraPreset}. If no preset is active the value + * is \l{Q3DCamera::CameraPresetNone}{Camera3D.CameraPresetNone}. + * \note The base camera orientation set by setBaseOrientation will affect the presets as all + * calculations are based on those values. + */ + +/*! + * \qmlproperty float Camera3D::zoomLevel + * + * This property contains the the camera zoom level in percentage. 100.0 means there is no zoom + * in or out set in the camera. + */ + +/*! + * \qmlproperty bool Camera3D::wrapXRotation + * + * This property determines the behavior of the minimum and maximum limits in the X-rotation. + * By default the X-rotation wraps from minimum value to maximum and from maximum to minimum. + * + * If set to \c true the X-rotation of the camera is wrapped from minimum to maximum and from maximum + * to minimum. If set to \c false the X-rotation of the camera is limited to the sector determined by + * minimum and maximum values. + */ + +/*! + * \qmlproperty bool Camera3D::wrapYRotation + * + * This property determines the behavior of the minimum and maximum limits in the Y-rotation. + * By default the Y-rotation is limited between the minimum and maximum values without any wrapping. + * + * If \c true the Y-rotation of the camera is wrapped from minimum to maximum and from maximum + * to minimum. If \c false the Y-rotation of the camera is limited to the sector determined by minimum + * and maximum values. + */ + +/*! + * Constructs a new 3D camera with position set to origin, up direction facing towards the Y-axis + * and looking at origin by default. An optional \a parent parameter can be given and is then passed + * to QObject constructor. */ Q3DCamera::Q3DCamera(QObject *parent) : Q3DObject(parent), @@ -60,59 +233,61 @@ Q3DCamera::~Q3DCamera() * Copies the 3D camera's properties from the given source camera. * Values are copied from the \a source to this object. */ -void Q3DCamera::copyValuesFrom(const Q3DCamera &source) +void Q3DCamera::copyValuesFrom(const Q3DObject &source) { Q3DObject::copyValuesFrom(source); - d_ptr->m_target.setX(source.d_ptr->m_target.x()); - d_ptr->m_target.setY(source.d_ptr->m_target.y()); - d_ptr->m_target.setZ(source.d_ptr->m_target.z()); + const Q3DCamera &sourceCamera = static_cast<const Q3DCamera &>(source); + + d_ptr->m_target.setX(sourceCamera.d_ptr->m_target.x()); + d_ptr->m_target.setY(sourceCamera.d_ptr->m_target.y()); + d_ptr->m_target.setZ(sourceCamera.d_ptr->m_target.z()); - d_ptr->m_up.setX(source.d_ptr->m_up.x()); - d_ptr->m_up.setY(source.d_ptr->m_up.y()); - d_ptr->m_up.setZ(source.d_ptr->m_up.z()); + d_ptr->m_up.setX(sourceCamera.d_ptr->m_up.x()); + d_ptr->m_up.setY(sourceCamera.d_ptr->m_up.y()); + d_ptr->m_up.setZ(sourceCamera.d_ptr->m_up.z()); float *values = new float[16]; - source.d_ptr->m_viewMatrix.copyDataTo(values); + sourceCamera.d_ptr->m_viewMatrix.copyDataTo(values); d_ptr->m_viewMatrix = QMatrix4x4(values); delete[] values; - d_ptr->m_xRotation = source.d_ptr->m_xRotation; - d_ptr->m_yRotation = source.d_ptr->m_yRotation; + d_ptr->m_xRotation = sourceCamera.d_ptr->m_xRotation; + d_ptr->m_yRotation = sourceCamera.d_ptr->m_yRotation; - d_ptr->m_minXRotation = source.d_ptr->m_minXRotation; - d_ptr->m_minYRotation = source.d_ptr->m_minYRotation; - d_ptr->m_maxXRotation = source.d_ptr->m_maxXRotation; - d_ptr->m_maxYRotation = source.d_ptr->m_maxYRotation; + d_ptr->m_minXRotation = sourceCamera.d_ptr->m_minXRotation; + d_ptr->m_minYRotation = sourceCamera.d_ptr->m_minYRotation; + d_ptr->m_maxXRotation = sourceCamera.d_ptr->m_maxXRotation; + d_ptr->m_maxYRotation = sourceCamera.d_ptr->m_maxYRotation; - d_ptr->m_wrapXRotation = source.d_ptr->m_wrapXRotation; - d_ptr->m_wrapYRotation = source.d_ptr->m_wrapYRotation; + d_ptr->m_wrapXRotation = sourceCamera.d_ptr->m_wrapXRotation; + d_ptr->m_wrapYRotation = sourceCamera.d_ptr->m_wrapYRotation; - d_ptr->m_zoomLevel = source.d_ptr->m_zoomLevel; - d_ptr->m_activePreset = source.d_ptr->m_activePreset; + d_ptr->m_zoomLevel = sourceCamera.d_ptr->m_zoomLevel; + d_ptr->m_activePreset = sourceCamera.d_ptr->m_activePreset; } /*! * \property Q3DCamera::xRotation * - * This property contains the X-rotation angle of the camera around the target point in degrees starting from - * the current base position set by the setBaseOrientation() methods. + * This property contains the X-rotation angle of the camera around the target point in degrees + * starting from the current base position set by the setBaseOrientation() method. */ -qreal Q3DCamera::xRotation() const { +float Q3DCamera::xRotation() const { return d_ptr->m_xRotation; } -void Q3DCamera::setXRotation(qreal rotation) +void Q3DCamera::setXRotation(float rotation) { if (d_ptr->m_wrapXRotation) rotation = Utils::wrapValue(rotation, d_ptr->m_minXRotation, d_ptr->m_maxXRotation); else - rotation = qBound(qreal(d_ptr->m_minXRotation), qreal(rotation), qreal(d_ptr->m_maxXRotation)); + rotation = qBound(float(d_ptr->m_minXRotation), float(rotation), float(d_ptr->m_maxXRotation)); if (d_ptr->m_xRotation != rotation) { d_ptr->setXRotation(rotation); - if (d_ptr->m_activePreset != QDataVis::CameraPresetNone) { - d_ptr->m_activePreset = QDataVis::CameraPresetNone; + if (d_ptr->m_activePreset != CameraPresetNone) { + d_ptr->m_activePreset = CameraPresetNone; setDirty(true); } @@ -123,24 +298,24 @@ void Q3DCamera::setXRotation(qreal rotation) /*! * \property Q3DCamera::yRotation * - * This property contains the Y-rotation angle of the camera around the target point in degrees starting from - * the current base position set by the setBaseOrientation() methods. + * This property contains the Y-rotation angle of the camera around the target point in degrees + * starting from the current base position set by the setBaseOrientation() method. */ -qreal Q3DCamera::yRotation() const { +float Q3DCamera::yRotation() const { return d_ptr->m_yRotation; } -void Q3DCamera::setYRotation(qreal rotation) +void Q3DCamera::setYRotation(float rotation) { if (d_ptr->m_wrapYRotation) rotation = Utils::wrapValue(rotation, d_ptr->m_minYRotation, d_ptr->m_maxYRotation); else - rotation = qBound(qreal(d_ptr->m_minYRotation), qreal(rotation), qreal(d_ptr->m_maxYRotation)); + rotation = qBound(float(d_ptr->m_minYRotation), float(rotation), float(d_ptr->m_maxYRotation)); if (d_ptr->m_yRotation != rotation) { d_ptr->setYRotation(rotation); - if (d_ptr->m_activePreset != QDataVis::CameraPresetNone) { - d_ptr->m_activePreset = QDataVis::CameraPresetNone; + if (d_ptr->m_activePreset != CameraPresetNone) { + d_ptr->m_activePreset = CameraPresetNone; setDirty(true); } @@ -152,22 +327,19 @@ void Q3DCamera::setYRotation(qreal rotation) * \property Q3DCamera::minXRotation * * This property contains the current minimum X-rotation for the camera. - * The full circle range is [-180,180] and the minimum value is limited to -180. + * The full circle range is \c{[-180, 180]} and the minimum value is limited to \c -180. * Also the value can't be higher than the maximum, and is adjusted if necessary. * * \sa wrapXRotation, maxXRotation */ -qreal Q3DCamera::minXRotation() const +float Q3DCamera::minXRotation() const { return d_ptr->m_minXRotation; } -/*! - * \internal - */ -void Q3DCamera::setMinXRotation(qreal minRotation) +void Q3DCamera::setMinXRotation(float minRotation) { - minRotation = qBound(qreal(-180.0), minRotation, qreal(180.0)); + minRotation = qBound(-180.0f, minRotation, 180.0f); if (minRotation > d_ptr->m_maxXRotation) minRotation = d_ptr->m_maxXRotation; @@ -184,22 +356,19 @@ void Q3DCamera::setMinXRotation(qreal minRotation) * \property Q3DCamera::minYRotation * * This property contains the current minimum Y-rotation for the camera. - * The full Y angle range is [-90,90] and the minimum value is limited to -90. + * The full Y angle range is \c{[-90, 90]} and the minimum value is limited to \c -90. * Also the value can't be higher than the maximum, and is adjusted if necessary. * * \sa wrapYRotation, maxYRotation */ -qreal Q3DCamera::minYRotation() const +float Q3DCamera::minYRotation() const { return d_ptr->m_minYRotation; } -/*! - * \internal - */ -void Q3DCamera::setMinYRotation(qreal minRotation) +void Q3DCamera::setMinYRotation(float minRotation) { - minRotation = qBound(qreal(-90.0), minRotation, qreal(90.0)); + minRotation = qBound(-90.0f, minRotation, 90.0f); if (minRotation > d_ptr->m_maxYRotation) minRotation = d_ptr->m_maxYRotation; @@ -216,22 +385,19 @@ void Q3DCamera::setMinYRotation(qreal minRotation) * \property Q3DCamera::maxXRotation * * This property contains the current maximum X-rotation for the camera. - * The full circle range is [-180,180] and the maximum value is limited to 180. + * The full circle range is \c{[-180, 180]} and the maximum value is limited to \c 180. * Also the value can't be lower than the minimum, and is adjusted if necessary. * * \sa wrapXRotation, minXRotation */ -qreal Q3DCamera::maxXRotation() const +float Q3DCamera::maxXRotation() const { return d_ptr->m_maxXRotation; } -/*! - * \internal - */ -void Q3DCamera::setMaxXRotation(qreal maxRotation) +void Q3DCamera::setMaxXRotation(float maxRotation) { - maxRotation = qBound(qreal(-180.0), maxRotation, qreal(180.0)); + maxRotation = qBound(-180.0f, maxRotation, 180.0f); if (maxRotation < d_ptr->m_minXRotation) maxRotation = d_ptr->m_minXRotation; @@ -249,22 +415,19 @@ void Q3DCamera::setMaxXRotation(qreal maxRotation) * \property Q3DCamera::maxYRotation * * This property contains the current maximum Y-rotation for the camera. - * The full Y angle range is [-90,90] and the maximum value is limited to 90. + * The full Y angle range is \c{[-90, 90]} and the maximum value is limited to \c 90. * Also the value can't be lower than the minimum, and is adjusted if necessary. * * \sa wrapYRotation, minYRotation */ -qreal Q3DCamera::maxYRotation() const +float Q3DCamera::maxYRotation() const { return d_ptr->m_maxYRotation; } -/*! - * \internal - */ -void Q3DCamera::setMaxYRotation(qreal maxRotation) +void Q3DCamera::setMaxYRotation(float maxRotation) { - maxRotation = qBound(qreal(-90.0), maxRotation, qreal(90.0)); + maxRotation = qBound(-90.0f, maxRotation, 90.0f); if (maxRotation < d_ptr->m_minYRotation) maxRotation = d_ptr->m_minYRotation; @@ -279,10 +442,11 @@ void Q3DCamera::setMaxYRotation(qreal maxRotation) } /*! - * Sets the base values for the camera that are used when calculating the camera position using the rotation values. - * The base position of the camera is defined by \a basePosition, expectation is that the x and y values are 0. - * Look at target point is defined by \a target and the camera rotates around it. Up direction for the camera is - * defined by \a baseUp, normally this is a vector with only y values set to 1. + * Sets the base values for the camera that are used when calculating the camera position using the + * rotation values. The base position of the camera is defined by \a basePosition, expectation is + * that the x and y values are 0. Look at target point is defined by \a target and the camera + * rotates around it. Up direction for the camera is defined by \a baseUp, normally this is a + * vector with only y value set to 1. */ void Q3DCamera::setBaseOrientation(const QVector3D &basePosition, const QVector3D &target, @@ -301,9 +465,11 @@ void Q3DCamera::setBaseOrientation(const QVector3D &basePosition, /*! * \property Q3DCamera::viewMatrix * - * This property contains the view matrix used in the 3D calculations. When the default orbiting camera behavior is sufficient, - * there is no need to touch this property. But if the default behavior is insufficient, the view matrix can be set directly. - * When setting the view matrix directly remember to set Q3DCamera::viewMatrixAutoUpdateEnabled to false. + * This property contains the view matrix used in the 3D calculations. When the default orbiting + * camera behavior is sufficient, there is no need to touch this property. If the default + * behavior is insufficient, the view matrix can be set directly. + * \note When setting the view matrix directly remember to set viewMatrixAutoUpdateEnabled to + * \c false. */ QMatrix4x4 Q3DCamera::viewMatrix() const { @@ -322,11 +488,11 @@ void Q3DCamera::setViewMatrix(const QMatrix4x4 &viewMatrix) /*! * \property Q3DCamera::viewMatrixAutoUpdateEnabled * - * This property determines if view matrix is automatically updated each render cycle using the current base orientation and - * rotations. If set to false, no automatic recalculation is done and the view matrix can be set using the - * Q3DMatrix::viewMatrix property. + * This property determines if view matrix is automatically updated each render cycle using the + * current base orientation and rotations. If set to \c false, no automatic recalculation is done + * and the view matrix can be set using the viewMatrix property. */ -bool Q3DCamera::isViewMatrixAutoUpdateEnabled() +bool Q3DCamera::isViewMatrixAutoUpdateEnabled() const { return d_ptr->m_isViewMatrixUpdateActive; } @@ -340,141 +506,141 @@ void Q3DCamera::setViewMatrixAutoUpdateEnabled(bool isEnabled) /*! * \property Q3DCamera::cameraPreset * - * This property contains the currently active camera preset, - * if no preset is active the value is QDataVis::CameraPresetNone. + * This property contains the currently active camera preset, if no preset is active the value + * is CameraPresetNone. * \note The base camera orientation set by setBaseOrientation() will affect * the presets as all calculations are based on those values. */ -QDataVis::CameraPreset Q3DCamera::cameraPreset() +Q3DCamera::CameraPreset Q3DCamera::cameraPreset() const { return d_ptr->m_activePreset; } -void Q3DCamera::setCameraPreset(QDataVis::CameraPreset preset) +void Q3DCamera::setCameraPreset(CameraPreset preset) { switch (preset) { - case QDataVis::CameraPresetFrontLow: { - setXRotation(0.0); - setYRotation(0.0); + case CameraPresetFrontLow: { + setXRotation(0.0f); + setYRotation(0.0f); break; } - case QDataVis::CameraPresetFront: { - setXRotation(0.0); - setYRotation(22.5); + case CameraPresetFront: { + setXRotation(0.0f); + setYRotation(22.5f); break; } - case QDataVis::CameraPresetFrontHigh: { - setXRotation(0.0); - setYRotation(45.0); + case CameraPresetFrontHigh: { + setXRotation(0.0f); + setYRotation(45.0f); break; } - case QDataVis::CameraPresetLeftLow: { - setXRotation(90.0); - setYRotation(0.0); + case CameraPresetLeftLow: { + setXRotation(90.0f); + setYRotation(0.0f); break; } - case QDataVis::CameraPresetLeft: { - setXRotation(90.0); - setYRotation(22.5); + case CameraPresetLeft: { + setXRotation(90.0f); + setYRotation(22.5f); break; } - case QDataVis::CameraPresetLeftHigh: { - setXRotation(90.0); - setYRotation(45.0); + case CameraPresetLeftHigh: { + setXRotation(90.0f); + setYRotation(45.0f); break; } - case QDataVis::CameraPresetRightLow: { - setXRotation(-90.0); - setYRotation(0.0); + case CameraPresetRightLow: { + setXRotation(-90.0f); + setYRotation(0.0f); break; } - case QDataVis::CameraPresetRight: { - setXRotation(-90.0); - setYRotation(22.5); + case CameraPresetRight: { + setXRotation(-90.0f); + setYRotation(22.5f); break; } - case QDataVis::CameraPresetRightHigh: { - setXRotation(-90.0); - setYRotation(45.0); + case CameraPresetRightHigh: { + setXRotation(-90.0f); + setYRotation(45.0f); break; } - case QDataVis::CameraPresetBehindLow: { - setXRotation(180.0); - setYRotation(0.0); + case CameraPresetBehindLow: { + setXRotation(180.0f); + setYRotation(0.0f); break; } - case QDataVis::CameraPresetBehind: { - setXRotation(180.0); - setYRotation(22.5); + case CameraPresetBehind: { + setXRotation(180.0f); + setYRotation(22.5f); break; } - case QDataVis::CameraPresetBehindHigh: { - setXRotation(180.0); - setYRotation(45.0); + case CameraPresetBehindHigh: { + setXRotation(180.0f); + setYRotation(45.0f); break; } - case QDataVis::CameraPresetIsometricLeft: { - setXRotation(45.0); - setYRotation(22.5); + case CameraPresetIsometricLeft: { + setXRotation(45.0f); + setYRotation(22.5f); break; } - case QDataVis::CameraPresetIsometricLeftHigh: { - setXRotation(45.0); - setYRotation(45.0); + case CameraPresetIsometricLeftHigh: { + setXRotation(45.0f); + setYRotation(45.0f); break; } - case QDataVis::CameraPresetIsometricRight: { - setXRotation(-45.0); - setYRotation(22.5); + case CameraPresetIsometricRight: { + setXRotation(-45.0f); + setYRotation(22.5f); break; } - case QDataVis::CameraPresetIsometricRightHigh: { - setXRotation(-45.0); - setYRotation(45.0); + case CameraPresetIsometricRightHigh: { + setXRotation(-45.0f); + setYRotation(45.0f); break; } - case QDataVis::CameraPresetDirectlyAbove: { - setXRotation(0.0); - setYRotation(90.0); + case CameraPresetDirectlyAbove: { + setXRotation(0.0f); + setYRotation(90.0f); break; } - case QDataVis::CameraPresetDirectlyAboveCW45: { - setXRotation(-45.0); - setYRotation(90.0); + case CameraPresetDirectlyAboveCW45: { + setXRotation(-45.0f); + setYRotation(90.0f); break; } - case QDataVis::CameraPresetDirectlyAboveCCW45: { - setXRotation(45.0); - setYRotation(90.0); + case CameraPresetDirectlyAboveCCW45: { + setXRotation(45.0f); + setYRotation(90.0f); break; } - case QDataVis::CameraPresetFrontBelow: { - setXRotation(0.0); - setYRotation(-45.0); + case CameraPresetFrontBelow: { + setXRotation(0.0f); + setYRotation(-45.0f); break; } - case QDataVis::CameraPresetLeftBelow: { - setXRotation(90.0); - setYRotation(-45.0); + case CameraPresetLeftBelow: { + setXRotation(90.0f); + setYRotation(-45.0f); break; } - case QDataVis::CameraPresetRightBelow: { - setXRotation(-90.0); - setYRotation(-45.0); + case CameraPresetRightBelow: { + setXRotation(-90.0f); + setYRotation(-45.0f); break; } - case QDataVis::CameraPresetBehindBelow: { - setXRotation(180.0); - setYRotation(-45.0); + case CameraPresetBehindBelow: { + setXRotation(180.0f); + setYRotation(-45.0f); break; } - case QDataVis::CameraPresetDirectlyBelow: { - setXRotation(0.0); - setYRotation(-90.0); + case CameraPresetDirectlyBelow: { + setXRotation(0.0f); + setYRotation(-90.0f); break; } default: - preset = QDataVis::CameraPresetNone; + preset = CameraPresetNone; break; } @@ -488,10 +654,10 @@ void Q3DCamera::setCameraPreset(QDataVis::CameraPreset preset) /*! * \property Q3DCamera::zoomLevel * - * This property contains the the camera zoom level in percentages. - * 100% means there is no zoom in or out set in the camera. + * This property contains the the camera zoom level in percentage. \c 100.0f means there is no zoom + * in or out set in the camera. */ -int Q3DCamera::zoomLevel() +int Q3DCamera::zoomLevel() const { return d_ptr->m_zoomLevel; } @@ -508,14 +674,16 @@ void Q3DCamera::setZoomLevel(int zoomLevel) /*! * Calculates and returns a position relative to the camera using the given parameters * and the current camera viewMatrix property. - * \a relativePosition defines the relative 3D offset to the current camera position. - * \a fixedRotation is optional, if given fixes rotation of the calculated point around the data visualization area to the given value in degrees. - * \a distanceModifier is also optional, if given modifies the distance of the calculated point from the data visualization. - * \return Calculated position relative to this camera's position. + * The relative 3D offset to the current camera position is defined in \a relativePosition. + * An optional fixed rotation of the calculated point around the data visualization area can be + * given in \a fixedRotation. The rotation is given in degrees. + * An optional \a distanceModifier modifies the distance of the calculated point from the data + * visualization. + * \return calculated position relative to this camera's position. */ QVector3D Q3DCamera::calculatePositionRelativeToCamera(const QVector3D &relativePosition, - qreal fixedRotation, - qreal distanceModifier) const + float fixedRotation, + float distanceModifier) const { // Move the position with camera GLfloat radiusFactor = cameraDistance * (1.5f + distanceModifier); @@ -545,8 +713,9 @@ QVector3D Q3DCamera::calculatePositionRelativeToCamera(const QVector3D &relative * This property determines the behavior of the minimum and maximum limits in the X-rotation. * By default the X-rotation wraps from minimum value to maximum and from maximum to minimum. * - * If set to true the X-rotation of the camera is wrapped from minimum to maximum and from maximum to minimum. - * If set to false the X-rotation of the camera is limited to the sector determined by minimum and maximum values. + * If set to \c true the X-rotation of the camera is wrapped from minimum to maximum and from + * maximum to minimum. If set to \c false the X-rotation of the camera is limited to the sector + * determined by minimum and maximum values. */ bool Q3DCamera::wrapXRotation() const { @@ -564,8 +733,9 @@ void Q3DCamera::setWrapXRotation(bool isEnabled) * This property determines the behavior of the minimum and maximum limits in the Y-rotation. * By default the Y-rotation is limited between the minimum and maximum values without any wrapping. * - * If true the Y-rotation of the camera is wrapped from minimum to maximum and from maximum to minimum. - * If false the Y-rotation of the camera is limited to the sector determined by minimum and maximum values. + * If \c true the Y-rotation of the camera is wrapped from minimum to maximum and from maximum to + * minimum. If \c false the Y-rotation of the camera is limited to the sector determined by minimum + * and maximum values. */ bool Q3DCamera::wrapYRotation() const { @@ -578,12 +748,14 @@ void Q3DCamera::setWrapYRotation(bool isEnabled) } /*! - * Utility function that sets the camera rotations and distance.\a horizontal and \a vertical define the camera rotations to be used. - * Optional \a zoom parameter can be given to set the zoom of the camera in range of 10-500%. + * Utility function that sets the camera rotations and distance.\a horizontal and \a vertical + * define the camera rotations to be used. + * Optional \a zoom parameter can be given to set the zoom percentage of the camera in range of + * \c{10.0f - 500.0f}. */ -void Q3DCamera::setCameraPosition(qreal horizontal, qreal vertical, qreal zoom) +void Q3DCamera::setCameraPosition(float horizontal, float vertical, float zoom) { - setZoomLevel(qBound(qreal(10.0), zoom, qreal(500.0))); + setZoomLevel(qBound(10.0f, zoom, 500.0f)); setXRotation(horizontal); setYRotation(vertical); } @@ -591,16 +763,16 @@ void Q3DCamera::setCameraPosition(qreal horizontal, qreal vertical, qreal zoom) Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) : q_ptr(q), m_isViewMatrixUpdateActive(true), - m_xRotation(0.0), - m_yRotation(0.0), - m_minXRotation(-180.0), - m_minYRotation(0.0), - m_maxXRotation(180.0), - m_maxYRotation(90.0), + m_xRotation(0.0f), + m_yRotation(0.0f), + m_minXRotation(-180.0f), + m_minYRotation(0.0f), + m_maxXRotation(180.0f), + m_maxYRotation(90.0f), m_wrapXRotation(true), m_wrapYRotation(false), m_zoomLevel(100), - m_activePreset(QDataVis::CameraPresetNone) + m_activePreset(Q3DCamera::CameraPresetNone) { } @@ -619,7 +791,7 @@ void Q3DCameraPrivate::sync(Q3DCamera &other) } } -void Q3DCameraPrivate::setXRotation(const qreal rotation) +void Q3DCameraPrivate::setXRotation(const float rotation) { if (m_xRotation != rotation) { m_xRotation = rotation; @@ -627,7 +799,7 @@ void Q3DCameraPrivate::setXRotation(const qreal rotation) } } -void Q3DCameraPrivate::setYRotation(const qreal rotation) +void Q3DCameraPrivate::setYRotation(const float rotation) { if (m_yRotation != rotation) { m_yRotation = rotation; @@ -635,7 +807,7 @@ void Q3DCameraPrivate::setYRotation(const qreal rotation) } } -void Q3DCameraPrivate::setMinXRotation(const qreal rotation) +void Q3DCameraPrivate::setMinXRotation(const float rotation) { if (m_minXRotation != rotation) { m_minXRotation = rotation; @@ -643,7 +815,7 @@ void Q3DCameraPrivate::setMinXRotation(const qreal rotation) } } -void Q3DCameraPrivate::setMinYRotation(const qreal rotation) +void Q3DCameraPrivate::setMinYRotation(const float rotation) { if (m_minYRotation != rotation) { m_minYRotation = rotation; @@ -651,7 +823,7 @@ void Q3DCameraPrivate::setMinYRotation(const qreal rotation) } } -void Q3DCameraPrivate::setMaxXRotation(const qreal rotation) +void Q3DCameraPrivate::setMaxXRotation(const float rotation) { if (m_maxXRotation != rotation) { m_maxXRotation = rotation; @@ -659,7 +831,7 @@ void Q3DCameraPrivate::setMaxXRotation(const qreal rotation) } } -void Q3DCameraPrivate::setMaxYRotation(const qreal rotation) +void Q3DCameraPrivate::setMaxYRotation(const float rotation) { if (m_maxYRotation != rotation) { m_maxYRotation = rotation; @@ -669,7 +841,7 @@ void Q3DCameraPrivate::setMaxYRotation(const qreal rotation) // Recalculates the view matrix based on the currently set base orientation, rotation and zoom level values. // zoomAdjustment is adjustment to ensure that the 3D visualization stays inside the view area in the 100% zoom. -void Q3DCameraPrivate::updateViewMatrix(qreal zoomAdjustment) +void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment) { if (!m_isViewMatrixUpdateActive) return; diff --git a/src/datavisualization/engine/q3dcamera.h b/src/datavisualization/engine/q3dcamera.h index ee750cec..9287e15a 100644 --- a/src/datavisualization/engine/q3dcamera.h +++ b/src/datavisualization/engine/q3dcamera.h @@ -32,34 +32,63 @@ class Q3DCameraPrivate; class QT_DATAVISUALIZATION_EXPORT Q3DCamera : public Q3DObject { Q_OBJECT - Q_PROPERTY(qreal xRotation READ xRotation WRITE setXRotation NOTIFY xRotationChanged) - Q_PROPERTY(qreal yRotation READ yRotation WRITE setYRotation NOTIFY yRotationChanged) - Q_PROPERTY(qreal minXRotation READ minXRotation NOTIFY minXRotationChanged) - Q_PROPERTY(qreal minYRotation READ minYRotation NOTIFY minYRotationChanged) - Q_PROPERTY(qreal maxXRotation READ maxXRotation NOTIFY maxXRotationChanged) - Q_PROPERTY(qreal maxYRotation READ maxYRotation NOTIFY maxYRotationChanged) + Q_ENUMS(CameraPreset) + Q_PROPERTY(float xRotation READ xRotation WRITE setXRotation NOTIFY xRotationChanged) + Q_PROPERTY(float yRotation READ yRotation WRITE setYRotation NOTIFY yRotationChanged) + Q_PROPERTY(float minXRotation READ minXRotation NOTIFY minXRotationChanged) + Q_PROPERTY(float minYRotation READ minYRotation NOTIFY minYRotationChanged) + Q_PROPERTY(float maxXRotation READ maxXRotation NOTIFY maxXRotationChanged) + Q_PROPERTY(float maxYRotation READ maxYRotation NOTIFY maxYRotationChanged) Q_PROPERTY(int zoomLevel READ zoomLevel WRITE setZoomLevel NOTIFY zoomLevelChanged) Q_PROPERTY(QMatrix4x4 viewMatrix READ viewMatrix WRITE setViewMatrix NOTIFY viewMatrixChanged) - Q_PROPERTY(QtDataVisualization::QDataVis::CameraPreset cameraPreset READ cameraPreset WRITE setCameraPreset NOTIFY cameraPresetChanged) + Q_PROPERTY(CameraPreset cameraPreset READ cameraPreset WRITE setCameraPreset NOTIFY cameraPresetChanged) Q_PROPERTY(bool viewMatrixAutoUpdateEnabled READ isViewMatrixAutoUpdateEnabled WRITE setViewMatrixAutoUpdateEnabled NOTIFY viewMatrixAutoUpdateChanged) - Q_PROPERTY(bool wrapXRotation READ wrapXRotation WRITE setWrapXRotation NOTIFY wrapXRotationChanged ) - Q_PROPERTY(bool wrapYRotation READ wrapYRotation WRITE setWrapYRotation NOTIFY wrapYRotationChanged ) - Q_ENUMS(QtDataVisualization::QDataVis::CameraPreset) + Q_PROPERTY(bool wrapXRotation READ wrapXRotation WRITE setWrapXRotation NOTIFY wrapXRotationChanged) + Q_PROPERTY(bool wrapYRotation READ wrapYRotation WRITE setWrapYRotation NOTIFY wrapYRotationChanged) + +public: + enum CameraPreset { + CameraPresetNone = -1, + CameraPresetFrontLow = 0, + CameraPresetFront, + CameraPresetFrontHigh, + CameraPresetLeftLow, + CameraPresetLeft, + CameraPresetLeftHigh, + CameraPresetRightLow, + CameraPresetRight, + CameraPresetRightHigh, + CameraPresetBehindLow, + CameraPresetBehind, + CameraPresetBehindHigh, + CameraPresetIsometricLeft, + CameraPresetIsometricLeftHigh, + CameraPresetIsometricRight, + CameraPresetIsometricRightHigh, + CameraPresetDirectlyAbove, + CameraPresetDirectlyAboveCW45, + CameraPresetDirectlyAboveCCW45, + CameraPresetFrontBelow, + CameraPresetLeftBelow, + CameraPresetRightBelow, + CameraPresetBehindBelow, + CameraPresetDirectlyBelow + }; public: Q3DCamera(QObject *parent = 0); virtual ~Q3DCamera(); - qreal xRotation() const; - void setXRotation(qreal rotation); - qreal yRotation() const; - void setYRotation(qreal rotation); + float xRotation() const; + void setXRotation(float rotation); + float yRotation() const; + void setYRotation(float rotation); - qreal minXRotation() const; - qreal maxXRotation() const; + float minXRotation() const; + float maxXRotation() const; - qreal minYRotation() const; - qreal maxYRotation() const; + float minYRotation() const; + float maxYRotation() const; bool wrapXRotation() const; void setWrapXRotation(bool isEnabled); @@ -67,48 +96,49 @@ public: bool wrapYRotation() const; void setWrapYRotation(bool isEnabled); - void copyValuesFrom(const Q3DCamera &source); + virtual void copyValuesFrom(const Q3DObject &source); QMatrix4x4 viewMatrix() const; void setViewMatrix(const QMatrix4x4 &viewMatrix); - bool isViewMatrixAutoUpdateEnabled(); + bool isViewMatrixAutoUpdateEnabled() const; void setViewMatrixAutoUpdateEnabled(bool isEnabled); - QDataVis::CameraPreset cameraPreset(); - void setCameraPreset(QDataVis::CameraPreset preset); + CameraPreset cameraPreset() const; + void setCameraPreset(CameraPreset preset); - int zoomLevel(); + int zoomLevel() const; void setZoomLevel(int zoomLevel); - void setBaseOrientation(const QVector3D &defaultPosition, - const QVector3D &defaultTarget, - const QVector3D &defaultUp); + Q_INVOKABLE void setBaseOrientation(const QVector3D &defaultPosition, + const QVector3D &defaultTarget, + const QVector3D &defaultUp); QVector3D calculatePositionRelativeToCamera(const QVector3D &relativePosition, - qreal fixedRotation, - qreal distanceModifier) const; - void setCameraPosition(qreal horizontal, qreal vertical, qreal distance = 100.0); + float fixedRotation, + float distanceModifier) const; + void setCameraPosition(float horizontal, float vertical, float distance = 100.0f); signals: - void xRotationChanged(qreal rotation); - void yRotationChanged(qreal rotation); - void minXRotationChanged(qreal rotation); - void minYRotationChanged(qreal rotation); - void maxXRotationChanged(qreal rotation); - void maxYRotationChanged(qreal rotation); + void xRotationChanged(float rotation); + void yRotationChanged(float rotation); + void minXRotationChanged(float rotation); + void minYRotationChanged(float rotation); + void maxXRotationChanged(float rotation); + void maxYRotationChanged(float rotation); void zoomLevelChanged(int zoomLevel); void viewMatrixChanged(QMatrix4x4 viewMatrix); - void cameraPresetChanged(QDataVis::CameraPreset preset); + void cameraPresetChanged(CameraPreset preset); void viewMatrixAutoUpdateChanged(bool enabled); void wrapXRotationChanged(bool isEnabled); void wrapYRotationChanged(bool isEnabled); -protected: - void setMinXRotation(qreal rotation); - void setMinYRotation(qreal rotation); - void setMaxXRotation(qreal rotation); - void setMaxYRotation(qreal rotation); +private: + // To be exposed in the future + void setMinXRotation(float rotation); + void setMinYRotation(float rotation); + void setMaxXRotation(float rotation); + void setMaxYRotation(float rotation); private: QScopedPointer<Q3DCameraPrivate> d_ptr; @@ -117,6 +147,7 @@ private: friend class Q3DCameraPrivate; friend class Q3DScenePrivate; + friend class Abstract3DRenderer; friend class Bars3DRenderer; friend class Surface3DRenderer; friend class Scatter3DRenderer; diff --git a/src/datavisualization/engine/q3dcamera_p.h b/src/datavisualization/engine/q3dcamera_p.h index e0528dcc..ac32248e 100644 --- a/src/datavisualization/engine/q3dcamera_p.h +++ b/src/datavisualization/engine/q3dcamera_p.h @@ -44,14 +44,14 @@ public: void sync(Q3DCamera &other); - void setXRotation(qreal rotation); - void setYRotation(qreal rotation); - void setMinXRotation(qreal rotation); - void setMinYRotation(qreal rotation); - void setMaxXRotation(qreal rotation); - void setMaxYRotation(qreal rotation); + void setXRotation(float rotation); + void setYRotation(float rotation); + void setMinXRotation(float rotation); + void setMinYRotation(float rotation); + void setMaxXRotation(float rotation); + void setMaxYRotation(float rotation); - void updateViewMatrix(qreal zoomAdjustment); + void updateViewMatrix(float zoomAdjustment); public: Q3DCamera *q_ptr; @@ -71,7 +71,7 @@ public: bool m_wrapXRotation; bool m_wrapYRotation; int m_zoomLevel; - QDataVis::CameraPreset m_activePreset; + Q3DCamera::CameraPreset m_activePreset; friend class Bars3DRenderer; friend class Surface3DRenderer; diff --git a/src/datavisualization/engine/q3dlight.cpp b/src/datavisualization/engine/q3dlight.cpp index f1b1b4e7..bc43c3d7 100644 --- a/src/datavisualization/engine/q3dlight.cpp +++ b/src/datavisualization/engine/q3dlight.cpp @@ -23,16 +23,27 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE /*! - \class Q3DLight - \inmodule QtDataVisualization - \brief Representation of a light source in 3D space. - \since Qt Data Visualization 1.0 + * \class Q3DLight + * \inmodule QtDataVisualization + * \brief Representation of a light source in 3D space. + * \since Qt Data Visualization 1.0 + * + * Q3DLight represents a monochrome non variable light source in 3D space. + */ - Q3DLight represents a monochrome non variable light source in 3D space. -*/ +/*! + * \qmltype Light3D + * \inqmlmodule QtDataVisualization + * \since QtDataVisualization 1.0 + * \ingroup datavisualization_qml + * \instantiates Q3DLight + * \brief Representation of a light source in 3D space. + * + * Light3D represents a monochrome non variable light source in 3D space. + */ /*! - * Constructs a new 3D light located at origo. An optional \a parent parameter can be given + * Constructs a new 3D light located at origin. An optional \a parent parameter can be given * and is then passed to QObject constructor. */ Q3DLight::Q3DLight(QObject *parent) : @@ -42,14 +53,6 @@ Q3DLight::Q3DLight(QObject *parent) : } /*! - * Copies the properties of the 3D light from the given source \a source light to this light instance. - */ -void Q3DLight::copyValuesFrom(const Q3DLight &source) -{ - Q3DObject::copyValuesFrom(source); -} - -/*! * Destroys the light object. */ Q3DLight::~Q3DLight() diff --git a/src/datavisualization/engine/q3dlight.h b/src/datavisualization/engine/q3dlight.h index 0a4ba174..f9109183 100644 --- a/src/datavisualization/engine/q3dlight.h +++ b/src/datavisualization/engine/q3dlight.h @@ -34,8 +34,6 @@ public: Q3DLight(QObject *parent = 0); virtual ~Q3DLight(); - void copyValuesFrom(const Q3DLight &source); - private: QScopedPointer<Q3DLightPrivate> d_ptr; diff --git a/src/datavisualization/engine/q3dobject.h b/src/datavisualization/engine/q3dobject.h index 930bb022..b5ce7804 100644 --- a/src/datavisualization/engine/q3dobject.h +++ b/src/datavisualization/engine/q3dobject.h @@ -26,9 +26,10 @@ #include <QVector3D> QT_DATAVISUALIZATION_BEGIN_NAMESPACE + class Q3DObjectPrivate; -class Q3DObject : public QObject +class QT_DATAVISUALIZATION_EXPORT Q3DObject : public QObject { Q_OBJECT Q_PROPERTY(Q3DScene* parentScene READ parentScene) @@ -38,7 +39,7 @@ public: Q3DObject(QObject *parent = 0); virtual ~Q3DObject(); - void copyValuesFrom(const Q3DObject &source); + virtual void copyValuesFrom(const Q3DObject &source); Q3DScene *parentScene(); diff --git a/src/datavisualization/engine/q3dscatter.cpp b/src/datavisualization/engine/q3dscatter.cpp index fc95842a..3c28b4a5 100644 --- a/src/datavisualization/engine/q3dscatter.cpp +++ b/src/datavisualization/engine/q3dscatter.cpp @@ -20,11 +20,10 @@ #include "q3dscatter_p.h" #include "scatter3dcontroller_p.h" #include "q3dvalueaxis.h" -#include "qscatterdataproxy.h" #include "q3dcamera.h" +#include "qscatter3dseries_p.h" #include <QMouseEvent> -#include <QDebug> QT_DATAVISUALIZATION_BEGIN_NAMESPACE @@ -44,20 +43,20 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * These default axes can be modified via axis accessors, but as soon any axis is set explicitly * for the orientation, the default axis for that orientation is destroyed. * - * Data proxies work similarly: if no data proxy is set explicitly, Q3DScatter creates a default - * proxy. If any other proxy is set as active data proxy later, the default proxy and all data - * added to it is destroyed. + * Q3DScatter supports more than one series visible at the same time. * - * Methods are provided for changing item styles, themes, item selection modes and so on. See the + * Methods are provided for changing themes, item selection modes and so on. See the * methods for more detailed descriptions. * * \section1 How to construct a minimal Q3DScatter graph * - * First, construct Q3DScatter: + * First, construct Q3DScatter. Since we are running the graph as top level window + * in this example, we need to clear the \c Qt::FramelessWindowHint flag, which gets set by + * default: * * \snippet doc_src_q3dscatter_construction.cpp 0 * - * Now Q3DScatter is ready to receive data to be rendered. Add one set of 3 QVector3D items: + * Now Q3DScatter is ready to receive data to be rendered. Add one series of 3 QVector3D items: * * \snippet doc_src_q3dscatter_construction.cpp 1 * @@ -81,199 +80,150 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE */ /*! - * Constructs a new 3D scatter window. + * Constructs a new 3D scatter graph with optional \a parent window. */ -Q3DScatter::Q3DScatter() - : d_ptr(new Q3DScatterPrivate(this, geometry())) +Q3DScatter::Q3DScatter(QWindow *parent) + : Q3DWindow(new Q3DScatterPrivate(this), parent) { - setVisualController(d_ptr->m_shared); - d_ptr->m_shared->initializeOpenGL(); - QObject::connect(d_ptr->m_shared, &Scatter3DController::selectedItemIndexChanged, this, - &Q3DScatter::selectedItemIndexChanged); - QObject::connect(d_ptr->m_shared, &Abstract3DController::needRender, this, - &Q3DWindow::renderLater); + dptr()->m_shared = new Scatter3DController(geometry()); + d_ptr->setVisualController(dptr()->m_shared); + dptr()->m_shared->initializeOpenGL(); + QObject::connect(dptr()->m_shared, &Abstract3DController::selectionModeChanged, this, + &Q3DScatter::selectionModeChanged); + QObject::connect(dptr()->m_shared, &Abstract3DController::shadowQualityChanged, this, + &Q3DScatter::shadowQualityChanged); + QObject::connect(dptr()->m_shared, &Abstract3DController::themeChanged, this, + &Q3DScatter::themeChanged); + QObject::connect(dptr()->m_shared, &Abstract3DController::needRender, d_ptr.data(), + &Q3DWindowPrivate::renderLater); + QObject::connect(dptr()->m_shared, &Abstract3DController::shadowQualityChanged, dptr(), + &Q3DScatterPrivate::handleShadowQualityUpdate); } /*! - * Destroys the 3D scatter window. + * Destroys the 3D scatter graph. */ Q3DScatter::~Q3DScatter() { } /*! - * \internal + * Adds the \a series to the graph. A graph can contain multiple series, but has only one set of + * axes. If the newly added series has specified a selected item, it will be highlighted and + * any existing selection will be cleared. Only one added series can have an active selection. */ -void Q3DScatter::mouseDoubleClickEvent(QMouseEvent *event) +void Q3DScatter::addSeries(QScatter3DSeries *series) { - d_ptr->m_shared->mouseDoubleClickEvent(event); + dptr()->m_shared->addSeries(series); } /*! - * \internal + * Removes the \a series from the graph. */ -void Q3DScatter::touchEvent(QTouchEvent *event) +void Q3DScatter::removeSeries(QScatter3DSeries *series) { - d_ptr->m_shared->touchEvent(event); + dptr()->m_shared->removeSeries(series); } /*! - * \internal + * \return list of series added to this graph. */ -void Q3DScatter::mousePressEvent(QMouseEvent *event) +QList<QScatter3DSeries *> Q3DScatter::seriesList() { - d_ptr->m_shared->mousePressEvent(event, event->pos()); + return dptr()->m_shared->scatterSeriesList(); } /*! * \internal */ -void Q3DScatter::mouseReleaseEvent(QMouseEvent *event) +void Q3DScatter::mouseDoubleClickEvent(QMouseEvent *event) { - d_ptr->m_shared->mouseReleaseEvent(event, event->pos()); + dptr()->m_shared->mouseDoubleClickEvent(event); } /*! * \internal */ -void Q3DScatter::mouseMoveEvent(QMouseEvent *event) +void Q3DScatter::touchEvent(QTouchEvent *event) { - d_ptr->m_shared->mouseMoveEvent(event, event->pos()); + dptr()->m_shared->touchEvent(event); } /*! * \internal */ -void Q3DScatter::wheelEvent(QWheelEvent *event) +void Q3DScatter::mousePressEvent(QMouseEvent *event) { - d_ptr->m_shared->wheelEvent(event); + dptr()->m_shared->mousePressEvent(event, event->pos()); } /*! * \internal */ -void Q3DScatter::resizeEvent(QResizeEvent *event) +void Q3DScatter::mouseReleaseEvent(QMouseEvent *event) { - Q_UNUSED(event); - d_ptr->m_shared->setSize(width(), height()); + dptr()->m_shared->mouseReleaseEvent(event, event->pos()); } /*! - * Sets window \a width. + * \internal */ -void Q3DScatter::setWidth(const int width) +void Q3DScatter::mouseMoveEvent(QMouseEvent *event) { - d_ptr->m_shared->setWidth(width); - QWindow::setWidth(width); + dptr()->m_shared->mouseMoveEvent(event, event->pos()); } /*! - * Sets window \a height. + * \internal */ -void Q3DScatter::setHeight(const int height) +void Q3DScatter::wheelEvent(QWheelEvent *event) { - d_ptr->m_shared->setHeight(height); - QWindow::setHeight(height); + dptr()->m_shared->wheelEvent(event); } -/*! - * Sets the item \a style to one of the values in \c QDataVis::MeshStyle. It is preset to - * \c QDataVis::MeshStyleSpheres by default. A \a smooth flag can be used to set shading to smooth. - * It is \c false by default. - * - * \sa setMeshFileName() - */ -void Q3DScatter::setObjectType(QDataVis::MeshStyle style, bool smooth) +Q3DScatterPrivate *Q3DScatter::dptr() { - d_ptr->m_shared->setObjectType(style, smooth); + return static_cast<Q3DScatterPrivate *>(d_ptr.data()); } -/*! - * Sets a predefined \a theme from \c QDataVis::Theme. It is preset to \c QDataVis::ThemeQt by - * default. Theme affects bar colors, label colors, text color, background color, window color and - * grid color. Lighting is also adjusted by themes. - * - * \sa setObjectColor() - * - * \preliminary - */ -void Q3DScatter::setTheme(QDataVis::Theme theme) +const Q3DScatterPrivate *Q3DScatter::dptrc() const { - d_ptr->m_shared->setTheme(theme); + return static_cast<const Q3DScatterPrivate *>(d_ptr.data()); } /*! - * Set item color using your own colors. \a baseColor sets the base color of a item. The \a uniform - * -flag is used to define if color needs to be uniform throughout item's length, or will the colors - * be applied by height. It is \c true by default. - * - * Calling this method overrides colors from theme. - * - * \sa setTheme() + * \property Q3DScatter::theme * - * \preliminary + * A \a theme to be used for the graph. Ownership of the \a theme is transferred. Previous theme + * is deleted when a new one is set. Properties of the \a theme can be modified even after setting + * it, and the modifications take effect immediately. */ -void Q3DScatter::setObjectColor(const QColor &baseColor, bool uniform) +void Q3DScatter::setTheme(Q3DTheme *theme) { - d_ptr->m_shared->setObjectColor(baseColor, uniform); + dptr()->m_shared->setTheme(theme); } -/*! - * \return item color in use. - */ -QColor Q3DScatter::objectColor() const +Q3DTheme *Q3DScatter::theme() const { - return d_ptr->m_shared->objectColor(); + return dptrc()->m_shared->theme(); } /*! * \property Q3DScatter::selectionMode * - * Sets item selection \a mode to one of \c QDataVis::SelectionMode. It is preset to - * \c QDataVis::SelectionModeItem by default. - */ -void Q3DScatter::setSelectionMode(QDataVis::SelectionMode mode) -{ - d_ptr->m_shared->setSelectionMode(mode); -} - -QDataVis::SelectionMode Q3DScatter::selectionMode() const -{ - return d_ptr->m_shared->selectionMode(); -} - -/*! - * \property Q3DScatter::meshFileName - * - * Override item type with a mesh object located in \a objFileName. - * \note Object needs to be in Wavefront obj format and include vertices, normals and UVs. - * It also needs to be in triangles. - * - * \sa setObjectType() - */ -void Q3DScatter::setMeshFileName(const QString &objFileName) -{ - d_ptr->m_shared->setMeshFileName(objFileName); -} - -QString Q3DScatter::meshFileName() const -{ - return d_ptr->m_shared->meshFileName(); -} - -/*! - * \property Q3DScatter::font + * Sets item selection \a mode to a combination of \c QDataVis::SelectionFlags. It is preset to + * \c QDataVis::SelectionItem by default. * - * Sets the \a font for labels. It is preset to \c Arial by default. + * \note Only \c QDataVis::SelectionItem and \c QDataVis::SelectionNone are supported. */ -void Q3DScatter::setFont(const QFont &font) +void Q3DScatter::setSelectionMode(QDataVis::SelectionFlags mode) { - d_ptr->m_shared->setFont(font); + dptr()->m_shared->setSelectionMode(mode); } -QFont Q3DScatter::font() const +QDataVis::SelectionFlags Q3DScatter::selectionMode() const { - return d_ptr->m_shared->font(); + return dptrc()->m_shared->selectionMode(); } /*! @@ -283,69 +233,7 @@ QFont Q3DScatter::font() const */ Q3DScene *Q3DScatter::scene() const { - return d_ptr->m_shared->scene(); -} - -/*! - * \property Q3DScatter::labelStyle - * - * Sets label \a style to one of \c QDataVis::LabelStyle. It is preset to - * \c QDataVis::LabelStyleFromTheme by default. - */ -void Q3DScatter::setLabelStyle(QDataVis::LabelStyle style) -{ - d_ptr->m_shared->setLabelStyle(style); -} - -QDataVis::LabelStyle Q3DScatter::labelStyle() const -{ - return d_ptr->m_shared->labelStyle(); -} - -/*! - * \property Q3DScatter::gridVisible - * - * Sets grid visibility to \a visible. It is preset to \c true by default. - */ -void Q3DScatter::setGridVisible(bool visible) -{ - d_ptr->m_shared->setGridEnabled(visible); -} - -bool Q3DScatter::isGridVisible() const -{ - return d_ptr->m_shared->gridEnabled(); -} - -/*! - * \property Q3DScatter::backgroundVisible - * - * Sets background visibility to \a visible. It is preset to \c true by default. - */ -void Q3DScatter::setBackgroundVisible(bool visible) -{ - d_ptr->m_shared->setBackgroundEnabled(visible); -} - -bool Q3DScatter::isBackgroundVisible() const -{ - return d_ptr->m_shared->backgroundEnabled(); -} - -/*! - * \property Q3DScatter::selectedItemIndex - * - * Selects an item in the \a index. Only one item can be selected at a time. - * To clear selection, specify an illegal \a index, e.g. -1. - */ -void Q3DScatter::setSelectedItemIndex(int index) -{ - d_ptr->m_shared->setSelectedItemIndex(index); -} - -int Q3DScatter::selectedItemIndex() const -{ - return d_ptr->m_shared->selectedItemIndex(); + return dptrc()->m_shared->scene(); } /*! @@ -360,12 +248,12 @@ int Q3DScatter::selectedItemIndex() const */ void Q3DScatter::setShadowQuality(QDataVis::ShadowQuality quality) { - return d_ptr->m_shared->setShadowQuality(quality); + return dptr()->m_shared->setShadowQuality(quality); } QDataVis::ShadowQuality Q3DScatter::shadowQuality() const { - return d_ptr->m_shared->shadowQuality(); + return dptrc()->m_shared->shadowQuality(); } /*! @@ -380,7 +268,7 @@ QDataVis::ShadowQuality Q3DScatter::shadowQuality() const */ void Q3DScatter::setAxisX(Q3DValueAxis *axis) { - d_ptr->m_shared->setAxisX(axis); + dptr()->m_shared->setAxisX(axis); } /*! @@ -388,7 +276,7 @@ void Q3DScatter::setAxisX(Q3DValueAxis *axis) */ Q3DValueAxis *Q3DScatter::axisX() const { - return static_cast<Q3DValueAxis *>(d_ptr->m_shared->axisX()); + return static_cast<Q3DValueAxis *>(dptrc()->m_shared->axisX()); } /*! @@ -403,7 +291,7 @@ Q3DValueAxis *Q3DScatter::axisX() const */ void Q3DScatter::setAxisY(Q3DValueAxis *axis) { - d_ptr->m_shared->setAxisY(axis); + dptr()->m_shared->setAxisY(axis); } /*! @@ -411,7 +299,7 @@ void Q3DScatter::setAxisY(Q3DValueAxis *axis) */ Q3DValueAxis *Q3DScatter::axisY() const { - return static_cast<Q3DValueAxis *>(d_ptr->m_shared->axisY()); + return static_cast<Q3DValueAxis *>(dptrc()->m_shared->axisY()); } /*! @@ -426,7 +314,7 @@ Q3DValueAxis *Q3DScatter::axisY() const */ void Q3DScatter::setAxisZ(Q3DValueAxis *axis) { - d_ptr->m_shared->setAxisZ(axis); + dptr()->m_shared->setAxisZ(axis); } /*! @@ -434,7 +322,7 @@ void Q3DScatter::setAxisZ(Q3DValueAxis *axis) */ Q3DValueAxis *Q3DScatter::axisZ() const { - return static_cast<Q3DValueAxis *>(d_ptr->m_shared->axisZ()); + return static_cast<Q3DValueAxis *>(dptrc()->m_shared->axisZ()); } /*! @@ -446,7 +334,7 @@ Q3DValueAxis *Q3DScatter::axisZ() const */ void Q3DScatter::addAxis(Q3DValueAxis *axis) { - d_ptr->m_shared->addAxis(axis); + dptr()->m_shared->addAxis(axis); } /*! @@ -459,7 +347,7 @@ void Q3DScatter::addAxis(Q3DValueAxis *axis) */ void Q3DScatter::releaseAxis(Q3DValueAxis *axis) { - d_ptr->m_shared->releaseAxis(axis); + dptr()->m_shared->releaseAxis(axis); } /*! @@ -469,7 +357,7 @@ void Q3DScatter::releaseAxis(Q3DValueAxis *axis) */ QList<Q3DValueAxis *> Q3DScatter::axes() const { - QList<Q3DAbstractAxis *> abstractAxes = d_ptr->m_shared->axes(); + QList<Q3DAbstractAxis *> abstractAxes = dptrc()->m_shared->axes(); QList<Q3DValueAxis *> retList; foreach (Q3DAbstractAxis *axis, abstractAxes) retList.append(static_cast<Q3DValueAxis *>(axis)); @@ -478,90 +366,29 @@ QList<Q3DValueAxis *> Q3DScatter::axes() const } /*! - * Sets the active data \a proxy. Implicitly calls addDataProxy() to transfer ownership of - * the \a proxy to this graph. - * - * If the \a proxy is null, a temporary default proxy is created and activated. - * This temporary proxy is destroyed if another \a proxy is set explicitly active via this method. - * - * \sa addDataProxy(), releaseDataProxy() - */ -void Q3DScatter::setActiveDataProxy(QScatterDataProxy *proxy) -{ - d_ptr->m_shared->setActiveDataProxy(proxy); -} - -/*! - * \return active data proxy. - */ -QScatterDataProxy *Q3DScatter::activeDataProxy() const -{ - return static_cast<QScatterDataProxy *>(d_ptr->m_shared->activeDataProxy()); -} - -/*! - * Adds data \a proxy to the graph. The proxies added via addDataProxy are not yet taken to use, - * addDataProxy is simply used to give the ownership of the data \a proxy to the graph. - * The \a proxy must not be null or added to another graph. - * - * \sa releaseDataProxy(), setActiveDataProxy() - */ -void Q3DScatter::addDataProxy(QScatterDataProxy *proxy) -{ - d_ptr->m_shared->addDataProxy(proxy); -} - -/*! - * Releases the ownership of the data \a proxy back to the caller, if it is added to this graph. - * If the released \a proxy is in use, a new empty default proxy is created and taken to use. - * - * If the default \a proxy is released and added back later, it behaves as any other proxy would. - * - * \sa addDataProxy(), setActiveDataProxy() - */ -void Q3DScatter::releaseDataProxy(QScatterDataProxy *proxy) -{ - d_ptr->m_shared->releaseDataProxy(proxy); -} - -/*! - * \return list of all added data proxies. - * - * \sa addDataProxy() - */ -QList<QScatterDataProxy *> Q3DScatter::dataProxies() const -{ - QList<QScatterDataProxy *> retList; - QList<QAbstractDataProxy *> abstractList = d_ptr->m_shared->dataProxies(); - foreach (QAbstractDataProxy *proxy, abstractList) - retList.append(static_cast<QScatterDataProxy *>(proxy)); - - return retList; -} - -/*! * \fn void Q3DScatter::shadowQualityChanged(QDataVis::ShadowQuality quality) * * This signal is emitted when shadow \a quality changes. */ -Q3DScatterPrivate::Q3DScatterPrivate(Q3DScatter *q, QRect rect) - : q_ptr(q), - m_shared(new Scatter3DController(rect)) +Q3DScatterPrivate::Q3DScatterPrivate(Q3DScatter *q) + : Q3DWindowPrivate(q) { - QObject::connect(m_shared, &Abstract3DController::shadowQualityChanged, this, - &Q3DScatterPrivate::handleShadowQualityUpdate); } Q3DScatterPrivate::~Q3DScatterPrivate() { - qDebug() << "Destroying Q3DScatterPrivate"; delete m_shared; } void Q3DScatterPrivate::handleShadowQualityUpdate(QDataVis::ShadowQuality quality) { - emit q_ptr->shadowQualityChanged(quality); + emit qptr()->shadowQualityChanged(quality); +} + +Q3DScatter *Q3DScatterPrivate::qptr() +{ + return static_cast<Q3DScatter *>(q_ptr); } QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/q3dscatter.h b/src/datavisualization/engine/q3dscatter.h index fdea604e..e58146a1 100644 --- a/src/datavisualization/engine/q3dscatter.h +++ b/src/datavisualization/engine/q3dscatter.h @@ -22,7 +22,9 @@ #include <QtDataVisualization/qdatavisualizationenums.h> #include <QtDataVisualization/q3dwindow.h> #include <QtDataVisualization/q3dscene.h> +#include <QtDataVisualization/q3dtheme.h> #include <QFont> +#include <QLinearGradient> QT_DATAVISUALIZATION_BEGIN_NAMESPACE @@ -30,62 +32,33 @@ class Q3DScatterPrivate; class LabelItem; class Q3DValueAxis; class Q3DCategoryAxis; -class QScatterDataProxy; +class QScatter3DSeries; +class Q3DTheme; class QT_DATAVISUALIZATION_EXPORT Q3DScatter : public Q3DWindow { Q_OBJECT - Q_PROPERTY(QtDataVisualization::QDataVis::SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) - Q_PROPERTY(QtDataVisualization::QDataVis::LabelStyle labelStyle READ labelStyle WRITE setLabelStyle) - Q_PROPERTY(QtDataVisualization::QDataVis::ShadowQuality shadowQuality READ shadowQuality WRITE setShadowQuality) - Q_PROPERTY(QString meshFileName READ meshFileName WRITE setMeshFileName) - Q_PROPERTY(QFont font READ font WRITE setFont) - Q_PROPERTY(bool gridVisible READ isGridVisible WRITE setGridVisible) - Q_PROPERTY(bool backgroundVisible READ isBackgroundVisible WRITE setBackgroundVisible) - Q_PROPERTY(int selectedItemIndex READ selectedItemIndex WRITE setSelectedItemIndex NOTIFY selectedItemIndexChanged) + Q_PROPERTY(QtDataVisualization::QDataVis::SelectionFlags selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged) + Q_PROPERTY(QtDataVisualization::QDataVis::ShadowQuality shadowQuality READ shadowQuality WRITE setShadowQuality NOTIFY shadowQualityChanged) + Q_PROPERTY(Q3DTheme* theme READ theme WRITE setTheme NOTIFY themeChanged) Q_PROPERTY(Q3DScene* scene READ scene) - Q_ENUMS(QtDataVisualization::QDataVis::SelectionMode) - Q_ENUMS(QtDataVisualization::QDataVis::ShadowQuality) - Q_ENUMS(QtDataVisualization::QDataVis::LabelStyle) - Q_ENUMS(QtDataVisualization::QDataVis::CameraPreset) public: - explicit Q3DScatter(); - ~Q3DScatter(); + explicit Q3DScatter(QWindow *parent = 0); + virtual ~Q3DScatter(); - void setObjectType(QDataVis::MeshStyle style, bool smooth = false); + void addSeries(QScatter3DSeries *series); + void removeSeries(QScatter3DSeries *series); + QList<QScatter3DSeries *> seriesList(); - void setTheme(QDataVis::Theme theme); + void setTheme(Q3DTheme *theme); + Q3DTheme *theme() const; - void setObjectColor(const QColor &baseColor, bool uniform = true); - QColor objectColor() const; - - void setMeshFileName(const QString &objFileName); - QString meshFileName() const; - - void setSelectionMode(QDataVis::SelectionMode mode); - QDataVis::SelectionMode selectionMode() const; - - void setFont(const QFont &font); - QFont font() const; + void setSelectionMode(QDataVis::SelectionFlags mode); + QDataVis::SelectionFlags selectionMode() const; Q3DScene *scene() const; - void setLabelStyle(QDataVis::LabelStyle style); - QDataVis::LabelStyle labelStyle() const; - - void setGridVisible(bool visible); - bool isGridVisible() const; - - void setWidth(const int width); - void setHeight(const int height); - - void setBackgroundVisible(bool visible); - bool isBackgroundVisible() const; - - void setSelectedItemIndex(int index); - int selectedItemIndex() const; - void setShadowQuality(QDataVis::ShadowQuality quality); QDataVis::ShadowQuality shadowQuality() const; @@ -99,15 +72,10 @@ public: void releaseAxis(Q3DValueAxis *axis); QList<Q3DValueAxis *> axes() const; - void setActiveDataProxy(QScatterDataProxy *proxy); - QScatterDataProxy *activeDataProxy() const; - void addDataProxy(QScatterDataProxy *proxy); - void releaseDataProxy(QScatterDataProxy *proxy); - QList<QScatterDataProxy *> dataProxies() const; - signals: + void selectionModeChanged(QDataVis::SelectionFlags mode); void shadowQualityChanged(QDataVis::ShadowQuality quality); - void selectedItemIndexChanged(int index); + void themeChanged(Q3DTheme* theme); protected: void mouseDoubleClickEvent(QMouseEvent *event); @@ -116,10 +84,10 @@ protected: void mouseReleaseEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void wheelEvent(QWheelEvent *event); - void resizeEvent(QResizeEvent *event); private: - QScopedPointer<Q3DScatterPrivate> d_ptr; + Q3DScatterPrivate *dptr(); + const Q3DScatterPrivate *dptrc() const; Q_DISABLE_COPY(Q3DScatter) }; diff --git a/src/datavisualization/engine/q3dscatter_p.h b/src/datavisualization/engine/q3dscatter_p.h index 775344d0..d65776c5 100644 --- a/src/datavisualization/engine/q3dscatter_p.h +++ b/src/datavisualization/engine/q3dscatter_p.h @@ -26,26 +26,29 @@ // // We mean it. -#ifndef Q3DSCATTER_p_H -#define Q3DSCATTER_p_H +#ifndef Q3DSCATTER_P_H +#define Q3DSCATTER_P_H #include "scatter3dcontroller_p.h" #include "qdatavisualizationenums.h" +#include "q3dwindow_p.h" QT_DATAVISUALIZATION_BEGIN_NAMESPACE class Q3DScatter; -class Q3DScatterPrivate : public QObject +class Q3DScatterPrivate : public Q3DWindowPrivate { + Q_OBJECT public: - Q3DScatterPrivate(Q3DScatter *q, QRect rect); + Q3DScatterPrivate(Q3DScatter *q); ~Q3DScatterPrivate(); // Used to detect when shadow quality changes autonomously due to e.g. resizing. void handleShadowQualityUpdate(QDataVis::ShadowQuality quality); - Q3DScatter *q_ptr; + Q3DScatter *qptr(); + Scatter3DController *m_shared; }; diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index 3789ea9f..d8104b3a 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -26,6 +26,7 @@ #include "q3dlight_p.h" QT_DATAVISUALIZATION_BEGIN_NAMESPACE + /*! * \class Q3DScene * \inmodule QtDataVisualization @@ -37,6 +38,27 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * * The 3D scene also keeps track of the viewport in which visualization rendering is done, * the primary subviewport inside the viewport where the main 3D data visualization view resides + * and the secondary subviewport where the 2D sliced view of the data resides. The subviewports are + * by default resized by the \a Q3DScene. To override the resize behavior you need to listen to both + * \l viewportChanged() and \l slicingActiveChanged() signals and recalculate the subviewports accordingly. + * + * Also the scene has flag for tracking if the secondary 2D slicing view is currently active or not. + * \note Not all visualizations support the secondary 2D slicing view. + */ + +/*! + * \qmltype Scene3D + * \inqmlmodule QtDataVisualization + * \since QtDataVisualization 1.0 + * \ingroup datavisualization_qml + * \instantiates Q3DScene + * \brief Scene3D type provides description of the 3D scene being visualized. + * + * The 3D scene contains a single active camera and a single active light source. + * Visualized data is assumed to be at a fixed location. + * + * The 3D scene also keeps track of the viewport in which visualization rendering is done, + * the primary subviewport inside the viewport where the main 3D data visualization view resides * and the secondary subviewport where the 2D sliced view of the data resides. * * Also the scene has flag for tracking if the secondary 2D slicing view is currently active or not. @@ -44,6 +66,71 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE */ /*! + * \qmlproperty rect Scene3D::viewport + * + * This property contains the current viewport rectangle where all 3D rendering + * is targeted. + */ + +/*! + * \qmlproperty rect Scene3D::primarySubViewport + * + * This property contains the current subviewport rectangle inside the viewport where the + * primary view of the data visualization is targeted to. + */ + +/*! + * \qmlproperty rect Scene3D::secondarySubViewport + * + * This property contains the secondary viewport rectangle inside the viewport. The secondary + * viewport is used for drawing the 2D slice view in some visualizations. + */ + +/*! + * \qmlproperty point Scene3D::selectionQueryPosition + * + * This property contains the coordinates for the user input that should be processed + * by the scene as selection. If this is set to value other than \c{(-1, -1)} the + * graph tries to select a data item at the given point within the primary viewport. + * After the rendering pass the property is returned to its default state of \c{(-1, -1)}. + */ + +/*! + * \qmlproperty bool Scene3D::slicingActive + * + * This property contains whether 2D slicing view is currently active or not. + * \note Not all visualizations support the 2D slicing view. + */ + +/*! + * \qmlproperty bool Scene3D::secondarySubviewOnTop + * + * This property contains whether 2D slicing view is currently drawn on top or if the 3D view is + * drawn on top. + */ + +/*! + * \qmlproperty Camera3D Scene3D::activeCamera + * + * This property contains the currently active camera in the 3D scene. + * When a Camera3D is set in the property it gets automatically added as child of the scene. + */ + +/*! + * \qmlproperty Light3D Scene3D::activeLight + * + * This property contains the currently active light in the 3D scene. + * When a Light3D is set in the property it gets automatically added as child of the scene. + */ + +/*! + * \qmlproperty float Scene3D::devicePixelRatio + * + * This property contains the current device pixel ratio that is used when mapping input + * coordinates to pixel coordinates. + */ + +/*! * Constructs a basic scene with one light and one camera in it. An * optional \a parent parameter can be given and is then passed to QObject constructor. */ @@ -65,7 +152,7 @@ Q3DScene::~Q3DScene() /*! * \property Q3DScene::viewport * - * This property contains the current viewport rectangle where all 3D rendering + * This read only property contains the current viewport rectangle where all 3D rendering * is targeted. */ QRect Q3DScene::viewport() const @@ -73,36 +160,10 @@ QRect Q3DScene::viewport() const return d_ptr->m_viewport; } -void Q3DScene::setViewport(const QRect &viewport) -{ - if (d_ptr->m_viewport.width() != viewport.width() - || d_ptr->m_viewport.height() != viewport.height()) { - d_ptr->m_viewport = viewport; - d_ptr->m_viewport.setX(0); - d_ptr->m_viewport.setY(0); - d_ptr->m_changeTracker.viewportChanged = true; - emit viewportChanged(viewport); - } -} - -/*! - * Sets the \a width and \a height of the viewport only, without changing its location. - */ -void Q3DScene::setViewportSize(int width, int height) -{ - if (d_ptr->m_viewport.width() != width - || d_ptr->m_viewport.height() != height) { - d_ptr->m_viewport.setWidth(width); - d_ptr->m_viewport.setHeight(height); - d_ptr->m_changeTracker.viewportChanged = true; - emit viewportChanged(d_ptr->m_viewport); - } -} - /*! * \property Q3DScene::primarySubViewport * - * This property contains the current main viewport rectangle inside the viewport where the + * This property contains the current subviewport rectangle inside the viewport where the * primary view of the data visualization is targeted to. */ QRect Q3DScene::primarySubViewport() const @@ -112,10 +173,14 @@ QRect Q3DScene::primarySubViewport() const void Q3DScene::setPrimarySubViewport(const QRect &primarySubViewport) { - if (d_ptr->m_primarySubViewport != primarySubViewport) { - d_ptr->m_primarySubViewport = primarySubViewport; + QRect clipRect = QRect(0, 0, d_ptr->m_viewport.width(), d_ptr->m_viewport.height()); + QRect intersectedViewport = primarySubViewport.intersected(clipRect); + if (d_ptr->m_primarySubViewport != intersectedViewport) { + d_ptr->m_primarySubViewport = intersectedViewport; + d_ptr->updateGLSubViewports(); d_ptr->m_changeTracker.primarySubViewportChanged = true; - emit primarySubViewportChanged(primarySubViewport); + emit primarySubViewportChanged(intersectedViewport); + emit d_ptr->needRender(); } } @@ -123,7 +188,7 @@ void Q3DScene::setPrimarySubViewport(const QRect &primarySubViewport) * Returns whether the given \a point resides inside the primary subview or not. * The method takes care of correctly mapping between OpenGL coordinates used in the * viewport definitions and the Qt event coordinate system used in the input system. - * \return true if the point is inside the primary subview. + * \return \c true if the point is inside the primary subview. */ bool Q3DScene::isPointInPrimarySubView(const QPoint &point) { @@ -131,9 +196,9 @@ bool Q3DScene::isPointInPrimarySubView(const QPoint &point) int x = point.x(); int y = point.y(); int areaMinX = d_ptr->m_primarySubViewport.x(); - int areaMaxX = d_ptr->m_primarySubViewport.x() + d_ptr->m_primarySubViewport.width(); - int areaMaxY = d_ptr->m_viewport.height() - d_ptr->m_primarySubViewport.y(); - int areaMinY = d_ptr->m_viewport.height() - (d_ptr->m_primarySubViewport.y() + d_ptr->m_primarySubViewport.height()); + int areaMaxX = d_ptr->m_viewport.x() + d_ptr->m_primarySubViewport.x() + d_ptr->m_primarySubViewport.width(); + int areaMaxY = d_ptr->m_viewport.y() + d_ptr->m_primarySubViewport.y() + d_ptr->m_primarySubViewport.height(); + int areaMinY = d_ptr->m_viewport.y() + d_ptr->m_primarySubViewport.y(); return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY ); } @@ -142,7 +207,7 @@ bool Q3DScene::isPointInPrimarySubView(const QPoint &point) * Returns whether the given \a point resides inside the secondary subview or not. * The method takes care of correctly mapping between OpenGL coordinates used in the * viewport definitions and the Qt event coordinate system used in the input system. - * \return true if the point is inside the secondary subview. + * \return \c true if the point is inside the secondary subview. */ bool Q3DScene::isPointInSecondarySubView(const QPoint &point) { @@ -150,9 +215,9 @@ bool Q3DScene::isPointInSecondarySubView(const QPoint &point) int x = point.x(); int y = point.y(); int areaMinX = d_ptr->m_secondarySubViewport.x(); - int areaMaxX = d_ptr->m_secondarySubViewport.x() + d_ptr->m_secondarySubViewport.width(); - int areaMaxY = d_ptr->m_viewport.height() - d_ptr->m_secondarySubViewport.y(); - int areaMinY = d_ptr->m_viewport.height() - (d_ptr->m_secondarySubViewport.y() + d_ptr->m_secondarySubViewport.height()); + int areaMaxX = d_ptr->m_viewport.x() + d_ptr->m_secondarySubViewport.x() + d_ptr->m_secondarySubViewport.width(); + int areaMaxY = d_ptr->m_viewport.y() + d_ptr->m_secondarySubViewport.y() + d_ptr->m_secondarySubViewport.height(); + int areaMinY = d_ptr->m_viewport.y() + d_ptr->m_secondarySubViewport.y(); return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY ); } @@ -170,14 +235,50 @@ QRect Q3DScene::secondarySubViewport() const void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport) { - if (d_ptr->m_secondarySubViewport != secondarySubViewport) { - d_ptr->m_secondarySubViewport = secondarySubViewport; + QRect clipRect = QRect(0, 0, d_ptr->m_viewport.width(), d_ptr->m_viewport.height()); + QRect intersectedViewport = secondarySubViewport.intersected(clipRect); + if (d_ptr->m_secondarySubViewport != intersectedViewport) { + d_ptr->m_secondarySubViewport = intersectedViewport; + d_ptr->updateGLSubViewports(); d_ptr->m_changeTracker.secondarySubViewportChanged = true; - emit secondarySubViewportChanged(secondarySubViewport); + emit secondarySubViewportChanged(intersectedViewport); + emit d_ptr->needRender(); } } /*! + * \property Q3DScene::selectionQueryPosition + * + * This property contains the coordinates for the user input that should be processed + * by the scene as selection. If this is set to value other than invalidSelectionPoint() the + * graph tries to select a data item at the given \a point within the primary viewport. + * After the rendering pass the property is returned to its default state of invalidSelectionPoint(). + */ +void Q3DScene::setSelectionQueryPosition(const QPoint &point) +{ + if (point != d_ptr->m_selectionQueryPosition) { + d_ptr->m_selectionQueryPosition = point; + d_ptr->m_changeTracker.selectionQueryPositionChanged = true; + emit selectionQueryPositionChanged(point); + emit d_ptr->needRender(); + } +} + +QPoint Q3DScene::selectionQueryPosition() const +{ + return d_ptr->m_selectionQueryPosition; +} + +/*! + * \return a QPoint signifying an invalid selection position. + */ +const QPoint Q3DScene::invalidSelectionPoint() +{ + static const QPoint invalidSelectionPos(-1, -1); + return invalidSelectionPos; +} + +/*! * \property Q3DScene::slicingActive * * This property contains whether 2D slicing view is currently active or not. @@ -193,8 +294,30 @@ void Q3DScene::setSlicingActive(bool isSlicing) if (d_ptr->m_isSlicingActive != isSlicing) { d_ptr->m_isSlicingActive = isSlicing; d_ptr->m_changeTracker.slicingActivatedChanged = true; + d_ptr->calculateSubViewports(); emit slicingActiveChanged(isSlicing); - emitNeedRender(); + emit d_ptr->needRender(); + } +} + +/*! + * \property Q3DScene::secondarySubviewOnTop + * + * This property contains whether 2D slicing view is currently drawn on top or if the 3D view is + * drawn on top. + */ +bool Q3DScene::isSecondarySubviewOnTop() const +{ + return d_ptr->m_isSecondarySubviewOnTop; +} + +void Q3DScene::setSecondarySubviewOnTop(bool isSecondaryOnTop) +{ + if (d_ptr->m_isSecondarySubviewOnTop != isSecondaryOnTop) { + d_ptr->m_isSecondarySubviewOnTop = isSecondaryOnTop; + d_ptr->m_changeTracker.subViewportOrderChanged = true; + emit secondarySubviewOnTopChanged(isSecondaryOnTop); + emit d_ptr->needRender(); } } @@ -202,7 +325,8 @@ void Q3DScene::setSlicingActive(bool isSlicing) * \property Q3DScene::activeCamera * * This property contains the currently active camera in the 3D scene. - * When a new Q3DCamera objects is set in the property it gets automatically added as child of the scene. + * When a new Q3DCamera objects is set in the property it gets automatically added as child of + * the scene. */ Q3DCamera *Q3DScene::activeCamera() const { @@ -219,37 +343,37 @@ void Q3DScene::setActiveCamera(Q3DCamera *camera) if (camera != d_ptr->m_camera) { if (d_ptr->m_camera) { - disconnect(d_ptr->m_camera, &Q3DCamera::xRotationChanged, this, - &Q3DScene::emitNeedRender); - disconnect(d_ptr->m_camera, &Q3DCamera::yRotationChanged, this, - &Q3DScene::emitNeedRender); + disconnect(d_ptr->m_camera, &Q3DCamera::xRotationChanged, d_ptr.data(), + &Q3DScenePrivate::needRender); + disconnect(d_ptr->m_camera, &Q3DCamera::yRotationChanged, d_ptr.data(), + &Q3DScenePrivate::needRender); + disconnect(d_ptr->m_camera, &Q3DCamera::zoomLevelChanged, d_ptr.data(), + &Q3DScenePrivate::needRender); } d_ptr->m_camera = camera; d_ptr->m_changeTracker.cameraChanged = true; if (camera) { - connect(camera, &Q3DCamera::xRotationChanged, this, - &Q3DScene::emitNeedRender); - connect(camera, &Q3DCamera::yRotationChanged, this, - &Q3DScene::emitNeedRender); + connect(camera, &Q3DCamera::xRotationChanged, d_ptr.data(), + &Q3DScenePrivate::needRender); + connect(camera, &Q3DCamera::yRotationChanged, d_ptr.data(), + &Q3DScenePrivate::needRender); + connect(camera, &Q3DCamera::zoomLevelChanged, d_ptr.data(), + &Q3DScenePrivate::needRender); } emit activeCameraChanged(camera); - emitNeedRender(); + emit d_ptr->needRender(); } } -void Q3DScene::emitNeedRender() -{ - emit needRender(); -} - /*! * \property Q3DScene::activeLight * * This property contains the currently active light in the 3D scene. - * When a new Q3DLight objects is set in the property it gets automatically added as child of the scene. + * When a new Q3DLight objects is set in the property it gets automatically added as child of + * the scene. */ Q3DLight *Q3DScene::activeLight() const { @@ -277,27 +401,32 @@ void Q3DScene::setActiveLight(Q3DLight *light) * This property contains the current device pixel ratio that is used when mapping input * coordinates to pixel coordinates. */ -qreal Q3DScene::devicePixelRatio() const +float Q3DScene::devicePixelRatio() const { return d_ptr->m_devicePixelRatio; } -void Q3DScene::setDevicePixelRatio(qreal pixelRatio) +void Q3DScene::setDevicePixelRatio(float pixelRatio) { if (d_ptr->m_devicePixelRatio != pixelRatio) { d_ptr->m_devicePixelRatio = pixelRatio; + d_ptr->m_changeTracker.devicePixelRatioChanged = true; emit devicePixelRatioChanged(pixelRatio); + d_ptr->updateGLViewport(); + emit d_ptr->needRender(); } } /*! - * Calculates and sets the light position relative to the currently active camera using the given parameters. - * \a relativePosition defines the relative 3D offset to the current camera position. - * Optional \a fixedRotation fixes the light rotation around the data visualization area to the given value in degrees. + * Calculates and sets the light position relative to the currently active camera using the given + * parameters. + * The relative 3D offset to the current camera position is defined in \a relativePosition. + * Optional \a fixedRotation fixes the light rotation around the data visualization area to the + * given value in degrees. * Optional \a distanceModifier modifies the distance of the light from the data visualization. */ void Q3DScene::setLightPositionRelativeToCamera(const QVector3D &relativePosition, - qreal fixedRotation, qreal distanceModifier) + float fixedRotation, float distanceModifier) { d_ptr->m_light->setPosition( d_ptr->m_camera->calculatePositionRelativeToCamera(relativePosition, @@ -305,18 +434,16 @@ void Q3DScene::setLightPositionRelativeToCamera(const QVector3D &relativePositio distanceModifier)); } -/*! - * \fn Q3DScene::needRender() - * \internal - */ - Q3DScenePrivate::Q3DScenePrivate(Q3DScene *q) : + QObject(0), q_ptr(q), + m_isSecondarySubviewOnTop(true), m_devicePixelRatio(1.f), m_camera(), m_light(), m_isUnderSideCameraEnabled(false), - m_isSlicingActive(false) + m_isSlicingActive(false), + m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()) { } @@ -330,11 +457,21 @@ Q3DScenePrivate::~Q3DScenePrivate() // those changes are discarded. void Q3DScenePrivate::sync(Q3DScenePrivate &other) { + if (m_changeTracker.windowSizeChanged) { + other.setWindowSize(windowSize()); + m_changeTracker.windowSizeChanged = false; + other.m_changeTracker.windowSizeChanged = false; + } if (m_changeTracker.viewportChanged) { - other.q_ptr->setViewport(q_ptr->viewport()); + other.setViewport(m_viewport); m_changeTracker.viewportChanged = false; other.m_changeTracker.viewportChanged = false; } + if (m_changeTracker.subViewportOrderChanged) { + other.q_ptr->setSecondarySubviewOnTop(q_ptr->isSecondarySubviewOnTop()); + m_changeTracker.subViewportOrderChanged = false; + other.m_changeTracker.subViewportOrderChanged = false; + } if (m_changeTracker.primarySubViewportChanged) { other.q_ptr->setPrimarySubViewport(q_ptr->primarySubViewport()); m_changeTracker.primarySubViewportChanged = false; @@ -345,6 +482,11 @@ void Q3DScenePrivate::sync(Q3DScenePrivate &other) m_changeTracker.secondarySubViewportChanged = false; other.m_changeTracker.secondarySubViewportChanged = false; } + if (m_changeTracker.selectionQueryPositionChanged) { + other.q_ptr->setSelectionQueryPosition(q_ptr->selectionQueryPosition()); + m_changeTracker.selectionQueryPositionChanged = false; + other.m_changeTracker.selectionQueryPositionChanged = false; + } if (m_changeTracker.cameraChanged) { m_camera->setDirty(true); m_changeTracker.cameraChanged = false; @@ -372,4 +514,107 @@ void Q3DScenePrivate::sync(Q3DScenePrivate &other) } } +void Q3DScenePrivate::setViewport(const QRect &viewport) +{ + if (m_viewport != viewport) { + m_viewport = viewport; + calculateSubViewports(); + emit needRender(); + } +} + +void Q3DScenePrivate::setViewportSize(int width, int height) +{ + if (m_viewport.width() != width + || m_viewport.height() != height) { + m_viewport.setWidth(width); + m_viewport.setHeight(height); + calculateSubViewports(); + emit needRender(); + } +} + +/*! + * \internal + * Sets the size of the window being rendered to. With widget based graphs, this + * is equal to the size of the QWindow and is same as the bounding rectangle. + * With declarative graphs this is equal to the size of the QQuickWindow and + * can be different from the bounding rectangle. + */ +void Q3DScenePrivate::setWindowSize(const QSize &size) +{ + if (m_windowSize != size) { + m_windowSize = size; + updateGLViewport(); + m_changeTracker.windowSizeChanged = true; + emit needRender(); + } +} + +QSize Q3DScenePrivate::windowSize() const +{ + return m_windowSize; +} + +void Q3DScenePrivate::calculateSubViewports() +{ + // Calculates the default subviewport layout + const float smallerViewPortRatio = 0.2f; + if (m_isSlicingActive) { + q_ptr->setPrimarySubViewport(QRect(0, + 0, + m_viewport.width() * smallerViewPortRatio, + m_viewport.height() * smallerViewPortRatio)); + q_ptr->setSecondarySubViewport(QRect(0, 0, m_viewport.width(), m_viewport.height())); + } else { + q_ptr->setPrimarySubViewport(QRect(0, 0, m_viewport.width(), m_viewport.height())); + q_ptr->setSecondarySubViewport(QRect(0, 0, 0, 0)); + } + + updateGLViewport(); +} + +void Q3DScenePrivate::updateGLViewport() +{ + // Update GL viewport + m_glViewport.setX(m_viewport.x() * m_devicePixelRatio); + m_glViewport.setY((m_windowSize.height() - (m_viewport.y() + m_viewport.height())) * m_devicePixelRatio); + m_glViewport.setWidth(m_viewport.width() * m_devicePixelRatio); + m_glViewport.setHeight(m_viewport.height() * m_devicePixelRatio); + + m_changeTracker.viewportChanged = true; + + // Do default subviewport changes first, then allow signal listeners to override. + updateGLSubViewports(); + emit q_ptr->viewportChanged(m_viewport); +} + +void Q3DScenePrivate::updateGLSubViewports() +{ + m_glPrimarySubViewport.setX((m_primarySubViewport.x() + m_viewport.x()) * m_devicePixelRatio); + m_glPrimarySubViewport.setY((m_windowSize.height() - (m_primarySubViewport.y() + m_viewport.y() + m_primarySubViewport.height())) * m_devicePixelRatio); + m_glPrimarySubViewport.setWidth(m_primarySubViewport.width() * m_devicePixelRatio); + m_glPrimarySubViewport.setHeight(m_primarySubViewport.height() * m_devicePixelRatio); + + m_glSecondarySubViewport.setX(m_secondarySubViewport.x() * m_devicePixelRatio); + m_glSecondarySubViewport.setY((m_windowSize.height() - (m_secondarySubViewport.y() + m_viewport.y() + m_secondarySubViewport.height())) * m_devicePixelRatio); + m_glSecondarySubViewport.setWidth(m_secondarySubViewport.width() * m_devicePixelRatio); + m_glSecondarySubViewport.setHeight(m_secondarySubViewport.height() * m_devicePixelRatio); +} + +QRect Q3DScenePrivate::glViewport() +{ + return m_glViewport; +} + +QRect Q3DScenePrivate::glPrimarySubViewport() +{ + return m_glPrimarySubViewport; +} + +QRect Q3DScenePrivate::glSecondarySubViewport() +{ + return m_glSecondarySubViewport; +} + QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/q3dscene.h b/src/datavisualization/engine/q3dscene.h index 745cef72..b0dadff9 100644 --- a/src/datavisualization/engine/q3dscene.h +++ b/src/datavisualization/engine/q3dscene.h @@ -33,21 +33,21 @@ class Q3DScenePrivate; class QT_DATAVISUALIZATION_EXPORT Q3DScene : public QObject { Q_OBJECT - Q_PROPERTY(QRect viewport READ viewport WRITE setViewport NOTIFY viewportChanged) + Q_PROPERTY(QRect viewport READ viewport NOTIFY viewportChanged) Q_PROPERTY(QRect primarySubViewport READ primarySubViewport WRITE setPrimarySubViewport NOTIFY primarySubViewportChanged) Q_PROPERTY(QRect secondarySubViewport READ secondarySubViewport WRITE setSecondarySubViewport NOTIFY secondarySubViewportChanged) + Q_PROPERTY(QPoint selectionQueryPosition READ selectionQueryPosition WRITE setSelectionQueryPosition NOTIFY selectionQueryPositionChanged) + Q_PROPERTY(bool secondarySubviewOnTop READ isSecondarySubviewOnTop WRITE setSecondarySubviewOnTop NOTIFY secondarySubviewOnTopChanged) Q_PROPERTY(bool slicingActive READ isSlicingActive WRITE setSlicingActive NOTIFY slicingActiveChanged) Q_PROPERTY(Q3DCamera* activeCamera READ activeCamera WRITE setActiveCamera NOTIFY activeCameraChanged) Q_PROPERTY(Q3DLight* activeLight READ activeLight WRITE setActiveLight NOTIFY activeLightChanged) - Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged) + Q_PROPERTY(float devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged) public: Q3DScene(QObject *parent = 0); ~Q3DScene(); QRect viewport() const; - void setViewport(const QRect &viewport); - void setViewportSize(int width, int height); QRect primarySubViewport() const; void setPrimarySubViewport(const QRect &primarySubViewport); @@ -57,39 +57,47 @@ public: void setSecondarySubViewport(const QRect &secondarySubViewport); bool isPointInSecondarySubView(const QPoint &point); + void setSelectionQueryPosition(const QPoint &point); + QPoint selectionQueryPosition() const; + static const QPoint invalidSelectionPoint(); + void setSlicingActive(bool isSlicing); bool isSlicingActive() const; + void setSecondarySubviewOnTop(bool isSecondaryOnTop); + bool isSecondarySubviewOnTop() const; + Q3DCamera *activeCamera() const; void setActiveCamera(Q3DCamera *camera); Q3DLight *activeLight() const; void setActiveLight(Q3DLight *light); - qreal devicePixelRatio() const; - void setDevicePixelRatio(qreal pixelRatio); - - void setLightPositionRelativeToCamera(const QVector3D &relativePosition, - qreal fixedRotation = 0.0, - qreal distanceModifier = 0.0); -private: - void emitNeedRender(); + float devicePixelRatio() const; + void setDevicePixelRatio(float pixelRatio); + Q_INVOKABLE void setLightPositionRelativeToCamera(const QVector3D &relativePosition, + float fixedRotation = 0.0f, + float distanceModifier = 0.0f); signals: void viewportChanged(QRect viewport); void primarySubViewportChanged(QRect subViewport); void secondarySubViewportChanged(QRect subViewport); + void secondarySubviewOnTopChanged(bool isSecondaryOnTop); void slicingActiveChanged(bool isSlicingActive); void activeCameraChanged(const Q3DCamera *camera); void activeLightChanged(const Q3DLight *light); - void devicePixelRatioChanged(qreal pixelRatio); - void needRender(); + void devicePixelRatioChanged(float pixelRatio); + void selectionQueryPositionChanged(const QPoint position); private: QScopedPointer<Q3DScenePrivate> d_ptr; Q_DISABLE_COPY(Q3DScene) + friend class AbstractDeclarative; + friend class Q3DWindow; + friend class Abstract3DController; friend class Q3DScenePrivate; friend class Abstract3DRenderer; friend class Bars3DRenderer; diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h index b28baaae..c9d9d1f6 100644 --- a/src/datavisualization/engine/q3dscene_p.h +++ b/src/datavisualization/engine/q3dscene_p.h @@ -42,42 +42,71 @@ struct Q3DSceneChangeBitField { bool viewportChanged : 1; bool primarySubViewportChanged : 1; bool secondarySubViewportChanged : 1; + bool subViewportOrderChanged : 1; bool cameraChanged : 1; bool lightChanged : 1; bool slicingActivatedChanged : 1; bool devicePixelRatioChanged : 1; + bool selectionQueryPositionChanged : 1; + bool windowSizeChanged : 1; Q3DSceneChangeBitField() : viewportChanged(true), primarySubViewportChanged(true), secondarySubViewportChanged(true), + subViewportOrderChanged(true), cameraChanged(true), lightChanged(true), slicingActivatedChanged(true), - devicePixelRatioChanged(true) + devicePixelRatioChanged(true), + selectionQueryPositionChanged(false), + windowSizeChanged(true) { } }; -class Q3DScenePrivate +class QT_DATAVISUALIZATION_EXPORT Q3DScenePrivate : public QObject { + Q_OBJECT public: Q3DScenePrivate(Q3DScene *q); ~Q3DScenePrivate(); void sync(Q3DScenePrivate &other); + void setViewport(const QRect &viewport); + void setViewportSize(int width, int height); + void setWindowSize(const QSize &size); + QSize windowSize() const; + void calculateSubViewports(); + void updateGLViewport(); + void updateGLSubViewports(); + + QRect glViewport(); + QRect glPrimarySubViewport(); + QRect glSecondarySubViewport(); + +signals: + void needRender(); + +public: Q3DScene *q_ptr; Q3DSceneChangeBitField m_changeTracker; QRect m_viewport; QRect m_primarySubViewport; QRect m_secondarySubViewport; - qreal m_devicePixelRatio; + bool m_isSecondarySubviewOnTop; + float m_devicePixelRatio; Q3DCamera *m_camera; Q3DLight *m_light; bool m_isUnderSideCameraEnabled; bool m_isSlicingActive; + QPoint m_selectionQueryPosition; + QSize m_windowSize; + QRect m_glViewport; + QRect m_glPrimarySubViewport; + QRect m_glSecondarySubViewport; }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/q3dsurface.cpp b/src/datavisualization/engine/q3dsurface.cpp index 42260e8b..ce738685 100644 --- a/src/datavisualization/engine/q3dsurface.cpp +++ b/src/datavisualization/engine/q3dsurface.cpp @@ -21,6 +21,7 @@ #include "q3dvalueaxis.h" #include "qsurfacedataproxy.h" #include "q3dcamera.h" +#include "qsurface3dseries_p.h" #include <QMouseEvent> @@ -33,17 +34,15 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * \since Qt Data Visualization 1.0 * * This class enables developers to render 3D surface plots and to view them by rotating the scene - * freely. The class provides configurable gradient texture to illustrate the height on the data. The - * surface plotting includes also gridline that can be set on or off. The visual appearance of the - * surface can be changed by controlling the smooth status. + * freely. The surface plotting includes also gridline that can be set on or off. + * The visual appearance of the surface can be changed by controlling the smooth status. * * The Q3DSurface supports selection by showing a highlighted ball on the data point where the user has clicked - * with left mouse button (when default input handler is in use). The selection pointer is accompanied with - * a label which in default case shows the value of the data point and the coordinates of the point. + * with left mouse button (when default input handler is in use) or selected via QSurface3DSeries. + * The selection pointer is accompanied with a label which in default case shows the value of the + * data point and the coordinates of the point. * * The value range and the label format shown on the axis can be controlled through Q3DValueAxis. - * The Q3DSurface supports only a grid with fixed steps, so when setting ranges set a value that matches - * the grid step. To calculate the steps divide the whole data range with the number of segments. * * To rotate the graph, hold down the right mouse button and move the mouse. Zooming is done using mouse * wheel. Both assume the default input handler is in use. @@ -52,13 +51,13 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * These default axes can be modified via axis accessors, but as soon any axis is set explicitly * for the orientation, the default axis for that orientation is destroyed. * - * Data proxies work similarly: if no data proxy is set explicitly, Q3DSurface creates a default - * proxy. If any other proxy is set as active data proxy later, the default proxy and all data - * added to it is destroyed. + * Q3DSurface supports only single series at a time. * * \section1 How to construct a minimal Q3DSurface graph * - * First, construct Q3DSurface: + * First, construct Q3DSurface. Since we are running the graph as top level window + * in this example, we need to clear the \c Qt::FramelessWindowHint flag, which gets set by + * default: * * \snippet doc_src_q3dsurface_construction.cpp 0 * @@ -70,7 +69,7 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * * \snippet doc_src_q3dsurface_construction.cpp 2 * - * For the active data proxy set pointer of the data element: + * Create a new series and set data to it: * * \snippet doc_src_q3dsurface_construction.cpp 3 * @@ -94,129 +93,132 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE */ /*! - * Constructs a new 3D surface window. + * Constructs a new 3D surface graph with optional \a parent window. */ -Q3DSurface::Q3DSurface() - : d_ptr(new Q3DSurfacePrivate(this, geometry())) +Q3DSurface::Q3DSurface(QWindow *parent) + : Q3DWindow(new Q3DSurfacePrivate(this), parent) { - setVisualController(d_ptr->m_shared); - d_ptr->m_shared->initializeOpenGL(); - QObject::connect(d_ptr->m_shared, &Abstract3DController::needRender, this, - &Q3DWindow::renderLater); + dptr()->m_shared = new Surface3DController(geometry()); + d_ptr->setVisualController(dptr()->m_shared); + dptr()->m_shared->initializeOpenGL(); + QObject::connect(dptr()->m_shared, &Abstract3DController::selectionModeChanged, this, + &Q3DSurface::selectionModeChanged); + QObject::connect(dptr()->m_shared, &Abstract3DController::themeChanged, this, + &Q3DSurface::themeChanged); + QObject::connect(dptr()->m_shared, &Abstract3DController::shadowQualityChanged, this, + &Q3DSurface::shadowQualityChanged); + QObject::connect(dptr()->m_shared, &Abstract3DController::needRender, d_ptr.data(), + &Q3DWindowPrivate::renderLater); } /*! - * Destroys the 3D surface window. + * Destroys the 3D surface graph. */ Q3DSurface::~Q3DSurface() { } /*! - * \internal + * Adds the \a series to the graph. + * + * \note The surface graph currently supports only a single series at a time. */ -void Q3DSurface::mouseDoubleClickEvent(QMouseEvent *event) +void Q3DSurface::addSeries(QSurface3DSeries *series) { - d_ptr->m_shared->mouseDoubleClickEvent(event); + dptr()->m_shared->addSeries(series); } /*! - * \internal + * Removes the \a series from the graph. */ -void Q3DSurface::touchEvent(QTouchEvent *event) +void Q3DSurface::removeSeries(QSurface3DSeries *series) { - d_ptr->m_shared->touchEvent(event); + dptr()->m_shared->removeSeries(series); } /*! - * \internal + * \return list of series added to this graph. + * + * \note The surface graph currently supports only a single series at a time. */ -void Q3DSurface::mousePressEvent(QMouseEvent *event) +QList<QSurface3DSeries *> Q3DSurface::seriesList() { - d_ptr->m_shared->mousePressEvent(event, event->pos()); + return dptr()->m_shared->surfaceSeriesList(); } /*! * \internal */ -void Q3DSurface::mouseReleaseEvent(QMouseEvent *event) +void Q3DSurface::mouseDoubleClickEvent(QMouseEvent *event) { - d_ptr->m_shared->mouseReleaseEvent(event, event->pos()); + dptr()->m_shared->mouseDoubleClickEvent(event); } /*! * \internal */ -void Q3DSurface::mouseMoveEvent(QMouseEvent *event) +void Q3DSurface::touchEvent(QTouchEvent *event) { - d_ptr->m_shared->mouseMoveEvent(event, event->pos()); + dptr()->m_shared->touchEvent(event); } /*! * \internal */ -void Q3DSurface::wheelEvent(QWheelEvent *event) +void Q3DSurface::mousePressEvent(QMouseEvent *event) { - d_ptr->m_shared->wheelEvent(event); + dptr()->m_shared->mousePressEvent(event, event->pos()); } /*! * \internal */ -void Q3DSurface::resizeEvent(QResizeEvent *event) +void Q3DSurface::mouseReleaseEvent(QMouseEvent *event) { - Q_UNUSED(event); - d_ptr->m_shared->setWidth(width()); - d_ptr->m_shared->setHeight(height()); + dptr()->m_shared->mouseReleaseEvent(event, event->pos()); } /*! - * \property Q3DSurface::gridVisible - * - * Sets grid visibility to \a visible. It is preset to \c true by default. + * \internal */ -void Q3DSurface::setGridVisible(bool visible) +void Q3DSurface::mouseMoveEvent(QMouseEvent *event) { - d_ptr->m_shared->setGridEnabled(visible); + dptr()->m_shared->mouseMoveEvent(event, event->pos()); } -bool Q3DSurface::isGridVisible() const +/*! + * \internal + */ +void Q3DSurface::wheelEvent(QWheelEvent *event) { - return d_ptr->m_shared->gridEnabled(); + dptr()->m_shared->wheelEvent(event); } -/*! - * \property Q3DSurface::backgroundVisible - * - * Sets background visibility to \a visible. It is preset to \c true by default. - */ -void Q3DSurface::setBackgroundVisible(bool visible) +Q3DSurfacePrivate *Q3DSurface::dptr() { - d_ptr->m_shared->setBackgroundEnabled(visible); + return static_cast<Q3DSurfacePrivate *>(d_ptr.data()); } -bool Q3DSurface::isBackgroundVisible() const +const Q3DSurfacePrivate *Q3DSurface::dptrc() const { - return d_ptr->m_shared->backgroundEnabled(); + return static_cast<const Q3DSurfacePrivate *>(d_ptr.data()); } /*! * \property Q3DSurface::theme * - * A predefined \a theme from \c QDataVis::Theme. It is preset to \c QDataVis::ThemeQt by - * default. Theme affects label colors, text color, background color, window color and - * grid color. Lighting is also adjusted by themes. - * - * \preliminary + * A \a theme to be used for the graph. Ownership of the \a theme is transferred. Previous theme + * is deleted when a new one is set. Properties of the \a theme can be modified even after setting + * it, and the modifications take effect immediately. */ -void Q3DSurface::setTheme(QDataVis::Theme theme) +void Q3DSurface::setTheme(Q3DTheme *theme) { - d_ptr->m_shared->setTheme(theme); + dptr()->m_shared->setTheme(theme); } -QDataVis::Theme Q3DSurface::theme() const +Q3DTheme *Q3DSurface::theme() const { - return d_ptr->m_shared->theme().theme(); + return dptrc()->m_shared->theme(); } /*! @@ -231,93 +233,29 @@ QDataVis::Theme Q3DSurface::theme() const */ void Q3DSurface::setShadowQuality(QDataVis::ShadowQuality quality) { - return d_ptr->m_shared->setShadowQuality(quality); + return dptr()->m_shared->setShadowQuality(quality); } QDataVis::ShadowQuality Q3DSurface::shadowQuality() const { - return d_ptr->m_shared->shadowQuality(); -} - -/*! - * \property Q3DSurface::smoothSurfaceEnabled - * - * Sets surface smoothing to \a enabled. It is preset to \c false by default. - * When enabled the normals on the surface are interpolated making edges looking round. If turned - * off the normals are kept same on a triangle making the color of the triangle solid. This makes - * the data more readable from the model. - */ -void Q3DSurface::setSmoothSurfaceEnabled(bool enabled) -{ - d_ptr->m_shared->setSmoothSurface(enabled); -} - -bool Q3DSurface::isSmoothSurfaceEnabled() const -{ - return d_ptr->m_shared->smoothSurface(); + return dptrc()->m_shared->shadowQuality(); } /*! * \property Q3DSurface::selectionMode * - * Sets point selection \a mode to one of \c QDataVis::SelectionMode. Surface supports SelectionModeItem, - * SelectionModeSliceRow and SelectionModeSliceColumn. It is preset to \c QDataVis::SelectionModeItem by default. - */ -void Q3DSurface::setSelectionMode(QDataVis::SelectionMode mode) -{ - d_ptr->m_shared->setSelectionMode(mode); -} - -QDataVis::SelectionMode Q3DSurface::selectionMode() const -{ - return d_ptr->m_shared->selectionMode(); -} - - -/*! - * \property Q3DSurface::surfaceGridEnabled - * - * Sets surface grid to \a enabled. It is preset to \c true by default. - */ -void Q3DSurface::setSurfaceGridEnabled(bool enabled) -{ - d_ptr->m_shared->setSurfaceGrid(enabled); -} - -bool Q3DSurface::isSurfaceGridEnabled() const -{ - return d_ptr->m_shared->surfaceGrid(); -} - -/*! - * \property Q3DSurface::gradient - * - * The current surface gradient. Setting this property replaces the previous gradient with - * the given \a gradient. - */ -void Q3DSurface::setGradient(const QLinearGradient &gradient) -{ - d_ptr->m_shared->setGradient(gradient); -} - -QLinearGradient Q3DSurface::gradient() const -{ - return d_ptr->m_shared->gradient(); -} - -/*! - * \property Q3DSurface::font - * - * Sets the \a font for labels. It is preset to \c Arial by default. + * Sets point selection \a mode to a combination of \c QDataVis::SelectionFlags. Surface supports + * \c SelectionItem and \c SelectionSlice with either \c SelectionRow or \c SelectionColumn. + * It is preset to \c SelectionItem by default. */ -void Q3DSurface::setFont(const QFont &font) +void Q3DSurface::setSelectionMode(QDataVis::SelectionFlags mode) { - d_ptr->m_shared->setFont(font); + dptr()->m_shared->setSelectionMode(mode); } -QFont Q3DSurface::font() const +QDataVis::SelectionFlags Q3DSurface::selectionMode() const { - return d_ptr->m_shared->font(); + return dptrc()->m_shared->selectionMode(); } /*! @@ -327,23 +265,7 @@ QFont Q3DSurface::font() const */ Q3DScene *Q3DSurface::scene() const { - return d_ptr->m_shared->scene(); -} - -/*! - * \property Q3DSurface::labelStyle - * - * Sets label \a style to one of \c QDataVis::LabelStyle. It is preset to - * \c QDataVis::LabelStyleFromTheme by default. - */ -void Q3DSurface::setLabelStyle(QDataVis::LabelStyle style) -{ - d_ptr->m_shared->setLabelStyle(style); -} - -QDataVis::LabelStyle Q3DSurface::labelStyle() const -{ - return d_ptr->m_shared->labelStyle(); + return dptrc()->m_shared->scene(); } /*! @@ -358,7 +280,7 @@ QDataVis::LabelStyle Q3DSurface::labelStyle() const */ void Q3DSurface::setAxisX(Q3DValueAxis *axis) { - d_ptr->m_shared->setAxisX(axis); + dptr()->m_shared->setAxisX(axis); } /*! @@ -366,7 +288,7 @@ void Q3DSurface::setAxisX(Q3DValueAxis *axis) */ Q3DValueAxis *Q3DSurface::axisX() const { - return static_cast<Q3DValueAxis *>(d_ptr->m_shared->axisX()); + return static_cast<Q3DValueAxis *>(dptrc()->m_shared->axisX()); } /*! @@ -381,7 +303,7 @@ Q3DValueAxis *Q3DSurface::axisX() const */ void Q3DSurface::setAxisY(Q3DValueAxis *axis) { - d_ptr->m_shared->setAxisY(axis); + dptr()->m_shared->setAxisY(axis); } /*! @@ -389,7 +311,7 @@ void Q3DSurface::setAxisY(Q3DValueAxis *axis) */ Q3DValueAxis *Q3DSurface::axisY() const { - return static_cast<Q3DValueAxis *>(d_ptr->m_shared->axisY()); + return static_cast<Q3DValueAxis *>(dptrc()->m_shared->axisY()); } /*! @@ -404,7 +326,7 @@ Q3DValueAxis *Q3DSurface::axisY() const */ void Q3DSurface::setAxisZ(Q3DValueAxis *axis) { - d_ptr->m_shared->setAxisZ(axis); + dptr()->m_shared->setAxisZ(axis); } /*! @@ -412,7 +334,7 @@ void Q3DSurface::setAxisZ(Q3DValueAxis *axis) */ Q3DValueAxis *Q3DSurface::axisZ() const { - return static_cast<Q3DValueAxis *>(d_ptr->m_shared->axisZ()); + return static_cast<Q3DValueAxis *>(dptrc()->m_shared->axisZ()); } /*! @@ -424,7 +346,7 @@ Q3DValueAxis *Q3DSurface::axisZ() const */ void Q3DSurface::addAxis(Q3DValueAxis *axis) { - d_ptr->m_shared->addAxis(axis); + dptr()->m_shared->addAxis(axis); } /*! @@ -437,7 +359,7 @@ void Q3DSurface::addAxis(Q3DValueAxis *axis) */ void Q3DSurface::releaseAxis(Q3DValueAxis *axis) { - d_ptr->m_shared->releaseAxis(axis); + dptr()->m_shared->releaseAxis(axis); } /*! @@ -447,7 +369,7 @@ void Q3DSurface::releaseAxis(Q3DValueAxis *axis) */ QList<Q3DValueAxis *> Q3DSurface::axes() const { - QList<Q3DAbstractAxis *> abstractAxes = d_ptr->m_shared->axes(); + QList<Q3DAbstractAxis *> abstractAxes = dptrc()->m_shared->axes(); QList<Q3DValueAxis *> retList; foreach (Q3DAbstractAxis *axis, abstractAxes) retList.append(static_cast<Q3DValueAxis *>(axis)); @@ -455,82 +377,10 @@ QList<Q3DValueAxis *> Q3DSurface::axes() const return retList; } -/*! - * Sets the active data \a proxy. Implicitly calls addDataProxy() to transfer ownership of - * the \a proxy to this graph. - * - * If the \a proxy is null, a temporary default proxy is created and activated. - * This temporary proxy is destroyed if another \a proxy is set explicitly active via this method. - * - * \sa addDataProxy(), releaseDataProxy() - */ -void Q3DSurface::setActiveDataProxy(QSurfaceDataProxy *proxy) -{ - d_ptr->m_shared->setActiveDataProxy(proxy); -} - -/*! - * \return active data proxy. - */ -QSurfaceDataProxy *Q3DSurface::activeDataProxy() const -{ - return static_cast<QSurfaceDataProxy *>(d_ptr->m_shared->activeDataProxy()); -} - -/*! - * Adds data \a proxy to the graph. The proxies added via addDataProxy are not yet taken to use, - * addDataProxy is simply used to give the ownership of the data \a proxy to the graph. - * The \a proxy must not be null or added to another graph. - * - * \sa releaseDataProxy(), setActiveDataProxy() - */ -void Q3DSurface::addDataProxy(QSurfaceDataProxy *proxy) -{ - d_ptr->m_shared->addDataProxy(proxy); -} - -/*! - * Releases the ownership of the data \a proxy back to the caller, if it is added to this graph. - * If the released \a proxy is in use, a new empty default proxy is created and taken to use. - * - * If the default \a proxy is released and added back later, it behaves as any other proxy would. - * - * \sa addDataProxy(), setActiveDataProxy() - */ -void Q3DSurface::releaseDataProxy(QSurfaceDataProxy *proxy) -{ - d_ptr->m_shared->releaseDataProxy(proxy); -} - -/*! - * \return list of all added data proxies. - * - * \sa addDataProxy() - */ -QList<QSurfaceDataProxy *> Q3DSurface::dataProxies() const -{ - QList<QSurfaceDataProxy *> retList; - QList<QAbstractDataProxy *> abstractList = d_ptr->m_shared->dataProxies(); - foreach (QAbstractDataProxy *proxy, abstractList) - retList.append(static_cast<QSurfaceDataProxy *>(proxy)); - - return retList; -} - - -/*! - * Modifies the current surface gradient. Sets gradient color to \a color at \a pos. - */ -void Q3DSurface::setGradientColorAt(qreal pos, const QColor &color) -{ - d_ptr->m_shared->setGradientColorAt(pos, color); -} - /////////////////// PRIVATE /////////////////////////////////// -Q3DSurfacePrivate::Q3DSurfacePrivate(Q3DSurface *q, QRect rect) - : q_ptr(q), - m_shared(new Surface3DController(rect)) +Q3DSurfacePrivate::Q3DSurfacePrivate(Q3DSurface *q) + : Q3DWindowPrivate(q) { } @@ -539,4 +389,9 @@ Q3DSurfacePrivate::~Q3DSurfacePrivate() delete m_shared; } +Q3DSurface *Q3DSurfacePrivate::qptr() +{ + return static_cast<Q3DSurface *>(q_ptr); +} + QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/q3dsurface.h b/src/datavisualization/engine/q3dsurface.h index 1b572a36..bdbe65f5 100644 --- a/src/datavisualization/engine/q3dsurface.h +++ b/src/datavisualization/engine/q3dsurface.h @@ -23,61 +23,38 @@ #include <QtDataVisualization/q3dwindow.h> #include <QtDataVisualization/q3dscene.h> #include <QFont> -#include <QLinearGradient> QT_DATAVISUALIZATION_BEGIN_NAMESPACE class Q3DSurfacePrivate; class Q3DValueAxis; -class QSurfaceDataProxy; +class QSurface3DSeries; +class Q3DTheme; class QT_DATAVISUALIZATION_EXPORT Q3DSurface : public Q3DWindow { Q_OBJECT - Q_PROPERTY(QtDataVisualization::QDataVis::SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) - Q_PROPERTY(QtDataVisualization::QDataVis::LabelStyle labelStyle READ labelStyle WRITE setLabelStyle) - Q_PROPERTY(QtDataVisualization::QDataVis::Theme theme READ theme WRITE setTheme) - Q_PROPERTY(QtDataVisualization::QDataVis::ShadowQuality shadowQuality READ shadowQuality WRITE setShadowQuality) - Q_PROPERTY(bool gridVisible READ isGridVisible WRITE setGridVisible) - Q_PROPERTY(bool backgroundVisible READ isBackgroundVisible WRITE setBackgroundVisible) - Q_PROPERTY(bool smoothSurfaceEnabled READ isSmoothSurfaceEnabled WRITE setSmoothSurfaceEnabled) - Q_PROPERTY(bool surfaceGridEnabled READ isSurfaceGridEnabled WRITE setSurfaceGridEnabled) - Q_PROPERTY(QLinearGradient gradient READ gradient WRITE setGradient) - Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(QtDataVisualization::QDataVis::SelectionFlags selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged) + Q_PROPERTY(Q3DTheme* theme READ theme WRITE setTheme NOTIFY themeChanged) + Q_PROPERTY(QtDataVisualization::QDataVis::ShadowQuality shadowQuality READ shadowQuality WRITE setShadowQuality NOTIFY shadowQualityChanged) Q_PROPERTY(Q3DScene* scene READ scene) - Q_ENUMS(QtDataVisualization::QDataVis::SelectionMode) - Q_ENUMS(QtDataVisualization::QDataVis::ShadowQuality) - Q_ENUMS(QtDataVisualization::QDataVis::LabelStyle) - Q_ENUMS(QtDataVisualization::QDataVis::CameraPreset) public: - explicit Q3DSurface(); - ~Q3DSurface(); + explicit Q3DSurface(QWindow *parent = 0); + virtual ~Q3DSurface(); - void setGridVisible(bool visible); - bool isGridVisible() const; + void addSeries(QSurface3DSeries *series); + void removeSeries(QSurface3DSeries *series); + QList<QSurface3DSeries *> seriesList(); - void setBackgroundVisible(bool visible); - bool isBackgroundVisible() const; - - void setTheme(QDataVis::Theme theme); - QDataVis::Theme theme() const; + void setTheme(Q3DTheme *theme); + Q3DTheme *theme() const; void setShadowQuality(QDataVis::ShadowQuality quality); QDataVis::ShadowQuality shadowQuality() const; - void setSmoothSurfaceEnabled(bool enabled); - bool isSmoothSurfaceEnabled() const; - - void setSelectionMode(QDataVis::SelectionMode mode); - QDataVis::SelectionMode selectionMode() const; - void setSurfaceGridEnabled(bool enabled); - bool isSurfaceGridEnabled() const; - - void setGradient(const QLinearGradient &gradient); - QLinearGradient gradient() const; - - void setGradientColorAt(qreal pos, const QColor &color); + void setSelectionMode(QDataVis::SelectionFlags mode); + QDataVis::SelectionFlags selectionMode() const; // Axes void setAxisX(Q3DValueAxis *axis); @@ -90,19 +67,12 @@ public: void releaseAxis(Q3DValueAxis *axis); QList<Q3DValueAxis *> axes() const; - void setActiveDataProxy(QSurfaceDataProxy *proxy); - QSurfaceDataProxy *activeDataProxy() const; - void addDataProxy(QSurfaceDataProxy *proxy); - void releaseDataProxy(QSurfaceDataProxy *proxy); - QList<QSurfaceDataProxy *> dataProxies() const; - - void setFont(const QFont &font); - QFont font() const; - Q3DScene *scene() const; - void setLabelStyle(QDataVis::LabelStyle style); - QDataVis::LabelStyle labelStyle() const; +signals: + void selectionModeChanged(QDataVis::SelectionFlags mode); + void themeChanged(Q3DTheme *theme); + void shadowQualityChanged(QDataVis::ShadowQuality quality); protected: void mouseDoubleClickEvent(QMouseEvent *event); @@ -111,10 +81,10 @@ protected: void mouseReleaseEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void wheelEvent(QWheelEvent *event); - void resizeEvent(QResizeEvent *event); private: - QScopedPointer<Q3DSurfacePrivate> d_ptr; + Q3DSurfacePrivate *dptr(); + const Q3DSurfacePrivate *dptrc() const; Q_DISABLE_COPY(Q3DSurface) }; diff --git a/src/datavisualization/engine/q3dsurface_p.h b/src/datavisualization/engine/q3dsurface_p.h index 7c70d08c..de2a518c 100644 --- a/src/datavisualization/engine/q3dsurface_p.h +++ b/src/datavisualization/engine/q3dsurface_p.h @@ -31,6 +31,7 @@ #include "surface3dcontroller_p.h" #include "qdatavisualizationenums.h" +#include "q3dwindow_p.h" #include <QList> @@ -38,13 +39,15 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE class Q3DSurface; -class Q3DSurfacePrivate : public QObject +class Q3DSurfacePrivate : public Q3DWindowPrivate { + Q_OBJECT public: - Q3DSurfacePrivate(Q3DSurface *q, QRect rect); + Q3DSurfacePrivate(Q3DSurface *q); ~Q3DSurfacePrivate(); - Q3DSurface *q_ptr; + Q3DSurface *qptr(); + Surface3DController *m_shared; }; diff --git a/src/datavisualization/engine/q3dwindow.cpp b/src/datavisualization/engine/q3dwindow.cpp index 2c0729ba..5285b585 100644 --- a/src/datavisualization/engine/q3dwindow.cpp +++ b/src/datavisualization/engine/q3dwindow.cpp @@ -19,13 +19,14 @@ #include "q3dwindow.h" #include "q3dwindow_p.h" #include "abstract3dcontroller_p.h" -#include <QGuiApplication> +#include "qabstract3dinputhandler_p.h" +#include "q3dscene_p.h" +#include <QGuiApplication> #include <QOpenGLContext> #include <QOpenGLPaintDevice> #include <QPainter> -#include <QDebug> QT_DATAVISUALIZATION_BEGIN_NAMESPACE @@ -36,19 +37,31 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * \since Qt Data Visualization 1.0 * * This class creates a QWindow and provides render loop for visualization types inheriting it. - * \warning This class is not intended to be used directly by developers. + * + * You should not need to use this class directly, but one of its subclasses instead. + * + * \note Q3DWindow sets window flag \c{Qt::FramelessWindowHint} on by default. If you want to display + * graph windows as standalone windows with regular window frame, clear this flag after constructing + * the graph. For example: + * + * \code + * Q3DBars *graphWindow = new Q3DBars; + * graphWindow->setFlags(graphWindow->flags() ^ Qt::FramelessWindowHint); + * \endcode * * \sa Q3DBars, Q3DScatter, Q3DSurface, {Qt Data Visualization C++ Classes} */ /*! - * Constructs Q3DWindow with \a parent. It creates a QWindow and an OpenGL context. It also sets - * surface format and initializes OpenGL functions for use. + * \internal */ -Q3DWindow::Q3DWindow(QWindow *parent) +Q3DWindow::Q3DWindow(Q3DWindowPrivate *d, QWindow *parent) : QWindow(parent), - d_ptr(new Q3DWindowPrivate(this)) + d_ptr(d) { + d_ptr->m_context = new QOpenGLContext(this); + + setFlags(flags() | Qt::FramelessWindowHint); setSurfaceType(QWindow::OpenGLSurface); QSurfaceFormat surfaceFormat; surfaceFormat.setDepthBufferSize(24); @@ -67,7 +80,6 @@ Q3DWindow::Q3DWindow(QWindow *parent) d_ptr->m_context->create(); d_ptr->m_context->makeCurrent(this); - qDebug() << "initializeOpenGLFunctions()"; initializeOpenGLFunctions(); const GLubyte *version = glGetString(GL_VERSION); @@ -80,7 +92,7 @@ Q3DWindow::Q3DWindow(QWindow *parent) if (splitversionstr[0].toFloat() < 1.2) qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers."); #endif - renderLater(); + d_ptr->renderLater(); } /*! @@ -91,46 +103,49 @@ Q3DWindow::~Q3DWindow() } /*! - * \internal + * Adds the given \a inputHandler to the graph. The input handlers added via addInputHandler + * are not taken in to use directly. Only the ownership of the a\ inputHandler is given to the graph. + * The \a inputHandler must not be null or already added to another graph. + * + * \sa releaseInputHandler(), setActiveInputHandler() */ -void Q3DWindow::setVisualController(Abstract3DController *controller) +void Q3DWindow::addInputHandler(QAbstract3DInputHandler *inputHandler) { - d_ptr->m_visualController = controller; + d_ptr->m_visualController->addInputHandler(inputHandler); } /*! - * \internal + * Releases the ownership of the \a inputHandler back to the caller, if it was added to this graph. + * If the released \a inputHandler is in use there will be no input handler active after this call. + * + * If the default input handler is released and added back later, it behaves as any other input handler would. + * + * \sa addInputHandler(), setActiveInputHandler() */ -void Q3DWindow::handleDevicePixelRatioChange() +void Q3DWindow::releaseInputHandler(QAbstract3DInputHandler *inputHandler) { - if (QWindow::devicePixelRatio() == d_ptr->m_devicePixelRatio || !d_ptr->m_visualController) - return; - - // Device pixel ratio changed, resize accordingly and inform the scene - d_ptr->m_devicePixelRatio = QWindow::devicePixelRatio(); - d_ptr->m_visualController->updateDevicePixelRatio(d_ptr->m_devicePixelRatio); - + d_ptr->m_visualController->releaseInputHandler(inputHandler); } /*! - * \internal + * Sets the active \a inputHandler. Implicitly calls addInputHandler() to transfer ownership of + * the \a inputHandler to this graph. + * + * If the \a inputHandler is null, no input handler will be active after this call. + * + * \sa addInputHandler(), releaseInputHandler() */ -void Q3DWindow::render() +void Q3DWindow::setActiveInputHandler(QAbstract3DInputHandler *inputHandler) { - handleDevicePixelRatioChange(); - d_ptr->m_visualController->synchDataToRenderer(); - d_ptr->m_visualController->render(); + d_ptr->m_visualController->setActiveInputHandler(inputHandler); } /*! - * \internal + * \return currently active input handler. */ -void Q3DWindow::renderLater() +QAbstract3DInputHandler *Q3DWindow::activeInputHandler() { - if (!d_ptr->m_updatePending) { - d_ptr->m_updatePending = true; - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); - } + return d_ptr->m_visualController->activeInputHandler(); } /*! @@ -140,7 +155,7 @@ bool Q3DWindow::event(QEvent *event) { switch (event->type()) { case QEvent::UpdateRequest: - renderNow(); + d_ptr->renderNow(); return true; case QEvent::TouchBegin: case QEvent::TouchCancel: @@ -156,35 +171,30 @@ bool Q3DWindow::event(QEvent *event) /*! * \internal */ -void Q3DWindow::exposeEvent(QExposeEvent *event) +void Q3DWindow::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); - if (isExposed()) - renderNow(); + Q3DScene *scene = d_ptr->m_visualController->scene(); + scene->d_ptr->setWindowSize(QSize(width(), height())); + scene->d_ptr->setViewport(QRect(0, 0, width(), height())); } /*! * \internal */ -void Q3DWindow::renderNow() +void Q3DWindow::exposeEvent(QExposeEvent *event) { - if (!isExposed()) - return; - - d_ptr->m_updatePending = false; - - d_ptr->m_context->makeCurrent(this); - - render(); + Q_UNUSED(event); - d_ptr->m_context->swapBuffers(this); + if (isExposed()) + d_ptr->renderNow(); } Q3DWindowPrivate::Q3DWindowPrivate(Q3DWindow *q) - : q_ptr(q), + : QObject(0), + q_ptr(q), m_updatePending(false), - m_context(new QOpenGLContext(q)), m_visualController(0), m_devicePixelRatio(1.f) { @@ -194,4 +204,47 @@ Q3DWindowPrivate::~Q3DWindowPrivate() { } +void Q3DWindowPrivate::render() +{ + handleDevicePixelRatioChange(); + m_visualController->synchDataToRenderer(); + m_visualController->render(); +} + +void Q3DWindowPrivate::renderLater() +{ + if (!m_updatePending) { + m_updatePending = true; + QCoreApplication::postEvent(q_ptr, new QEvent(QEvent::UpdateRequest)); + } +} + +void Q3DWindowPrivate::renderNow() +{ + if (!q_ptr->isExposed()) + return; + + m_updatePending = false; + + m_context->makeCurrent(q_ptr); + + render(); + + m_context->swapBuffers(q_ptr); +} + +void Q3DWindowPrivate::setVisualController(Abstract3DController *controller) +{ + m_visualController = controller; +} + +void Q3DWindowPrivate::handleDevicePixelRatioChange() +{ + if (q_ptr->devicePixelRatio() == m_devicePixelRatio || !m_visualController) + return; + + m_devicePixelRatio = q_ptr->devicePixelRatio(); + m_visualController->scene()->setDevicePixelRatio(m_devicePixelRatio); +} + QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/q3dwindow.h b/src/datavisualization/engine/q3dwindow.h index 1848ff29..cdffc6b9 100644 --- a/src/datavisualization/engine/q3dwindow.h +++ b/src/datavisualization/engine/q3dwindow.h @@ -31,26 +31,26 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE class Q3DWindowPrivate; class Abstract3DController; +class QAbstract3DInputHandler; class QT_DATAVISUALIZATION_EXPORT Q3DWindow : public QWindow, protected QOpenGLFunctions { Q_OBJECT +protected: + explicit Q3DWindow(Q3DWindowPrivate *d, QWindow *parent = 0); public: - explicit Q3DWindow(QWindow *parent = 0); virtual ~Q3DWindow(); -protected slots: - void renderLater(); - void renderNow(); + void addInputHandler(QAbstract3DInputHandler *inputHandler); + void releaseInputHandler(QAbstract3DInputHandler *inputHandler); + void setActiveInputHandler(QAbstract3DInputHandler *inputHandler); + QAbstract3DInputHandler *activeInputHandler(); protected: - virtual void render(); - bool event(QEvent *event); + void resizeEvent(QResizeEvent *event); void exposeEvent(QExposeEvent *event); - void setVisualController(Abstract3DController *controller); - void handleDevicePixelRatioChange(); private: QScopedPointer<Q3DWindowPrivate> d_ptr; diff --git a/src/datavisualization/engine/q3dwindow_p.h b/src/datavisualization/engine/q3dwindow_p.h index 6bef7e10..ba8c67da 100644 --- a/src/datavisualization/engine/q3dwindow_p.h +++ b/src/datavisualization/engine/q3dwindow_p.h @@ -26,11 +26,13 @@ // // We mean it. -#ifndef Q3DWINDOW_p_H -#define Q3DWINDOW_p_H +#ifndef Q3DWINDOW_P_H +#define Q3DWINDOW_P_H #include "datavisualizationglobal_p.h" +#include <QObject> + class QOpenGLContext; class QOpenGLPaintDevice; @@ -39,19 +41,29 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE class Q3DWindow; class Abstract3DController; -class Q3DWindowPrivate +class Q3DWindowPrivate : public QObject { + Q_OBJECT public: Q3DWindowPrivate(Q3DWindow *q); ~Q3DWindowPrivate(); + void render(); + + void setVisualController(Abstract3DController *controller); + void handleDevicePixelRatioChange(); + +public slots: + void renderLater(); + void renderNow(); + public: Q3DWindow *q_ptr; bool m_updatePending; QOpenGLContext *m_context; Abstract3DController *m_visualController; - qreal m_devicePixelRatio; + float m_devicePixelRatio; }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/scatter3dcontroller.cpp b/src/datavisualization/engine/scatter3dcontroller.cpp index 9f43d94e..0a506f19 100644 --- a/src/datavisualization/engine/scatter3dcontroller.cpp +++ b/src/datavisualization/engine/scatter3dcontroller.cpp @@ -22,6 +22,7 @@ #include "q3dabstractaxis_p.h" #include "q3dvalueaxis_p.h" #include "qscatterdataproxy_p.h" +#include "qscatter3dseries_p.h" #include <QMatrix4x4> #include <qmath.h> @@ -31,13 +32,9 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE Scatter3DController::Scatter3DController(QRect boundRect) : Abstract3DController(boundRect), m_renderer(0), - m_selectedItemIndex(noSelectionIndex()) + m_selectedItem(invalidSelectionIndex()), + m_selectedItemSeries(0) { - // Default object type; specific to scatter - setObjectType(QDataVis::MeshStyleSpheres, false); - - setActiveDataProxy(new QScatterDataProxy); - // Setting a null axis creates a new default axis according to orientation and graph type. // Note: These cannot be set in Abstract3DController constructor, as they will call virtual // functions implemented by subclasses. @@ -60,8 +57,8 @@ void Scatter3DController::initializeOpenGL() setRenderer(m_renderer); synchDataToRenderer(); - QObject::connect(m_renderer, &Scatter3DRenderer::selectedItemIndexChanged, this, - &Scatter3DController::handleSelectedItemIndexChanged, Qt::QueuedConnection); + QObject::connect(m_renderer, &Scatter3DRenderer::itemClicked, this, + &Scatter3DController::handleItemClicked, Qt::QueuedConnection); emitNeedRender(); } @@ -73,61 +70,59 @@ void Scatter3DController::synchDataToRenderer() return; // Notify changes to renderer - if (m_changeTracker.slicingActiveChanged) { - // TODO: Add notification. - m_changeTracker.slicingActiveChanged = false; + if (m_changeTracker.selectedItemChanged) { + m_renderer->updateSelectedItem(m_selectedItem, m_selectedItemSeries); + m_changeTracker.selectedItemChanged = false; } +} - if (m_changeTracker.selectedItemIndexChanged) { - m_renderer->updateSelectedItemIndex(m_selectedItemIndex); - m_changeTracker.selectedItemIndexChanged = false; - } +void Scatter3DController::addSeries(QAbstract3DSeries *series) +{ + Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesTypeScatter); - if (m_isDataDirty) { - m_renderer->updateDataModel(static_cast<QScatterDataProxy *>(m_data)); - m_isDataDirty = false; - } -} + bool firstAdded = !m_seriesList.size(); + + Abstract3DController::addSeries(series); + if (firstAdded) + adjustValueAxisRange(); + + QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(series); + if (scatterSeries->selectedItem() != invalidSelectionIndex()) + setSelectedItem(scatterSeries->selectedItem(), scatterSeries); +} -void Scatter3DController::setActiveDataProxy(QAbstractDataProxy *proxy) +void Scatter3DController::removeSeries(QAbstract3DSeries *series) { - // Setting null proxy indicates default proxy - if (!proxy) { - proxy = new QScatterDataProxy; - proxy->d_ptr->setDefaultProxy(true); - } + bool firstRemoved = (m_seriesList.size() && m_seriesList.at(0) == series); - Q_ASSERT(proxy->type() == QAbstractDataProxy::DataTypeScatter); + Abstract3DController::removeSeries(series); - Abstract3DController::setActiveDataProxy(proxy); + if (m_selectedItemSeries == series) + setSelectedItem(invalidSelectionIndex(), 0); - QScatterDataProxy *scatterDataProxy = static_cast<QScatterDataProxy *>(m_data); + if (firstRemoved) + adjustValueAxisRange(); +} - QObject::connect(scatterDataProxy, &QScatterDataProxy::arrayReset, - this, &Scatter3DController::handleArrayReset); - QObject::connect(scatterDataProxy, &QScatterDataProxy::itemsAdded, - this, &Scatter3DController::handleItemsAdded); - QObject::connect(scatterDataProxy, &QScatterDataProxy::itemsChanged, - this, &Scatter3DController::handleItemsChanged); - QObject::connect(scatterDataProxy, &QScatterDataProxy::itemsRemoved, - this, &Scatter3DController::handleItemsRemoved); - QObject::connect(scatterDataProxy, &QScatterDataProxy::itemsInserted, - this, &Scatter3DController::handleItemsInserted); +QList<QScatter3DSeries *> Scatter3DController::scatterSeriesList() +{ + QList<QAbstract3DSeries *> abstractSeriesList = seriesList(); + QList<QScatter3DSeries *> scatterSeriesList; + foreach (QAbstract3DSeries *abstractSeries, abstractSeriesList) { + QScatter3DSeries *scatterSeries = qobject_cast<QScatter3DSeries *>(abstractSeries); + if (scatterSeries) + scatterSeriesList.append(scatterSeries); + } - adjustValueAxisRange(); - setSelectedItemIndex(noSelectionIndex()); - setSlicingActive(false); - m_isDataDirty = true; - emitNeedRender(); + return scatterSeriesList; } void Scatter3DController::handleArrayReset() { - setSlicingActive(false); adjustValueAxisRange(); m_isDataDirty = true; - setSelectedItemIndex(noSelectionIndex()); + setSelectedItem(m_selectedItem, m_selectedItemSeries); emitNeedRender(); } @@ -158,8 +153,10 @@ void Scatter3DController::handleItemsRemoved(int startIndex, int count) // TODO should dirty only affected values? adjustValueAxisRange(); m_isDataDirty = true; - if (startIndex >= static_cast<QScatterDataProxy *>(m_data)->itemCount()) - setSelectedItemIndex(noSelectionIndex()); + + // Clear selection unless it is still valid + setSelectedItem(m_selectedItem, m_selectedItemSeries); + emitNeedRender(); } @@ -173,13 +170,12 @@ void Scatter3DController::handleItemsInserted(int startIndex, int count) emitNeedRender(); } -void Scatter3DController::handleSelectedItemIndexChanged(int index) +void Scatter3DController::handleItemClicked(int index, QScatter3DSeries *series) { - if (index != m_selectedItemIndex) { - m_selectedItemIndex = index; - emit selectedItemIndexChanged(index); - emitNeedRender(); - } + setSelectedItem(index, series); + + // TODO: pass clicked to parent. (QTRD-2517) + // TODO: Also hover needed? (QTRD-2131) } void Scatter3DController::handleAxisAutoAdjustRangeChangedInOrientation( @@ -190,80 +186,163 @@ void Scatter3DController::handleAxisAutoAdjustRangeChangedInOrientation( adjustValueAxisRange(); } -void Scatter3DController::setObjectType(QDataVis::MeshStyle style, bool smooth) +void Scatter3DController::handleAxisRangeChangedBySender(QObject *sender) { - QString objFile; - if (style == QDataVis::MeshStyleSpheres) { - if (smooth) - objFile = QStringLiteral(":/defaultMeshes/sphereSmooth"); - else - objFile = QStringLiteral(":/defaultMeshes/sphere"); - } else { - if (smooth) - objFile = QStringLiteral(":/defaultMeshes/dotSmooth"); - else - objFile = QStringLiteral(":/defaultMeshes/dot"); - } - Abstract3DController::setMeshFileName(objFile); + Abstract3DController::handleAxisRangeChangedBySender(sender); + + // Update selected index - may be moved offscreen + setSelectedItem(m_selectedItem, m_selectedItemSeries); } -void Scatter3DController::setSelectionMode(QDataVis::SelectionMode mode) +void Scatter3DController::setSelectionMode(QDataVis::SelectionFlags mode) { - if (mode > QDataVis::SelectionModeItem) { - qWarning("Unsupported selection mode."); + // We only support single item selection mode and no selection mode + if (mode != QDataVis::SelectionItem && mode != QDataVis::SelectionNone) { + qWarning("Unsupported selection mode - only none and item selection modes are supported."); return; } - // Disable zoom if selection mode changes - setSlicingActive(false); + Abstract3DController::setSelectionMode(mode); } -void Scatter3DController::setSelectedItemIndex(int index) +void Scatter3DController::setSelectedItem(int index, QScatter3DSeries *series) { - // TODO If items not within axis ranges are culled from drawing, should they be - // TODO unselectable as well? - if (index < 0 || index >= static_cast<QScatterDataProxy *>(m_data)->itemCount()) - index = noSelectionIndex(); - - if (index != m_selectedItemIndex) { - m_selectedItemIndex = index; - m_changeTracker.selectedItemIndexChanged = true; - emit selectedItemIndexChanged(index); + const QScatterDataProxy *proxy = 0; + + // Series may already have been removed, so check it before setting the selection. + if (!m_seriesList.contains(series)) + series = 0; + + if (series) + proxy = series->dataProxy(); + + if (!proxy || index < 0 || index >= proxy->itemCount()) + index = invalidSelectionIndex(); + + if (index != m_selectedItem || series != m_selectedItemSeries) { + m_selectedItem = index; + m_selectedItemSeries = series; + m_changeTracker.selectedItemChanged = true; + + // Clear selection from other series and finally set new selection to the specified series + foreach (QAbstract3DSeries *otherSeries, m_seriesList) { + QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(otherSeries); + if (scatterSeries != m_selectedItemSeries) + scatterSeries->dptr()->setSelectedItem(invalidSelectionIndex()); + } + if (m_selectedItemSeries) + m_selectedItemSeries->dptr()->setSelectedItem(m_selectedItem); + emitNeedRender(); } } -int Scatter3DController::selectedItemIndex() const -{ - return m_selectedItemIndex; -} - void Scatter3DController::adjustValueAxisRange() { - if (m_data) { - QVector3D limits = static_cast<QScatterDataProxy *>(m_data)->dptr()->limitValues(); - Q3DValueAxis *valueAxis = static_cast<Q3DValueAxis *>(m_axisX); - if (valueAxis && valueAxis->isAutoAdjustRange()) { - if (limits.x() > 0) - valueAxis->dptr()->setRange(-limits.x(), limits.x()); - else - valueAxis->dptr()->setRange(-1.0, 1.0); // Only zero value values in data set, set range to default. + Q3DValueAxis *valueAxisX = static_cast<Q3DValueAxis *>(m_axisX); + Q3DValueAxis *valueAxisY = static_cast<Q3DValueAxis *>(m_axisY); + Q3DValueAxis *valueAxisZ = static_cast<Q3DValueAxis *>(m_axisZ); + bool adjustX = (valueAxisX && valueAxisX->isAutoAdjustRange()); + bool adjustY = (valueAxisY && valueAxisY->isAutoAdjustRange()); + bool adjustZ = (valueAxisZ && valueAxisZ->isAutoAdjustRange()); + + if (adjustX || adjustY || adjustZ) { + float minValueX = 0.0f; + float maxValueX = 0.0f; + float minValueY = 0.0f; + float maxValueY = 0.0f; + float minValueZ = 0.0f; + float maxValueZ = 0.0f; + int seriesCount = m_seriesList.size(); + for (int series = 0; series < seriesCount; series++) { + const QScatter3DSeries *scatterSeries = + static_cast<QScatter3DSeries *>(m_seriesList.at(series)); + const QScatterDataProxy *proxy = scatterSeries->dataProxy(); + if (scatterSeries->isVisible() && proxy) { + QVector3D minLimits; + QVector3D maxLimits; + proxy->dptrc()->limitValues(minLimits, maxLimits); + if (adjustX) { + if (!series) { + // First series initializes the values + minValueX = minLimits.x(); + maxValueX = maxLimits.x(); + } else { + minValueX = qMin(minValueX, minLimits.x()); + maxValueX = qMax(maxValueX, maxLimits.x()); + } + } + if (adjustY) { + if (!series) { + // First series initializes the values + minValueY = minLimits.y(); + maxValueY = maxLimits.y(); + } else { + minValueY = qMin(minValueY, minLimits.y()); + maxValueY = qMax(maxValueY, maxLimits.y()); + } + } + if (adjustZ) { + if (!series) { + // First series initializes the values + minValueZ = minLimits.z(); + maxValueZ = maxLimits.z(); + } else { + minValueZ = qMin(minValueZ, minLimits.z()); + maxValueZ = qMax(maxValueZ, maxLimits.z()); + } + } + } } - valueAxis = static_cast<Q3DValueAxis *>(m_axisY); - if (valueAxis && valueAxis->isAutoAdjustRange()) { - if (limits.y() > 0) - valueAxis->dptr()->setRange(-limits.y(), limits.y()); - else - valueAxis->dptr()->setRange(-1.0, 1.0); // Only zero value values in data set, set range to default. + static const float adjustmentRatio = 20.0f; + static const float defaultAdjustment = 1.0f; + + if (adjustX) { + // If all points at same coordinate, need to default to some valid range + float adjustment = 0.0f; + if (minValueX == maxValueX) { + if (adjustZ) { + // X and Z are linked to have similar unit size, so choose the valid range based on it + if (minValueZ == maxValueZ) + adjustment = defaultAdjustment; + else + adjustment = qAbs(maxValueZ - minValueZ) / adjustmentRatio; + } else { + if (valueAxisZ) + adjustment = qAbs(valueAxisZ->max() - valueAxisZ->min()) / adjustmentRatio; + else + adjustment = defaultAdjustment; + } + } + valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment); } - - valueAxis = static_cast<Q3DValueAxis *>(m_axisZ); - if (valueAxis && valueAxis->isAutoAdjustRange()) { - if (limits.z() > 0) - valueAxis->dptr()->setRange(-limits.z(), limits.z()); - else - valueAxis->dptr()->setRange(-1.0, 1.0); // Only zero value values in data set, set range to default. + if (adjustY) { + // If all points at same coordinate, need to default to some valid range + // Y-axis unit is not dependent on other axes, so simply adjust +-1.0f + float adjustment = 0.0f; + if (minValueY == maxValueY) + adjustment = defaultAdjustment; + valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment); + } + if (adjustZ) { + // If all points at same coordinate, need to default to some valid range + float adjustment = 0.0f; + if (minValueZ == maxValueZ) { + if (adjustX) { + // X and Z are linked to have similar unit size, so choose the valid range based on it + if (minValueX == maxValueX) + adjustment = defaultAdjustment; + else + adjustment = qAbs(maxValueX - minValueX) / adjustmentRatio; + } else { + if (valueAxisX) + adjustment = qAbs(valueAxisX->max() - valueAxisX->min()) / adjustmentRatio; + else + adjustment = defaultAdjustment; + } + } + valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment); } } } diff --git a/src/datavisualization/engine/scatter3dcontroller_p.h b/src/datavisualization/engine/scatter3dcontroller_p.h index 63735aca..00c87d4f 100644 --- a/src/datavisualization/engine/scatter3dcontroller_p.h +++ b/src/datavisualization/engine/scatter3dcontroller_p.h @@ -38,14 +38,13 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE class Scatter3DRenderer; class QScatterDataProxy; +class QScatter3DSeries; struct Scatter3DChangeBitField { - bool slicingActiveChanged : 1; - bool selectedItemIndexChanged : 1; + bool selectedItemChanged : 1; Scatter3DChangeBitField() : - slicingActiveChanged(true), - selectedItemIndexChanged(true) + selectedItemChanged(true) { } }; @@ -59,29 +58,30 @@ private: // Rendering Scatter3DRenderer *m_renderer; - int m_selectedItemIndex; + int m_selectedItem; + QScatter3DSeries *m_selectedItemSeries; // Points to the series for which the bar is selected + // in single series selection cases. public: explicit Scatter3DController(QRect rect); ~Scatter3DController(); - void initializeOpenGL(); - - // Object type - void setObjectType(QDataVis::MeshStyle style, bool smooth = false); + virtual void initializeOpenGL(); // Change selection mode - void setSelectionMode(QDataVis::SelectionMode mode); - - void setSelectedItemIndex(int index); - int selectedItemIndex() const; - static inline int noSelectionIndex() { return -1; } + void setSelectionMode(QDataVis::SelectionFlags mode); - virtual void setActiveDataProxy(QAbstractDataProxy *proxy); + void setSelectedItem(int index, QScatter3DSeries *series); + static inline int invalidSelectionIndex() { return -1; } void synchDataToRenderer(); + virtual void addSeries(QAbstract3DSeries *series); + virtual void removeSeries(QAbstract3DSeries *series); + virtual QList<QScatter3DSeries *> scatterSeriesList(); + virtual void handleAxisAutoAdjustRangeChangedInOrientation(Q3DAbstractAxis::AxisOrientation orientation, bool autoAdjust); + virtual void handleAxisRangeChangedBySender(QObject *sender); public slots: void handleArrayReset(); @@ -89,10 +89,9 @@ public slots: void handleItemsChanged(int startIndex, int count); void handleItemsRemoved(int startIndex, int count); void handleItemsInserted(int startIndex, int count); - void handleSelectedItemIndexChanged(int index); -signals: - void selectedItemIndexChanged(int index); + // Renderer callback handlers + void handleItemClicked(int index, QScatter3DSeries *series); private: void adjustValueAxisRange(); diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index a457b545..00452cf7 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -25,6 +25,7 @@ #include "texturehelper_p.h" #include "utils_p.h" #include "q3dlight.h" +#include "qscatter3dseries_p.h" #include <QMatrix4x4> #include <QMouseEvent> @@ -46,25 +47,28 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE const GLfloat aspectRatio = 2.0f; // Forced ratio of x and z to y. Dynamic will make it look odd. const GLfloat labelMargin = 0.05f; -// TODO: Make margin modifiable? -const GLfloat backgroundMargin = 1.1f; // Margin for background (1.1f = make it 10% larger to avoid items being drawn inside background) +const GLfloat defaultMinSize = 0.01f; +const GLfloat defaultMaxSize = 0.1f; +const GLfloat defaultMargin = 1.0f + defaultMaxSize; // Default margin for background +const GLfloat itemScaler = 3.0f; const GLfloat gridLineWidth = 0.005f; -static QVector3D selectionSkipColor = QVector3D(255, 255, 255); // Selection texture's background color Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) : Abstract3DRenderer(controller), - m_controller(controller), m_selectedItem(0), m_xFlipped(false), m_zFlipped(false), m_yFlipped(false), m_updateLabels(false), m_dotShader(0), + m_dotGradientShader(0), + #if defined(QT_OPENGL_ES_2) + m_pointShader(0), + #endif m_depthShader(0), m_selectionShader(0), m_backgroundShader(0), m_labelShader(0), - m_dotObj(0), m_backgroundObj(0), m_gridLineObj(0), m_labelObj(0), @@ -78,11 +82,15 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_shadowQualityMultiplier(3), m_heightNormalizer(1.0f), m_scaleFactor(0), - m_selection(selectionSkipColor), - m_previousSelection(selectionSkipColor), + m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()), + m_selectedItemTotalIndex(Scatter3DController::invalidSelectionIndex()), + m_selectedItemSeriesIndex(Scatter3DController::invalidSelectionIndex()), + m_selectedSeries(0), m_areaSize(QSizeF(0.0, 0.0)), m_dotSizeScale(1.0f), - m_hasHeightAdjustmentChanged(true) + m_hasHeightAdjustmentChanged(true), + m_backgroundMargin(defaultMargin), + m_maxItemSize(0.0f) { initializeOpenGLFunctions(); initializeOpenGL(); @@ -96,11 +104,11 @@ Scatter3DRenderer::~Scatter3DRenderer() m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer); m_textureHelper->deleteTexture(&m_bgrTexture); delete m_dotShader; + delete m_dotGradientShader; delete m_depthShader; delete m_selectionShader; delete m_backgroundShader; delete m_labelShader; - delete m_dotObj; delete m_backgroundObj; delete m_gridLineObj; delete m_labelObj; @@ -111,14 +119,15 @@ void Scatter3DRenderer::initializeOpenGL() Abstract3DRenderer::initializeOpenGL(); // Initialize shaders - handleShadowQualityChange(); - 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(); +#else + // Init point shader + initPointShader(); #endif // Init selection shader @@ -131,52 +140,81 @@ void Scatter3DRenderer::initializeOpenGL() loadLabelMesh(); // Set view port - glViewport(m_mainViewPort.x(), m_mainViewPort.y(), - m_mainViewPort.width(), m_mainViewPort.height()); + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); // Load background mesh (we need to be initialized first) loadBackgroundMesh(); } -void Scatter3DRenderer::updateDataModel(QScatterDataProxy *dataProxy) +void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList, + bool updateVisibility) { - const QScatterDataArray &dataArray = *dataProxy->array(); + Abstract3DRenderer::updateSeries(seriesList, updateVisibility); + + int seriesCount = m_visibleSeriesList.size(); + float maxItemSize = 0.0f; + float itemSize = 0.0f; + + for (int series = 0; series < seriesCount; series++) { + itemSize = static_cast<QScatter3DSeries *>(m_visibleSeriesList.at(series).series())->itemSize(); + if (maxItemSize < itemSize) + maxItemSize = itemSize; + } + m_backgroundMargin = defaultMargin; + m_maxItemSize = maxItemSize; + if (maxItemSize > defaultMaxSize) + m_backgroundMargin += maxItemSize / itemScaler; +} + +void Scatter3DRenderer::updateData() +{ + int seriesCount = m_visibleSeriesList.size(); calculateSceneScalingFactors(); - int dataSize = dataArray.size(); float minX = float(m_axisCacheX.min()); float maxX = float(m_axisCacheX.max()); float minY = float(m_axisCacheY.min()); float maxY = float(m_axisCacheY.max()); float minZ = float(m_axisCacheZ.min()); float maxZ = float(m_axisCacheZ.max()); - - if (dataSize != m_renderItemArray.size()) - m_renderItemArray.resize(dataSize); - for (int i = 0; i < dataSize ; i++) { - QVector3D dotPos = dataArray.at(i).position(); - // TODO: Check if this still works always when ranges are no longer required to be zero centered - // TODO: qreal -> float conversion for axis min/max may cause issues like in surface - if ((dotPos.x() >= minX && dotPos.x() <= maxX ) - && (dotPos.y() >= minY && dotPos.y() <= maxY) - && (dotPos.z() >= minZ && dotPos.z() <= maxZ)) { - m_renderItemArray[i].setPosition(dotPos); - m_renderItemArray[i].setVisible(true); - calculateTranslation(m_renderItemArray[i]); - } else { - m_renderItemArray[i].setVisible(false); + int totalDataSize = 0; + + if (m_renderingArrays.size() != seriesCount) + m_renderingArrays.resize(seriesCount); + + for (int series = 0; series < seriesCount; series++) { + QScatterDataProxy *dataProxy = + static_cast<QScatter3DSeries *>(m_visibleSeriesList.at(series).series())->dataProxy(); + const QScatterDataArray &dataArray = *dataProxy->array(); + int dataSize = dataArray.size(); + totalDataSize += dataSize; + + if (dataSize != m_renderingArrays.at(series).size()) + m_renderingArrays[series].resize(dataSize); + + for (int i = 0; i < dataSize; i++) { + QVector3D dotPos = dataArray.at(i).position(); + if ((dotPos.x() >= minX && dotPos.x() <= maxX ) + && (dotPos.y() >= minY && dotPos.y() <= maxY) + && (dotPos.z() >= minZ && dotPos.z() <= maxZ)) { + m_renderingArrays[series][i].setPosition(dotPos); + m_renderingArrays[series][i].setVisible(true); + calculateTranslation(m_renderingArrays[series][i]); + } else { + m_renderingArrays[series][i].setVisible(false); + } } } - m_dotSizeScale = (GLfloat)qBound(0.01, (2.0 / qSqrt((qreal)dataSize)), 0.1); - m_selectedItem = 0; + m_dotSizeScale = GLfloat(qBound(defaultMinSize, 2.0f / float(qSqrt(qreal(totalDataSize))), + defaultMaxSize)); - Abstract3DRenderer::updateDataModel(dataProxy); + updateSelectedItem(m_selectedItemIndex, m_selectedSeries); } void Scatter3DRenderer::updateScene(Q3DScene *scene) { - // TODO: Move these to more suitable place e.g. controller should be controlling the viewports. - scene->setPrimarySubViewport(m_mainViewPort); - // TODO: See QTRD-2374 scene->activeCamera()->setMinYRotation(-90.0f); @@ -186,10 +224,6 @@ void Scatter3DRenderer::updateScene(Q3DScene *scene) m_hasHeightAdjustmentChanged = false; } - scene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); - // Set light position (rotate light with m_cachedScene->activeCamera(), a bit above it (as set in defaultLightPos)) - scene->setLightPositionRelativeToCamera(defaultLightPos); - Abstract3DRenderer::updateScene(scene); } @@ -209,19 +243,22 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); // Specify viewport - glViewport(m_mainViewPort.x(), m_mainViewPort.y(), - m_mainViewPort.width(), m_mainViewPort.height()); + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); // Set up projection matrix QMatrix4x4 projectionMatrix; - GLfloat viewPortRatio = (GLfloat)m_mainViewPort.width() / (GLfloat)m_mainViewPort.height(); + GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width() / (GLfloat)m_primarySubViewport.height(); projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f); // Calculate view matrix QMatrix4x4 viewMatrix = activeCamera->viewMatrix(); - QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; + int seriesCount = m_visibleSeriesList.size(); + // Calculate label flipping if (viewMatrix.row(0).x() > 0) m_zFlipped = false; @@ -249,25 +286,36 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) backgroundRotation = 0.0f; // Get light position from the scene - QVector3D lightPos = m_cachedScene->activeLight()->position(); - - // Map adjustment direction to model matrix scaling - // TODO: Let's use these for testing the autoscaling of dots based on their number - GLfloat heightMultiplier = m_dotSizeScale; //1.0f; - GLfloat widthMultiplier = m_dotSizeScale; //1.0f; - GLfloat depthMultiplier = m_dotSizeScale; //1.0f; - GLfloat heightScaler = 0.0f; - GLfloat widthScaler = 0.0f; - GLfloat depthScaler = 0.0f; - QVector3D modelScaler(widthMultiplier + widthScaler, - heightMultiplier + heightScaler, - depthMultiplier + depthScaler); + QVector3D lightPos = m_cachedScene->activeLight()->position(); + // Introduce regardless of shadow quality to simplify logic QMatrix4x4 depthViewMatrix; QMatrix4x4 depthProjectionMatrix; QMatrix4x4 depthProjectionViewMatrix; + // Check if we have any series with points + bool havePointSeries = false; + bool haveMeshSeries = false; + bool haveUniformColorMeshSeries = false; + bool haveGradientMeshSeries = false; + for (int i = 0; i < seriesCount; i++) { + if (m_visibleSeriesList.at(i).mesh() == QAbstract3DSeries::MeshPoint) { + havePointSeries = true; + } else { + haveMeshSeries = true; + if (m_visibleSeriesList.at(i).colorStyle() == Q3DTheme::ColorStyleUniform) + haveUniformColorMeshSeries = true; + else + haveGradientMeshSeries = true; + } + } + #if !defined(QT_OPENGL_ES_2) + if (havePointSeries) { + glEnable(GL_POINT_SMOOTH); + glEnable(GL_PROGRAM_POINT_SIZE); + } + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { // Render scene into a depth texture for using with shadow mapping // Bind depth shader @@ -275,8 +323,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows. glViewport(0, 0, - m_mainViewPort.width() * m_shadowQualityMultiplier, - m_mainViewPort.height() * m_shadowQualityMultiplier); + m_primarySubViewport.width() * m_shadowQualityMultiplier, + m_primarySubViewport.height() * m_shadowQualityMultiplier); // Enable drawing to framebuffer glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); @@ -310,40 +358,61 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; // Draw dots to depth buffer - for (int dot = 0; dot < m_renderItemArray.size(); dot++) { - const ScatterRenderItem &item = m_renderItemArray.at(dot); - if (!item.isVisible()) - continue; + for (int series = 0; series < seriesCount; series++) { + ObjectHelper *dotObj = m_visibleSeriesList.at(series).object(); + bool drawingPoints = (m_visibleSeriesList.at(series).mesh() == QAbstract3DSeries::MeshPoint); + + // TODO: Accessing series directly during rendering + float itemSize = + static_cast<QScatter3DSeries *>(m_visibleSeriesList.at(series).series())->itemSize() + / itemScaler; + if (itemSize == 0.0f) + itemSize = m_dotSizeScale; + if (drawingPoints) { + // Scale points based on shadow quality for shadows, not by zoom level + glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier); + } + QVector3D modelScaler(itemSize, itemSize, itemSize); - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; + for (int dot = 0; dot < m_renderingArrays.at(series).size(); dot++) { + const ScatterRenderItem &item = m_renderingArrays.at(series).at(dot); + if (!item.isVisible()) + continue; - modelMatrix.translate(item.translation()); - modelMatrix.scale(modelScaler); - //modelMatrix.scale(QVector3D(widthMultiplier * item.size() + widthScaler, - // heightMultiplier * item.size() + heightScaler, - // depthMultiplier * item.size() + depthScaler)); + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + modelMatrix.translate(item.translation()); + if (!drawingPoints) + modelMatrix.scale(modelScaler); - m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); + MVPMatrix = depthProjectionViewMatrix * modelMatrix; - // 1st attribute buffer : vertices - glEnableVertexAttribArray(m_depthShader->posAtt()); - glBindBuffer(GL_ARRAY_BUFFER, m_dotObj->vertexBuf()); - glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0); + m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); - // Index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_dotObj->elementBuf()); + if (drawingPoints) { + m_drawer->drawPoint(m_depthShader); + } else { + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_depthShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf()); + glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, + (void *)0); - // Draw the triangles - glDrawElements(GL_TRIANGLES, m_dotObj->indexCount(), GL_UNSIGNED_SHORT, (void *)0); + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf()); - // Free buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + // Draw the triangles + glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_SHORT, + (void *)0); - glDisableVertexAttribArray(m_depthShader->posAtt()); + // 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) @@ -352,12 +421,11 @@ void Scatter3DRenderer::drawScene(const GLuint 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()); + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.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) @@ -370,16 +438,18 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) m_labelShader->setUniformValue(m_labelShader->MVP(), MVPMatrix); m_drawer->drawObject(m_labelShader, m_labelObj, m_depthTexture); glDisable(GL_TEXTURE_2D); - m_labelShader->release(); #endif } + + ShaderHelper *pointSelectionShader = m_selectionShader; +#else + ShaderHelper *pointSelectionShader = m_pointShader; #endif + ShaderHelper *selectionShader = m_selectionShader; // Skip selection mode drawing if we have no selection mode - if (m_cachedSelectionMode > QDataVis::SelectionModeNone) { - // Bind selection shader - m_selectionShader->bind(); - + if (m_cachedSelectionMode > QDataVis::SelectionNone + && SelectOnScene == m_selectionState && seriesCount > 0) { // Draw dots to selection buffer glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used @@ -387,62 +457,101 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) 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 - int arraySize = m_renderItemArray.size(); - if (arraySize > 0xfffffe) // Max possible different selection colors, 0xffffff being skipColor - qFatal("Too many objects"); + int arraySize = 0; + int totalArraySize = 0; + int dotNo = 0; + + // Init previous to opposite of first to ensure we change binding for first series + bool previousDrawingPoints = (m_visibleSeriesList.at(0).mesh() != QAbstract3DSeries::MeshPoint); + for (int series = 0; series < seriesCount; series++) { + ObjectHelper *dotObj = m_visibleSeriesList.at(series).object(); + bool drawingPoints = (m_visibleSeriesList.at(series).mesh() == QAbstract3DSeries::MeshPoint); + + // TODO: Accessing series directly during rendering + float itemSize = + static_cast<QScatter3DSeries *>(m_visibleSeriesList.at(series).series())->itemSize() + / itemScaler; + if (itemSize == 0.0f) + itemSize = m_dotSizeScale; +#if !defined(QT_OPENGL_ES_2) + if (drawingPoints) + glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom +#endif + QVector3D modelScaler(itemSize, itemSize, itemSize); - for (int dot = 0; dot < arraySize; dot++) { - const ScatterRenderItem &item = m_renderItemArray.at(dot); - if (!item.isVisible()) - continue; + // Rebind selection shader if it has changed + if (drawingPoints != previousDrawingPoints) { + previousDrawingPoints = drawingPoints; + if (drawingPoints) + selectionShader = pointSelectionShader; + else + selectionShader = m_selectionShader; - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; + selectionShader->bind(); + } + arraySize = m_renderingArrays.at(series).size(); + totalArraySize += arraySize; - modelMatrix.translate(item.translation()); - modelMatrix.scale(modelScaler); - //modelMatrix.scale(QVector3D(widthMultiplier * item.size() + widthScaler, - // heightMultiplier * item.size() + heightScaler, - // depthMultiplier * item.size() + depthScaler)); + if (totalArraySize > 0xfffffe) // Max possible different selection colors, 0xffffff being skipColor + qFatal("Too many objects"); - MVPMatrix = projectionViewMatrix * modelMatrix; + for (int dot = 0; dot < arraySize; dot++) { + const ScatterRenderItem &item = m_renderingArrays.at(series).at(dot); + if (!item.isVisible()) + continue; - QVector3D dotColor = indexToSelectionColor(dot); - dotColor /= 255.0f; + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + modelMatrix.translate(item.translation()); + if (!drawingPoints) + modelMatrix.scale(modelScaler); + + MVPMatrix = projectionViewMatrix * modelMatrix; - m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); - m_selectionShader->setUniformValue(m_selectionShader->color(), dotColor); + QVector3D dotColor = indexToSelectionColor(dotNo); + dotColor /= 255.0f; - // 1st attribute buffer : vertices - glEnableVertexAttribArray(m_selectionShader->posAtt()); - glBindBuffer(GL_ARRAY_BUFFER, m_dotObj->vertexBuf()); - glVertexAttribPointer(m_selectionShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0); + selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix); + selectionShader->setUniformValue(selectionShader->color(), dotColor); - // Index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_dotObj->elementBuf()); + if (drawingPoints) { + m_drawer->drawPoint(selectionShader); + } else { + // 1st attribute buffer : vertices + glEnableVertexAttribArray(selectionShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf()); + glVertexAttribPointer(selectionShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, + (void *)0); - // Draw the triangles - glDrawElements(GL_TRIANGLES, m_dotObj->indexCount(), GL_UNSIGNED_SHORT, (void *)0); + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf()); - // Free buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + // Draw the triangles + glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_SHORT, + (void *)0); - glDisableVertexAttribArray(m_selectionShader->posAtt()); + // Free buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableVertexAttribArray(selectionShader->posAtt()); + } + dotNo++; + } } glEnable(GL_DITHER); // Read color under cursor - if (QDataVis::InputStateOnScene == m_controller->inputState()) { - m_selection = Utils::getSelection(m_controller->inputPosition(), - m_cachedBoundingRect.height()); - } + QVector3D clickedColor = Utils::getSelection(m_inputPosition, + m_viewport.height()); + int clickedIndex = 0; + QScatter3DSeries *clickedSeries = 0; + selectionColorToSeriesAndIndex(clickedColor, clickedIndex, clickedSeries); + emit itemClicked(clickedIndex, clickedSeries); 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); @@ -454,114 +563,189 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) m_labelShader->setUniformValue(m_labelShader->MVP(), MVPMatrix); m_drawer->drawObject(m_labelShader, m_labelObj, m_selectionTexture); glDisable(GL_TEXTURE_2D); - m_labelShader->release(); #endif } - // Bind dot shader - m_dotShader->bind(); - - // Set unchanging shader bindings - m_dotShader->setUniformValue(m_dotShader->lightP(), lightPos); - m_dotShader->setUniformValue(m_dotShader->view(), viewMatrix); - m_dotShader->setUniformValue(m_dotShader->ambientS(), m_cachedTheme.m_ambientStrength); - - // Enable texture - glEnable(GL_TEXTURE_2D); - // Draw dots + ShaderHelper *dotShader = 0; + GLuint gradientTexture = 0; bool dotSelectionFound = false; - int selectedIndex; - if (m_selection == selectionSkipColor) { - selectedIndex = Scatter3DController::noSelectionIndex(); + ScatterRenderItem *selectedItem(0); + int dotNo = 0; + QVector3D baseColor; + QVector3D dotColor; + + bool previousDrawingPoints = false; + Q3DTheme::ColorStyle previousMeshColorStyle = Q3DTheme::ColorStyleUniform; + if (haveMeshSeries) { + // Set unchanging shader bindings + if (haveGradientMeshSeries) { + m_dotGradientShader->bind(); + m_dotGradientShader->setUniformValue(m_dotGradientShader->lightP(), lightPos); + m_dotGradientShader->setUniformValue(m_dotGradientShader->view(), viewMatrix); + m_dotGradientShader->setUniformValue(m_dotGradientShader->ambientS(), m_cachedTheme->ambientLightStrength()); + } + if (haveUniformColorMeshSeries) { + m_dotShader->bind(); + m_dotShader->setUniformValue(m_dotShader->lightP(), lightPos); + m_dotShader->setUniformValue(m_dotShader->view(), viewMatrix); + m_dotShader->setUniformValue(m_dotShader->ambientS(), m_cachedTheme->ambientLightStrength()); + dotShader = m_dotShader; + } else { + dotShader = m_dotGradientShader; + previousMeshColorStyle = Q3DTheme::ColorStyleRangeGradient; + m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.0f); + } + glEnable(GL_TEXTURE_2D); } else { - selectedIndex = int(m_selection.x()) - + (int(m_selection.y()) << 8) - + (int(m_selection.z()) << 16); + dotShader = pointSelectionShader; + previousDrawingPoints = true; + dotShader->bind(); } - if (m_selection != m_previousSelection) { - emit selectedItemIndexChanged(selectedIndex); - m_previousSelection = m_selection; - } + for (int series = 0; series < seriesCount; series++) { + const SeriesRenderCache ¤tSeries = m_visibleSeriesList.at(series); + ObjectHelper *dotObj = currentSeries.object(); + bool drawingPoints = (currentSeries.mesh() == QAbstract3DSeries::MeshPoint); + Q3DTheme::ColorStyle colorStyle = currentSeries.colorStyle(); + bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform); + bool useColor = colorStyleIsUniform || drawingPoints; + + // TODO: Accessing series directly during rendering + float itemSize = + static_cast<QScatter3DSeries *>(currentSeries.series())->itemSize() + / itemScaler; + if (itemSize == 0.0f) + itemSize = m_dotSizeScale; +#if !defined(QT_OPENGL_ES_2) + if (drawingPoints) + glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom +#endif + QVector3D modelScaler(itemSize, itemSize, itemSize); + + // Rebind shader if it has changed + if (drawingPoints != previousDrawingPoints + || (!drawingPoints && + (colorStyleIsUniform != (previousMeshColorStyle == Q3DTheme::ColorStyleUniform)))) { + previousDrawingPoints = drawingPoints; + if (drawingPoints) { + dotShader = pointSelectionShader; + } else { + if (colorStyleIsUniform) + dotShader = m_dotShader; + else + dotShader = m_dotGradientShader; + } + dotShader->bind(); + } - ScatterRenderItem *selectedItem(0); + if (!drawingPoints && !colorStyleIsUniform && previousMeshColorStyle != colorStyle) { + if (colorStyle == Q3DTheme::ColorStyleObjectGradient) { + m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientMin(), 0.0f); + m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.5f); + } else { + // Each ball is of uniform color according to its Y-coordinate + m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.0f); + } + } - QVector3D baseColor = Utils::vectorFromColor(m_cachedTheme.m_baseColor); + if (!drawingPoints) + previousMeshColorStyle = colorStyle; - for (int dot = 0; dot < m_renderItemArray.size(); dot++) { - ScatterRenderItem &item = m_renderItemArray[dot]; - if (!item.isVisible()) - continue; + if (useColor) { + baseColor = currentSeries.baseColor(); + dotColor = baseColor; + } - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + int seriesSize = m_renderingArrays.at(series).size(); + for (int dot = 0; dot < seriesSize; dot++) { + ScatterRenderItem &item = m_renderingArrays[series][dot]; + if (!item.isVisible()) + continue; - modelMatrix.translate(item.translation()); - modelMatrix.scale(modelScaler); - //modelMatrix.scale(QVector3D(widthMultiplier * item.size() + widthScaler, - // heightMultiplier * item.size() + heightScaler, - // depthMultiplier * item.size() + depthScaler)); - itModelMatrix.scale(modelScaler); - //itModelMatrix.scale(QVector3D(widthMultiplier * item.size() + widthScaler, - // heightMultiplier * item.size() + heightScaler, - // depthMultiplier * item.size() + depthScaler)); + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; + modelMatrix.translate(item.translation()); + if (!drawingPoints) { + modelMatrix.scale(modelScaler); + itModelMatrix.scale(modelScaler); + } #ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; -#else - MVPMatrix = projectionViewMatrix * modelMatrix; -#endif - -#if 0 - QVector3D heightColor = - Utils::vectorFromColor(m_cachedTheme.m_heightColor) * item.translation().y(); - - QVector3D dotColor = baseColor + heightColor; + MVPMatrix = depthProjectionViewMatrix * modelMatrix; #else - QVector3D dotColor = baseColor; + MVPMatrix = projectionViewMatrix * modelMatrix; #endif - GLfloat lightStrength = m_cachedTheme.m_lightStrength; - if (m_cachedSelectionMode > QDataVis::SelectionModeNone && (selectedIndex == dot)) { - dotColor = Utils::vectorFromColor(m_cachedTheme.m_highlightBarColor); - lightStrength = m_cachedTheme.m_highlightLightStrength; - // Insert data to ScatterRenderItem. We have no ownership, don't delete the previous one - selectedItem = &item; - dotSelectionFound = true; - } + if (useColor) + dotColor = baseColor; + else + gradientTexture = currentSeries.baseGradientTexture(); - // Set shader bindings - m_dotShader->setUniformValue(m_dotShader->model(), modelMatrix); - m_dotShader->setUniformValue(m_dotShader->nModel(), - itModelMatrix.inverted().transposed()); - m_dotShader->setUniformValue(m_dotShader->MVP(), MVPMatrix); - m_dotShader->setUniformValue(m_dotShader->color(), dotColor); + GLfloat lightStrength = m_cachedTheme->lightStrength(); + if (m_cachedSelectionMode > QDataVis::SelectionNone && (m_selectedItemTotalIndex == dotNo)) { + if (useColor) + dotColor = currentSeries.singleHighlightColor(); + else + gradientTexture = currentSeries.singleHighlightGradientTexture(); + lightStrength = m_cachedTheme->highlightLightStrength(); + // Insert data to ScatterRenderItem. We have no ownership, don't delete the previous one + selectedItem = &item; + dotSelectionFound = true; + } + if (!drawingPoints) { + // Set shader bindings + dotShader->setUniformValue(dotShader->model(), modelMatrix); + dotShader->setUniformValue(dotShader->nModel(), + itModelMatrix.inverted().transposed()); + } + dotShader->setUniformValue(dotShader->MVP(), MVPMatrix); + if (useColor) { + dotShader->setUniformValue(dotShader->color(), dotColor); + } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { + dotShader->setUniformValue(dotShader->gradientMin(), + (item.position().y() + 1.0f) / 2.0f); + } #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_dotShader->setUniformValue(m_dotShader->shadowQ(), m_shadowQualityToShader); - m_dotShader->setUniformValue(m_dotShader->depth(), depthMVPMatrix); - m_dotShader->setUniformValue(m_dotShader->lightS(), lightStrength / 10.0f); + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + if (!drawingPoints) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + dotShader->setUniformValue(dotShader->shadowQ(), m_shadowQualityToShader); + dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix); + dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f); - // Draw the object - m_drawer->drawObject(m_dotShader, m_dotObj, 0, m_depthTexture); - } else + // Draw the object + m_drawer->drawObject(dotShader, dotObj, gradientTexture, m_depthTexture); + } else { + // Draw the object + m_drawer->drawPoint(dotShader); + } + } else #endif - { - // Set shadowless shader bindings - m_dotShader->setUniformValue(m_dotShader->lightS(), lightStrength); - - // Draw the object - m_drawer->drawObject(m_dotShader, m_dotObj); + { + if (!drawingPoints) { + // Set shadowless shader bindings + dotShader->setUniformValue(dotShader->lightS(), lightStrength); + // Draw the object + m_drawer->drawObject(dotShader, dotObj, gradientTexture); + } else { + // Draw the object + m_drawer->drawPoint(dotShader); + } + } + dotNo++; } } - // Release dot shader - m_dotShader->release(); +#if !defined(QT_OPENGL_ES_2) + if (havePointSeries) { + glDisable(GL_POINT_SMOOTH); + glDisable(GL_PROGRAM_POINT_SIZE); + } +#endif // Bind background shader m_backgroundShader->bind(); @@ -569,19 +753,23 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glCullFace(GL_BACK); // Draw background - if (m_cachedIsBackgroundEnabled && m_backgroundObj) { + if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - QVector3D bgScale((aspectRatio * backgroundMargin * m_areaSize.width()) / m_scaleFactor, - backgroundMargin, - (aspectRatio * backgroundMargin * m_areaSize.height()) / m_scaleFactor); + GLfloat xScale = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor; + GLfloat zScale = (aspectRatio * m_backgroundMargin * m_areaSize.height()) / m_scaleFactor; + if (m_maxItemSize > xScale) + xScale = m_maxItemSize; + if (m_maxItemSize > zScale) + zScale = m_maxItemSize; + QVector3D bgScale(xScale, m_backgroundMargin, zScale); #else // ..and this if we want uniform scaling based on largest dimension - QVector3D bgScale((aspectRatio * backgroundMargin), - backgroundMargin, - (aspectRatio * backgroundMargin)); + QVector3D bgScale((aspectRatio * m_backgroundMargin), + m_backgroundMargin, + (aspectRatio * m_backgroundMargin)); #endif modelMatrix.scale(bgScale); // If we're viewing from below, background object must be flipped @@ -598,7 +786,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) #else MVPMatrix = projectionViewMatrix * modelMatrix; #endif - QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme.m_backgroundColor); + QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); // Set shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); @@ -609,7 +797,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) 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); + m_cachedTheme->ambientLightStrength() * 2.0f); #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { @@ -619,7 +807,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) m_shadowQualityToShader); m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix); m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), - m_cachedTheme.m_lightStrength / 10.0f); + m_cachedTheme->lightStrength() / 10.0f); // Draw the object m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); @@ -628,16 +816,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) { // Set shadowless shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), - m_cachedTheme.m_lightStrength); + m_cachedTheme->lightStrength()); // Draw the object m_drawer->drawObject(m_backgroundShader, m_backgroundObj); } } - // Release background shader - m_backgroundShader->release(); - // Disable textures glDisable(GL_TEXTURE_2D); @@ -650,37 +835,54 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) axisCacheMax = &m_axisCacheX; #endif - if (m_cachedIsGridEnabled && m_heightNormalizer) { + if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { ShaderHelper *lineShader = m_backgroundShader; // Bind line shader lineShader->bind(); // Set unchanging shader bindings - QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme.m_gridLine); + QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); lineShader->setUniformValue(lineShader->lightP(), lightPos); lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); - lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme.m_ambientStrength); + lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength()); #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { // Set shadowed shader bindings lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); lineShader->setUniformValue(lineShader->lightS(), - m_cachedTheme.m_lightStrength / 20.0f); + m_cachedTheme->lightStrength() / 20.0f); } else #endif { // Set shadowless shader bindings - lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme.m_lightStrength / 2.5f); + lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 2.5f); } + QQuaternion lineYRotation = QQuaternion(); + QQuaternion lineXRotation = QQuaternion(); + + if (m_xFlipped) + lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f); + else + lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + + if (m_yFlipped) + lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f); + else + lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); + + GLfloat yFloorLinePosition = -m_backgroundMargin + gridLineOffset; + if (m_yFlipped) + yFloorLinePosition = -yFloorLinePosition; + // Rows (= Z) if (m_axisCacheZ.segmentCount() > 0) { // Floor lines #ifndef USE_UNIFORM_SCALING GLfloat lineStep = aspectRatio * m_axisCacheZ.subSegmentStep(); - GLfloat linePos = -aspectRatio * m_axisCacheZ.min(); // Start line + GLfloat linePos = -aspectRatio * (m_axisCacheZ.min() - m_translationOffset.z()); // Start line int lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount(); #else GLfloat lineStep = aspectRatio * axisCacheMax->subSegmentStep(); @@ -689,11 +891,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) #endif #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - QVector3D gridLineScaler( - (aspectRatio * backgroundMargin * m_areaSize.width()) / m_scaleFactor, - gridLineWidth, gridLineWidth); + GLfloat xScale = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor; + if (m_maxItemSize > xScale) + xScale = m_maxItemSize; + QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth); #else // ..and this if we want uniform scaling based on largest dimension - QVector3D gridLineScaler((aspectRatio * backgroundMargin), + QVector3D gridLineScaler((aspectRatio * m_backgroundMargin), gridLineWidth, gridLineWidth); #endif @@ -702,19 +905,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - if (m_yFlipped) - modelMatrix.translate(0.0f, backgroundMargin, linePos / m_scaleFactor); - else - modelMatrix.translate(0.0f, -backgroundMargin, linePos / m_scaleFactor); + modelMatrix.translate(0.0f, yFloorLinePosition, linePos / m_scaleFactor); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); - // If we're viewing from below, grid line object must be flipped - if (m_yFlipped) { - modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - itModelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - } + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); MVPMatrix = projectionViewMatrix * modelMatrix; @@ -741,13 +938,15 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } // Side wall lines - gridLineScaler = QVector3D(gridLineWidth, backgroundMargin, gridLineWidth); + gridLineScaler = QVector3D(gridLineWidth, m_backgroundMargin, gridLineWidth); #ifndef USE_UNIFORM_SCALING - GLfloat lineXTrans = (aspectRatio * backgroundMargin * m_areaSize.width()) - / m_scaleFactor; - linePos = -aspectRatio * m_axisCacheZ.min(); // Start line + GLfloat lineXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width()) + / m_scaleFactor - gridLineOffset; + if (m_maxItemSize > lineXTrans) + lineXTrans = m_maxItemSize - gridLineOffset; + linePos = -aspectRatio * (m_axisCacheZ.min() - m_translationOffset.z()); // Start line #else - GLfloat lineXTrans = aspectRatio * backgroundMargin; + GLfloat lineXTrans = aspectRatio * m_backgroundMargin - gridLineOffset; linePos = -aspectRatio * m_scaleFactor; // Start line #endif if (!m_xFlipped) @@ -759,9 +958,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; modelMatrix.translate(lineXTrans, 0.0f, linePos / m_scaleFactor); + modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); + MVPMatrix = projectionViewMatrix * modelMatrix; // Set the rest of the shader bindings @@ -792,17 +995,18 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Floor lines #ifndef USE_UNIFORM_SCALING GLfloat lineStep = aspectRatio * m_axisCacheX.subSegmentStep(); - GLfloat linePos = aspectRatio * m_axisCacheX.min(); + GLfloat linePos = aspectRatio * (m_axisCacheX.min() - m_translationOffset.x()); int lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount(); - QVector3D gridLineScaler( - gridLineWidth, gridLineWidth, - (aspectRatio * backgroundMargin * m_areaSize.height()) / m_scaleFactor); + GLfloat zScale = (aspectRatio * m_backgroundMargin * m_areaSize.height()) / m_scaleFactor; + if (m_maxItemSize > zScale) + zScale = m_maxItemSize; + QVector3D gridLineScaler(gridLineWidth, gridLineWidth, zScale); #else GLfloat lineStep = aspectRatio * axisCacheMax->subSegmentStep(); GLfloat linePos = -aspectRatio * m_scaleFactor; int lastSegment = axisCacheMax->subSegmentCount() * axisCacheMax->segmentCount(); QVector3D gridLineScaler(gridLineWidth, gridLineWidth, - aspectRatio * backgroundMargin); + aspectRatio * m_backgroundMargin); #endif for (int segment = 0; segment <= lastSegment; segment++) { @@ -810,18 +1014,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - if (m_yFlipped) - modelMatrix.translate(linePos / m_scaleFactor, backgroundMargin, 0.0f); - else - modelMatrix.translate(linePos / m_scaleFactor, -backgroundMargin, 0.0f); + modelMatrix.translate(linePos / m_scaleFactor, yFloorLinePosition, 0.0f); + modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); - // If we're viewing from below, grid line object must be flipped - if (m_yFlipped) { - modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - itModelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - } + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); MVPMatrix = projectionViewMatrix * modelMatrix; @@ -849,17 +1048,19 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Back wall lines #ifndef USE_UNIFORM_SCALING - GLfloat lineZTrans = (aspectRatio * backgroundMargin * m_areaSize.height()) - / m_scaleFactor; - linePos = aspectRatio * m_axisCacheX.min(); + GLfloat lineZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height()) + / m_scaleFactor - gridLineOffset; + if (m_maxItemSize > lineZTrans) + lineZTrans = m_maxItemSize - gridLineOffset; + linePos = aspectRatio * (m_axisCacheX.min() - m_translationOffset.x()); #else - GLfloat lineZTrans = aspectRatio * backgroundMargin; + GLfloat lineZTrans = aspectRatio * m_backgroundMargin - gridLineOffset; linePos = -aspectRatio * m_scaleFactor; #endif if (!m_zFlipped) lineZTrans = -lineZTrans; - gridLineScaler = QVector3D(gridLineWidth, backgroundMargin, gridLineWidth); + gridLineScaler = QVector3D(gridLineWidth, m_backgroundMargin, gridLineWidth); for (int segment = 0; segment <= lastSegment; segment++) { QMatrix4x4 modelMatrix; @@ -867,9 +1068,15 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; modelMatrix.translate(linePos / m_scaleFactor, 0.0f, lineZTrans); + modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); + if (m_zFlipped) { + modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + } + MVPMatrix = projectionViewMatrix * modelMatrix; // Set the rest of the shader bindings @@ -899,18 +1106,21 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (m_axisCacheY.segmentCount() > 0) { // Back wall GLfloat lineStep = m_axisCacheY.subSegmentStep(); - GLfloat linePos = m_axisCacheY.min(); + GLfloat linePos = m_axisCacheY.min() - m_translationOffset.y(); int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat lineZTrans = (aspectRatio * backgroundMargin * m_areaSize.height()) - / m_scaleFactor; - QVector3D gridLineScaler( - (aspectRatio * backgroundMargin * m_areaSize.width() / m_scaleFactor), - gridLineWidth, gridLineWidth); + GLfloat lineZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height()) + / m_scaleFactor - gridLineOffset; + if (m_maxItemSize > lineZTrans) + lineZTrans = m_maxItemSize - gridLineOffset; + GLfloat xScale = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor; + if (m_maxItemSize > xScale) + xScale = m_maxItemSize; + QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth); #else // ..and this if we want uniform scaling based on largest dimension - GLfloat lineZTrans = aspectRatio * backgroundMargin; - QVector3D gridLineScaler((aspectRatio * backgroundMargin), + GLfloat lineZTrans = aspectRatio * m_backgroundMargin - gridLineOffset; + QVector3D gridLineScaler((aspectRatio * m_backgroundMargin), gridLineWidth, gridLineWidth); #endif if (!m_zFlipped) @@ -926,6 +1136,11 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); + if (m_zFlipped) { + modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + } + MVPMatrix = projectionViewMatrix * modelMatrix; // Set the rest of the shader bindings @@ -951,18 +1166,22 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } // Side wall - linePos = m_axisCacheY.min(); + linePos = m_axisCacheY.min() - m_translationOffset.y(); lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat lineXTrans = (aspectRatio * backgroundMargin * m_areaSize.width()) + GLfloat lineXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width()) + / m_scaleFactor - gridLineOffset; + if (m_maxItemSize > lineXTrans) + lineXTrans = m_maxItemSize - gridLineOffset; + GLfloat zScale = (aspectRatio * m_backgroundMargin * m_areaSize.height()) / m_scaleFactor; - gridLineScaler = QVector3D( - gridLineWidth, gridLineWidth, - (aspectRatio * backgroundMargin * m_areaSize.height()) / m_scaleFactor); + if (m_maxItemSize > zScale) + zScale = m_maxItemSize; + gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, zScale); #else // ..and this if we want uniform scaling based on largest dimension - GLfloat lineXTrans = aspectRatio * backgroundMargin; + GLfloat lineXTrans = aspectRatio * m_backgroundMargin - gridLineOffset; gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, - aspectRatio * backgroundMargin); + aspectRatio * m_backgroundMargin); #endif if (!m_xFlipped) lineXTrans = -lineXTrans; @@ -977,6 +1196,9 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); + MVPMatrix = projectionViewMatrix * modelMatrix; // Set the rest of the shader bindings @@ -1001,9 +1223,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) linePos += lineStep; } } - - // Release line shader - lineShader->release(); } // Draw axis labels @@ -1015,23 +1234,26 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_POLYGON_OFFSET_FILL); // Z Labels if (m_axisCacheZ.segmentCount() > 0) { #ifndef USE_UNIFORM_SCALING GLfloat posStep = aspectRatio * m_axisCacheZ.segmentStep(); - GLfloat labelPos = -aspectRatio * m_axisCacheZ.min(); + GLfloat labelPos = -aspectRatio * (m_axisCacheZ.min() - m_translationOffset.z()); int lastSegment = m_axisCacheZ.segmentCount(); - GLfloat labelXTrans = (aspectRatio * backgroundMargin * m_areaSize.width()) + GLfloat labelXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor + labelMargin; + if (m_maxItemSize > labelXTrans) + labelXTrans = m_maxItemSize + labelMargin; #else GLfloat posStep = aspectRatio * axisCacheMax->segmentStep(); GLfloat labelPos = aspectRatio * m_scaleFactor; int lastSegment = axisCacheMax->segmentCount(); - GLfloat labelXTrans = aspectRatio * backgroundMargin + labelMargin; + GLfloat labelXTrans = aspectRatio * m_backgroundMargin + labelMargin; #endif int labelNbr = 0; - GLfloat labelYTrans = -backgroundMargin; + GLfloat labelYTrans = -m_backgroundMargin; GLfloat rotLabelX = -90.0f; GLfloat rotLabelY = 0.0f; GLfloat rotLabelZ = 0.0f; @@ -1057,6 +1279,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) #endif labelTrans.setZ(labelPos / m_scaleFactor); + glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); + // Draw the label here m_dummyRenderItem.setTranslation(labelTrans); #ifndef USE_UNIFORM_SCALING @@ -1078,18 +1302,20 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (m_axisCacheX.segmentCount() > 0) { #ifndef USE_UNIFORM_SCALING GLfloat posStep = aspectRatio * m_axisCacheX.segmentStep(); - GLfloat labelPos = aspectRatio * m_axisCacheX.min(); + GLfloat labelPos = aspectRatio * (m_axisCacheX.min() - m_translationOffset.x()); int lastSegment = m_axisCacheX.segmentCount(); - GLfloat labelZTrans = (aspectRatio * backgroundMargin * m_areaSize.height()) + GLfloat labelZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height()) / m_scaleFactor + labelMargin; + if (m_maxItemSize > labelZTrans) + labelZTrans = m_maxItemSize + labelMargin; #else GLfloat posStep = aspectRatio * axisCacheMax->segmentStep(); GLfloat labelPos = -aspectRatio * m_scaleFactor; int lastSegment = axisCacheMax->segmentCount(); - GLfloat labelZTrans = aspectRatio * backgroundMargin + labelMargin; + GLfloat labelZTrans = aspectRatio * m_backgroundMargin + labelMargin; #endif int labelNbr = 0; - GLfloat labelYTrans = -backgroundMargin; + GLfloat labelYTrans = -m_backgroundMargin; GLfloat rotLabelX = -90.0f; GLfloat rotLabelY = 90.0f; GLfloat rotLabelZ = 0.0f; @@ -1115,6 +1341,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) #endif labelTrans.setX(labelPos / m_scaleFactor); + glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); + // Draw the label here m_dummyRenderItem.setTranslation(labelTrans); #ifndef USE_UNIFORM_SCALING @@ -1135,15 +1363,19 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Y Labels if (m_axisCacheY.segmentCount() > 0) { GLfloat posStep = m_axisCacheY.segmentStep(); - GLfloat labelPos = m_axisCacheY.min(); + GLfloat labelPos = m_axisCacheY.min() - m_translationOffset.y(); int labelNbr = 0; #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat labelXTrans = (aspectRatio * backgroundMargin * m_areaSize.width()) + GLfloat labelXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor; - GLfloat labelZTrans = (aspectRatio * backgroundMargin * m_areaSize.height()) + GLfloat labelZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height()) / m_scaleFactor; + if (m_maxItemSize > labelXTrans) + labelXTrans = m_maxItemSize; + if (m_maxItemSize > labelZTrans) + labelZTrans = m_maxItemSize; #else // ..and this if we want uniform scaling based on largest dimension - GLfloat labelXTrans = aspectRatio * backgroundMargin; + GLfloat labelXTrans = aspectRatio * m_backgroundMargin; GLfloat labelZTrans = labelXTrans; #endif // Back wall init @@ -1185,6 +1417,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); const GLfloat labelYTrans = labelPos / m_heightNormalizer; + glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); + // Back wall labelTransBack.setY(labelYTrans); m_dummyRenderItem.setTranslation(labelTransBack); @@ -1205,6 +1439,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) labelPos += posStep; } } + glDisable(GL_POLYGON_OFFSET_FILL); // Handle selection clearing and selection label drawing if (!dotSelectionFound) { @@ -1225,7 +1460,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) static const QString yLabelTag(QStringLiteral("@yLabel")); static const QString zLabelTag(QStringLiteral("@zLabel")); - labelText = itemLabelFormat(); + labelText = m_visibleSeriesList[m_selectedItemSeriesIndex].itemLabelFormat(); labelText.replace(xTitleTag, m_axisCacheX.title()); labelText.replace(yTitleTag, m_axisCacheY.title()); @@ -1274,34 +1509,31 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); - // Release label shader - m_labelShader->release(); -} + // Release shader + glUseProgram(0); -void Scatter3DRenderer::updateSelectedItemIndex(int index) -{ - if (index == Scatter3DController::noSelectionIndex()) - m_selection = selectionSkipColor; - else - m_selection = indexToSelectionColor(index); + m_selectionDirty = false; } -void Scatter3DRenderer::handleResize() +void Scatter3DRenderer::updateSelectedItem(int index, const QScatter3DSeries *series) { - if (m_cachedBoundingRect.width() == 0 || m_cachedBoundingRect.height() == 0) - return; - - // Set view port - m_mainViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); - - Abstract3DRenderer::handleResize(); -} - -void Scatter3DRenderer::updateBackgroundEnabled(bool enable) -{ - if (enable != m_cachedIsBackgroundEnabled) { - Abstract3DRenderer::updateBackgroundEnabled(enable); - loadMeshFile(); // Load changed dot type + m_selectionDirty = true; + m_selectedSeries = series; + m_selectedItemIndex = Scatter3DController::invalidSelectionIndex(); + m_selectedItemTotalIndex = Scatter3DController::invalidSelectionIndex(); + m_selectedItemSeriesIndex = Scatter3DController::invalidSelectionIndex(); + + if (!m_renderingArrays.isEmpty() && index != Scatter3DController::invalidSelectionIndex()) { + int totalIndex = 0; + for (int i = 0; i < m_visibleSeriesList.size(); i++) { + if (m_visibleSeriesList.at(i).series() == series) { + m_selectedItemSeriesIndex = i; + m_selectedItemIndex = index; + m_selectedItemTotalIndex = index + totalIndex; + break; + } + totalIndex += m_renderingArrays.at(i).size(); + } } } @@ -1347,15 +1579,6 @@ void Scatter3DRenderer::updateShadowQuality(QDataVis::ShadowQuality quality) #endif } -void Scatter3DRenderer::loadMeshFile() -{ - QString objectFileName = m_cachedObjFile; - if (m_dotObj) - delete m_dotObj; - m_dotObj = new ObjectHelper(objectFileName); - m_dotObj->load(); -} - void Scatter3DRenderer::loadBackgroundMesh() { if (m_backgroundObj) @@ -1368,7 +1591,7 @@ void Scatter3DRenderer::loadGridLineMesh() { if (m_gridLineObj) delete m_gridLineObj; - m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/bar")); + m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); m_gridLineObj->load(); } @@ -1376,7 +1599,7 @@ void Scatter3DRenderer::loadLabelMesh() { if (m_labelObj) delete m_labelObj; - m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/label")); + m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); m_labelObj->load(); } @@ -1386,37 +1609,44 @@ void Scatter3DRenderer::updateTextures() m_updateLabels = true; } +void Scatter3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh) +{ + // Load full version of meshes that have it available + if (mesh != QAbstract3DSeries::MeshSphere + && mesh != QAbstract3DSeries::MeshMinimal + && mesh != QAbstract3DSeries::MeshPoint) { + fileName.append(QStringLiteral("Full")); + } +} + void Scatter3DRenderer::updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, - qreal min, qreal max) + float min, float max) { Abstract3DRenderer::updateAxisRange(orientation, min, max); } void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item) { - // Origin should be in the center of scene, ie. both positive and negative values are drawn - // above background - // We need to normalize translations - GLfloat xTrans = (aspectRatio * item.position().x()) / m_scaleFactor; - GLfloat zTrans = -(aspectRatio * item.position().z()) / m_scaleFactor; - GLfloat yTrans = item.position().y() / m_heightNormalizer; + GLfloat xTrans = (aspectRatio * (item.position().x() - m_translationOffset.x())) + / m_scaleFactor; + GLfloat zTrans = -(aspectRatio * (item.position().z() - m_translationOffset.z())) + / m_scaleFactor; + GLfloat yTrans = (item.position().y() - m_translationOffset.y()) / m_heightNormalizer; item.setTranslation(QVector3D(xTrans, yTrans, zTrans)); - //qDebug() << item.translation(); } void Scatter3DRenderer::calculateSceneScalingFactors() { - m_heightNormalizer = (GLfloat)qMax(qAbs(m_axisCacheY.max()), qAbs(m_axisCacheY.min())); - m_areaSize.setHeight(qMax(qAbs(m_axisCacheZ.max()), qAbs(m_axisCacheZ.min()))); - m_areaSize.setWidth(qMax(qAbs(m_axisCacheX.max()), qAbs(m_axisCacheX.min()))); + m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()) / 2.0f; + m_areaSize.setHeight((m_axisCacheZ.max() - m_axisCacheZ.min()) / 2.0f); + m_areaSize.setWidth((m_axisCacheX.max() - m_axisCacheX.min()) / 2.0f); m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); - //qDebug() << m_heightNormalizer << m_areaSize << m_scaleFactor << m_axisCacheY.max() << m_axisCacheX.max() << m_axisCacheZ.max(); -} -QRect Scatter3DRenderer::mainViewPort() -{ - return m_mainViewPort; + // Calculate translation offsets + m_translationOffset = QVector3D((m_axisCacheX.max() + m_axisCacheX.min()) / 2.0f, + (m_axisCacheY.max() + m_axisCacheY.min()) / 2.0f, + (m_axisCacheZ.max() + m_axisCacheZ.min()) / 2.0f); } void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) @@ -1427,12 +1657,20 @@ void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString & m_dotShader->initialize(); } +void Scatter3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader) +{ + if (m_dotGradientShader) + delete m_dotGradientShader; + m_dotGradientShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_dotGradientShader->initialize(); +} + void Scatter3DRenderer::initSelectionShader() { if (m_selectionShader) delete m_selectionShader; - m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSelection"), - QStringLiteral(":/shaders/fragmentSelection")); + m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"), + QStringLiteral(":/shaders/fragmentPlainColor")); m_selectionShader->initialize(); } @@ -1443,10 +1681,10 @@ void Scatter3DRenderer::initSelectionBuffer() m_selectionTexture = 0; } - if (m_mainViewPort.size().isEmpty()) + if (m_primarySubViewport.size().isEmpty()) return; - m_selectionTexture = m_textureHelper->createSelectionTexture(m_mainViewPort.size(), + m_selectionTexture = m_textureHelper->createSelectionTexture(m_primarySubViewport.size(), m_selectionFrameBuffer, m_selectionDepthBuffer); } @@ -1468,52 +1706,26 @@ void Scatter3DRenderer::updateDepthBuffer() m_depthTexture = 0; } - if (m_mainViewPort.size().isEmpty()) + if (m_primarySubViewport.size().isEmpty()) return; if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { - m_depthTexture = m_textureHelper->createDepthTexture(m_mainViewPort.size(), - m_depthFrameBuffer, - m_shadowQualityMultiplier); - if (!m_depthTexture) { - switch (m_cachedShadowQuality) { - case QDataVis::ShadowQualityHigh: - qWarning("Creating high quality shadows failed. Changing to medium quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityMedium); - updateShadowQuality(QDataVis::ShadowQualityMedium); - break; - case QDataVis::ShadowQualityMedium: - qWarning("Creating medium quality shadows failed. Changing to low quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityLow); - updateShadowQuality(QDataVis::ShadowQualityLow); - break; - case QDataVis::ShadowQualityLow: - qWarning("Creating low quality shadows failed. Switching shadows off."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityNone); - updateShadowQuality(QDataVis::ShadowQualityNone); - break; - case QDataVis::ShadowQualitySoftHigh: - qWarning("Creating soft high quality shadows failed. Changing to soft medium quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualitySoftMedium); - updateShadowQuality(QDataVis::ShadowQualitySoftMedium); - break; - case QDataVis::ShadowQualitySoftMedium: - qWarning("Creating soft medium quality shadows failed. Changing to soft low quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualitySoftLow); - updateShadowQuality(QDataVis::ShadowQualitySoftLow); - break; - case QDataVis::ShadowQualitySoftLow: - qWarning("Creating soft low quality shadows failed. Switching shadows off."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityNone); - updateShadowQuality(QDataVis::ShadowQualityNone); - break; - default: - // You'll never get here - break; - } - } + m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + if (!m_depthTexture) + lowerShadowQuality(); } } +#else +void Scatter3DRenderer::initPointShader() +{ + if (m_pointShader) + delete m_pointShader; + m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"), + QStringLiteral(":/shaders/fragmentPlainColor")); + m_pointShader->initialize(); +} #endif void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader, @@ -1542,4 +1754,26 @@ QVector3D Scatter3DRenderer::indexToSelectionColor(GLint index) return QVector3D(dotIdxRed, dotIdxGreen, dotIdxBlue); } +void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector3D &color, int &index, QScatter3DSeries *&series) +{ + if (color != selectionSkipColor) { + index = int(color.x()) + + (int(color.y()) << 8) + + (int(color.z()) << 16); + // Find the series and adjust the index accordingly + for (int i = 0; i < m_renderingArrays.size(); i++) { + if (index < m_renderingArrays.at(i).size()) { + series = static_cast<QScatter3DSeries *>(m_visibleSeriesList.at(i).series()); + return; // Valid found and already set to return parameters, so we can return + } else { + index -= m_renderingArrays.at(i).size(); + } + } + } + + // No valid match found + index = Scatter3DController::invalidSelectionIndex(); + series = 0; +} + QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index f444f891..30ec7489 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -52,25 +52,21 @@ class QT_DATAVISUALIZATION_EXPORT Scatter3DRenderer : public Abstract3DRenderer 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; - // Internal state ScatterRenderItem *m_selectedItem; // points to renderitem array bool m_xFlipped; bool m_zFlipped; bool m_yFlipped; - QRect m_mainViewPort; bool m_updateLabels; ShaderHelper *m_dotShader; + ShaderHelper *m_dotGradientShader; +#if defined(QT_OPENGL_ES_2) + ShaderHelper *m_pointShader; +#endif ShaderHelper *m_depthShader; ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; ShaderHelper *m_labelShader; - ObjectHelper *m_dotObj; ObjectHelper *m_backgroundObj; ObjectHelper *m_gridLineObj; ObjectHelper *m_labelObj; @@ -84,37 +80,40 @@ private: GLint m_shadowQualityMultiplier; GLfloat m_heightNormalizer; GLfloat m_scaleFactor; - QVector3D m_selection; - QVector3D m_previousSelection; + int m_selectedItemIndex; + int m_selectedItemTotalIndex; + int m_selectedItemSeriesIndex; + const QScatter3DSeries *m_selectedSeries; QSizeF m_areaSize; GLfloat m_dotSizeScale; - + QVector3D m_translationOffset; bool m_hasHeightAdjustmentChanged; ScatterRenderItem m_dummyRenderItem; - - ScatterRenderItemArray m_renderItemArray; + QVector<ScatterRenderItemArray> m_renderingArrays; + GLfloat m_backgroundMargin; + GLfloat m_maxItemSize; public: explicit Scatter3DRenderer(Scatter3DController *controller); ~Scatter3DRenderer(); - void updateDataModel(QScatterDataProxy *dataProxy); + void updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility); + void updateData(); void updateScene(Q3DScene *scene); - void render(GLuint defaultFboHandle); - QRect mainViewPort(); + void render(GLuint defaultFboHandle); protected: virtual void initializeOpenGL(); - virtual void loadMeshFile(); private: virtual void initShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader); virtual void updateShadowQuality(QDataVis::ShadowQuality quality); virtual void updateTextures(); + virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh); void drawScene(GLuint defaultFboHandle); - void handleResize(); void loadBackgroundMesh(); void loadGridLineMesh(); @@ -126,6 +125,8 @@ private: #if !defined(QT_OPENGL_ES_2) void initDepthShader(); void updateDepthBuffer(); +#else + void initPointShader(); #endif void calculateTranslation(ScatterRenderItem &item); void calculateSceneScalingFactors(); @@ -135,19 +136,17 @@ private: friend class ScatterRenderItem; public slots: - void updateBackgroundEnabled(bool enable); - // Overloaded from abstract renderer - virtual void updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, qreal min, qreal max); + virtual void updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation, float min, float max); - void updateSelectedItemIndex(int index); + void updateSelectedItem(int index, const QScatter3DSeries *series); signals: - void selectionUpdated(QVector3D selection); - void selectedItemIndexChanged(int index); + void itemClicked(int index, QScatter3DSeries *series); private: QVector3D indexToSelectionColor(GLint index); + void selectionColorToSeriesAndIndex(const QVector3D &color, int &index, QScatter3DSeries *&series); }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/selectionpointer.cpp b/src/datavisualization/engine/selectionpointer.cpp index 19294ac8..c4eced30 100644 --- a/src/datavisualization/engine/selectionpointer.cpp +++ b/src/datavisualization/engine/selectionpointer.cpp @@ -43,9 +43,8 @@ SelectionPointer::SelectionPointer(Drawer *drawer) m_labelObj(0), m_pointObj(0), m_textureHelper(0), - m_isInitialized(false), m_cachedTheme(drawer->theme()), - m_labelStyle(QDataVis::LabelStyleFromTheme), + m_labelBackground(false), m_drawer(drawer), m_cachedScene(0) { @@ -60,27 +59,18 @@ SelectionPointer::~SelectionPointer() delete m_labelShader; delete m_pointShader; delete m_labelObj; - delete m_pointObj; delete m_textureHelper; } void SelectionPointer::initializeOpenGL() { - if (m_isInitialized) - return; - initializeOpenGLFunctions(); m_textureHelper = new TextureHelper(); m_drawer->initializeOpenGL(); initShaders(); - loadLabelMesh(); - loadPointMesh(); - - // Set initialized -flag - m_isInitialized = true; } void SelectionPointer::updateScene(Q3DScene *scene) @@ -142,16 +132,13 @@ void SelectionPointer::render(GLuint defaultFboHandle) m_pointShader->setUniformValue(m_pointShader->view(), viewMatrix); m_pointShader->setUniformValue(m_pointShader->model(), modelMatrix); m_pointShader->setUniformValue(m_pointShader->nModel(), itModelMatrix.inverted().transposed()); - m_pointShader->setUniformValue(m_pointShader->color(), - Utils::vectorFromColor(m_cachedTheme.m_highlightBarColor)); + m_pointShader->setUniformValue(m_pointShader->color(), m_highlightColor); m_pointShader->setUniformValue(m_pointShader->MVP(), MVPMatrix); - m_pointShader->setUniformValue(m_pointShader->ambientS(), m_cachedTheme.m_ambientStrength); - m_pointShader->setUniformValue(m_pointShader->lightS(), m_cachedTheme.m_lightStrength * 2.0f); + m_pointShader->setUniformValue(m_pointShader->ambientS(), m_cachedTheme->ambientLightStrength()); + m_pointShader->setUniformValue(m_pointShader->lightS(), m_cachedTheme->lightStrength() * 2.0f); m_drawer->drawObject(m_pointShader, m_pointObj); - m_pointShader->release(); - // // Draw the label // @@ -162,8 +149,8 @@ void SelectionPointer::render(GLuint defaultFboHandle) modelMatrixLabel.translate(m_position + labelAlign); // Position the label towards the camera - qreal camRotationsX = camera->xRotation(); - qreal camRotationsY = camera->yRotation(); + float camRotationsX = camera->xRotation(); + float camRotationsY = camera->yRotation(); if (!m_cachedIsSlicingActivated) { modelMatrixLabel.rotate(-camRotationsX, 0.0f, 1.0f, 0.0f); modelMatrixLabel.rotate(-camRotationsY, 1.0f, 0.0f, 0.0f); @@ -190,7 +177,8 @@ void SelectionPointer::render(GLuint defaultFboHandle) // Draw the object m_drawer->drawObject(m_labelShader, m_labelObj, m_labelItem.textureId()); - m_labelShader->release(); + // Release shader + glUseProgram(0); // Disable textures glDisable(GL_TEXTURE_2D); @@ -213,13 +201,22 @@ void SelectionPointer::updateSliceData(bool sliceActivated, GLfloat autoScaleAdj m_autoScaleAdjustment = autoScaleAdjustment; } -void SelectionPointer::setLabel(QString label) +void SelectionPointer::setHighlightColor(QVector3D colorVector) { - m_label = label; + m_highlightColor = colorVector; +} +void SelectionPointer::setLabel(const QString &label) +{ + m_label = label; m_drawer->generateLabelItem(m_labelItem, m_label); } +void SelectionPointer::setPointerObject(ObjectHelper *object) +{ + m_pointObj = object; +} + void SelectionPointer::handleDrawerChange() { m_cachedTheme = m_drawer->theme(); @@ -247,7 +244,7 @@ void SelectionPointer::initShaders() m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragment")); #else - m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexES2"), + m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentES2")); #endif m_pointShader->initialize(); @@ -258,16 +255,8 @@ void SelectionPointer::loadLabelMesh() { if (m_labelObj) delete m_labelObj; - m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/label")); + m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); m_labelObj->load(); } -void SelectionPointer::loadPointMesh() -{ - if (m_pointObj) - delete m_pointObj; - m_pointObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/sphereSmooth")); - m_pointObj->load(); -} - QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/selectionpointer_p.h b/src/datavisualization/engine/selectionpointer_p.h index 0e766035..8faeb9ac 100644 --- a/src/datavisualization/engine/selectionpointer_p.h +++ b/src/datavisualization/engine/selectionpointer_p.h @@ -51,7 +51,7 @@ class Theme; class Drawer; class Q3DCamera; -class QT_DATAVISUALIZATION_EXPORT SelectionPointer : public QObject, protected QOpenGLFunctions +class SelectionPointer : public QObject, protected QOpenGLFunctions { Q_OBJECT @@ -59,29 +59,29 @@ public: explicit SelectionPointer(Drawer *drawer); ~SelectionPointer(); - void initializeOpenGL(); void render(GLuint defaultFboHandle = 0); void setPosition(QVector3D position); - void setLabel(QString label); + void setLabel(const QString &label); + void setPointerObject(ObjectHelper *object); void handleDrawerChange(); void updateBoundingRect(QRect rect); void updateScene(Q3DScene *scene); void updateSliceData(bool sliceActivated, GLfloat autoScaleAdjustment); + void setHighlightColor(QVector3D colorVector); private: + void initializeOpenGL(); void initShaders(); void loadLabelMesh(); - void loadPointMesh(); private: ShaderHelper *m_labelShader; ShaderHelper *m_pointShader; ObjectHelper *m_labelObj; - ObjectHelper *m_pointObj; + ObjectHelper *m_pointObj; // Not owned TextureHelper *m_textureHelper; - bool m_isInitialized; - Theme m_cachedTheme; - QDataVis::LabelStyle m_labelStyle; + Q3DTheme *m_cachedTheme; + bool m_labelBackground; LabelItem m_labelItem; Drawer *m_drawer; QRect m_mainViewPort; @@ -90,6 +90,7 @@ private: QString m_label; bool m_cachedIsSlicingActivated; GLfloat m_autoScaleAdjustment; + QVector3D m_highlightColor; }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/seriesrendercache.cpp b/src/datavisualization/engine/seriesrendercache.cpp new file mode 100644 index 00000000..04b2249d --- /dev/null +++ b/src/datavisualization/engine/seriesrendercache.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "seriesrendercache_p.h" +#include "objecthelper_p.h" +#include "abstract3drenderer_p.h" +#include "texturehelper_p.h" +#include "utils_p.h" + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +const QString smoothString(QStringLiteral("Smooth")); + +SeriesRenderCache::SeriesRenderCache() + : m_series(0), + m_object(0), + m_mesh(QAbstract3DSeries::MeshCube), + m_baseGradientTexture(0), + m_singleHighlightGradientTexture(0), + m_multiHighlightGradientTexture(0) +{ +} + +SeriesRenderCache::~SeriesRenderCache() +{ +} + +void SeriesRenderCache::populate(QAbstract3DSeries *series, Abstract3DRenderer *renderer) +{ + Q_ASSERT(series); + + bool seriesChanged = false; + + if (m_series != series) { + m_series = series; + seriesChanged = true; + } + + QAbstract3DSeriesChangeBitField &changeTracker = series->d_ptr->m_changeTracker; + + if (seriesChanged || changeTracker.itemLabelFormatChanged) { + m_itemLabelFormat = series->itemLabelFormat(); + changeTracker.itemLabelFormatChanged = false; + } + + if (seriesChanged || changeTracker.meshChanged || changeTracker.meshSmoothChanged + || changeTracker.userDefinedMeshChanged) { + m_mesh = series->mesh(); + changeTracker.meshChanged = false; + changeTracker.meshSmoothChanged = false; + changeTracker.userDefinedMeshChanged = false; + + QString meshFileName; + + // Compose mesh filename + if (m_mesh == QAbstract3DSeries::MeshUserDefined) { + // Always use the supplied mesh directly + meshFileName = series->userDefinedMesh(); + } else { + switch (m_mesh) { + case QAbstract3DSeries::MeshBar: + case QAbstract3DSeries::MeshCube: + meshFileName = QStringLiteral(":/defaultMeshes/bar"); + break; + case QAbstract3DSeries::MeshPyramid: + meshFileName = QStringLiteral(":/defaultMeshes/pyramid"); + break; + case QAbstract3DSeries::MeshCone: + meshFileName = QStringLiteral(":/defaultMeshes/cone"); + break; + case QAbstract3DSeries::MeshCylinder: + meshFileName = QStringLiteral(":/defaultMeshes/cylinder"); + break; + case QAbstract3DSeries::MeshBevelBar: + case QAbstract3DSeries::MeshBevelCube: + meshFileName = QStringLiteral(":/defaultMeshes/bevelbar"); + break; + case QAbstract3DSeries::MeshSphere: + meshFileName = QStringLiteral(":/defaultMeshes/sphere"); + break; + case QAbstract3DSeries::MeshMinimal: + meshFileName = QStringLiteral(":/defaultMeshes/minimal"); + break; + case QAbstract3DSeries::MeshPoint: +#if defined(QT_OPENGL_ES_2) + qWarning("QAbstract3DSeries::MeshPoint is not fully supported on OpenGL ES2"); +#endif + break; + default: + // Default to cube + meshFileName = QStringLiteral(":/defaultMeshes/bar"); + break; + } + + if (series->isMeshSmooth() && m_mesh != QAbstract3DSeries::MeshPoint) + meshFileName += smoothString; + + // Give renderer an opportunity to customize the mesh + renderer->fixMeshFileName(meshFileName, m_mesh); + } + + // TODO: Optimize by having some kind of object cache in renderer instead of having separate ObjectHelper for each series? + delete m_object; + if (meshFileName.isEmpty()) { + m_object = 0; + } else { + m_object = new ObjectHelper(meshFileName); + m_object->load(); + } + } + + if (seriesChanged || changeTracker.colorStyleChanged) { + m_colorStyle = series->colorStyle(); + changeTracker.colorStyleChanged = false; + } + + if (seriesChanged || changeTracker.baseColorChanged) { + m_baseColor = Utils::vectorFromColor(series->baseColor()); + changeTracker.baseColorChanged = false; + } + + if (seriesChanged || changeTracker.baseGradientChanged) { + QLinearGradient gradient = series->baseGradient(); + renderer->fixGradientAndGenerateTexture(&gradient, &m_baseGradientTexture); + changeTracker.baseGradientChanged = false; + } + + if (seriesChanged || changeTracker.singleHighlightColorChanged) { + m_singleHighlightColor = Utils::vectorFromColor(series->singleHighlightColor()); + changeTracker.singleHighlightColorChanged = false; + } + + if (seriesChanged || changeTracker.singleHighlightGradientChanged) { + QLinearGradient gradient = series->singleHighlightGradient(); + renderer->fixGradientAndGenerateTexture(&gradient, &m_singleHighlightGradientTexture); + changeTracker.singleHighlightGradientChanged = false; + } + + if (seriesChanged || changeTracker.multiHighlightColorChanged) { + m_multiHighlightColor = Utils::vectorFromColor(series->multiHighlightColor()); + changeTracker.multiHighlightColorChanged = false; + } + + if (seriesChanged || changeTracker.multiHighlightGradientChanged) { + QLinearGradient gradient = series->multiHighlightGradient(); + renderer->fixGradientAndGenerateTexture(&gradient, &m_multiHighlightGradientTexture); + changeTracker.multiHighlightGradientChanged = false; + } +} + +void SeriesRenderCache::cleanup(TextureHelper *texHelper) +{ + delete m_object; + texHelper->deleteTexture(&m_baseGradientTexture); + texHelper->deleteTexture(&m_singleHighlightGradientTexture); + texHelper->deleteTexture(&m_multiHighlightGradientTexture); +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/seriesrendercache_p.h b/src/datavisualization/engine/seriesrendercache_p.h new file mode 100644 index 00000000..906a6c0d --- /dev/null +++ b/src/datavisualization/engine/seriesrendercache_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef SERIESRENDERCACHE_P_H +#define SERIESRENDERCACHE_P_H + +#include "datavisualizationglobal_p.h" +#include "qabstract3dseries_p.h" + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class Abstract3DRenderer; +class ObjectHelper; +class TextureHelper; + +class SeriesRenderCache +{ +public: + SeriesRenderCache(); + virtual ~SeriesRenderCache(); + + void populate(QAbstract3DSeries *series, Abstract3DRenderer *renderer); + void cleanup(TextureHelper *texHelper); + + // NOTE: Series pointer can only be used to access the series when syncing with controller. + // It is not guaranteed to be valid while rendering and should only be used as an identifier. + inline QAbstract3DSeries *series() const { return m_series; } + + inline const QString &itemLabelFormat() const { return m_itemLabelFormat; } + inline const QAbstract3DSeries::Mesh &mesh() const { return m_mesh; } + inline ObjectHelper *object() const { return m_object; } + inline const Q3DTheme::ColorStyle &colorStyle() const { return m_colorStyle; } + inline const QVector3D &baseColor() const { return m_baseColor; } + inline const GLuint &baseGradientTexture() const { return m_baseGradientTexture; } + inline const QVector3D &singleHighlightColor() const { return m_singleHighlightColor; } + inline const GLuint &singleHighlightGradientTexture() const { return m_singleHighlightGradientTexture; } + inline const QVector3D &multiHighlightColor() const { return m_multiHighlightColor; } + inline const GLuint &multiHighlightGradientTexture() const { return m_multiHighlightGradientTexture; } + +protected: + QAbstract3DSeries *m_series; + QString m_itemLabelFormat; + ObjectHelper *m_object; + QAbstract3DSeries::Mesh m_mesh; + + Q3DTheme::ColorStyle m_colorStyle; + QVector3D m_baseColor; + GLuint m_baseGradientTexture; + QVector3D m_singleHighlightColor; + GLuint m_singleHighlightGradientTexture; + QVector3D m_multiHighlightColor; + GLuint m_multiHighlightGradientTexture; +}; + +QT_DATAVISUALIZATION_END_NAMESPACE + +#endif + diff --git a/src/datavisualization/engine/shaders/ambient.frag b/src/datavisualization/engine/shaders/ambient.frag deleted file mode 100644 index 88f850f3..00000000 --- a/src/datavisualization/engine/shaders/ambient.frag +++ /dev/null @@ -1,32 +0,0 @@ -#version 120 - -uniform highp vec3 lightPosition_wrld; -uniform highp vec3 color_mdl; - -varying highp vec3 position_wrld; -varying highp vec3 normal_cmr; -varying highp vec3 eyeDirection_cmr; -varying highp vec3 lightDirection_cmr; - -void main() { - highp vec3 lightColor = vec3(1.0, 1.0, 1.0); - highp float lightPower = 10.0; - highp vec3 materialAmbientColor = vec3(0.5, 0.5, 0.5) * color_mdl; - highp vec3 materialSpecularColor = vec3(0.3, 0.3, 0.3) * color_mdl; - - highp float distance = length(lightPosition_wrld - position_wrld); - - highp vec3 n = normalize(normal_cmr); - highp vec3 l = normalize(lightDirection_cmr); - highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); - - highp vec3 E = normalize(eyeDirection_cmr); - highp vec3 R = reflect(-l, n); - highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); - - gl_FragColor.rgb = - materialAmbientColor + - materialSpecularColor * lightColor * lightPower * pow(cosAlpha, 5) / (distance * distance); - gl_FragColor.a = 1.0; -} - diff --git a/src/datavisualization/engine/shaders/colorOnY.frag b/src/datavisualization/engine/shaders/colorOnY.frag index 80e54a61..caea959b 100644 --- a/src/datavisualization/engine/shaders/colorOnY.frag +++ b/src/datavisualization/engine/shaders/colorOnY.frag @@ -1,9 +1,11 @@ #version 120 uniform highp vec3 lightPosition_wrld; -uniform highp vec3 color_mdl; uniform highp float lightStrength; uniform highp float ambientStrength; +uniform sampler2D textureSampler; +uniform highp float gradMin; +uniform highp float gradHeight; varying highp vec3 position_wrld; varying highp vec3 normal_cmr; @@ -12,8 +14,8 @@ varying highp vec3 lightDirection_cmr; varying highp vec2 coords_mdl; void main() { - highp float heightMod = (coords_mdl.y * 0.3) + 0.7; // Add 30% black to the bottom - highp vec3 materialDiffuseColor = heightMod * color_mdl; + highp vec2 gradientUV = vec2(0.0, gradMin + ((coords_mdl.y + 1.0) * gradHeight)); + highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); diff --git a/src/datavisualization/engine/shaders/colorOnY_ES2.frag b/src/datavisualization/engine/shaders/colorOnY_ES2.frag index aba52cfe..bb6e28c7 100644 --- a/src/datavisualization/engine/shaders/colorOnY_ES2.frag +++ b/src/datavisualization/engine/shaders/colorOnY_ES2.frag @@ -1,7 +1,9 @@ uniform highp vec3 lightPosition_wrld; -uniform highp vec3 color_mdl; uniform highp float lightStrength; uniform highp float ambientStrength; +uniform sampler2D textureSampler; +uniform highp float gradMin; +uniform highp float gradHeight; varying highp vec3 position_wrld; varying highp vec3 normal_cmr; @@ -10,8 +12,8 @@ varying highp vec3 lightDirection_cmr; varying highp vec2 coords_mdl; void main() { - highp float heightMod = (coords_mdl.y * 0.3) + 0.7; // Add 30% black to the bottom - highp vec3 materialDiffuseColor = heightMod * color_mdl; + highp vec2 gradientUV = vec2(0.0, gradMin + ((coords_mdl.y + 1.0) * gradHeight)); + highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); diff --git a/src/datavisualization/engine/shaders/default.vert b/src/datavisualization/engine/shaders/default.vert index 14f77773..efb40862 100644 --- a/src/datavisualization/engine/shaders/default.vert +++ b/src/datavisualization/engine/shaders/default.vert @@ -1,5 +1,3 @@ -#version 120 - attribute highp vec3 vertexPosition_mdl; attribute highp vec2 vertexUV; attribute highp vec3 vertexNormal_mdl; diff --git a/src/datavisualization/engine/shaders/default_ES2.vert b/src/datavisualization/engine/shaders/default_ES2.vert deleted file mode 100644 index efb40862..00000000 --- a/src/datavisualization/engine/shaders/default_ES2.vert +++ /dev/null @@ -1,26 +0,0 @@ -attribute highp vec3 vertexPosition_mdl; -attribute highp vec2 vertexUV; -attribute highp vec3 vertexNormal_mdl; - -uniform highp mat4 MVP; -uniform highp mat4 V; -uniform highp mat4 M; -uniform highp mat4 itM; -uniform highp vec3 lightPosition_wrld; - -varying highp vec3 position_wrld; -varying highp vec3 normal_cmr; -varying highp vec3 eyeDirection_cmr; -varying highp vec3 lightDirection_cmr; -varying highp vec2 coords_mdl; - -void main() { - gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); - coords_mdl = vertexPosition_mdl.xy; - position_wrld = (M * vec4(vertexPosition_mdl, 1.0)).xyz; - vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz; - eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr; - vec3 lightPosition_cmr = (V * vec4(lightPosition_wrld, 1.0)).xyz; - lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; - normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; -} diff --git a/src/datavisualization/engine/shaders/selection.frag b/src/datavisualization/engine/shaders/plainColor.frag index 099c87a1..099c87a1 100644 --- a/src/datavisualization/engine/shaders/selection.frag +++ b/src/datavisualization/engine/shaders/plainColor.frag diff --git a/src/datavisualization/engine/shaders/selection.vert b/src/datavisualization/engine/shaders/plainColor.vert index 64d17e15..64d17e15 100644 --- a/src/datavisualization/engine/shaders/selection.vert +++ b/src/datavisualization/engine/shaders/plainColor.vert diff --git a/src/datavisualization/engine/shaders/surfaceGrid.vert b/src/datavisualization/engine/shaders/point_ES2.vert index 5582d633..b2dfdd7b 100644 --- a/src/datavisualization/engine/shaders/surfaceGrid.vert +++ b/src/datavisualization/engine/shaders/point_ES2.vert @@ -1,6 +1,8 @@ -attribute highp vec3 vertexPosition_mdl; uniform highp mat4 MVP; +attribute highp vec3 vertexPosition_mdl; + void main() { + gl_PointSize = 5.0; gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); } diff --git a/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag b/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag index 68ba2368..9882cd92 100644 --- a/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag +++ b/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag @@ -3,8 +3,10 @@ uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp float shadowQuality; -uniform highp vec3 color_mdl; uniform highp sampler2DShadow shadowMap; +uniform sampler2D textureSampler; +uniform highp float gradMin; +uniform highp float gradHeight; varying highp vec4 shadowCoord; varying highp vec3 position_wrld; @@ -37,8 +39,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), }*/ void main() { - highp float heightMod = (coords_mdl.y * 0.3) + 0.7; // Add 30% black to the bottom - highp vec3 materialDiffuseColor = heightMod * color_mdl; + highp vec2 gradientUV = vec2(0.0, gradMin + ((coords_mdl.y + 1.0) * gradHeight)); + highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); diff --git a/src/datavisualization/engine/shaders/surface.frag b/src/datavisualization/engine/shaders/surface.frag index 4b1357b1..576cc51f 100644 --- a/src/datavisualization/engine/shaders/surface.frag +++ b/src/datavisualization/engine/shaders/surface.frag @@ -1,6 +1,6 @@ #version 120 -varying highp vec3 coords_mdl; +varying highp vec2 coords_mdl; varying highp vec3 position_wrld; varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; @@ -12,7 +12,7 @@ uniform highp float lightStrength; uniform highp float ambientStrength; void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.001) / 2.0); // 1000 pixel texture, we need a margin for 1/1000 rounding error + highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); diff --git a/src/datavisualization/engine/shaders/surfaceFlat.frag b/src/datavisualization/engine/shaders/surfaceFlat.frag index 1f2a3822..fd42a289 100644 --- a/src/datavisualization/engine/shaders/surfaceFlat.frag +++ b/src/datavisualization/engine/shaders/surfaceFlat.frag @@ -14,7 +14,7 @@ uniform highp float lightStrength; uniform highp float ambientStrength; void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.001) / 2.0); // 1000 pixel texture, we need a margin for 1/1000 rounding error + highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); diff --git a/src/datavisualization/engine/shaders/surfaceGrid.frag b/src/datavisualization/engine/shaders/surfaceGrid.frag deleted file mode 100644 index 1658b316..00000000 --- a/src/datavisualization/engine/shaders/surfaceGrid.frag +++ /dev/null @@ -1,6 +0,0 @@ -uniform highp vec3 color_mdl; - -void main() { - gl_FragColor.rgb = color_mdl; -} - diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag new file mode 100644 index 00000000..6341136e --- /dev/null +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag @@ -0,0 +1,70 @@ +#version 120 + +#extension GL_EXT_gpu_shader4 : require + +varying highp vec3 coords_mdl; +varying highp vec3 position_wrld; +flat varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; + +uniform highp sampler2DShadow shadowMap; +uniform sampler2D textureSampler; +varying highp vec4 shadowCoord; +uniform highp vec3 lightPosition_wrld; +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp float shadowQuality; + +highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), + vec2(0.94558609, -0.76890725), + vec2(-0.094184101, -0.92938870), + vec2(0.34495938, 0.29387760), + vec2(-0.91588581, 0.45771432), + vec2(-0.81544232, -0.87912464), + vec2(-0.38277543, 0.27676845), + vec2(0.97484398, 0.75648379), + vec2(0.44323325, -0.97511554), + vec2(0.53742981, -0.47373420), + vec2(-0.26496911, -0.41893023), + vec2(0.79197514, 0.19090188), + vec2(-0.24188840, 0.99706507), + vec2(-0.81409955, 0.91437590), + vec2(0.19984126, 0.78641367), + vec2(0.14383161, -0.14100790)); + +void main() { + highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; + highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; + highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); + + highp vec3 n = normalize(normal_cmr); + highp vec3 l = normalize(lightDirection_cmr); + highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); + + highp vec3 E = normalize(eyeDirection_cmr); + highp vec3 R = reflect(-l, n); + highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); + + highp float bias = 0.005 * tan(acos(cosTheta)); + bias = clamp(bias, 0.0, 0.01); + + vec4 shadCoords = shadowCoord; + shadCoords.z -= bias; + + highp float visibility = 0.6; + for (int i = 0; i < 15; i++) { + vec4 shadCoordsPD = shadCoords; + shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality; + shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality; + visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r; + } + + gl_FragColor.rgb = + visibility * (materialAmbientColor + + materialDiffuseColor * lightStrength * cosTheta + + materialSpecularColor * lightStrength * pow(cosAlpha, 10)); + gl_FragColor.a = 1.0; +} + diff --git a/src/datavisualization/engine/shaders/surface.vert b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert index 28152abc..0a6e967f 100644 --- a/src/datavisualization/engine/shaders/surface.vert +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert @@ -1,3 +1,7 @@ +#version 120 + +#extension GL_EXT_gpu_shader4 : require + attribute highp vec3 vertexPosition_mdl; attribute highp vec3 vertexNormal_mdl; @@ -5,17 +9,25 @@ uniform highp mat4 MVP; uniform highp mat4 V; uniform highp mat4 M; uniform highp mat4 itM; +uniform highp mat4 depthMVP; uniform highp vec3 lightPosition_wrld; varying highp vec3 position_wrld; -varying highp vec3 normal_cmr; +flat varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; +varying highp vec4 shadowCoord; varying highp vec3 coords_mdl; +const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + void main() { gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); coords_mdl = vertexPosition_mdl; + shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0); position_wrld = (M * vec4(vertexPosition_mdl, 1.0)).xyz; vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz; eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr; diff --git a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag new file mode 100644 index 00000000..755be3f1 --- /dev/null +++ b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag @@ -0,0 +1,68 @@ +#version 120 + +varying highp vec2 coords_mdl; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; + +uniform highp sampler2DShadow shadowMap; +uniform sampler2D textureSampler; +varying highp vec4 shadowCoord; +uniform highp vec3 lightPosition_wrld; +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp float shadowQuality; + +highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), + vec2(0.94558609, -0.76890725), + vec2(-0.094184101, -0.92938870), + vec2(0.34495938, 0.29387760), + vec2(-0.91588581, 0.45771432), + vec2(-0.81544232, -0.87912464), + vec2(-0.38277543, 0.27676845), + vec2(0.97484398, 0.75648379), + vec2(0.44323325, -0.97511554), + vec2(0.53742981, -0.47373420), + vec2(-0.26496911, -0.41893023), + vec2(0.79197514, 0.19090188), + vec2(-0.24188840, 0.99706507), + vec2(-0.81409955, 0.91437590), + vec2(0.19984126, 0.78641367), + vec2(0.14383161, -0.14100790)); + +void main() { + highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; + highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; + highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); + + highp vec3 n = normalize(normal_cmr); + highp vec3 l = normalize(lightDirection_cmr); + highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); + + highp vec3 E = normalize(eyeDirection_cmr); + highp vec3 R = reflect(-l, n); + highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); + + highp float bias = 0.005 * tan(acos(cosTheta)); + bias = clamp(bias, 0.0, 0.01); + + vec4 shadCoords = shadowCoord; + shadCoords.z -= bias; + + highp float visibility = 0.6; + for (int i = 0; i < 15; i++) { + vec4 shadCoordsPD = shadCoords; + shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality; + shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality; + visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r; + } + + gl_FragColor.rgb = + visibility * (materialAmbientColor + + materialDiffuseColor * lightStrength * cosTheta + + materialSpecularColor * lightStrength * pow(cosAlpha, 10)); + gl_FragColor.a = 1.0; +} + diff --git a/src/datavisualization/engine/shaders/surface_ES2.frag b/src/datavisualization/engine/shaders/surface_ES2.frag index 7f40ba4d..c7e75594 100644 --- a/src/datavisualization/engine/shaders/surface_ES2.frag +++ b/src/datavisualization/engine/shaders/surface_ES2.frag @@ -1,5 +1,5 @@ varying highp vec2 UV; -varying highp vec3 coords_mdl; +varying highp vec2 coords_mdl; varying highp vec3 position_wrld; varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; @@ -11,7 +11,7 @@ uniform highp float lightStrength; uniform highp float ambientStrength; void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.001) / 2.0); // ~1000 pixel texture, we need a margin for 1/1000 rounding error + highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); diff --git a/src/datavisualization/engine/shaders/texture.frag b/src/datavisualization/engine/shaders/texture.frag deleted file mode 100644 index a6d7b2eb..00000000 --- a/src/datavisualization/engine/shaders/texture.frag +++ /dev/null @@ -1,35 +0,0 @@ -#version 120 - -uniform highp vec3 lightPosition_wrld; -uniform highp vec3 color_mdl; -uniform highp float lightStrength; -uniform highp float ambientStrength; -uniform sampler2D textureSampler; - -varying highp vec2 UV; -varying highp vec3 position_wrld; -varying highp vec3 normal_cmr; -varying highp vec3 eyeDirection_cmr; -varying highp vec3 lightDirection_cmr; - -void main() { - highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb; - highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; - highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); - - highp float distance = length(lightPosition_wrld - position_wrld); - highp vec3 n = normalize(normal_cmr); - highp vec3 l = normalize(lightDirection_cmr); - highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); - - highp vec3 E = normalize(eyeDirection_cmr); - highp vec3 R = reflect(-l, n); - highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); - - gl_FragColor.rgb = - materialAmbientColor + - materialDiffuseColor * lightStrength * (cosTheta * cosTheta) / distance + - materialSpecularColor * lightStrength * pow(cosAlpha, 10) / distance; - gl_FragColor.a = texture2D(textureSampler, UV).a; -} - diff --git a/src/datavisualization/engine/shaders/texture.vert b/src/datavisualization/engine/shaders/texture.vert deleted file mode 100644 index 01f922e0..00000000 --- a/src/datavisualization/engine/shaders/texture.vert +++ /dev/null @@ -1,26 +0,0 @@ -uniform highp mat4 MVP; -uniform highp mat4 V; -uniform highp mat4 M; -uniform highp mat4 itM; -uniform highp vec3 lightPosition_wrld; - -attribute highp vec3 vertexPosition_mdl; -attribute highp vec2 vertexUV; -attribute highp vec3 vertexNormal_mdl; - -varying highp vec2 UV; -varying highp vec3 position_wrld; -varying highp vec3 normal_cmr; -varying highp vec3 eyeDirection_cmr; -varying highp vec3 lightDirection_cmr; - -void main() { - gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); - position_wrld = (M * vec4(vertexPosition_mdl, 1.0)).xyz; - vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz; - eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr; - vec3 lightPosition_cmr = (V * vec4(lightPosition_wrld, 1.0)).xyz; - lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; - normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; - UV = vertexUV; -} diff --git a/src/datavisualization/engine/shaders/texture_ES2.frag b/src/datavisualization/engine/shaders/texture_ES2.frag deleted file mode 100644 index 58097ba5..00000000 --- a/src/datavisualization/engine/shaders/texture_ES2.frag +++ /dev/null @@ -1,37 +0,0 @@ -uniform highp vec3 lightPosition_wrld; -uniform highp vec3 color_mdl; -uniform highp float lightStrength; -uniform highp float ambientStrength; -uniform sampler2D textureSampler; - -varying highp vec2 UV; -varying highp vec3 position_wrld; -varying highp vec3 normal_cmr; -varying highp vec3 eyeDirection_cmr; -varying highp vec3 lightDirection_cmr; - -void main() { - highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb; - highp vec3 materialAmbientColor = vec3(ambientStrength, ambientStrength, ambientStrength) * materialDiffuseColor; - highp vec3 materialSpecularColor = vec3(1.0, 1.0, 1.0); - - highp float distance = length(lightPosition_wrld - position_wrld); - highp vec3 n = normalize(normal_cmr); - highp vec3 l = normalize(lightDirection_cmr); - highp float cosTheta = dot(n, l); - if (cosTheta < 0.0) cosTheta = 0.0; - else if (cosTheta > 1.0) cosTheta = 1.0; - - highp vec3 E = normalize(eyeDirection_cmr); - highp vec3 R = reflect(-l, n); - highp float cosAlpha = dot(E, R); - if (cosAlpha < 0.0) cosAlpha = 0.0; - else if (cosAlpha > 1.0) cosAlpha = 1.0; - - gl_FragColor.rgb = - materialAmbientColor + - materialDiffuseColor * lightStrength * (cosTheta * cosTheta) / distance + - materialSpecularColor * lightStrength * (cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha) / distance; - gl_FragColor.a = texture2D(textureSampler, UV).a; -} - diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp index 8d1bcf85..3cec72d1 100644 --- a/src/datavisualization/engine/surface3dcontroller.cpp +++ b/src/datavisualization/engine/surface3dcontroller.cpp @@ -23,6 +23,8 @@ #include "q3dvalueaxis_p.h" #include "q3dcategoryaxis.h" #include "qsurfacedataproxy_p.h" +#include "qsurface3dseries_p.h" +#include "shaderhelper_p.h" #include <QMatrix4x4> @@ -33,20 +35,16 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE Surface3DController::Surface3DController(QRect rect) : Abstract3DController(rect), m_renderer(0), - m_isSmoothSurfaceEnabled(false), - m_isSurfaceGridEnabled(true) + m_selectedPoint(invalidSelectionPosition()), + m_selectedSeries(0), + m_flatShadingSupported(true) { - setActiveDataProxy(0); - // Setting a null axis creates a new default axis according to orientation and graph type. // Note: these cannot be set in the Abstract3DController constructor, as they will call virtual // functions implemented by subclasses. setAxisX(0); setAxisY(0); setAxisZ(0); - - // Set the default from the theme - m_userDefinedGradient = theme().m_surfaceGradient; } Surface3DController::~Surface3DController() @@ -62,38 +60,34 @@ void Surface3DController::initializeOpenGL() m_renderer = new Surface3DRenderer(this); setRenderer(m_renderer); synchDataToRenderer(); + QObject::connect(m_renderer, &Surface3DRenderer::pointClicked, this, + &Surface3DController::handlePointClicked, Qt::QueuedConnection); emitNeedRender(); } void Surface3DController::synchDataToRenderer() { - Abstract3DController::synchDataToRenderer(); - if (!isInitialized()) return; - // Notify changes to renderer - if (m_changeTracker.gradientColorChanged) { - m_renderer->updateSurfaceGradient(m_userDefinedGradient); - m_changeTracker.gradientColorChanged = false; - } + Abstract3DController::synchDataToRenderer(); - if (m_changeTracker.smoothStatusChanged) { - bool oldSmoothStatus = m_isSmoothSurfaceEnabled; - m_isSmoothSurfaceEnabled = m_renderer->updateSmoothStatus(m_isSmoothSurfaceEnabled); - m_changeTracker.smoothStatusChanged = false; - if (oldSmoothStatus != m_isSmoothSurfaceEnabled) - emit smoothSurfaceEnabledChanged(m_isSmoothSurfaceEnabled); + // Notify changes to renderer + if (m_changeTracker.rowsChanged) { + m_renderer->updateRows(m_changedRows); + m_changeTracker.rowsChanged = false; + m_changedRows.clear(); } - if (m_changeTracker.surfaceGridChanged) { - m_renderer->updateSurfaceGridStatus(m_isSurfaceGridEnabled); - m_changeTracker.surfaceGridChanged = false; + if (m_changeTracker.itemChanged) { + m_renderer->updateItem(m_changedItems); + m_changeTracker.itemChanged = false; + m_changedItems.clear(); } - if (m_isDataDirty) { - m_renderer->updateDataModel(static_cast<QSurfaceDataProxy *>(m_data)); - m_isDataDirty = false; + if (m_changeTracker.selectedPointChanged) { + m_renderer->updateSelectedPoint(m_selectedPoint, m_selectedSeries); + m_changeTracker.selectedPointChanged = false; } } @@ -107,132 +101,392 @@ void Surface3DController::handleAxisAutoAdjustRangeChangedInOrientation(Q3DAbstr void Surface3DController::handleAxisRangeChangedBySender(QObject *sender) { - scene()->setSlicingActive(false); Abstract3DController::handleAxisRangeChangedBySender(sender); + + // Update selected point - may be moved offscreen + setSelectedPoint(m_selectedPoint, m_selectedSeries); } -void Surface3DController::setSmoothSurface(bool enable) +void Surface3DController::handleSeriesVisibilityChangedBySender(QObject *sender) { - bool changed = m_isSmoothSurfaceEnabled != enable; - m_isSmoothSurfaceEnabled = enable; - m_changeTracker.smoothStatusChanged = true; - emitNeedRender(); + Abstract3DController::handleSeriesVisibilityChangedBySender(sender); - if (changed) - emit smoothSurfaceEnabledChanged(m_isSmoothSurfaceEnabled); + // Visibility changes may require disabling/enabling slicing, + // so just reset selection to ensure everything is still valid. + setSelectedPoint(m_selectedPoint, m_selectedSeries); } -bool Surface3DController::smoothSurface() +QPoint Surface3DController::invalidSelectionPosition() { - return m_isSmoothSurfaceEnabled; + static QPoint invalidSelectionPoint(-1, -1); + return invalidSelectionPoint; } -void Surface3DController::setSurfaceGrid(bool enable) +bool Surface3DController::isFlatShadingSupported() { - m_isSurfaceGridEnabled = enable; - m_changeTracker.surfaceGridChanged = true; - emitNeedRender(); + return m_flatShadingSupported; } -bool Surface3DController::surfaceGrid() +void Surface3DController::addSeries(QAbstract3DSeries *series) { - return m_isSurfaceGridEnabled; -} + Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesTypeSurface); -void Surface3DController::setGradient(const QLinearGradient &gradient) -{ - m_userDefinedGradient = gradient; - m_userDefinedGradient.setStart(2, 1024); - m_userDefinedGradient.setFinalStop(0, 0); - m_changeTracker.gradientColorChanged = true; - emitNeedRender(); + if (!m_seriesList.size()) { + Abstract3DController::addSeries(series); + + adjustValueAxisRange(); + } else { + qWarning("Surface graph only supports a single series."); + } + + QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series); + if (surfaceSeries->selectedPoint() != invalidSelectionPosition()) + setSelectedPoint(surfaceSeries->selectedPoint(), surfaceSeries); } -QLinearGradient Surface3DController::gradient() const +void Surface3DController::removeSeries(QAbstract3DSeries *series) { - return m_userDefinedGradient; + if (series && series->d_ptr->m_controller == this) { + Abstract3DController::removeSeries(series); + + if (m_selectedSeries == series) + setSelectedPoint(invalidSelectionPosition(), 0); + + adjustValueAxisRange(); + } } -void Surface3DController::setGradientColorAt(qreal pos, const QColor &color) +QList<QSurface3DSeries *> Surface3DController::surfaceSeriesList() { - m_userDefinedGradient.setColorAt(pos, color); - m_changeTracker.gradientColorChanged = true; - emitNeedRender(); + QList<QAbstract3DSeries *> abstractSeriesList = seriesList(); + QList<QSurface3DSeries *> surfaceSeriesList; + foreach (QAbstract3DSeries *abstractSeries, abstractSeriesList) { + QSurface3DSeries *surfaceSeries = qobject_cast<QSurface3DSeries *>(abstractSeries); + if (surfaceSeries) + surfaceSeriesList.append(surfaceSeries); + } + + return surfaceSeriesList; } -void Surface3DController::setSelectionMode(QDataVis::SelectionMode mode) +void Surface3DController::setSelectionMode(QDataVis::SelectionFlags mode) { - if (!(mode == QDataVis::SelectionModeNone || mode == QDataVis::SelectionModeItem - || mode == QDataVis::SelectionModeSliceRow - || mode == QDataVis::SelectionModeSliceColumn)) { + // Currently surface only supports row and column modes when also slicing + if ((mode.testFlag(QDataVis::SelectionRow) || mode.testFlag(QDataVis::SelectionColumn)) + && !mode.testFlag(QDataVis::SelectionSlice)) { qWarning("Unsupported selection mode."); return; + } else if (mode.testFlag(QDataVis::SelectionSlice) + && (mode.testFlag(QDataVis::SelectionRow) == mode.testFlag(QDataVis::SelectionColumn))) { + qWarning("Must specify one of either row or column selection mode in conjunction with slicing mode."); + } else { + QDataVis::SelectionFlags oldMode = selectionMode(); + + Abstract3DController::setSelectionMode(mode); + + if (mode != oldMode) { + // Refresh selection upon mode change to ensure slicing is correctly updated + // according to series the visibility. + setSelectedPoint(m_selectedPoint, m_selectedSeries); + + // Special case: Always deactivate slicing when changing away from slice + // automanagement, as this can't be handled in setSelectedBar. + if (!mode.testFlag(QDataVis::SelectionSlice) + && oldMode.testFlag(QDataVis::SelectionSlice)) { + scene()->setSlicingActive(false); + } + } } - // Disable zoom if selection mode changes - setSlicingActive(false); - Abstract3DController::setSelectionMode(mode); } +void Surface3DController::setSelectedPoint(const QPoint &position, QSurface3DSeries *series) +{ + // If the selection targets non-existent point, clear selection instead. + QPoint pos = position; + + // Series may already have been removed, so check it before setting the selection. + if (!m_seriesList.contains(series)) + series = 0; + + const QSurfaceDataProxy *proxy = 0; + if (series) + proxy = series->dataProxy(); + + if (!proxy) + pos = invalidSelectionPosition(); + + if (pos != invalidSelectionPosition()) { + int maxRow = proxy->rowCount() - 1; + int maxCol = proxy->columnCount() - 1; + + if (pos.x() < 0 || pos.x() > maxRow || pos.y() < 0 || pos.y() > maxCol) + pos = invalidSelectionPosition(); + } + + if (selectionMode().testFlag(QDataVis::SelectionSlice)) { + if (pos == invalidSelectionPosition() || !series->isVisible()) { + scene()->setSlicingActive(false); + } else { + // If the selected point is outside data window, or there is no selected point, disable slicing + // TODO: (QTRD-2351) This logic doesn't match the renderer logic for non straight surfaces, + // but that logic needs to change anyway, so this is good for now. + float axisMinX = m_axisX->min(); + float axisMaxX = m_axisX->max(); + float axisMinZ = m_axisZ->min(); + float axisMaxZ = m_axisZ->max(); + + QSurfaceDataItem item = proxy->array()->at(pos.x())->at(pos.y()); + if (item.x() < axisMinX || item.x() > axisMaxX + || item.z() < axisMinZ || item.z() > axisMaxZ) { + scene()->setSlicingActive(false); + } else { + scene()->setSlicingActive(true); + } + } + emitNeedRender(); + } + + if (pos != m_selectedPoint) { + m_selectedPoint = pos; + m_selectedSeries = series; + m_changeTracker.selectedPointChanged = true; + + // Clear selection from other series and finally set new selection to the specified series + foreach (QAbstract3DSeries *otherSeries, m_seriesList) { + QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(otherSeries); + if (surfaceSeries != m_selectedSeries) + surfaceSeries->dptr()->setSelectedPoint(invalidSelectionPosition()); + } + if (m_selectedSeries) + m_selectedSeries->dptr()->setSelectedPoint(m_selectedPoint); -void Surface3DController::setActiveDataProxy(QAbstractDataProxy *proxy) + emitNeedRender(); + } +} + +void Surface3DController::handleArrayReset() +{ + adjustValueAxisRange(); + m_isDataDirty = true; + // Clear selection unless still valid + setSelectedPoint(m_selectedPoint, m_selectedSeries); + emitNeedRender(); +} + +void Surface3DController::handlePointClicked(const QPoint &position, QSurface3DSeries *series) { - // Setting null proxy indicates default proxy - if (!proxy) { - proxy = new QSurfaceDataProxy; - proxy->d_ptr->setDefaultProxy(true); + setSelectedPoint(position, series); + // TODO: pass clicked to parent. (QTRD-2517) + // TODO: Also hover needed? (QTRD-2131) +} + +void Surface3DController::handleFlatShadingSupportedChange(bool supported) +{ + // Handle renderer flat surface support indicator signal. This happens exactly once per renderer. + if (m_flatShadingSupported != supported) { + m_flatShadingSupported = supported; + // Emit the change for all added surfaces + foreach (QAbstract3DSeries *series, m_seriesList) { + QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series); + emit surfaceSeries->flatShadingSupportedChanged(m_flatShadingSupported); + } } +} - Q_ASSERT(proxy->type() == QAbstractDataProxy::DataTypeSurface); +void Surface3DController::handleRowsChanged(int startIndex, int count) +{ + QSurfaceDataProxy *sender = static_cast<QSurfaceDataProxy *>(QObject::sender()); + if (m_changedRows.size() == 0) + m_changedRows.reserve(sender->rowCount()); + + if (static_cast<QSurface3DSeries *>(m_seriesList.at(0)) == sender->series()) { + // Change is for the visible series, put the change to queue + int oldChangeCount = m_changedRows.size(); + for (int i = 0; i < count; i++) { + bool newItem = true; + int candidate = startIndex + i; + for (int i = 0; i < oldChangeCount; i++) { + if (m_changedRows.at(i) == candidate) { + newItem = false; + break; + } + } + if (newItem) + m_changedRows.append(candidate); + } + if (m_changedRows.size()) { + m_changeTracker.rowsChanged = true; - Abstract3DController::setActiveDataProxy(proxy); + adjustValueAxisRange(); + // Clear selection unless still valid + setSelectedPoint(m_selectedPoint, m_selectedSeries); + emitNeedRender(); + } + } +} - QSurfaceDataProxy *surfaceDataProxy = static_cast<QSurfaceDataProxy *>(m_data); +void Surface3DController::handleItemChanged(int rowIndex, int columnIndex) +{ + QSurfaceDataProxy *sender = static_cast<QSurfaceDataProxy *>(QObject::sender()); + if (static_cast<QSurface3DSeries *>(m_seriesList.at(0)) == sender->series()) { + // Change is for the visible series, put the change to queue + bool newItem = true; + QPoint candidate(columnIndex, rowIndex); + foreach (QPoint item, m_changedItems) { + if (item == candidate) { + newItem = false; + break; + } + } + if (newItem) { + m_changedItems.append(candidate); + m_changeTracker.itemChanged = true; + + adjustValueAxisRange(); + // Clear selection unless still valid + setSelectedPoint(m_selectedPoint, m_selectedSeries); + emitNeedRender(); + } + } +} - QObject::connect(surfaceDataProxy, &QSurfaceDataProxy::arrayReset, - this, &Surface3DController::handleArrayReset); +void Surface3DController::handleRowsAdded(int startIndex, int count) +{ + Q_UNUSED(startIndex) + Q_UNUSED(count) + adjustValueAxisRange(); + m_isDataDirty = true; + emitNeedRender(); +} - scene()->setSlicingActive(false); +void Surface3DController::handleRowsInserted(int startIndex, int count) +{ + Q_UNUSED(startIndex) + Q_UNUSED(count) adjustValueAxisRange(); m_isDataDirty = true; emitNeedRender(); } -void Surface3DController::handleArrayReset() +void Surface3DController::handleRowsRemoved(int startIndex, int count) { - scene()->setSlicingActive(false); + Q_UNUSED(startIndex) + Q_UNUSED(count) adjustValueAxisRange(); m_isDataDirty = true; + + // Clear selection unless still valid + setSelectedPoint(m_selectedPoint, m_selectedSeries); + emitNeedRender(); } void Surface3DController::adjustValueAxisRange() { - if (m_data) { - QVector3D minLimits; - QVector3D maxLimits; - static_cast<QSurfaceDataProxy *>(m_data)->dptr()->limitValues(minLimits, maxLimits); - Q3DValueAxis *valueAxis = static_cast<Q3DValueAxis *>(m_axisX); - if (valueAxis && valueAxis->isAutoAdjustRange()) { - if (minLimits.x() != maxLimits.x()) - valueAxis->dptr()->setRange(minLimits.x(), maxLimits.x()); - else - valueAxis->dptr()->setRange(minLimits.x() - 1.0f, minLimits.x() + 1.0f); // Default to some valid range + Q3DValueAxis *valueAxisX = static_cast<Q3DValueAxis *>(m_axisX); + Q3DValueAxis *valueAxisY = static_cast<Q3DValueAxis *>(m_axisY); + Q3DValueAxis *valueAxisZ = static_cast<Q3DValueAxis *>(m_axisZ); + bool adjustX = (valueAxisX && valueAxisX->isAutoAdjustRange()); + bool adjustY = (valueAxisY && valueAxisY->isAutoAdjustRange()); + bool adjustZ = (valueAxisZ && valueAxisZ->isAutoAdjustRange()); + + if (adjustX || adjustY || adjustZ) { + float minValueX = 0.0f; + float maxValueX = 0.0f; + float minValueY = 0.0f; + float maxValueY = 0.0f; + float minValueZ = 0.0f; + float maxValueZ = 0.0f; + int seriesCount = m_seriesList.size(); + for (int series = 0; series < seriesCount; series++) { + const QSurface3DSeries *surfaceSeries = + static_cast<QSurface3DSeries *>(m_seriesList.at(series)); + const QSurfaceDataProxy *proxy = surfaceSeries->dataProxy(); + if (surfaceSeries->isVisible() && proxy) { + QVector3D minLimits; + QVector3D maxLimits; + proxy->dptrc()->limitValues(minLimits, maxLimits); + if (adjustX) { + if (!series) { + // First series initializes the values + minValueX = minLimits.x(); + maxValueX = maxLimits.x(); + } else { + minValueX = qMin(minValueX, minLimits.x()); + maxValueX = qMax(maxValueX, maxLimits.x()); + } + } + if (adjustY) { + if (!series) { + // First series initializes the values + minValueY = minLimits.y(); + maxValueY = maxLimits.y(); + } else { + minValueY = qMin(minValueY, minLimits.y()); + maxValueY = qMax(maxValueY, maxLimits.y()); + } + } + if (adjustZ) { + if (!series) { + // First series initializes the values + minValueZ = minLimits.z(); + maxValueZ = maxLimits.z(); + } else { + minValueZ = qMin(minValueZ, minLimits.z()); + maxValueZ = qMax(maxValueZ, maxLimits.z()); + } + } + } } - valueAxis = static_cast<Q3DValueAxis *>(m_axisY); - if (valueAxis && valueAxis->isAutoAdjustRange()) { - if (minLimits.y() != maxLimits.y()) - valueAxis->dptr()->setRange(minLimits.y(), maxLimits.y()); - else - valueAxis->dptr()->setRange(minLimits.y() - 1.0f, minLimits.y() + 1.0f); // Default to some valid range + static const float adjustmentRatio = 20.0f; + static const float defaultAdjustment = 1.0f; + + if (adjustX) { + // If all points at same coordinate, need to default to some valid range + float adjustment = 0.0f; + if (minValueX == maxValueX) { + if (adjustZ) { + // X and Z are linked to have similar unit size, so choose the valid range based on it + if (minValueZ == maxValueZ) + adjustment = defaultAdjustment; + else + adjustment = qAbs(maxValueZ - minValueZ) / adjustmentRatio; + } else { + if (valueAxisZ) + adjustment = qAbs(valueAxisZ->max() - valueAxisZ->min()) / adjustmentRatio; + else + adjustment = defaultAdjustment; + } + } + valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment); } - - valueAxis = static_cast<Q3DValueAxis *>(m_axisZ); - if (valueAxis && valueAxis->isAutoAdjustRange()) { - if (minLimits.z() != maxLimits.z()) - valueAxis->dptr()->setRange(minLimits.z(), maxLimits.z()); - else - valueAxis->dptr()->setRange(minLimits.z() - 1.0f, minLimits.z() + 1.0f); // Default to some valid range + if (adjustY) { + // If all points at same coordinate, need to default to some valid range + // Y-axis unit is not dependent on other axes, so simply adjust +-1.0f + float adjustment = 0.0f; + if (minValueY == maxValueY) + adjustment = defaultAdjustment; + valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment); + } + if (adjustZ) { + // If all points at same coordinate, need to default to some valid range + float adjustment = 0.0f; + if (minValueZ == maxValueZ) { + if (adjustX) { + // X and Z are linked to have similar unit size, so choose the valid range based on it + if (minValueX == maxValueX) + adjustment = defaultAdjustment; + else + adjustment = qAbs(maxValueX - minValueX) / adjustmentRatio; + } else { + if (valueAxisX) + adjustment = qAbs(valueAxisX->max() - valueAxisX->min()) / adjustmentRatio; + else + adjustment = defaultAdjustment; + } + } + valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment); } } } diff --git a/src/datavisualization/engine/surface3dcontroller_p.h b/src/datavisualization/engine/surface3dcontroller_p.h index 0698c291..714420a4 100644 --- a/src/datavisualization/engine/surface3dcontroller_p.h +++ b/src/datavisualization/engine/surface3dcontroller_p.h @@ -32,21 +32,25 @@ #include "abstract3dcontroller_p.h" #include "datavisualizationglobal_p.h" -#include <QLinearGradient> QT_DATAVISUALIZATION_BEGIN_NAMESPACE class Surface3DRenderer; +class QSurface3DSeries; struct Surface3DChangeBitField { - bool gradientColorChanged : 1; bool smoothStatusChanged : 1; bool surfaceGridChanged : 1; + bool selectedPointChanged : 1; + bool rowsChanged : 1; + bool itemChanged : 1; Surface3DChangeBitField() : - gradientColorChanged(true), smoothStatusChanged(true), - surfaceGridChanged(true) + surfaceGridChanged(true), + selectedPointChanged(true), + rowsChanged(false), + itemChanged(false) { } }; @@ -57,43 +61,47 @@ class QT_DATAVISUALIZATION_EXPORT Surface3DController : public Abstract3DControl private: Surface3DChangeBitField m_changeTracker; - - // Rendering Surface3DRenderer *m_renderer; - bool m_isSmoothSurfaceEnabled; - bool m_isSurfaceGridEnabled; - QLinearGradient m_userDefinedGradient; + QPoint m_selectedPoint; + QSurface3DSeries *m_selectedSeries; // Points to the series for which the point is selected in + // single series selection cases. + bool m_flatShadingSupported; + QVector<QPoint> m_changedItems; + QVector<int> m_changedRows; public: explicit Surface3DController(QRect rect); ~Surface3DController(); - void initializeOpenGL(); + virtual void initializeOpenGL(); virtual void synchDataToRenderer(); - void setSmoothSurface(bool enable); - bool smoothSurface(); - - void setSurfaceGrid(bool enable); - bool surfaceGrid(); - - void setGradient(const QLinearGradient &gradient); - QLinearGradient gradient() const; - - void setGradientColorAt(qreal pos, const QColor &color); - - void setSelectionMode(QDataVis::SelectionMode mode); - - virtual void setActiveDataProxy(QAbstractDataProxy *proxy); + void setSelectionMode(QDataVis::SelectionFlags mode); + void setSelectedPoint(const QPoint &position, QSurface3DSeries *series); virtual void handleAxisAutoAdjustRangeChangedInOrientation(Q3DAbstractAxis::AxisOrientation orientation, bool autoAdjust); virtual void handleAxisRangeChangedBySender(QObject *sender); + virtual void handleSeriesVisibilityChangedBySender(QObject *sender); + + static QPoint invalidSelectionPosition(); + bool isFlatShadingSupported(); + + virtual void addSeries(QAbstract3DSeries *series); + virtual void removeSeries(QAbstract3DSeries *series); + virtual QList<QSurface3DSeries *> surfaceSeriesList(); public slots: void handleArrayReset(); + void handleRowsAdded(int startIndex, int count); + void handleRowsChanged(int startIndex, int count); + void handleRowsRemoved(int startIndex, int count); + void handleRowsInserted(int startIndex, int count); + void handleItemChanged(int rowIndex, int columnIndex); + + // Renderer callback handlers + void handlePointClicked(const QPoint &position, QSurface3DSeries *series); -signals: - void smoothSurfaceEnabledChanged(bool enable); + void handleFlatShadingSupportedChange(bool supported); private: void adjustValueAxisRange(); diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 63bd93a7..7049632d 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -25,18 +25,15 @@ #include "surfaceobject_p.h" #include "texturehelper_p.h" #include "selectionpointer_p.h" -#include "theme_p.h" #include "utils_p.h" #include "drawer_p.h" #include "q3dlight.h" +#include "qsurface3dseries_p.h" #include <QMatrix4x4> #include <QMouseEvent> #include <qmath.h> -#include <QLinearGradient> -#include <QPainter> - #include <QDebug> static const int ID_TO_RGBA_MASK = 0xff; @@ -56,13 +53,14 @@ const GLfloat gridLineWidth = 0.005f; const GLfloat sliceZScale = 0.1f; const GLfloat sliceUnits = 2.5f; const int subViewDivider = 5; +const uint invalidSelectionId = uint(-1); Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) : Abstract3DRenderer(controller), - m_controller(controller), - m_labelStyle(QDataVis::LabelStyleFromTheme), + m_labelBackground(false), m_font(QFont(QStringLiteral("Arial"))), m_isGridEnabled(true), + m_cachedIsSlicingActivated(false), m_shader(0), m_depthShader(0), m_backgroundShader(0), @@ -95,11 +93,13 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_depthFrameBuffer(0), m_selectionFrameBuffer(0), m_selectionDepthBuffer(0), - m_gradientTexture(0), m_selectionTexture(0), m_selectionResultTexture(0), m_shadowQualityToShader(33.3f), + m_cachedFlatShading(false), m_flatSupported(true), + m_cachedSurfaceVisible(true), + m_cachedSurfaceGridOn(true), m_selectionPointer(0), m_selectionActive(false), m_xFlipped(false), @@ -107,28 +107,29 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_yFlipped(false), m_sampleSpace(QRect(0, 0, 0, 0)), m_shadowQualityMultiplier(3), - m_cachedSelectionId(0), - m_selectionModeChanged(false), - m_hasHeightAdjustmentChanged(true) + m_clickedPointId(invalidSelectionId), + m_hasHeightAdjustmentChanged(true), + m_selectedPoint(Surface3DController::invalidSelectionPosition()), + m_selectedSeries(0), + m_uniformGradientTexture(0) { // Check if flat feature is supported ShaderHelper tester(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), QStringLiteral(":/shaders/fragmentSurfaceFlat")); if (!tester.testCompile()) { m_flatSupported = false; - m_controller->setSmoothSurface(true); + connect(this, &Surface3DRenderer::flatShadingSupportedChanged, + controller, &Surface3DController::handleFlatShadingSupportedChange); + emit flatShadingSupportedChanged(m_flatSupported); qWarning() << "Warning: Flat qualifier not supported on your platform's GLSL language." " Requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension."; } - m_cachedSmoothSurface = m_controller->smoothSurface(); - updateSurfaceGridStatus(m_controller->surfaceGrid()); - - // Shadows are disabled for Q3DSurface in Tech Preview - updateShadowQuality(QDataVis::ShadowQualityNone); - initializeOpenGLFunctions(); initializeOpenGL(); + + // Create initial uniform gradient + generateUniformGradient(m_uniformGradientTextureColor); } Surface3DRenderer::~Surface3DRenderer() @@ -138,9 +139,10 @@ Surface3DRenderer::~Surface3DRenderer() m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer); m_textureHelper->deleteTexture(&m_depthTexture); - m_textureHelper->deleteTexture(&m_gradientTexture); + m_textureHelper->deleteTexture(&m_depthModelTexture); m_textureHelper->deleteTexture(&m_selectionTexture); m_textureHelper->deleteTexture(&m_selectionResultTexture); + m_textureHelper->deleteTexture(&m_uniformGradientTexture); delete m_shader; delete m_depthShader; @@ -172,10 +174,7 @@ void Surface3DRenderer::initializeOpenGL() Abstract3DRenderer::initializeOpenGL(); // Initialize shaders - handleShadowQualityChange(); - initSurfaceShaders(); - initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), QStringLiteral(":/shaders/fragmentLabel")); @@ -201,15 +200,22 @@ void Surface3DRenderer::initializeOpenGL() loadBackgroundMesh(); } -void Surface3DRenderer::updateDataModel(QSurfaceDataProxy *dataProxy) +void Surface3DRenderer::updateData() { - calculateSceneScalingFactors(); + // Surface only supports single series for now, so we are only interested in the first series + const QSurfaceDataArray *array = 0; + if (m_visibleSeriesList.size()) { + QSurface3DSeries *firstSeries = static_cast<QSurface3DSeries *>(m_visibleSeriesList.at(0).series()); + QSurfaceDataProxy *dataProxy = firstSeries->dataProxy(); + if (dataProxy) + array = dataProxy->array(); + } - const QSurfaceDataArray &array = *dataProxy->array(); + calculateSceneScalingFactors(); // Need minimum of 2x2 array to draw a surface - if (array.size() >= 2 && array.at(0)->size() >= 2) { - QRect sampleSpace = calculateSampleRect(array); + if (array && array->size() >= 2 && array->at(0)->size() >= 2) { + QRect sampleSpace = calculateSampleRect(*array); bool dimensionChanged = false; if (m_sampleSpace != sampleSpace) { @@ -230,7 +236,7 @@ void Surface3DRenderer::updateDataModel(QSurfaceDataProxy *dataProxy) } for (int i = 0; i < sampleSpace.height(); i++) { for (int j = 0; j < sampleSpace.width(); j++) - (*(m_dataArray.at(i)))[j] = array.at(i + sampleSpace.y())->at(j + sampleSpace.x()); + (*(m_dataArray.at(i)))[j] = array->at(i + sampleSpace.y())->at(j + sampleSpace.x()); } if (m_dataArray.size() > 0) { @@ -238,7 +244,7 @@ void Surface3DRenderer::updateDataModel(QSurfaceDataProxy *dataProxy) loadSurfaceObj(); // Note: Data setup can change sample space (as min width/height is 1) - if (m_cachedSmoothSurface) { + if (!m_cachedFlatShading) { m_surfaceObj->setUpSmoothData(m_dataArray, m_sampleSpace, m_heightNormalizer, m_axisCacheY.min(), dimensionChanged); } else { @@ -250,21 +256,134 @@ void Surface3DRenderer::updateDataModel(QSurfaceDataProxy *dataProxy) updateSelectionTexture(); } } + } else { + for (int i = 0; i < m_dataArray.size(); i++) + delete m_dataArray.at(i); + m_dataArray.clear(); + m_sampleSpace = QRect(); + + delete m_surfaceObj; + m_surfaceObj = 0; } - m_selectionActive = false; - m_cachedSelectionId = 0; for (int i = 0; i < m_sliceDataArray.size(); i++) delete m_sliceDataArray.at(i); m_sliceDataArray.clear(); - Abstract3DRenderer::updateDataModel(dataProxy); + updateSelectedPoint(m_selectedPoint, m_selectedSeries); +} + +void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility) +{ + Abstract3DRenderer::updateSeries(seriesList, updateVisibility); + + // TODO: move to render cache when multiseries support implemented QTRD-2657 + // TODO: until then just update them always. + if (m_visibleSeriesList.size()) { + QSurface3DSeries *series = static_cast<QSurface3DSeries *>(m_visibleSeriesList.at(0).series()); + updateFlatStatus(series->isFlatShadingEnabled()); + + QSurface3DSeries::DrawFlags drawMode = series->drawMode(); + m_cachedSurfaceVisible = drawMode.testFlag(QSurface3DSeries::DrawSurface); + m_cachedSurfaceGridOn = drawMode.testFlag(QSurface3DSeries::DrawWireframe); + + QVector3D seriesColor = Utils::vectorFromColor(series->baseColor()); + if (m_uniformGradientTextureColor != seriesColor) + generateUniformGradient(seriesColor); + if (m_selectionPointer) { + m_selectionPointer->setHighlightColor(Utils::vectorFromColor(series->singleHighlightColor())); + // Make sure selection pointer object reference is still good + m_selectionPointer->setPointerObject(m_visibleSeriesList.at(0).object()); + } + } +} + +void Surface3DRenderer::updateRows(const QVector<int> &rows) +{ + // Surface only supports single series for now, so we are only interested in the first series + const QSurfaceDataArray *array = 0; + if (m_visibleSeriesList.size()) { + QSurface3DSeries *firstSeries = static_cast<QSurface3DSeries *>(m_visibleSeriesList.at(0).series()); + if (m_cachedSurfaceGridOn || m_cachedSurfaceVisible) { + QSurfaceDataProxy *dataProxy = firstSeries->dataProxy(); + if (dataProxy) + array = dataProxy->array(); + } + } + + if (array && array->size() >= 2 && array->at(0)->size() >= 2 && + m_sampleSpace.width() >= 2 && m_sampleSpace.height() >= 2) { + bool updateBuffers = false; + int sampleSpaceTop = m_sampleSpace.y() + m_sampleSpace.height(); + foreach (int row, rows) { + if (row >= m_sampleSpace.y() && row <= sampleSpaceTop) { + updateBuffers = true; + for (int j = 0; j < m_sampleSpace.width(); j++) + (*(m_dataArray.at(row - m_sampleSpace.y())))[j] = + array->at(row)->at(j + m_sampleSpace.x()); + + if (m_cachedFlatShading) { + m_surfaceObj->updateCoarseRow(m_dataArray, row - m_sampleSpace.y(), m_heightNormalizer, + m_axisCacheY.min()); + } else { + m_surfaceObj->updateSmoothRow(m_dataArray, row - m_sampleSpace.y(), m_heightNormalizer, + m_axisCacheY.min()); + } + } + } + if (updateBuffers) + m_surfaceObj->uploadBuffers(); + } + + updateSelectedPoint(m_selectedPoint, m_selectedSeries); } -void Surface3DRenderer::updateSliceDataModel(int selectionId) +void Surface3DRenderer::updateItem(const QVector<QPoint> &points) { - int column = (selectionId - 1) % m_sampleSpace.width(); - int row = (selectionId - 1) / m_sampleSpace.width(); + // TODO: Properly support non-straight rows and columns (QTRD-2643) + + // Surface only supports single series for now, so we are only interested in the first series + const QSurfaceDataArray *array = 0; + if (m_visibleSeriesList.size()) { + QSurface3DSeries *firstSeries = static_cast<QSurface3DSeries *>(m_visibleSeriesList.at(0).series()); + if (m_cachedSurfaceGridOn || m_cachedSurfaceVisible) { + QSurfaceDataProxy *dataProxy = firstSeries->dataProxy(); + if (dataProxy) + array = dataProxy->array(); + } + } + + if (array && array->size() >= 2 && array->at(0)->size() >= 2 && + m_sampleSpace.width() >= 2 && m_sampleSpace.height() >= 2) { + int sampleSpaceTop = m_sampleSpace.y() + m_sampleSpace.height(); + int sampleSpaceRight = m_sampleSpace.x() + m_sampleSpace.width(); + bool updateBuffers = false; + foreach (QPoint item, points) { + if (item.y() <= sampleSpaceTop && item.y() >= m_sampleSpace.y() && + item.x() <= sampleSpaceRight && item.x() >= m_sampleSpace.x()) { + updateBuffers = true; + int x = item.x() - m_sampleSpace.x(); + int y = item.y() - m_sampleSpace.y(); + (*(m_dataArray.at(y)))[x] = array->at(item.y())->at(item.x()); + + if (m_cachedFlatShading) { + m_surfaceObj->updateCoarseItem(m_dataArray, y, x, m_heightNormalizer, m_axisCacheY.min()); + } else { + m_surfaceObj->updateSmoothItem(m_dataArray, y, x, m_heightNormalizer, m_axisCacheY.min()); + } + } + } + if (updateBuffers) + m_surfaceObj->uploadBuffers(); + } + + updateSelectedPoint(m_selectedPoint, m_selectedSeries); +} + +void Surface3DRenderer::updateSliceDataModel(const QPoint &point) +{ + int column = point.y(); + int row = point.x(); for (int i = 0; i < m_sliceDataArray.size(); i++) delete m_sliceDataArray.at(i); @@ -273,19 +392,19 @@ void Surface3DRenderer::updateSliceDataModel(int selectionId) m_sliceDataArray.reserve(2); QSurfaceDataRow *sliceRow; - qreal adjust = (0.025 * m_heightNormalizer) / 2.0; - qreal stepDown = 2.0 * adjust; - if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { + float adjust = (0.025f * m_heightNormalizer) / 2.0f; + float stepDown = 2.0f * adjust; + if (m_cachedSelectionMode.testFlag(QDataVis::SelectionRow)) { QSurfaceDataRow *src = m_dataArray.at(row); sliceRow = new QSurfaceDataRow(src->size()); for (int i = 0; i < sliceRow->size(); i++) - (*sliceRow)[i].setPosition(QVector3D(src->at(i).x(), src->at(i).y() + adjust, -1.0)); + (*sliceRow)[i].setPosition(QVector3D(src->at(i).x(), src->at(i).y() + adjust, -1.0f)); } else { sliceRow = new QSurfaceDataRow(m_sampleSpace.height()); for (int i = 0; i < m_sampleSpace.height(); i++) { (*sliceRow)[i].setPosition(QVector3D(m_dataArray.at(i)->at(column).z(), m_dataArray.at(i)->at(column).y() + adjust, - -1.0)); + -1.0f)); } } @@ -294,7 +413,7 @@ void Surface3DRenderer::updateSliceDataModel(int selectionId) // Make a duplicate, so that we get a little bit depth QSurfaceDataRow *duplicateRow = new QSurfaceDataRow(*sliceRow); for (int i = 0; i < sliceRow->size(); i++) - (*sliceRow)[i].setPosition(QVector3D(sliceRow->at(i).x(), sliceRow->at(i).y() - stepDown, 1.0)); + (*sliceRow)[i].setPosition(QVector3D(sliceRow->at(i).x(), sliceRow->at(i).y() - stepDown, 1.0f)); m_sliceDataArray << duplicateRow; @@ -304,7 +423,7 @@ void Surface3DRenderer::updateSliceDataModel(int selectionId) if (!m_sliceSurfaceObj) loadSliceSurfaceObj(); - if (m_cachedSmoothSurface) { + if (!m_cachedFlatShading) { m_sliceSurfaceObj->setUpSmoothData(m_sliceDataArray, sliceRect, m_heightNormalizer, m_axisCacheY.min(), true); } else { @@ -323,20 +442,10 @@ QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array) int i; bool found; - float axisMinX = float(m_axisCacheX.min()); - float axisMaxX = float(m_axisCacheX.max()); - float axisMinZ = float(m_axisCacheZ.min()); - float axisMaxZ = float(m_axisCacheZ.max()); - - // Comparisons between float and double are not accurate, so fudge our comparison values - //a little to get all rows and columns into view that need to be visible. - const float fudgeFactor = 0.00001f; - float fudgedAxisXRange = (axisMaxX - axisMinX) * fudgeFactor; - float fudgedAxisZRange = (axisMaxZ - axisMinZ) * fudgeFactor; - axisMinX -= fudgedAxisXRange; - axisMinZ -= fudgedAxisZRange; - axisMaxX += fudgedAxisXRange; - axisMaxZ += fudgedAxisZRange; + float axisMinX = m_axisCacheX.min(); + float axisMaxX = m_axisCacheX.max(); + float axisMinZ = m_axisCacheZ.min(); + float axisMaxZ = m_axisCacheZ.max(); // m_minVisibleColumnValue for (i = 0, found = false; i < columnCount; i++) { @@ -414,10 +523,6 @@ QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array) void Surface3DRenderer::updateScene(Q3DScene *scene) { - // TODO: Move these to more suitable place e.g. controller should be controlling the viewports. - scene->setSecondarySubViewport(m_sliceViewPort); - scene->setPrimarySubViewport(m_mainViewPort); - // Set initial camera position // X must be 0 for rotation to work - we can use "setCameraRotation" for setting it later if (m_hasHeightAdjustmentChanged) { @@ -426,41 +531,28 @@ void Surface3DRenderer::updateScene(Q3DScene *scene) m_hasHeightAdjustmentChanged = false; } - scene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); - scene->setLightPositionRelativeToCamera(defaultLightPos); - Abstract3DRenderer::updateScene(scene); if (m_selectionPointer) m_selectionPointer->updateScene(m_cachedScene); + + updateSlicingActive(scene->isSlicingActive()); } void Surface3DRenderer::render(GLuint defaultFboHandle) { - bool slicingActivated = m_cachedScene->isSlicingActive(); - bool slicingChanged = m_cachedIsSlicingActivated != slicingActivated; - - updateSlicingActive(slicingActivated); - // Handle GL state setup for FBO buffers and clearing of the render surface Abstract3DRenderer::render(defaultFboHandle); - // Draw the surface scene drawScene(defaultFboHandle); - - // In slice mode; draw slice and render selection ball if (m_cachedIsSlicingActivated) drawSlicedScene(); - // Render selection ball if not in slice mode - if (m_selectionPointer && m_selectionActive) + // Render selection ball + if (m_selectionPointer && m_selectionActive + && m_cachedSelectionMode.testFlag(QDataVis::SelectionItem)) { m_selectionPointer->render(defaultFboHandle); - - // If slicing has been activated by this render pass, we need another render - // Also trigger another render always when slicing changes in general to ensure - // final draw is correct. - if (slicingActivated != m_cachedScene->isSlicingActive() || slicingChanged) - emit needRender(); + } } void Surface3DRenderer::drawSlicedScene() @@ -468,13 +560,15 @@ void Surface3DRenderer::drawSlicedScene() QVector3D lightPos; // Specify viewport - glViewport(m_sliceViewPort.x(), m_sliceViewPort.y(), - m_sliceViewPort.width(), m_sliceViewPort.height()); + glViewport(m_secondarySubViewport.x(), + m_secondarySubViewport.y(), + m_secondarySubViewport.width(), + m_secondarySubViewport.height()); // Set up projection matrix QMatrix4x4 projectionMatrix; - GLfloat aspect = (GLfloat)m_mainViewPort.width() / (GLfloat)m_mainViewPort.height(); + GLfloat aspect = (GLfloat)m_secondarySubViewport.width() / (GLfloat)m_secondarySubViewport.height(); projectionMatrix.ortho(-sliceUnits * aspect, sliceUnits * aspect, -sliceUnits, sliceUnits, -1.0f, 4.0f); @@ -487,30 +581,24 @@ void Surface3DRenderer::drawSlicedScene() QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; + bool rowMode = m_cachedSelectionMode.testFlag(QDataVis::SelectionRow); + GLfloat scaleX = 0.0f; GLfloat scaleXBackground = 0.0f; GLfloat offset = 0.0f; - if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { + if (rowMode) { scaleX = m_surfaceScaleX; scaleXBackground = m_scaleXWithBackground; offset = m_surfaceOffsetX; - } else if (m_cachedSelectionMode == QDataVis::SelectionModeSliceColumn) { + } else { scaleX = m_surfaceScaleZ; scaleXBackground = m_scaleZWithBackground; offset = -m_surfaceOffsetZ; } if (m_surfaceObj) { - ShaderHelper *surfaceShader = m_shader; - surfaceShader->bind(); - - if (m_cachedSurfaceGridOn) { - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(0.5f, 1.0f); - } - - QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; + QMatrix4x4 modelMatrix; QMatrix4x4 itModelMatrix; modelMatrix.translate(offset, 0.0f, 0.0f); @@ -520,36 +608,40 @@ void Surface3DRenderer::drawSlicedScene() MVPMatrix = projectionViewMatrix * modelMatrix; - QVector3D color; - if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) - color = Utils::vectorFromColor(m_cachedTheme.m_highlightRowColor); - else - color = Utils::vectorFromColor(m_cachedTheme.m_highlightColumnColor); - - // Set shader bindings - surfaceShader->setUniformValue(surfaceShader->lightP(), lightPos); - surfaceShader->setUniformValue(surfaceShader->view(), viewMatrix); - surfaceShader->setUniformValue(surfaceShader->model(), modelMatrix); - surfaceShader->setUniformValue(surfaceShader->nModel(), - itModelMatrix.inverted().transposed()); - surfaceShader->setUniformValue(surfaceShader->MVP(), MVPMatrix); - surfaceShader->setUniformValue(surfaceShader->color(), color); - surfaceShader->setUniformValue(surfaceShader->lightS(), 0.25f); - surfaceShader->setUniformValue(surfaceShader->ambientS(), - m_cachedTheme.m_ambientStrength * 2.0f); - - m_drawer->drawObject(surfaceShader, m_sliceSurfaceObj); + if (m_cachedSurfaceVisible) { + if (m_cachedSurfaceGridOn) { + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(0.5f, 1.0f); + } - surfaceShader->release(); + ShaderHelper *surfaceShader = m_shader; + surfaceShader->bind(); + + QVector3D color; + color = Utils::vectorFromColor(m_cachedTheme->multiHighlightColor()); + + // Set shader bindings + surfaceShader->setUniformValue(surfaceShader->lightP(), lightPos); + surfaceShader->setUniformValue(surfaceShader->view(), viewMatrix); + surfaceShader->setUniformValue(surfaceShader->model(), modelMatrix); + surfaceShader->setUniformValue(surfaceShader->nModel(), + itModelMatrix.inverted().transposed()); + surfaceShader->setUniformValue(surfaceShader->MVP(), MVPMatrix); + surfaceShader->setUniformValue(surfaceShader->color(), color); + surfaceShader->setUniformValue(surfaceShader->lightS(), 0.25f); + surfaceShader->setUniformValue(surfaceShader->ambientS(), + m_cachedTheme->ambientLightStrength() * 2.0f); + + m_drawer->drawObject(surfaceShader, m_sliceSurfaceObj); + } // Draw surface grid if (m_cachedSurfaceGridOn) { m_surfaceGridShader->bind(); m_surfaceGridShader->setUniformValue(m_surfaceGridShader->color(), - Utils::vectorFromColor(m_cachedTheme.m_gridLine)); + Utils::vectorFromColor(m_cachedTheme->gridLineColor())); m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), MVPMatrix); m_drawer->drawSurfaceGrid(m_surfaceGridShader, m_sliceSurfaceObj); - m_surfaceGridShader->release(); glDisable(GL_POLYGON_OFFSET_FILL); } @@ -559,17 +651,17 @@ void Surface3DRenderer::drawSlicedScene() glDisable(GL_TEXTURE_2D); // Grid lines - if (m_cachedIsGridEnabled && m_heightNormalizer) { + if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { ShaderHelper *lineShader = m_backgroundShader; // Bind line shader lineShader->bind(); // Set unchanging shader bindings - QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme.m_gridLine); + QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); lineShader->setUniformValue(lineShader->lightP(), lightPos); lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); - lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme.m_ambientStrength * 2.0f); + lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength() * 2.0f); lineShader->setUniformValue(lineShader->lightS(), 0.25f); // Horizontal lines @@ -611,7 +703,7 @@ void Surface3DRenderer::drawSlicedScene() int lastSegment; GLfloat lineStep; GLfloat linePos; - if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { + if (rowMode) { lineStep = -2.0f * aspectRatio * m_axisCacheX.subSegmentStep() / m_scaleFactor; lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount(); linePos = m_scaleX; @@ -643,9 +735,6 @@ void Surface3DRenderer::drawSlicedScene() linePos += lineStep; } - - // Release line shader - lineShader->release(); } // Draw axis labels @@ -653,10 +742,8 @@ void Surface3DRenderer::drawSlicedScene() glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glCullFace(GL_BACK); - if (m_cachedLabelStyle > QDataVis::LabelStyleOpaque) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Y Labels to back wall GLfloat posStep = 2.0f * m_axisCacheY.segmentStep() / m_heightNormalizer; @@ -676,7 +763,7 @@ void Surface3DRenderer::drawSlicedScene() m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, positionComp, rotation, 0, m_cachedSelectionMode, m_labelShader, m_labelObj, m_cachedScene->activeCamera(), - true, true, Drawer::LabelMid, Qt::AlignRight); + true, true, Drawer::LabelMid, Qt::AlignRight, true); } labelNbr++; labelPos += posStep; @@ -685,7 +772,7 @@ void Surface3DRenderer::drawSlicedScene() // X Labels to ground int countLabelItems; int lastSegment; - if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { + if (rowMode) { posStep = 2.0f * aspectRatio * m_axisCacheX.segmentStep() / m_scaleFactor; labelPos = -m_scaleX; lastSegment = m_axisCacheX.segmentCount(); @@ -709,15 +796,15 @@ void Surface3DRenderer::drawSlicedScene() m_dummyRenderItem.setTranslation(labelTrans); LabelItem *axisLabelItem; - if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) + if (rowMode) axisLabelItem = m_axisCacheX.labelItems().at(labelNbr); else axisLabelItem = m_axisCacheZ.labelItems().at(labelNbr); m_drawer->drawLabel(m_dummyRenderItem, *axisLabelItem, viewMatrix, projectionMatrix, - positionComp, rotation, 0, QDataVis::SelectionModeSliceRow, + positionComp, rotation, 0, QDataVis::SelectionRow, m_labelShader, m_labelObj, m_cachedScene->activeCamera(), - false, false, Drawer::LabelBelow, Qt::AlignTop); + false, false, Drawer::LabelBelow, Qt::AlignTop, true); } labelNbr++; labelPos += posStep; @@ -725,26 +812,26 @@ void Surface3DRenderer::drawSlicedScene() glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); - if (m_cachedLabelStyle > QDataVis::LabelStyleOpaque) - glDisable(GL_BLEND); + glDisable(GL_BLEND); - // Release label shader - m_labelShader->release(); + // Release shader + glUseProgram(0); } void Surface3DRenderer::drawScene(GLuint defaultFboHandle) { GLfloat backgroundRotation = 0; - uint selectionId = 0; + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); // 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); + projectionMatrix.perspective(45.0f, (GLfloat)m_primarySubViewport.width() + / (GLfloat)m_primarySubViewport.height(), 0.1f, 100.0f); // Calculate view matrix QMatrix4x4 viewMatrix = m_cachedScene->activeCamera()->viewMatrix(); @@ -782,11 +869,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Draw depth buffer #if !defined(QT_OPENGL_ES_2) - GLfloat adjustedLightStrength = m_cachedTheme.m_lightStrength / 10.0f; - if (m_cachedShadowQuality > QDataVis::ShadowQualityNone && m_surfaceObj) { + GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone && m_surfaceObj && m_cachedSurfaceVisible) { // Render scene into a depth texture for using with shadow mapping // Enable drawing to depth framebuffer glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); + // Attach texture to depth attachment + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexture, 0); glClear(GL_DEPTH_BUFFER_BIT); // Bind depth shader @@ -794,8 +883,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows. glViewport(0, 0, - m_mainViewPort.width() * m_shadowQualityMultiplier, - m_mainViewPort.height() * m_shadowQualityMultiplier); + m_primarySubViewport.width() * m_shadowQualityMultiplier, + m_primarySubViewport.height() * m_shadowQualityMultiplier); // Get the depth view matrix // It may be possible to hack lightPos here if we want to make some tweaks to shadow @@ -809,8 +898,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Set the depth projection matrix #ifndef USE_WIDER_SHADOWS // Use this for perspective shadows - depthProjectionMatrix.perspective(10.0f, (GLfloat)m_mainViewPort.width() - / (GLfloat)m_mainViewPort.height(), 3.0f, 100.0f); + depthProjectionMatrix.perspective(10.0f, (GLfloat)m_primarySubViewport.width() + / (GLfloat)m_primarySubViewport.height(), 3.0f, 100.0f); #else // Use these for orthographic shadows depthProjectionMatrix.ortho(-2.0f * 2.0f, 2.0f * 2.0f, @@ -819,7 +908,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) #endif depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; - glCullFace(GL_FRONT); + glDisable(GL_CULL_FACE); QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -841,7 +930,17 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_surfaceObj->elementBuf()); // Draw the triangles - glDrawElements(GL_TRIANGLES, m_surfaceObj->indexCount(), GL_UNSIGNED_SHORT, + glDrawElements(GL_TRIANGLES, m_surfaceObj->indexCount(), m_surfaceObj->indicesType(), + (void *)0); + + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthModelTexture, 0); + glClear(GL_DEPTH_BUFFER_BIT); + + // Draw the triangles + glDrawElements(GL_TRIANGLES, m_surfaceObj->indexCount(), m_surfaceObj->indicesType(), (void *)0); // Free buffers @@ -853,14 +952,14 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Disable drawing to depth framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); - // Release depth shader - m_depthShader->release(); - // Revert to original viewport - glViewport(m_mainViewPort.x(), m_mainViewPort.y(), - m_mainViewPort.width(), m_mainViewPort.height()); + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); // Reset culling to normal + glEnable(GL_CULL_FACE); glCullFace(GL_BACK); #if 0 // Use this if you want to see what is being drawn to the framebuffer @@ -875,24 +974,27 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) 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 - - bool selectionDirty = false; - // Enable texturing glEnable(GL_TEXTURE_2D); // Draw selection buffer - if (!m_cachedIsSlicingActivated && m_controller->inputState() == QDataVis::InputStateOnScene - && m_surfaceObj && m_cachedSelectionMode > QDataVis::SelectionModeNone) { + if (!m_cachedIsSlicingActivated && m_surfaceObj && m_selectionState == SelectOnScene + && m_cachedSelectionMode > QDataVis::SelectionNone + && (m_cachedSurfaceVisible || m_cachedSurfaceGridOn) + && m_visibleSeriesList.size() > 0) { m_selectionShader->bind(); glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); + glViewport(0, + 0, + m_primarySubViewport.width(), + m_primarySubViewport.height()); + glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used - glClearColor(0, 0, 0, 0); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 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 @@ -912,30 +1014,32 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glEnable(GL_DITHER); - QPoint point = m_controller->inputPosition(); - GLubyte pixel[4] = {0}; - glReadPixels(point.x(), m_cachedBoundingRect.height() - point.y(), 1, 1, - GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixel); + GLubyte pixel[4] = {0, 0, 0, 0}; + glReadPixels(m_inputPosition.x(), m_viewport.height() - m_inputPosition.y(), + 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixel); glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); - // Release selection shader - m_selectionShader->release(); - // Put the RGBA value back to uint #if !defined(QT_OPENGL_ES_2) - selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536 + pixel[3] * 16777216; + uint selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536 + pixel[3] * 16777216; #else - selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536; + uint selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536; #endif - selectionDirty = true; + emit pointClicked(QPoint(selectionIdToSurfacePoint(selectionId)), + static_cast<QSurface3DSeries *>(m_visibleSeriesList.at(0).series())); + + // Revert to original viewport + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); } // Draw the surface if (m_surfaceObj && m_sampleSpace.width() >= 2 && m_sampleSpace.height() >= 2) { m_surfaceShader->bind(); - // For surface we can see climpses from underneath glDisable(GL_CULL_FACE); if (m_cachedSurfaceGridOn) { @@ -956,49 +1060,55 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) #else MVPMatrix = projectionViewMatrix * modelMatrix; #endif - // Set shader bindings - m_surfaceShader->setUniformValue(m_surfaceShader->lightP(), lightPos); - m_surfaceShader->setUniformValue(m_surfaceShader->view(), viewMatrix); - m_surfaceShader->setUniformValue(m_surfaceShader->model(), modelMatrix); - m_surfaceShader->setUniformValue(m_surfaceShader->nModel(), - itModelMatrix.inverted().transposed()); - m_surfaceShader->setUniformValue(m_surfaceShader->MVP(), MVPMatrix); - m_surfaceShader->setUniformValue(m_surfaceShader->ambientS(), - m_cachedTheme.m_ambientStrength); + + if (m_cachedSurfaceVisible) { + // Set shader bindings + m_surfaceShader->setUniformValue(m_surfaceShader->lightP(), lightPos); + m_surfaceShader->setUniformValue(m_surfaceShader->view(), viewMatrix); + m_surfaceShader->setUniformValue(m_surfaceShader->model(), modelMatrix); + m_surfaceShader->setUniformValue(m_surfaceShader->nModel(), + itModelMatrix.inverted().transposed()); + m_surfaceShader->setUniformValue(m_surfaceShader->MVP(), MVPMatrix); + m_surfaceShader->setUniformValue(m_surfaceShader->ambientS(), + m_cachedTheme->ambientLightStrength()); + + // TODO: Do properly when multiseries support implemented QTRD-2657 + GLuint gradientTexture; + if (m_visibleSeriesList.at(0).colorStyle() == Q3DTheme::ColorStyleUniform) + gradientTexture = m_uniformGradientTexture; + else + gradientTexture = m_visibleSeriesList.at(0).baseGradientTexture(); #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_surfaceShader->setUniformValue(m_surfaceShader->shadowQ(), m_shadowQualityToShader); - m_surfaceShader->setUniformValue(m_surfaceShader->depth(), depthMVPMatrix); - m_surfaceShader->setUniformValue(m_surfaceShader->lightS(), adjustedLightStrength); + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + m_surfaceShader->setUniformValue(m_surfaceShader->shadowQ(), m_shadowQualityToShader); + m_surfaceShader->setUniformValue(m_surfaceShader->depth(), depthMVPMatrix); + m_surfaceShader->setUniformValue(m_surfaceShader->lightS(), adjustedLightStrength); - // Draw the object - m_drawer->drawObject(m_surfaceShader, m_surfaceObj, m_gradientTexture, m_depthTexture); - } else + // Draw the object + m_drawer->drawObject(m_surfaceShader, m_surfaceObj, gradientTexture, m_depthModelTexture); + } else #endif - { - // Set shadowless shader bindings - m_surfaceShader->setUniformValue(m_surfaceShader->lightS(), - m_cachedTheme.m_lightStrength); + { + // Set shadowless shader bindings + m_surfaceShader->setUniformValue(m_surfaceShader->lightS(), + m_cachedTheme->lightStrength()); - // Draw the object - m_drawer->drawObject(m_surfaceShader, m_surfaceObj, m_gradientTexture); + // Draw the object + m_drawer->drawObject(m_surfaceShader, m_surfaceObj, gradientTexture); + } + glEnable(GL_CULL_FACE); } - m_surfaceShader->release(); - - glEnable(GL_CULL_FACE); - // Draw surface grid if (m_cachedSurfaceGridOn) { m_surfaceGridShader->bind(); m_surfaceGridShader->setUniformValue(m_surfaceGridShader->color(), - Utils::vectorFromColor(m_cachedTheme.m_gridLine)); + Utils::vectorFromColor(m_cachedTheme->gridLineColor())); m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), MVPMatrix); m_drawer->drawSurfaceGrid(m_surfaceGridShader, m_surfaceObj); - m_surfaceGridShader->release(); glDisable(GL_POLYGON_OFFSET_FILL); } @@ -1009,7 +1119,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glCullFace(GL_BACK); // Draw background - if (m_cachedIsBackgroundEnabled && m_backgroundObj) { + if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; @@ -1019,7 +1129,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // If we're viewing from below, background object must be flipped if (m_yFlipped) { - modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); + modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f); } else { modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); @@ -1033,7 +1143,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) MVPMatrix = projectionViewMatrix * modelMatrix; #endif - QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme.m_backgroundColor); + QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); // Set shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); @@ -1044,7 +1154,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) 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); + m_cachedTheme->ambientLightStrength() * 2.0f); #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { @@ -1063,46 +1173,60 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) { // Set shadowless shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), - m_cachedTheme.m_lightStrength); + m_cachedTheme->lightStrength()); // Draw the object m_drawer->drawObject(m_backgroundShader, m_backgroundObj); } } - // Release background shader - m_backgroundShader->release(); - // Draw grid lines QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth); QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground); QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth); - if (m_cachedIsGridEnabled && m_heightNormalizer) { + if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { ShaderHelper *lineShader = m_backgroundShader; // Bind line shader lineShader->bind(); // Set unchanging shader bindings - QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme.m_gridLine); + QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); lineShader->setUniformValue(lineShader->lightP(), lightPos); lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); - lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme.m_ambientStrength); + lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength()); #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { // Set shadowed shader bindings lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); lineShader->setUniformValue(lineShader->lightS(), - m_cachedTheme.m_lightStrength / 20.0f); + m_cachedTheme->lightStrength() / 20.0f); } else #endif { // Set shadowless shader bindings - lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme.m_lightStrength / 2.5f); + lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 2.5f); } + QQuaternion lineYRotation = QQuaternion(); + QQuaternion lineXRotation = QQuaternion(); + + if (m_xFlipped) + lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f); + else + lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + + if (m_yFlipped) + lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f); + else + lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); + + GLfloat yFloorLinePosition = -backgroundMargin + gridLineOffset; + if (m_yFlipped) + yFloorLinePosition = -yFloorLinePosition; + // Rows (= Z) if (m_axisCacheZ.segmentCount() > 0) { // Floor lines @@ -1115,19 +1239,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - if (m_yFlipped) - modelMatrix.translate(0.0f, backgroundMargin, linePos); - else - modelMatrix.translate(0.0f, -backgroundMargin, linePos); + modelMatrix.translate(0.0f, yFloorLinePosition, linePos); modelMatrix.scale(gridLineScaleX); itModelMatrix.scale(gridLineScaleX); - // If we're viewing from below, grid line object must be flipped - if (m_yFlipped) { - modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - itModelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - } + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1154,7 +1272,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } // Side wall lines - GLfloat lineXTrans = m_scaleXWithBackground; + GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; linePos = m_scaleZ; // Start line if (!m_xFlipped) @@ -1166,9 +1284,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; modelMatrix.translate(lineXTrans, 0.0f, linePos); + modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); + MVPMatrix = projectionViewMatrix * modelMatrix; // Set the rest of the shader bindings @@ -1206,19 +1328,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - if (m_yFlipped) - modelMatrix.translate(linePos, backgroundMargin, 0.0f); - else - modelMatrix.translate(linePos, -backgroundMargin, 0.0f); + modelMatrix.translate(linePos, yFloorLinePosition, 0.0f); modelMatrix.scale(gridLineScaleZ); itModelMatrix.scale(gridLineScaleZ); - // If we're viewing from below, grid line object must be flipped - if (m_yFlipped) { - modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - itModelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); - } + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1245,7 +1361,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } // Back wall lines - GLfloat lineZTrans = m_scaleZWithBackground; + GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; linePos = m_scaleX; if (!m_zFlipped) @@ -1257,9 +1373,15 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; modelMatrix.translate(linePos, 0.0f, lineZTrans); + modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); + if (m_zFlipped) { + modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + } + MVPMatrix = projectionViewMatrix * modelMatrix; // Set the rest of the shader bindings @@ -1292,7 +1414,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) GLfloat linePos = -1.0f; int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); - GLfloat lineZTrans = m_scaleZWithBackground; + GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; if (!m_zFlipped) lineZTrans = -lineZTrans; @@ -1307,6 +1429,11 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) modelMatrix.scale(gridLineScaleX); itModelMatrix.scale(gridLineScaleX); + if (m_zFlipped) { + modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + } + MVPMatrix = projectionViewMatrix * modelMatrix; // Set the rest of the shader bindings @@ -1334,7 +1461,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Side wall linePos = -1.0f; lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); - GLfloat lineXTrans = m_scaleXWithBackground; + GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; if (!m_xFlipped) lineXTrans = -lineXTrans; @@ -1349,6 +1476,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) modelMatrix.scale(gridLineScaleZ); itModelMatrix.scale(gridLineScaleZ); + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); + MVPMatrix = projectionViewMatrix * modelMatrix; // Set the rest of the shader bindings @@ -1373,9 +1503,6 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) linePos += lineStep; } } - - // Release line shader - lineShader->release(); } // Draw axis labels @@ -1383,6 +1510,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_POLYGON_OFFSET_FILL); // Z Labels QVector3D positionZComp(0.0f, 0.0f, 0.0f); @@ -1415,9 +1543,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) for (int segment = 0; segment <= lastSegment; segment++) { if (m_axisCacheZ.labelItems().size() > labelNbr) { - labelTrans.setZ(labelPos); - + glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); // Draw the label here + labelTrans.setZ(labelPos); m_dummyRenderItem.setTranslation(labelTrans); const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(labelNbr); @@ -1461,6 +1589,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) for (int segment = 0; segment <= lastSegment; segment++) { if (m_axisCacheX.labelItems().size() > labelNbr) { + glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); // Draw the label here labelTrans.setX(labelPos); m_dummyRenderItem.setTranslation(labelTrans); @@ -1521,6 +1650,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) if (m_axisCacheY.labelItems().size() > labelNbr) { const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); + glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); + // Back wall labelTransBack.setY(labelPos); m_dummyRenderItem.setTranslation(labelTransBack); @@ -1541,65 +1672,40 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) labelPos += posStep; } } + glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - // Release label shader - m_labelShader->release(); + // Release shader + glUseProgram(0); // Selection handling - if (m_selectionModeChanged || selectionDirty) { - if (selectionDirty) - m_cachedSelectionId = selectionId; - if (m_cachedSelectionMode == QDataVis::SelectionModeNone) { - m_cachedSelectionId = 0; - m_selectionActive = false; - } - if (m_cachedSelectionMode == QDataVis::SelectionModeItem) { - if (m_cachedSelectionId) - surfacePointSelected(m_cachedSelectionId); - else - m_selectionActive = false; - } - if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow - || m_cachedSelectionMode == QDataVis::SelectionModeSliceColumn) { - if (m_cachedSelectionId) { - updateSliceDataModel(m_cachedSelectionId); - m_cachedScene->setSlicingActive(true); - - surfacePointSelected(m_cachedSelectionId); - - emit needRender(); + if (m_selectionDirty) { + QPoint visiblePoint = Surface3DController::invalidSelectionPosition(); + if (m_selectedPoint != Surface3DController::invalidSelectionPosition()) { + int x = m_selectedPoint.x() - m_sampleSpace.y(); + int y = m_selectedPoint.y() - m_sampleSpace.x(); + if (x >= 0 && y >= 0 && x < m_sampleSpace.height() && y < m_sampleSpace.width() + && m_dataArray.size()) { + visiblePoint = QPoint(x, y); } } - m_selectionModeChanged = false; - } - if (m_controller->inputState() == QDataVis::InputStateOnOverview) { - if (m_cachedIsSlicingActivated) { - m_cachedScene->setSlicingActive(false); + if (m_cachedSelectionMode == QDataVis::SelectionNone + || visiblePoint == Surface3DController::invalidSelectionPosition()) { m_selectionActive = false; - m_cachedSelectionId = 0; + } else { + // TODO: Need separate selection ball for slice and main surface view QTRD-2515 + if (m_cachedIsSlicingActivated) + updateSliceDataModel(visiblePoint); + if (m_cachedSelectionMode.testFlag(QDataVis::SelectionItem)) + surfacePointSelected(visiblePoint); + m_selectionActive = true; } - } -} -void Surface3DRenderer::updateSurfaceGradient(const QLinearGradient &gradient) -{ - QImage image(QSize(2, 1024), QImage::Format_RGB32); - QPainter pmp(&image); - pmp.setBrush(QBrush(gradient)); - pmp.setPen(Qt::NoPen); - pmp.drawRect(0, 0, 2, 1024); - - if (m_gradientTexture) { - m_textureHelper->deleteTexture(&m_gradientTexture); - m_gradientTexture = 0; + m_selectionDirty = false; } - - m_gradientTexture = m_textureHelper->create2DTexture(image, false, true); } // This one needs to be called when the data size changes @@ -1656,7 +1762,7 @@ void Surface3DRenderer::initSelectionBuffer() m_selectionResultTexture = 0; } - m_selectionResultTexture = m_textureHelper->createSelectionTexture(m_mainViewPort.size(), + m_selectionResultTexture = m_textureHelper->createSelectionTexture(m_primarySubViewport.size(), m_selectionFrameBuffer, m_selectionDepthBuffer); } @@ -1689,11 +1795,6 @@ void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a *a = (id >> 24) & ID_TO_RGBA_MASK; } -void Surface3DRenderer::updateTextures() -{ - updateSurfaceGradient(m_cachedTheme.m_surfaceGradient); -} - void Surface3DRenderer::calculateSceneScalingFactors() { // Calculate scene scaling and translation factors @@ -1714,24 +1815,24 @@ void Surface3DRenderer::calculateSceneScalingFactors() #endif } -bool Surface3DRenderer::updateSmoothStatus(bool enable) +bool Surface3DRenderer::updateFlatStatus(bool enable) { - if (!enable && !m_flatSupported) { + if (enable && !m_flatSupported) { qWarning() << "Warning: Flat qualifier not supported on your platform's GLSL language." " Requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension."; - enable = true; + enable = false; } bool changed = false; - if (enable != m_cachedSmoothSurface) { - m_cachedSmoothSurface = enable; + if (enable != m_cachedFlatShading) { + m_cachedFlatShading = enable; changed = true; initSurfaceShaders(); } // If no surface object created yet, don't try to update the object if (m_surfaceObj && changed && m_sampleSpace.width() >= 2 && m_sampleSpace.height() >= 2) { - if (m_cachedSmoothSurface) { + if (!m_cachedFlatShading) { m_surfaceObj->setUpSmoothData(m_dataArray, m_sampleSpace, m_heightNormalizer, m_axisCacheY.min(), true); } else { @@ -1740,15 +1841,14 @@ bool Surface3DRenderer::updateSmoothStatus(bool enable) } } - return m_cachedSmoothSurface; + return m_cachedFlatShading; } -void Surface3DRenderer::updateSelectionMode(QDataVis::SelectionMode mode) +void Surface3DRenderer::updateSelectedPoint(const QPoint &position, const QSurface3DSeries *series) { - if (mode != m_cachedSelectionMode) - m_selectionModeChanged = true; - - Abstract3DRenderer::updateSelectionMode(mode); + m_selectedPoint = position; + m_selectedSeries = series; + m_selectionDirty = true; } void Surface3DRenderer::updateSurfaceGridStatus(bool enable) @@ -1782,65 +1882,63 @@ void Surface3DRenderer::loadGridLineMesh() { if (m_gridLineObj) delete m_gridLineObj; - m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/bar")); + m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); m_gridLineObj->load(); } -void Surface3DRenderer::handleResize() +void Surface3DRenderer::surfacePointSelected(const QPoint &point) { - if (m_cachedBoundingRect.width() == 0 || m_cachedBoundingRect.height() == 0) - return; + int row = point.x(); + int column = point.y(); - setViewPorts(); - - Abstract3DRenderer::handleResize(); -} - -void Surface3DRenderer::surfacePointSelected(int id) -{ - int column = (id - 1) % m_sampleSpace.width(); - int row = (id - 1) / m_sampleSpace.width(); - - if (row < 0 || column < 0 || m_dataArray.size() < row || m_dataArray.at(row)->size() < column) - return; - - qreal value = qreal(m_dataArray.at(row)->at(column).y()); + float value = m_dataArray.at(row)->at(column).y(); if (!m_selectionPointer) m_selectionPointer = new SelectionPointer(m_drawer); QVector3D pos; - if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { - pos = m_sliceSurfaceObj->vertexAt(column, 0); - pos *= QVector3D(m_surfaceScaleX, 1.0f, 0.0f); - pos += QVector3D(m_surfaceOffsetX, 0.0f, 0.0f); - m_selectionPointer->updateBoundingRect(m_sliceViewPort); - m_selectionPointer->updateSliceData(true, m_autoScaleAdjustment); - } else if (m_cachedSelectionMode == QDataVis::SelectionModeSliceColumn) { - pos = m_sliceSurfaceObj->vertexAt(row, 0); - pos *= QVector3D(m_surfaceScaleZ, 1.0f, 0.0f); - pos += QVector3D(-m_surfaceOffsetZ, 0.0f, 0.0f); - m_selectionPointer->updateBoundingRect(m_sliceViewPort); - m_selectionPointer->updateSliceData(true, m_autoScaleAdjustment); + if (m_cachedIsSlicingActivated) { + if (m_cachedSelectionMode.testFlag(QDataVis::SelectionRow)) { + pos = m_sliceSurfaceObj->vertexAt(column, 0); + pos *= QVector3D(m_surfaceScaleX, 1.0f, 0.0f); + pos += QVector3D(m_surfaceOffsetX, 0.0f, 0.0f); + m_selectionPointer->updateBoundingRect(m_secondarySubViewport); + m_selectionPointer->updateSliceData(true, m_autoScaleAdjustment); + } else if (m_cachedSelectionMode.testFlag(QDataVis::SelectionColumn)) { + pos = m_sliceSurfaceObj->vertexAt(row, 0); + pos *= QVector3D(m_surfaceScaleZ, 1.0f, 0.0f); + pos += QVector3D(-m_surfaceOffsetZ, 0.0f, 0.0f); + m_selectionPointer->updateBoundingRect(m_secondarySubViewport); + m_selectionPointer->updateSliceData(true, m_autoScaleAdjustment); + } } else { pos = m_surfaceObj->vertexAt(column, row); pos *= QVector3D(m_surfaceScaleX, 1.0f, m_surfaceScaleZ);; pos += QVector3D(m_surfaceOffsetX, 0.0f, m_surfaceOffsetZ); - m_selectionPointer->updateBoundingRect(m_mainViewPort); + m_selectionPointer->updateBoundingRect(m_primarySubViewport); m_selectionPointer->updateSliceData(false, m_autoScaleAdjustment); } m_selectionPointer->setPosition(pos); m_selectionPointer->setLabel(createSelectionLabel(value, column, row)); + // TODO: Get pointer object from correct series once multiseries support implemented + m_selectionPointer->setPointerObject(m_visibleSeriesList.at(0).object()); + m_selectionPointer->setHighlightColor(m_visibleSeriesList.at(0).singleHighlightColor()); m_selectionPointer->updateScene(m_cachedScene); +} - //Put the selection pointer flag active - m_selectionActive = true; +// Maps selection Id to surface point in data array +QPoint Surface3DRenderer::selectionIdToSurfacePoint(uint id) +{ + int column = ((id - 1) % m_sampleSpace.width()) + m_sampleSpace.x(); + int row = ((id - 1) / m_sampleSpace.width()) + m_sampleSpace.y(); + return QPoint(row, column); } -QString Surface3DRenderer::createSelectionLabel(qreal value, int column, int row) +QString Surface3DRenderer::createSelectionLabel(float value, int column, int row) { - QString labelText = itemLabelFormat(); + // TODO: Get from correct series once multiple series supported + QString labelText = m_visibleSeriesList[0].itemLabelFormat(); static const QString xTitleTag(QStringLiteral("@xTitle")); static const QString yTitleTag(QStringLiteral("@yTitle")); static const QString zTitleTag(QStringLiteral("@zTitle")); @@ -1879,16 +1977,10 @@ QString Surface3DRenderer::createSelectionLabel(qreal value, int column, int row return labelText; } -void Surface3DRenderer::loadMeshFile() -{ - // Do nothing, not yet supported by this renderer - // TODO: To be used for overriding the selection ball mesh after technology preview -} - void Surface3DRenderer::updateShadowQuality(QDataVis::ShadowQuality quality) { - qWarning() << "Shadows have been disabled for Q3DSurface in technology preview"; - m_cachedShadowQuality = QDataVis::ShadowQualityNone; //quality; + m_cachedShadowQuality = quality; + switch (quality) { case QDataVis::ShadowQualityLow: m_shadowQualityToShader = 33.3f; @@ -1920,11 +2012,18 @@ void Surface3DRenderer::updateShadowQuality(QDataVis::ShadowQuality quality) break; } + handleShadowQualityChange(); + #if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); #endif } +void Surface3DRenderer::updateTextures() +{ + // Do nothing, but required as it is pure virtual on parent +} + void Surface3DRenderer::updateSlicingActive(bool isSlicing) { if (m_cachedIsSlicingActivated == isSlicing) @@ -1932,50 +2031,58 @@ void Surface3DRenderer::updateSlicingActive(bool isSlicing) m_cachedIsSlicingActivated = isSlicing; - setViewPorts(); - if (!m_cachedIsSlicingActivated) initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize #if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well #endif -} -void Surface3DRenderer::setViewPorts() -{ - // Update view ports - if (m_cachedIsSlicingActivated) { - m_mainViewPort = QRect(0, - m_cachedBoundingRect.height() - - (m_cachedBoundingRect.height() / subViewDivider), - m_cachedBoundingRect.width() / subViewDivider, - m_cachedBoundingRect.height() / subViewDivider); - m_sliceViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); - if (m_selectionPointer) - m_selectionPointer->updateBoundingRect(m_sliceViewPort); - } else { - m_mainViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); - m_sliceViewPort = QRect(0, 0, 0, 0); - if (m_selectionPointer) - m_selectionPointer->updateBoundingRect(m_mainViewPort); - } + m_selectionDirty = true; } void Surface3DRenderer::loadLabelMesh() { if (m_labelObj) delete m_labelObj; - m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/label")); + m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); m_labelObj->load(); } void Surface3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) { + // m_shader is used slice view surface only. if (m_shader) delete m_shader; m_shader = new ShaderHelper(this, vertexShader, fragmentShader); m_shader->initialize(); + + // draw the shader for the surface according to smooth status, shadow and uniform color + if (m_surfaceShader) + delete m_surfaceShader; +#if !defined(QT_OPENGL_ES_2) + if (!m_cachedFlatShading) { + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex")); + } else { + m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurface")); + } + } else { + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), + QStringLiteral(":/shaders/fragmentSurfaceShadowFlat")); + } else { + m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceFlat")); + } + } +#else + m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); +#endif + m_surfaceShader->initialize(); } void Surface3DRenderer::initBackgroundShaders(const QString &vertexShader, @@ -1998,30 +2105,15 @@ void Surface3DRenderer::initSelectionShaders() void Surface3DRenderer::initSurfaceShaders() { - if (m_surfaceShader) - delete m_surfaceShader; - -#if !defined(QT_OPENGL_ES_2) - if (m_cachedSmoothSurface) { - m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurface"), - QStringLiteral(":/shaders/fragmentSurface")); - } else { - m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), - QStringLiteral(":/shaders/fragmentSurfaceFlat")); - } -#else - m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurface"), - QStringLiteral(":/shaders/fragmentSurfaceES2")); -#endif - m_surfaceShader->initialize(); - + // Gridline shader if (m_surfaceGridShader) delete m_surfaceGridShader; - - m_surfaceGridShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceGrid"), - QStringLiteral(":/shaders/fragmentSurfaceGrid")); - + m_surfaceGridShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"), + QStringLiteral(":/shaders/fragmentPlainColor")); m_surfaceGridShader->initialize(); + + // Triggers surface shader selection by shadow setting + handleShadowQualityChange(); } void Surface3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) @@ -2035,7 +2127,6 @@ void Surface3DRenderer::initLabelShaders(const QString &vertexShader, const QStr #if !defined(QT_OPENGL_ES_2) void Surface3DRenderer::initDepthShader() { - // TODO: Implement a depth shader for surface after technology preview if (m_depthShader) delete m_depthShader; m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), @@ -2050,52 +2141,33 @@ void Surface3DRenderer::updateDepthBuffer() m_depthTexture = 0; } - if (m_mainViewPort.size().isEmpty()) + if (m_primarySubViewport.size().isEmpty()) return; if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { - m_depthTexture = m_textureHelper->createDepthTexture(m_mainViewPort.size(), - m_depthFrameBuffer, - m_shadowQualityMultiplier); - if (!m_depthTexture) { - switch (m_cachedShadowQuality) { - case QDataVis::ShadowQualityHigh: - qWarning("Creating high quality shadows failed. Changing to medium quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityMedium); - updateShadowQuality(QDataVis::ShadowQualityMedium); - break; - case QDataVis::ShadowQualityMedium: - qWarning("Creating medium quality shadows failed. Changing to low quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityLow); - updateShadowQuality(QDataVis::ShadowQualityLow); - break; - case QDataVis::ShadowQualityLow: - qWarning("Creating low quality shadows failed. Switching shadows off."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityNone); - updateShadowQuality(QDataVis::ShadowQualityNone); - break; - case QDataVis::ShadowQualitySoftHigh: - qWarning("Creating soft high quality shadows failed. Changing to soft medium quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualitySoftMedium); - updateShadowQuality(QDataVis::ShadowQualitySoftMedium); - break; - case QDataVis::ShadowQualitySoftMedium: - qWarning("Creating soft medium quality shadows failed. Changing to soft low quality."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualitySoftLow); - updateShadowQuality(QDataVis::ShadowQualitySoftLow); - break; - case QDataVis::ShadowQualitySoftLow: - qWarning("Creating soft low quality shadows failed. Switching shadows off."); - (void)m_controller->setShadowQuality(QDataVis::ShadowQualityNone); - updateShadowQuality(QDataVis::ShadowQualityNone); - break; - default: - // You'll never get here - break; - } - } + m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + m_textureHelper->fillDepthTexture(m_depthTexture, m_primarySubViewport.size(), m_shadowQualityMultiplier, 1.0f); + m_depthModelTexture = m_textureHelper->createDepthTexture(m_primarySubViewport.size(), + m_shadowQualityMultiplier); + if (!m_depthTexture || !m_depthModelTexture) + lowerShadowQuality(); } } #endif +void Surface3DRenderer::generateUniformGradient(const QVector3D newColor) +{ + if (m_visibleSeriesList.size()) { + // TODO: move uniform gradient to render cache when multiseries support implemented QTRD-2657 + QColor newQColor = Utils::colorFromVector(newColor); + m_uniformGradientTextureColor = newColor; + QLinearGradient newGradient; + newGradient.setColorAt(0.0, newQColor); + newGradient.setColorAt(1.0, newQColor); + fixGradientAndGenerateTexture(&newGradient, &m_uniformGradientTexture); + } +} + QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index c805e508..f70fde9b 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -33,7 +33,6 @@ #include <QtCore/QObject> #include <QtGui/QOpenGLFunctions> #include <QtGui/QFont> -#include <QLinearGradient> #include <QWindow> #include "datavisualizationglobal_p.h" @@ -60,11 +59,9 @@ class QT_DATAVISUALIZATION_EXPORT Surface3DRenderer : public Abstract3DRenderer Q_OBJECT public: - Surface3DController *m_controller; - // Visual parameters QRect m_boundingRect; - QDataVis::LabelStyle m_labelStyle; + bool m_labelBackground; QFont m_font; bool m_isGridEnabled; @@ -72,8 +69,6 @@ private: bool m_cachedIsSlicingActivated; // Internal attributes purely related to how the scene is drawn with GL. - QRect m_mainViewPort; - QRect m_sliceViewPort; ShaderHelper *m_shader; ShaderHelper *m_depthShader; ShaderHelper *m_backgroundShader; @@ -103,15 +98,16 @@ private: SurfaceObject *m_surfaceObj; SurfaceObject *m_sliceSurfaceObj; GLuint m_depthTexture; + GLuint m_depthModelTexture; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; GLuint m_selectionDepthBuffer; - GLuint m_gradientTexture; GLuint m_selectionTexture; GLuint m_selectionResultTexture; GLfloat m_shadowQualityToShader; - bool m_cachedSmoothSurface; + bool m_cachedFlatShading; bool m_flatSupported; + bool m_cachedSurfaceVisible; bool m_cachedSurfaceGridOn; SelectionPointer *m_selectionPointer; bool m_selectionActive; @@ -124,36 +120,42 @@ private: QRect m_sampleSpace; GLint m_shadowQualityMultiplier; QSizeF m_areaSize; - uint m_cachedSelectionId; - bool m_selectionModeChanged; + uint m_clickedPointId; bool m_hasHeightAdjustmentChanged; + QPoint m_selectedPoint; + const QSurface3DSeries *m_selectedSeries; + GLuint m_uniformGradientTexture; + QVector3D m_uniformGradientTextureColor; public: explicit Surface3DRenderer(Surface3DController *controller); ~Surface3DRenderer(); - void updateDataModel(QSurfaceDataProxy *dataProxy); + void updateData(); + void updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility); + void updateRows(const QVector<int> &rows); + void updateItem(const QVector<QPoint> &points); void updateScene(Q3DScene *scene); + bool updateFlatStatus(bool enable); + void updateSurfaceGridStatus(bool enable); + void updateSlicingActive(bool isSlicing); + void updateSelectedPoint(const QPoint &position, const QSurface3DSeries *series); + void drawSlicedScene(); void render(GLuint defaultFboHandle = 0); protected: void initializeOpenGL(); - virtual void loadMeshFile(); -public slots: - bool updateSmoothStatus(bool enable); - void updateSurfaceGridStatus(bool enable); - void updateSurfaceGradient(const QLinearGradient &gradient); - void updateSlicingActive(bool isSlicing); - void updateSelectionMode(QDataVis::SelectionMode mode); +signals: + void pointClicked(QPoint position, QSurface3DSeries *series); + void flatShadingSupportedChanged(bool supported); private: - void setViewPorts(); - void updateSliceDataModel(int selectionId); - virtual void updateShadowQuality(QDataVis::ShadowQuality quality); - virtual void updateTextures(); - virtual void initShaders(const QString &vertexShader, const QString &fragmentShader); + void updateSliceDataModel(const QPoint &point); + void updateShadowQuality(QDataVis::ShadowQuality quality); + void updateTextures(); + void initShaders(const QString &vertexShader, const QString &fragmentShader); QRect calculateSampleRect(const QSurfaceDataArray &array); void loadBackgroundMesh(); void loadGridLineMesh(); @@ -161,7 +163,6 @@ private: void loadSurfaceObj(); void loadSliceSurfaceObj(); void drawScene(GLuint defaultFboHandle); - void handleResize(); void calculateSceneScalingFactors(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); @@ -172,11 +173,14 @@ private: void updateSelectionTexture(); void idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a); void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride); - void surfacePointSelected(int id); - QString createSelectionLabel(qreal value, int column, int row); + void surfacePointSelected(const QPoint &point); + QPoint selectionIdToSurfacePoint(uint id); + QString createSelectionLabel(float value, int column, int row); #if !defined(QT_OPENGL_ES_2) void updateDepthBuffer(); #endif + void emitSelectedPointChanged(QPoint position); + void generateUniformGradient(const QVector3D newColor); Q_DISABLE_COPY(Surface3DRenderer) }; diff --git a/src/datavisualization/engine/theme.cpp b/src/datavisualization/engine/theme.cpp deleted file mode 100644 index 387540b1..00000000 --- a/src/datavisualization/engine/theme.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the QtDataVisualization module. -** -** Licensees holding valid Qt Enterprise licenses may use this file in -** accordance with the Qt Enterprise License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** -****************************************************************************/ - -#include "theme_p.h" - -#ifdef Q_OS_WIN -#include <windows.h> -#include <stdio.h> -#endif - -QT_DATAVISUALIZATION_BEGIN_NAMESPACE - -Theme::Theme() - : m_baseColor(QColor(Qt::gray)), - m_heightColor(QColor(Qt::black)), - m_depthColor(QColor(Qt::black)), - m_backgroundColor(QColor(Qt::gray)), - m_windowColor(QColor(Qt::gray)), - m_textColor(QColor(Qt::white)), - m_textBackgroundColor(QColor(0x00, 0x00, 0x00, 0xa0)), - m_gridLine(QColor(Qt::black)), - m_highlightBarColor(QColor(Qt::red)), - m_highlightRowColor(QColor(Qt::darkRed)), - m_highlightColumnColor(QColor(Qt::darkMagenta)), - m_surfaceGradient(QLinearGradient(2, 1024, 0, 0)), - m_lightStrength(4.0f), - m_ambientStrength(0.3f), - m_highlightLightStrength(8.0f), - m_uniformColor(true), - m_labelBorders(false) -{ - // Default values for surface gradient -} - -Theme::~Theme() -{ -} - -QDataVis::Theme Theme::theme() -{ - return m_theme; -} - -void Theme::useTheme(QDataVis::Theme theme) -{ - m_theme = theme; - switch (theme) { - case QDataVis::ThemeQt: { - m_baseColor = QColor(QRgb(0x80c342)); - //m_heightColor = QColor(QRgb(0x)); - //m_depthColor = QColor(QRgb(0x)); - m_backgroundColor = QColor(QRgb(0xffffff)); - m_windowColor = QColor(QRgb(0xffffff)); - m_textColor = QColor(QRgb(0x35322f)); - m_textBackgroundColor = QColor(0xff, 0xff, 0xff, 0x99); - m_gridLine = QColor(QRgb(0xd7d6d5)); - m_highlightBarColor = QColor(QRgb(0x14aaff)); - m_highlightRowColor = QColor(QRgb(0x6400aa)); - m_highlightColumnColor = QColor(QRgb(0x6400aa)); - m_lightStrength = 5.0f; - m_ambientStrength = 0.5f; - m_highlightLightStrength = 5.0f; - m_uniformColor = true; - m_labelBorders = true; - break; - } - case QDataVis::ThemePrimaryColors: { - m_baseColor = QColor(QRgb(0xffe400)); - //m_heightColor = QColor(QRgb(0x)); - //m_depthColor = QColor(QRgb(0x)); - m_backgroundColor = QColor(QRgb(0xffffff)); - m_windowColor = QColor(QRgb(0xffffff)); - m_textColor = QColor(QRgb(0x000000)); - m_textBackgroundColor = QColor(0xff, 0xff, 0xff, 0x99); - m_gridLine = QColor(QRgb(0xd7d6d5)); - m_highlightBarColor = QColor(QRgb(0x27beee)); - m_highlightRowColor = QColor(QRgb(0xee1414)); - m_highlightColumnColor = QColor(QRgb(0xee1414)); - m_lightStrength = 5.0f; - m_ambientStrength = 0.5f; - m_highlightLightStrength = 5.0f; - m_uniformColor = true; - m_labelBorders = false; - break; - } - case QDataVis::ThemeDigia: { - m_baseColor = QColor(QRgb(0xcccccc)); - //m_heightColor = QColor(QRgb(0x)); - //m_depthColor = QColor(QRgb(0x)); - m_backgroundColor = QColor(QRgb(0xffffff)); - m_windowColor = QColor(QRgb(0xffffff)); - m_textColor = QColor(QRgb(0x000000)); - m_textBackgroundColor = QColor(0xff, 0xff, 0xff, 0x80); - m_gridLine = QColor(QRgb(0xd7d6d5)); - m_highlightBarColor = QColor(QRgb(0xfa0000)); - m_highlightRowColor = QColor(QRgb(0x555555)); - m_highlightColumnColor = QColor(QRgb(0x555555)); - m_lightStrength = 5.0f; - m_ambientStrength = 0.5f; - m_highlightLightStrength = 5.0f; - m_uniformColor = false; - m_labelBorders = false; - break; - } - case QDataVis::ThemeStoneMoss: { - m_baseColor = QColor(QRgb(0xbeb32b)); - //m_heightColor = QColor(QRgb(0x)); - //m_depthColor = QColor(QRgb(0x)); - m_backgroundColor = QColor(QRgb(0x4d4d4f)); - m_windowColor = QColor(QRgb(0x4d4d4f)); - m_textColor = QColor(QRgb(0xffffff)); - m_textBackgroundColor = QColor(0x4d, 0x4d, 0x4f, 0xcd); - m_gridLine = QColor(QRgb(0x3e3e40)); - m_highlightBarColor = QColor(QRgb(0xfbf6d6)); - m_highlightRowColor = QColor(QRgb(0x442f20)); - m_highlightColumnColor = QColor(QRgb(0x442f20)); - m_lightStrength = 5.0f; - m_ambientStrength = 0.5f; - m_highlightLightStrength = 5.0f; - m_uniformColor = true; - m_labelBorders = true; - break; - } - case QDataVis::ThemeArmyBlue: { - m_baseColor = QColor(QRgb(0x495f76)); - //m_heightColor = QColor(QRgb(0x)); - //m_depthColor = QColor(QRgb(0x)); - m_backgroundColor = QColor(QRgb(0xd5d6d7)); - m_windowColor = QColor(QRgb(0xd5d6d7)); - m_textColor = QColor(QRgb(0x000000)); - m_textBackgroundColor = QColor(0xd5, 0xd6, 0xd7, 0xcd); - m_gridLine = QColor(QRgb(0xaeadac)); - m_highlightBarColor = QColor(QRgb(0x2aa2f9)); - m_highlightRowColor = QColor(QRgb(0x103753)); - m_highlightColumnColor = QColor(QRgb(0x103753)); - m_lightStrength = 5.0f; - m_ambientStrength = 0.5f; - m_highlightLightStrength = 5.0f; - m_uniformColor = false; - m_labelBorders = false; - break; - } - case QDataVis::ThemeRetro: { - m_baseColor = QColor(QRgb(0x533b23)); - //m_heightColor = QColor(QRgb(0x)); - //m_depthColor = QColor(QRgb(0x)); - m_backgroundColor = QColor(QRgb(0xe9e2ce)); - m_windowColor = QColor(QRgb(0xe9e2ce)); - m_textColor = QColor(QRgb(0x000000)); - m_textBackgroundColor = QColor(0xe9, 0xe2, 0xce, 0xc0); - m_gridLine = QColor(QRgb(0xd0c0b0)); - m_highlightBarColor = QColor(QRgb(0x8ea317)); - m_highlightRowColor = QColor(QRgb(0xc25708)); - m_highlightColumnColor = QColor(QRgb(0xc25708)); - m_lightStrength = 5.0f; - m_ambientStrength = 0.5f; - m_highlightLightStrength = 5.0f; - m_uniformColor = false; - m_labelBorders = false; - break; - } - case QDataVis::ThemeEbony: { - m_baseColor = QColor(QRgb(0xffffff)); - //m_heightColor = QColor(QRgb(0x)); - //m_depthColor = QColor(QRgb(0x)); - m_backgroundColor = QColor(QRgb(0x000000)); - m_windowColor = QColor(QRgb(0x000000)); - m_textColor = QColor(QRgb(0xaeadac)); - m_textBackgroundColor = QColor(0x00, 0x00, 0x00, 0xcd); - m_gridLine = QColor(QRgb(0x35322f)); - m_highlightBarColor = QColor(QRgb(0xf5dc0d)); - m_highlightRowColor = QColor(QRgb(0xd72222)); - m_highlightColumnColor = QColor(QRgb(0xd72222)); - m_lightStrength = 5.0f; - m_ambientStrength = 0.5f; - m_highlightLightStrength = 5.0f; - m_uniformColor = true; - m_labelBorders = false; - break; - } - case QDataVis::ThemeIsabelle: { - m_baseColor = QColor(QRgb(0xf9d900)); - //m_heightColor = QColor(QRgb(0x)); - //m_depthColor = QColor(QRgb(0x)); - m_backgroundColor = QColor(QRgb(0x000000)); - m_windowColor = QColor(QRgb(0x000000)); - m_textColor = QColor(QRgb(0xaeadac)); - m_textBackgroundColor = QColor(0x00, 0x00, 0x00, 0xc0); - m_gridLine = QColor(QRgb(0x35322f)); - m_highlightBarColor = QColor(QRgb(0xfff7cc)); - m_highlightRowColor = QColor(QRgb(0xde0a0a)); - m_highlightColumnColor = QColor(QRgb(0xde0a0a)); - m_lightStrength = 5.0f; - m_ambientStrength = 0.5f; - m_highlightLightStrength = 5.0f; - m_uniformColor = true; - m_labelBorders = false; - break; - } - default: - break; - } - if (m_uniformColor) { - m_surfaceGradient.setColorAt(0.0, m_baseColor); - } else { - m_surfaceGradient.setColorAt(0.0, QColor(m_baseColor.redF() * 0.7, - m_baseColor.greenF() * 0.7, - m_baseColor.blueF() * 0.7)); - } - m_surfaceGradient.setColorAt(1.0, m_baseColor); -} - -void Theme::setFromTheme(Theme &theme) -{ - m_theme = theme.m_theme; - m_baseColor = theme.m_baseColor; - m_heightColor = theme.m_heightColor; - m_depthColor = theme.m_depthColor; - m_backgroundColor = theme.m_backgroundColor; - m_windowColor = theme.m_windowColor; - m_textColor = theme.m_textColor; - m_textBackgroundColor = theme.m_textBackgroundColor; - m_gridLine = theme.m_gridLine; - m_highlightBarColor = theme.m_highlightBarColor; - m_highlightRowColor = theme.m_highlightRowColor; - m_highlightColumnColor = theme.m_highlightColumnColor; - m_surfaceGradient = theme.m_surfaceGradient; - m_lightStrength = theme.m_lightStrength; - m_ambientStrength = theme.m_ambientStrength; - m_highlightLightStrength = theme.m_highlightLightStrength; - m_uniformColor = theme.m_uniformColor; - m_labelBorders = theme.m_labelBorders; -} - -QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/engine/theme_p.h b/src/datavisualization/engine/theme_p.h deleted file mode 100644 index ec689f63..00000000 --- a/src/datavisualization/engine/theme_p.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the QtDataVisualization module. -** -** Licensees holding valid Qt Enterprise licenses may use this file in -** accordance with the Qt Enterprise License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the QtDataVisualization API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. - -#ifndef THEME_P_H -#define THEME_P_H - -#include "datavisualizationglobal_p.h" -#include "q3dbars.h" -#include <QLinearGradient> - -class QColor; - -QT_DATAVISUALIZATION_BEGIN_NAMESPACE - -class QT_DATAVISUALIZATION_EXPORT Theme -{ -public: - explicit Theme(); - ~Theme(); - - void useTheme(QDataVis::Theme theme); - QDataVis::Theme theme(); - void setFromTheme(Theme &theme); - -private: - friend class Abstract3DController; - friend class Abstract3DRenderer; - friend class Bars3DRenderer; - friend class Surface3DRenderer; - friend class Surface3DController; - friend class Scatter3DRenderer; - friend class SelectionPointer; - friend class Drawer; - - QDataVis::Theme m_theme; - QColor m_baseColor; - QColor m_heightColor; - QColor m_depthColor; - QColor m_backgroundColor; - QColor m_windowColor; - QColor m_textColor; - QColor m_textBackgroundColor; - QColor m_gridLine; - QColor m_highlightBarColor; - QColor m_highlightRowColor; - QColor m_highlightColumnColor; - QLinearGradient m_surfaceGradient; - float m_lightStrength; - float m_ambientStrength; - float m_highlightLightStrength; - bool m_uniformColor; - bool m_labelBorders; -}; - -QT_DATAVISUALIZATION_END_NAMESPACE - -#endif |