1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Data Visualization module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "qabstract3dgraph.h"
31 #include "qabstract3dgraph_p.h"
32 #include "abstract3dcontroller_p.h"
33 #include "qabstract3dinputhandler_p.h"
34 #include "q3dscene_p.h"
35 #include "qutils.h"
36 #include "utils_p.h"
37 
38 #include <QtGui/QGuiApplication>
39 #include <QtGui/QOpenGLContext>
40 #include <QtGui/QOpenGLPaintDevice>
41 #include <QtGui/QPainter>
42 #include <QtGui/QOpenGLFramebufferObject>
43 #include <QtGui/QOffscreenSurface>
44 #if defined(Q_OS_OSX)
45 #include <qpa/qplatformnativeinterface.h>
46 #endif
47 
48 QT_BEGIN_NAMESPACE_DATAVISUALIZATION
49 
50 /*!
51  * \class QAbstract3DGraph
52  * \inmodule QtDataVisualization
53  * \brief The QAbstract3DGraph class provides a window and render loop for graphs.
54  * \since QtDataVisualization 1.0
55  *
56  * This class subclasses a QWindow and provides render loop for graphs inheriting it.
57  *
58  * You should not need to use this class directly, but one of its subclasses instead.
59  *
60  * Anti-aliasing is turned on by default on C++, except in OpenGL ES2
61  * environments, where anti-aliasing is not supported by Qt Data Visualization.
62  * To specify non-default anti-aliasing for a graph, give a custom surface format as
63  * a constructor parameter. You can use the convenience function \c QtDataVisualization::qDefaultSurfaceFormat()
64  * to create the surface format object.
65  *
66  * \note QAbstract3DGraph sets window flag \c Qt::FramelessWindowHint on by default. If you want to display
67  * graph windows as standalone windows with regular window frame, clear this flag after constructing
68  * the graph. For example:
69  *
70  * \code
71  *  Q3DBars *graphWindow = new Q3DBars;
72  *  graphWindow->setFlags(graphWindow->flags() ^ Qt::FramelessWindowHint);
73  * \endcode
74  *
75  * \sa Q3DBars, Q3DScatter, Q3DSurface, {Qt Data Visualization C++ Classes}
76  */
77 
78 /*!
79     \enum QAbstract3DGraph::SelectionFlag
80 
81     Item selection modes. Values of this enumeration can be combined with OR operator.
82 
83     \value SelectionNone
84            Selection mode disabled.
85     \value SelectionItem
86            Selection highlights a single item.
87     \value SelectionRow
88            Selection highlights a single row.
89     \value SelectionItemAndRow
90            Combination flag for highlighting both item and row with different colors.
91     \value SelectionColumn
92            Selection highlights a single column.
93     \value SelectionItemAndColumn
94            Combination flag for highlighting both item and column with different colors.
95     \value SelectionRowAndColumn
96            Combination flag for highlighting both row and column.
97     \value SelectionItemRowAndColumn
98            Combination flag for highlighting item, row, and column.
99     \value SelectionSlice
100            Setting this mode flag indicates that the graph should take care of the slice view handling
101            automatically. If you wish to control the slice view yourself via Q3DScene, do not set this
102            flag. When setting this mode flag, either \c SelectionRow or \c SelectionColumn must also
103            be set, but not both. Slicing is supported by Q3DBars and Q3DSurface only.
104            When this flag is set, slice mode is entered in the following situations:
105            \list
106            \li When selection is changed explicitly via series API to a visible item
107            \li When selection is changed by clicking on the graph
108            \li When the selection mode changes and the selected item is visible
109            \endlist
110     \value SelectionMultiSeries
111            Setting this mode means that items for all series at same position are highlighted, instead
112            of just the selected item. The actual selection in the other series doesn't change.
113            Multi-series selection is not supported for Q3DScatter.
114 */
115 
116 /*!
117     \enum QAbstract3DGraph::ShadowQuality
118 
119     Quality of shadows.
120 
121     \value ShadowQualityNone
122            Shadows are disabled.
123     \value ShadowQualityLow
124            Shadows are rendered in low quality.
125     \value ShadowQualityMedium
126            Shadows are rendered in medium quality.
127     \value ShadowQualityHigh
128            Shadows are rendered in high quality.
129     \value ShadowQualitySoftLow
130            Shadows are rendered in low quality with softened edges.
131     \value ShadowQualitySoftMedium
132            Shadows are rendered in medium quality with softened edges.
133     \value ShadowQualitySoftHigh
134            Shadows are rendered in high quality with softened edges.
135 */
136 
137 /*!
138     \enum QAbstract3DGraph::ElementType
139     \since QtDataVisualization 1.1
140 
141     Type of an element in the graph.
142 
143     \value ElementNone
144            No defined element.
145     \value ElementSeries
146            A series (that is, an item in a series).
147     \value ElementAxisXLabel
148            The x-axis label.
149     \value ElementAxisYLabel
150            The y-axis label.
151     \value ElementAxisZLabel
152            The z-axis label.
153     \value ElementCustomItem
154            A custom item.
155 */
156 
157 /*!
158     \enum QAbstract3DGraph::OptimizationHint
159     \since Qt Data Visualization 1.1
160 
161     The optimization hint for rendering.
162 
163     \value OptimizationDefault
164            Provides the full feature set at a reasonable performance.
165     \value OptimizationStatic
166            Optimizes the rendering of static data sets at the expense of some features.
167 */
168 
169 /*!
170  * \internal
171  */
QAbstract3DGraph(QAbstract3DGraphPrivate * d,const QSurfaceFormat * format,QWindow * parent)172 QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format,
173                                    QWindow *parent)
174     : QWindow(parent),
175       d_ptr(d)
176 {
177     qRegisterMetaType<QAbstract3DGraph::ShadowQuality>("QAbstract3DGraph::ShadowQuality");
178     qRegisterMetaType<QAbstract3DGraph::ElementType>("QAbstract3DGraph::ElementType");
179 
180     // Default to frameless window, as typically graphs are not toplevel
181     setFlags(flags() | Qt::FramelessWindowHint);
182 
183     QSurfaceFormat surfaceFormat;
184     if (format) {
185         surfaceFormat = *format;
186         // Make sure renderable type is correct
187         surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType);
188     } else {
189         surfaceFormat = qDefaultSurfaceFormat();
190     }
191 
192     d_ptr->m_context = new QOpenGLContext(this);
193     setSurfaceType(QWindow::OpenGLSurface);
194     setFormat(surfaceFormat);
195 
196     create();
197 
198     d_ptr->m_context->setFormat(requestedFormat());
199     d_ptr->m_context->create();
200     bool makeSuccess = d_ptr->m_context->makeCurrent(this);
201 
202     // If we fail to get context, just abort
203     if (!makeSuccess || !QOpenGLContext::currentContext())
204         return;
205 
206     initializeOpenGLFunctions();
207 
208     const GLubyte *shaderVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);
209 #ifndef QT_NO_DEBUG
210     const GLubyte *openGLVersion = glGetString(GL_VERSION);
211     qDebug() << "OpenGL version:" << (const char *)openGLVersion;
212     qDebug() << "GLSL version:" << (const char *)shaderVersion;
213 #endif
214 
215     if (!Utils::isOpenGLES()) {
216         // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not.
217         QStringList splitversionstr =
218                 QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' '));
219         if (splitversionstr[0].toFloat() < 1.2)
220             qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers.");
221     }
222 
223     d_ptr->m_initialized = true;
224 
225     d_ptr->renderLater();
226 
227 #if defined(Q_OS_OSX)
228     // Enable touch events for Mac touchpads
229     typedef void * (*EnableTouch)(QWindow*, bool);
230     EnableTouch enableTouch =
231             (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow");
232     if (enableTouch)
233         enableTouch(this, true);
234 #endif
235 }
236 
237 /*!
238  * Destroys QAbstract3DGraph.
239  */
~QAbstract3DGraph()240 QAbstract3DGraph::~QAbstract3DGraph()
241 {
242 }
243 
244 /*!
245  * Adds the given \a inputHandler to the graph. The input handlers added via addInputHandler
246  * are not taken in to use directly. Only the ownership of the \a inputHandler is given to the graph.
247  * The \a inputHandler must not be null or already added to another graph.
248  *
249  * \sa releaseInputHandler(), setActiveInputHandler()
250  */
addInputHandler(QAbstract3DInputHandler * inputHandler)251 void QAbstract3DGraph::addInputHandler(QAbstract3DInputHandler *inputHandler)
252 {
253     d_ptr->m_visualController->addInputHandler(inputHandler);
254 }
255 
256 /*!
257  * Releases the ownership of the \a inputHandler back to the caller, if it was added to this graph.
258  * If the released \a inputHandler is in use there will be no input handler active after this call.
259  *
260  * If the default input handler is released and added back later, it behaves as any other input handler would.
261  *
262  * \sa addInputHandler(), setActiveInputHandler()
263  */
releaseInputHandler(QAbstract3DInputHandler * inputHandler)264 void QAbstract3DGraph::releaseInputHandler(QAbstract3DInputHandler *inputHandler)
265 {
266     d_ptr->m_visualController->releaseInputHandler(inputHandler);
267 }
268 
269 /*!
270  * \property QAbstract3DGraph::activeInputHandler
271  *
272  * \brief The active input handler used in the graph.
273  */
274 
275 /*!
276  * Sets \a inputHandler as the active input handler used in the graph.
277  * Implicitly calls addInputHandler() to transfer ownership of \a inputHandler
278  * to this graph.
279  *
280  * If \a inputHandler is null, no input handler will be active after this call.
281  *
282  * \sa addInputHandler(), releaseInputHandler()
283  */
setActiveInputHandler(QAbstract3DInputHandler * inputHandler)284 void QAbstract3DGraph::setActiveInputHandler(QAbstract3DInputHandler *inputHandler)
285 {
286     d_ptr->m_visualController->setActiveInputHandler(inputHandler);
287 }
288 
activeInputHandler() const289 QAbstract3DInputHandler *QAbstract3DGraph::activeInputHandler() const
290 {
291     return d_ptr->m_visualController->activeInputHandler();
292 }
293 
294 /*!
295  * Returns the list of all added input handlers.
296  *
297  * \sa addInputHandler()
298  */
inputHandlers() const299 QList<QAbstract3DInputHandler *> QAbstract3DGraph::inputHandlers() const
300 {
301     return d_ptr->m_visualController->inputHandlers();
302 }
303 
304 /*!
305  * Adds the given \a theme to the graph. The themes added via addTheme are not taken in to use
306  * directly. Only the ownership of the theme is given to the graph.
307  * The \a theme must not be null or already added to another graph.
308  *
309  * \sa releaseTheme(), setActiveTheme()
310  */
addTheme(Q3DTheme * theme)311 void QAbstract3DGraph::addTheme(Q3DTheme *theme)
312 {
313     d_ptr->m_visualController->addTheme(theme);
314 }
315 
316 /*!
317  * Releases the ownership of the \a theme back to the caller, if it was added to this graph.
318  * If the released \a theme is in use, a new default theme will be created and set active.
319  *
320  * If the default theme is released and added back later, it behaves as any other theme would.
321  *
322  * \sa addTheme(), setActiveTheme()
323  */
releaseTheme(Q3DTheme * theme)324 void QAbstract3DGraph::releaseTheme(Q3DTheme *theme)
325 {
326     d_ptr->m_visualController->releaseTheme(theme);
327 }
328 
329 /*!
330  * \property QAbstract3DGraph::activeTheme
331  *
332  * \brief The active theme of the graph.
333  */
334 
335 /*!
336  * Sets \a theme as the active theme to be used for the graph. Implicitly calls
337  * addTheme() to transfer the ownership of the theme to this graph.
338  *
339  * If \a theme is null, a temporary default theme is created. This temporary theme is destroyed
340  * if any theme is explicitly set later.
341  * Properties of the theme can be modified even after setting it, and the modifications take
342  * effect immediately.
343  */
setActiveTheme(Q3DTheme * theme)344 void QAbstract3DGraph::setActiveTheme(Q3DTheme *theme)
345 {
346     d_ptr->m_visualController->setActiveTheme(theme);
347 }
348 
349 
activeTheme() const350 Q3DTheme *QAbstract3DGraph::activeTheme() const
351 {
352     return d_ptr->m_visualController->activeTheme();
353 }
354 
355 /*!
356  * Returns the list of all added themes.
357  *
358  * \sa addTheme()
359  */
themes() const360 QList<Q3DTheme *> QAbstract3DGraph::themes() const
361 {
362     return d_ptr->m_visualController->themes();
363 }
364 
365 /*!
366  * \property QAbstract3DGraph::selectionMode
367  *
368  * \brief Item selection mode.
369  *
370  * A combination of SelectionFlags. By default, \c SelectionItem.
371  * Different graph types support different selection modes.
372  *
373  * \sa SelectionFlags
374  */
setSelectionMode(SelectionFlags mode)375 void QAbstract3DGraph::setSelectionMode(SelectionFlags mode)
376 {
377     d_ptr->m_visualController->setSelectionMode(mode);
378 }
379 
selectionMode() const380 QAbstract3DGraph::SelectionFlags QAbstract3DGraph::selectionMode() const
381 {
382     return d_ptr->m_visualController->selectionMode();
383 }
384 
385 /*!
386  * \property QAbstract3DGraph::shadowQuality
387  *
388  * \brief The quality of the shadow.
389  *
390  * One of the ShadowQuality enum values. By default, \c ShadowQualityMedium.
391  *
392  * \note If setting the shadow quality to a certain level fails, the level is lowered
393  * until it is successfully set. The \c shadowQualityChanged signal is emitted each time
394  * a change is made.
395  *
396  * \sa ShadowQuality
397  */
setShadowQuality(ShadowQuality quality)398 void QAbstract3DGraph::setShadowQuality(ShadowQuality quality)
399 {
400     d_ptr->m_visualController->setShadowQuality(quality);
401 }
402 
shadowQuality() const403 QAbstract3DGraph::ShadowQuality QAbstract3DGraph::shadowQuality() const
404 {
405     return d_ptr->m_visualController->shadowQuality();
406 }
407 
408 /*!
409  * Returns \c true if shadows are supported with the current configuration.
410  * OpenGL ES2 configurations do not support shadows.
411  */
shadowsSupported() const412 bool QAbstract3DGraph::shadowsSupported() const
413 {
414     return d_ptr->m_visualController->shadowsSupported();
415 }
416 
417 /*!
418  * \property QAbstract3DGraph::scene
419  *
420  * \brief The Q3DScene pointer that can be used to manipulate the scene and
421  * access the scene elements, such as the active camera.
422  *
423  * This property is read-only.
424  */
scene() const425 Q3DScene *QAbstract3DGraph::scene() const
426 {
427     return d_ptr->m_visualController->scene();
428 }
429 
430 /*!
431  * Clears selection from all attached series.
432  */
clearSelection()433 void QAbstract3DGraph::clearSelection()
434 {
435     d_ptr->m_visualController->clearSelection();
436 }
437 
438 /*!
439  * Adds a QCustom3DItem \a item to the graph. Graph takes ownership of the added item.
440  *
441  * Returns the index to the added item if the add operation was successful, -1
442  * if trying to add a null item, and the index of the item if trying to add an
443  * already added item.
444  *
445  * Items are rendered in the order they have been inserted. The rendering order needs to
446  * be taken into account when having solid and transparent items.
447  *
448  * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt(), customItems()
449  *
450  * \since QtDataVisualization 1.1
451  */
addCustomItem(QCustom3DItem * item)452 int QAbstract3DGraph::addCustomItem(QCustom3DItem *item)
453 {
454     return d_ptr->m_visualController->addCustomItem(item);
455 }
456 
457 /*!
458  * Removes all custom items. Deletes the resources allocated to them.
459  *
460  * \since QtDataVisualization 1.1
461  */
removeCustomItems()462 void QAbstract3DGraph::removeCustomItems()
463 {
464     d_ptr->m_visualController->deleteCustomItems();
465 }
466 
467 /*!
468  * Removes the custom \a {item}. Deletes the resources allocated to it.
469  *
470  * \since QtDataVisualization 1.1
471  */
removeCustomItem(QCustom3DItem * item)472 void QAbstract3DGraph::removeCustomItem(QCustom3DItem *item)
473 {
474     d_ptr->m_visualController->deleteCustomItem(item);
475 }
476 
477 /*!
478  * Removes all custom items at \a {position}. Deletes the resources allocated to them.
479  *
480  * \since QtDataVisualization 1.1
481  */
removeCustomItemAt(const QVector3D & position)482 void QAbstract3DGraph::removeCustomItemAt(const QVector3D &position)
483 {
484     d_ptr->m_visualController->deleteCustomItem(position);
485 }
486 
487 /*!
488  * Gets ownership of given \a item back and removes the \a item from the graph.
489  *
490  * \since QtDataVisualization 1.1
491  *
492  * \note If the same item is added back to the graph, the texture or the texture file needs to be
493  * re-set.
494  *
495  * \sa QCustom3DItem::setTextureImage(), QCustom3DItem::setTextureFile()
496  */
releaseCustomItem(QCustom3DItem * item)497 void QAbstract3DGraph::releaseCustomItem(QCustom3DItem *item)
498 {
499     return d_ptr->m_visualController->releaseCustomItem(item);
500 }
501 
502 /*!
503  * Returns the list of all added custom items.
504  * \since QtDataVisualization 1.2
505  * \sa addCustomItem()
506  */
customItems() const507 QList<QCustom3DItem *> QAbstract3DGraph::customItems() const
508 {
509     return d_ptr->m_visualController->customItems();
510 }
511 
512 /*!
513  * Can be used to query the index of the selected label after receiving \c selectedElementChanged
514  * signal with any label type. Selection is valid until the next \c selectedElementChanged signal.
515  *
516  * Returns the index of the selected label, or -1.
517  *
518  * \since QtDataVisualization 1.1
519  *
520  * \sa selectedElement
521  */
selectedLabelIndex() const522 int QAbstract3DGraph::selectedLabelIndex() const
523 {
524     return d_ptr->m_visualController->selectedLabelIndex();
525 }
526 
527 /*!
528  * Can be used to get the selected axis after receiving \c selectedElementChanged signal with any label
529  * type. Selection is valid until the next \c selectedElementChanged signal.
530  *
531  * Returns the pointer to the selected axis, or null.
532  *
533  * \since QtDataVisualization 1.1
534  *
535  * \sa selectedElement
536  */
selectedAxis() const537 QAbstract3DAxis *QAbstract3DGraph::selectedAxis() const
538 {
539     return d_ptr->m_visualController->selectedAxis();
540 }
541 
542 /*!
543  * Can be used to query the index of the selected custom item after receiving \c selectedElementChanged
544  * signal with QAbstract3DGraph::ElementCustomItem type. Selection is valid until the next
545  * \c selectedElementChanged signal.
546  *
547  * Returns the index of the selected custom item, or -1.
548  *
549  * \since QtDataVisualization 1.1
550  *
551  * \sa selectedElement
552  */
selectedCustomItemIndex() const553 int QAbstract3DGraph::selectedCustomItemIndex() const
554 {
555     return d_ptr->m_visualController->selectedCustomItemIndex();
556 }
557 
558 /*!
559  * Can be used to get the selected custom item after receiving \c selectedElementChanged signal with
560  * QAbstract3DGraph::ElementCustomItem type. Ownership of the item remains with the graph.
561  * Selection is valid until the next \c selectedElementChanged signal.
562  *
563  * Returns the pointer to the selected custom item, or null.
564  *
565  * \since QtDataVisualization 1.1
566  *
567  * \sa selectedElement
568  */
selectedCustomItem() const569 QCustom3DItem *QAbstract3DGraph::selectedCustomItem() const
570 {
571     return d_ptr->m_visualController->selectedCustomItem();
572 }
573 
574 /*!
575  * \property QAbstract3DGraph::selectedElement
576  *
577  * \brief The element selected in the graph.
578  *
579  * This property can be used to query the selected element type. The type is
580  * valid until a new selection is made in the graph and the
581  * \c selectedElementChanged signal is emitted.
582  *
583  * The signal can be used for example for implementing custom input handlers, as
584  * demonstrated by the \l {Axis Range Dragging With Labels Example}.
585  *
586  * \sa selectedLabelIndex(), selectedAxis(), selectedCustomItemIndex(), selectedCustomItem(),
587  * Q3DBars::selectedSeries(), Q3DScatter::selectedSeries(), Q3DSurface::selectedSeries(),
588  * Q3DScene::setSelectionQueryPosition()
589  *
590  * \since QtDataVisualization 1.1
591  */
selectedElement() const592 QAbstract3DGraph::ElementType QAbstract3DGraph::selectedElement() const
593 {
594     return d_ptr->m_visualController->selectedElement();
595 }
596 
597 /*!
598  * Renders current frame to an image of \a imageSize. Default size is the window size. Image is
599  * rendered with antialiasing level given in \a msaaSamples. Default level is \c{0}.
600  *
601  * \since QtDataVisualization 1.1
602  *
603  * Returns the rendered image.
604  *
605  * \note OpenGL ES2 does not support anitialiasing, so \a msaaSamples is always forced to \c{0}.
606  */
renderToImage(int msaaSamples,const QSize & imageSize)607 QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize)
608 {
609     QSize renderSize = imageSize;
610     if (renderSize.isEmpty())
611         renderSize = size();
612     return d_ptr->renderToImage(msaaSamples, renderSize);
613 }
614 
615 /*!
616  * \property QAbstract3DGraph::measureFps
617  * \since QtDataVisualization 1.1
618  *
619  * \brief Whether rendering is done continuously instead of on demand.
620  *
621  * If \c {true}, rendering is continuous and the value of the currentFps
622  * property is updated. Defaults to \c{false}.
623  *
624  * \sa currentFps
625  */
setMeasureFps(bool enable)626 void QAbstract3DGraph::setMeasureFps(bool enable)
627 {
628     d_ptr->m_visualController->setMeasureFps(enable);
629 }
630 
measureFps() const631 bool QAbstract3DGraph::measureFps() const
632 {
633     return d_ptr->m_visualController->measureFps();
634 }
635 
636 /*!
637  * \property QAbstract3DGraph::currentFps
638  * \since QtDataVisualization 1.1
639  *
640  * \brief The rendering results for the last second.
641  *
642  * The results are stored in this read-only property when FPS measuring is
643  * enabled. It takes at least a second before this value is updated after
644  * measuring is activated.
645  *
646  * \sa measureFps
647  */
currentFps() const648 qreal QAbstract3DGraph::currentFps() const
649 {
650     return d_ptr->m_visualController->currentFps();
651 }
652 
653 /*!
654  * \property QAbstract3DGraph::orthoProjection
655  * \since QtDataVisualization 1.1
656  *
657  * \brief Whether orthographic projection is used for displaying the graph.
658  *
659  * If \c {true}, ortographic projection is used to create 2D graphs by replacing
660  * the default input handler with one that does not allow rotating the graph and
661  * by setting the camera to view the graph
662  * directly from the side or from the top. Also, axis labels typically need to be rotated when
663  * viewing the graph from the sides.
664  * Defaults to \c{false}.
665  * \note Shadows will be disabled when set to \c{true}.
666  *
667  * \sa QAbstract3DAxis::labelAutoRotation, Q3DCamera::cameraPreset
668  */
setOrthoProjection(bool enable)669 void QAbstract3DGraph::setOrthoProjection(bool enable)
670 {
671     d_ptr->m_visualController->setOrthoProjection(enable);
672 }
673 
isOrthoProjection() const674 bool QAbstract3DGraph::isOrthoProjection() const
675 {
676     return d_ptr->m_visualController->isOrthoProjection();
677 }
678 
679 /*!
680  * \property QAbstract3DGraph::aspectRatio
681  * \since QtDataVisualization 1.1
682  *
683  * \brief The ratio of the graph scaling between the longest axis on the
684  * horizontal plane and the y-axis.
685  *
686  * Defaults to \c{2.0}.
687  *
688  * \note Has no effect on Q3DBars.
689  *
690  * \sa horizontalAspectRatio
691  */
setAspectRatio(qreal ratio)692 void QAbstract3DGraph::setAspectRatio(qreal ratio)
693 {
694     d_ptr->m_visualController->setAspectRatio(ratio);
695 }
696 
aspectRatio() const697 qreal QAbstract3DGraph::aspectRatio() const
698 {
699     return d_ptr->m_visualController->aspectRatio();
700 }
701 
702 /*!
703  * \property QAbstract3DGraph::optimizationHints
704  *
705  * \brief Whether the default or static mode is used for rendering optimization.
706  *
707  * The default mode provides the full feature set at a reasonable level of
708  * performance. The static mode optimizes graph rendering and is ideal for
709  * large non-changing data sets. It is slower with dynamic data changes and item rotations.
710  * Selection is not optimized, so using the static mode with massive data sets is not advisable.
711  * Static optimization works only on scatter graphs.
712  * Defaults to \l{OptimizationDefault}.
713  *
714  * \note On some environments, large graphs using static optimization may not render, because
715  * all of the items are rendered using a single draw call, and different graphics drivers
716  * support different maximum vertice counts per call.
717  * This is mostly an issue on 32bit and OpenGL ES2 platforms.
718  * To work around this issue, choose an item mesh with a low vertex count or use
719  * the point mesh.
720  *
721  * \sa QAbstract3DSeries::mesh
722  */
setOptimizationHints(OptimizationHints hints)723 void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints)
724 {
725     d_ptr->m_visualController->setOptimizationHints(hints);
726 }
727 
optimizationHints() const728 QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const
729 {
730     return d_ptr->m_visualController->optimizationHints();
731 }
732 
733 /*!
734  * \property QAbstract3DGraph::polar
735  * \since QtDataVisualization 1.2
736  *
737  * \brief Whether horizontal axes are changed into polar axes.
738  *
739  * If \c {true}, the x-axis becomes the angular axis and the z-axis becomes the
740  * radial axis.
741  * Polar mode is not available for bar graphs.
742  *
743  * Defaults to \c{false}.
744  *
745  * \sa orthoProjection, radialLabelOffset
746  */
setPolar(bool enable)747 void QAbstract3DGraph::setPolar(bool enable)
748 {
749     d_ptr->m_visualController->setPolar(enable);
750 }
751 
isPolar() const752 bool QAbstract3DGraph::isPolar() const
753 {
754     return d_ptr->m_visualController->isPolar();
755 }
756 
757 /*!
758  * \property QAbstract3DGraph::radialLabelOffset
759  * \since QtDataVisualization 1.2
760  *
761  * \brief The normalized horizontal offset for the axis labels of the radial
762  * polar axis.
763  *
764  * The value \c 0.0 indicates that the labels should be drawn next to the 0-angle
765  * angular axis grid line. The value \c 1.0 indicates that the labels are drawn
766  * in their usual place at the edge of the graph background. Defaults to \c 1.0.
767  *
768  * This property is ignored if the \l polar property value is \c{false}.
769  *
770  * \sa polar
771  */
setRadialLabelOffset(float offset)772 void QAbstract3DGraph::setRadialLabelOffset(float offset)
773 {
774     d_ptr->m_visualController->setRadialLabelOffset(offset);
775 }
776 
radialLabelOffset() const777 float QAbstract3DGraph::radialLabelOffset() const
778 {
779     return d_ptr->m_visualController->radialLabelOffset();
780 }
781 
782 /*!
783  * \property QAbstract3DGraph::horizontalAspectRatio
784  * \since QtDataVisualization 1.2
785  *
786  * \brief The ratio of the graph scaling between the x-axis and z-axis.
787  *
788  * The value of \c 0.0 indicates automatic scaling according to axis ranges.
789  * Defaults to \c{0.0}.
790  *
791  * Has no effect on Q3DBars, which handles scaling on the horizontal plane via
792  * the \l{Q3DBars::barThickness}{barThickness} and \l{Q3DBars::barSpacing}{barSpacing} properties.
793  * Polar graphs also ignore this property.
794  *
795  * \sa aspectRatio, polar, Q3DBars::barThickness, Q3DBars::barSpacing
796  */
setHorizontalAspectRatio(qreal ratio)797 void QAbstract3DGraph::setHorizontalAspectRatio(qreal ratio)
798 {
799     d_ptr->m_visualController->setHorizontalAspectRatio(ratio);
800 }
801 
horizontalAspectRatio() const802 qreal QAbstract3DGraph::horizontalAspectRatio() const
803 {
804     return d_ptr->m_visualController->horizontalAspectRatio();
805 }
806 
807 /*!
808  * \property QAbstract3DGraph::reflection
809  * \since QtDataVisualization 1.2
810  *
811  * \brief Whether floor reflections are on or off.
812  *
813  * Defaults to \c{false}.
814  *
815  * Affects only Q3DBars. However, in Q3DBars graphs holding both positive and
816  * negative values, reflections are not supported for custom items that
817  * intersect the floor plane. In that case, reflections should be turned off
818  * to avoid incorrect rendering.
819  *
820  * If using a custom surface format, the stencil buffer needs to be defined
821  * (QSurfaceFormat::setStencilBufferSize()) for reflections to work.
822  *
823  * \sa reflectivity
824  */
setReflection(bool enable)825 void QAbstract3DGraph::setReflection(bool enable)
826 {
827     d_ptr->m_visualController->setReflection(enable);
828 }
829 
isReflection() const830 bool QAbstract3DGraph::isReflection() const
831 {
832     return d_ptr->m_visualController->reflection();
833 }
834 
835 /*!
836  * \property QAbstract3DGraph::reflectivity
837  * \since QtDataVisualization 1.2
838  *
839  * \brief Floor reflectivity.
840  *
841  * Larger numbers make the floor more reflective. The valid range is \c{[0...1]}.
842  * Defaults to \c{0.5}.
843  *
844  * \note Affects only Q3DBars.
845  *
846  * \sa reflection
847  */
setReflectivity(qreal reflectivity)848 void QAbstract3DGraph::setReflectivity(qreal reflectivity)
849 {
850     d_ptr->m_visualController->setReflectivity(reflectivity);
851 }
852 
reflectivity() const853 qreal QAbstract3DGraph::reflectivity() const
854 {
855     return d_ptr->m_visualController->reflectivity();
856 }
857 
858 /*!
859  * \property QAbstract3DGraph::locale
860  * \since QtDataVisualization 1.2
861  *
862  * \brief The locale used for formatting various numeric labels.
863  *
864  * Defaults to the \c{"C"} locale.
865  *
866  * \sa QValue3DAxis::labelFormat
867  */
setLocale(const QLocale & locale)868 void QAbstract3DGraph::setLocale(const QLocale &locale)
869 {
870     d_ptr->m_visualController->setLocale(locale);
871 }
872 
locale() const873 QLocale QAbstract3DGraph::locale() const
874 {
875     return d_ptr->m_visualController->locale();
876 }
877 
878 /*!
879  * \property QAbstract3DGraph::queriedGraphPosition
880  * \since QtDataVisualization 1.2
881  *
882  * \brief The latest queried graph position values along each axis.
883  *
884  * This read-only property contains the results from
885  * Q3DScene::graphPositionQuery. The values are normalized to the range \c{[-1, 1]}.
886  * If the queried position was outside the graph bounds, the values
887  * will not reflect the real position, but will instead indicate an undefined position outside
888  * the range \c{[-1, 1]}. The value will be undefined until a query is made.
889  *
890  * There is no single correct 3D coordinate to match a particular screen position, so to be
891  * consistent, the queries are always done against the inner sides of an invisible box surrounding
892  * the graph.
893  *
894  * \note Bar graphs only allow querying graph position at the graph floor level,
895  * so the y-value is always zero for bar graphs and the valid queries can be only made at
896  * screen positions that contain the floor of the graph.
897  *
898  * \sa Q3DScene::graphPositionQuery
899  */
queriedGraphPosition() const900 QVector3D QAbstract3DGraph::queriedGraphPosition() const
901 {
902     return d_ptr->m_visualController->queriedGraphPosition();
903 }
904 
905 /*!
906  * \property QAbstract3DGraph::margin
907  * \since QtDataVisualization 1.2
908  *
909  * \brief The absolute value used for the space left between the edge of the
910  * plottable graph area and the edge of the graph background.
911  *
912  * If the margin value is negative, the margins are determined automatically and can vary according
913  * to the size of the items in the series and the type of the graph.
914  * The value is interpreted as a fraction of the y-axis range if the graph
915  * aspect ratios have not beed changed from the default values.
916  * Defaults to \c{-1.0}.
917  *
918  * \note Setting a smaller margin for a scatter graph than the automatically
919  * determined margin can cause the scatter items at the edges of the graph to
920  * overlap with the graph background.
921  *
922  * \note On scatter and surface graphs, if the margin is small in comparison to the axis label
923  * size, the positions of the edge labels of the axes are adjusted to avoid overlap with
924  * the edge labels of the neighboring axes.
925  */
setMargin(qreal margin)926 void QAbstract3DGraph::setMargin(qreal margin)
927 {
928     d_ptr->m_visualController->setMargin(margin);
929 }
930 
margin() const931 qreal QAbstract3DGraph::margin() const
932 {
933     return d_ptr->m_visualController->margin();
934 }
935 
936 /*!
937  * Returns \c{true} if the OpenGL context of the graph has been successfully initialized.
938  * Trying to use a graph when the context initialization has failed typically results in a crash.
939  * A common reason for a context initialization failure is lack of sufficient platform support
940  * for OpenGL.
941  */
hasContext() const942 bool QAbstract3DGraph::hasContext() const
943 {
944     if (d_ptr->m_initialized)
945         return true;
946     else
947         return false;
948 }
949 
950 /*!
951  * \internal
952  */
event(QEvent * event)953 bool QAbstract3DGraph::event(QEvent *event)
954 {
955     switch (event->type()) {
956     case QEvent::UpdateRequest:
957         d_ptr->renderNow();
958         return true;
959     case QEvent::TouchBegin:
960     case QEvent::TouchCancel:
961     case QEvent::TouchUpdate:
962     case QEvent::TouchEnd:
963         d_ptr->m_visualController->touchEvent(static_cast<QTouchEvent *>(event));
964         return true;
965     default:
966         return QWindow::event(event);
967     }
968 }
969 
970 /*!
971  * \internal
972  */
resizeEvent(QResizeEvent * event)973 void QAbstract3DGraph::resizeEvent(QResizeEvent *event)
974 {
975     Q_UNUSED(event);
976 
977     if (d_ptr->m_visualController) {
978         Q3DScene *scene = d_ptr->m_visualController->scene();
979         scene->d_ptr->setWindowSize(QSize(width(), height()));
980         scene->d_ptr->setViewport(QRect(0, 0, width(), height()));
981     }
982 }
983 
984 /*!
985  * \internal
986  */
exposeEvent(QExposeEvent * event)987 void QAbstract3DGraph::exposeEvent(QExposeEvent *event)
988 {
989     Q_UNUSED(event);
990 
991     if (isExposed())
992         d_ptr->renderNow();
993 }
994 
995 /*!
996  * \internal
997  */
mouseDoubleClickEvent(QMouseEvent * event)998 void QAbstract3DGraph::mouseDoubleClickEvent(QMouseEvent *event)
999 {
1000     d_ptr->m_visualController->mouseDoubleClickEvent(event);
1001 }
1002 
1003 /*!
1004  * \internal
1005  */
touchEvent(QTouchEvent * event)1006 void QAbstract3DGraph::touchEvent(QTouchEvent *event)
1007 {
1008     d_ptr->m_visualController->touchEvent(event);
1009 }
1010 
1011 /*!
1012  * \internal
1013  */
mousePressEvent(QMouseEvent * event)1014 void QAbstract3DGraph::mousePressEvent(QMouseEvent *event)
1015 {
1016     d_ptr->m_visualController->mousePressEvent(event, event->pos());
1017 }
1018 
1019 /*!
1020  * \internal
1021  */
mouseReleaseEvent(QMouseEvent * event)1022 void QAbstract3DGraph::mouseReleaseEvent(QMouseEvent *event)
1023 {
1024     d_ptr->m_visualController->mouseReleaseEvent(event, event->pos());
1025 }
1026 
1027 /*!
1028  * \internal
1029  */
mouseMoveEvent(QMouseEvent * event)1030 void QAbstract3DGraph::mouseMoveEvent(QMouseEvent *event)
1031 {
1032     d_ptr->m_visualController->mouseMoveEvent(event, event->pos());
1033 }
1034 
1035 #if QT_CONFIG(wheelevent)
1036 /*!
1037  * \internal
1038  */
wheelEvent(QWheelEvent * event)1039 void QAbstract3DGraph::wheelEvent(QWheelEvent *event)
1040 {
1041     d_ptr->m_visualController->wheelEvent(event);
1042 }
1043 #endif
1044 
QAbstract3DGraphPrivate(QAbstract3DGraph * q)1045 QAbstract3DGraphPrivate::QAbstract3DGraphPrivate(QAbstract3DGraph *q)
1046     : QObject(0),
1047       q_ptr(q),
1048       m_updatePending(false),
1049       m_visualController(0),
1050       m_devicePixelRatio(1.f),
1051       m_offscreenSurface(0),
1052       m_initialized(false)
1053 {
1054 }
1055 
~QAbstract3DGraphPrivate()1056 QAbstract3DGraphPrivate::~QAbstract3DGraphPrivate()
1057 {
1058     if (m_offscreenSurface) {
1059         m_offscreenSurface->destroy();
1060         delete m_offscreenSurface;
1061     }
1062     if (m_context)
1063         m_context->makeCurrent(q_ptr);
1064 
1065     delete m_visualController;
1066 }
1067 
setVisualController(Abstract3DController * controller)1068 void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controller)
1069 {
1070     m_visualController = controller;
1071 
1072     QObject::connect(m_visualController, &Abstract3DController::activeInputHandlerChanged, q_ptr,
1073                      &QAbstract3DGraph::activeInputHandlerChanged);
1074     QObject::connect(m_visualController, &Abstract3DController::activeThemeChanged, q_ptr,
1075                      &QAbstract3DGraph::activeThemeChanged);
1076     QObject::connect(m_visualController, &Abstract3DController::selectionModeChanged, q_ptr,
1077                      &QAbstract3DGraph::selectionModeChanged);
1078     QObject::connect(m_visualController, &Abstract3DController::shadowQualityChanged, q_ptr,
1079                      &QAbstract3DGraph::shadowQualityChanged);
1080     QObject::connect(m_visualController, &Abstract3DController::optimizationHintsChanged, q_ptr,
1081                      &QAbstract3DGraph::optimizationHintsChanged);
1082     QObject::connect(m_visualController, &Abstract3DController::elementSelected, q_ptr,
1083                      &QAbstract3DGraph::selectedElementChanged);
1084 
1085     QObject::connect(m_visualController, &Abstract3DController::needRender, this,
1086                      &QAbstract3DGraphPrivate::renderLater);
1087 
1088     QObject::connect(m_visualController, &Abstract3DController::axisXChanged, this,
1089                      &QAbstract3DGraphPrivate::handleAxisXChanged);
1090     QObject::connect(m_visualController, &Abstract3DController::axisYChanged, this,
1091                      &QAbstract3DGraphPrivate::handleAxisYChanged);
1092     QObject::connect(m_visualController, &Abstract3DController::axisZChanged, this,
1093                      &QAbstract3DGraphPrivate::handleAxisZChanged);
1094 
1095     QObject::connect(m_visualController, &Abstract3DController::measureFpsChanged, q_ptr,
1096                      &QAbstract3DGraph::measureFpsChanged);
1097     QObject::connect(m_visualController, &Abstract3DController::currentFpsChanged, q_ptr,
1098                      &QAbstract3DGraph::currentFpsChanged);
1099 
1100     QObject::connect(m_visualController, &Abstract3DController::orthoProjectionChanged, q_ptr,
1101                      &QAbstract3DGraph::orthoProjectionChanged);
1102 
1103     QObject::connect(m_visualController, &Abstract3DController::aspectRatioChanged, q_ptr,
1104                      &QAbstract3DGraph::aspectRatioChanged);
1105     QObject::connect(m_visualController, &Abstract3DController::polarChanged, q_ptr,
1106                      &QAbstract3DGraph::polarChanged);
1107     QObject::connect(m_visualController, &Abstract3DController::radialLabelOffsetChanged, q_ptr,
1108                      &QAbstract3DGraph::radialLabelOffsetChanged);
1109     QObject::connect(m_visualController, &Abstract3DController::horizontalAspectRatioChanged, q_ptr,
1110                      &QAbstract3DGraph::horizontalAspectRatioChanged);
1111 
1112     QObject::connect(m_visualController, &Abstract3DController::reflectionChanged, q_ptr,
1113                      &QAbstract3DGraph::reflectionChanged);
1114     QObject::connect(m_visualController, &Abstract3DController::reflectivityChanged, q_ptr,
1115                      &QAbstract3DGraph::reflectivityChanged);
1116     QObject::connect(m_visualController, &Abstract3DController::localeChanged, q_ptr,
1117                      &QAbstract3DGraph::localeChanged);
1118     QObject::connect(m_visualController, &Abstract3DController::queriedGraphPositionChanged, q_ptr,
1119                      &QAbstract3DGraph::queriedGraphPositionChanged);
1120     QObject::connect(m_visualController, &Abstract3DController::marginChanged, q_ptr,
1121                      &QAbstract3DGraph::marginChanged);
1122 }
1123 
handleDevicePixelRatioChange()1124 void QAbstract3DGraphPrivate::handleDevicePixelRatioChange()
1125 {
1126     if (q_ptr->devicePixelRatio() == m_devicePixelRatio || !m_visualController)
1127         return;
1128 
1129     m_devicePixelRatio = q_ptr->devicePixelRatio();
1130     m_visualController->scene()->setDevicePixelRatio(m_devicePixelRatio);
1131 }
1132 
render()1133 void QAbstract3DGraphPrivate::render()
1134 {
1135     handleDevicePixelRatioChange();
1136     m_visualController->synchDataToRenderer();
1137     m_visualController->render();
1138 }
1139 
renderLater()1140 void QAbstract3DGraphPrivate::renderLater()
1141 {
1142     if (!m_updatePending) {
1143         m_updatePending = true;
1144         QCoreApplication::postEvent(q_ptr, new QEvent(QEvent::UpdateRequest));
1145     }
1146 }
1147 
renderNow()1148 void QAbstract3DGraphPrivate::renderNow()
1149 {
1150     if (!q_ptr->isExposed())
1151         return;
1152 
1153     m_updatePending = false;
1154 
1155     m_context->makeCurrent(q_ptr);
1156 
1157     render();
1158 
1159     m_context->swapBuffers(q_ptr);
1160 }
1161 
renderToImage(int msaaSamples,const QSize & imageSize)1162 QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imageSize)
1163 {
1164     QImage image;
1165     QOpenGLFramebufferObject *fbo;
1166     QOpenGLFramebufferObjectFormat fboFormat;
1167     if (!m_offscreenSurface) {
1168         // Create an offscreen surface for rendering to images without rendering on screen
1169         m_offscreenSurface = new QOffscreenSurface(q_ptr->screen());
1170         m_offscreenSurface->setFormat(q_ptr->requestedFormat());
1171         m_offscreenSurface->create();
1172     }
1173     // Render the wanted frame offscreen
1174     m_context->makeCurrent(m_offscreenSurface);
1175     fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
1176     if (!Utils::isOpenGLES()) {
1177         fboFormat.setInternalTextureFormat(GL_RGB);
1178         fboFormat.setSamples(msaaSamples);
1179     }
1180     fbo = new QOpenGLFramebufferObject(imageSize, fboFormat);
1181     if (fbo->isValid()) {
1182         QRect originalViewport = m_visualController->m_scene->viewport();
1183         m_visualController->m_scene->d_ptr->setWindowSize(imageSize);
1184         m_visualController->m_scene->d_ptr->setViewport(QRect(0, 0,
1185                                                               imageSize.width(),
1186                                                               imageSize.height()));
1187         m_visualController->synchDataToRenderer();
1188         fbo->bind();
1189         m_visualController->requestRender(fbo);
1190         image = fbo->toImage();
1191         fbo->release();
1192         m_visualController->m_scene->d_ptr->setWindowSize(originalViewport.size());
1193         m_visualController->m_scene->d_ptr->setViewport(originalViewport);
1194     }
1195     delete fbo;
1196     m_context->makeCurrent(q_ptr);
1197 
1198     return image;
1199 }
1200 
1201 QT_END_NAMESPACE_DATAVISUALIZATION
1202