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
48QT_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 */
172QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format,
173 QWindow *parent)
174 : QWindow(parent),
175 d_ptr(d)
176{
177 qRegisterMetaType<QAbstract3DGraph::ShadowQuality>(typeName: "QAbstract3DGraph::ShadowQuality");
178 qRegisterMetaType<QAbstract3DGraph::ElementType>(typeName: "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(surface: 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(str: (const char *)shaderVersion).split(sep: QChar::fromLatin1(c: ' '));
219 if (splitversionstr[0].toFloat() < 1.2)
220 qFatal(msg: "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 */
240QAbstract3DGraph::~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 */
251void 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 */
264void 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 */
284void QAbstract3DGraph::setActiveInputHandler(QAbstract3DInputHandler *inputHandler)
285{
286 d_ptr->m_visualController->setActiveInputHandler(inputHandler);
287}
288
289QAbstract3DInputHandler *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 */
299QList<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 */
311void 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 */
324void 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 */
344void QAbstract3DGraph::setActiveTheme(Q3DTheme *theme)
345{
346 d_ptr->m_visualController->setActiveTheme(theme);
347}
348
349
350Q3DTheme *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 */
360QList<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 */
375void QAbstract3DGraph::setSelectionMode(SelectionFlags mode)
376{
377 d_ptr->m_visualController->setSelectionMode(mode);
378}
379
380QAbstract3DGraph::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 */
398void QAbstract3DGraph::setShadowQuality(ShadowQuality quality)
399{
400 d_ptr->m_visualController->setShadowQuality(quality);
401}
402
403QAbstract3DGraph::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 */
412bool 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 */
425Q3DScene *QAbstract3DGraph::scene() const
426{
427 return d_ptr->m_visualController->scene();
428}
429
430/*!
431 * Clears selection from all attached series.
432 */
433void 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 */
452int 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 */
462void 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 */
472void 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 */
482void 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 */
497void 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 */
507QList<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 */
522int 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 */
537QAbstract3DAxis *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 */
553int 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 */
569QCustom3DItem *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 */
592QAbstract3DGraph::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 */
607QImage 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, imageSize: 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 */
626void QAbstract3DGraph::setMeasureFps(bool enable)
627{
628 d_ptr->m_visualController->setMeasureFps(enable);
629}
630
631bool 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 */
648qreal 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 * Defaults to \c{false}.
660 * \note Shadows will be disabled when set to \c{true}.
661 *
662 * \sa QAbstract3DAxis::labelAutoRotation, Q3DCamera::cameraPreset
663 */
664void QAbstract3DGraph::setOrthoProjection(bool enable)
665{
666 d_ptr->m_visualController->setOrthoProjection(enable);
667}
668
669bool QAbstract3DGraph::isOrthoProjection() const
670{
671 return d_ptr->m_visualController->isOrthoProjection();
672}
673
674/*!
675 * \property QAbstract3DGraph::aspectRatio
676 * \since QtDataVisualization 1.1
677 *
678 * \brief The ratio of the graph scaling between the longest axis on the
679 * horizontal plane and the y-axis.
680 *
681 * Defaults to \c{2.0}.
682 *
683 * \note Has no effect on Q3DBars.
684 *
685 * \sa horizontalAspectRatio
686 */
687void QAbstract3DGraph::setAspectRatio(qreal ratio)
688{
689 d_ptr->m_visualController->setAspectRatio(ratio);
690}
691
692qreal QAbstract3DGraph::aspectRatio() const
693{
694 return d_ptr->m_visualController->aspectRatio();
695}
696
697/*!
698 * \property QAbstract3DGraph::optimizationHints
699 *
700 * \brief Whether the default or static mode is used for rendering optimization.
701 *
702 * The default mode provides the full feature set at a reasonable level of
703 * performance. The static mode optimizes graph rendering and is ideal for
704 * large non-changing data sets. It is slower with dynamic data changes and item rotations.
705 * Selection is not optimized, so using the static mode with massive data sets is not advisable.
706 * Static optimization works only on scatter graphs.
707 * Defaults to \l{OptimizationDefault}.
708 *
709 * \note On some environments, large graphs using static optimization may not render, because
710 * all of the items are rendered using a single draw call, and different graphics drivers
711 * support different maximum vertice counts per call.
712 * This is mostly an issue on 32bit and OpenGL ES2 platforms.
713 * To work around this issue, choose an item mesh with a low vertex count or use
714 * the point mesh.
715 *
716 * \sa QAbstract3DSeries::mesh
717 */
718void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints)
719{
720 d_ptr->m_visualController->setOptimizationHints(hints);
721}
722
723QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const
724{
725 return d_ptr->m_visualController->optimizationHints();
726}
727
728/*!
729 * \property QAbstract3DGraph::polar
730 * \since QtDataVisualization 1.2
731 *
732 * \brief Whether horizontal axes are changed into polar axes.
733 *
734 * If \c {true}, the x-axis becomes the angular axis and the z-axis becomes the
735 * radial axis.
736 * Polar mode is not available for bar graphs.
737 *
738 * Defaults to \c{false}.
739 *
740 * \sa orthoProjection, radialLabelOffset
741 */
742void QAbstract3DGraph::setPolar(bool enable)
743{
744 d_ptr->m_visualController->setPolar(enable);
745}
746
747bool QAbstract3DGraph::isPolar() const
748{
749 return d_ptr->m_visualController->isPolar();
750}
751
752/*!
753 * \property QAbstract3DGraph::radialLabelOffset
754 * \since QtDataVisualization 1.2
755 *
756 * \brief The normalized horizontal offset for the axis labels of the radial
757 * polar axis.
758 *
759 * The value \c 0.0 indicates that the labels should be drawn next to the 0-angle
760 * angular axis grid line. The value \c 1.0 indicates that the labels are drawn
761 * in their usual place at the edge of the graph background. Defaults to \c 1.0.
762 *
763 * This property is ignored if the \l polar property value is \c{false}.
764 *
765 * \sa polar
766 */
767void QAbstract3DGraph::setRadialLabelOffset(float offset)
768{
769 d_ptr->m_visualController->setRadialLabelOffset(offset);
770}
771
772float QAbstract3DGraph::radialLabelOffset() const
773{
774 return d_ptr->m_visualController->radialLabelOffset();
775}
776
777/*!
778 * \property QAbstract3DGraph::horizontalAspectRatio
779 * \since QtDataVisualization 1.2
780 *
781 * \brief The ratio of the graph scaling between the x-axis and z-axis.
782 *
783 * The value of \c 0.0 indicates automatic scaling according to axis ranges.
784 * Defaults to \c{0.0}.
785 *
786 * Has no effect on Q3DBars, which handles scaling on the horizontal plane via
787 * the \l{Q3DBars::barThickness}{barThickness} and \l{Q3DBars::barSpacing}{barSpacing} properties.
788 * Polar graphs also ignore this property.
789 *
790 * \sa aspectRatio, polar, Q3DBars::barThickness, Q3DBars::barSpacing
791 */
792void QAbstract3DGraph::setHorizontalAspectRatio(qreal ratio)
793{
794 d_ptr->m_visualController->setHorizontalAspectRatio(ratio);
795}
796
797qreal QAbstract3DGraph::horizontalAspectRatio() const
798{
799 return d_ptr->m_visualController->horizontalAspectRatio();
800}
801
802/*!
803 * \property QAbstract3DGraph::reflection
804 * \since QtDataVisualization 1.2
805 *
806 * \brief Whether floor reflections are on or off.
807 *
808 * Defaults to \c{false}.
809 *
810 * Affects only Q3DBars. However, in Q3DBars graphs holding both positive and
811 * negative values, reflections are not supported for custom items that
812 * intersect the floor plane. In that case, reflections should be turned off
813 * to avoid incorrect rendering.
814 *
815 * If using a custom surface format, the stencil buffer needs to be defined
816 * (QSurfaceFormat::setStencilBufferSize()) for reflections to work.
817 *
818 * \sa reflectivity
819 */
820void QAbstract3DGraph::setReflection(bool enable)
821{
822 d_ptr->m_visualController->setReflection(enable);
823}
824
825bool QAbstract3DGraph::isReflection() const
826{
827 return d_ptr->m_visualController->reflection();
828}
829
830/*!
831 * \property QAbstract3DGraph::reflectivity
832 * \since QtDataVisualization 1.2
833 *
834 * \brief Floor reflectivity.
835 *
836 * Larger numbers make the floor more reflective. The valid range is \c{[0...1]}.
837 * Defaults to \c{0.5}.
838 *
839 * \note Affects only Q3DBars.
840 *
841 * \sa reflection
842 */
843void QAbstract3DGraph::setReflectivity(qreal reflectivity)
844{
845 d_ptr->m_visualController->setReflectivity(reflectivity);
846}
847
848qreal QAbstract3DGraph::reflectivity() const
849{
850 return d_ptr->m_visualController->reflectivity();
851}
852
853/*!
854 * \property QAbstract3DGraph::locale
855 * \since QtDataVisualization 1.2
856 *
857 * \brief The locale used for formatting various numeric labels.
858 *
859 * Defaults to the \c{"C"} locale.
860 *
861 * \sa QValue3DAxis::labelFormat
862 */
863void QAbstract3DGraph::setLocale(const QLocale &locale)
864{
865 d_ptr->m_visualController->setLocale(locale);
866}
867
868QLocale QAbstract3DGraph::locale() const
869{
870 return d_ptr->m_visualController->locale();
871}
872
873/*!
874 * \property QAbstract3DGraph::queriedGraphPosition
875 * \since QtDataVisualization 1.2
876 *
877 * \brief The latest queried graph position values along each axis.
878 *
879 * This read-only property contains the results from
880 * Q3DScene::graphPositionQuery. The values are normalized to the range \c{[-1, 1]}.
881 * If the queried position was outside the graph bounds, the values
882 * will not reflect the real position, but will instead indicate an undefined position outside
883 * the range \c{[-1, 1]}. The value will be undefined until a query is made.
884 *
885 * There is no single correct 3D coordinate to match a particular screen position, so to be
886 * consistent, the queries are always done against the inner sides of an invisible box surrounding
887 * the graph.
888 *
889 * \note Bar graphs only allow querying graph position at the graph floor level,
890 * so the y-value is always zero for bar graphs and the valid queries can be only made at
891 * screen positions that contain the floor of the graph.
892 *
893 * \sa Q3DScene::graphPositionQuery
894 */
895QVector3D QAbstract3DGraph::queriedGraphPosition() const
896{
897 return d_ptr->m_visualController->queriedGraphPosition();
898}
899
900/*!
901 * \property QAbstract3DGraph::margin
902 * \since QtDataVisualization 1.2
903 *
904 * \brief The absolute value used for the space left between the edge of the
905 * plottable graph area and the edge of the graph background.
906 *
907 * If the margin value is negative, the margins are determined automatically and can vary according
908 * to the size of the items in the series and the type of the graph.
909 * The value is interpreted as a fraction of the y-axis range if the graph
910 * aspect ratios have not beed changed from the default values.
911 * Defaults to \c{-1.0}.
912 *
913 * \note Setting a smaller margin for a scatter graph than the automatically
914 * determined margin can cause the scatter items at the edges of the graph to
915 * overlap with the graph background.
916 *
917 * \note On scatter and surface graphs, if the margin is small in comparison to the axis label
918 * size, the positions of the edge labels of the axes are adjusted to avoid overlap with
919 * the edge labels of the neighboring axes.
920 */
921void QAbstract3DGraph::setMargin(qreal margin)
922{
923 d_ptr->m_visualController->setMargin(margin);
924}
925
926qreal QAbstract3DGraph::margin() const
927{
928 return d_ptr->m_visualController->margin();
929}
930
931/*!
932 * Returns \c{true} if the OpenGL context of the graph has been successfully initialized.
933 * Trying to use a graph when the context initialization has failed typically results in a crash.
934 * A common reason for a context initialization failure is lack of sufficient platform support
935 * for OpenGL.
936 */
937bool QAbstract3DGraph::hasContext() const
938{
939 if (d_ptr->m_initialized)
940 return true;
941 else
942 return false;
943}
944
945/*!
946 * \internal
947 */
948bool QAbstract3DGraph::event(QEvent *event)
949{
950 switch (event->type()) {
951 case QEvent::UpdateRequest:
952 d_ptr->renderNow();
953 return true;
954 case QEvent::TouchBegin:
955 case QEvent::TouchCancel:
956 case QEvent::TouchUpdate:
957 case QEvent::TouchEnd:
958 d_ptr->m_visualController->touchEvent(event: static_cast<QTouchEvent *>(event));
959 return true;
960 default:
961 return QWindow::event(event);
962 }
963}
964
965/*!
966 * \internal
967 */
968void QAbstract3DGraph::resizeEvent(QResizeEvent *event)
969{
970 Q_UNUSED(event);
971
972 if (d_ptr->m_visualController) {
973 Q3DScene *scene = d_ptr->m_visualController->scene();
974 scene->d_ptr->setWindowSize(QSize(width(), height()));
975 scene->d_ptr->setViewport(QRect(0, 0, width(), height()));
976 }
977}
978
979/*!
980 * \internal
981 */
982void QAbstract3DGraph::exposeEvent(QExposeEvent *event)
983{
984 Q_UNUSED(event);
985
986 if (isExposed())
987 d_ptr->renderNow();
988}
989
990/*!
991 * \internal
992 */
993void QAbstract3DGraph::mouseDoubleClickEvent(QMouseEvent *event)
994{
995 d_ptr->m_visualController->mouseDoubleClickEvent(event);
996}
997
998/*!
999 * \internal
1000 */
1001void QAbstract3DGraph::touchEvent(QTouchEvent *event)
1002{
1003 d_ptr->m_visualController->touchEvent(event);
1004}
1005
1006/*!
1007 * \internal
1008 */
1009void QAbstract3DGraph::mousePressEvent(QMouseEvent *event)
1010{
1011 d_ptr->m_visualController->mousePressEvent(event, mousePos: event->pos());
1012}
1013
1014/*!
1015 * \internal
1016 */
1017void QAbstract3DGraph::mouseReleaseEvent(QMouseEvent *event)
1018{
1019 d_ptr->m_visualController->mouseReleaseEvent(event, mousePos: event->pos());
1020}
1021
1022/*!
1023 * \internal
1024 */
1025void QAbstract3DGraph::mouseMoveEvent(QMouseEvent *event)
1026{
1027 d_ptr->m_visualController->mouseMoveEvent(event, mousePos: event->pos());
1028}
1029
1030#if QT_CONFIG(wheelevent)
1031/*!
1032 * \internal
1033 */
1034void QAbstract3DGraph::wheelEvent(QWheelEvent *event)
1035{
1036 d_ptr->m_visualController->wheelEvent(event);
1037}
1038#endif
1039
1040QAbstract3DGraphPrivate::QAbstract3DGraphPrivate(QAbstract3DGraph *q)
1041 : QObject(0),
1042 q_ptr(q),
1043 m_updatePending(false),
1044 m_visualController(0),
1045 m_devicePixelRatio(1.f),
1046 m_offscreenSurface(0),
1047 m_initialized(false)
1048{
1049}
1050
1051QAbstract3DGraphPrivate::~QAbstract3DGraphPrivate()
1052{
1053 if (m_offscreenSurface) {
1054 m_offscreenSurface->destroy();
1055 delete m_offscreenSurface;
1056 }
1057 if (m_context)
1058 m_context->makeCurrent(surface: q_ptr);
1059
1060 delete m_visualController;
1061}
1062
1063void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controller)
1064{
1065 m_visualController = controller;
1066
1067 QObject::connect(sender: m_visualController, signal: &Abstract3DController::activeInputHandlerChanged, receiver: q_ptr,
1068 slot: &QAbstract3DGraph::activeInputHandlerChanged);
1069 QObject::connect(sender: m_visualController, signal: &Abstract3DController::activeThemeChanged, receiver: q_ptr,
1070 slot: &QAbstract3DGraph::activeThemeChanged);
1071 QObject::connect(sender: m_visualController, signal: &Abstract3DController::selectionModeChanged, receiver: q_ptr,
1072 slot: &QAbstract3DGraph::selectionModeChanged);
1073 QObject::connect(sender: m_visualController, signal: &Abstract3DController::shadowQualityChanged, receiver: q_ptr,
1074 slot: &QAbstract3DGraph::shadowQualityChanged);
1075 QObject::connect(sender: m_visualController, signal: &Abstract3DController::optimizationHintsChanged, receiver: q_ptr,
1076 slot: &QAbstract3DGraph::optimizationHintsChanged);
1077 QObject::connect(sender: m_visualController, signal: &Abstract3DController::elementSelected, receiver: q_ptr,
1078 slot: &QAbstract3DGraph::selectedElementChanged);
1079
1080 QObject::connect(sender: m_visualController, signal: &Abstract3DController::needRender, receiver: this,
1081 slot: &QAbstract3DGraphPrivate::renderLater);
1082
1083 QObject::connect(sender: m_visualController, signal: &Abstract3DController::axisXChanged, receiver: this,
1084 slot: &QAbstract3DGraphPrivate::handleAxisXChanged);
1085 QObject::connect(sender: m_visualController, signal: &Abstract3DController::axisYChanged, receiver: this,
1086 slot: &QAbstract3DGraphPrivate::handleAxisYChanged);
1087 QObject::connect(sender: m_visualController, signal: &Abstract3DController::axisZChanged, receiver: this,
1088 slot: &QAbstract3DGraphPrivate::handleAxisZChanged);
1089
1090 QObject::connect(sender: m_visualController, signal: &Abstract3DController::measureFpsChanged, receiver: q_ptr,
1091 slot: &QAbstract3DGraph::measureFpsChanged);
1092 QObject::connect(sender: m_visualController, signal: &Abstract3DController::currentFpsChanged, receiver: q_ptr,
1093 slot: &QAbstract3DGraph::currentFpsChanged);
1094
1095 QObject::connect(sender: m_visualController, signal: &Abstract3DController::orthoProjectionChanged, receiver: q_ptr,
1096 slot: &QAbstract3DGraph::orthoProjectionChanged);
1097
1098 QObject::connect(sender: m_visualController, signal: &Abstract3DController::aspectRatioChanged, receiver: q_ptr,
1099 slot: &QAbstract3DGraph::aspectRatioChanged);
1100 QObject::connect(sender: m_visualController, signal: &Abstract3DController::polarChanged, receiver: q_ptr,
1101 slot: &QAbstract3DGraph::polarChanged);
1102 QObject::connect(sender: m_visualController, signal: &Abstract3DController::radialLabelOffsetChanged, receiver: q_ptr,
1103 slot: &QAbstract3DGraph::radialLabelOffsetChanged);
1104 QObject::connect(sender: m_visualController, signal: &Abstract3DController::horizontalAspectRatioChanged, receiver: q_ptr,
1105 slot: &QAbstract3DGraph::horizontalAspectRatioChanged);
1106
1107 QObject::connect(sender: m_visualController, signal: &Abstract3DController::reflectionChanged, receiver: q_ptr,
1108 slot: &QAbstract3DGraph::reflectionChanged);
1109 QObject::connect(sender: m_visualController, signal: &Abstract3DController::reflectivityChanged, receiver: q_ptr,
1110 slot: &QAbstract3DGraph::reflectivityChanged);
1111 QObject::connect(sender: m_visualController, signal: &Abstract3DController::localeChanged, receiver: q_ptr,
1112 slot: &QAbstract3DGraph::localeChanged);
1113 QObject::connect(sender: m_visualController, signal: &Abstract3DController::queriedGraphPositionChanged, receiver: q_ptr,
1114 slot: &QAbstract3DGraph::queriedGraphPositionChanged);
1115 QObject::connect(sender: m_visualController, signal: &Abstract3DController::marginChanged, receiver: q_ptr,
1116 slot: &QAbstract3DGraph::marginChanged);
1117}
1118
1119void QAbstract3DGraphPrivate::handleDevicePixelRatioChange()
1120{
1121 if (q_ptr->devicePixelRatio() == m_devicePixelRatio || !m_visualController)
1122 return;
1123
1124 m_devicePixelRatio = q_ptr->devicePixelRatio();
1125 m_visualController->scene()->setDevicePixelRatio(m_devicePixelRatio);
1126}
1127
1128void QAbstract3DGraphPrivate::render()
1129{
1130 handleDevicePixelRatioChange();
1131 m_visualController->synchDataToRenderer();
1132 m_visualController->render();
1133}
1134
1135void QAbstract3DGraphPrivate::renderLater()
1136{
1137 if (!m_updatePending) {
1138 m_updatePending = true;
1139 QCoreApplication::postEvent(receiver: q_ptr, event: new QEvent(QEvent::UpdateRequest));
1140 }
1141}
1142
1143void QAbstract3DGraphPrivate::renderNow()
1144{
1145 if (!q_ptr->isExposed())
1146 return;
1147
1148 m_updatePending = false;
1149
1150 m_context->makeCurrent(surface: q_ptr);
1151
1152 render();
1153
1154 m_context->swapBuffers(surface: q_ptr);
1155}
1156
1157QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imageSize)
1158{
1159 QImage image;
1160 QOpenGLFramebufferObject *fbo;
1161 QOpenGLFramebufferObjectFormat fboFormat;
1162 if (!m_offscreenSurface) {
1163 // Create an offscreen surface for rendering to images without rendering on screen
1164 m_offscreenSurface = new QOffscreenSurface(q_ptr->screen());
1165 m_offscreenSurface->setFormat(q_ptr->requestedFormat());
1166 m_offscreenSurface->create();
1167 }
1168 // Render the wanted frame offscreen
1169 m_context->makeCurrent(surface: m_offscreenSurface);
1170 fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
1171 if (!Utils::isOpenGLES()) {
1172 fboFormat.setInternalTextureFormat(GL_RGB);
1173 fboFormat.setSamples(msaaSamples);
1174 }
1175 fbo = new QOpenGLFramebufferObject(imageSize, fboFormat);
1176 if (fbo->isValid()) {
1177 QRect originalViewport = m_visualController->m_scene->viewport();
1178 m_visualController->m_scene->d_ptr->setWindowSize(imageSize);
1179 m_visualController->m_scene->d_ptr->setViewport(QRect(0, 0,
1180 imageSize.width(),
1181 imageSize.height()));
1182 m_visualController->synchDataToRenderer();
1183 fbo->bind();
1184 m_visualController->requestRender(fbo);
1185 image = fbo->toImage();
1186 fbo->release();
1187 m_visualController->m_scene->d_ptr->setWindowSize(originalViewport.size());
1188 m_visualController->m_scene->d_ptr->setViewport(originalViewport);
1189 }
1190 delete fbo;
1191 m_context->makeCurrent(surface: q_ptr);
1192
1193 return image;
1194}
1195
1196QT_END_NAMESPACE_DATAVISUALIZATION
1197

source code of qtdatavis3d/src/datavisualization/engine/qabstract3dgraph.cpp