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

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