From 0ab45b018fa98e9ef6cffd70178f208eb4f16550 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 25 Sep 2014 15:04:44 +0300 Subject: Implement zooming to cursor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zooming to cursor is now default operating mode of the default input handler. Task-number: QTRD-3263 Change-Id: I5699fc0ce7393059538972cd52f31f06d87e3d8d Reviewed-by: Tomi Korpipää --- ...tdatavisualization-qml-abstractdeclarative.qdoc | 22 ++++ .../engine/abstract3dcontroller.cpp | 33 ++++-- .../engine/abstract3dcontroller_p.h | 7 +- .../engine/abstract3drenderer.cpp | 124 +++++++++++++++++++-- .../engine/abstract3drenderer_p.h | 19 +++- src/datavisualization/engine/bars3drenderer.cpp | 20 +++- src/datavisualization/engine/engine.qrc | 3 +- src/datavisualization/engine/q3dscene.cpp | 62 ++++++++++- src/datavisualization/engine/q3dscene.h | 5 + src/datavisualization/engine/q3dscene_p.h | 3 + src/datavisualization/engine/qabstract3dgraph.cpp | 27 +++++ src/datavisualization/engine/qabstract3dgraph.h | 4 + src/datavisualization/engine/scatter3drenderer.cpp | 10 +- .../engine/shaders/colorandposition.vert | 10 -- src/datavisualization/engine/shaders/position.vert | 10 ++ .../engine/shaders/positionmap.frag | 11 ++ src/datavisualization/engine/surface3drenderer.cpp | 17 ++- src/datavisualization/input/q3dinputhandler.cpp | 122 +++++++++++++++++++- src/datavisualization/input/q3dinputhandler.h | 4 + src/datavisualization/input/q3dinputhandler_p.h | 19 +++- .../input/qtouch3dinputhandler.cpp | 22 +++- .../input/qtouch3dinputhandler_p.h | 9 +- src/datavisualization/utils/texturehelper.cpp | 25 +++++ src/datavisualization/utils/texturehelper_p.h | 2 + 24 files changed, 529 insertions(+), 61 deletions(-) delete mode 100644 src/datavisualization/engine/shaders/colorandposition.vert create mode 100644 src/datavisualization/engine/shaders/position.vert create mode 100644 src/datavisualization/engine/shaders/positionmap.frag (limited to 'src/datavisualization') diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc index 5f06d404..3dc5beea 100644 --- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc +++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc @@ -385,3 +385,25 @@ * * \sa ValueAxis3D::labelFormat */ + +/*! + * \qmlproperty vector3d AbstractGraph3D::queriedGraphPosition + * \since QtDataVisualization 1.2 + * + * This read-only property contains the latest graph position values along each axis queried using + * Scene3D::graphPositionQuery. The values are normalized to range \c{[-1, 1]}. + * If the queried position was outside the graph bounds, the values + * will not reflect the real position, but will instead be some undefined position outside + * the range \c{[-1, 1]}. The value will be undefined before any queries are made. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs only allow querying graph position at the graph floor level, + * so the Y-value is always zero for bar graphs and the valid queries can be only made at + * screen positions that contain the floor of the graph. + * + * \sa Scene3D::graphPositionQuery + */ + diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index e2f8c1ad..c1d27bab 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -62,6 +62,7 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen { if (!m_scene) m_scene = new Q3DScene; + m_scene->setParent(this); // Set initial theme Q3DTheme *defaultTheme = new Q3DTheme(Q3DTheme::ThemeQt); @@ -165,7 +166,12 @@ void Abstract3DController::synchDataToRenderer() { // Subclass implementations check for renderer validity already, so no need to check here. - // If there is a pending click from renderer, handle that first. + // If there are pending queries, handle those first + if (m_renderer->isGraphPositionQueryPending()) { + handlePendingGraphPositionQuery(); + m_renderer->clearGraphPositionQueryPending(); + } + if (m_renderer->isClickPending()) { handlePendingClick(); m_renderer->clearClickPending(); @@ -804,14 +810,15 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH addInputHandler(inputHandler); m_activeInputHandler = inputHandler; - if (m_activeInputHandler) + if (m_activeInputHandler) { m_activeInputHandler->setScene(m_scene); - // Connect the input handler - QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this, - &Abstract3DController::handleInputViewChanged); - QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this, - &Abstract3DController::handleInputPositionChanged); + // Connect the input handler + QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this, + &Abstract3DController::handleInputViewChanged); + QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this, + &Abstract3DController::handleInputPositionChanged); + } // Notify change of input handler emit activeInputHandlerChanged(m_activeInputHandler); @@ -1468,6 +1475,12 @@ void Abstract3DController::handlePendingClick() emit elementSelected(type); } +void Abstract3DController::handlePendingGraphPositionQuery() +{ + m_queriedGraphPosition = m_renderer->queriedGraphPosition(); + emit queriedGraphPositionChanged(m_queriedGraphPosition); +} + int Abstract3DController::selectedLabelIndex() const { int index = m_renderer->m_selectedLabelIndex; @@ -1659,4 +1672,10 @@ QLocale Abstract3DController::locale() const return m_locale; } +QVector3D Abstract3DController::queriedGraphPosition() const +{ + return m_queriedGraphPosition; +} + + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 03061a13..125e9885 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -173,6 +173,7 @@ private: bool m_reflectionEnabled; qreal m_reflectivity; QLocale m_locale; + QVector3D m_queriedGraphPosition; protected: Q3DScene *m_scene; @@ -302,6 +303,8 @@ public: void setLocale(const QLocale &locale); QLocale locale() const; + QVector3D queriedGraphPosition() const; + void emitNeedRender(); virtual void clearSelection() = 0; @@ -327,7 +330,8 @@ public: virtual void handleAxisTitleVisibilityChangedBySender(QObject *sender); virtual void handleAxisTitleFixedChangedBySender(QObject *sender); virtual void handleSeriesVisibilityChangedBySender(QObject *sender); - virtual void handlePendingClick() = 0; + virtual void handlePendingClick(); + virtual void handlePendingGraphPositionQuery(); virtual void adjustAxisRanges() = 0; void markSeriesItemLabelsDirty(); @@ -384,6 +388,7 @@ signals: void reflectionChanged(bool enabled); void reflectivityChanged(qreal reflectivity); void localeChanged(const QLocale &locale); + void queriedGraphPositionChanged(const QVector3D &data); protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index d0f3c04b..a5e1bc9b 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -54,8 +54,11 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_devicePixelRatio(1.0f), m_selectionLabelDirty(true), m_clickPending(false), + m_graphPositionQueryPending(false), m_clickedSeries(0), m_clickedType(QAbstract3DGraph::ElementNone), + m_selectedLabelIndex(-1), + m_selectedCustomItemIndex(-1), m_selectionLabelItem(0), m_visibleSeriesCount(0), m_customItemShader(0), @@ -64,6 +67,9 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_volumeTextureSliceShader(0), m_volumeSliceFrameShader(0), m_labelShader(0), + m_cursorPositionShader(0), + m_cursorPositionFrameBuffer(0), + m_cursorPositionTexture(0), m_useOrthoProjection(false), m_xFlipped(false), m_yFlipped(false), @@ -72,6 +78,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_backgroundObj(0), m_gridLineObj(0), m_labelObj(0), + m_positionMapperObj(0), m_graphAspectRatio(2.0f), m_graphHorizontalAspectRatio(0.0f), m_polarGraph(false), @@ -110,6 +117,7 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_volumeSliceFrameShader; delete m_volumeTextureSliceShader; delete m_labelShader; + delete m_cursorPositionShader; foreach (SeriesRenderCache *cache, m_renderCacheList) { cache->cleanup(m_textureHelper); @@ -127,9 +135,15 @@ Abstract3DRenderer::~Abstract3DRenderer() ObjectHelper::releaseObjectHelper(this, m_backgroundObj); ObjectHelper::releaseObjectHelper(this, m_gridLineObj); ObjectHelper::releaseObjectHelper(this, m_labelObj); + ObjectHelper::releaseObjectHelper(this, m_positionMapperObj); if (m_textureHelper) { m_textureHelper->deleteTexture(&m_depthTexture); + m_textureHelper->deleteTexture(&m_cursorPositionTexture); + + if (QOpenGLContext::currentContext()) + m_textureHelper->glDeleteFramebuffers(1, &m_cursorPositionFrameBuffer); + delete m_textureHelper; } } @@ -157,6 +171,12 @@ void Abstract3DRenderer::initializeOpenGL() initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), QStringLiteral(":/shaders/fragmentLabel")); + + initCursorPositionShaders(QStringLiteral(":/shaders/vertexPosition"), + QStringLiteral(":/shaders/fragmentPositionMap")); + + loadLabelMesh(); + loadPositionMapperMesh(); } void Abstract3DRenderer::render(const GLuint defaultFboHandle) @@ -191,11 +211,6 @@ void Abstract3DRenderer::updateSelectionState(SelectionState state) m_selectionState = state; } -void Abstract3DRenderer::updateInputPosition(const QPoint &position) -{ - m_inputPosition = position; -} - void Abstract3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader) { @@ -244,6 +259,27 @@ void Abstract3DRenderer::initLabelShaders(const QString &vertexShader, const QSt m_labelShader->initialize(); } +void Abstract3DRenderer::initCursorPositionShaders(const QString &vertexShader, + const QString &fragmentShader) +{ + // Init the shader + delete m_cursorPositionShader; + m_cursorPositionShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_cursorPositionShader->initialize(); +} + +void Abstract3DRenderer::initCursorPositionBuffer() +{ + m_textureHelper->deleteTexture(&m_cursorPositionTexture); + + if (m_primarySubViewport.size().isEmpty()) + return; + + m_cursorPositionTexture = + m_textureHelper->createCursorPositionTexture(m_primarySubViewport.size(), + m_cursorPositionFrameBuffer); +} + void Abstract3DRenderer::updateTheme(Q3DTheme *theme) { // Synchronize the controller theme with renderer @@ -270,8 +306,12 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) } QPoint logicalPixelPosition = scene->selectionQueryPosition(); - updateInputPosition(QPoint(logicalPixelPosition.x() * m_devicePixelRatio, - logicalPixelPosition.y() * m_devicePixelRatio)); + m_inputPosition = QPoint(logicalPixelPosition.x() * m_devicePixelRatio, + logicalPixelPosition.y() * m_devicePixelRatio); + + QPoint logicalGraphPosition = scene->graphPositionQuery(); + m_graphPositionQuery = QPoint(logicalGraphPosition.x() * m_devicePixelRatio, + logicalGraphPosition.y() * m_devicePixelRatio); // Synchronize the renderer scene to controller scene scene->d_ptr->sync(*m_cachedScene->d_ptr); @@ -296,6 +336,11 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) updateSelectionState(SelectOnScene); } } + + if (Q3DScene::invalidSelectionPoint() != logicalGraphPosition) { + m_graphPositionQueryPending = true; + scene->setGraphPositionQuery(Q3DScene::invalidSelectionPoint()); + } } void Abstract3DRenderer::updateTextures() @@ -347,7 +392,7 @@ void Abstract3DRenderer::reInitShaders() QStringLiteral(":/shaders/fragmentTexture3D"), QStringLiteral(":/shaders/fragmentTexture3DLowDef"), QStringLiteral(":/shaders/fragmentTexture3DSlice"), - QStringLiteral(":/shaders/colorAndPosition"), + QStringLiteral(":/shaders/vertexPosition"), QStringLiteral(":/shaders/fragment3DSliceFrames")); #else if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) @@ -434,6 +479,8 @@ void Abstract3DRenderer::handleResize() // Re-init depth buffer updateDepthBuffer(); #endif + + initCursorPositionBuffer(); } void Abstract3DRenderer::calculateZoomLevel() @@ -929,6 +976,12 @@ void Abstract3DRenderer::loadLabelMesh() QStringLiteral(":/defaultMeshes/plane")); } +void Abstract3DRenderer::loadPositionMapperMesh() +{ + ObjectHelper::resetObjectHelper(this, m_positionMapperObj, + QStringLiteral(":/defaultMeshes/barFull")); +} + void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture) { m_textureHelper->deleteTexture(texture); @@ -1305,7 +1358,7 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, // Check that the render item is visible, and skip drawing if not // Also check if reflected item is on the "wrong" side, and skip drawing if it is if (!item->isVisible() || ((m_reflectionEnabled && reflection < 0.0f) - && (m_yFlipped == item->translation().y() >= 0.0))) { + && (m_yFlipped == (item->translation().y() >= 0.0)))) { continue; } if (loopCount == 0) { @@ -1627,6 +1680,59 @@ void Abstract3DRenderer::drawVolumeSliceFrame(const CustomRenderItem *item, Qt:: } +void Abstract3DRenderer::queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix, + const QVector3D &scaling, + GLuint defaultFboHandle) +{ + m_cursorPositionShader->bind(); + + // Set up mapper framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, m_cursorPositionFrameBuffer); + glViewport(0, 0, + m_primarySubViewport.width(), + m_primarySubViewport.height()); + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DITHER); // Dither may affect colors if enabled + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + + // Draw a cube scaled to the graph dimensions + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + modelMatrix.scale(scaling); + + MVPMatrix = projectionViewMatrix * modelMatrix; + m_cursorPositionShader->setUniformValue(m_cursorPositionShader->MVP(), MVPMatrix); + m_drawer->drawObject(m_cursorPositionShader, m_positionMapperObj); + + QVector4D dataColor = Utils::getSelection(m_graphPositionQuery, + m_primarySubViewport.height()); + if (dataColor.w() > 0.0f) { + // If position is outside the graph, set the position well outside the graph boundaries + dataColor = QVector4D(-10000.0f, -10000.0f, -10000.0f, 0.0f); + } else { + // Normalize to range [0.0, 1.0] + dataColor /= 255.0f; + } + + // Restore state + glEnable(GL_DITHER); + glCullFace(GL_BACK); + glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); + + QVector3D normalizedValues = dataColor.toVector3D() * 2.0f; + normalizedValues -= oneVector; + m_queriedGraphPosition = QVector3D(normalizedValues.x(), + normalizedValues.y(), + normalizedValues.z()); +} + void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const { // x is angular, z is radial diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index a46e6176..6d134ee3 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -80,7 +80,6 @@ public: virtual void updateTextures(); 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; @@ -99,6 +98,9 @@ public: const QString &sliceFrameVertexShader, const QString &sliceFrameShader); virtual void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initCursorPositionShaders(const QString &vertexShader, + const QString &fragmentShader); + virtual void initCursorPositionBuffer(); virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); @@ -120,7 +122,7 @@ public: virtual void updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation, float angle); virtual void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, - bool visible); + bool visible); virtual void updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation, bool fixed); virtual void modifiedSeriesList(const QVector &seriesList); @@ -145,6 +147,9 @@ public: inline void clearClickPending() { m_clickPending = false; } inline QAbstract3DSeries *clickedSeries() const { return m_clickedSeries; } inline QAbstract3DGraph::ElementType clickedType() { return m_clickedType; } + inline bool isGraphPositionQueryPending() { return m_graphPositionQueryPending; } + inline void clearGraphPositionQueryPending() { m_graphPositionQueryPending = false; } + inline QVector3D queriedGraphPosition() const { return m_queriedGraphPosition; } LabelItem &selectionLabelItem(); void setSelectionLabel(const QString &label); @@ -199,6 +204,7 @@ protected: void loadGridLineMesh(); void loadLabelMesh(); + void loadPositionMapperMesh(); void drawRadialGrid(ShaderHelper *shader, float yFloorLinePos, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix); @@ -213,6 +219,8 @@ protected: virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0; void drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis, const QMatrix4x4 &projectionViewMatrix); + void queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix, const QVector3D &scaling, + GLuint defaultFboHandle); bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; @@ -241,10 +249,13 @@ protected: float m_devicePixelRatio; bool m_selectionLabelDirty; bool m_clickPending; + bool m_graphPositionQueryPending; QAbstract3DSeries *m_clickedSeries; QAbstract3DGraph::ElementType m_clickedType; int m_selectedLabelIndex; int m_selectedCustomItemIndex; + QVector3D m_queriedGraphPosition; + QPoint m_graphPositionQuery; QString m_selectionLabel; LabelItem *m_selectionLabelItem; @@ -256,6 +267,9 @@ protected: ShaderHelper *m_volumeTextureSliceShader; ShaderHelper *m_volumeSliceFrameShader; ShaderHelper *m_labelShader; + ShaderHelper *m_cursorPositionShader; + GLuint m_cursorPositionFrameBuffer; + GLuint m_cursorPositionTexture; bool m_useOrthoProjection; bool m_xFlipped; @@ -266,6 +280,7 @@ protected: ObjectHelper *m_backgroundObj; // Shared reference ObjectHelper *m_gridLineObj; // Shared reference ObjectHelper *m_labelObj; // Shared reference + ObjectHelper *m_positionMapperObj; // Shared reference float m_graphAspectRatio; float m_graphHorizontalAspectRatio; diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 9f3c2e2a..2d394d57 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -122,9 +122,6 @@ void Bars3DRenderer::initializeOpenGL() // Load grid line mesh loadGridLineMesh(); - // Load label mesh - loadLabelMesh(); - // Load background mesh (we need to be initialized first) loadBackgroundMesh(); } @@ -1161,6 +1158,16 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } #endif + // Do position mapping when necessary + if (m_graphPositionQueryPending) { + QVector3D graphDimensions(m_xScaleFactor, 0.0f, m_zScaleFactor); + queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle); + + // Y is always at floor level + m_queriedGraphPosition.setY(0.0f); + emit needRender(); + } + // Skip selection mode drawing if we're slicing or have no selection mode if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone && m_selectionState == SelectOnScene @@ -2732,8 +2739,11 @@ void Bars3DRenderer::updateSlicingActive(bool isSlicing) m_cachedIsSlicingActivated = isSlicing; - if (!m_cachedIsSlicingActivated) - initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize + if (!m_cachedIsSlicingActivated) { + // We need to re-init selection buffer in case there has been a resize + initSelectionBuffer(); + initCursorPositionBuffer(); + } #if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index af759ba3..8c234a3b 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -67,6 +67,7 @@ shaders/texture3dlowdef.frag shaders/point_ES2_UV.vert shaders/3dsliceframes.frag - shaders/colorandposition.vert + shaders/position.vert + shaders/positionmap.frag diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index 08721b97..87826a8b 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -95,6 +95,26 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * After the rendering pass the property is returned to its default state of invalidSelectionPoint. */ +/*! + * \qmlproperty point Scene3D::graphPositionQuery + * + * This property contains the coordinates for the user input that should be processed + * by the scene as a graph position query. If this is set to value other than + * invalidSelectionPoint, the graph tries to match a graph position to the given \a point + * within the primary viewport. + * After the rendering pass this property is returned to its default state of + * invalidSelectionPoint. The queried graph position can be read from + * AbstractGraph3D::queriedGraphPosition property after the next render pass. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs allow graph position queries only at the graph floor level. + * + * \sa AbstractGraph3D::queriedGraphPosition + */ + /*! * \qmlproperty bool Scene3D::slicingActive * @@ -258,7 +278,7 @@ void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport) * \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 + * by the scene as a selection. If this is set to value other than invalidSelectionPoint(), the * graph tries to select a data item, axis label, or a custom item at the given \a point within * the primary viewport. * After the rendering pass the property is returned to its default state of @@ -292,6 +312,42 @@ QPoint Q3DScene::invalidSelectionPoint() return invalidSelectionPos; } +/*! + * \property Q3DScene::graphPositionQuery + * + * This property contains the coordinates for the user input that should be processed + * by the scene as a graph position query. If this is set to value other than + * invalidSelectionPoint(), the graph tries to match a graph position to the given \a point + * within the primary viewport. + * After the rendering pass this property is returned to its default state of + * invalidSelectionPoint(). The queried graph position can be read from + * QAbstract3DGraph::queriedGraphPosition property after the next render pass. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs allow graph position queries only at the graph floor level. + * + * \sa QAbstract3DGraph::queriedGraphPosition + */ +void Q3DScene::setGraphPositionQuery(const QPoint &point) +{ + if (point != d_ptr->m_graphPositionQueryPosition) { + d_ptr->m_graphPositionQueryPosition = point; + d_ptr->m_changeTracker.graphPositionQueryPositionChanged = true; + d_ptr->m_sceneDirty = true; + + emit graphPositionQueryChanged(point); + emit d_ptr->needRender(); + } +} + +QPoint Q3DScene::graphPositionQuery() const +{ + return d_ptr->m_graphPositionQueryPosition; +} + /*! * \property Q3DScene::slicingActive * @@ -453,7 +509,9 @@ Q3DScenePrivate::Q3DScenePrivate(Q3DScene *q) : m_light(), m_isUnderSideCameraEnabled(false), m_isSlicingActive(false), - m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()) + m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()), + m_graphPositionQueryPosition(Q3DScene::invalidSelectionPoint()), + m_sceneDirty(true) { } diff --git a/src/datavisualization/engine/q3dscene.h b/src/datavisualization/engine/q3dscene.h index 1699b125..a46b4d7b 100644 --- a/src/datavisualization/engine/q3dscene.h +++ b/src/datavisualization/engine/q3dscene.h @@ -41,6 +41,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DScene : public QObject Q_PROPERTY(Q3DCamera* activeCamera READ activeCamera WRITE setActiveCamera NOTIFY activeCameraChanged) Q_PROPERTY(Q3DLight* activeLight READ activeLight WRITE setActiveLight NOTIFY activeLightChanged) Q_PROPERTY(float devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged) + Q_PROPERTY(QPoint graphPositionQuery READ graphPositionQuery WRITE setGraphPositionQuery NOTIFY graphPositionQueryChanged REVISION 1) public: Q3DScene(QObject *parent = 0); @@ -60,6 +61,9 @@ public: QPoint selectionQueryPosition() const; static QPoint invalidSelectionPoint(); + void setGraphPositionQuery(const QPoint &point); + QPoint graphPositionQuery() const; + void setSlicingActive(bool isSlicing); bool isSlicingActive() const; @@ -85,6 +89,7 @@ signals: void activeLightChanged(Q3DLight *light); void devicePixelRatioChanged(float pixelRatio); void selectionQueryPositionChanged(const QPoint &position); + Q_REVISION(1) void graphPositionQueryChanged(const QPoint &position); private: QScopedPointer d_ptr; diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h index c86a0ec1..b7321c25 100644 --- a/src/datavisualization/engine/q3dscene_p.h +++ b/src/datavisualization/engine/q3dscene_p.h @@ -47,6 +47,7 @@ struct Q3DSceneChangeBitField { bool slicingActivatedChanged : 1; bool devicePixelRatioChanged : 1; bool selectionQueryPositionChanged : 1; + bool graphPositionQueryPositionChanged : 1; bool windowSizeChanged : 1; Q3DSceneChangeBitField() @@ -59,6 +60,7 @@ struct Q3DSceneChangeBitField { slicingActivatedChanged(true), devicePixelRatioChanged(true), selectionQueryPositionChanged(false), + graphPositionQueryPositionChanged(false), windowSizeChanged(true) { } @@ -108,6 +110,7 @@ public: bool m_isUnderSideCameraEnabled; bool m_isSlicingActive; QPoint m_selectionQueryPosition; + QPoint m_graphPositionQueryPosition; QSize m_windowSize; QRect m_glViewport; QRect m_glPrimarySubViewport; diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index 8ced09fe..0249478f 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -785,6 +785,31 @@ QLocale QAbstract3DGraph::locale() const return d_ptr->m_visualController->locale(); } +/*! + * \property QAbstract3DGraph::queriedGraphPosition + * \since QtDataVisualization 1.2 + * + * This read-only property contains the latest graph position values along each axis queried using + * Q3DScene::graphPositionQuery. The values are normalized to range \c{[-1, 1]}. + * If the queried position was outside the graph bounds, the values + * will not reflect the real position, but will instead be some undefined position outside + * the range \c{[-1, 1]}. The value will be undefined before any queries are made. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs only allow querying graph position at the graph floor level, + * so the Y-value is always zero for bar graphs and the valid queries can be only made at + * screen positions that contain the floor of the graph. + * + * \sa Q3DScene::graphPositionQuery + */ +QVector3D QAbstract3DGraph::queriedGraphPosition() const +{ + return d_ptr->m_visualController->queriedGraphPosition(); +} + /*! * \internal */ @@ -948,6 +973,8 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll &QAbstract3DGraph::reflectivityChanged); QObject::connect(m_visualController, &Abstract3DController::localeChanged, q_ptr, &QAbstract3DGraph::localeChanged); + QObject::connect(m_visualController, &Abstract3DController::queriedGraphPositionChanged, q_ptr, + &QAbstract3DGraph::queriedGraphPositionChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index 2d76f780..4a6de4f2 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -57,6 +57,7 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged) Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged) Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged) + Q_PROPERTY(QVector3D queriedGraphPosition READ queriedGraphPosition NOTIFY queriedGraphPositionChanged) protected: explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -175,6 +176,8 @@ public: void setLocale(const QLocale &locale); QLocale locale() const; + QVector3D queriedGraphPosition() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -204,6 +207,7 @@ signals: void reflectionChanged(bool enabled); void reflectivityChanged(qreal reflectivity); void localeChanged(const QLocale &locale); + void queriedGraphPositionChanged(const QVector3D &data); private: Q_DISABLE_COPY(QAbstract3DGraph) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index e9e395be..d1dca8c6 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -117,9 +117,6 @@ void Scatter3DRenderer::initializeOpenGL() loadGridLineMesh(); #endif - // Load label mesh - loadLabelMesh(); - // Set view port glViewport(m_primarySubViewport.x(), m_primarySubViewport.y(), @@ -651,6 +648,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) #endif ShaderHelper *selectionShader = m_selectionShader; + // Do position mapping when necessary + if (m_graphPositionQueryPending) { + QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ); + queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle); + emit needRender(); + } + // Skip selection mode drawing if we have no selection mode if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone && SelectOnScene == m_selectionState diff --git a/src/datavisualization/engine/shaders/colorandposition.vert b/src/datavisualization/engine/shaders/colorandposition.vert deleted file mode 100644 index 34849eae..00000000 --- a/src/datavisualization/engine/shaders/colorandposition.vert +++ /dev/null @@ -1,10 +0,0 @@ -uniform highp mat4 MVP; - -attribute highp vec3 vertexPosition_mdl; - -varying highp vec3 pos; - -void main() { - gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); - pos = vertexPosition_mdl; -} diff --git a/src/datavisualization/engine/shaders/position.vert b/src/datavisualization/engine/shaders/position.vert new file mode 100644 index 00000000..34849eae --- /dev/null +++ b/src/datavisualization/engine/shaders/position.vert @@ -0,0 +1,10 @@ +uniform highp mat4 MVP; + +attribute highp vec3 vertexPosition_mdl; + +varying highp vec3 pos; + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + pos = vertexPosition_mdl; +} diff --git a/src/datavisualization/engine/shaders/positionmap.frag b/src/datavisualization/engine/shaders/positionmap.frag new file mode 100644 index 00000000..9a277ab8 --- /dev/null +++ b/src/datavisualization/engine/shaders/positionmap.frag @@ -0,0 +1,11 @@ +varying highp vec3 pos; + +void main() { + // This shader encodes the axis position into the vertex color, assuming the object + // is a cube filling the entire data area of the graph + gl_FragColor = vec4((pos.x + 1.0) / 2.0, + (pos.y + 1.0) / 2.0, + (-pos.z + 1.0) / 2.0, + 0.0); +} + diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 9121e37c..3d39a71d 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -131,9 +131,6 @@ void Surface3DRenderer::initializeOpenGL() loadGridLineMesh(); #endif - // Load label mesh - loadLabelMesh(); - // Resize in case we've missed resize events // Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here handleResize(); @@ -1297,6 +1294,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } #endif + // Do position mapping when necessary + if (m_graphPositionQueryPending) { + QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ); + queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle); + emit needRender(); + } + // Draw selection buffer if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty()) @@ -2830,8 +2834,11 @@ void Surface3DRenderer::updateSlicingActive(bool isSlicing) m_cachedIsSlicingActivated = isSlicing; - if (!m_cachedIsSlicingActivated) - initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize + if (!m_cachedIsSlicingActivated) { + // We need to re-init selection buffer in case there has been a resize + initSelectionBuffer(); + initCursorPositionBuffer(); + } #if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well diff --git a/src/datavisualization/input/q3dinputhandler.cpp b/src/datavisualization/input/q3dinputhandler.cpp index e6f3de24..0bed3cb9 100644 --- a/src/datavisualization/input/q3dinputhandler.cpp +++ b/src/datavisualization/input/q3dinputhandler.cpp @@ -18,13 +18,16 @@ #include "datavisualizationglobal_p.h" #include "q3dinputhandler_p.h" +#include "abstract3dcontroller_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION -const int minZoomLevel = 10; -const int halfSizeZoomLevel = 50; -const int oneToOneZoomLevel = 100; -const int maxZoomLevel = 500; +const int minZoomLevel = 10; +const int halfSizeZoomLevel = 50; +const int oneToOneZoomLevel = 100; +const int maxZoomLevel = 500; +const int driftTowardCenterLevel = 175; +const float wheelZoomDrift = 0.1f; const int nearZoomRangeDivider = 12; const int midZoomRangeDivider = 60; @@ -84,6 +87,7 @@ const float rotationSpeed = 100.0f; * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows graph rotation. + * Defaults to \c{true}. */ /*! @@ -91,6 +95,7 @@ const float rotationSpeed = 100.0f; * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows graph zooming. + * Defaults to \c{true}. */ /*! @@ -98,6 +103,16 @@ const float rotationSpeed = 100.0f; * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows selection from the graph. + * Defaults to \c{true}. + */ + +/*! + * \qmlproperty bool InputHandler3D::zoomAtTargetEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if zooming changes the camera target to the position of the input + * at the time of the zoom. + * Defaults to \c{true}. */ /*! @@ -236,7 +251,16 @@ void Q3DInputHandler::wheelEvent(QWheelEvent *event) else if (zoomLevel < minZoomLevel) zoomLevel = minZoomLevel; - scene()->activeCamera()->setZoomLevel(zoomLevel); + if (isZoomAtTargetEnabled()) { + scene()->setGraphPositionQuery(event->pos()); + d_ptr->m_zoomAtTargetPending = true; + // If zoom at target is enabled, we don't want to zoom yet, as that causes + // jitter. Instead, we zoom next frame, when we apply the camera position. + d_ptr->m_requestedZoomLevel = zoomLevel; + d_ptr->m_driftMultiplier = wheelZoomDrift; + } else { + scene()->activeCamera()->setZoomLevel(zoomLevel); + } } } @@ -245,6 +269,7 @@ void Q3DInputHandler::wheelEvent(QWheelEvent *event) * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows graph rotation. + * Defaults to \c{true}. */ void Q3DInputHandler::setRotationEnabled(bool enable) { @@ -264,6 +289,7 @@ bool Q3DInputHandler::isRotationEnabled() const * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows graph zooming. + * Defaults to \c{true}. */ void Q3DInputHandler::setZoomEnabled(bool enable) { @@ -283,6 +309,7 @@ bool Q3DInputHandler::isZoomEnabled() const * \since QtDataVisualization 1.2 * * This property specifies if this input handler allows selection from the graph. + * Defaults to \c{true}. */ void Q3DInputHandler::setSelectionEnabled(bool enable) { @@ -297,17 +324,100 @@ bool Q3DInputHandler::isSelectionEnabled() const return d_ptr->m_selectionEnabled; } +/*! + * \property Q3DInputHandler::zoomAtTargetEnabled + * \since QtDataVisualization 1.2 + * + * This property specifies if zooming should change the camera target so that the zoomed point + * of the graph stays at the same location after the zoom. + * Defaults to \c{true}. + */ +void Q3DInputHandler::setZoomAtTargetEnabled(bool enable) +{ + if (d_ptr->m_zoomAtTargetEnabled != enable) { + d_ptr->m_zoomAtTargetEnabled = enable; + emit zoomAtTargetEnabledChanged(enable); + } +} + +bool Q3DInputHandler::isZoomAtTargetEnabled() const +{ + return d_ptr->m_zoomAtTargetEnabled; +} + Q3DInputHandlerPrivate::Q3DInputHandlerPrivate(Q3DInputHandler *q) : q_ptr(q), m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone), m_rotationEnabled(true), m_zoomEnabled(true), - m_selectionEnabled(true) + m_selectionEnabled(true), + m_zoomAtTargetEnabled(true), + m_zoomAtTargetPending(false), + m_controller(0), + m_requestedZoomLevel(0.0f), + m_driftMultiplier(0.0f) { + QObject::connect(q, &QAbstract3DInputHandler::sceneChanged, + this, &Q3DInputHandlerPrivate::handleSceneChange); } Q3DInputHandlerPrivate::~Q3DInputHandlerPrivate() { } +void Q3DInputHandlerPrivate::handleSceneChange(Q3DScene *scene) +{ + if (scene) { + if (m_controller) { + QObject::disconnect(m_controller, &Abstract3DController::queriedGraphPositionChanged, + this, &Q3DInputHandlerPrivate::handleQueriedGraphPositionChange); + } + + m_controller = qobject_cast(scene->parent()); + + if (m_controller) { + QObject::connect(m_controller, &Abstract3DController::queriedGraphPositionChanged, + this, &Q3DInputHandlerPrivate::handleQueriedGraphPositionChange); + } + } +} + +void Q3DInputHandlerPrivate::handleQueriedGraphPositionChange() +{ + if (m_zoomAtTargetPending) { + // Check if the zoom point is on graph + QVector3D newTarget = m_controller->queriedGraphPosition(); + float currentZoom = m_requestedZoomLevel; + float previousZoom = q_ptr->scene()->activeCamera()->zoomLevel(); + q_ptr->scene()->activeCamera()->setZoomLevel(currentZoom); + float diffAdj = 0.0f; + + // If zooming in/out outside the graph, or zooming out after certain point, + // move towards the center. + if ((qAbs(newTarget.x()) > 1.0f + || qAbs(newTarget.y()) > 1.0f + || qAbs(newTarget.z()) > 1.0f) + || (previousZoom > currentZoom && currentZoom <= driftTowardCenterLevel)) { + newTarget = zeroVector; + // Add some extra correction so that we actually reach the center eventually + diffAdj = m_driftMultiplier; + if (previousZoom > currentZoom) + diffAdj *= 2.0f; // Correct towards center little more when zooming out + } + + float zoomFraction = 1.0f - (previousZoom / currentZoom); + + // Adjust camera towards the zoom point, attempting to keep the cursor at same graph point + QVector3D oldTarget = q_ptr->scene()->activeCamera()->target(); + QVector3D origDiff = newTarget - oldTarget; + QVector3D diff = origDiff * zoomFraction + (origDiff.normalized() * diffAdj); + if (diff.length() > origDiff.length()) + diff = origDiff; + q_ptr->scene()->activeCamera()->setTarget(oldTarget + diff); + + if (q_ptr->scene()->selectionQueryPosition() == Q3DScene::invalidSelectionPoint()) + m_zoomAtTargetPending = false; + } +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/input/q3dinputhandler.h b/src/datavisualization/input/q3dinputhandler.h index 9afeb945..5423b4ea 100644 --- a/src/datavisualization/input/q3dinputhandler.h +++ b/src/datavisualization/input/q3dinputhandler.h @@ -31,6 +31,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DInputHandler : public QAbstract3DInputHandl Q_PROPERTY(bool rotationEnabled READ isRotationEnabled WRITE setRotationEnabled NOTIFY rotationEnabledChanged) Q_PROPERTY(bool zoomEnabled READ isZoomEnabled WRITE setZoomEnabled NOTIFY zoomEnabledChanged) Q_PROPERTY(bool selectionEnabled READ isSelectionEnabled WRITE setSelectionEnabled NOTIFY selectionEnabledChanged) + Q_PROPERTY(bool zoomAtTargetEnabled READ isZoomAtTargetEnabled WRITE setZoomAtTargetEnabled NOTIFY zoomAtTargetEnabledChanged) public: explicit Q3DInputHandler(QObject *parent = 0); @@ -42,6 +43,8 @@ public: bool isZoomEnabled() const; void setSelectionEnabled(bool enable); bool isSelectionEnabled() const; + void setZoomAtTargetEnabled(bool enable); + bool isZoomAtTargetEnabled() const; // Input event listeners virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos); @@ -53,6 +56,7 @@ signals: void rotationEnabledChanged(bool enable); void zoomEnabledChanged(bool enable); void selectionEnabledChanged(bool enable); + void zoomAtTargetEnabledChanged(bool enable); private: Q_DISABLE_COPY(Q3DInputHandler) diff --git a/src/datavisualization/input/q3dinputhandler_p.h b/src/datavisualization/input/q3dinputhandler_p.h index a3ed8c9c..79b1c8dd 100644 --- a/src/datavisualization/input/q3dinputhandler_p.h +++ b/src/datavisualization/input/q3dinputhandler_p.h @@ -34,19 +34,34 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -class Q3DInputHandlerPrivate +class Abstract3DController; + +class Q3DInputHandlerPrivate : public QObject { + Q_OBJECT public: Q3DInputHandlerPrivate(Q3DInputHandler *q); ~Q3DInputHandlerPrivate(); -protected: +public slots: + void handleSceneChange(Q3DScene *scene); + void handleQueriedGraphPositionChange(); + +private: Q3DInputHandler *q_ptr; +protected: QAbstract3DInputHandlerPrivate::InputState m_inputState; bool m_rotationEnabled; bool m_zoomEnabled; bool m_selectionEnabled; + bool m_zoomAtTargetEnabled; + bool m_zoomAtTargetPending; + + Abstract3DController *m_controller; // Not owned + + float m_requestedZoomLevel; + float m_driftMultiplier; friend class Q3DInputHandler; }; diff --git a/src/datavisualization/input/qtouch3dinputhandler.cpp b/src/datavisualization/input/qtouch3dinputhandler.cpp index 5d62922b..b811548b 100644 --- a/src/datavisualization/input/qtouch3dinputhandler.cpp +++ b/src/datavisualization/input/qtouch3dinputhandler.cpp @@ -33,6 +33,7 @@ const int tapAndHoldTime = 250; const float rotationSpeed = 200.0f; const int minZoomLevel = 10; const int maxZoomLevel = 500; +const float touchZoomDrift = 0.02f; /*! * \class QTouch3DInputHandler @@ -113,7 +114,8 @@ void QTouch3DInputHandler::touchEvent(QTouchEvent *event) if (!scene()->isSlicingActive() && points.count() == 2) { d_ptr->m_holdTimer->stop(); QPointF distance = points.at(0).pos() - points.at(1).pos(); - d_ptr->handlePinchZoom(distance.manhattanLength()); + QPoint midPoint = ((points.at(0).pos() + points.at(1).pos()) / 2.0).toPoint(); + d_ptr->handlePinchZoom(distance.manhattanLength(), midPoint); } else if (points.count() == 1) { QPointF pointerPos = points.at(0).pos(); if (event->type() == QEvent::TouchBegin) { @@ -165,7 +167,8 @@ void QTouch3DInputHandler::touchEvent(QTouchEvent *event) } QTouch3DInputHandlerPrivate::QTouch3DInputHandlerPrivate(QTouch3DInputHandler *q) - : q_ptr(q), + : Q3DInputHandlerPrivate(q), + q_ptr(q), m_holdTimer(0), m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone) { @@ -181,7 +184,7 @@ QTouch3DInputHandlerPrivate::~QTouch3DInputHandlerPrivate() delete m_holdTimer; } -void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance) +void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance, const QPoint &pos) { if (q_ptr->isZoomEnabled()) { int newDistance = distance; @@ -200,7 +203,18 @@ void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance) zoomLevel = maxZoomLevel; else if (zoomLevel < minZoomLevel) zoomLevel = minZoomLevel; - camera->setZoomLevel(zoomLevel); + + if (q_ptr->isZoomAtTargetEnabled()) { + q_ptr->scene()->setGraphPositionQuery(pos); + m_zoomAtTargetPending = true; + // If zoom at target is enabled, we don't want to zoom yet, as that causes + // jitter. Instead, we zoom next frame, when we apply the camera position. + m_requestedZoomLevel = zoomLevel; + m_driftMultiplier = touchZoomDrift; + } else { + camera->setZoomLevel(zoomLevel); + } + q_ptr->setPrevDistance(newDistance); } } diff --git a/src/datavisualization/input/qtouch3dinputhandler_p.h b/src/datavisualization/input/qtouch3dinputhandler_p.h index 613b5f28..b01904ca 100644 --- a/src/datavisualization/input/qtouch3dinputhandler_p.h +++ b/src/datavisualization/input/qtouch3dinputhandler_p.h @@ -19,7 +19,7 @@ #ifndef QTOUCH3DINPUTHANDLER_P_H #define QTOUCH3DINPUTHANDLER_P_H -#include "qabstract3dinputhandler_p.h" +#include "q3dinputhandler_p.h" #include "qtouch3dinputhandler.h" class QTimer; @@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION class QAbstract3DInputHandler; -class QTouch3DInputHandlerPrivate : public QObject +class QTouch3DInputHandlerPrivate : public Q3DInputHandlerPrivate { Q_OBJECT @@ -36,13 +36,14 @@ public: QTouch3DInputHandlerPrivate(QTouch3DInputHandler *q); ~QTouch3DInputHandlerPrivate(); - void handlePinchZoom(float distance); + void handlePinchZoom(float distance, const QPoint &pos); void handleTapAndHold(); void handleSelection(const QPointF &position); void handleRotation(const QPointF &position); -public: +private: QTouch3DInputHandler *q_ptr; +public: QTimer *m_holdTimer; QAbstract3DInputHandlerPrivate::InputState m_inputState; QPointF m_startHoldPos; diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 5e21b5cd..9dbf0613 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -218,6 +218,31 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf return textureid; } +GLuint TextureHelper::createCursorPositionTexture(const QSize &size, GLuint &frameBuffer) +{ + GLuint textureid; + glGenTextures(1, &textureid); + glBindTexture(GL_TEXTURE_2D, textureid); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenFramebuffers(1, &frameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + textureid, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + qCritical() << "Cursor position mapper frame buffer creation failed:" << status; + glDeleteTextures(1, &textureid); + textureid = 0; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + return textureid; +} + GLuint TextureHelper::createUniformTexture(const QColor &color) { QImage image(QSize(int(uniformTextureWidth), int(uniformTextureHeight)), diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h index c1dfe8c9..03080b2a 100644 --- a/src/datavisualization/utils/texturehelper_p.h +++ b/src/datavisualization/utils/texturehelper_p.h @@ -55,6 +55,7 @@ class TextureHelper : protected QOpenGLFunctions GLuint createCubeMapTexture(const QImage &image, bool useTrilinearFiltering = false); // Returns selection texture and inserts generated framebuffers to framebuffer parameters GLuint createSelectionTexture(const QSize &size, GLuint &frameBuffer, GLuint &depthBuffer); + GLuint createCursorPositionTexture(const QSize &size, GLuint &frameBuffer); GLuint createUniformTexture(const QColor &color); GLuint createGradientTexture(const QLinearGradient &gradient); #if !defined(QT_OPENGL_ES_2) @@ -75,6 +76,7 @@ class TextureHelper : protected QOpenGLFunctions friend class Bars3DRenderer; friend class Surface3DRenderer; friend class Scatter3DRenderer; + friend class Abstract3DRenderer; }; QT_END_NAMESPACE_DATAVISUALIZATION -- cgit v1.2.3