| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Data Visualization module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qabstract3dgraph.h" |
| #include "qabstract3dgraph_p.h" |
| #include "abstract3dcontroller_p.h" |
| #include "qabstract3dinputhandler_p.h" |
| #include "q3dscene_p.h" |
| #include "qutils.h" |
| #include "utils_p.h" |
| |
| #include <QtGui/QGuiApplication> |
| #include <QtGui/QOpenGLContext> |
| #include <QtGui/QOpenGLPaintDevice> |
| #include <QtGui/QPainter> |
| #include <QtGui/QOpenGLFramebufferObject> |
| #include <QtGui/QOffscreenSurface> |
| #if defined(Q_OS_OSX) |
| #include <qpa/qplatformnativeinterface.h> |
| #endif |
| |
| QT_BEGIN_NAMESPACE_DATAVISUALIZATION |
| |
| /*! |
| * \class QAbstract3DGraph |
| * \inmodule QtDataVisualization |
| * \brief The QAbstract3DGraph class provides a window and render loop for graphs. |
| * \since QtDataVisualization 1.0 |
| * |
| * This class subclasses a QWindow and provides render loop for graphs inheriting it. |
| * |
| * You should not need to use this class directly, but one of its subclasses instead. |
| * |
| * Anti-aliasing is turned on by default on C++, except in OpenGL ES2 |
| * environments, where anti-aliasing is not supported by Qt Data Visualization. |
| * To specify non-default anti-aliasing for a graph, give a custom surface format as |
| * a constructor parameter. You can use the convenience function \c QtDataVisualization::qDefaultSurfaceFormat() |
| * to create the surface format object. |
| * |
| * \note QAbstract3DGraph 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} |
| */ |
| |
| /*! |
| \enum QAbstract3DGraph::SelectionFlag |
| |
| Item selection modes. Values of this enumeration can be combined with OR operator. |
| |
| \value SelectionNone |
| Selection mode disabled. |
| \value SelectionItem |
| Selection highlights a single item. |
| \value SelectionRow |
| Selection highlights a single row. |
| \value SelectionItemAndRow |
| Combination flag for highlighting both item and row with different colors. |
| \value SelectionColumn |
| Selection highlights a single column. |
| \value SelectionItemAndColumn |
| Combination flag for highlighting both item and column with different colors. |
| \value SelectionRowAndColumn |
| Combination flag for highlighting both row and column. |
| \value SelectionItemRowAndColumn |
| Combination flag for highlighting item, row, and column. |
| \value SelectionSlice |
| Setting this mode flag indicates that the graph should take care of the slice view handling |
| automatically. If you wish to control the slice view yourself via Q3DScene, do not set this |
| flag. When setting this mode flag, either \c SelectionRow or \c SelectionColumn must also |
| be set, but not both. Slicing is supported by Q3DBars and Q3DSurface only. |
| When this flag is set, slice mode is entered in the following situations: |
| \list |
| \li When selection is changed explicitly via series API to a visible item |
| \li When selection is changed by clicking on the graph |
| \li When the selection mode changes and the selected item is visible |
| \endlist |
| \value SelectionMultiSeries |
| Setting this mode means that items for all series at same position are highlighted, instead |
| of just the selected item. The actual selection in the other series doesn't change. |
| Multi-series selection is not supported for Q3DScatter. |
| */ |
| |
| /*! |
| \enum QAbstract3DGraph::ShadowQuality |
| |
| Quality of shadows. |
| |
| \value ShadowQualityNone |
| Shadows are disabled. |
| \value ShadowQualityLow |
| Shadows are rendered in low quality. |
| \value ShadowQualityMedium |
| Shadows are rendered in medium quality. |
| \value ShadowQualityHigh |
| Shadows are rendered in high quality. |
| \value ShadowQualitySoftLow |
| Shadows are rendered in low quality with softened edges. |
| \value ShadowQualitySoftMedium |
| Shadows are rendered in medium quality with softened edges. |
| \value ShadowQualitySoftHigh |
| Shadows are rendered in high quality with softened edges. |
| */ |
| |
| /*! |
| \enum QAbstract3DGraph::ElementType |
| \since QtDataVisualization 1.1 |
| |
| Type of an element in the graph. |
| |
| \value ElementNone |
| No defined element. |
| \value ElementSeries |
| A series (that is, an item in a series). |
| \value ElementAxisXLabel |
| The x-axis label. |
| \value ElementAxisYLabel |
| The y-axis label. |
| \value ElementAxisZLabel |
| The z-axis label. |
| \value ElementCustomItem |
| A custom item. |
| */ |
| |
| /*! |
| \enum QAbstract3DGraph::OptimizationHint |
| \since Qt Data Visualization 1.1 |
| |
| The optimization hint for rendering. |
| |
| \value OptimizationDefault |
| Provides the full feature set at a reasonable performance. |
| \value OptimizationStatic |
| Optimizes the rendering of static data sets at the expense of some features. |
| */ |
| |
| /*! |
| * \internal |
| */ |
| QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, |
| QWindow *parent) |
| : QWindow(parent), |
| d_ptr(d) |
| { |
| qRegisterMetaType<QAbstract3DGraph::ShadowQuality>("QAbstract3DGraph::ShadowQuality"); |
| qRegisterMetaType<QAbstract3DGraph::ElementType>("QAbstract3DGraph::ElementType"); |
| |
| // Default to frameless window, as typically graphs are not toplevel |
| setFlags(flags() | Qt::FramelessWindowHint); |
| |
| QSurfaceFormat surfaceFormat; |
| if (format) { |
| surfaceFormat = *format; |
| // Make sure renderable type is correct |
| surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType); |
| } else { |
| surfaceFormat = qDefaultSurfaceFormat(); |
| } |
| |
| d_ptr->m_context = new QOpenGLContext(this); |
| setSurfaceType(QWindow::OpenGLSurface); |
| setFormat(surfaceFormat); |
| |
| create(); |
| |
| d_ptr->m_context->setFormat(requestedFormat()); |
| d_ptr->m_context->create(); |
| bool makeSuccess = d_ptr->m_context->makeCurrent(this); |
| |
| // If we fail to get context, just abort |
| if (!makeSuccess || !QOpenGLContext::currentContext()) |
| return; |
| |
| initializeOpenGLFunctions(); |
| |
| const GLubyte *shaderVersion = glGetString(GL_SHADING_LANGUAGE_VERSION); |
| #ifndef QT_NO_DEBUG |
| const GLubyte *openGLVersion = glGetString(GL_VERSION); |
| qDebug() << "OpenGL version:" << (const char *)openGLVersion; |
| qDebug() << "GLSL version:" << (const char *)shaderVersion; |
| #endif |
| |
| if (!Utils::isOpenGLES()) { |
| // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not. |
| QStringList splitversionstr = |
| QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' ')); |
| if (splitversionstr[0].toFloat() < 1.2) |
| qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers."); |
| } |
| |
| d_ptr->m_initialized = true; |
| |
| d_ptr->renderLater(); |
| |
| #if defined(Q_OS_OSX) |
| // Enable touch events for Mac touchpads |
| typedef void * (*EnableTouch)(QWindow*, bool); |
| EnableTouch enableTouch = |
| (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow"); |
| if (enableTouch) |
| enableTouch(this, true); |
| #endif |
| } |
| |
| /*! |
| * Destroys QAbstract3DGraph. |
| */ |
| QAbstract3DGraph::~QAbstract3DGraph() |
| { |
| } |
| |
| /*! |
| * 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 QAbstract3DGraph::addInputHandler(QAbstract3DInputHandler *inputHandler) |
| { |
| d_ptr->m_visualController->addInputHandler(inputHandler); |
| } |
| |
| /*! |
| * 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 QAbstract3DGraph::releaseInputHandler(QAbstract3DInputHandler *inputHandler) |
| { |
| d_ptr->m_visualController->releaseInputHandler(inputHandler); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::activeInputHandler |
| * |
| * \brief The active input handler used in the graph. |
| */ |
| |
| /*! |
| * Sets \a inputHandler as the active input handler used in the graph. |
| * Implicitly calls addInputHandler() to transfer ownership of \a inputHandler |
| * to this graph. |
| * |
| * If \a inputHandler is null, no input handler will be active after this call. |
| * |
| * \sa addInputHandler(), releaseInputHandler() |
| */ |
| void QAbstract3DGraph::setActiveInputHandler(QAbstract3DInputHandler *inputHandler) |
| { |
| d_ptr->m_visualController->setActiveInputHandler(inputHandler); |
| } |
| |
| QAbstract3DInputHandler *QAbstract3DGraph::activeInputHandler() const |
| { |
| return d_ptr->m_visualController->activeInputHandler(); |
| } |
| |
| /*! |
| * Returns the list of all added input handlers. |
| * |
| * \sa addInputHandler() |
| */ |
| QList<QAbstract3DInputHandler *> QAbstract3DGraph::inputHandlers() const |
| { |
| return d_ptr->m_visualController->inputHandlers(); |
| } |
| |
| /*! |
| * Adds the given \a theme to the graph. The themes added via addTheme are not taken in to use |
| * directly. Only the ownership of the theme is given to the graph. |
| * The \a theme must not be null or already added to another graph. |
| * |
| * \sa releaseTheme(), setActiveTheme() |
| */ |
| void QAbstract3DGraph::addTheme(Q3DTheme *theme) |
| { |
| d_ptr->m_visualController->addTheme(theme); |
| } |
| |
| /*! |
| * Releases the ownership of the \a theme back to the caller, if it was added to this graph. |
| * If the released \a theme is in use, a new default theme will be created and set active. |
| * |
| * If the default theme is released and added back later, it behaves as any other theme would. |
| * |
| * \sa addTheme(), setActiveTheme() |
| */ |
| void QAbstract3DGraph::releaseTheme(Q3DTheme *theme) |
| { |
| d_ptr->m_visualController->releaseTheme(theme); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::activeTheme |
| * |
| * \brief The active theme of the graph. |
| */ |
| |
| /*! |
| * Sets \a theme as the active theme to be used for the graph. Implicitly calls |
| * addTheme() to transfer the ownership of the theme to this graph. |
| * |
| * If \a theme is null, a temporary default theme is created. This temporary theme is destroyed |
| * if any theme is explicitly set later. |
| * Properties of the theme can be modified even after setting it, and the modifications take |
| * effect immediately. |
| */ |
| void QAbstract3DGraph::setActiveTheme(Q3DTheme *theme) |
| { |
| d_ptr->m_visualController->setActiveTheme(theme); |
| } |
| |
| |
| Q3DTheme *QAbstract3DGraph::activeTheme() const |
| { |
| return d_ptr->m_visualController->activeTheme(); |
| } |
| |
| /*! |
| * Returns the list of all added themes. |
| * |
| * \sa addTheme() |
| */ |
| QList<Q3DTheme *> QAbstract3DGraph::themes() const |
| { |
| return d_ptr->m_visualController->themes(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::selectionMode |
| * |
| * \brief Item selection mode. |
| * |
| * A combination of SelectionFlags. By default, \c SelectionItem. |
| * Different graph types support different selection modes. |
| * |
| * \sa SelectionFlags |
| */ |
| void QAbstract3DGraph::setSelectionMode(SelectionFlags mode) |
| { |
| d_ptr->m_visualController->setSelectionMode(mode); |
| } |
| |
| QAbstract3DGraph::SelectionFlags QAbstract3DGraph::selectionMode() const |
| { |
| return d_ptr->m_visualController->selectionMode(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::shadowQuality |
| * |
| * \brief The quality of the shadow. |
| * |
| * One of the ShadowQuality enum values. By default, \c ShadowQualityMedium. |
| * |
| * \note If setting the shadow quality to a certain level fails, the level is lowered |
| * until it is successfully set. The \c shadowQualityChanged signal is emitted each time |
| * a change is made. |
| * |
| * \sa ShadowQuality |
| */ |
| void QAbstract3DGraph::setShadowQuality(ShadowQuality quality) |
| { |
| d_ptr->m_visualController->setShadowQuality(quality); |
| } |
| |
| QAbstract3DGraph::ShadowQuality QAbstract3DGraph::shadowQuality() const |
| { |
| return d_ptr->m_visualController->shadowQuality(); |
| } |
| |
| /*! |
| * Returns \c true if shadows are supported with the current configuration. |
| * OpenGL ES2 configurations do not support shadows. |
| */ |
| bool QAbstract3DGraph::shadowsSupported() const |
| { |
| return d_ptr->m_visualController->shadowsSupported(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::scene |
| * |
| * \brief The Q3DScene pointer that can be used to manipulate the scene and |
| * access the scene elements, such as the active camera. |
| * |
| * This property is read-only. |
| */ |
| Q3DScene *QAbstract3DGraph::scene() const |
| { |
| return d_ptr->m_visualController->scene(); |
| } |
| |
| /*! |
| * Clears selection from all attached series. |
| */ |
| void QAbstract3DGraph::clearSelection() |
| { |
| d_ptr->m_visualController->clearSelection(); |
| } |
| |
| /*! |
| * Adds a QCustom3DItem \a item to the graph. Graph takes ownership of the added item. |
| * |
| * Returns the index to the added item if the add operation was successful, -1 |
| * if trying to add a null item, and the index of the item if trying to add an |
| * already added item. |
| * |
| * Items are rendered in the order they have been inserted. The rendering order needs to |
| * be taken into account when having solid and transparent items. |
| * |
| * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt(), customItems() |
| * |
| * \since QtDataVisualization 1.1 |
| */ |
| int QAbstract3DGraph::addCustomItem(QCustom3DItem *item) |
| { |
| return d_ptr->m_visualController->addCustomItem(item); |
| } |
| |
| /*! |
| * Removes all custom items. Deletes the resources allocated to them. |
| * |
| * \since QtDataVisualization 1.1 |
| */ |
| void QAbstract3DGraph::removeCustomItems() |
| { |
| d_ptr->m_visualController->deleteCustomItems(); |
| } |
| |
| /*! |
| * Removes the custom \a {item}. Deletes the resources allocated to it. |
| * |
| * \since QtDataVisualization 1.1 |
| */ |
| void QAbstract3DGraph::removeCustomItem(QCustom3DItem *item) |
| { |
| d_ptr->m_visualController->deleteCustomItem(item); |
| } |
| |
| /*! |
| * Removes all custom items at \a {position}. Deletes the resources allocated to them. |
| * |
| * \since QtDataVisualization 1.1 |
| */ |
| void QAbstract3DGraph::removeCustomItemAt(const QVector3D &position) |
| { |
| d_ptr->m_visualController->deleteCustomItem(position); |
| } |
| |
| /*! |
| * Gets ownership of given \a item back and removes the \a item from the graph. |
| * |
| * \since QtDataVisualization 1.1 |
| * |
| * \note If the same item is added back to the graph, the texture or the texture file needs to be |
| * re-set. |
| * |
| * \sa QCustom3DItem::setTextureImage(), QCustom3DItem::setTextureFile() |
| */ |
| void QAbstract3DGraph::releaseCustomItem(QCustom3DItem *item) |
| { |
| return d_ptr->m_visualController->releaseCustomItem(item); |
| } |
| |
| /*! |
| * Returns the list of all added custom items. |
| * \since QtDataVisualization 1.2 |
| * \sa addCustomItem() |
| */ |
| QList<QCustom3DItem *> QAbstract3DGraph::customItems() const |
| { |
| return d_ptr->m_visualController->customItems(); |
| } |
| |
| /*! |
| * Can be used to query the index of the selected label after receiving \c selectedElementChanged |
| * signal with any label type. Selection is valid until the next \c selectedElementChanged signal. |
| * |
| * Returns the index of the selected label, or -1. |
| * |
| * \since QtDataVisualization 1.1 |
| * |
| * \sa selectedElement |
| */ |
| int QAbstract3DGraph::selectedLabelIndex() const |
| { |
| return d_ptr->m_visualController->selectedLabelIndex(); |
| } |
| |
| /*! |
| * Can be used to get the selected axis after receiving \c selectedElementChanged signal with any label |
| * type. Selection is valid until the next \c selectedElementChanged signal. |
| * |
| * Returns the pointer to the selected axis, or null. |
| * |
| * \since QtDataVisualization 1.1 |
| * |
| * \sa selectedElement |
| */ |
| QAbstract3DAxis *QAbstract3DGraph::selectedAxis() const |
| { |
| return d_ptr->m_visualController->selectedAxis(); |
| } |
| |
| /*! |
| * Can be used to query the index of the selected custom item after receiving \c selectedElementChanged |
| * signal with QAbstract3DGraph::ElementCustomItem type. Selection is valid until the next |
| * \c selectedElementChanged signal. |
| * |
| * Returns the index of the selected custom item, or -1. |
| * |
| * \since QtDataVisualization 1.1 |
| * |
| * \sa selectedElement |
| */ |
| int QAbstract3DGraph::selectedCustomItemIndex() const |
| { |
| return d_ptr->m_visualController->selectedCustomItemIndex(); |
| } |
| |
| /*! |
| * Can be used to get the selected custom item after receiving \c selectedElementChanged signal with |
| * QAbstract3DGraph::ElementCustomItem type. Ownership of the item remains with the graph. |
| * Selection is valid until the next \c selectedElementChanged signal. |
| * |
| * Returns the pointer to the selected custom item, or null. |
| * |
| * \since QtDataVisualization 1.1 |
| * |
| * \sa selectedElement |
| */ |
| QCustom3DItem *QAbstract3DGraph::selectedCustomItem() const |
| { |
| return d_ptr->m_visualController->selectedCustomItem(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::selectedElement |
| * |
| * \brief The element selected in the graph. |
| * |
| * This property can be used to query the selected element type. The type is |
| * valid until a new selection is made in the graph and the |
| * \c selectedElementChanged signal is emitted. |
| * |
| * The signal can be used for example for implementing custom input handlers, as |
| * demonstrated by the \l {Axis Range Dragging With Labels Example}. |
| * |
| * \sa selectedLabelIndex(), selectedAxis(), selectedCustomItemIndex(), selectedCustomItem(), |
| * Q3DBars::selectedSeries(), Q3DScatter::selectedSeries(), Q3DSurface::selectedSeries(), |
| * Q3DScene::setSelectionQueryPosition() |
| * |
| * \since QtDataVisualization 1.1 |
| */ |
| QAbstract3DGraph::ElementType QAbstract3DGraph::selectedElement() const |
| { |
| return d_ptr->m_visualController->selectedElement(); |
| } |
| |
| /*! |
| * Renders current frame to an image of \a imageSize. Default size is the window size. Image is |
| * rendered with antialiasing level given in \a msaaSamples. Default level is \c{0}. |
| * |
| * \since QtDataVisualization 1.1 |
| * |
| * Returns the rendered image. |
| * |
| * \note OpenGL ES2 does not support anitialiasing, so \a msaaSamples is always forced to \c{0}. |
| */ |
| QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize) |
| { |
| QSize renderSize = imageSize; |
| if (renderSize.isEmpty()) |
| renderSize = size(); |
| return d_ptr->renderToImage(msaaSamples, renderSize); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::measureFps |
| * \since QtDataVisualization 1.1 |
| * |
| * \brief Whether rendering is done continuously instead of on demand. |
| * |
| * If \c {true}, rendering is continuous and the value of the currentFps |
| * property is updated. Defaults to \c{false}. |
| * |
| * \sa currentFps |
| */ |
| void QAbstract3DGraph::setMeasureFps(bool enable) |
| { |
| d_ptr->m_visualController->setMeasureFps(enable); |
| } |
| |
| bool QAbstract3DGraph::measureFps() const |
| { |
| return d_ptr->m_visualController->measureFps(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::currentFps |
| * \since QtDataVisualization 1.1 |
| * |
| * \brief The rendering results for the last second. |
| * |
| * The results are stored in this read-only property when FPS measuring is |
| * enabled. It takes at least a second before this value is updated after |
| * measuring is activated. |
| * |
| * \sa measureFps |
| */ |
| qreal QAbstract3DGraph::currentFps() const |
| { |
| return d_ptr->m_visualController->currentFps(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::orthoProjection |
| * \since QtDataVisualization 1.1 |
| * |
| * \brief Whether orthographic projection is used for displaying the graph. |
| * |
| * If \c {true}, ortographic projection is used to create 2D graphs by replacing |
| * the default input handler with one that does not allow rotating the graph and |
| * by setting the camera to view the graph |
| * directly from the side or from the top. Also, axis labels typically need to be rotated when |
| * viewing the graph from the sides. |
| * Defaults to \c{false}. |
| * \note Shadows will be disabled when set to \c{true}. |
| * |
| * \sa QAbstract3DAxis::labelAutoRotation, Q3DCamera::cameraPreset |
| */ |
| void QAbstract3DGraph::setOrthoProjection(bool enable) |
| { |
| d_ptr->m_visualController->setOrthoProjection(enable); |
| } |
| |
| bool QAbstract3DGraph::isOrthoProjection() const |
| { |
| return d_ptr->m_visualController->isOrthoProjection(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::aspectRatio |
| * \since QtDataVisualization 1.1 |
| * |
| * \brief The ratio of the graph scaling between the longest axis on the |
| * horizontal plane and the y-axis. |
| * |
| * Defaults to \c{2.0}. |
| * |
| * \note Has no effect on Q3DBars. |
| * |
| * \sa horizontalAspectRatio |
| */ |
| void QAbstract3DGraph::setAspectRatio(qreal ratio) |
| { |
| d_ptr->m_visualController->setAspectRatio(ratio); |
| } |
| |
| qreal QAbstract3DGraph::aspectRatio() const |
| { |
| return d_ptr->m_visualController->aspectRatio(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::optimizationHints |
| * |
| * \brief Whether the default or static mode is used for rendering optimization. |
| * |
| * The default mode provides the full feature set at a reasonable level of |
| * performance. The static mode optimizes graph rendering and is ideal for |
| * large non-changing data sets. It is slower with dynamic data changes and item rotations. |
| * Selection is not optimized, so using the static mode with massive data sets is not advisable. |
| * Static optimization works only on scatter graphs. |
| * Defaults to \l{OptimizationDefault}. |
| * |
| * \note On some environments, large graphs using static optimization may not render, because |
| * all of the items are rendered using a single draw call, and different graphics drivers |
| * support different maximum vertice counts per call. |
| * This is mostly an issue on 32bit and OpenGL ES2 platforms. |
| * To work around this issue, choose an item mesh with a low vertex count or use |
| * the point mesh. |
| * |
| * \sa QAbstract3DSeries::mesh |
| */ |
| void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints) |
| { |
| d_ptr->m_visualController->setOptimizationHints(hints); |
| } |
| |
| QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const |
| { |
| return d_ptr->m_visualController->optimizationHints(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::polar |
| * \since QtDataVisualization 1.2 |
| * |
| * \brief Whether horizontal axes are changed into polar axes. |
| * |
| * If \c {true}, the x-axis becomes the angular axis and the z-axis becomes the |
| * radial axis. |
| * Polar mode is not available for bar graphs. |
| * |
| * Defaults to \c{false}. |
| * |
| * \sa orthoProjection, radialLabelOffset |
| */ |
| void QAbstract3DGraph::setPolar(bool enable) |
| { |
| d_ptr->m_visualController->setPolar(enable); |
| } |
| |
| bool QAbstract3DGraph::isPolar() const |
| { |
| return d_ptr->m_visualController->isPolar(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::radialLabelOffset |
| * \since QtDataVisualization 1.2 |
| * |
| * \brief The normalized horizontal offset for the axis labels of the radial |
| * polar axis. |
| * |
| * The value \c 0.0 indicates that the labels should be drawn next to the 0-angle |
| * angular axis grid line. The value \c 1.0 indicates that the labels are drawn |
| * in their usual place at the edge of the graph background. Defaults to \c 1.0. |
| * |
| * This property is ignored if the \l polar property value is \c{false}. |
| * |
| * \sa polar |
| */ |
| void QAbstract3DGraph::setRadialLabelOffset(float offset) |
| { |
| d_ptr->m_visualController->setRadialLabelOffset(offset); |
| } |
| |
| float QAbstract3DGraph::radialLabelOffset() const |
| { |
| return d_ptr->m_visualController->radialLabelOffset(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::horizontalAspectRatio |
| * \since QtDataVisualization 1.2 |
| * |
| * \brief The ratio of the graph scaling between the x-axis and z-axis. |
| * |
| * The value of \c 0.0 indicates automatic scaling according to axis ranges. |
| * Defaults to \c{0.0}. |
| * |
| * Has no effect on Q3DBars, which handles scaling on the horizontal plane via |
| * the \l{Q3DBars::barThickness}{barThickness} and \l{Q3DBars::barSpacing}{barSpacing} properties. |
| * Polar graphs also ignore this property. |
| * |
| * \sa aspectRatio, polar, Q3DBars::barThickness, Q3DBars::barSpacing |
| */ |
| void QAbstract3DGraph::setHorizontalAspectRatio(qreal ratio) |
| { |
| d_ptr->m_visualController->setHorizontalAspectRatio(ratio); |
| } |
| |
| qreal QAbstract3DGraph::horizontalAspectRatio() const |
| { |
| return d_ptr->m_visualController->horizontalAspectRatio(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::reflection |
| * \since QtDataVisualization 1.2 |
| * |
| * \brief Whether floor reflections are on or off. |
| * |
| * Defaults to \c{false}. |
| * |
| * Affects only Q3DBars. However, in Q3DBars graphs holding both positive and |
| * negative values, reflections are not supported for custom items that |
| * intersect the floor plane. In that case, reflections should be turned off |
| * to avoid incorrect rendering. |
| * |
| * If using a custom surface format, the stencil buffer needs to be defined |
| * (QSurfaceFormat::setStencilBufferSize()) for reflections to work. |
| * |
| * \sa reflectivity |
| */ |
| void QAbstract3DGraph::setReflection(bool enable) |
| { |
| d_ptr->m_visualController->setReflection(enable); |
| } |
| |
| bool QAbstract3DGraph::isReflection() const |
| { |
| return d_ptr->m_visualController->reflection(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::reflectivity |
| * \since QtDataVisualization 1.2 |
| * |
| * \brief Floor reflectivity. |
| * |
| * Larger numbers make the floor more reflective. The valid range is \c{[0...1]}. |
| * Defaults to \c{0.5}. |
| * |
| * \note Affects only Q3DBars. |
| * |
| * \sa reflection |
| */ |
| void QAbstract3DGraph::setReflectivity(qreal reflectivity) |
| { |
| d_ptr->m_visualController->setReflectivity(reflectivity); |
| } |
| |
| qreal QAbstract3DGraph::reflectivity() const |
| { |
| return d_ptr->m_visualController->reflectivity(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::locale |
| * \since QtDataVisualization 1.2 |
| * |
| * \brief The locale used for formatting various numeric labels. |
| * |
| * Defaults to the \c{"C"} locale. |
| * |
| * \sa QValue3DAxis::labelFormat |
| */ |
| void QAbstract3DGraph::setLocale(const QLocale &locale) |
| { |
| d_ptr->m_visualController->setLocale(locale); |
| } |
| |
| QLocale QAbstract3DGraph::locale() const |
| { |
| return d_ptr->m_visualController->locale(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::queriedGraphPosition |
| * \since QtDataVisualization 1.2 |
| * |
| * \brief The latest queried graph position values along each axis. |
| * |
| * This read-only property contains the results from |
| * Q3DScene::graphPositionQuery. The values are normalized to the range \c{[-1, 1]}. |
| * If the queried position was outside the graph bounds, the values |
| * will not reflect the real position, but will instead indicate an undefined position outside |
| * the range \c{[-1, 1]}. The value will be undefined until a query is made. |
| * |
| * There is no single correct 3D coordinate to match a particular 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(); |
| } |
| |
| /*! |
| * \property QAbstract3DGraph::margin |
| * \since QtDataVisualization 1.2 |
| * |
| * \brief The absolute value used for the space left between the edge of the |
| * plottable graph area and the edge of the graph background. |
| * |
| * If the margin value is negative, the margins are determined automatically and can vary according |
| * to the size of the items in the series and the type of the graph. |
| * The value is interpreted as a fraction of the y-axis range if the graph |
| * aspect ratios have not beed changed from the default values. |
| * Defaults to \c{-1.0}. |
| * |
| * \note Setting a smaller margin for a scatter graph than the automatically |
| * determined margin can cause the scatter items at the edges of the graph to |
| * overlap with the graph background. |
| * |
| * \note On scatter and surface graphs, if the margin is small in comparison to the axis label |
| * size, the positions of the edge labels of the axes are adjusted to avoid overlap with |
| * the edge labels of the neighboring axes. |
| */ |
| void QAbstract3DGraph::setMargin(qreal margin) |
| { |
| d_ptr->m_visualController->setMargin(margin); |
| } |
| |
| qreal QAbstract3DGraph::margin() const |
| { |
| return d_ptr->m_visualController->margin(); |
| } |
| |
| /*! |
| * Returns \c{true} if the OpenGL context of the graph has been successfully initialized. |
| * Trying to use a graph when the context initialization has failed typically results in a crash. |
| * A common reason for a context initialization failure is lack of sufficient platform support |
| * for OpenGL. |
| */ |
| bool QAbstract3DGraph::hasContext() const |
| { |
| if (d_ptr->m_initialized) |
| return true; |
| else |
| return false; |
| } |
| |
| /*! |
| * \internal |
| */ |
| bool QAbstract3DGraph::event(QEvent *event) |
| { |
| switch (event->type()) { |
| case QEvent::UpdateRequest: |
| d_ptr->renderNow(); |
| return true; |
| case QEvent::TouchBegin: |
| case QEvent::TouchCancel: |
| case QEvent::TouchUpdate: |
| case QEvent::TouchEnd: |
| d_ptr->m_visualController->touchEvent(static_cast<QTouchEvent *>(event)); |
| return true; |
| default: |
| return QWindow::event(event); |
| } |
| } |
| |
| /*! |
| * \internal |
| */ |
| void QAbstract3DGraph::resizeEvent(QResizeEvent *event) |
| { |
| Q_UNUSED(event); |
| |
| if (d_ptr->m_visualController) { |
| 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 QAbstract3DGraph::exposeEvent(QExposeEvent *event) |
| { |
| Q_UNUSED(event); |
| |
| if (isExposed()) |
| d_ptr->renderNow(); |
| } |
| |
| /*! |
| * \internal |
| */ |
| void QAbstract3DGraph::mouseDoubleClickEvent(QMouseEvent *event) |
| { |
| d_ptr->m_visualController->mouseDoubleClickEvent(event); |
| } |
| |
| /*! |
| * \internal |
| */ |
| void QAbstract3DGraph::touchEvent(QTouchEvent *event) |
| { |
| d_ptr->m_visualController->touchEvent(event); |
| } |
| |
| /*! |
| * \internal |
| */ |
| void QAbstract3DGraph::mousePressEvent(QMouseEvent *event) |
| { |
| d_ptr->m_visualController->mousePressEvent(event, event->pos()); |
| } |
| |
| /*! |
| * \internal |
| */ |
| void QAbstract3DGraph::mouseReleaseEvent(QMouseEvent *event) |
| { |
| d_ptr->m_visualController->mouseReleaseEvent(event, event->pos()); |
| } |
| |
| /*! |
| * \internal |
| */ |
| void QAbstract3DGraph::mouseMoveEvent(QMouseEvent *event) |
| { |
| d_ptr->m_visualController->mouseMoveEvent(event, event->pos()); |
| } |
| |
| #if QT_CONFIG(wheelevent) |
| /*! |
| * \internal |
| */ |
| void QAbstract3DGraph::wheelEvent(QWheelEvent *event) |
| { |
| d_ptr->m_visualController->wheelEvent(event); |
| } |
| #endif |
| |
| QAbstract3DGraphPrivate::QAbstract3DGraphPrivate(QAbstract3DGraph *q) |
| : QObject(0), |
| q_ptr(q), |
| m_updatePending(false), |
| m_visualController(0), |
| m_devicePixelRatio(1.f), |
| m_offscreenSurface(0), |
| m_initialized(false) |
| { |
| } |
| |
| QAbstract3DGraphPrivate::~QAbstract3DGraphPrivate() |
| { |
| if (m_offscreenSurface) { |
| m_offscreenSurface->destroy(); |
| delete m_offscreenSurface; |
| } |
| if (m_context) |
| m_context->makeCurrent(q_ptr); |
| |
| delete m_visualController; |
| } |
| |
| void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controller) |
| { |
| m_visualController = controller; |
| |
| QObject::connect(m_visualController, &Abstract3DController::activeInputHandlerChanged, q_ptr, |
| &QAbstract3DGraph::activeInputHandlerChanged); |
| QObject::connect(m_visualController, &Abstract3DController::activeThemeChanged, q_ptr, |
| &QAbstract3DGraph::activeThemeChanged); |
| QObject::connect(m_visualController, &Abstract3DController::selectionModeChanged, q_ptr, |
| &QAbstract3DGraph::selectionModeChanged); |
| QObject::connect(m_visualController, &Abstract3DController::shadowQualityChanged, q_ptr, |
| &QAbstract3DGraph::shadowQualityChanged); |
| QObject::connect(m_visualController, &Abstract3DController::optimizationHintsChanged, q_ptr, |
| &QAbstract3DGraph::optimizationHintsChanged); |
| QObject::connect(m_visualController, &Abstract3DController::elementSelected, q_ptr, |
| &QAbstract3DGraph::selectedElementChanged); |
| |
| QObject::connect(m_visualController, &Abstract3DController::needRender, this, |
| &QAbstract3DGraphPrivate::renderLater); |
| |
| QObject::connect(m_visualController, &Abstract3DController::axisXChanged, this, |
| &QAbstract3DGraphPrivate::handleAxisXChanged); |
| QObject::connect(m_visualController, &Abstract3DController::axisYChanged, this, |
| &QAbstract3DGraphPrivate::handleAxisYChanged); |
| QObject::connect(m_visualController, &Abstract3DController::axisZChanged, this, |
| &QAbstract3DGraphPrivate::handleAxisZChanged); |
| |
| QObject::connect(m_visualController, &Abstract3DController::measureFpsChanged, q_ptr, |
| &QAbstract3DGraph::measureFpsChanged); |
| QObject::connect(m_visualController, &Abstract3DController::currentFpsChanged, q_ptr, |
| &QAbstract3DGraph::currentFpsChanged); |
| |
| QObject::connect(m_visualController, &Abstract3DController::orthoProjectionChanged, q_ptr, |
| &QAbstract3DGraph::orthoProjectionChanged); |
| |
| QObject::connect(m_visualController, &Abstract3DController::aspectRatioChanged, q_ptr, |
| &QAbstract3DGraph::aspectRatioChanged); |
| QObject::connect(m_visualController, &Abstract3DController::polarChanged, q_ptr, |
| &QAbstract3DGraph::polarChanged); |
| QObject::connect(m_visualController, &Abstract3DController::radialLabelOffsetChanged, q_ptr, |
| &QAbstract3DGraph::radialLabelOffsetChanged); |
| QObject::connect(m_visualController, &Abstract3DController::horizontalAspectRatioChanged, q_ptr, |
| &QAbstract3DGraph::horizontalAspectRatioChanged); |
| |
| QObject::connect(m_visualController, &Abstract3DController::reflectionChanged, q_ptr, |
| &QAbstract3DGraph::reflectionChanged); |
| QObject::connect(m_visualController, &Abstract3DController::reflectivityChanged, q_ptr, |
| &QAbstract3DGraph::reflectivityChanged); |
| QObject::connect(m_visualController, &Abstract3DController::localeChanged, q_ptr, |
| &QAbstract3DGraph::localeChanged); |
| QObject::connect(m_visualController, &Abstract3DController::queriedGraphPositionChanged, q_ptr, |
| &QAbstract3DGraph::queriedGraphPositionChanged); |
| QObject::connect(m_visualController, &Abstract3DController::marginChanged, q_ptr, |
| &QAbstract3DGraph::marginChanged); |
| } |
| |
| void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() |
| { |
| if (q_ptr->devicePixelRatio() == m_devicePixelRatio || !m_visualController) |
| return; |
| |
| m_devicePixelRatio = q_ptr->devicePixelRatio(); |
| m_visualController->scene()->setDevicePixelRatio(m_devicePixelRatio); |
| } |
| |
| void QAbstract3DGraphPrivate::render() |
| { |
| handleDevicePixelRatioChange(); |
| m_visualController->synchDataToRenderer(); |
| m_visualController->render(); |
| } |
| |
| void QAbstract3DGraphPrivate::renderLater() |
| { |
| if (!m_updatePending) { |
| m_updatePending = true; |
| QCoreApplication::postEvent(q_ptr, new QEvent(QEvent::UpdateRequest)); |
| } |
| } |
| |
| void QAbstract3DGraphPrivate::renderNow() |
| { |
| if (!q_ptr->isExposed()) |
| return; |
| |
| m_updatePending = false; |
| |
| m_context->makeCurrent(q_ptr); |
| |
| render(); |
| |
| m_context->swapBuffers(q_ptr); |
| } |
| |
| QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imageSize) |
| { |
| QImage image; |
| QOpenGLFramebufferObject *fbo; |
| QOpenGLFramebufferObjectFormat fboFormat; |
| if (!m_offscreenSurface) { |
| // Create an offscreen surface for rendering to images without rendering on screen |
| m_offscreenSurface = new QOffscreenSurface(q_ptr->screen()); |
| m_offscreenSurface->setFormat(q_ptr->requestedFormat()); |
| m_offscreenSurface->create(); |
| } |
| // Render the wanted frame offscreen |
| m_context->makeCurrent(m_offscreenSurface); |
| fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); |
| if (!Utils::isOpenGLES()) { |
| fboFormat.setInternalTextureFormat(GL_RGB); |
| fboFormat.setSamples(msaaSamples); |
| } |
| fbo = new QOpenGLFramebufferObject(imageSize, fboFormat); |
| if (fbo->isValid()) { |
| QRect originalViewport = m_visualController->m_scene->viewport(); |
| m_visualController->m_scene->d_ptr->setWindowSize(imageSize); |
| m_visualController->m_scene->d_ptr->setViewport(QRect(0, 0, |
| imageSize.width(), |
| imageSize.height())); |
| m_visualController->synchDataToRenderer(); |
| fbo->bind(); |
| m_visualController->requestRender(fbo); |
| image = fbo->toImage(); |
| fbo->release(); |
| m_visualController->m_scene->d_ptr->setWindowSize(originalViewport.size()); |
| m_visualController->m_scene->d_ptr->setViewport(originalViewport); |
| } |
| delete fbo; |
| m_context->makeCurrent(q_ptr); |
| |
| return image; |
| } |
| |
| QT_END_NAMESPACE_DATAVISUALIZATION |