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
44QT_BEGIN_NAMESPACE_DATAVISUALIZATION
45
46Abstract3DController::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(theme: 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(sender: m_scene->d_ptr.data(), signal: &Q3DScenePrivate::needRender, receiver: this,
98 slot: &Abstract3DController::emitNeedRender);
99}
100
101Abstract3DController::~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
111void 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 */
126void 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(sender: renderer->thread(), signal: &QThread::finished, receiver: this,
135 slot: &Abstract3DController::destroyRenderer, type: Qt::DirectConnection);
136 }
137}
138
139void Abstract3DController::addSeries(QAbstract3DSeries *series)
140{
141 insertSeries(index: m_seriesList.size(), series);
142}
143
144void Abstract3DController::insertSeries(int index, QAbstract3DSeries *series)
145{
146 if (series) {
147 if (m_seriesList.contains(t: series)) {
148 int oldIndex = m_seriesList.indexOf(t: series);
149 if (index != oldIndex) {
150 m_seriesList.removeOne(t: series);
151 if (oldIndex < index)
152 index--;
153 m_seriesList.insert(i: index, t: series);
154 }
155 } else {
156 int oldSize = m_seriesList.size();
157 m_seriesList.insert(i: index, t: series);
158 series->d_ptr->setController(this);
159 QObject::connect(sender: series, signal: &QAbstract3DSeries::visibilityChanged,
160 receiver: this, slot: &Abstract3DController::handleSeriesVisibilityChanged);
161 series->d_ptr->resetToTheme(theme: *m_themeManager->activeTheme(), seriesIndex: oldSize, force: false);
162 }
163 if (series->isVisible())
164 handleSeriesVisibilityChangedBySender(sender: series);
165 }
166}
167
168void Abstract3DController::removeSeries(QAbstract3DSeries *series)
169{
170 if (series && series->d_ptr->m_controller == this) {
171 m_seriesList.removeAll(t: series);
172 QObject::disconnect(sender: series, signal: &QAbstract3DSeries::visibilityChanged,
173 receiver: this, slot: &Abstract3DController::handleSeriesVisibilityChanged);
174 series->d_ptr->setController(0);
175 m_isDataDirty = true;
176 m_isSeriesVisualsDirty = true;
177 emitNeedRender();
178 }
179}
180
181QList<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 */
189void 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(scene: m_scene);
206
207 m_renderer->updateTheme(theme: m_themeManager->activeTheme());
208
209 if (m_changeTracker.polarChanged) {
210 m_renderer->updatePolar(enable: m_isPolar);
211 m_changeTracker.polarChanged = false;
212 }
213
214 if (m_changeTracker.radialLabelOffsetChanged) {
215 m_renderer->updateRadialLabelOffset(offset: m_radialLabelOffset);
216 m_changeTracker.radialLabelOffsetChanged = false;
217 }
218
219 if (m_changeTracker.shadowQualityChanged) {
220 m_renderer->updateShadowQuality(quality: m_shadowQuality);
221 m_changeTracker.shadowQualityChanged = false;
222 }
223
224 if (m_changeTracker.selectionModeChanged) {
225 m_renderer->updateSelectionMode(newMode: 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(ratio: float(m_aspectRatio));
236 m_changeTracker.aspectRatioChanged = false;
237 }
238
239 if (m_changeTracker.horizontalAspectRatioChanged) {
240 m_renderer->updateHorizontalAspectRatio(ratio: float(m_horizontalAspectRatio));
241 m_changeTracker.horizontalAspectRatioChanged = false;
242 }
243
244 if (m_changeTracker.optimizationHintChanged) {
245 m_renderer->updateOptimizationHint(hint: 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(orientation: QAbstract3DAxis::AxisOrientationX,
265 formatter: 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(orientation: QAbstract3DAxis::AxisOrientationY,
273 formatter: 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(orientation: QAbstract3DAxis::AxisOrientationZ,
281 formatter: valueAxisZ->formatter());
282 }
283 }
284
285 if (m_changeTracker.axisXTypeChanged) {
286 m_renderer->updateAxisType(orientation: QAbstract3DAxis::AxisOrientationX, type: m_axisX->type());
287 m_changeTracker.axisXTypeChanged = false;
288 }
289
290 if (m_changeTracker.axisYTypeChanged) {
291 m_renderer->updateAxisType(orientation: QAbstract3DAxis::AxisOrientationY, type: m_axisY->type());
292 m_changeTracker.axisYTypeChanged = false;
293 }
294
295 if (m_changeTracker.axisZTypeChanged) {
296 m_renderer->updateAxisType(orientation: QAbstract3DAxis::AxisOrientationZ, type: m_axisZ->type());
297 m_changeTracker.axisZTypeChanged = false;
298 }
299
300 if (m_changeTracker.axisXTitleChanged) {
301 m_renderer->updateAxisTitle(orientation: QAbstract3DAxis::AxisOrientationX, title: m_axisX->title());
302 m_changeTracker.axisXTitleChanged = false;
303 }
304
305 if (m_changeTracker.axisYTitleChanged) {
306 m_renderer->updateAxisTitle(orientation: QAbstract3DAxis::AxisOrientationY, title: m_axisY->title());
307 m_changeTracker.axisYTitleChanged = false;
308 }
309
310 if (m_changeTracker.axisZTitleChanged) {
311 m_renderer->updateAxisTitle(orientation: QAbstract3DAxis::AxisOrientationZ, title: m_axisZ->title());
312 m_changeTracker.axisZTitleChanged = false;
313 }
314
315 if (m_changeTracker.axisXLabelsChanged) {
316 m_renderer->updateAxisLabels(orientation: QAbstract3DAxis::AxisOrientationX, labels: m_axisX->labels());
317 m_changeTracker.axisXLabelsChanged = false;
318 }
319
320 if (m_changeTracker.axisYLabelsChanged) {
321 m_renderer->updateAxisLabels(orientation: QAbstract3DAxis::AxisOrientationY, labels: m_axisY->labels());
322 m_changeTracker.axisYLabelsChanged = false;
323 }
324 if (m_changeTracker.axisZLabelsChanged) {
325 m_renderer->updateAxisLabels(orientation: QAbstract3DAxis::AxisOrientationZ, labels: m_axisZ->labels());
326 m_changeTracker.axisZLabelsChanged = false;
327 }
328
329 if (m_changeTracker.axisXRangeChanged) {
330 m_renderer->updateAxisRange(orientation: QAbstract3DAxis::AxisOrientationX, min: m_axisX->min(),
331 max: m_axisX->max());
332 m_changeTracker.axisXRangeChanged = false;
333 }
334
335 if (m_changeTracker.axisYRangeChanged) {
336 m_renderer->updateAxisRange(orientation: QAbstract3DAxis::AxisOrientationY, min: m_axisY->min(),
337 max: m_axisY->max());
338 m_changeTracker.axisYRangeChanged = false;
339 }
340
341 if (m_changeTracker.axisZRangeChanged) {
342 m_renderer->updateAxisRange(orientation: QAbstract3DAxis::AxisOrientationZ, min: m_axisZ->min(),
343 max: 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(orientation: QAbstract3DAxis::AxisOrientationX,
352 count: 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(orientation: QAbstract3DAxis::AxisOrientationY,
361 count: 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(orientation: QAbstract3DAxis::AxisOrientationZ,
370 count: 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(orientation: QAbstract3DAxis::AxisOrientationX,
379 count: 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(orientation: QAbstract3DAxis::AxisOrientationY,
388 count: 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(orientation: QAbstract3DAxis::AxisOrientationZ,
397 count: 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(orientation: QAbstract3DAxis::AxisOrientationX,
406 format: 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(orientation: QAbstract3DAxis::AxisOrientationY,
415 format: 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(orientation: QAbstract3DAxis::AxisOrientationZ,
424 format: 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(orientation: QAbstract3DAxis::AxisOrientationX,
433 enable: 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(orientation: QAbstract3DAxis::AxisOrientationY,
442 enable: 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(orientation: QAbstract3DAxis::AxisOrientationZ,
451 enable: valueAxisZ->reversed());
452 }
453 }
454
455 if (m_changeTracker.axisXLabelAutoRotationChanged) {
456 m_renderer->updateAxisLabelAutoRotation(orientation: QAbstract3DAxis::AxisOrientationX,
457 angle: m_axisX->labelAutoRotation());
458 m_changeTracker.axisXLabelAutoRotationChanged = false;
459 }
460
461 if (m_changeTracker.axisYLabelAutoRotationChanged) {
462 m_renderer->updateAxisLabelAutoRotation(orientation: QAbstract3DAxis::AxisOrientationY,
463 angle: m_axisY->labelAutoRotation());
464 m_changeTracker.axisYLabelAutoRotationChanged = false;
465 }
466
467 if (m_changeTracker.axisZLabelAutoRotationChanged) {
468 m_renderer->updateAxisLabelAutoRotation(orientation: QAbstract3DAxis::AxisOrientationZ,
469 angle: m_axisZ->labelAutoRotation());
470 m_changeTracker.axisZLabelAutoRotationChanged = false;
471 }
472
473 if (m_changeTracker.axisXTitleVisibilityChanged) {
474 m_renderer->updateAxisTitleVisibility(orientation: QAbstract3DAxis::AxisOrientationX,
475 visible: m_axisX->isTitleVisible());
476 m_changeTracker.axisXTitleVisibilityChanged = false;
477 }
478 if (m_changeTracker.axisYTitleVisibilityChanged) {
479 m_renderer->updateAxisTitleVisibility(orientation: QAbstract3DAxis::AxisOrientationY,
480 visible: m_axisY->isTitleVisible());
481 m_changeTracker.axisYTitleVisibilityChanged = false;
482 }
483 if (m_changeTracker.axisZTitleVisibilityChanged) {
484 m_renderer->updateAxisTitleVisibility(orientation: QAbstract3DAxis::AxisOrientationZ,
485 visible: m_axisZ->isTitleVisible());
486 m_changeTracker.axisZTitleVisibilityChanged = false;
487 }
488 if (m_changeTracker.axisXTitleFixedChanged) {
489 m_renderer->updateAxisTitleFixed(orientation: QAbstract3DAxis::AxisOrientationX,
490 fixed: m_axisX->isTitleFixed());
491 m_changeTracker.axisXTitleFixedChanged = false;
492 }
493 if (m_changeTracker.axisYTitleFixedChanged) {
494 m_renderer->updateAxisTitleFixed(orientation: QAbstract3DAxis::AxisOrientationY,
495 fixed: m_axisY->isTitleFixed());
496 m_changeTracker.axisYTitleFixedChanged = false;
497 }
498 if (m_changeTracker.axisZTitleFixedChanged) {
499 m_renderer->updateAxisTitleFixed(orientation: QAbstract3DAxis::AxisOrientationZ,
500 fixed: m_axisZ->isTitleFixed());
501 m_changeTracker.axisZTitleFixedChanged = false;
502 }
503
504 if (m_changeTracker.marginChanged) {
505 m_renderer->updateMargin(margin: float(m_margin));
506 m_changeTracker.marginChanged = false;
507 }
508
509 if (m_changedSeriesList.size()) {
510 m_renderer->modifiedSeriesList(seriesList: m_changedSeriesList);
511 m_changedSeriesList.clear();
512 }
513
514 if (m_isSeriesVisualsDirty) {
515 m_renderer->updateSeries(seriesList: 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(customItems: m_customItems);
528 m_isCustomDataDirty = false;
529 }
530
531 if (m_isCustomItemDirty) {
532 m_renderer->updateCustomItems();
533 m_isCustomItemDirty = false;
534 }
535}
536
537void 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(fps: 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
562void Abstract3DController::mouseDoubleClickEvent(QMouseEvent *event)
563{
564 if (m_activeInputHandler)
565 m_activeInputHandler->mouseDoubleClickEvent(event);
566}
567
568void Abstract3DController::touchEvent(QTouchEvent *event)
569{
570 if (m_activeInputHandler)
571 m_activeInputHandler->touchEvent(event);
572}
573
574void Abstract3DController::mousePressEvent(QMouseEvent *event, const QPoint &mousePos)
575{
576 if (m_activeInputHandler)
577 m_activeInputHandler->mousePressEvent(event, mousePos);
578}
579
580void Abstract3DController::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos)
581{
582 if (m_activeInputHandler)
583 m_activeInputHandler->mouseReleaseEvent(event, mousePos);
584}
585
586void 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)
593void Abstract3DController::wheelEvent(QWheelEvent *event)
594{
595 if (m_activeInputHandler)
596 m_activeInputHandler->wheelEvent(event);
597}
598#endif
599
600void 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
612void 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(i: colorIdx));
619 series->d_ptr->m_themeTracker.baseColorOverride = false;
620 }
621 if (++colorIdx >= colors.size())
622 colorIdx = 0;
623 }
624 markSeriesVisualsDirty();
625}
626
627void 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(i: gradientIdx));
634 series->d_ptr->m_themeTracker.baseGradientOverride = false;
635 }
636 if (++gradientIdx >= gradients.size())
637 gradientIdx = 0;
638 }
639 markSeriesVisualsDirty();
640}
641
642void 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
654void 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
667void 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
679void 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
691void 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(theme: *activeTheme, seriesIndex: i, force: true);
701 markSeriesVisualsDirty();
702}
703
704void Abstract3DController::setAxisX(QAbstract3DAxis *axis)
705{
706 // Setting null axis will always create new default axis
707 if (!axis || axis != m_axisX) {
708 setAxisHelper(orientation: QAbstract3DAxis::AxisOrientationX, axis, axisPtr: &m_axisX);
709 emit axisXChanged(axis: m_axisX);
710 }
711}
712
713QAbstract3DAxis *Abstract3DController::axisX() const
714{
715 return m_axisX;
716}
717
718void Abstract3DController::setAxisY(QAbstract3DAxis *axis)
719{
720 // Setting null axis will always create new default axis
721 if (!axis || axis != m_axisY) {
722 setAxisHelper(orientation: QAbstract3DAxis::AxisOrientationY, axis, axisPtr: &m_axisY);
723 emit axisYChanged(axis: m_axisY);
724 }
725}
726
727QAbstract3DAxis *Abstract3DController::axisY() const
728{
729 return m_axisY;
730}
731
732void Abstract3DController::setAxisZ(QAbstract3DAxis *axis)
733{
734 // Setting null axis will always create new default axis
735 if (!axis || axis != m_axisZ) {
736 setAxisHelper(orientation: QAbstract3DAxis::AxisOrientationZ, axis, axisPtr: &m_axisZ);
737 emit axisZChanged(axis: m_axisZ);
738 }
739}
740
741QAbstract3DAxis *Abstract3DController::axisZ() const
742{
743 return m_axisZ;
744}
745
746void Abstract3DController::addAxis(QAbstract3DAxis *axis)
747{
748 Q_ASSERT(axis);
749 Abstract3DController *owner = qobject_cast<Abstract3DController *>(object: 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(t: axis))
755 m_axes.append(t: axis);
756}
757
758void Abstract3DController::releaseAxis(QAbstract3DAxis *axis)
759{
760 if (axis && m_axes.contains(t: 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(t: axis);
781 axis->setParent(0);
782 }
783}
784
785QList<QAbstract3DAxis *> Abstract3DController::axes() const
786{
787 return m_axes;
788}
789
790void Abstract3DController::addInputHandler(QAbstract3DInputHandler *inputHandler)
791{
792 Q_ASSERT(inputHandler);
793 Abstract3DController *owner = qobject_cast<Abstract3DController *>(object: 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(t: inputHandler))
801 m_inputHandlers.append(t: inputHandler);
802}
803
804void Abstract3DController::releaseInputHandler(QAbstract3DInputHandler *inputHandler)
805{
806 if (inputHandler && m_inputHandlers.contains(t: 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(t: inputHandler);
816 inputHandler->setParent(0);
817 }
818}
819
820void 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(t: m_activeInputHandler);
829 delete m_activeInputHandler;
830 } else {
831 // Disconnect the old input handler
832 m_activeInputHandler->setScene(0);
833 QObject::disconnect(sender: m_activeInputHandler, signal: 0, receiver: this, member: 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(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::inputViewChanged, receiver: this,
847 slot: &Abstract3DController::handleInputViewChanged);
848 QObject::connect(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::positionChanged, receiver: this,
849 slot: &Abstract3DController::handleInputPositionChanged);
850 }
851
852 // Notify change of input handler
853 emit activeInputHandlerChanged(inputHandler: m_activeInputHandler);
854}
855
856QAbstract3DInputHandler* Abstract3DController::activeInputHandler()
857{
858 return m_activeInputHandler;
859}
860
861QList<QAbstract3DInputHandler *> Abstract3DController::inputHandlers() const
862{
863 return m_inputHandlers;
864}
865
866void Abstract3DController::addTheme(Q3DTheme *theme)
867{
868 m_themeManager->addTheme(theme);
869}
870
871void 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(activeTheme: m_themeManager->activeTheme());
879}
880
881QList<Q3DTheme *> Abstract3DController::themes() const
882{
883 return m_themeManager->themes();
884}
885
886void 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(theme: *newActiveTheme, seriesIndex: i, force);
896 markSeriesVisualsDirty();
897 emit activeThemeChanged(activeTheme: newActiveTheme);
898 }
899}
900
901Q3DTheme *Abstract3DController::activeTheme() const
902{
903 return m_themeManager->activeTheme();
904}
905
906void 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
916QAbstract3DGraph::SelectionFlags Abstract3DController::selectionMode() const
917{
918 return m_selectionMode;
919}
920
921void Abstract3DController::setShadowQuality(QAbstract3DGraph::ShadowQuality quality)
922{
923 if (!m_useOrthoProjection)
924 doSetShadowQuality(quality);
925}
926
927void Abstract3DController::doSetShadowQuality(QAbstract3DGraph::ShadowQuality quality)
928{
929 if (quality != m_shadowQuality) {
930 m_shadowQuality = quality;
931 m_changeTracker.shadowQualityChanged = true;
932 emit shadowQualityChanged(quality: m_shadowQuality);
933 emitNeedRender();
934 }
935}
936
937QAbstract3DGraph::ShadowQuality Abstract3DController::shadowQuality() const
938{
939 return m_shadowQuality;
940}
941
942void 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
953QAbstract3DGraph::OptimizationHints Abstract3DController::optimizationHints() const
954{
955 return m_optimizationHints;
956}
957
958bool Abstract3DController::shadowsSupported() const
959{
960 return !isOpenGLES();
961}
962
963bool Abstract3DController::isSlicingActive() const
964{
965 return m_scene->isSlicingActive();
966}
967
968void Abstract3DController::setSlicingActive(bool isSlicing)
969{
970 m_scene->setSlicingActive(isSlicing);
971}
972
973Q3DScene *Abstract3DController::scene()
974{
975 return m_scene;
976}
977
978void Abstract3DController::markDataDirty()
979{
980 m_isDataDirty = true;
981
982 markSeriesItemLabelsDirty();
983 emitNeedRender();
984}
985
986void Abstract3DController::markSeriesVisualsDirty()
987{
988 m_isSeriesVisualsDirty = true;
989 emitNeedRender();
990}
991
992void Abstract3DController::requestRender(QOpenGLFramebufferObject *fbo)
993{
994 QMutexLocker mutexLocker(&m_renderMutex);
995 m_renderer->render(defaultFboHandle: fbo->handle());
996}
997
998int Abstract3DController::addCustomItem(QCustom3DItem *item)
999{
1000 if (!item)
1001 return -1;
1002
1003 int index = m_customItems.indexOf(t: item);
1004
1005 if (index != -1)
1006 return index;
1007
1008 item->setParent(this);
1009 connect(sender: item->d_ptr.data(), signal: &QCustom3DItemPrivate::needUpdate,
1010 receiver: this, slot: &Abstract3DController::updateCustomItem);
1011 m_customItems.append(t: item);
1012 item->d_ptr->resetDirtyBits();
1013 m_isCustomDataDirty = true;
1014 emitNeedRender();
1015 return m_customItems.count() - 1;
1016}
1017
1018void Abstract3DController::deleteCustomItems()
1019{
1020 foreach (QCustom3DItem *item, m_customItems)
1021 delete item;
1022 m_customItems.clear();
1023 m_isCustomDataDirty = true;
1024 emitNeedRender();
1025}
1026
1027void Abstract3DController::deleteCustomItem(QCustom3DItem *item)
1028{
1029 if (!item)
1030 return;
1031
1032 m_customItems.removeOne(t: item);
1033 delete item;
1034 item = 0;
1035 m_isCustomDataDirty = true;
1036 emitNeedRender();
1037}
1038
1039void 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
1048void Abstract3DController::releaseCustomItem(QCustom3DItem *item)
1049{
1050 if (item && m_customItems.contains(t: item)) {
1051 disconnect(sender: item->d_ptr.data(), signal: &QCustom3DItemPrivate::needUpdate,
1052 receiver: this, slot: &Abstract3DController::updateCustomItem);
1053 m_customItems.removeOne(t: item);
1054 item->setParent(0);
1055 m_isCustomDataDirty = true;
1056 emitNeedRender();
1057 }
1058}
1059
1060QList<QCustom3DItem *> Abstract3DController::customItems() const
1061{
1062 return m_customItems;
1063}
1064
1065void Abstract3DController::updateCustomItem()
1066{
1067 m_isCustomItemDirty = true;
1068 emitNeedRender();
1069}
1070
1071void Abstract3DController::handleAxisTitleChanged(const QString &title)
1072{
1073 Q_UNUSED(title)
1074 handleAxisTitleChangedBySender(sender: sender());
1075}
1076
1077void 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
1092void Abstract3DController::handleAxisLabelsChanged()
1093{
1094 handleAxisLabelsChangedBySender(sender: sender());
1095}
1096
1097void 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
1112void Abstract3DController::handleAxisRangeChanged(float min, float max)
1113{
1114 Q_UNUSED(min)
1115 Q_UNUSED(max)
1116 handleAxisRangeChangedBySender(sender: sender());
1117}
1118
1119void 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
1136void Abstract3DController::handleAxisSegmentCountChanged(int count)
1137{
1138 Q_UNUSED(count)
1139 handleAxisSegmentCountChangedBySender(sender: sender());
1140}
1141
1142void 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
1155void Abstract3DController::handleAxisSubSegmentCountChanged(int count)
1156{
1157 Q_UNUSED(count)
1158 handleAxisSubSegmentCountChangedBySender(sender: sender());
1159}
1160
1161void 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
1174void 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(orientation: axis->orientation(), autoAdjust);
1182}
1183
1184void Abstract3DController::handleAxisLabelFormatChanged(const QString &format)
1185{
1186 Q_UNUSED(format)
1187 handleAxisLabelFormatChangedBySender(sender: sender());
1188}
1189
1190void Abstract3DController::handleAxisReversedChanged(bool enable)
1191{
1192 Q_UNUSED(enable)
1193 handleAxisReversedChangedBySender(sender: sender());
1194}
1195
1196void Abstract3DController::handleAxisFormatterDirty()
1197{
1198 handleAxisFormatterDirtyBySender(sender: sender());
1199}
1200
1201void Abstract3DController::handleAxisLabelAutoRotationChanged(float angle)
1202{
1203 Q_UNUSED(angle)
1204 handleAxisLabelAutoRotationChangedBySender(sender: sender());
1205}
1206
1207void Abstract3DController::handleAxisTitleVisibilityChanged(bool visible)
1208{
1209 Q_UNUSED(visible)
1210 handleAxisTitleVisibilityChangedBySender(sender: sender());
1211}
1212
1213void Abstract3DController::handleAxisTitleFixedChanged(bool fixed)
1214{
1215 Q_UNUSED(fixed)
1216 handleAxisTitleFixedChangedBySender(sender: sender());
1217}
1218
1219void 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(flag: QAbstract3DGraph::SelectionSlice)
1223 && view == QAbstract3DInputHandler::InputViewOnPrimary) {
1224 setSlicingActive(false);
1225 }
1226
1227 emitNeedRender();
1228}
1229
1230void Abstract3DController::handleInputPositionChanged(const QPoint &position)
1231{
1232 Q_UNUSED(position)
1233 emitNeedRender();
1234}
1235
1236void Abstract3DController::handleSeriesVisibilityChanged(bool visible)
1237{
1238 Q_UNUSED(visible)
1239
1240 handleSeriesVisibilityChangedBySender(sender: sender());
1241}
1242
1243void Abstract3DController::handleRequestShadowQuality(QAbstract3DGraph::ShadowQuality quality)
1244{
1245 setShadowQuality(quality);
1246}
1247
1248void 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(enabled: enable);
1260 }
1261}
1262
1263void 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
1281void 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
1299void 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
1318void 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
1332void 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
1346void 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
1360void 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
1373void Abstract3DController::markSeriesItemLabelsDirty()
1374{
1375 for (int i = 0; i < m_seriesList.size(); i++)
1376 m_seriesList.at(i)->d_ptr->markItemLabelDirty();
1377}
1378
1379bool Abstract3DController::isOpenGLES() const
1380{
1381 return Utils::isOpenGLES();
1382}
1383
1384void 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(t: oldAxis);
1396 delete oldAxis;
1397 oldAxis = 0;
1398 } else {
1399 // Disconnect the old axis from use
1400 QObject::disconnect(sender: oldAxis, signal: 0, receiver: this, member: 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(sender: axis, signal: &QAbstract3DAxis::titleChanged,
1414 receiver: this, slot: &Abstract3DController::handleAxisTitleChanged);
1415 QObject::connect(sender: axis, signal: &QAbstract3DAxis::labelsChanged,
1416 receiver: this, slot: &Abstract3DController::handleAxisLabelsChanged);
1417 QObject::connect(sender: axis, signal: &QAbstract3DAxis::rangeChanged,
1418 receiver: this, slot: &Abstract3DController::handleAxisRangeChanged);
1419 QObject::connect(sender: axis, signal: &QAbstract3DAxis::autoAdjustRangeChanged,
1420 receiver: this, slot: &Abstract3DController::handleAxisAutoAdjustRangeChanged);
1421 QObject::connect(sender: axis, signal: &QAbstract3DAxis::labelAutoRotationChanged,
1422 receiver: this, slot: &Abstract3DController::handleAxisLabelAutoRotationChanged);
1423 QObject::connect(sender: axis, signal: &QAbstract3DAxis::titleVisibilityChanged,
1424 receiver: this, slot: &Abstract3DController::handleAxisTitleVisibilityChanged);
1425 QObject::connect(sender: axis, signal: &QAbstract3DAxis::titleFixedChanged,
1426 receiver: this, slot: &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(sender: axis);
1436 handleAxisLabelsChangedBySender(sender: axis);
1437 handleAxisRangeChangedBySender(sender: axis);
1438 handleAxisAutoAdjustRangeChangedInOrientation(orientation: axis->orientation(),
1439 autoAdjust: axis->isAutoAdjustRange());
1440 handleAxisLabelAutoRotationChangedBySender(sender: axis);
1441 handleAxisTitleVisibilityChangedBySender(sender: axis);
1442 handleAxisTitleFixedChangedBySender(sender: axis);
1443
1444 if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
1445 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
1446 QObject::connect(sender: valueAxis, signal: &QValue3DAxis::segmentCountChanged,
1447 receiver: this, slot: &Abstract3DController::handleAxisSegmentCountChanged);
1448 QObject::connect(sender: valueAxis, signal: &QValue3DAxis::subSegmentCountChanged,
1449 receiver: this, slot: &Abstract3DController::handleAxisSubSegmentCountChanged);
1450 QObject::connect(sender: valueAxis, signal: &QValue3DAxis::labelFormatChanged,
1451 receiver: this, slot: &Abstract3DController::handleAxisLabelFormatChanged);
1452 QObject::connect(sender: valueAxis, signal: &QValue3DAxis::reversedChanged,
1453 receiver: this, slot: &Abstract3DController::handleAxisReversedChanged);
1454 QObject::connect(sender: valueAxis->dptr(), signal: &QValue3DAxisPrivate::formatterDirty,
1455 receiver: this, slot: &Abstract3DController::handleAxisFormatterDirty);
1456
1457 handleAxisSegmentCountChangedBySender(sender: valueAxis);
1458 handleAxisSubSegmentCountChangedBySender(sender: valueAxis);
1459 handleAxisLabelFormatChangedBySender(sender: valueAxis);
1460 handleAxisReversedChangedBySender(sender: valueAxis);
1461 handleAxisFormatterDirtyBySender(sender: valueAxis->dptr());
1462
1463 valueAxis->formatter()->setLocale(m_locale);
1464 }
1465}
1466
1467QAbstract3DAxis *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
1478QValue3DAxis *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
1487QCategory3DAxis *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
1495void Abstract3DController::startRecordingRemovesAndInserts()
1496{
1497 // Default implementation does nothing
1498}
1499
1500void Abstract3DController::emitNeedRender()
1501{
1502 if (!m_renderPending) {
1503 emit needRender();
1504 m_renderPending = true;
1505 }
1506}
1507
1508void 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(type: m_clickedType);
1522}
1523
1524void 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(data: m_queriedGraphPosition);
1536}
1537
1538int 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
1547QAbstract3DAxis *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
1569int Abstract3DController::selectedCustomItemIndex() const
1570{
1571 int index = m_selectedCustomItemIndex;
1572 if (m_customItems.count() <= index)
1573 index = -1;
1574 return index;
1575}
1576
1577QCustom3DItem *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
1586QAbstract3DGraph::ElementType Abstract3DController::selectedElement() const
1587{
1588 return m_clickedType;
1589}
1590
1591void Abstract3DController::setOrthoProjection(bool enable)
1592{
1593 if (enable != m_useOrthoProjection) {
1594 m_useOrthoProjection = enable;
1595 m_changeTracker.projectionChanged = true;
1596 emit orthoProjectionChanged(enabled: m_useOrthoProjection);
1597 // If changed to ortho, disable shadows
1598 if (m_useOrthoProjection)
1599 doSetShadowQuality(quality: QAbstract3DGraph::ShadowQualityNone);
1600 emitNeedRender();
1601 }
1602}
1603
1604bool Abstract3DController::isOrthoProjection() const
1605{
1606 return m_useOrthoProjection;
1607}
1608
1609void Abstract3DController::setAspectRatio(qreal ratio)
1610{
1611 if (m_aspectRatio != ratio) {
1612 m_aspectRatio = ratio;
1613 m_changeTracker.aspectRatioChanged = true;
1614 emit aspectRatioChanged(ratio: m_aspectRatio);
1615 m_isDataDirty = true;
1616 emitNeedRender();
1617 }
1618}
1619
1620qreal Abstract3DController::aspectRatio()
1621{
1622 return m_aspectRatio;
1623}
1624
1625void Abstract3DController::setHorizontalAspectRatio(qreal ratio)
1626{
1627 if (m_horizontalAspectRatio != ratio) {
1628 m_horizontalAspectRatio = ratio;
1629 m_changeTracker.horizontalAspectRatioChanged = true;
1630 emit horizontalAspectRatioChanged(ratio: m_horizontalAspectRatio);
1631 m_isDataDirty = true;
1632 emitNeedRender();
1633 }
1634}
1635
1636qreal Abstract3DController::horizontalAspectRatio() const
1637{
1638 return m_horizontalAspectRatio;
1639}
1640
1641void Abstract3DController::setReflection(bool enable)
1642{
1643 if (m_reflectionEnabled != enable) {
1644 m_reflectionEnabled = enable;
1645 m_changeTracker.reflectionChanged = true;
1646 emit reflectionChanged(enabled: m_reflectionEnabled);
1647 emitNeedRender();
1648 }
1649}
1650
1651bool Abstract3DController::reflection() const
1652{
1653 return m_reflectionEnabled;
1654}
1655
1656void Abstract3DController::setReflectivity(qreal reflectivity)
1657{
1658 if (m_reflectivity != reflectivity) {
1659 m_reflectivity = reflectivity;
1660 m_changeTracker.reflectivityChanged = true;
1661 emit reflectivityChanged(reflectivity: m_reflectivity);
1662 emitNeedRender();
1663 }
1664}
1665
1666qreal Abstract3DController::reflectivity() const
1667{
1668 return m_reflectivity;
1669}
1670
1671void 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(enabled: m_isPolar);
1678 emitNeedRender();
1679 }
1680}
1681
1682bool Abstract3DController::isPolar() const
1683{
1684 return m_isPolar;
1685}
1686
1687void Abstract3DController::setRadialLabelOffset(float offset)
1688{
1689 if (m_radialLabelOffset != offset) {
1690 m_radialLabelOffset = offset;
1691 m_changeTracker.radialLabelOffsetChanged = true;
1692 emit radialLabelOffsetChanged(offset: m_radialLabelOffset);
1693 emitNeedRender();
1694 }
1695}
1696
1697float Abstract3DController::radialLabelOffset() const
1698{
1699 return m_radialLabelOffset;
1700}
1701
1702void 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 *>(object: m_axisX);
1709 if (axis)
1710 axis->formatter()->setLocale(m_locale);
1711 axis = qobject_cast<QValue3DAxis *>(object: m_axisY);
1712 if (axis)
1713 axis->formatter()->setLocale(m_locale);
1714 axis = qobject_cast<QValue3DAxis *>(object: m_axisZ);
1715 if (axis)
1716 axis->formatter()->setLocale(m_locale);
1717 emit localeChanged(locale: m_locale);
1718 }
1719}
1720
1721QLocale Abstract3DController::locale() const
1722{
1723 return m_locale;
1724}
1725
1726QVector3D Abstract3DController::queriedGraphPosition() const
1727{
1728 return m_queriedGraphPosition;
1729}
1730
1731void 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
1741qreal Abstract3DController::margin() const
1742{
1743 return m_margin;
1744}
1745
1746
1747QT_END_NAMESPACE_DATAVISUALIZATION
1748

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