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

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