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

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