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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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