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