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