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 "abstract3dcontroller_p.h"
31 #include "qabstract3daxis_p.h"
32 #include "qvalue3daxis_p.h"
33 #include "abstract3drenderer_p.h"
34 #include "qabstract3dinputhandler_p.h"
35 #include "qtouch3dinputhandler.h"
36 #include "thememanager_p.h"
37 #include "q3dtheme_p.h"
38 #include "qcustom3ditem_p.h"
39 #include "utils_p.h"
40 #include <QtCore/QThread>
41 #include <QtGui/QOpenGLFramebufferObject>
42 #include <QtCore/QMutexLocker>
43 
44 QT_BEGIN_NAMESPACE_DATAVISUALIZATION
45 
Abstract3DController(QRect initialViewport,Q3DScene * scene,QObject * parent)46 Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scene,
47                                            QObject *parent) :
48     QObject(parent),
49     m_themeManager(new ThemeManager(this)),
50     m_selectionMode(QAbstract3DGraph::SelectionItem),
51     m_shadowQuality(QAbstract3DGraph::ShadowQualityMedium),
52     m_useOrthoProjection(false),
53     m_aspectRatio(2.0),
54     m_horizontalAspectRatio(0.0),
55     m_optimizationHints(QAbstract3DGraph::OptimizationDefault),
56     m_reflectionEnabled(false),
57     m_reflectivity(0.5),
58     m_locale(QLocale::c()),
59     m_scene(scene),
60     m_activeInputHandler(0),
61     m_axisX(0),
62     m_axisY(0),
63     m_axisZ(0),
64     m_renderer(0),
65     m_isDataDirty(true),
66     m_isCustomDataDirty(true),
67     m_isCustomItemDirty(true),
68     m_isSeriesVisualsDirty(true),
69     m_renderPending(false),
70     m_isPolar(false),
71     m_radialLabelOffset(1.0f),
72     m_measureFps(false),
73     m_numFrames(0),
74     m_currentFps(0.0),
75     m_clickedType(QAbstract3DGraph::ElementNone),
76     m_selectedLabelIndex(-1),
77     m_selectedCustomItemIndex(-1),
78     m_margin(-1.0)
79 {
80     if (!m_scene)
81         m_scene = new Q3DScene;
82     m_scene->setParent(this);
83 
84     // Set initial theme
85     Q3DTheme *defaultTheme = new Q3DTheme(Q3DTheme::ThemeQt);
86     defaultTheme->d_ptr->setDefaultTheme(true);
87     setActiveTheme(defaultTheme);
88 
89     m_scene->d_ptr->setViewport(initialViewport);
90     m_scene->activeLight()->setAutoPosition(true);
91 
92     // Create initial default input handler
93     QAbstract3DInputHandler *inputHandler;
94     inputHandler = new QTouch3DInputHandler();
95     inputHandler->d_ptr->m_isDefaultHandler = true;
96     setActiveInputHandler(inputHandler);
97     connect(m_scene->d_ptr.data(), &Q3DScenePrivate::needRender, this,
98             &Abstract3DController::emitNeedRender);
99 }
100 
~Abstract3DController()101 Abstract3DController::~Abstract3DController()
102 {
103     destroyRenderer();
104     delete m_scene;
105     delete m_themeManager;
106     foreach (QCustom3DItem *item, m_customItems)
107         delete item;
108     m_customItems.clear();
109 }
110 
destroyRenderer()111 void Abstract3DController::destroyRenderer()
112 {
113     QMutexLocker mutexLocker(&m_renderMutex);
114     // Renderer can be in another thread, don't delete it directly in that case
115     if (m_renderer && m_renderer->thread() && m_renderer->thread() != this->thread())
116         m_renderer->deleteLater();
117     else
118         delete m_renderer;
119     m_renderer = 0;
120 }
121 
122 /**
123  * @brief setRenderer Sets the renderer to be used. isInitialized returns true from this point onwards.
124  * @param renderer Renderer to be used.
125  */
setRenderer(Abstract3DRenderer * renderer)126 void Abstract3DController::setRenderer(Abstract3DRenderer *renderer)
127 {
128     // Note: This function must be called within render mutex
129     m_renderer = renderer;
130 
131     // If renderer is created in different thread than controller, make sure renderer gets
132     // destroyed before the render thread finishes.
133     if (renderer->thread() != this->thread()) {
134         QObject::connect(renderer->thread(), &QThread::finished, this,
135                          &Abstract3DController::destroyRenderer, Qt::DirectConnection);
136     }
137 }
138 
addSeries(QAbstract3DSeries * series)139 void Abstract3DController::addSeries(QAbstract3DSeries *series)
140 {
141     insertSeries(m_seriesList.size(), series);
142 }
143 
insertSeries(int index,QAbstract3DSeries * series)144 void Abstract3DController::insertSeries(int index, QAbstract3DSeries *series)
145 {
146     if (series) {
147         if (m_seriesList.contains(series)) {
148             int oldIndex = m_seriesList.indexOf(series);
149             if (index != oldIndex) {
150                 m_seriesList.removeOne(series);
151                 if (oldIndex < index)
152                     index--;
153                 m_seriesList.insert(index, series);
154             }
155         } else {
156             int oldSize = m_seriesList.size();
157             m_seriesList.insert(index, series);
158             series->d_ptr->setController(this);
159             QObject::connect(series, &QAbstract3DSeries::visibilityChanged,
160                              this, &Abstract3DController::handleSeriesVisibilityChanged);
161             series->d_ptr->resetToTheme(*m_themeManager->activeTheme(), oldSize, false);
162         }
163         if (series->isVisible())
164             handleSeriesVisibilityChangedBySender(series);
165     }
166 }
167 
removeSeries(QAbstract3DSeries * series)168 void Abstract3DController::removeSeries(QAbstract3DSeries *series)
169 {
170     if (series && series->d_ptr->m_controller == this) {
171         m_seriesList.removeAll(series);
172         QObject::disconnect(series, &QAbstract3DSeries::visibilityChanged,
173                             this, &Abstract3DController::handleSeriesVisibilityChanged);
174         series->d_ptr->setController(0);
175         m_isDataDirty = true;
176         m_isSeriesVisualsDirty = true;
177         emitNeedRender();
178     }
179 }
180 
seriesList()181 QList<QAbstract3DSeries *> Abstract3DController::seriesList()
182 {
183     return m_seriesList;
184 }
185 
186 /**
187  * @brief synchDataToRenderer Called on the render thread while main GUI thread is blocked before rendering.
188  */
synchDataToRenderer()189 void Abstract3DController::synchDataToRenderer()
190 {
191     // Subclass implementations check for renderer validity already, so no need to check here.
192 
193     m_renderPending = false;
194 
195     // If there are pending queries, handle those first
196     if (m_renderer->isGraphPositionQueryResolved())
197         handlePendingGraphPositionQuery();
198 
199     if (m_renderer->isClickQueryResolved())
200         handlePendingClick();
201 
202     startRecordingRemovesAndInserts();
203 
204     if (m_scene->d_ptr->m_sceneDirty)
205         m_renderer->updateScene(m_scene);
206 
207     m_renderer->updateTheme(m_themeManager->activeTheme());
208 
209     if (m_changeTracker.polarChanged) {
210         m_renderer->updatePolar(m_isPolar);
211         m_changeTracker.polarChanged = false;
212     }
213 
214     if (m_changeTracker.radialLabelOffsetChanged) {
215         m_renderer->updateRadialLabelOffset(m_radialLabelOffset);
216         m_changeTracker.radialLabelOffsetChanged = false;
217     }
218 
219     if (m_changeTracker.shadowQualityChanged) {
220         m_renderer->updateShadowQuality(m_shadowQuality);
221         m_changeTracker.shadowQualityChanged = false;
222     }
223 
224     if (m_changeTracker.selectionModeChanged) {
225         m_renderer->updateSelectionMode(m_selectionMode);
226         m_changeTracker.selectionModeChanged = false;
227     }
228 
229     if (m_changeTracker.projectionChanged) {
230         m_renderer->m_useOrthoProjection = m_useOrthoProjection;
231         m_changeTracker.projectionChanged = false;
232     }
233 
234     if (m_changeTracker.aspectRatioChanged) {
235         m_renderer->updateAspectRatio(float(m_aspectRatio));
236         m_changeTracker.aspectRatioChanged = false;
237     }
238 
239     if (m_changeTracker.horizontalAspectRatioChanged) {
240         m_renderer->updateHorizontalAspectRatio(float(m_horizontalAspectRatio));
241         m_changeTracker.horizontalAspectRatioChanged = false;
242     }
243 
244     if (m_changeTracker.optimizationHintChanged) {
245         m_renderer->updateOptimizationHint(m_optimizationHints);
246         m_changeTracker.optimizationHintChanged = false;
247     }
248 
249     if (m_changeTracker.reflectionChanged) {
250         m_renderer->m_reflectionEnabled = m_reflectionEnabled;
251         m_changeTracker.reflectionChanged = false;
252     }
253 
254     if (m_changeTracker.reflectivityChanged) {
255         // Invert value to match functionality to the property description
256         m_renderer->m_reflectivity = -(m_reflectivity - 1.0);
257         m_changeTracker.reflectivityChanged = false;
258     }
259 
260     if (m_changeTracker.axisXFormatterChanged) {
261         m_changeTracker.axisXFormatterChanged = false;
262         if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) {
263             QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
264             m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationX,
265                                             valueAxisX->formatter());
266         }
267     }
268     if (m_changeTracker.axisYFormatterChanged) {
269         m_changeTracker.axisYFormatterChanged = false;
270         if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) {
271             QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
272             m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationY,
273                                             valueAxisY->formatter());
274         }
275     }
276     if (m_changeTracker.axisZFormatterChanged) {
277         m_changeTracker.axisZFormatterChanged = false;
278         if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
279             QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ);
280             m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationZ,
281                                             valueAxisZ->formatter());
282         }
283     }
284 
285     if (m_changeTracker.axisXTypeChanged) {
286         m_renderer->updateAxisType(QAbstract3DAxis::AxisOrientationX, m_axisX->type());
287         m_changeTracker.axisXTypeChanged = false;
288     }
289 
290     if (m_changeTracker.axisYTypeChanged) {
291         m_renderer->updateAxisType(QAbstract3DAxis::AxisOrientationY, m_axisY->type());
292         m_changeTracker.axisYTypeChanged = false;
293     }
294 
295     if (m_changeTracker.axisZTypeChanged) {
296         m_renderer->updateAxisType(QAbstract3DAxis::AxisOrientationZ, m_axisZ->type());
297         m_changeTracker.axisZTypeChanged = false;
298     }
299 
300     if (m_changeTracker.axisXTitleChanged) {
301         m_renderer->updateAxisTitle(QAbstract3DAxis::AxisOrientationX, m_axisX->title());
302         m_changeTracker.axisXTitleChanged = false;
303     }
304 
305     if (m_changeTracker.axisYTitleChanged) {
306         m_renderer->updateAxisTitle(QAbstract3DAxis::AxisOrientationY, m_axisY->title());
307         m_changeTracker.axisYTitleChanged = false;
308     }
309 
310     if (m_changeTracker.axisZTitleChanged) {
311         m_renderer->updateAxisTitle(QAbstract3DAxis::AxisOrientationZ, m_axisZ->title());
312         m_changeTracker.axisZTitleChanged = false;
313     }
314 
315     if (m_changeTracker.axisXLabelsChanged) {
316         m_renderer->updateAxisLabels(QAbstract3DAxis::AxisOrientationX, m_axisX->labels());
317         m_changeTracker.axisXLabelsChanged = false;
318     }
319 
320     if (m_changeTracker.axisYLabelsChanged) {
321         m_renderer->updateAxisLabels(QAbstract3DAxis::AxisOrientationY, m_axisY->labels());
322         m_changeTracker.axisYLabelsChanged = false;
323     }
324     if (m_changeTracker.axisZLabelsChanged) {
325         m_renderer->updateAxisLabels(QAbstract3DAxis::AxisOrientationZ, m_axisZ->labels());
326         m_changeTracker.axisZLabelsChanged = false;
327     }
328 
329     if (m_changeTracker.axisXRangeChanged) {
330         m_renderer->updateAxisRange(QAbstract3DAxis::AxisOrientationX, m_axisX->min(),
331                                     m_axisX->max());
332         m_changeTracker.axisXRangeChanged = false;
333     }
334 
335     if (m_changeTracker.axisYRangeChanged) {
336         m_renderer->updateAxisRange(QAbstract3DAxis::AxisOrientationY, m_axisY->min(),
337                                     m_axisY->max());
338         m_changeTracker.axisYRangeChanged = false;
339     }
340 
341     if (m_changeTracker.axisZRangeChanged) {
342         m_renderer->updateAxisRange(QAbstract3DAxis::AxisOrientationZ, m_axisZ->min(),
343                                     m_axisZ->max());
344         m_changeTracker.axisZRangeChanged = false;
345     }
346 
347     if (m_changeTracker.axisXSegmentCountChanged) {
348         m_changeTracker.axisXSegmentCountChanged = false;
349         if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) {
350             QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
351             m_renderer->updateAxisSegmentCount(QAbstract3DAxis::AxisOrientationX,
352                                                valueAxisX->segmentCount());
353         }
354     }
355 
356     if (m_changeTracker.axisYSegmentCountChanged) {
357         m_changeTracker.axisYSegmentCountChanged = false;
358         if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) {
359             QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
360             m_renderer->updateAxisSegmentCount(QAbstract3DAxis::AxisOrientationY,
361                                                valueAxisY->segmentCount());
362         }
363     }
364 
365     if (m_changeTracker.axisZSegmentCountChanged) {
366         m_changeTracker.axisZSegmentCountChanged = false;
367         if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
368             QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ);
369             m_renderer->updateAxisSegmentCount(QAbstract3DAxis::AxisOrientationZ,
370                                                valueAxisZ->segmentCount());
371         }
372     }
373 
374     if (m_changeTracker.axisXSubSegmentCountChanged) {
375         m_changeTracker.axisXSubSegmentCountChanged = false;
376         if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) {
377             QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
378             m_renderer->updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientationX,
379                                                   valueAxisX->subSegmentCount());
380         }
381     }
382 
383     if (m_changeTracker.axisYSubSegmentCountChanged) {
384         m_changeTracker.axisYSubSegmentCountChanged = false;
385         if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) {
386             QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
387             m_renderer->updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientationY,
388                                                   valueAxisY->subSegmentCount());
389         }
390     }
391 
392     if (m_changeTracker.axisZSubSegmentCountChanged) {
393         m_changeTracker.axisZSubSegmentCountChanged = false;
394         if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
395             QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ);
396             m_renderer->updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientationZ,
397                                                   valueAxisZ->subSegmentCount());
398         }
399     }
400 
401     if (m_changeTracker.axisXLabelFormatChanged) {
402         m_changeTracker.axisXLabelFormatChanged = false;
403         if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) {
404             QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
405             m_renderer->updateAxisLabelFormat(QAbstract3DAxis::AxisOrientationX,
406                                               valueAxisX->labelFormat());
407         }
408     }
409 
410     if (m_changeTracker.axisYLabelFormatChanged) {
411         m_changeTracker.axisYLabelFormatChanged = false;
412         if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) {
413             QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
414             m_renderer->updateAxisLabelFormat(QAbstract3DAxis::AxisOrientationY,
415                                               valueAxisY->labelFormat());
416         }
417     }
418 
419     if (m_changeTracker.axisZLabelFormatChanged) {
420         m_changeTracker.axisZLabelFormatChanged = false;
421         if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
422             QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ);
423             m_renderer->updateAxisLabelFormat(QAbstract3DAxis::AxisOrientationZ,
424                                               valueAxisZ->labelFormat());
425         }
426     }
427 
428     if (m_changeTracker.axisXReversedChanged) {
429         m_changeTracker.axisXReversedChanged = false;
430         if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) {
431             QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
432             m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationX,
433                                            valueAxisX->reversed());
434         }
435     }
436 
437     if (m_changeTracker.axisYReversedChanged) {
438         m_changeTracker.axisYReversedChanged = false;
439         if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) {
440             QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
441             m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationY,
442                                            valueAxisY->reversed());
443         }
444     }
445 
446     if (m_changeTracker.axisZReversedChanged) {
447         m_changeTracker.axisZReversedChanged = false;
448         if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
449             QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ);
450             m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationZ,
451                                            valueAxisZ->reversed());
452         }
453     }
454 
455     if (m_changeTracker.axisXLabelAutoRotationChanged) {
456         m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationX,
457                                                 m_axisX->labelAutoRotation());
458         m_changeTracker.axisXLabelAutoRotationChanged = false;
459     }
460 
461     if (m_changeTracker.axisYLabelAutoRotationChanged) {
462         m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationY,
463                                                 m_axisY->labelAutoRotation());
464         m_changeTracker.axisYLabelAutoRotationChanged = false;
465     }
466 
467     if (m_changeTracker.axisZLabelAutoRotationChanged) {
468         m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationZ,
469                                                 m_axisZ->labelAutoRotation());
470         m_changeTracker.axisZLabelAutoRotationChanged = false;
471     }
472 
473     if (m_changeTracker.axisXTitleVisibilityChanged) {
474         m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationX,
475                                               m_axisX->isTitleVisible());
476         m_changeTracker.axisXTitleVisibilityChanged = false;
477     }
478     if (m_changeTracker.axisYTitleVisibilityChanged) {
479         m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationY,
480                                               m_axisY->isTitleVisible());
481         m_changeTracker.axisYTitleVisibilityChanged = false;
482     }
483     if (m_changeTracker.axisZTitleVisibilityChanged) {
484         m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationZ,
485                                               m_axisZ->isTitleVisible());
486         m_changeTracker.axisZTitleVisibilityChanged = false;
487     }
488     if (m_changeTracker.axisXTitleFixedChanged) {
489         m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationX,
490                                          m_axisX->isTitleFixed());
491         m_changeTracker.axisXTitleFixedChanged = false;
492     }
493     if (m_changeTracker.axisYTitleFixedChanged) {
494         m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationY,
495                                          m_axisY->isTitleFixed());
496         m_changeTracker.axisYTitleFixedChanged = false;
497     }
498     if (m_changeTracker.axisZTitleFixedChanged) {
499         m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationZ,
500                                          m_axisZ->isTitleFixed());
501         m_changeTracker.axisZTitleFixedChanged = false;
502     }
503 
504     if (m_changeTracker.marginChanged) {
505         m_renderer->updateMargin(float(m_margin));
506         m_changeTracker.marginChanged = false;
507     }
508 
509     if (m_changedSeriesList.size()) {
510         m_renderer->modifiedSeriesList(m_changedSeriesList);
511         m_changedSeriesList.clear();
512     }
513 
514     if (m_isSeriesVisualsDirty) {
515         m_renderer->updateSeries(m_seriesList);
516         m_isSeriesVisualsDirty = false;
517     }
518 
519     if (m_isDataDirty) {
520         // Series list supplied above in updateSeries() is used to access the data,
521         // so no data needs to be passed in updateData()
522         m_renderer->updateData();
523         m_isDataDirty = false;
524     }
525 
526     if (m_isCustomDataDirty) {
527         m_renderer->updateCustomData(m_customItems);
528         m_isCustomDataDirty = false;
529     }
530 
531     if (m_isCustomItemDirty) {
532         m_renderer->updateCustomItems();
533         m_isCustomItemDirty = false;
534     }
535 }
536 
render(const GLuint defaultFboHandle)537 void Abstract3DController::render(const GLuint defaultFboHandle)
538 {
539     QMutexLocker mutexLocker(&m_renderMutex);
540 
541     // If not initialized, do nothing.
542     if (!m_renderer)
543         return;
544 
545     if (m_measureFps) {
546         // Measure speed (as milliseconds per frame)
547         m_numFrames++;
548         int elapsed = m_frameTimer.elapsed();
549         if (elapsed >= 1000) {
550             m_currentFps = qreal(m_numFrames) * 1000.0 / qreal(elapsed);
551             emit currentFpsChanged(m_currentFps);
552             m_numFrames = 0;
553             m_frameTimer.restart();
554         }
555         // To get meaningful framerate, don't just do render on demand.
556         emitNeedRender();
557     }
558 
559     m_renderer->render(defaultFboHandle);
560 }
561 
mouseDoubleClickEvent(QMouseEvent * event)562 void Abstract3DController::mouseDoubleClickEvent(QMouseEvent *event)
563 {
564     if (m_activeInputHandler)
565         m_activeInputHandler->mouseDoubleClickEvent(event);
566 }
567 
touchEvent(QTouchEvent * event)568 void Abstract3DController::touchEvent(QTouchEvent *event)
569 {
570     if (m_activeInputHandler)
571         m_activeInputHandler->touchEvent(event);
572 }
573 
mousePressEvent(QMouseEvent * event,const QPoint & mousePos)574 void Abstract3DController::mousePressEvent(QMouseEvent *event, const QPoint &mousePos)
575 {
576     if (m_activeInputHandler)
577         m_activeInputHandler->mousePressEvent(event, mousePos);
578 }
579 
mouseReleaseEvent(QMouseEvent * event,const QPoint & mousePos)580 void Abstract3DController::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos)
581 {
582     if (m_activeInputHandler)
583         m_activeInputHandler->mouseReleaseEvent(event, mousePos);
584 }
585 
mouseMoveEvent(QMouseEvent * event,const QPoint & mousePos)586 void Abstract3DController::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
587 {
588     if (m_activeInputHandler)
589         m_activeInputHandler->mouseMoveEvent(event, mousePos);
590 }
591 
592 #if QT_CONFIG(wheelevent)
wheelEvent(QWheelEvent * event)593 void Abstract3DController::wheelEvent(QWheelEvent *event)
594 {
595     if (m_activeInputHandler)
596         m_activeInputHandler->wheelEvent(event);
597 }
598 #endif
599 
handleThemeColorStyleChanged(Q3DTheme::ColorStyle style)600 void Abstract3DController::handleThemeColorStyleChanged(Q3DTheme::ColorStyle style)
601 {
602     // Set value for series that have not explicitly set this value
603     foreach (QAbstract3DSeries *series, m_seriesList) {
604         if (!series->d_ptr->m_themeTracker.colorStyleOverride) {
605             series->setColorStyle(style);
606             series->d_ptr->m_themeTracker.colorStyleOverride = false;
607         }
608     }
609     markSeriesVisualsDirty();
610 }
611 
handleThemeBaseColorsChanged(const QList<QColor> & colors)612 void Abstract3DController::handleThemeBaseColorsChanged(const QList<QColor> &colors)
613 {
614     int colorIdx = 0;
615     // Set value for series that have not explicitly set this value
616     foreach (QAbstract3DSeries *series, m_seriesList) {
617         if (!series->d_ptr->m_themeTracker.baseColorOverride) {
618             series->setBaseColor(colors.at(colorIdx));
619             series->d_ptr->m_themeTracker.baseColorOverride = false;
620         }
621         if (++colorIdx >= colors.size())
622             colorIdx = 0;
623     }
624     markSeriesVisualsDirty();
625 }
626 
handleThemeBaseGradientsChanged(const QList<QLinearGradient> & gradients)627 void Abstract3DController::handleThemeBaseGradientsChanged(const QList<QLinearGradient> &gradients)
628 {
629     int gradientIdx = 0;
630     // Set value for series that have not explicitly set this value
631     foreach (QAbstract3DSeries *series, m_seriesList) {
632         if (!series->d_ptr->m_themeTracker.baseGradientOverride) {
633             series->setBaseGradient(gradients.at(gradientIdx));
634             series->d_ptr->m_themeTracker.baseGradientOverride = false;
635         }
636         if (++gradientIdx >= gradients.size())
637             gradientIdx = 0;
638     }
639     markSeriesVisualsDirty();
640 }
641 
handleThemeSingleHighlightColorChanged(const QColor & color)642 void Abstract3DController::handleThemeSingleHighlightColorChanged(const QColor &color)
643 {
644     // Set value for series that have not explicitly set this value
645     foreach (QAbstract3DSeries *series, m_seriesList) {
646         if (!series->d_ptr->m_themeTracker.singleHighlightColorOverride) {
647             series->setSingleHighlightColor(color);
648             series->d_ptr->m_themeTracker.singleHighlightColorOverride = false;
649         }
650     }
651     markSeriesVisualsDirty();
652 }
653 
handleThemeSingleHighlightGradientChanged(const QLinearGradient & gradient)654 void Abstract3DController::handleThemeSingleHighlightGradientChanged(
655         const QLinearGradient &gradient)
656 {
657     // Set value for series that have not explicitly set this value
658     foreach (QAbstract3DSeries *series, m_seriesList) {
659         if (!series->d_ptr->m_themeTracker.singleHighlightGradientOverride) {
660             series->setSingleHighlightGradient(gradient);
661             series->d_ptr->m_themeTracker.singleHighlightGradientOverride = false;
662         }
663     }
664     markSeriesVisualsDirty();
665 }
666 
handleThemeMultiHighlightColorChanged(const QColor & color)667 void Abstract3DController::handleThemeMultiHighlightColorChanged(const QColor &color)
668 {
669     // Set value for series that have not explicitly set this value
670     foreach (QAbstract3DSeries *series, m_seriesList) {
671         if (!series->d_ptr->m_themeTracker.multiHighlightColorOverride) {
672             series->setMultiHighlightColor(color);
673             series->d_ptr->m_themeTracker.multiHighlightColorOverride = false;
674         }
675     }
676     markSeriesVisualsDirty();
677 }
678 
handleThemeMultiHighlightGradientChanged(const QLinearGradient & gradient)679 void Abstract3DController::handleThemeMultiHighlightGradientChanged(const QLinearGradient &gradient)
680 {
681     // Set value for series that have not explicitly set this value
682     foreach (QAbstract3DSeries *series, m_seriesList) {
683         if (!series->d_ptr->m_themeTracker.multiHighlightGradientOverride) {
684             series->setMultiHighlightGradient(gradient);
685             series->d_ptr->m_themeTracker.multiHighlightGradientOverride = false;
686         }
687     }
688     markSeriesVisualsDirty();
689 }
690 
handleThemeTypeChanged(Q3DTheme::Theme theme)691 void Abstract3DController::handleThemeTypeChanged(Q3DTheme::Theme theme)
692 {
693     Q_UNUSED(theme)
694 
695     // Changing theme type is logically equivalent of changing the entire theme
696     // object, so reset all attached series to the new theme.
697 
698     Q3DTheme *activeTheme = m_themeManager->activeTheme();
699     for (int i = 0; i < m_seriesList.size(); i++)
700         m_seriesList.at(i)->d_ptr->resetToTheme(*activeTheme, i, true);
701     markSeriesVisualsDirty();
702 }
703 
setAxisX(QAbstract3DAxis * axis)704 void Abstract3DController::setAxisX(QAbstract3DAxis *axis)
705 {
706     // Setting null axis will always create new default axis
707     if (!axis || axis != m_axisX) {
708         setAxisHelper(QAbstract3DAxis::AxisOrientationX, axis, &m_axisX);
709         emit axisXChanged(m_axisX);
710     }
711 }
712 
axisX() const713 QAbstract3DAxis *Abstract3DController::axisX() const
714 {
715     return m_axisX;
716 }
717 
setAxisY(QAbstract3DAxis * axis)718 void Abstract3DController::setAxisY(QAbstract3DAxis *axis)
719 {
720     // Setting null axis will always create new default axis
721     if (!axis || axis != m_axisY) {
722         setAxisHelper(QAbstract3DAxis::AxisOrientationY, axis, &m_axisY);
723         emit axisYChanged(m_axisY);
724     }
725 }
726 
axisY() const727 QAbstract3DAxis *Abstract3DController::axisY() const
728 {
729     return m_axisY;
730 }
731 
setAxisZ(QAbstract3DAxis * axis)732 void Abstract3DController::setAxisZ(QAbstract3DAxis *axis)
733 {
734     // Setting null axis will always create new default axis
735     if (!axis || axis != m_axisZ) {
736         setAxisHelper(QAbstract3DAxis::AxisOrientationZ, axis, &m_axisZ);
737         emit axisZChanged(m_axisZ);
738     }
739 }
740 
axisZ() const741 QAbstract3DAxis *Abstract3DController::axisZ() const
742 {
743     return m_axisZ;
744 }
745 
addAxis(QAbstract3DAxis * axis)746 void Abstract3DController::addAxis(QAbstract3DAxis *axis)
747 {
748     Q_ASSERT(axis);
749     Abstract3DController *owner = qobject_cast<Abstract3DController *>(axis->parent());
750     if (owner != this) {
751         Q_ASSERT_X(!owner, "addAxis", "Axis already attached to a graph.");
752         axis->setParent(this);
753     }
754     if (!m_axes.contains(axis))
755         m_axes.append(axis);
756 }
757 
releaseAxis(QAbstract3DAxis * axis)758 void Abstract3DController::releaseAxis(QAbstract3DAxis *axis)
759 {
760     if (axis && m_axes.contains(axis)) {
761         // Clear the default status from released default axes
762         if (axis->d_ptr->isDefaultAxis())
763             axis->d_ptr->setDefaultAxis(false);
764 
765         // If the axis is in use, replace it with a temporary one
766         switch (axis->orientation()) {
767         case QAbstract3DAxis::AxisOrientationX:
768             setAxisX(0);
769             break;
770         case QAbstract3DAxis::AxisOrientationY:
771             setAxisY(0);
772             break;
773         case QAbstract3DAxis::AxisOrientationZ:
774             setAxisZ(0);
775             break;
776         default:
777             break;
778         }
779 
780         m_axes.removeAll(axis);
781         axis->setParent(0);
782     }
783 }
784 
axes() const785 QList<QAbstract3DAxis *> Abstract3DController::axes() const
786 {
787     return m_axes;
788 }
789 
addInputHandler(QAbstract3DInputHandler * inputHandler)790 void Abstract3DController::addInputHandler(QAbstract3DInputHandler *inputHandler)
791 {
792     Q_ASSERT(inputHandler);
793     Abstract3DController *owner = qobject_cast<Abstract3DController *>(inputHandler->parent());
794     if (owner != this) {
795         Q_ASSERT_X(!owner, "addInputHandler",
796                    "Input handler already attached to another component.");
797         inputHandler->setParent(this);
798     }
799 
800     if (!m_inputHandlers.contains(inputHandler))
801         m_inputHandlers.append(inputHandler);
802 }
803 
releaseInputHandler(QAbstract3DInputHandler * inputHandler)804 void Abstract3DController::releaseInputHandler(QAbstract3DInputHandler *inputHandler)
805 {
806     if (inputHandler && m_inputHandlers.contains(inputHandler)) {
807         // Clear the default status from released default input handler
808         if (inputHandler->d_ptr->m_isDefaultHandler)
809             inputHandler->d_ptr->m_isDefaultHandler = false;
810 
811         // If the input handler is in use, remove it
812         if (m_activeInputHandler == inputHandler)
813             setActiveInputHandler(0);
814 
815         m_inputHandlers.removeAll(inputHandler);
816         inputHandler->setParent(0);
817     }
818 }
819 
setActiveInputHandler(QAbstract3DInputHandler * inputHandler)820 void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputHandler)
821 {
822     if (inputHandler == m_activeInputHandler)
823         return;
824 
825     // If existing input handler is the default input handler, delete it
826     if (m_activeInputHandler) {
827         if (m_activeInputHandler->d_ptr->m_isDefaultHandler) {
828             m_inputHandlers.removeAll(m_activeInputHandler);
829             delete m_activeInputHandler;
830         } else {
831             // Disconnect the old input handler
832             m_activeInputHandler->setScene(0);
833             QObject::disconnect(m_activeInputHandler, 0, this, 0);
834         }
835     }
836 
837     // Assume ownership and connect to this controller's scene
838     if (inputHandler)
839         addInputHandler(inputHandler);
840 
841     m_activeInputHandler = inputHandler;
842     if (m_activeInputHandler) {
843         m_activeInputHandler->setScene(m_scene);
844 
845         // Connect the input handler
846         QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this,
847                          &Abstract3DController::handleInputViewChanged);
848         QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this,
849                          &Abstract3DController::handleInputPositionChanged);
850     }
851 
852     // Notify change of input handler
853     emit activeInputHandlerChanged(m_activeInputHandler);
854 }
855 
activeInputHandler()856 QAbstract3DInputHandler* Abstract3DController::activeInputHandler()
857 {
858     return m_activeInputHandler;
859 }
860 
inputHandlers() const861 QList<QAbstract3DInputHandler *> Abstract3DController::inputHandlers() const
862 {
863     return m_inputHandlers;
864 }
865 
addTheme(Q3DTheme * theme)866 void Abstract3DController::addTheme(Q3DTheme *theme)
867 {
868     m_themeManager->addTheme(theme);
869 }
870 
releaseTheme(Q3DTheme * theme)871 void Abstract3DController::releaseTheme(Q3DTheme *theme)
872 {
873     Q3DTheme *oldTheme = m_themeManager->activeTheme();
874 
875     m_themeManager->releaseTheme(theme);
876 
877     if (oldTheme != m_themeManager->activeTheme())
878         emit activeThemeChanged(m_themeManager->activeTheme());
879 }
880 
themes() const881 QList<Q3DTheme *> Abstract3DController::themes() const
882 {
883     return m_themeManager->themes();
884 }
885 
setActiveTheme(Q3DTheme * theme,bool force)886 void Abstract3DController::setActiveTheme(Q3DTheme *theme, bool force)
887 {
888     if (theme != m_themeManager->activeTheme()) {
889         m_themeManager->setActiveTheme(theme);
890         m_changeTracker.themeChanged = true;
891         // Default theme can be created by theme manager, so ensure we have correct theme
892         Q3DTheme *newActiveTheme = m_themeManager->activeTheme();
893         // Reset all attached series to the new theme
894         for (int i = 0; i < m_seriesList.size(); i++)
895             m_seriesList.at(i)->d_ptr->resetToTheme(*newActiveTheme, i, force);
896         markSeriesVisualsDirty();
897         emit activeThemeChanged(newActiveTheme);
898     }
899 }
900 
activeTheme() const901 Q3DTheme *Abstract3DController::activeTheme() const
902 {
903     return m_themeManager->activeTheme();
904 }
905 
setSelectionMode(QAbstract3DGraph::SelectionFlags mode)906 void Abstract3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode)
907 {
908     if (mode != m_selectionMode) {
909         m_selectionMode = mode;
910         m_changeTracker.selectionModeChanged = true;
911         emit selectionModeChanged(mode);
912         emitNeedRender();
913     }
914 }
915 
selectionMode() const916 QAbstract3DGraph::SelectionFlags Abstract3DController::selectionMode() const
917 {
918     return m_selectionMode;
919 }
920 
setShadowQuality(QAbstract3DGraph::ShadowQuality quality)921 void Abstract3DController::setShadowQuality(QAbstract3DGraph::ShadowQuality quality)
922 {
923     if (!m_useOrthoProjection)
924         doSetShadowQuality(quality);
925 }
926 
doSetShadowQuality(QAbstract3DGraph::ShadowQuality quality)927 void Abstract3DController::doSetShadowQuality(QAbstract3DGraph::ShadowQuality quality)
928 {
929     if (quality != m_shadowQuality) {
930         m_shadowQuality = quality;
931         m_changeTracker.shadowQualityChanged = true;
932         emit shadowQualityChanged(m_shadowQuality);
933         emitNeedRender();
934     }
935 }
936 
shadowQuality() const937 QAbstract3DGraph::ShadowQuality Abstract3DController::shadowQuality() const
938 {
939     return m_shadowQuality;
940 }
941 
setOptimizationHints(QAbstract3DGraph::OptimizationHints hints)942 void Abstract3DController::setOptimizationHints(QAbstract3DGraph::OptimizationHints hints)
943 {
944     if (hints != m_optimizationHints) {
945         m_optimizationHints = hints;
946         m_changeTracker.optimizationHintChanged = true;
947         m_isDataDirty = true;
948         emit optimizationHintsChanged(hints);
949         emitNeedRender();
950     }
951 }
952 
optimizationHints() const953 QAbstract3DGraph::OptimizationHints Abstract3DController::optimizationHints() const
954 {
955     return m_optimizationHints;
956 }
957 
shadowsSupported() const958 bool Abstract3DController::shadowsSupported() const
959 {
960     return !isOpenGLES();
961 }
962 
isSlicingActive() const963 bool Abstract3DController::isSlicingActive() const
964 {
965     return m_scene->isSlicingActive();
966 }
967 
setSlicingActive(bool isSlicing)968 void Abstract3DController::setSlicingActive(bool isSlicing)
969 {
970     m_scene->setSlicingActive(isSlicing);
971 }
972 
scene()973 Q3DScene *Abstract3DController::scene()
974 {
975     return m_scene;
976 }
977 
markDataDirty()978 void Abstract3DController::markDataDirty()
979 {
980     m_isDataDirty = true;
981 
982     markSeriesItemLabelsDirty();
983     emitNeedRender();
984 }
985 
markSeriesVisualsDirty()986 void Abstract3DController::markSeriesVisualsDirty()
987 {
988     m_isSeriesVisualsDirty = true;
989     emitNeedRender();
990 }
991 
requestRender(QOpenGLFramebufferObject * fbo)992 void Abstract3DController::requestRender(QOpenGLFramebufferObject *fbo)
993 {
994     QMutexLocker mutexLocker(&m_renderMutex);
995     m_renderer->render(fbo->handle());
996 }
997 
addCustomItem(QCustom3DItem * item)998 int Abstract3DController::addCustomItem(QCustom3DItem *item)
999 {
1000     if (!item)
1001         return -1;
1002 
1003     int index = m_customItems.indexOf(item);
1004 
1005     if (index != -1)
1006         return index;
1007 
1008     item->setParent(this);
1009     connect(item->d_ptr.data(), &QCustom3DItemPrivate::needUpdate,
1010             this, &Abstract3DController::updateCustomItem);
1011     m_customItems.append(item);
1012     item->d_ptr->resetDirtyBits();
1013     m_isCustomDataDirty = true;
1014     emitNeedRender();
1015     return m_customItems.count() - 1;
1016 }
1017 
deleteCustomItems()1018 void Abstract3DController::deleteCustomItems()
1019 {
1020     foreach (QCustom3DItem *item, m_customItems)
1021         delete item;
1022     m_customItems.clear();
1023     m_isCustomDataDirty = true;
1024     emitNeedRender();
1025 }
1026 
deleteCustomItem(QCustom3DItem * item)1027 void Abstract3DController::deleteCustomItem(QCustom3DItem *item)
1028 {
1029     if (!item)
1030         return;
1031 
1032     m_customItems.removeOne(item);
1033     delete item;
1034     item = 0;
1035     m_isCustomDataDirty = true;
1036     emitNeedRender();
1037 }
1038 
deleteCustomItem(const QVector3D & position)1039 void Abstract3DController::deleteCustomItem(const QVector3D &position)
1040 {
1041     // Get the item for the position
1042     foreach (QCustom3DItem *item, m_customItems) {
1043         if (item->position() == position)
1044             deleteCustomItem(item);
1045     }
1046 }
1047 
releaseCustomItem(QCustom3DItem * item)1048 void Abstract3DController::releaseCustomItem(QCustom3DItem *item)
1049 {
1050     if (item && m_customItems.contains(item)) {
1051         disconnect(item->d_ptr.data(), &QCustom3DItemPrivate::needUpdate,
1052                    this, &Abstract3DController::updateCustomItem);
1053         m_customItems.removeOne(item);
1054         item->setParent(0);
1055         m_isCustomDataDirty = true;
1056         emitNeedRender();
1057     }
1058 }
1059 
customItems() const1060 QList<QCustom3DItem *> Abstract3DController::customItems() const
1061 {
1062     return m_customItems;
1063 }
1064 
updateCustomItem()1065 void Abstract3DController::updateCustomItem()
1066 {
1067     m_isCustomItemDirty = true;
1068     emitNeedRender();
1069 }
1070 
handleAxisTitleChanged(const QString & title)1071 void Abstract3DController::handleAxisTitleChanged(const QString &title)
1072 {
1073     Q_UNUSED(title)
1074     handleAxisTitleChangedBySender(sender());
1075 }
1076 
handleAxisTitleChangedBySender(QObject * sender)1077 void Abstract3DController::handleAxisTitleChangedBySender(QObject *sender)
1078 {
1079     if (sender == m_axisX)
1080         m_changeTracker.axisXTitleChanged = true;
1081     else if (sender == m_axisY)
1082         m_changeTracker.axisYTitleChanged = true;
1083     else if (sender == m_axisZ)
1084         m_changeTracker.axisZTitleChanged = true;
1085     else
1086         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1087 
1088     markSeriesItemLabelsDirty();
1089     emitNeedRender();
1090 }
1091 
handleAxisLabelsChanged()1092 void Abstract3DController::handleAxisLabelsChanged()
1093 {
1094     handleAxisLabelsChangedBySender(sender());
1095 }
1096 
handleAxisLabelsChangedBySender(QObject * sender)1097 void Abstract3DController::handleAxisLabelsChangedBySender(QObject *sender)
1098 {
1099     if (sender == m_axisX)
1100         m_changeTracker.axisXLabelsChanged = true;
1101     else if (sender == m_axisY)
1102         m_changeTracker.axisYLabelsChanged = true;
1103     else if (sender == m_axisZ)
1104         m_changeTracker.axisZLabelsChanged = true;
1105     else
1106         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1107 
1108     markSeriesItemLabelsDirty();
1109     emitNeedRender();
1110 }
1111 
handleAxisRangeChanged(float min,float max)1112 void Abstract3DController::handleAxisRangeChanged(float min, float max)
1113 {
1114     Q_UNUSED(min)
1115     Q_UNUSED(max)
1116     handleAxisRangeChangedBySender(sender());
1117 }
1118 
handleAxisRangeChangedBySender(QObject * sender)1119 void Abstract3DController::handleAxisRangeChangedBySender(QObject *sender)
1120 {
1121     if (sender == m_axisX) {
1122         m_isDataDirty = true;
1123         m_changeTracker.axisXRangeChanged = true;
1124     } else if (sender == m_axisY) {
1125         m_isDataDirty = true;
1126         m_changeTracker.axisYRangeChanged = true;
1127     } else if (sender == m_axisZ) {
1128         m_isDataDirty = true;
1129         m_changeTracker.axisZRangeChanged = true;
1130     } else {
1131         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1132     }
1133     emitNeedRender();
1134 }
1135 
handleAxisSegmentCountChanged(int count)1136 void Abstract3DController::handleAxisSegmentCountChanged(int count)
1137 {
1138     Q_UNUSED(count)
1139     handleAxisSegmentCountChangedBySender(sender());
1140 }
1141 
handleAxisSegmentCountChangedBySender(QObject * sender)1142 void Abstract3DController::handleAxisSegmentCountChangedBySender(QObject *sender)
1143 {
1144     if (sender == m_axisX)
1145         m_changeTracker.axisXSegmentCountChanged = true;
1146     else if (sender == m_axisY)
1147         m_changeTracker.axisYSegmentCountChanged = true;
1148     else if (sender == m_axisZ)
1149         m_changeTracker.axisZSegmentCountChanged = true;
1150     else
1151         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1152     emitNeedRender();
1153 }
1154 
handleAxisSubSegmentCountChanged(int count)1155 void Abstract3DController::handleAxisSubSegmentCountChanged(int count)
1156 {
1157     Q_UNUSED(count)
1158     handleAxisSubSegmentCountChangedBySender(sender());
1159 }
1160 
handleAxisSubSegmentCountChangedBySender(QObject * sender)1161 void Abstract3DController::handleAxisSubSegmentCountChangedBySender(QObject *sender)
1162 {
1163     if (sender == m_axisX)
1164         m_changeTracker.axisXSubSegmentCountChanged = true;
1165     else if (sender == m_axisY)
1166         m_changeTracker.axisYSubSegmentCountChanged = true;
1167     else if (sender == m_axisZ)
1168         m_changeTracker.axisZSubSegmentCountChanged = true;
1169     else
1170         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1171     emitNeedRender();
1172 }
1173 
handleAxisAutoAdjustRangeChanged(bool autoAdjust)1174 void Abstract3DController::handleAxisAutoAdjustRangeChanged(bool autoAdjust)
1175 {
1176     QObject *sender = QObject::sender();
1177     if (sender != m_axisX && sender != m_axisY && sender != m_axisZ)
1178         return;
1179 
1180     QAbstract3DAxis *axis = static_cast<QAbstract3DAxis*>(sender);
1181     handleAxisAutoAdjustRangeChangedInOrientation(axis->orientation(), autoAdjust);
1182 }
1183 
handleAxisLabelFormatChanged(const QString & format)1184 void Abstract3DController::handleAxisLabelFormatChanged(const QString &format)
1185 {
1186     Q_UNUSED(format)
1187     handleAxisLabelFormatChangedBySender(sender());
1188 }
1189 
handleAxisReversedChanged(bool enable)1190 void Abstract3DController::handleAxisReversedChanged(bool enable)
1191 {
1192     Q_UNUSED(enable)
1193     handleAxisReversedChangedBySender(sender());
1194 }
1195 
handleAxisFormatterDirty()1196 void Abstract3DController::handleAxisFormatterDirty()
1197 {
1198     handleAxisFormatterDirtyBySender(sender());
1199 }
1200 
handleAxisLabelAutoRotationChanged(float angle)1201 void Abstract3DController::handleAxisLabelAutoRotationChanged(float angle)
1202 {
1203     Q_UNUSED(angle)
1204     handleAxisLabelAutoRotationChangedBySender(sender());
1205 }
1206 
handleAxisTitleVisibilityChanged(bool visible)1207 void Abstract3DController::handleAxisTitleVisibilityChanged(bool visible)
1208 {
1209     Q_UNUSED(visible)
1210     handleAxisTitleVisibilityChangedBySender(sender());
1211 }
1212 
handleAxisTitleFixedChanged(bool fixed)1213 void Abstract3DController::handleAxisTitleFixedChanged(bool fixed)
1214 {
1215     Q_UNUSED(fixed)
1216     handleAxisTitleFixedChangedBySender(sender());
1217 }
1218 
handleInputViewChanged(QAbstract3DInputHandler::InputView view)1219 void Abstract3DController::handleInputViewChanged(QAbstract3DInputHandler::InputView view)
1220 {
1221     // When in automatic slicing mode, input view change to primary disables slice mode
1222     if (m_selectionMode.testFlag(QAbstract3DGraph::SelectionSlice)
1223             && view == QAbstract3DInputHandler::InputViewOnPrimary) {
1224         setSlicingActive(false);
1225     }
1226 
1227     emitNeedRender();
1228 }
1229 
handleInputPositionChanged(const QPoint & position)1230 void Abstract3DController::handleInputPositionChanged(const QPoint &position)
1231 {
1232     Q_UNUSED(position)
1233     emitNeedRender();
1234 }
1235 
handleSeriesVisibilityChanged(bool visible)1236 void Abstract3DController::handleSeriesVisibilityChanged(bool visible)
1237 {
1238     Q_UNUSED(visible)
1239 
1240     handleSeriesVisibilityChangedBySender(sender());
1241 }
1242 
handleRequestShadowQuality(QAbstract3DGraph::ShadowQuality quality)1243 void Abstract3DController::handleRequestShadowQuality(QAbstract3DGraph::ShadowQuality quality)
1244 {
1245     setShadowQuality(quality);
1246 }
1247 
setMeasureFps(bool enable)1248 void Abstract3DController::setMeasureFps(bool enable)
1249 {
1250     if (m_measureFps != enable) {
1251         m_measureFps = enable;
1252         m_currentFps = 0.0;
1253 
1254         if (enable) {
1255             m_frameTimer.start();
1256             m_numFrames = -1;
1257             emitNeedRender();
1258         }
1259         emit measureFpsChanged(enable);
1260     }
1261 }
1262 
handleAxisLabelFormatChangedBySender(QObject * sender)1263 void Abstract3DController::handleAxisLabelFormatChangedBySender(QObject *sender)
1264 {
1265     // Label format changing needs to dirty the data so that labels are reset.
1266     if (sender == m_axisX) {
1267         m_isDataDirty = true;
1268         m_changeTracker.axisXLabelFormatChanged = true;
1269     } else if (sender == m_axisY) {
1270         m_isDataDirty = true;
1271         m_changeTracker.axisYLabelFormatChanged = true;
1272     } else if (sender == m_axisZ) {
1273         m_isDataDirty = true;
1274         m_changeTracker.axisZLabelFormatChanged = true;
1275     } else {
1276         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1277     }
1278     emitNeedRender();
1279 }
1280 
handleAxisReversedChangedBySender(QObject * sender)1281 void Abstract3DController::handleAxisReversedChangedBySender(QObject *sender)
1282 {
1283     // Reversing change needs to dirty the data so item positions are recalculated
1284     if (sender == m_axisX) {
1285         m_isDataDirty = true;
1286         m_changeTracker.axisXReversedChanged = true;
1287     } else if (sender == m_axisY) {
1288         m_isDataDirty = true;
1289         m_changeTracker.axisYReversedChanged = true;
1290     } else if (sender == m_axisZ) {
1291         m_isDataDirty = true;
1292         m_changeTracker.axisZReversedChanged = true;
1293     } else {
1294         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1295     }
1296     emitNeedRender();
1297 }
1298 
handleAxisFormatterDirtyBySender(QObject * sender)1299 void Abstract3DController::handleAxisFormatterDirtyBySender(QObject *sender)
1300 {
1301     // Sender is QValue3DAxisPrivate
1302     QValue3DAxis *valueAxis = static_cast<QValue3DAxisPrivate *>(sender)->qptr();
1303     if (valueAxis == m_axisX) {
1304         m_isDataDirty = true;
1305         m_changeTracker.axisXFormatterChanged = true;
1306     } else if (valueAxis == m_axisY) {
1307         m_isDataDirty = true;
1308         m_changeTracker.axisYFormatterChanged = true;
1309     } else if (valueAxis == m_axisZ) {
1310         m_isDataDirty = true;
1311         m_changeTracker.axisZFormatterChanged = true;
1312     } else {
1313         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1314     }
1315     emitNeedRender();
1316 }
1317 
handleAxisLabelAutoRotationChangedBySender(QObject * sender)1318 void Abstract3DController::handleAxisLabelAutoRotationChangedBySender(QObject *sender)
1319 {
1320     if (sender == m_axisX)
1321         m_changeTracker.axisXLabelAutoRotationChanged = true;
1322     else if (sender == m_axisY)
1323         m_changeTracker.axisYLabelAutoRotationChanged = true;
1324     else if (sender == m_axisZ)
1325         m_changeTracker.axisZLabelAutoRotationChanged = true;
1326     else
1327         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1328 
1329     emitNeedRender();
1330 }
1331 
handleAxisTitleVisibilityChangedBySender(QObject * sender)1332 void Abstract3DController::handleAxisTitleVisibilityChangedBySender(QObject *sender)
1333 {
1334     if (sender == m_axisX)
1335         m_changeTracker.axisXTitleVisibilityChanged = true;
1336     else if (sender == m_axisY)
1337         m_changeTracker.axisYTitleVisibilityChanged = true;
1338     else if (sender == m_axisZ)
1339         m_changeTracker.axisZTitleVisibilityChanged = true;
1340     else
1341         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1342 
1343     emitNeedRender();
1344 }
1345 
handleAxisTitleFixedChangedBySender(QObject * sender)1346 void Abstract3DController::handleAxisTitleFixedChangedBySender(QObject *sender)
1347 {
1348     if (sender == m_axisX)
1349         m_changeTracker.axisXTitleFixedChanged = true;
1350     else if (sender == m_axisY)
1351         m_changeTracker.axisYTitleFixedChanged = true;
1352     else if (sender == m_axisZ)
1353         m_changeTracker.axisZTitleFixedChanged = true;
1354     else
1355         qWarning() << __FUNCTION__ << "invoked for invalid axis";
1356 
1357     emitNeedRender();
1358 }
1359 
handleSeriesVisibilityChangedBySender(QObject * sender)1360 void Abstract3DController::handleSeriesVisibilityChangedBySender(QObject *sender)
1361 {
1362     QAbstract3DSeries *series = static_cast<QAbstract3DSeries *>(sender);
1363     series->d_ptr->m_changeTracker.visibilityChanged = true;
1364 
1365     m_isDataDirty = true;
1366     m_isSeriesVisualsDirty = true;
1367 
1368     adjustAxisRanges();
1369 
1370     emitNeedRender();
1371 }
1372 
markSeriesItemLabelsDirty()1373 void Abstract3DController::markSeriesItemLabelsDirty()
1374 {
1375     for (int i = 0; i < m_seriesList.size(); i++)
1376         m_seriesList.at(i)->d_ptr->markItemLabelDirty();
1377 }
1378 
isOpenGLES() const1379 bool Abstract3DController::isOpenGLES() const
1380 {
1381     return Utils::isOpenGLES();
1382 }
1383 
setAxisHelper(QAbstract3DAxis::AxisOrientation orientation,QAbstract3DAxis * axis,QAbstract3DAxis ** axisPtr)1384 void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orientation,
1385                                          QAbstract3DAxis *axis, QAbstract3DAxis **axisPtr)
1386 {
1387     // Setting null axis indicates using default axis
1388     if (!axis)
1389         axis = createDefaultAxis(orientation);
1390 
1391     // If old axis is default axis, delete it
1392     QAbstract3DAxis *oldAxis = *axisPtr;
1393     if (oldAxis) {
1394         if (oldAxis->d_ptr->isDefaultAxis()) {
1395             m_axes.removeAll(oldAxis);
1396             delete oldAxis;
1397             oldAxis = 0;
1398         } else {
1399             // Disconnect the old axis from use
1400             QObject::disconnect(oldAxis, 0, this, 0);
1401             oldAxis->d_ptr->setOrientation(QAbstract3DAxis::AxisOrientationNone);
1402         }
1403     }
1404 
1405     // Assume ownership
1406     addAxis(axis);
1407 
1408     // Connect the new axis
1409     *axisPtr = axis;
1410 
1411     axis->d_ptr->setOrientation(orientation);
1412 
1413     QObject::connect(axis, &QAbstract3DAxis::titleChanged,
1414                      this, &Abstract3DController::handleAxisTitleChanged);
1415     QObject::connect(axis, &QAbstract3DAxis::labelsChanged,
1416                      this, &Abstract3DController::handleAxisLabelsChanged);
1417     QObject::connect(axis, &QAbstract3DAxis::rangeChanged,
1418                      this, &Abstract3DController::handleAxisRangeChanged);
1419     QObject::connect(axis, &QAbstract3DAxis::autoAdjustRangeChanged,
1420                      this, &Abstract3DController::handleAxisAutoAdjustRangeChanged);
1421     QObject::connect(axis, &QAbstract3DAxis::labelAutoRotationChanged,
1422                      this, &Abstract3DController::handleAxisLabelAutoRotationChanged);
1423     QObject::connect(axis, &QAbstract3DAxis::titleVisibilityChanged,
1424                      this, &Abstract3DController::handleAxisTitleVisibilityChanged);
1425     QObject::connect(axis, &QAbstract3DAxis::titleFixedChanged,
1426                      this, &Abstract3DController::handleAxisTitleFixedChanged);
1427 
1428     if (orientation == QAbstract3DAxis::AxisOrientationX)
1429         m_changeTracker.axisXTypeChanged = true;
1430     else if (orientation == QAbstract3DAxis::AxisOrientationY)
1431         m_changeTracker.axisYTypeChanged = true;
1432     else if (orientation == QAbstract3DAxis::AxisOrientationZ)
1433         m_changeTracker.axisZTypeChanged = true;
1434 
1435     handleAxisTitleChangedBySender(axis);
1436     handleAxisLabelsChangedBySender(axis);
1437     handleAxisRangeChangedBySender(axis);
1438     handleAxisAutoAdjustRangeChangedInOrientation(axis->orientation(),
1439                                                   axis->isAutoAdjustRange());
1440     handleAxisLabelAutoRotationChangedBySender(axis);
1441     handleAxisTitleVisibilityChangedBySender(axis);
1442     handleAxisTitleFixedChangedBySender(axis);
1443 
1444     if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
1445         QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
1446         QObject::connect(valueAxis, &QValue3DAxis::segmentCountChanged,
1447                          this, &Abstract3DController::handleAxisSegmentCountChanged);
1448         QObject::connect(valueAxis, &QValue3DAxis::subSegmentCountChanged,
1449                          this, &Abstract3DController::handleAxisSubSegmentCountChanged);
1450         QObject::connect(valueAxis, &QValue3DAxis::labelFormatChanged,
1451                          this, &Abstract3DController::handleAxisLabelFormatChanged);
1452         QObject::connect(valueAxis, &QValue3DAxis::reversedChanged,
1453                          this, &Abstract3DController::handleAxisReversedChanged);
1454         QObject::connect(valueAxis->dptr(), &QValue3DAxisPrivate::formatterDirty,
1455                          this, &Abstract3DController::handleAxisFormatterDirty);
1456 
1457         handleAxisSegmentCountChangedBySender(valueAxis);
1458         handleAxisSubSegmentCountChangedBySender(valueAxis);
1459         handleAxisLabelFormatChangedBySender(valueAxis);
1460         handleAxisReversedChangedBySender(valueAxis);
1461         handleAxisFormatterDirtyBySender(valueAxis->dptr());
1462 
1463         valueAxis->formatter()->setLocale(m_locale);
1464     }
1465 }
1466 
createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation)1467 QAbstract3DAxis *Abstract3DController::createDefaultAxis(
1468         QAbstract3DAxis::AxisOrientation orientation)
1469 {
1470     Q_UNUSED(orientation)
1471 
1472     // The default default axis is a value axis. If the graph type has a different default axis
1473     // for some orientation, this function needs to be overridden.
1474     QAbstract3DAxis *defaultAxis = createDefaultValueAxis();
1475     return defaultAxis;
1476 }
1477 
createDefaultValueAxis()1478 QValue3DAxis *Abstract3DController::createDefaultValueAxis()
1479 {
1480     // Default value axis has single segment, empty label format, and auto scaling
1481     QValue3DAxis *defaultAxis = new QValue3DAxis;
1482     defaultAxis->d_ptr->setDefaultAxis(true);
1483 
1484     return defaultAxis;
1485 }
1486 
createDefaultCategoryAxis()1487 QCategory3DAxis *Abstract3DController::createDefaultCategoryAxis()
1488 {
1489     // Default category axis has no labels
1490     QCategory3DAxis *defaultAxis = new QCategory3DAxis;
1491     defaultAxis->d_ptr->setDefaultAxis(true);
1492     return defaultAxis;
1493 }
1494 
startRecordingRemovesAndInserts()1495 void Abstract3DController::startRecordingRemovesAndInserts()
1496 {
1497     // Default implementation does nothing
1498 }
1499 
emitNeedRender()1500 void Abstract3DController::emitNeedRender()
1501 {
1502     if (!m_renderPending) {
1503         emit needRender();
1504         m_renderPending = true;
1505     }
1506 }
1507 
handlePendingClick()1508 void Abstract3DController::handlePendingClick()
1509 {
1510     m_clickedType = m_renderer->clickedType();
1511     m_selectedLabelIndex = m_renderer->m_selectedLabelIndex;
1512     m_selectedCustomItemIndex = m_renderer->m_selectedCustomItemIndex;
1513 
1514     // Invalidate query position to indicate the query has been handled, unless another
1515     // point has been queried.
1516     if (m_renderer->cachedClickQuery() == m_scene->selectionQueryPosition())
1517         m_scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint());
1518 
1519     m_renderer->clearClickQueryResolved();
1520 
1521     emit elementSelected(m_clickedType);
1522 }
1523 
handlePendingGraphPositionQuery()1524 void Abstract3DController::handlePendingGraphPositionQuery()
1525 {
1526     m_queriedGraphPosition = m_renderer->queriedGraphPosition();
1527 
1528     // Invalidate query position to indicate the query has been handled, unless another
1529     // point has been queried.
1530     if (m_renderer->cachedGraphPositionQuery() == m_scene->graphPositionQuery())
1531         m_scene->setGraphPositionQuery(Q3DScene::invalidSelectionPoint());
1532 
1533     m_renderer->clearGraphPositionQueryResolved();
1534 
1535     emit queriedGraphPositionChanged(m_queriedGraphPosition);
1536 }
1537 
selectedLabelIndex() const1538 int Abstract3DController::selectedLabelIndex() const
1539 {
1540     int index = m_selectedLabelIndex;
1541     QAbstract3DAxis *axis = selectedAxis();
1542     if (axis && axis->labels().count() <= index)
1543         index = -1;
1544     return index;
1545 }
1546 
selectedAxis() const1547 QAbstract3DAxis *Abstract3DController::selectedAxis() const
1548 {
1549     QAbstract3DAxis *axis = 0;
1550     QAbstract3DGraph::ElementType type = m_clickedType;
1551     switch (type) {
1552     case QAbstract3DGraph::ElementAxisXLabel:
1553         axis = axisX();
1554         break;
1555     case QAbstract3DGraph::ElementAxisYLabel:
1556         axis = axisY();
1557         break;
1558     case QAbstract3DGraph::ElementAxisZLabel:
1559         axis = axisZ();
1560         break;
1561     default:
1562         axis = 0;
1563         break;
1564     }
1565 
1566     return axis;
1567 }
1568 
selectedCustomItemIndex() const1569 int Abstract3DController::selectedCustomItemIndex() const
1570 {
1571     int index = m_selectedCustomItemIndex;
1572     if (m_customItems.count() <= index)
1573         index = -1;
1574     return index;
1575 }
1576 
selectedCustomItem() const1577 QCustom3DItem *Abstract3DController::selectedCustomItem() const
1578 {
1579     QCustom3DItem *item = 0;
1580     int index = selectedCustomItemIndex();
1581     if (index >= 0)
1582         item = m_customItems[index];
1583     return item;
1584 }
1585 
selectedElement() const1586 QAbstract3DGraph::ElementType Abstract3DController::selectedElement() const
1587 {
1588     return m_clickedType;
1589 }
1590 
setOrthoProjection(bool enable)1591 void Abstract3DController::setOrthoProjection(bool enable)
1592 {
1593     if (enable != m_useOrthoProjection) {
1594         m_useOrthoProjection = enable;
1595         m_changeTracker.projectionChanged = true;
1596         emit orthoProjectionChanged(m_useOrthoProjection);
1597         // If changed to ortho, disable shadows
1598         if (m_useOrthoProjection)
1599             doSetShadowQuality(QAbstract3DGraph::ShadowQualityNone);
1600         emitNeedRender();
1601     }
1602 }
1603 
isOrthoProjection() const1604 bool Abstract3DController::isOrthoProjection() const
1605 {
1606     return m_useOrthoProjection;
1607 }
1608 
setAspectRatio(qreal ratio)1609 void Abstract3DController::setAspectRatio(qreal ratio)
1610 {
1611     if (m_aspectRatio != ratio) {
1612         m_aspectRatio = ratio;
1613         m_changeTracker.aspectRatioChanged = true;
1614         emit aspectRatioChanged(m_aspectRatio);
1615         m_isDataDirty = true;
1616         emitNeedRender();
1617     }
1618 }
1619 
aspectRatio()1620 qreal Abstract3DController::aspectRatio()
1621 {
1622     return m_aspectRatio;
1623 }
1624 
setHorizontalAspectRatio(qreal ratio)1625 void Abstract3DController::setHorizontalAspectRatio(qreal ratio)
1626 {
1627     if (m_horizontalAspectRatio != ratio) {
1628         m_horizontalAspectRatio = ratio;
1629         m_changeTracker.horizontalAspectRatioChanged = true;
1630         emit horizontalAspectRatioChanged(m_horizontalAspectRatio);
1631         m_isDataDirty = true;
1632         emitNeedRender();
1633     }
1634 }
1635 
horizontalAspectRatio() const1636 qreal Abstract3DController::horizontalAspectRatio() const
1637 {
1638     return m_horizontalAspectRatio;
1639 }
1640 
setReflection(bool enable)1641 void Abstract3DController::setReflection(bool enable)
1642 {
1643     if (m_reflectionEnabled != enable) {
1644         m_reflectionEnabled = enable;
1645         m_changeTracker.reflectionChanged = true;
1646         emit reflectionChanged(m_reflectionEnabled);
1647         emitNeedRender();
1648     }
1649 }
1650 
reflection() const1651 bool Abstract3DController::reflection() const
1652 {
1653     return m_reflectionEnabled;
1654 }
1655 
setReflectivity(qreal reflectivity)1656 void Abstract3DController::setReflectivity(qreal reflectivity)
1657 {
1658     if (m_reflectivity != reflectivity) {
1659         m_reflectivity = reflectivity;
1660         m_changeTracker.reflectivityChanged = true;
1661         emit reflectivityChanged(m_reflectivity);
1662         emitNeedRender();
1663     }
1664 }
1665 
reflectivity() const1666 qreal Abstract3DController::reflectivity() const
1667 {
1668     return m_reflectivity;
1669 }
1670 
setPolar(bool enable)1671 void Abstract3DController::setPolar(bool enable)
1672 {
1673     if (enable != m_isPolar) {
1674         m_isPolar = enable;
1675         m_changeTracker.polarChanged = true;
1676         m_isDataDirty = true;
1677         emit polarChanged(m_isPolar);
1678         emitNeedRender();
1679     }
1680 }
1681 
isPolar() const1682 bool Abstract3DController::isPolar() const
1683 {
1684     return m_isPolar;
1685 }
1686 
setRadialLabelOffset(float offset)1687 void Abstract3DController::setRadialLabelOffset(float offset)
1688 {
1689     if (m_radialLabelOffset != offset) {
1690         m_radialLabelOffset = offset;
1691         m_changeTracker.radialLabelOffsetChanged = true;
1692         emit radialLabelOffsetChanged(m_radialLabelOffset);
1693         emitNeedRender();
1694     }
1695 }
1696 
radialLabelOffset() const1697 float Abstract3DController::radialLabelOffset() const
1698 {
1699     return m_radialLabelOffset;
1700 }
1701 
setLocale(const QLocale & locale)1702 void Abstract3DController::setLocale(const QLocale &locale)
1703 {
1704     if (m_locale != locale) {
1705         m_locale = locale;
1706 
1707         // Value axis formatters need to be updated
1708         QValue3DAxis *axis = qobject_cast<QValue3DAxis *>(m_axisX);
1709         if (axis)
1710             axis->formatter()->setLocale(m_locale);
1711         axis = qobject_cast<QValue3DAxis *>(m_axisY);
1712         if (axis)
1713             axis->formatter()->setLocale(m_locale);
1714         axis = qobject_cast<QValue3DAxis *>(m_axisZ);
1715         if (axis)
1716             axis->formatter()->setLocale(m_locale);
1717         emit localeChanged(m_locale);
1718     }
1719 }
1720 
locale() const1721 QLocale Abstract3DController::locale() const
1722 {
1723     return m_locale;
1724 }
1725 
queriedGraphPosition() const1726 QVector3D Abstract3DController::queriedGraphPosition() const
1727 {
1728     return m_queriedGraphPosition;
1729 }
1730 
setMargin(qreal margin)1731 void Abstract3DController::setMargin(qreal margin)
1732 {
1733     if (m_margin != margin) {
1734         m_margin = margin;
1735         m_changeTracker.marginChanged = true;
1736         emit marginChanged(margin);
1737         emitNeedRender();
1738     }
1739 }
1740 
margin() const1741 qreal Abstract3DController::margin() const
1742 {
1743     return m_margin;
1744 }
1745 
1746 
1747 QT_END_NAMESPACE_DATAVISUALIZATION
1748