1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qabstract3dinputhandler_p.h"
5#include "qquickgraphsitem_p.h"
6
7#include "abstract3dcontroller_p.h"
8#include "declarativetheme_p.h"
9#include "declarativescene_p.h"
10#include "q3dscene_p.h"
11#include "qcustom3dlabel.h"
12#include "qcustom3ditem.h"
13#include "qcustom3ditem_p.h"
14#include "qtouch3dinputhandler.h"
15#include "qvalue3daxis.h"
16#include "qcategory3daxis.h"
17#include "utils_p.h"
18#include "qcustom3dvolume.h"
19
20#include <QtGui/QGuiApplication>
21
22#include <QtQuick3D/private/qquick3dperspectivecamera_p.h>
23#include <QtQuick3D/private/qquick3dorthographiccamera_p.h>
24#include <QtQuick3D/private/qquick3dcustommaterial_p.h>
25#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
26#include <QtQuick3D/private/qquick3ddirectionallight_p.h>
27#include <QtQuick3D/private/qquick3drepeater_p.h>
28#include <QtQuick3D/private/qquick3dloader_p.h>
29#include <QtQuick/private/qquickitem_p.h>
30
31#if defined(Q_OS_IOS)
32#include <QtCore/QTimer>
33#endif
34
35#if defined(Q_OS_MACOS)
36#include <qpa/qplatformnativeinterface.h>
37#endif
38
39QT_BEGIN_NAMESPACE
40
41QQuickGraphsItem::QQuickGraphsItem(QQuickItem *parent) :
42 QQuick3DViewport(parent),
43 m_controller(nullptr)
44{
45 m_nodeMutex = QSharedPointer<QMutex>::create();
46
47 QQuick3DSceneEnvironment *scene = environment();
48 scene->setBackgroundMode(QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes::Color);
49 scene->setClearColor(Qt::transparent);
50
51 auto sceneManager = QQuick3DObjectPrivate::get(item: rootNode())->sceneManager;
52 connect(sender: sceneManager.data(), signal: &QQuick3DSceneManager::windowChanged, context: this, slot: &QQuickGraphsItem::handleWindowChanged);
53 // Set contents to false in case we are in qml designer to make component look nice
54 m_runningInDesigner = QGuiApplication::applicationDisplayName() == QLatin1String("Qml2Puppet");
55 setFlag(flag: ItemHasContents/*, !m_runningInDesigner*/); // Is this relevant anymore?
56
57 // Set 4x MSAA by default
58 setRenderingMode(QAbstract3DGraph::RenderIndirect);
59 setMsaaSamples(4);
60
61 // Accept touchevents
62 setAcceptTouchEvents(true);
63}
64
65QQuickGraphsItem::~QQuickGraphsItem()
66{
67 disconnect(sender: this, signal: 0, receiver: this, member: 0);
68 checkWindowList(window: 0);
69
70 m_repeaterX->model().clear();
71 m_repeaterY->model().clear();
72 m_repeaterZ->model().clear();
73 m_repeaterX->deleteLater();
74 m_repeaterY->deleteLater();
75 m_repeaterZ->deleteLater();
76
77 m_segmentLineRepeaterX->model().clear();
78 m_segmentLineRepeaterY->model().clear();
79 m_segmentLineRepeaterZ->model().clear();
80 m_segmentLineRepeaterX->deleteLater();
81 m_segmentLineRepeaterY->deleteLater();
82 m_segmentLineRepeaterZ->deleteLater();
83
84 m_subsegmentLineRepeaterX->model().clear();
85 m_subsegmentLineRepeaterY->model().clear();
86 m_subsegmentLineRepeaterZ->model().clear();
87 m_subsegmentLineRepeaterX->deleteLater();
88 m_subsegmentLineRepeaterY->deleteLater();
89 m_subsegmentLineRepeaterZ->deleteLater();
90
91 if (m_sliceVerticalGridRepeater) {
92 m_sliceVerticalGridRepeater->model().clear();
93 m_sliceHorizontalGridRepeater->model().clear();
94 m_sliceHorizontalLabelRepeater->model().clear();
95 m_sliceVerticalLabelRepeater->model().clear();
96 m_sliceVerticalGridRepeater->deleteLater();
97 m_sliceHorizontalGridRepeater->deleteLater();
98 m_sliceHorizontalLabelRepeater->deleteLater();
99 m_sliceVerticalLabelRepeater->deleteLater();
100 }
101
102 // Make sure not deleting locked mutex
103 QMutexLocker locker(&m_mutex);
104 locker.unlock();
105
106 m_nodeMutex.clear();
107}
108
109void QQuickGraphsItem::setRenderingMode(QAbstract3DGraph::RenderingMode mode)
110{
111 if (mode == m_renderMode)
112 return;
113
114 QAbstract3DGraph::RenderingMode previousMode = m_renderMode;
115
116 m_renderMode = mode;
117
118 m_initialisedSize = QSize(0, 0);
119 setFlag(flag: ItemHasContents/*, !m_runningInDesigner*/);
120
121 // TODO - Need to check if the mode is set properly
122 switch (mode) {
123 case QAbstract3DGraph::RenderDirectToBackground:
124 update();
125 setRenderMode(QQuick3DViewport::Underlay);
126 if (previousMode == QAbstract3DGraph::RenderIndirect) {
127 checkWindowList(window: window());
128 setAntialiasing(m_windowSamples > 0);
129 if (m_windowSamples != m_samples)
130 emit msaaSamplesChanged(samples: m_windowSamples);
131 }
132 break;
133 case QAbstract3DGraph::RenderIndirect:
134 update();
135 setRenderMode(QQuick3DViewport::Offscreen);
136 break;
137 }
138
139 updateWindowParameters();
140
141 emit renderingModeChanged(mode);
142}
143
144QAbstract3DGraph::RenderingMode QQuickGraphsItem::renderingMode() const
145{
146 return m_renderMode;
147}
148
149void QQuickGraphsItem::keyPressEvent(QKeyEvent *ev)
150{
151 ev->ignore();
152 setFlag(flag: ItemHasContents);
153 update();
154}
155
156bool QQuickGraphsItem::handleMousePressedEvent(QMouseEvent *event)
157{
158 if (Qt::LeftButton == event->button()) {
159 if (scene()->isSlicingActive()) {
160 m_sliceActivatedChanged = true;
161 return false;
162 }
163 checkSliceEnabled();
164 }
165 return true;
166}
167
168bool QQuickGraphsItem::handleTouchEvent(QTouchEvent *event)
169{
170 if (!event->isUpdateEvent()) {
171 if (scene()->isSlicingActive()) {
172 m_sliceActivatedChanged = true;
173 return false;
174 }
175 checkSliceEnabled();
176 }
177 return true;
178}
179
180void QQuickGraphsItem::checkSliceEnabled()
181{
182 auto selectionMode = m_controller->selectionMode();
183 if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionSlice)
184 && (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)
185 != selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow))) {
186 m_sliceEnabled = true;
187 } else {
188 m_sliceEnabled = false;
189 }
190}
191
192void QQuickGraphsItem::handleThemeTypeChange()
193{
194}
195
196void QQuickGraphsItem::handleFpsChanged()
197{
198 int fps = renderStats()->fps();
199 if (m_currentFps != fps) {
200 m_currentFps = fps;
201 emit currentFpsChanged(fps);
202 }
203}
204
205void QQuickGraphsItem::handleParentWidthChange()
206{
207 if (m_sliceView->isVisible())
208 setWidth(parentItem()->width() * .2f);
209 else
210 setWidth(parentItem()->width());
211
212 if (m_sliceView) {
213 const float scale = qMin(a: m_sliceView->width(), b: m_sliceView->height());
214 QQuick3DOrthographicCamera *camera = static_cast<QQuick3DOrthographicCamera *>(m_sliceView->camera());
215 const float magnificationScaleFactor = .16f; // this controls the size of the slice view
216 const float magnification = scale * magnificationScaleFactor;
217 camera->setHorizontalMagnification(magnification);
218 camera->setVerticalMagnification(magnification);
219 }
220}
221
222void QQuickGraphsItem::handleParentHeightChange()
223{
224 if (m_sliceView->isVisible())
225 setHeight(parentItem()->height() * .2f);
226 else
227 setHeight(parentItem()->height());
228
229 if (m_sliceView) {
230 const float scale = qMin(a: m_sliceView->width(), b: m_sliceView->height());
231 QQuick3DOrthographicCamera *camera = static_cast<QQuick3DOrthographicCamera *>(m_sliceView->camera());
232 const float magnificationScaleFactor = .16f; // this controls the size of the slice view
233 const float magnification = scale * magnificationScaleFactor;
234 camera->setHorizontalMagnification(magnification);
235 camera->setVerticalMagnification(magnification);
236 }
237}
238
239void QQuickGraphsItem::componentComplete()
240{
241 QQuick3DViewport::componentComplete();
242
243 auto url = QUrl(QStringLiteral("defaultMeshes/backgroundMesh"));
244 m_background = new QQuick3DModel();
245 m_backgroundScale = new QQuick3DNode();
246 m_backgroundRotation = new QQuick3DNode();
247 m_graphNode = new QQuick3DNode();
248
249 m_backgroundScale->setParent(rootNode());
250 m_backgroundScale->setParentItem(rootNode());
251
252 m_backgroundRotation->setParent(m_backgroundScale);
253 m_backgroundRotation->setParentItem(m_backgroundScale);
254
255 m_background->setObjectName("Background");
256 m_background->setParent(m_backgroundRotation);
257 m_background->setParentItem(m_backgroundRotation);
258
259 m_background->setSource(url);
260
261 m_backgroundBB = new QQuick3DModel();
262 m_backgroundBB->setObjectName("BackgroundBB");
263 m_backgroundBB->setParent(m_background);
264 m_backgroundBB->setParentItem(m_background);
265 m_backgroundBB->setSource(QUrl(QStringLiteral("defaultMeshes/barMeshFull")));
266 m_backgroundBB->setPickable(true);
267
268 m_graphNode->setParent(rootNode());
269 m_graphNode->setParentItem(rootNode());
270
271 setUpCamera();
272 setUpLight();
273
274 // Create repeaters for each axis X, Y, Z
275 m_repeaterX = createRepeater();
276 m_repeaterY = createRepeater();
277 m_repeaterZ = createRepeater();
278
279 auto delegateModelX = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel"));
280 auto delegateModelY = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel"));
281 auto delegateModelZ = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel"));
282
283 m_repeaterX->setDelegate(delegateModelX);
284 m_repeaterY->setDelegate(delegateModelY);
285 m_repeaterZ->setDelegate(delegateModelZ);
286
287 // title labels for axes
288 m_titleLabelX = createTitleLabel();
289 m_titleLabelX->setVisible(m_controller->axisX()->isTitleVisible());
290 m_titleLabelX->setProperty(name: "labelText", value: m_controller->axisX()->title());
291
292 m_titleLabelY = createTitleLabel();
293 m_titleLabelY->setVisible(m_controller->axisY()->isTitleVisible());
294 m_titleLabelY->setProperty(name: "labelText", value: m_controller->axisY()->title());
295
296 m_titleLabelZ = createTitleLabel();
297 m_titleLabelZ->setVisible(m_controller->axisZ()->isTitleVisible());
298 m_titleLabelZ->setProperty(name: "labelText", value: m_controller->axisZ()->title());
299
300 // Testing gridline
301
302 // X lines
303 m_segmentLineRepeaterX = createRepeater();
304
305 auto segmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
306 m_segmentLineRepeaterX->setDelegate(segmentLineDelegate);
307
308 m_subsegmentLineRepeaterX = createRepeater();
309
310 auto subsegmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
311 m_subsegmentLineRepeaterX->setDelegate(subsegmentLineDelegate);
312
313 // Y lines
314 m_segmentLineRepeaterY = createRepeater();
315
316 segmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
317 m_segmentLineRepeaterY->setDelegate(segmentLineDelegate);
318
319 m_subsegmentLineRepeaterY = createRepeater();
320
321 subsegmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
322 m_subsegmentLineRepeaterY->setDelegate(subsegmentLineDelegate);
323
324 // Z lines
325 m_segmentLineRepeaterZ = createRepeater();
326
327 segmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
328 m_segmentLineRepeaterZ->setDelegate(segmentLineDelegate);
329
330 m_subsegmentLineRepeaterZ = createRepeater();
331
332 subsegmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
333 m_subsegmentLineRepeaterZ->setDelegate(subsegmentLineDelegate);
334
335 createItemLabel();
336
337 auto axis = m_controller->axisX();
338 int segmentCount = 0;
339 int subSegmentCount = 0;
340 int gridLineCount = 0;
341 int subGridLineCount = 0;
342 if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
343 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
344 segmentCount = valueAxis->segmentCount();
345 subSegmentCount = valueAxis->subSegmentCount();
346 gridLineCount = 2 * (segmentCount + 1);
347 subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1));
348 } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) {
349 gridLineCount = axis->labels().size();
350 }
351 m_segmentLineRepeaterX->setModel(gridLineCount);
352 m_subsegmentLineRepeaterX->setModel(subGridLineCount);
353 m_repeaterX->setModel(axis->labels().size());
354 m_controller->handleAxisLabelsChangedBySender(sender: m_controller->axisX());
355
356 axis = m_controller->axisY();
357 if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
358 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
359 segmentCount = valueAxis->segmentCount();
360 subSegmentCount = valueAxis->subSegmentCount();
361 gridLineCount = 2 * (segmentCount + 1);
362 subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1));
363 } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) {
364 gridLineCount = axis->labels().size();
365 }
366 m_segmentLineRepeaterY->setModel(gridLineCount);
367 m_subsegmentLineRepeaterY->setModel(subGridLineCount);
368 m_repeaterY->setModel(2 * axis->labels().size());
369 m_controller->handleAxisLabelsChangedBySender(sender: m_controller->axisY());
370
371 axis = m_controller->axisZ();
372 if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
373 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
374 segmentCount = valueAxis->segmentCount();
375 subSegmentCount = valueAxis->subSegmentCount();
376 gridLineCount = 2 * (segmentCount + 1);
377 subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1));
378 } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) {
379 gridLineCount = axis->labels().size();
380 }
381 m_segmentLineRepeaterZ->setModel(gridLineCount);
382 m_subsegmentLineRepeaterZ->setModel(subGridLineCount);
383 m_repeaterZ->setModel(axis->labels().size());
384 m_controller->handleAxisLabelsChangedBySender(sender: m_controller->axisZ());
385
386 if (!m_pendingCustomItemList.isEmpty()) {
387 foreach (auto item, m_pendingCustomItemList)
388 addCustomItem(item);
389 }
390
391 // Create initial default input handler, if one hasn't already been created by QML
392 if (!activeInputHandler()) {
393 QAbstract3DInputHandler *inputHandler;
394 inputHandler = new QTouch3DInputHandler(this);
395 inputHandler->d_func()->m_isDefaultHandler = true;
396 setActiveInputHandler(inputHandler);
397 }
398}
399
400QQuick3DDirectionalLight *QQuickGraphsItem::light() const
401{
402 return m_light;
403}
404
405// TODO: Check if it would make sense to remove these from Abstract3DController - QTBUG-113812
406// begin..
407void QQuickGraphsItem::addTheme(Q3DTheme *theme)
408{
409 m_controller->addTheme(theme);
410}
411
412void QQuickGraphsItem::releaseTheme(Q3DTheme *theme)
413{
414 m_controller->releaseTheme(theme);
415}
416
417QList<Q3DTheme *> QQuickGraphsItem::themes() const
418{
419 return m_controller->themes();
420}
421
422void QQuickGraphsItem::setTheme(Q3DTheme *theme)
423{
424 m_controller->setActiveTheme(theme, force: isComponentComplete());
425}
426
427Q3DTheme *QQuickGraphsItem::theme() const
428{
429 return m_controller->activeTheme();
430}
431
432void QQuickGraphsItem::clearSelection()
433{
434 m_controller->clearSelection();
435}
436
437bool QQuickGraphsItem::hasSeries(QAbstract3DSeries *series)
438{
439 return m_controller->hasSeries(series);
440}
441
442void QQuickGraphsItem::setSelectionMode(QAbstract3DGraph::SelectionFlags mode)
443{
444 int intmode = int(mode);
445 m_controller->setSelectionMode(QAbstract3DGraph::SelectionFlags(intmode));
446}
447
448QAbstract3DGraph::SelectionFlags QQuickGraphsItem::selectionMode() const
449{
450 return m_controller->selectionMode();
451}
452
453void QQuickGraphsItem::setShadowQuality(QAbstract3DGraph::ShadowQuality quality)
454{
455 m_controller->setShadowQuality(quality);
456}
457
458QAbstract3DGraph::ShadowQuality QQuickGraphsItem::shadowQuality() const
459{
460 return m_controller->shadowQuality();
461}
462
463int QQuickGraphsItem::addCustomItem(QCustom3DItem *item)
464{
465 if (isComponentComplete()) {
466 if (m_controller->isCustomLabelItem(item)) {
467 QQuick3DNode *label = createTitleLabel();
468 QCustom3DLabel *key = static_cast<QCustom3DLabel *>(item);
469 m_customLabelList.insert(key, value: label);
470 } else if (m_controller->isCustomVolumeItem(item)) {
471 QQuick3DModel *model = new QQuick3DModel();
472 model->setParent(graphNode());
473 model->setParentItem(graphNode());
474 m_customItemList.insert(key: item, value: model);
475 } else {
476 QQuick3DModel *model = new QQuick3DModel();
477 model->setParent(graphNode());
478 model->setParentItem(graphNode());
479 QQmlListReference materialsRef(model, "materials");
480 QQuick3DPrincipledMaterial *material = new QQuick3DPrincipledMaterial();
481 material->setParent(model);
482 material->setParentItem(model);
483 materialsRef.append(material);
484 if (!selectionMode().testFlag(flag: QAbstract3DGraph::SelectionNone))
485 model->setPickable(true);
486 m_customItemList.insert(key: item, value: model);
487 }
488 } else {
489 m_pendingCustomItemList.append(t: item);
490 }
491 return m_controller->addCustomItem(item);
492}
493
494void QQuickGraphsItem::removeCustomItems()
495{
496 m_customItemList.clear();
497 m_customLabelList.clear();
498 m_controller->deleteCustomItems();
499}
500
501void QQuickGraphsItem::removeCustomItem(QCustom3DItem *item)
502{
503 if (m_controller->isCustomLabelItem(item)) {
504 m_customLabelList.remove(key: static_cast<QCustom3DLabel *>(item));
505 } else if (m_controller->isCustomVolumeItem(item)) {
506 m_customItemList.remove(key: item);
507 auto volume = static_cast<QCustom3DVolume *>(item);
508 if (m_customVolumes.contains(key: volume)) {
509 m_customVolumes[volume].model->deleteLater();
510 m_customVolumes.remove(key: volume);
511 }
512 } else {
513 m_customItemList.remove(key: item);
514 }
515 m_controller->deleteCustomItem(item);
516}
517
518void QQuickGraphsItem::removeCustomItemAt(const QVector3D &position)
519{
520 auto labelIterator = m_customLabelList.constBegin();
521 while (labelIterator != m_customLabelList.constEnd()) {
522 QCustom3DLabel *label = labelIterator.key();
523 if (label->position() == position) {
524 labelIterator.value()->setVisible(false);
525 labelIterator = m_customLabelList.erase(it: labelIterator);
526 } else {
527 ++labelIterator;
528 }
529 }
530
531 auto itemIterator = m_customItemList.constBegin();
532 while (itemIterator != m_customItemList.constEnd()) {
533 QCustom3DItem *item = itemIterator.key();
534 if (item->position() == position) {
535 itemIterator.value()->setVisible(false);
536 itemIterator = m_customItemList.erase(it: itemIterator);
537 if (m_controller->isCustomVolumeItem(item)) {
538 auto volume = static_cast<QCustom3DVolume *>(item);
539 if (m_customVolumes.contains(key: volume)) {
540 m_customVolumes[volume].model->deleteLater();
541 m_customVolumes.remove(key: volume);
542 }
543 }
544 } else {
545 ++itemIterator;
546 }
547 }
548 m_controller->deleteCustomItem(position);
549}
550
551void QQuickGraphsItem::releaseCustomItem(QCustom3DItem *item)
552{
553 if (m_controller->isCustomLabelItem(item)) {
554 m_customLabelList.remove(key: static_cast<QCustom3DLabel *>(item));
555 } else if (m_controller->isCustomVolumeItem(item)) {
556 m_customItemList.remove(key: item);
557 auto volume = static_cast<QCustom3DVolume *>(item);
558 if (m_customVolumes.contains(key: volume)) {
559 m_customVolumes[volume].model->deleteLater();
560 m_customVolumes.remove(key: volume);
561 }
562 } else {
563 m_customItemList.remove(key: item);
564 }
565 m_controller->releaseCustomItem(item);
566}
567
568int QQuickGraphsItem::selectedLabelIndex() const
569{
570 return m_controller->selectedLabelIndex();
571}
572
573QAbstract3DAxis *QQuickGraphsItem::selectedAxis() const
574{
575 return m_controller->selectedAxis();
576}
577
578int QQuickGraphsItem::selectedCustomItemIndex() const
579{
580 return m_controller->selectedCustomItemIndex();
581}
582
583QCustom3DItem *QQuickGraphsItem::selectedCustomItem() const
584{
585 return m_controller->selectedCustomItem();
586}
587// ...end
588// TODO: Check if it would make sense to remove these from Abstract3DController - QTBUG-113812
589
590QQmlListProperty<QCustom3DItem> QQuickGraphsItem::customItemList()
591{
592 return QQmlListProperty<QCustom3DItem>(this, this,
593 &QQuickGraphsItem::appendCustomItemFunc,
594 &QQuickGraphsItem::countCustomItemFunc,
595 &QQuickGraphsItem::atCustomItemFunc,
596 &QQuickGraphsItem::clearCustomItemFunc);
597}
598
599void QQuickGraphsItem::appendCustomItemFunc(QQmlListProperty<QCustom3DItem> *list,
600 QCustom3DItem *item)
601{
602 QQuickGraphsItem *decl = reinterpret_cast<QQuickGraphsItem *>(list->data);
603 decl->addCustomItem(item);
604}
605
606qsizetype QQuickGraphsItem::countCustomItemFunc(QQmlListProperty<QCustom3DItem> *list)
607{
608 Q_UNUSED(list);
609 return reinterpret_cast<QQuickGraphsItem *>(list->data)->m_controller->m_customItems.size();
610}
611
612QCustom3DItem *QQuickGraphsItem::atCustomItemFunc(QQmlListProperty<QCustom3DItem> *list,
613 qsizetype index)
614{
615 Q_UNUSED(list);
616 Q_UNUSED(index);
617 return reinterpret_cast<QQuickGraphsItem *>(list->data)->m_controller->m_customItems.at(i: index);
618}
619
620void QQuickGraphsItem::clearCustomItemFunc(QQmlListProperty<QCustom3DItem> *list)
621{
622 QQuickGraphsItem *decl = reinterpret_cast<QQuickGraphsItem *>(list->data);
623 decl->removeCustomItems();
624}
625
626void QQuickGraphsItem::setSharedController(Abstract3DController *controller)
627{
628 Q_ASSERT(controller);
629 m_controller = controller;
630 m_controller->m_qml = this;
631
632 // Reset default theme, as the default C++ theme is Q3DTheme, not DeclarativeTheme3D.
633 DeclarativeTheme3D *defaultTheme = new DeclarativeTheme3D;
634 defaultTheme->d_func()->setDefaultTheme(true);
635 defaultTheme->setType(Q3DTheme::ThemeQt);
636 m_controller->setActiveTheme(theme: defaultTheme);
637
638 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::shadowQualityChanged, context: this,
639 slot: &QQuickGraphsItem::handleShadowQualityChange);
640 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::activeThemeChanged, context: this,
641 slot: &QQuickGraphsItem::themeChanged);
642 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::themeTypeChanged, context: this,
643 slot: &QQuickGraphsItem::handleThemeTypeChange);
644 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::selectionModeChanged, context: this,
645 slot: &QQuickGraphsItem::handleSelectionModeChange);
646 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::elementSelected, context: this,
647 slot: &QQuickGraphsItem::handleSelectedElementChange);
648
649 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisXChanged, context: this,
650 slot: &QQuickGraphsItem::handleAxisXChanged);
651 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisYChanged, context: this,
652 slot: &QQuickGraphsItem::handleAxisYChanged);
653 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisZChanged, context: this,
654 slot: &QQuickGraphsItem::handleAxisZChanged);
655
656 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::orthoProjectionChanged, context: this,
657 slot: &QQuickGraphsItem::orthoProjectionChanged);
658
659 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::aspectRatioChanged, context: this,
660 slot: &QQuickGraphsItem::aspectRatioChanged);
661 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::optimizationHintsChanged, context: this,
662 slot: &QQuickGraphsItem::handleOptimizationHintChange);
663 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::polarChanged, context: this,
664 slot: &QQuickGraphsItem::polarChanged);
665 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::radialLabelOffsetChanged, context: this,
666 slot: &QQuickGraphsItem::radialLabelOffsetChanged);
667 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::horizontalAspectRatioChanged, context: this,
668 slot: &QQuickGraphsItem::horizontalAspectRatioChanged);
669 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::reflectionChanged, context: this,
670 slot: &QQuickGraphsItem::reflectionChanged);
671 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::reflectivityChanged, context: this,
672 slot: &QQuickGraphsItem::reflectivityChanged);
673 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::localeChanged, context: this,
674 slot: &QQuickGraphsItem::localeChanged);
675 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::queriedGraphPositionChanged, context: this,
676 slot: &QQuickGraphsItem::queriedGraphPositionChanged);
677 QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::marginChanged, context: this,
678 slot: &QQuickGraphsItem::marginChanged);
679}
680
681void QQuickGraphsItem::synchData()
682{
683 if (!isVisible())
684 return;
685 m_controller->m_renderPending = false;
686
687 if (m_controller->m_changeTracker.selectionModeChanged) {
688 updateSelectionMode(newMode: m_controller->selectionMode());
689 m_controller->m_changeTracker.selectionModeChanged = false;
690 }
691
692 bool recalculateScale = false;
693 if (m_controller->m_changeTracker.aspectRatioChanged) {
694 recalculateScale = true;
695 m_controller->m_changeTracker.aspectRatioChanged = false;
696 }
697
698 if (m_controller->m_changeTracker.horizontalAspectRatioChanged) {
699 recalculateScale = true;
700 m_controller->m_changeTracker.horizontalAspectRatioChanged = false;
701 }
702
703 if (m_controller->m_changeTracker.marginChanged) {
704 recalculateScale = true;
705 m_controller->m_changeTracker.marginChanged = false;
706 }
707
708 if (recalculateScale)
709 calculateSceneScalingFactors();
710
711 bool axisDirty = recalculateScale;
712 if (m_controller->m_changeTracker.axisXFormatterChanged) {
713 m_controller->m_changeTracker.axisXFormatterChanged = false;
714 QAbstract3DAxis *axisX = m_controller->axisX();
715 if (axisX->type() & QAbstract3DAxis::AxisTypeValue) {
716 QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX);
717 valueAxisX->recalculate();
718 repeaterX()->setModel(valueAxisX->formatter()->labelPositions().size());
719 }
720 axisDirty = true;
721 }
722
723 if (m_controller->m_changeTracker.axisYFormatterChanged) {
724 m_controller->m_changeTracker.axisYFormatterChanged = false;
725 QAbstract3DAxis *axisY = m_controller->axisY();
726 if (axisY->type() & QAbstract3DAxis::AxisTypeValue) {
727 QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY);
728 valueAxisY->recalculate();
729 repeaterY()->setModel(2 * valueAxisY->formatter()->labelPositions().size());
730 }
731 axisDirty = true;
732 }
733
734 if (m_controller->m_changeTracker.axisZFormatterChanged) {
735 m_controller->m_changeTracker.axisZFormatterChanged = false;
736 QAbstract3DAxis *axisZ = m_controller->axisZ();
737 if (axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
738 QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ);
739 valueAxisZ->recalculate();
740 repeaterZ()->setModel(valueAxisZ->formatter()->labelPositions().size());
741 }
742 axisDirty = true;
743 }
744
745 if (m_controller->m_changeTracker.axisXSegmentCountChanged) {
746 QAbstract3DAxis *axisX = m_controller->axisX();
747 if (axisX->type() & QAbstract3DAxis::AxisTypeValue) {
748 QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX);
749 valueAxisX->recalculate();
750 }
751 handleSegmentLineCountChanged(axis: axisX, repeater: m_segmentLineRepeaterX);
752 m_controller->m_changeTracker.axisXSegmentCountChanged = false;
753 axisDirty = true;
754 }
755
756 if (m_controller->m_changeTracker.axisYSegmentCountChanged) {
757 QAbstract3DAxis *axisY = m_controller->axisY();
758 if (axisY->type() & QAbstract3DAxis::AxisTypeValue) {
759 QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY);
760 valueAxisY->recalculate();
761 }
762 handleSegmentLineCountChanged(axis: axisY, repeater: m_segmentLineRepeaterY);
763 m_controller->m_changeTracker.axisYSegmentCountChanged = false;
764 axisDirty = true;
765 }
766
767 if (m_controller->m_changeTracker.axisZSegmentCountChanged) {
768 QAbstract3DAxis *axisZ = m_controller->axisZ();
769 if (axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
770 QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ);
771 valueAxisZ->recalculate();
772 }
773 handleSegmentLineCountChanged(axis: axisZ, repeater: m_segmentLineRepeaterZ);
774 m_controller->m_changeTracker.axisZSegmentCountChanged = false;
775 axisDirty = true;
776 }
777
778 if (m_controller->m_changeTracker.axisXSubSegmentCountChanged) {
779 QAbstract3DAxis *axisX = m_controller->axisX();
780 if (axisX->type() & QAbstract3DAxis::AxisTypeValue) {
781 QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX);
782 valueAxisX->recalculate();
783 }
784 handleSubSegmentLineCountChanged(axis: axisX, repeater: m_subsegmentLineRepeaterX);
785 m_controller->m_changeTracker.axisXSubSegmentCountChanged = false;
786 axisDirty = true;
787 }
788
789 if (m_controller->m_changeTracker.axisYSubSegmentCountChanged) {
790 QAbstract3DAxis *axisY = m_controller->axisY();
791 if (axisY->type() & QAbstract3DAxis::AxisTypeValue) {
792 QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY);
793 valueAxisY->recalculate();
794 }
795 handleSubSegmentLineCountChanged(axis: axisY, repeater: m_subsegmentLineRepeaterY);
796 m_controller->m_changeTracker.axisYSubSegmentCountChanged = false;
797 axisDirty = true;
798 }
799
800 if (m_controller->m_changeTracker.axisZSubSegmentCountChanged) {
801 QAbstract3DAxis *axisZ = m_controller->axisZ();
802 if (axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
803 QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ);
804 valueAxisZ->recalculate();
805 }
806 handleSubSegmentLineCountChanged(axis: axisZ, repeater: m_subsegmentLineRepeaterZ);
807 m_controller->m_changeTracker.axisZSubSegmentCountChanged = false;
808 axisDirty = true;
809 }
810
811 if (m_controller->m_changeTracker.axisXLabelsChanged) {
812 QAbstract3DAxis *axisX = m_controller->axisX();
813 if (axisX->type() & QAbstract3DAxis::AxisTypeValue) {
814 auto valueAxisX = static_cast<QValue3DAxis *>(axisX);
815 valueAxisX->recalculate();
816 repeaterX()->setModel(valueAxisX->formatter()->labelPositions().size());
817 } else if (axisX->type() & QAbstract3DAxis::AxisTypeCategory) {
818 auto categoryAxis = static_cast<QCategory3DAxis *>(axisX);
819 repeaterX()->setModel(categoryAxis->labels().size());
820 }
821
822 handleSegmentLineCountChanged(axis: axisX, repeater: segmentLineRepeaterX());
823 handleSubSegmentLineCountChanged(axis: axisX, repeater: subsegmentLineRepeaterX());
824 m_controller->m_changeTracker.axisXLabelsChanged = false;
825 handleLabelCountChanged(repeater: m_repeaterX);
826 axisDirty = true;
827 }
828
829 if (m_controller->m_changeTracker.axisYLabelsChanged) {
830 QAbstract3DAxis *axisY = m_controller->axisY();
831 if (axisY->type() & QAbstract3DAxis::AxisTypeValue) {
832 auto valueAxisY = static_cast<QValue3DAxis *>(axisY);
833 valueAxisY->recalculate();
834 repeaterY()->setModel(2 * valueAxisY->formatter()->labelPositions().size());
835 } else if (axisY->type() & QAbstract3DAxis::AxisTypeCategory) {
836 auto categoryAxis = static_cast<QCategory3DAxis *>(axisY);
837 repeaterY()->setModel(2 * categoryAxis->labels().size());
838 }
839
840 handleSegmentLineCountChanged(axis: axisY, repeater: segmentLineRepeaterY());
841 handleSubSegmentLineCountChanged(axis: axisY, repeater: subsegmentLineRepeaterY());
842 m_controller->m_changeTracker.axisYLabelsChanged = false;
843 handleLabelCountChanged(repeater: m_repeaterY);
844 axisDirty = true;
845 }
846
847 if (m_controller->m_changeTracker.axisZLabelsChanged) {
848 QAbstract3DAxis *axisZ = m_controller->axisZ();
849 if (axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
850 auto valueAxisZ = static_cast<QValue3DAxis *>(axisZ);
851 valueAxisZ->recalculate();
852 repeaterZ()->setModel(valueAxisZ->formatter()->labelPositions().size());
853 } else if (axisZ->type() & QAbstract3DAxis::AxisTypeCategory) {
854 auto categoryAxis = static_cast<QCategory3DAxis *>(axisZ);
855 repeaterZ()->setModel(categoryAxis->labels().size());
856 }
857
858 handleSegmentLineCountChanged(axis: axisZ, repeater: segmentLineRepeaterZ());
859 handleSubSegmentLineCountChanged(axis: axisZ, repeater: subsegmentLineRepeaterZ());
860 m_controller->m_changeTracker.axisZLabelsChanged = false;
861 handleLabelCountChanged(repeater: m_repeaterZ);
862 axisDirty = true;
863 }
864
865 updateTitleLabels();
866
867 if (m_controller->m_changeTracker.shadowQualityChanged) {
868 updateShadowQuality(quality: shadowQuality());
869 m_controller->m_changeTracker.shadowQualityChanged = false;
870 }
871
872 if (m_controller->m_changeTracker.axisXRangeChanged) {
873 axisDirty = true;
874 calculateSceneScalingFactors();
875 m_controller->m_changeTracker.axisXRangeChanged = false;
876 }
877
878 if (m_controller->m_changeTracker.axisYRangeChanged) {
879 axisDirty = true;
880 QAbstract3DAxis *axis = m_controller->axisY();
881 updateAxisRange(min: axis->min(), max: axis->max());
882 calculateSceneScalingFactors();
883 m_controller->m_changeTracker.axisYRangeChanged = false;
884 }
885
886 if (m_controller->m_changeTracker.axisZRangeChanged) {
887 axisDirty = true;
888 calculateSceneScalingFactors();
889 m_controller->m_changeTracker.axisZRangeChanged = false;
890 }
891
892 if (m_controller->m_changeTracker.axisYReversedChanged) {
893 m_controller->m_changeTracker.axisYReversedChanged = false;
894 if (m_controller->m_axisY->type() & QAbstract3DAxis::AxisTypeValue) {
895 QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_controller->m_axisY);
896 updateAxisReversed(enable: valueAxisY->reversed());
897 }
898 }
899
900 if (m_controller->m_changeTracker.polarChanged) {
901 axisDirty = true;
902 m_controller->m_changeTracker.polarChanged = false;
903 }
904
905 if (m_controller->m_changeTracker.axisXLabelAutoRotationChanged) {
906 axisDirty = true;
907 m_controller->m_changeTracker.axisXLabelAutoRotationChanged = false;
908 }
909
910 if (m_controller->m_changeTracker.axisYLabelAutoRotationChanged) {
911 axisDirty = true;
912 m_controller->m_changeTracker.axisYLabelAutoRotationChanged = false;
913 }
914
915 if (m_controller->m_changeTracker.axisZLabelAutoRotationChanged) {
916 axisDirty = true;
917 m_controller->m_changeTracker.axisZLabelAutoRotationChanged = false;
918 }
919
920 if (m_controller->m_changeTracker.axisXTitleFixedChanged) {
921 axisDirty = true;
922 m_controller->m_changeTracker.axisXTitleFixedChanged = false;
923 }
924
925 if (m_controller->m_changeTracker.axisYTitleFixedChanged) {
926 axisDirty = true;
927 m_controller->m_changeTracker.axisYTitleFixedChanged = false;
928 }
929
930 if (m_controller->m_changeTracker.axisZTitleFixedChanged) {
931 axisDirty = true;
932 m_controller->m_changeTracker.axisZTitleFixedChanged = false;
933 }
934
935 updateCamera();
936
937 QVector3D forward = camera()->forward();
938 auto targetRotation = cameraTarget()->rotation();
939 if (m_yFlipped != (targetRotation.x() > 0)) {
940 m_yFlipped = (targetRotation.x() > 0);
941 axisDirty = true;
942 }
943 if (m_xFlipped != (forward.x() > 0)) {
944 m_xFlipped = (forward.x() > 0);
945 axisDirty = true;
946 }
947 if (m_zFlipped != ((forward.z() > .1f))) {
948 m_zFlipped = ((forward.z() > .1f));
949 axisDirty = true;
950 }
951
952 if (axisDirty) {
953 updateGrid();
954 updateLabels();
955 updateCustomData();
956 if (m_sliceView) {
957 updateSliceGrid();
958 updateSliceLabels();
959 }
960 m_gridUpdated = true;
961 }
962
963 if (m_controller->m_changeTracker.radialLabelOffsetChanged) {
964 updateRadialLabelOffset();
965 m_controller->m_changeTracker.radialLabelOffsetChanged = false;
966 }
967
968 QMatrix4x4 modelMatrix;
969 m_backgroundScale->setScale(m_scaleWithBackground + m_backgroundScaleMargin);
970
971 QVector3D rotVec;
972 if (!m_yFlipped) {
973 rotVec = QVector3D(0, 270, 0);
974 if (m_xFlipped && m_zFlipped)
975 rotVec.setY(90);
976 else if (!m_xFlipped && m_zFlipped)
977 rotVec.setY(0);
978 else if (m_xFlipped && !m_zFlipped)
979 rotVec.setY(180);
980 } else {
981 rotVec = QVector3D(0, 180, 180);
982 if (m_xFlipped && m_zFlipped)
983 rotVec.setY(0);
984 else if (!m_xFlipped && m_zFlipped)
985 rotVec.setY(270);
986 else if (m_xFlipped && !m_zFlipped)
987 rotVec.setY(90);
988 }
989
990 auto rotation = Utils::calculateRotation(xyzRotations: rotVec);
991 if (m_yFlipped) {
992 m_backgroundRotation->setRotation(rotation);
993 } else {
994 modelMatrix.rotate(quaternion: rotation);
995 m_backgroundRotation->setRotation(rotation);
996 }
997
998 if (m_controller->graphPositionQueryPending())
999 graphPositionAt(point: m_controller->scene()->graphPositionQuery());
1000
1001 bool forceUpdateCustomVolumes = false;
1002 if (m_controller->m_changeTracker.projectionChanged) {
1003 forceUpdateCustomVolumes = true;
1004 bool useOrtho = m_controller->isOrthoProjection();
1005 if (useOrtho)
1006 setCamera(m_oCamera);
1007 else
1008 setCamera(m_pCamera);
1009 m_controller->m_changeTracker.projectionChanged = false;
1010 }
1011
1012 Q3DTheme *theme = m_controller->activeTheme();
1013 if (m_controller->m_changeTracker.themeChanged) {
1014 environment()->setClearColor(theme->windowColor());
1015 m_controller->m_changeTracker.themeChanged = false;
1016 }
1017
1018 if (theme->d_func()->m_dirtyBits.lightStrengthDirty) {
1019 light()->setBrightness(theme->lightStrength() * .2f);
1020 if (qFuzzyIsNull(f: light()->brightness()))
1021 light()->setBrightness(.0000001f);
1022 updateLightStrength();
1023 theme->d_func()->m_dirtyBits.lightStrengthDirty = false;
1024 }
1025
1026 if (theme->d_func()->m_dirtyBits.ambientLightStrengthDirty) {
1027 float ambientStrength = theme->ambientLightStrength();
1028 QColor ambientColor = QColor::fromRgbF(r: ambientStrength, g: ambientStrength, b: ambientStrength);
1029 light()->setAmbientColor(ambientColor);
1030 if (qFuzzyIsNull(f: light()->brightness()))
1031 light()->setBrightness(.0000001f);
1032 theme->d_func()->m_dirtyBits.ambientLightStrengthDirty = false;
1033 }
1034
1035 if (theme->d_func()->m_dirtyBits.lightColorDirty) {
1036 light()->setColor(theme->lightColor());
1037 theme->d_func()->m_dirtyBits.lightColorDirty = false;
1038 }
1039
1040 if (theme->d_func()->m_dirtyBits.shadowStrengthDirty) {
1041 light()->setShadowFactor(theme->shadowStrength());
1042 theme->d_func()->m_dirtyBits.shadowStrengthDirty = false;
1043 }
1044
1045 // label Adjustments
1046 if (theme->d_func()->m_dirtyBits.labelBackgroundColorDirty) {
1047 QColor labelBackgroundColor = theme->labelBackgroundColor();
1048 changeLabelBackgroundColor(repeater: m_repeaterX, color: labelBackgroundColor);
1049 changeLabelBackgroundColor(repeater: m_repeaterY, color: labelBackgroundColor);
1050 changeLabelBackgroundColor(repeater: m_repeaterZ, color: labelBackgroundColor);
1051 m_titleLabelX->setProperty(name: "backgroundColor", value: labelBackgroundColor);
1052 m_titleLabelY->setProperty(name: "backgroundColor", value: labelBackgroundColor);
1053 m_titleLabelZ->setProperty(name: "backgroundColor", value: labelBackgroundColor);
1054 m_itemLabel->setProperty(name: "backgroundColor", value: labelBackgroundColor);
1055
1056 if (m_sliceView) {
1057 changeLabelBackgroundColor(repeater: m_sliceHorizontalLabelRepeater, color: labelBackgroundColor);
1058 changeLabelBackgroundColor(repeater: m_sliceVerticalLabelRepeater, color: labelBackgroundColor);
1059 m_sliceItemLabel->setProperty(name: "backgroundColor", value: labelBackgroundColor);
1060 m_sliceHorizontalTitleLabel->setProperty(name: "backgroundColor", value: labelBackgroundColor);
1061 m_sliceVerticalTitleLabel->setProperty(name: "backgroundColor", value: labelBackgroundColor);
1062 }
1063 theme->d_func()->m_dirtyBits.labelBackgroundColorDirty = false;
1064 }
1065
1066 if (theme->d_func()->m_dirtyBits.labelBackgroundEnabledDirty) {
1067 bool enabled = theme->isLabelBackgroundEnabled();
1068 changeLabelBackgroundEnabled(repeater: m_repeaterX, enabled);
1069 changeLabelBackgroundEnabled(repeater: m_repeaterY, enabled);
1070 changeLabelBackgroundEnabled(repeater: m_repeaterZ, enabled);
1071 m_titleLabelX->setProperty(name: "backgroundEnabled", value: enabled);
1072 m_titleLabelY->setProperty(name: "backgroundEnabled", value: enabled);
1073 m_titleLabelZ->setProperty(name: "backgroundEnabled", value: enabled);
1074 m_itemLabel->setProperty(name: "backgroundEnabled", value: enabled);
1075
1076 if (m_sliceView) {
1077 changeLabelBackgroundEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled);
1078 changeLabelBackgroundEnabled(repeater: m_sliceVerticalLabelRepeater, enabled);
1079 m_sliceItemLabel->setProperty(name: "backgroundEnabled", value: enabled);
1080 m_sliceHorizontalTitleLabel->setProperty(name: "backgroundEnabled", value: enabled);
1081 m_sliceVerticalTitleLabel->setProperty(name: "backgroundEnabled", value: enabled);
1082 }
1083 theme->d_func()->m_dirtyBits.labelBackgroundEnabledDirty = false;
1084 }
1085
1086 if (theme->d_func()->m_dirtyBits.labelBorderEnabledDirty) {
1087 bool enabled = theme->isLabelBorderEnabled();
1088 changeLabelBorderEnabled(repeater: m_repeaterX, enabled);
1089 changeLabelBorderEnabled(repeater: m_repeaterY, enabled);
1090 changeLabelBorderEnabled(repeater: m_repeaterZ, enabled);
1091 m_titleLabelX->setProperty(name: "borderEnabled", value: enabled);
1092 m_titleLabelY->setProperty(name: "borderEnabled", value: enabled);
1093 m_titleLabelZ->setProperty(name: "borderEnabled", value: enabled);
1094 m_itemLabel->setProperty(name: "borderEnabled", value: enabled);
1095
1096 if (m_sliceView) {
1097 changeLabelBorderEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled);
1098 changeLabelBorderEnabled(repeater: m_sliceVerticalLabelRepeater, enabled);
1099 m_sliceItemLabel->setProperty(name: "borderEnabled", value: enabled);
1100 m_sliceHorizontalTitleLabel->setProperty(name: "borderEnabled", value: enabled);
1101 m_sliceVerticalTitleLabel->setProperty(name: "borderEnabled", value: enabled);
1102 }
1103 theme->d_func()->m_dirtyBits.labelBorderEnabledDirty = false;
1104 }
1105
1106 if (theme->d_func()->m_dirtyBits.labelTextColorDirty) {
1107 QColor labelTextColor = theme->labelTextColor();
1108 changeLabelTextColor(repeater: m_repeaterX, color: labelTextColor);
1109 changeLabelTextColor(repeater: m_repeaterY, color: labelTextColor);
1110 changeLabelTextColor(repeater: m_repeaterZ, color: labelTextColor);
1111 m_titleLabelX->setProperty(name: "labelTextColor", value: labelTextColor);
1112 m_titleLabelY->setProperty(name: "labelTextColor", value: labelTextColor);
1113 m_titleLabelZ->setProperty(name: "labelTextColor", value: labelTextColor);
1114 m_itemLabel->setProperty(name: "labelTextColor", value: labelTextColor);
1115
1116 if (m_sliceView) {
1117 changeLabelTextColor(repeater: m_sliceHorizontalLabelRepeater, color: labelTextColor);
1118 changeLabelTextColor(repeater: m_sliceVerticalLabelRepeater, color: labelTextColor);
1119 m_sliceItemLabel->setProperty(name: "labelTextColor", value: labelTextColor);
1120 m_sliceHorizontalTitleLabel->setProperty(name: "labelTextColor", value: labelTextColor);
1121 m_sliceVerticalTitleLabel->setProperty(name: "labelTextColor", value: labelTextColor);
1122 }
1123 theme->d_func()->m_dirtyBits.labelTextColorDirty = false;
1124 }
1125
1126 if (theme->d_func()->m_dirtyBits.fontDirty) {
1127 auto font = theme->font();
1128 changeLabelFont(repeater: m_repeaterX, font);
1129 changeLabelFont(repeater: m_repeaterY, font);
1130 changeLabelFont(repeater: m_repeaterZ, font);
1131 m_titleLabelX->setProperty(name: "labelFont", value: font);
1132 m_titleLabelY->setProperty(name: "labelFont", value: font);
1133 m_titleLabelZ->setProperty(name: "labelFont", value: font);
1134 m_itemLabel->setProperty(name: "labelFont", value: font);
1135 updateLabels();
1136
1137 if (m_sliceView) {
1138 changeLabelFont(repeater: m_sliceHorizontalLabelRepeater, font);
1139 changeLabelFont(repeater: m_sliceVerticalLabelRepeater, font);
1140 m_sliceItemLabel->setProperty(name: "labelFont", value: font);
1141 m_sliceHorizontalTitleLabel->setProperty(name: "labelFont", value: font);
1142 m_sliceVerticalTitleLabel->setProperty(name: "labelFont", value: font);
1143 updateSliceLabels();
1144 }
1145 theme->d_func()->m_dirtyBits.fontDirty = false;
1146 }
1147
1148 if (theme->d_func()->m_dirtyBits.labelsEnabledDirty) {
1149 bool enabled = theme->isLabelsEnabled();
1150 changeLabelsEnabled(repeater: m_repeaterX, enabled);
1151 changeLabelsEnabled(repeater: m_repeaterY, enabled);
1152 changeLabelsEnabled(repeater: m_repeaterZ, enabled);
1153 m_titleLabelX->setProperty(name: "visible", value: enabled && m_controller->axisX()->isTitleVisible());
1154 m_titleLabelY->setProperty(name: "visible", value: enabled && m_controller->axisY()->isTitleVisible());
1155 m_titleLabelZ->setProperty(name: "visible", value: enabled && m_controller->axisZ()->isTitleVisible());
1156 m_itemLabel->setProperty(name: "visible", value: enabled);
1157
1158 if (m_sliceView) {
1159 changeLabelsEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled);
1160 changeLabelsEnabled(repeater: m_sliceVerticalLabelRepeater, enabled);
1161 m_sliceItemLabel->setProperty(name: "visible", value: enabled);
1162 m_sliceHorizontalTitleLabel->setProperty(name: "visible", value: enabled);
1163 m_sliceVerticalTitleLabel->setProperty(name: "visible", value: enabled);
1164 }
1165 theme->d_func()->m_dirtyBits.labelsEnabledDirty = false;
1166 }
1167
1168 // Grid and background adjustments
1169 if (theme->d_func()->m_dirtyBits.backgroundColorDirty) {
1170 QQmlListReference materialsRef(m_background, "materials");
1171 QQuick3DPrincipledMaterial *bgMat;
1172 if (!materialsRef.size()) {
1173 bgMat = new QQuick3DPrincipledMaterial();
1174 bgMat->setParent(m_background);
1175 bgMat->setMetalness(0.f);
1176 bgMat->setRoughness(.3f);
1177 bgMat->setEmissiveFactor(QVector3D(.001f, .001f, .001f));
1178 materialsRef.append(bgMat);
1179 } else {
1180 bgMat = static_cast<QQuick3DPrincipledMaterial *>(materialsRef.at(0));
1181 }
1182 bgMat->setBaseColor(theme->backgroundColor());
1183 theme->d_func()->m_dirtyBits.backgroundColorDirty = false;
1184 }
1185
1186 if (theme->d_func()->m_dirtyBits.backgroundEnabledDirty) {
1187 m_background->setLocalOpacity(theme->isBackgroundEnabled());
1188 theme->d_func()->m_dirtyBits.backgroundEnabledDirty = false;
1189 }
1190
1191 if (theme->d_func()->m_dirtyBits.gridEnabledDirty) {
1192 bool enabled = theme->isGridEnabled();
1193 m_segmentLineRepeaterX->setVisible(enabled);
1194 m_segmentLineRepeaterY->setVisible(enabled);
1195 m_segmentLineRepeaterZ->setVisible(enabled);
1196
1197 m_subsegmentLineRepeaterX->setVisible(enabled);
1198 m_subsegmentLineRepeaterY->setVisible(enabled);
1199 m_subsegmentLineRepeaterZ->setVisible(enabled);
1200
1201 if (m_sliceEnabled && m_controller->isSlicingActive()) {
1202 m_sliceHorizontalGridRepeater->setVisible(enabled);
1203 m_sliceVerticalGridRepeater->setVisible(enabled);
1204 }
1205 theme->d_func()->m_dirtyBits.gridEnabledDirty = false;
1206 }
1207
1208 if (theme->d_func()->m_dirtyBits.gridLineColorDirty) {
1209 QColor gridLineColor = theme->gridLineColor();
1210 changeGridLineColor(repeater: m_segmentLineRepeaterX, color: gridLineColor);
1211 changeGridLineColor(repeater: m_subsegmentLineRepeaterX, color: gridLineColor);
1212 changeGridLineColor(repeater: m_segmentLineRepeaterY, color: gridLineColor);
1213 changeGridLineColor(repeater: m_subsegmentLineRepeaterY, color: gridLineColor);
1214 changeGridLineColor(repeater: m_segmentLineRepeaterZ, color: gridLineColor);
1215 changeGridLineColor(repeater: m_subsegmentLineRepeaterZ, color: gridLineColor);
1216 theme->d_func()->m_dirtyBits.gridLineColorDirty = false;
1217 }
1218
1219 if (theme->d_func()->m_dirtyBits.singleHighlightColorDirty) {
1220 updateSingleHighlightColor();
1221 theme->d_func()->m_dirtyBits.singleHighlightColorDirty = false;
1222 }
1223
1224 // Other adjustments
1225 if (theme->d_func()->m_dirtyBits.windowColorDirty) {
1226 window()->setColor(theme->windowColor());
1227 environment()->setClearColor(theme->windowColor());
1228 theme->d_func()->m_dirtyBits.windowColorDirty = false;
1229 }
1230 if (m_controller->activeTheme()->windowColor() != window()->color()) {
1231 window()->setColor(m_controller->activeTheme()->windowColor());
1232 }
1233
1234 if (m_controller->isCustomDataDirty()) {
1235 forceUpdateCustomVolumes = true;
1236 updateCustomData();
1237 m_controller->setCustomDataDirty(false);
1238 }
1239
1240 if (m_controller->m_changedSeriesList.size()) {
1241 forceUpdateCustomVolumes = true;
1242 updateGraph();
1243 m_controller->m_changedSeriesList.clear();
1244 }
1245
1246 if (m_controller->m_isDataDirty) {
1247 forceUpdateCustomVolumes = true;
1248 updateGraph();
1249 m_controller->m_isDataDirty = false;
1250 }
1251
1252 if (m_controller->m_isSeriesVisualsDirty) {
1253 forceUpdateCustomVolumes = true;
1254 updateLabels();
1255 updateGraph();
1256 m_controller->m_isSeriesVisualsDirty = false;
1257 }
1258
1259 if (m_sliceActivatedChanged)
1260 updateSliceGraph();
1261
1262 if (m_controller->isCustomItemDirty() || forceUpdateCustomVolumes)
1263 updateCustomVolumes();
1264}
1265
1266void QQuickGraphsItem::calculateSceneScalingFactors()
1267{
1268 float scaleX, scaleY, scaleZ;
1269 float marginH, marginV;
1270 float aspectRatio = m_controller->aspectRatio();
1271 float horizontalAspectRatio = m_controller->horizontalAspectRatio();
1272
1273 float margin = m_controller->margin();
1274 if (margin < 0.0f) {
1275 marginH = .1f;
1276 marginV = .1f;
1277 } else {
1278 marginH = margin;
1279 marginV = margin;
1280 }
1281
1282 QSizeF areaSize;
1283 if (qFuzzyIsNull(f: horizontalAspectRatio)) {
1284 areaSize.setHeight(m_controller->axisZ()->max() - m_controller->axisZ()->min());
1285 areaSize.setWidth(m_controller->axisX()->max() - m_controller->axisX()->min());
1286 } else {
1287 areaSize.setHeight(1.0);
1288 areaSize.setWidth(horizontalAspectRatio);
1289 }
1290
1291 float horizontalMaxDimension;
1292 if (aspectRatio > 2.0f) {
1293 horizontalMaxDimension = 2.0f;
1294 scaleY = 2.0f / aspectRatio;
1295 } else {
1296 horizontalMaxDimension = aspectRatio;
1297 scaleY = 1.0f;
1298 }
1299
1300 float scaleFactor = qMax(a: areaSize.width(), b: areaSize.height());
1301 scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor;
1302 scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor;
1303
1304 m_scale = QVector3D(scaleX, scaleY, scaleZ);
1305 m_scaleWithBackground = QVector3D(scaleX, scaleY, scaleZ);
1306 m_backgroundScaleMargin = QVector3D(marginH, marginV, marginH);
1307}
1308
1309void QQuickGraphsItem::updateGrid()
1310{
1311 int gridLineCountX = segmentLineRepeaterX()->count();
1312 int subGridLineCountX = subsegmentLineRepeaterX()->count();
1313 int gridLineCountY = segmentLineRepeaterY()->count() / 2;
1314 int subGridLineCountY = subsegmentLineRepeaterY()->count() / 2;
1315 int gridLineCountZ = segmentLineRepeaterZ()->count();
1316 int subGridLineCountZ = subsegmentLineRepeaterZ()->count();
1317
1318 if (!m_isFloorGridInRange) {
1319 gridLineCountX /= 2;
1320 subGridLineCountX /= 2;
1321 gridLineCountZ /= 2;
1322 subGridLineCountZ /= 2;
1323 }
1324
1325 auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin;
1326 QVector3D scaleX(backgroundScale.x() * lineLengthScaleFactor(), lineWidthScaleFactor(), lineWidthScaleFactor());
1327 QVector3D scaleY(lineWidthScaleFactor(), backgroundScale.y() * lineLengthScaleFactor(), lineWidthScaleFactor());
1328 QVector3D scaleZ(backgroundScale.z() * lineLengthScaleFactor(), lineWidthScaleFactor(), lineWidthScaleFactor());
1329
1330 auto axisX = m_controller->axisX();
1331 auto axisY = m_controller->axisY();
1332 auto axisZ = m_controller->axisZ();
1333
1334 const bool xFlipped = isXFlipped();
1335 const bool yFlipped = isYFlipped();
1336 const bool zFlipped = isZFlipped();
1337
1338 QQuaternion lineRotation(.0f, .0f, .0f, .0f);
1339 QVector3D rotation(90.0f, 0.0f, 0.0f);
1340
1341 // Floor horizontal line
1342 float linePosX = 0.0f;
1343 float linePosY = backgroundScale.y();
1344 float linePosZ = 0.0f;
1345 float scale = m_scaleWithBackground.z();
1346
1347 if (!yFlipped) {
1348 linePosY *= -1.0f;
1349 rotation.setZ(180.0f);
1350 }
1351 lineRotation = Utils::calculateRotation(xyzRotations: rotation);
1352 for (int i = 0; i < subGridLineCountZ; i++) {
1353 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterZ()->objectAt(index: i));
1354 if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
1355 linePosZ = static_cast<QValue3DAxis *>(axisZ)->subGridPositionAt(gridLine: i) * -scale * 2.0f + scale;
1356 } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
1357 linePosZ = calculateCategoryGridLinePosition(axis: axisZ, index: i);
1358 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1359 }
1360 if (isPolar()) {
1361 lineNode->setPosition(QVector3D(0.0f, linePosY, 0.0f));
1362 lineNode->setScale(QVector3D(scaleX.x() * .5f, scaleX.x() * .5f, scaleX.z()));
1363 lineNode->setProperty(name: "polarRadius",
1364 value: static_cast<QValue3DAxis *>(axisZ)->subGridPositionAt(gridLine: i)
1365 / (scaleX.x() * .5f) * 2.0f);
1366 } else {
1367 positionAndScaleLine(lineNode, scale: scaleX, position: QVector3D(linePosX, linePosY, linePosZ));
1368 }
1369 lineNode->setRotation(lineRotation);
1370 lineNode->setProperty(name: "isPolar", value: isPolar());
1371 }
1372
1373 for (int i = 0; i < gridLineCountZ; i++) {
1374 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterZ()->objectAt(index: i));
1375 if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
1376 linePosZ = static_cast<QValue3DAxis *>(axisZ)->gridPositionAt(gridLine: i) * -scale * 2.0f + scale;
1377 } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
1378 linePosZ = calculateCategoryGridLinePosition(axis: axisZ, index: i);
1379 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1380 }
1381 if (isPolar()) {
1382 lineNode->setPosition(QVector3D(0.0f, linePosY, 0.0f));
1383 lineNode->setScale(QVector3D(scaleX.x() * .5f, scaleX.x() * .5f, scaleX.z()));
1384 lineNode->setProperty(name: "polarRadius",
1385 value: static_cast<QValue3DAxis *>(axisZ)->gridPositionAt(gridLine: i)
1386 / (scaleX.x() * .5f) * 2.0f);
1387 } else {
1388 positionAndScaleLine(lineNode, scale: scaleX, position: QVector3D(linePosX, linePosY, linePosZ));
1389 }
1390 lineNode->setRotation(lineRotation);
1391 lineNode->setProperty(name: "isPolar", value: isPolar());
1392 }
1393
1394 // Side vertical line
1395 linePosX = -backgroundScale.x();
1396 linePosY = 0.0f;
1397 rotation = QVector3D(0.0f, 90.0f, 0.0f);
1398 if (xFlipped) {
1399 linePosX *= -1.0f;
1400 rotation.setY(-90.0f);
1401 }
1402 lineRotation = Utils::calculateRotation(xyzRotations: rotation);
1403 if (m_hasVerticalSegmentLine) {
1404 for (int i = 0; i < subGridLineCountZ; i++) {
1405 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterZ()->objectAt(index: i + subGridLineCountZ));
1406 if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
1407 linePosZ = static_cast<QValue3DAxis *>(axisZ)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale;
1408 } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
1409 linePosX = calculateCategoryGridLinePosition(axis: axisZ, index: i);
1410 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1411 }
1412 positionAndScaleLine(lineNode, scale: scaleY, position: QVector3D(linePosX, linePosY, linePosZ));
1413 lineNode->setRotation(lineRotation);
1414 }
1415 for (int i = 0; i < gridLineCountZ; i++) {
1416 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterZ()->objectAt(index: i + gridLineCountZ));
1417 if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
1418 linePosZ = static_cast<QValue3DAxis *>(axisZ)->gridPositionAt(gridLine: i) * scale * 2.0f - scale;
1419 } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
1420 linePosX = calculateCategoryGridLinePosition(axis: axisZ, index: i);
1421 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1422 }
1423 positionAndScaleLine(lineNode, scale: scaleY, position: QVector3D(linePosX, linePosY, linePosZ));
1424 lineNode->setRotation(lineRotation);
1425 }
1426 }
1427
1428 // Side horizontal line
1429 linePosZ = 0.0f;
1430 scale = m_scaleWithBackground.y();
1431 rotation = QVector3D(180.0f, -90.0f, 0.0f);
1432 if (xFlipped)
1433 rotation.setY(90.0f);
1434 lineRotation = Utils::calculateRotation(xyzRotations: rotation);
1435 for (int i = 0; i < gridLineCountY; i++) {
1436 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterY()->objectAt(index: i));
1437 if (axisY->type() == QAbstract3DAxis::AxisTypeValue)
1438 linePosY = static_cast<QValue3DAxis *>(axisY)->gridPositionAt(gridLine: i) * scale * 2.0f - scale;
1439 else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory)
1440 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1441 positionAndScaleLine(lineNode, scale: scaleZ, position: QVector3D(linePosX, linePosY, linePosZ));
1442 lineNode->setRotation(lineRotation);
1443 }
1444
1445 for (int i = 0; i < subGridLineCountY; i++) {
1446 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterY()->objectAt(index: i));
1447 if (axisY->type() == QAbstract3DAxis::AxisTypeValue)
1448 linePosY = static_cast<QValue3DAxis *>(axisY)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale;
1449 else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory)
1450 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1451 positionAndScaleLine(lineNode, scale: scaleZ, position: QVector3D(linePosX, linePosY, linePosZ));
1452 lineNode->setRotation(lineRotation);
1453 }
1454
1455 // Floor vertical line
1456 linePosZ = 0.0f;
1457 linePosY = -backgroundScale.y();
1458 rotation = QVector3D(-90.0f, 90.0f, 0.0f);
1459 if (yFlipped) {
1460 linePosY *= -1.0f;
1461 rotation.setZ(180.0f);
1462 }
1463 scale = m_scaleWithBackground.x();
1464 for (int i = 0; i < subGridLineCountX; i++) {
1465 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterX()->objectAt(index: i));
1466 if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
1467 linePosX = static_cast<QValue3DAxis *>(axisX)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale;
1468 } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
1469 linePosX = calculateCategoryGridLinePosition(axis: axisX, index: i);
1470 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1471 }
1472 if (isPolar()) {
1473 lineNode->setPosition(QVector3D(.0f, linePosY, .0f));
1474 rotation.setY(static_cast<QValue3DAxis *>(axisX)->gridPositionAt(gridLine: i) * 360.0f);
1475 } else {
1476 positionAndScaleLine(lineNode, scale: scaleZ, position: QVector3D(linePosX, linePosY, linePosZ));
1477 }
1478 lineRotation = Utils::calculateRotation(xyzRotations: rotation);
1479 lineNode->setRotation(lineRotation);
1480 }
1481 for (int i = 0; i < gridLineCountX; i++) {
1482 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterX()->objectAt(index: i));
1483 if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
1484 linePosX = static_cast<QValue3DAxis *>(axisX)->gridPositionAt(gridLine: i) * scale * 2.0f - scale;
1485 } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
1486 linePosX = calculateCategoryGridLinePosition(axis: axisX, index: i);
1487 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1488 }
1489 if (isPolar()) {
1490 lineNode->setPosition(QVector3D(.0f, linePosY, .0f));
1491 rotation.setY(static_cast<QValue3DAxis *>(axisX)->gridPositionAt(gridLine: i) * 360.0f);
1492 } else {
1493 positionAndScaleLine(lineNode, scale: scaleZ, position: QVector3D(linePosX, linePosY, linePosZ));
1494 }
1495 lineRotation = Utils::calculateRotation(xyzRotations: rotation);
1496 lineNode->setRotation(lineRotation);
1497 }
1498
1499 // Back horizontal line
1500 linePosX = 0.0f;
1501 linePosZ = -backgroundScale.z();
1502 rotation = QVector3D(0.0f, 0.0f, 0.0f);
1503 if (zFlipped) {
1504 linePosZ *= -1.0f;
1505 rotation.setX(180.0f);
1506 }
1507 lineRotation = Utils::calculateRotation(xyzRotations: rotation);
1508 scale = m_scaleWithBackground.y();
1509 for (int i = 0; i < subGridLineCountY; i++) {
1510 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterY()->objectAt(index: i + subGridLineCountY));
1511 if (axisY->type() == QAbstract3DAxis::AxisTypeValue)
1512 linePosY = static_cast<QValue3DAxis *>(axisY)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale;
1513 else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory)
1514 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1515 positionAndScaleLine(lineNode, scale: scaleX, position: QVector3D(linePosX, linePosY, linePosZ));
1516 lineNode->setRotation(lineRotation);
1517 }
1518
1519 for (int i = 0; i < gridLineCountY; i++) {
1520 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterY()->objectAt(index: i + gridLineCountY));
1521 if (axisY->type() == QAbstract3DAxis::AxisTypeValue)
1522 linePosY = static_cast<QValue3DAxis *>(axisY)->gridPositionAt(gridLine: i) * scale * 2.0f - scale;
1523 else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory)
1524 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1525 positionAndScaleLine(lineNode, scale: scaleX, position: QVector3D(linePosX, linePosY, linePosZ));
1526 lineNode->setRotation(lineRotation);
1527 }
1528
1529 // Back vertical line
1530 linePosY = 0.0f;
1531 scale = m_scaleWithBackground.x();
1532 rotation = QVector3D(0.0f, 0.0f, 0.0f);
1533 if (zFlipped)
1534 rotation.setY(180.0f);
1535 lineRotation = Utils::calculateRotation(xyzRotations: rotation);
1536 if (m_hasVerticalSegmentLine) {
1537 for (int i = 0; i < gridLineCountX; i++) {
1538 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterX()->objectAt(index: i + gridLineCountX));
1539 if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
1540 linePosX = static_cast<QValue3DAxis *>(axisX)->gridPositionAt(gridLine: i) * scale * 2.0f - scale;
1541 } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
1542 linePosX = calculateCategoryGridLinePosition(axis: axisX, index: i);
1543 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1544 }
1545 positionAndScaleLine(lineNode, scale: scaleY, position: QVector3D(linePosX, linePosY, linePosZ));
1546 lineNode->setRotation(lineRotation);
1547 }
1548
1549 for (int i = 0; i < subGridLineCountX; i++) {
1550 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterX()->objectAt(index: i + subGridLineCountX));
1551 if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
1552 linePosX = static_cast<QValue3DAxis *>(axisX)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale;
1553 } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
1554 linePosX = calculateCategoryGridLinePosition(axis: axisX, index: i);
1555 linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i);
1556 }
1557 positionAndScaleLine(lineNode, scale: scaleY, position: QVector3D(linePosX, linePosY, linePosZ));
1558 lineNode->setRotation(lineRotation);
1559 }
1560 }
1561}
1562
1563float QQuickGraphsItem::fontScaleFactor(float pointSize)
1564{
1565 return m_fontScaleFactorA * pointSize
1566 + m_fontScaleFactorB;
1567}
1568
1569float QQuickGraphsItem::labelAdjustment(float width)
1570{
1571 float a = -2.43761e-13f;
1572 float b = 4.23579e-10f;
1573 float c = 0.00414881f;
1574
1575 float factor = a * qPow(x: width, y: 3) + b * qPow(x: width, y: 2) + c;
1576#if defined(Q_OS_WIN)
1577 factor *= .8f;
1578#endif
1579 float ret = width * .5f * factor;
1580 return ret;
1581}
1582
1583void QQuickGraphsItem::updateLabels()
1584{
1585 auto axisX = m_controller->axisX();
1586
1587 auto labels = axisX->labels();
1588 int labelCount = labels.size();
1589 float labelAutoAngle = axisX->labelAutoRotation();
1590 float labelAngleFraction = labelAutoAngle / 90.0f;
1591 float fractionCamX = m_controller->scene()->activeCamera()->xRotation() * labelAngleFraction;
1592 float fractionCamY = m_controller->scene()->activeCamera()->yRotation() * labelAngleFraction;
1593
1594 QVector3D labelRotation = QVector3D(0.0f, 0.0f, 0.0f);
1595
1596 float xPos = 0.0f;
1597 float yPos = 0.0f;
1598 float zPos = 0.0f;
1599
1600 const bool xFlipped = isXFlipped();
1601 const bool yFlipped = isYFlipped();
1602 const bool zFlipped = isZFlipped();
1603
1604 auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin;
1605
1606 if (labelAutoAngle == 0.0f) {
1607 labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
1608 if (xFlipped)
1609 labelRotation.setY(-90.0f);
1610 if (yFlipped) {
1611 if (xFlipped)
1612 labelRotation.setY(-90.0f);
1613 else
1614 labelRotation.setY(90.0f);
1615 labelRotation.setX(90.0f);
1616 }
1617 } else {
1618 if (xFlipped)
1619 labelRotation.setY(-90.0f);
1620 else
1621 labelRotation.setY(90.0f);
1622 if (yFlipped) {
1623 if (zFlipped) {
1624 if (xFlipped) {
1625 labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
1626 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
1627 labelRotation.setZ(-labelAutoAngle - fractionCamY);
1628 } else {
1629 labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
1630 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
1631 labelRotation.setZ(labelAutoAngle + fractionCamY);
1632 }
1633 } else {
1634 if (xFlipped) {
1635 labelRotation.setX(90.0f + fractionCamX
1636 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
1637 labelRotation.setZ(labelAutoAngle + fractionCamY);
1638 } else {
1639 labelRotation.setX(90.0f - fractionCamX
1640 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
1641 labelRotation.setZ(-labelAutoAngle - fractionCamY);
1642 }
1643 }
1644 } else {
1645 if (zFlipped) {
1646 if (xFlipped) {
1647 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
1648 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
1649 labelRotation.setZ(labelAutoAngle - fractionCamY);
1650 } else {
1651 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
1652 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
1653 labelRotation.setZ(-labelAutoAngle + fractionCamY);
1654 }
1655 } else {
1656 if (xFlipped) {
1657 labelRotation.setX(-90.0f - fractionCamX
1658 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
1659 labelRotation.setZ(-labelAutoAngle + fractionCamY);
1660 } else {
1661 labelRotation.setX(-90.0f + fractionCamX
1662 * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
1663 labelRotation.setZ(labelAutoAngle - fractionCamY);
1664 }
1665 }
1666 }
1667 }
1668 if (isPolar())
1669 labelRotation.setY(0.0f);
1670 auto totalRotation = Utils::calculateRotation(xyzRotations: labelRotation);
1671
1672 float scale = backgroundScale.x() - m_backgroundScaleMargin.x();
1673
1674 float pointSize = m_controller->activeTheme()->font().pointSizeF();
1675
1676 float textPadding = pointSize * .5f;
1677
1678 float labelsMaxWidth = float(findLabelsMaxWidth(labels: axisX->labels())) + textPadding;
1679 QFontMetrics fm(m_controller->activeTheme()->font());
1680 float labelHeight = fm.height() + textPadding;
1681
1682 float scaleFactor = fontScaleFactor(pointSize) * pointSize;
1683 float fontRatio = labelsMaxWidth / labelHeight;
1684 QVector3D fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f);
1685 auto adjustment = labelAdjustment(width: labelsMaxWidth);
1686 zPos = backgroundScale.z() + adjustment + m_labelMargin;
1687
1688 adjustment *= qAbs(t: qSin(v: qDegreesToRadians(degrees: labelRotation.z())));
1689 yPos = backgroundScale.y() + adjustment;
1690
1691 if (!yFlipped)
1692 yPos *= -1.0f;
1693
1694 if (zFlipped)
1695 zPos *= -1.0f;
1696
1697 auto labelTrans = QVector3D(0.0f, yPos, zPos);
1698 float polarLabelZPos = 0.0f;
1699
1700 if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
1701 auto valueAxisX = static_cast<QValue3DAxis *>(axisX);
1702 for (int i = 0; i < repeaterX()->count(); i++) {
1703 if (labelCount <= i)
1704 break;
1705 auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(index: i));
1706 if (isPolar()) {
1707 if (i == repeaterX()->count() - 1) {
1708 obj->setVisible(false);
1709 break;
1710 }
1711 float rad = qDegreesToRadians(degrees: valueAxisX->labelPositionAt(index: i) * 360.0f);
1712 labelTrans.setX(-qSin(v: rad) * -scale + qSin(v: rad) * m_labelMargin * 2.0f);
1713 labelTrans.setY(yPos);
1714 labelTrans.setZ(qCos(v: rad) * -scale - qCos(v: rad) * m_labelMargin * 2.0f);
1715 if (i == 0)
1716 polarLabelZPos = labelTrans.z();
1717 } else {
1718 labelTrans.setX(valueAxisX->labelPositionAt(index: i) * scale * 2.0f - scale);
1719 }
1720 obj->setObjectName(QStringLiteral("ElementAxisXLabel"));
1721 obj->setScale(fontScaled);
1722 obj->setPosition(labelTrans);
1723 obj->setRotation(totalRotation);
1724 obj->setProperty(name: "labelText", value: labels[i]);
1725 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
1726 obj->setProperty(name: "labelHeight", value: labelHeight);
1727 }
1728 } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
1729 for (int i = 0; i < repeaterX()->count(); i++) {
1730 if (labelCount <= i)
1731 break;
1732 labelTrans = calculateCategoryLabelPosition(axis: axisX, labelPosition: labelTrans, index: i);
1733 auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(index: i));
1734 obj->setObjectName(QStringLiteral("ElementAxisXLabel"));
1735 obj->setScale(fontScaled);
1736 obj->setPosition(labelTrans);
1737 obj->setRotation(totalRotation);
1738 obj->setProperty(name: "labelText", value: labels[i]);
1739 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
1740 obj->setProperty(name: "labelHeight", value: labelHeight);
1741 }
1742 }
1743
1744 float x = labelTrans.x();
1745 labelTrans.setX(0.0f);
1746 updateXTitle(labelRotation, labelTrans, totalRotation, labelsMaxWidth, scale: fontScaled);
1747 if (isPolar()) {
1748 m_titleLabelX->setZ(polarLabelZPos - m_labelMargin * 2.0f);
1749 m_titleLabelX->setRotation(totalRotation);
1750 }
1751 labelTrans.setX(x);
1752
1753 auto axisY = m_controller->axisY();
1754 labels = axisY->labels();
1755 labelCount = labels.size();
1756 labelAutoAngle = axisY->labelAutoRotation();
1757 labelAngleFraction = labelAutoAngle / 90.0f;
1758 fractionCamX = m_controller->scene()->activeCamera()->xRotation() * labelAngleFraction;
1759 fractionCamY = m_controller->scene()->activeCamera()->yRotation() * labelAngleFraction;
1760
1761 QVector3D sideLabelRotation(0.0f, -90.0f, 0.0f);
1762 QVector3D backLabelRotation(0.0f, 0.0f, 0.0f);
1763
1764 if (labelAutoAngle == 0.0f) {
1765 if (!xFlipped)
1766 sideLabelRotation.setY(90.0f);
1767 if (zFlipped)
1768 backLabelRotation.setY(180.f);
1769 } else {
1770 // Orient side labels somewhat towards the camera
1771 if (xFlipped) {
1772 if (zFlipped)
1773 backLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
1774 else
1775 backLabelRotation.setY(-fractionCamX);
1776 sideLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
1777 } else {
1778 if (zFlipped)
1779 backLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
1780 else
1781 backLabelRotation.setY(-fractionCamX);
1782 sideLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
1783 }
1784 }
1785
1786 backLabelRotation.setX(-fractionCamY);
1787 sideLabelRotation.setX(-fractionCamY);
1788
1789 totalRotation = Utils::calculateRotation(xyzRotations: sideLabelRotation);
1790 scale = backgroundScale.y() - m_backgroundScaleMargin.y();
1791 labelsMaxWidth = float(findLabelsMaxWidth(labels: axisY->labels())) + textPadding;
1792 fontRatio = labelsMaxWidth / labelHeight;
1793 fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f);
1794
1795 xPos = backgroundScale.x();
1796 if (!xFlipped)
1797 xPos *= -1.0f;
1798 labelTrans.setX(xPos);
1799
1800 adjustment = labelAdjustment(width: labelsMaxWidth);
1801 zPos = backgroundScale.z() + adjustment + m_labelMargin;
1802 if (zFlipped)
1803 zPos *= -1.0f;
1804 labelTrans.setZ(zPos);
1805
1806 for (int i = 0; i < repeaterY()->count() / 2; i++) {
1807 if (labelCount <= i)
1808 break;
1809 auto obj = static_cast<QQuick3DNode *>(repeaterY()->objectAt(index: i));
1810 labelTrans.setY(static_cast<QValue3DAxis *>(axisY)->labelPositionAt(index: i) * scale * 2.0f - scale);
1811 obj->setObjectName(QStringLiteral("ElementAxisYLabel"));
1812 obj->setScale(fontScaled);
1813 obj->setPosition(labelTrans);
1814 obj->setRotation(totalRotation);
1815 obj->setProperty(name: "labelText", value: labels[i]);
1816 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
1817 obj->setProperty(name: "labelHeight", value: labelHeight);
1818 }
1819
1820 auto sideLabelTrans = labelTrans;
1821 auto totalSideLabelRotation = totalRotation;
1822
1823 auto axisZ = m_controller->axisZ();
1824 labels = axisZ->labels();
1825 labelCount = labels.size();
1826 labelAutoAngle = axisZ->labelAutoRotation();
1827 labelAngleFraction = labelAutoAngle / 90.0f;
1828 fractionCamX = m_controller->scene()->activeCamera()->xRotation() * labelAngleFraction;
1829 fractionCamY = m_controller->scene()->activeCamera()->yRotation() * labelAngleFraction;
1830
1831 if (labelAutoAngle == 0.0f) {
1832 labelRotation = QVector3D(90.0f, 0.0f, 0.0f);
1833 if (zFlipped)
1834 labelRotation.setY(180.0f);
1835 if (yFlipped) {
1836 if (zFlipped)
1837 labelRotation.setY(180.0f);
1838 else
1839 labelRotation.setY(0.0f);
1840 labelRotation.setX(90.0f);
1841 } else {
1842 labelRotation.setX(-90.0f);
1843 }
1844 } else {
1845 if (zFlipped)
1846 labelRotation.setY(180.0f);
1847 else
1848 labelRotation.setY(0.0f);
1849 if (yFlipped) {
1850 if (zFlipped) {
1851 if (xFlipped) {
1852 labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
1853 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
1854 labelRotation.setZ(labelAutoAngle + fractionCamY);
1855 } else {
1856 labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
1857 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
1858 labelRotation.setZ(-labelAutoAngle - fractionCamY);
1859 }
1860 } else {
1861 if (xFlipped) {
1862 labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
1863 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
1864 labelRotation.setZ(-labelAutoAngle - fractionCamY);
1865 } else {
1866 labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
1867 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
1868 labelRotation.setZ(labelAutoAngle + fractionCamY);
1869 }
1870 }
1871 } else {
1872 if (zFlipped) {
1873 if (xFlipped) {
1874 labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
1875 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
1876 labelRotation.setZ(-labelAutoAngle + fractionCamY);
1877 } else {
1878 labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
1879 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
1880 labelRotation.setZ(labelAutoAngle - fractionCamY);
1881 }
1882 } else {
1883 if (xFlipped) {
1884 labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
1885 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
1886 labelRotation.setZ(labelAutoAngle - fractionCamY);
1887 } else {
1888 labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
1889 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
1890 labelRotation.setZ(-labelAutoAngle + fractionCamY);
1891 }
1892 }
1893 }
1894 }
1895
1896 totalRotation = Utils::calculateRotation(xyzRotations: labelRotation);
1897
1898 scale = backgroundScale.z() - m_backgroundScaleMargin.z();
1899 labelsMaxWidth = float(findLabelsMaxWidth(labels: axisZ->labels())) + textPadding ;
1900 fontRatio = labelsMaxWidth / labelHeight;
1901 fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f);
1902 adjustment = labelAdjustment(width: labelsMaxWidth);
1903 xPos = backgroundScale.x() + adjustment + m_labelMargin;
1904 if (xFlipped)
1905 xPos *= -1.0f;
1906
1907 adjustment *= qAbs(t: qSin(v: qDegreesToRadians(degrees: labelRotation.z())));
1908 yPos = backgroundScale.y() + adjustment;
1909 if (!yFlipped)
1910 yPos *= -1.0f;
1911
1912 labelTrans = QVector3D(xPos, yPos, 0.0f);
1913
1914 if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
1915 auto valueAxisZ = static_cast<QValue3DAxis *>(axisZ);
1916 float offset = m_controller->radialLabelOffset();
1917 for (int i = 0; i < repeaterZ()->count(); i++) {
1918 if (labelCount <= i)
1919 break;
1920 auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i));
1921 if (isPolar()) {
1922 float polarX = backgroundScale.x() * offset + m_labelMargin * 2.0f;
1923 if (xFlipped)
1924 polarX *= -1;
1925 labelTrans.setX(polarX);
1926 labelTrans.setY(yPos);
1927 labelTrans.setZ(-valueAxisZ->labelPositionAt(index: i));
1928 } else {
1929 labelTrans.setZ(valueAxisZ->labelPositionAt(index: i) * scale * -2.0f + scale);
1930 }
1931 obj->setObjectName(QStringLiteral("ElementAxisZLabel"));
1932 obj->setScale(fontScaled);
1933 obj->setPosition(labelTrans);
1934 obj->setRotation(totalRotation);
1935 obj->setProperty(name: "labelText", value: labels[i]);
1936 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
1937 obj->setProperty(name: "labelHeight", value: labelHeight);
1938 }
1939 } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
1940 for (int i = 0; i < repeaterZ()->count(); i++) {
1941 if (labelCount <= i)
1942 break;
1943 labelTrans = calculateCategoryLabelPosition(axis: axisZ, labelPosition: labelTrans, index: i);
1944 auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i));
1945 obj->setObjectName(QStringLiteral("ElementAxisZLabel"));
1946 obj->setScale(fontScaled);
1947 obj->setPosition(labelTrans);
1948 obj->setRotation(totalRotation);
1949 obj->setProperty(name: "labelText", value: labels[i]);
1950 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
1951 obj->setProperty(name: "labelHeight", value: labelHeight);
1952 }
1953 }
1954
1955 float z = labelTrans.z();
1956 labelTrans.setZ(0.0f);
1957 updateZTitle(labelRotation, labelTrans, totalRotation, labelsMaxWidth, scale: fontScaled);
1958 labelTrans.setZ(z);
1959
1960 labels = axisY->labels();
1961 labelCount = labels.size();
1962 totalRotation = Utils::calculateRotation(xyzRotations: backLabelRotation);
1963 scale = backgroundScale.y() - m_backgroundScaleMargin.y();
1964 labelsMaxWidth = float(findLabelsMaxWidth(labels: axisY->labels())) + textPadding;
1965 fontRatio = labelsMaxWidth / labelHeight;
1966 fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f);
1967 adjustment = labelAdjustment(width: labelsMaxWidth);
1968
1969 xPos = backgroundScale.x() + adjustment + m_labelMargin;
1970 if (xFlipped)
1971 xPos *= -1.0f;
1972 labelTrans.setX(xPos);
1973
1974 zPos = -backgroundScale.z();
1975 if (zFlipped)
1976 zPos *= -1.0f;
1977 labelTrans.setZ(zPos);
1978
1979 for (int i = 0; i < repeaterY()->count() / 2; i++) {
1980 if (labelCount <= i)
1981 break;
1982 auto obj = static_cast<QQuick3DNode *>(repeaterY()->objectAt(index: i + (repeaterY()->count() / 2)));
1983 labelTrans.setY(static_cast<QValue3DAxis *>(axisY)->labelPositionAt(index: i) * scale * 2.0f - scale);
1984 obj->setScale(fontScaled);
1985 obj->setPosition(labelTrans);
1986 obj->setRotation(totalRotation);
1987 obj->setProperty(name: "labelText", value: labels[i]);
1988 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
1989 obj->setProperty(name: "labelHeight", value: labelHeight);
1990 }
1991
1992 auto backLabelTrans = labelTrans;
1993 auto totalBackLabelRotation = totalRotation;
1994 updateYTitle(sideLabelRotation, backLabelRotation,
1995 sideLabelTrans, backLabelTrans,
1996 totalSideRotation: totalSideLabelRotation, totalBackRotation: totalBackLabelRotation,
1997 labelsMaxWidth, scale: fontScaled);
1998}
1999
2000void QQuickGraphsItem::updateRadialLabelOffset()
2001{
2002 if (!isPolar())
2003 return;
2004
2005 QAbstract3DAxis *axisZ = m_controller->axisZ();
2006 QVector3D backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin;
2007 float offset = m_controller->radialLabelOffset();
2008 float scale = backgroundScale.x() + (m_backgroundScaleMargin.x());
2009 float polarX = scale * offset + m_labelMargin * 2.0f;
2010 if (isXFlipped())
2011 polarX *= -1;
2012 if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
2013 for (int i = 0; i < repeaterZ()->count(); i++) {
2014 QQuick3DNode *obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i));
2015 QVector3D pos = obj->position();
2016 pos.setX(polarX);
2017 obj->setPosition(pos);
2018 }
2019 }
2020
2021 polarX += m_labelMargin * 2.5f;
2022 QVector3D pos = m_titleLabelZ->position();
2023 pos.setX(polarX);
2024 m_titleLabelZ->setPosition(pos);
2025}
2026
2027void QQuickGraphsItem::positionAndScaleLine(QQuick3DNode *lineNode, QVector3D scale, QVector3D position)
2028{
2029 lineNode->setScale(scale);
2030 lineNode->setPosition(position);
2031}
2032
2033void QQuickGraphsItem::graphPositionAt(const QPoint &point)
2034{
2035 bool isHit = false;
2036 auto result = pick(x: point.x(), y: point.y());
2037 if (result.objectHit()) {
2038 isHit = true;
2039 m_controller->setQueriedGraphPosition(QVector3D(result.scenePosition().x(),
2040 result.scenePosition().y(),
2041 result.scenePosition().z()));
2042 }
2043
2044 if (!isHit)
2045 m_controller->setQueriedGraphPosition(QVector3D(0,0,0));
2046
2047 emit queriedGraphPositionChanged(data: m_controller->queriedGraphPosition());
2048 emit m_controller->queriedGraphPositionChanged(data: m_controller->queriedGraphPosition());
2049 m_controller->setGraphPositionQueryPending(false);
2050 scene()->setGraphPositionQuery(Q3DScene::invalidSelectionPoint());
2051}
2052
2053void QQuickGraphsItem::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality)
2054{
2055 if (quality != QAbstract3DGraph::ShadowQualityNone) {
2056 light()->setCastsShadow(true);
2057 light()->setShadowFactor(25.f);
2058
2059 QQuick3DAbstractLight::QSSGShadowMapQuality shadowMapQuality;
2060 switch (quality) {
2061 case QAbstract3DGraph::ShadowQualityLow:
2062 case QAbstract3DGraph::ShadowQualitySoftLow:
2063 shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityMedium;
2064 break;
2065 case QAbstract3DGraph::ShadowQualityMedium:
2066 case QAbstract3DGraph::ShadowQualitySoftMedium:
2067 shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityHigh;
2068 break;
2069 case QAbstract3DGraph::ShadowQualityHigh:
2070 case QAbstract3DGraph::ShadowQualitySoftHigh:
2071 shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityVeryHigh;
2072 break;
2073 default:
2074 shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityHigh;
2075 break;
2076 }
2077 light()->setShadowMapQuality(shadowMapQuality);
2078 if (quality >= QAbstract3DGraph::ShadowQualitySoftLow)
2079 light()->setShadowFilter(10.f);
2080 else
2081 light()->setShadowFilter(2.f);
2082 } else {
2083 light()->setCastsShadow(false);
2084 light()->setShadowFactor(0.f);
2085 }
2086}
2087
2088void QQuickGraphsItem::updateItemLabel(const QVector3D &position)
2089{
2090 if (m_labelPosition != position)
2091 m_labelPosition = position;
2092 QVector3D pos2d = mapFrom3DScene(scenePos: m_labelPosition);
2093 int pointSize = m_controller->activeTheme()->font().pointSize();
2094 float scale = m_labelScale.x() * ((-10.0f * pointSize) + 650.0f) / pos2d.z();
2095 if (m_sliceView && m_sliceView->isVisible())
2096 m_itemLabel->setScale(scale * .2f);
2097 else
2098 m_itemLabel->setScale(scale);
2099 pos2d.setX(pos2d.x() - (m_itemLabel->width() / 2.f));
2100 pos2d.setY(pos2d.y() - (m_itemLabel->height() / 2.f) - (m_itemLabel->height() * m_itemLabel->scale()));
2101 m_itemLabel->setPosition(pos2d.toPointF());
2102}
2103
2104void QQuickGraphsItem::createVolumeMaterial(QCustom3DVolume *volume, Volume &volumeItem)
2105{
2106 if (volumeItem.texture)
2107 volumeItem.texture->deleteLater();
2108 volumeItem.texture = new QQuick3DTexture();
2109 auto texture = volumeItem.texture;
2110
2111 texture->setParent(this);
2112 texture->setMinFilter(QQuick3DTexture::Filter::Nearest);
2113 texture->setMagFilter(QQuick3DTexture::Filter::Nearest);
2114 texture->setHorizontalTiling(QQuick3DTexture::TilingMode::ClampToEdge);
2115 texture->setVerticalTiling(QQuick3DTexture::TilingMode::ClampToEdge);
2116
2117 if (volumeItem.textureData)
2118 volumeItem.textureData->deleteLater();
2119 volumeItem.textureData = new QQuick3DTextureData();
2120 auto textureData = volumeItem.textureData;
2121
2122 int color8Bit = (volume->textureFormat() == QImage::Format_Indexed8) ? 1 : 0;
2123
2124 textureData->setParent(texture);
2125 textureData->setParentItem(texture);
2126 textureData->setSize(QSize(volume->textureWidth(), volume->textureHeight()));
2127 textureData->setDepth(volume->textureDepth());
2128 if (color8Bit)
2129 textureData->setFormat(QQuick3DTextureData::R8);
2130 else
2131 textureData->setFormat(QQuick3DTextureData::RGBA8);
2132 textureData->setTextureData(QByteArray::fromRawData(
2133 data: reinterpret_cast<const char *>(volume->textureData()->constData()),
2134 size: volume->textureData()->size()));
2135 texture->setTextureData(textureData);
2136
2137 QObject::connect(sender: volume, signal: &QCustom3DVolume::textureDataChanged, context: this, slot: [this, volume] {
2138 m_customVolumes[volume].updateTextureData = true;
2139 });
2140
2141 if (color8Bit) {
2142 if (volumeItem.colorTexture)
2143 volumeItem.colorTexture->deleteLater();
2144 volumeItem.colorTexture = new QQuick3DTexture();
2145 auto colorTexture = volumeItem.colorTexture;
2146
2147 colorTexture->setParent(this);
2148 colorTexture->setMinFilter(QQuick3DTexture::Filter::Nearest);
2149 colorTexture->setMagFilter(QQuick3DTexture::Filter::Nearest);
2150
2151 QByteArray colorTableBytes;
2152 const QList<QRgb> &colorTable = volume->colorTable();
2153 for (int i = 0; i < colorTable.size(); i++) {
2154 QRgb shifted = qRgba(r: qBlue(rgb: colorTable[i]), g: qGreen(rgb: colorTable[i]), b: qRed(rgb: colorTable[i]), a: qAlpha(rgb: colorTable[i]));
2155 colorTableBytes.append(a: QByteArray::fromRawData(data: reinterpret_cast<const char *>(&shifted), size: sizeof(shifted)));
2156 }
2157
2158 if (volumeItem.colorTextureData)
2159 volumeItem.colorTextureData->deleteLater();
2160 volumeItem.colorTextureData = new QQuick3DTextureData();
2161 auto colorTextureData = volumeItem.colorTextureData;
2162
2163 colorTextureData->setParent(colorTexture);
2164 colorTextureData->setParentItem(colorTexture);
2165 colorTextureData->setSize(QSize(volume->colorTable().size(), 1));
2166 colorTextureData->setFormat(QQuick3DTextureData::RGBA8);
2167 colorTextureData->setTextureData(colorTableBytes);
2168 colorTexture->setTextureData(colorTextureData);
2169
2170 QObject::connect(sender: volume, signal: &QCustom3DVolume::colorTableChanged, context: this, slot: [this, volume] {
2171 m_customVolumes[volume].updateColorTextureData = true;
2172 });
2173 }
2174
2175 auto model = volumeItem.model;
2176 QQmlListReference materialsRef(model, "materials");
2177
2178 QQuick3DCustomMaterial *material = nullptr;
2179
2180 if (volume->drawSlices())
2181 material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeSliceMaterial"));
2182 else if (volume->useHighDefShader())
2183 material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeMaterial"));
2184 else
2185 material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeLowDefMaterial"));
2186
2187 auto textureSamplerVariant = material->property(name: "textureSampler");
2188 auto textureSampler = textureSamplerVariant.value<QQuick3DShaderUtilsTextureInput *>();
2189 textureSampler->setTexture(volumeItem.texture);
2190
2191 if (color8Bit) {
2192 auto colorSamplerVariant = material->property(name: "colorSampler");
2193 auto colorSampler = colorSamplerVariant.value<QQuick3DShaderUtilsTextureInput *>();
2194 colorSampler->setTexture(volumeItem.colorTexture);
2195 }
2196
2197 material->setProperty(name: "textureDimensions", value: QVector3D(1.0f / float(volume->textureWidth()),
2198 1.0f / float(volume->textureHeight()),
2199 1.0f / float(volume->textureDepth())));
2200
2201 materialsRef.append(material);
2202
2203 volumeItem.useHighDefShader = volume->useHighDefShader();
2204 volumeItem.drawSlices = volume->drawSlices();
2205}
2206
2207QQuick3DModel *QQuickGraphsItem::createSliceFrame(Volume &volumeItem)
2208{
2209 QQuick3DModel *model = new QQuick3DModel();
2210 model->setParent(volumeItem.model);
2211 model->setParentItem(volumeItem.model);
2212 model->setSource(QUrl(QStringLiteral("defaultMeshes/barMeshFull")));
2213 model->setScale(QVector3D(1, 1, 0.01f));
2214 model->setDepthBias(-100.0f);
2215
2216 QQmlListReference materialsRef(model, "materials");
2217 QQuick3DCustomMaterial *material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeFrameMaterial"));
2218 material->setParent(model);
2219 material->setParentItem(model);
2220 material->setCullMode(QQuick3DMaterial::NoCulling);
2221 materialsRef.append(material);
2222
2223 return model;
2224}
2225
2226void QQuickGraphsItem::updateSliceFrameMaterials(QCustom3DVolume *volume, Volume &volumeItem)
2227{
2228 QQmlListReference materialsRefX(volumeItem.sliceFrameX, "materials");
2229 QQmlListReference materialsRefY(volumeItem.sliceFrameY, "materials");
2230 QQmlListReference materialsRefZ(volumeItem.sliceFrameZ, "materials");
2231
2232 QVector2D frameWidth;
2233 QVector3D frameScaling;
2234
2235 frameScaling = QVector3D(volume->scaling().z()
2236 + (volume->scaling().z() * volume->sliceFrameGaps().z())
2237 + (volume->scaling().z() * volume->sliceFrameWidths().z()),
2238 volume->scaling().y()
2239 + (volume->scaling().y() * volume->sliceFrameGaps().y())
2240 + (volume->scaling().y() * volume->sliceFrameWidths().y()),
2241 volume->scaling().x() * volume->sliceFrameThicknesses().x());
2242
2243 frameWidth = QVector2D(volume->scaling().z() * volume->sliceFrameWidths().z(),
2244 volume->scaling().y() * volume->sliceFrameWidths().y());
2245
2246 frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x()));
2247 frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y()));
2248
2249 auto material = materialsRefX.at(0);
2250 material->setProperty(name: "color", value: volume->sliceFrameColor());
2251 material->setProperty(name: "sliceFrameWidth", value: frameWidth);
2252
2253 frameScaling = QVector3D(volume->scaling().x()
2254 + (volume->scaling().x() * volume->sliceFrameGaps().x())
2255 + (volume->scaling().x() * volume->sliceFrameWidths().x()),
2256 volume->scaling().z()
2257 + (volume->scaling().z() * volume->sliceFrameGaps().z())
2258 + (volume->scaling().z() * volume->sliceFrameWidths().z()),
2259 volume->scaling().y() * volume->sliceFrameThicknesses().y());
2260 frameWidth = QVector2D(volume->scaling().x() * volume->sliceFrameWidths().x(),
2261 volume->scaling().z() * volume->sliceFrameWidths().z());
2262
2263 frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x()));
2264 frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y()));
2265
2266 material = materialsRefY.at(0);
2267 material->setProperty(name: "color", value: volume->sliceFrameColor());
2268 material->setProperty(name: "sliceFrameWidth", value: frameWidth);
2269
2270 frameScaling = QVector3D(volume->scaling().x()
2271 + (volume->scaling().x() * volume->sliceFrameGaps().x())
2272 + (volume->scaling().x() * volume->sliceFrameWidths().x()),
2273 volume->scaling().y()
2274 + (volume->scaling().y() * volume->sliceFrameGaps().y())
2275 + (volume->scaling().y() * volume->sliceFrameWidths().y()),
2276 volume->scaling().z() * volume->sliceFrameThicknesses().z());
2277 frameWidth = QVector2D(volume->scaling().x() * volume->sliceFrameWidths().x(),
2278 volume->scaling().y() * volume->sliceFrameWidths().y());
2279
2280 frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x()));
2281 frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y()));
2282
2283 material = materialsRefZ.at(0);
2284 material->setProperty(name: "color", value: volume->sliceFrameColor());
2285 material->setProperty(name: "sliceFrameWidth", value: frameWidth);
2286}
2287
2288void QQuickGraphsItem::updateCustomVolumes()
2289{
2290 QAbstract3DAxis *axisX = m_controller->axisX();
2291 QAbstract3DAxis *axisY = m_controller->axisY();
2292 QAbstract3DAxis *axisZ = m_controller->axisZ();
2293
2294 int maxX = axisX->max();
2295 int minX = axisX->min();
2296 int maxY = axisY->max();
2297 int minY = axisY->min();
2298 int maxZ = axisZ->max();
2299 int minZ = axisZ->min();
2300 QVector3D adjustment = m_scaleWithBackground * QVector3D(1.0f, 1.0f, -1.0f);
2301 bool isPolar = m_controller->isPolar();
2302
2303 auto itemIterator = m_customItemList.constBegin();
2304 while (itemIterator != m_customItemList.constEnd()) {
2305 QCustom3DItem *item = itemIterator.key();
2306 QQuick3DModel *model = itemIterator.value();
2307
2308 if (auto volume = qobject_cast<QCustom3DVolume *>(object: item)) {
2309 QVector3D pos = item->position();
2310 if (!item->isPositionAbsolute()) {
2311 if (item->position().x() < minX
2312 || item->position().x() > maxX
2313 || item->position().y() < minY
2314 || item->position().y() > maxY
2315 || item->position().z() < minZ
2316 || item->position().z() > maxZ) {
2317 model->setVisible(false);
2318 ++itemIterator;
2319 continue;
2320 }
2321 float xNormalizer = maxX - minX;
2322 float xPos = (pos.x() - minX) / xNormalizer;
2323 float yNormalizer = maxY - minY;
2324 float yPos = (pos.y() - minY) / yNormalizer;
2325 float zNormalizer = maxZ - minZ;
2326 float zPos = (pos.z() - minZ) / zNormalizer;
2327 pos = QVector3D(xPos, yPos, zPos);
2328 if (isPolar) {
2329 float angle = xPos * M_PI * 2.0f;
2330 float radius = zPos;
2331 xPos = radius * qSin(v: angle) * 1.0f;
2332 zPos = -(radius * qCos(v: angle)) * 1.0f;
2333 yPos = yPos * adjustment.y() * 2.0f - adjustment.y();
2334 pos = QVector3D(xPos, yPos, zPos);
2335 } else {
2336 pos = pos * adjustment * 2.0f - adjustment;
2337 }
2338 }
2339 model->setPosition(pos);
2340
2341 auto &&volumeItem = m_customVolumes[volume];
2342
2343 QQmlListReference materialsRef(model, "materials");
2344 if (volumeItem.useHighDefShader != volume->useHighDefShader()) {
2345 materialsRef.clear();
2346 createVolumeMaterial(volume, volumeItem);
2347 }
2348
2349 if (volumeItem.drawSlices != volume->drawSlices()) {
2350 materialsRef.clear();
2351 createVolumeMaterial(volume, volumeItem);
2352 }
2353
2354 QVector3D sliceIndices((float(volume->sliceIndexX()) + 0.5f) / float(volume->textureWidth()) * 2.0 - 1.0,
2355 (float(volume->sliceIndexY()) + 0.5f) / float(volume->textureHeight()) * 2.0 - 1.0,
2356 (float(volume->sliceIndexZ()) + 0.5f) / float(volume->textureDepth()) * 2.0 - 1.0);
2357
2358 if (volumeItem.drawSliceFrames != volume->drawSliceFrames()) {
2359 if (volume->drawSliceFrames()) {
2360 volumeItem.sliceFrameX->setVisible(true);
2361 volumeItem.sliceFrameY->setVisible(true);
2362 volumeItem.sliceFrameZ->setVisible(true);
2363
2364 volumeItem.sliceFrameX->setRotation(QQuaternion::fromEulerAngles(pitch: 0, yaw: 90, roll: 0));
2365 volumeItem.sliceFrameY->setRotation(QQuaternion::fromEulerAngles(pitch: 90, yaw: 0, roll: 0));
2366
2367 updateSliceFrameMaterials(volume, volumeItem);
2368 } else {
2369 volumeItem.sliceFrameX->setVisible(false);
2370 volumeItem.sliceFrameY->setVisible(false);
2371 volumeItem.sliceFrameZ->setVisible(false);
2372 }
2373 volumeItem.drawSliceFrames = volume->drawSliceFrames();
2374 }
2375
2376 auto material = materialsRef.at(0);
2377
2378 QVector3D minBounds(-1, 1, 1);
2379 QVector3D maxBounds(1, -1, -1);
2380 QVector3D translation(0, 0, 0);
2381 QVector3D scaling(1, 1, 1);
2382
2383 if (!volume->isScalingAbsolute() && !volume->isPositionAbsolute()) {
2384 if (m_controller->axisX()->type() == QAbstract3DAxis::AxisTypeValue) {
2385 auto axis = static_cast<QValue3DAxis *>(m_controller->axisX());
2386 translation.setX(axis->positionAt(x: volume->position().x()) + translate().x() / scale().x());
2387 scaling.setX((axis->max() - axis->min()) / volume->scaling().x());
2388 minBounds.setX(minBounds.x() * scaling.x() - translation.x());
2389 maxBounds.setX(maxBounds.x() * scaling.x() - translation.x());
2390 }
2391
2392 if (m_controller->axisY()->type() == QAbstract3DAxis::AxisTypeValue) {
2393 auto axis = static_cast<QValue3DAxis *>(m_controller->axisY());
2394 translation.setY(axis->positionAt(x: volume->position().y()) + translate().y() / scale().y());
2395 scaling.setY((axis->max() - axis->min()) / volume->scaling().y());
2396 minBounds.setY(minBounds.y() * scaling.y() + translation.y());
2397 maxBounds.setY(maxBounds.y() * scaling.y() + translation.y());
2398 }
2399
2400 if (m_controller->axisZ()->type() == QAbstract3DAxis::AxisTypeValue) {
2401 auto axis = static_cast<QValue3DAxis *>(m_controller->axisZ());
2402 translation.setZ(axis->positionAt(x: volume->position().z()) + translate().z() / scale().z());
2403 scaling.setZ((axis->max() - axis->min()) / volume->scaling().z());
2404 minBounds.setZ(minBounds.z() * scaling.z() - translation.z());
2405 maxBounds.setZ(maxBounds.z() * scaling.z() - translation.z());
2406 }
2407
2408 model->setPosition(QVector3D());
2409 model->setScale(QVector3D(qAbs(t: scale().x()) / 2, qAbs(t: scale().y()) / 2, qAbs(t: scale().z()) / 2));
2410 } else {
2411 model->setScale(volume->scaling());
2412 }
2413 model->setRotation(volume->rotation());
2414
2415 material->setProperty(name: "minBounds", value: minBounds);
2416 material->setProperty(name: "maxBounds", value: maxBounds);
2417
2418 if (volume->drawSlices())
2419 material->setProperty(name: "volumeSliceIndices", value: sliceIndices);
2420
2421 if (volume->drawSliceFrames()) {
2422 float sliceFrameX = sliceIndices.x();
2423 float sliceFrameY = sliceIndices.y();
2424 float sliceFrameZ = sliceIndices.z();
2425 if (volume->sliceIndexX() >= 0 && scaling.x() > 0)
2426 sliceFrameX = (sliceFrameX + translation.x()) / scaling.x();
2427 if (volume->sliceIndexY() >= 0 && scaling.y() > 0)
2428 sliceFrameY = (sliceFrameY - translation.y()) / scaling.y();
2429 if (volume->sliceIndexZ() >= 0 && scaling.z() > 0)
2430 sliceFrameZ = (sliceFrameZ + translation.z()) / scaling.z();
2431
2432 if (sliceFrameX < - 1 || sliceFrameX > 1) {
2433 volumeItem.sliceFrameX->setVisible(false);
2434 } else {
2435 volumeItem.sliceFrameX->setVisible(true);
2436 }
2437
2438 if (sliceFrameY < - 1 || sliceFrameY > 1) {
2439 volumeItem.sliceFrameY->setVisible(false);
2440 } else {
2441 volumeItem.sliceFrameY->setVisible(true);
2442 }
2443
2444 if (sliceFrameZ < - 1 || sliceFrameZ > 1) {
2445 volumeItem.sliceFrameZ->setVisible(false);
2446 } else {
2447 volumeItem.sliceFrameZ->setVisible(true);
2448 }
2449
2450 volumeItem.sliceFrameX->setX(sliceFrameX);
2451 volumeItem.sliceFrameY->setY(-sliceFrameY);
2452 volumeItem.sliceFrameZ->setZ(-sliceFrameZ);
2453 }
2454
2455 material->setProperty(name: "alphaMultiplier", value: volume->alphaMultiplier());
2456 material->setProperty(name: "preserveOpacity", value: volume->preserveOpacity());
2457 material->setProperty(name: "useOrtho", value: m_controller->isOrthoProjection());
2458
2459 int sampleCount = volume->textureWidth() + volume->textureHeight() + volume->textureDepth();
2460 material->setProperty(name: "sampleCount", value: sampleCount);
2461
2462 int color8Bit = (volume->textureFormat() == QImage::Format_Indexed8) ? 1 : 0;
2463 material->setProperty(name: "color8Bit", value: color8Bit);
2464
2465 if (volumeItem.updateTextureData) {
2466 auto textureData = volumeItem.textureData;
2467 textureData->setSize(QSize(volume->textureWidth(), volume->textureHeight()));
2468 textureData->setDepth(volume->textureDepth());
2469
2470 if (color8Bit)
2471 textureData->setFormat(QQuick3DTextureData::R8);
2472 else
2473 textureData->setFormat(QQuick3DTextureData::RGBA8);
2474
2475 textureData->setTextureData(QByteArray::fromRawData(
2476 data: reinterpret_cast<const char *>(volume->textureData()->constData()),
2477 size: volume->textureData()->size()));
2478
2479 material->setProperty(name: "textureDimensions", value: QVector3D(1.0f / float(volume->textureWidth()),
2480 1.0f / float(volume->textureHeight()),
2481 1.0f / float(volume->textureDepth())));
2482
2483 volumeItem.updateTextureData = false;
2484 }
2485
2486 if (volumeItem.updateColorTextureData) {
2487 auto colorTextureData = volumeItem.colorTextureData;
2488 QByteArray colorTableBytes;
2489 const QList<QRgb> &colorTable = volume->colorTable();
2490 for (int i = 0; i < colorTable.size(); i++) {
2491 QRgb shifted = qRgba(r: qBlue(rgb: colorTable[i]), g: qGreen(rgb: colorTable[i]), b: qRed(rgb: colorTable[i]), a: qAlpha(rgb: colorTable[i]));
2492 colorTableBytes.append(a: QByteArray::fromRawData(data: reinterpret_cast<const char *>(&shifted), size: sizeof(shifted)));
2493 }
2494 colorTextureData->setTextureData(colorTableBytes);
2495 }
2496 }
2497 ++itemIterator;
2498 }
2499}
2500
2501void QQuickGraphsItem::updateAxisRange(float min, float max)
2502{
2503 Q_UNUSED(min);
2504 Q_UNUSED(max);
2505}
2506
2507void QQuickGraphsItem::updateAxisReversed(bool enable)
2508{
2509 Q_UNUSED(enable);
2510}
2511
2512int QQuickGraphsItem::findLabelsMaxWidth(const QStringList &labels)
2513{
2514 int labelWidth = 0;
2515 QFontMetrics labelFM(m_controller->activeTheme()->font());
2516
2517 for (const auto &label : std::as_const(t: labels)) {
2518 auto width = labelFM.horizontalAdvance(label);
2519 if (labelWidth < width)
2520 labelWidth = width;
2521 }
2522 return labelWidth;
2523}
2524
2525QVector3D QQuickGraphsItem::calculateCategoryLabelPosition(QAbstract3DAxis *axis, QVector3D labelPosition, int index)
2526{
2527 Q_UNUSED(axis);
2528 Q_UNUSED(index);
2529 return labelPosition;
2530}
2531
2532float QQuickGraphsItem::calculateCategoryGridLinePosition(QAbstract3DAxis *axis, int index)
2533{
2534 Q_UNUSED(axis);
2535 Q_UNUSED(index);
2536 return 0.0f;
2537}
2538
2539void QQuickGraphsItem::updateXTitle(const QVector3D &labelRotation, const QVector3D &labelTrans,
2540 const QQuaternion &totalRotation, float labelsMaxWidth,
2541 const QVector3D &scale)
2542{
2543 float pointSize = m_controller->activeTheme()->font().pointSizeF();
2544 float textPadding = pointSize * .5f;
2545 QFontMetrics fm(m_controller->activeTheme()->font());
2546 float height = fm.height() + textPadding;
2547 float width = fm.horizontalAdvance(m_controller->axisX()->title()) + textPadding;
2548
2549 float titleOffset;
2550
2551 bool radial = false;
2552 if (radial)
2553 titleOffset = -2.0f * (m_labelMargin + scale.y());
2554 else
2555 titleOffset = 2.0f * m_labelMargin + (labelsMaxWidth * scale.y());
2556
2557 float zRotation = 0.0f;
2558 float yRotation = 0.0f;
2559 float xRotation = -90.0f + labelRotation.z();
2560 float offsetRotation = labelRotation.z();
2561 float extraRotation = -90.0f;
2562 if (m_yFlipped) {
2563 zRotation = 180.0f;
2564 if (m_zFlipped) {
2565 titleOffset = -titleOffset;
2566 if (m_xFlipped) {
2567 offsetRotation = -offsetRotation;
2568 extraRotation = -extraRotation;
2569 } else {
2570 xRotation = -90.0f - labelRotation.z();
2571 }
2572 } else {
2573 yRotation = 180.0f;
2574 if (m_xFlipped) {
2575 offsetRotation = -offsetRotation;
2576 xRotation = -90.0f - labelRotation.z();
2577 } else {
2578 extraRotation = -extraRotation;
2579 }
2580 }
2581 } else {
2582 if (m_zFlipped) {
2583 titleOffset = -titleOffset;
2584 if (m_xFlipped) {
2585 offsetRotation = -offsetRotation;
2586 } else {
2587 xRotation = -90.0f - labelRotation.z();
2588 extraRotation = -extraRotation;
2589 }
2590 yRotation = 180.0f;
2591 if (m_yFlipped) {
2592 extraRotation = -extraRotation;
2593 if (m_xFlipped)
2594 xRotation = 90.0f + labelRotation.z();
2595 else
2596 xRotation = 90.0f - labelRotation.z();
2597 }
2598 } else {
2599 if (m_xFlipped) {
2600 offsetRotation = -offsetRotation;
2601 xRotation = -90.0f - labelRotation.z();
2602 extraRotation = -extraRotation;
2603 }
2604 if (m_yFlipped) {
2605 xRotation = 90.0f + labelRotation.z();
2606 extraRotation = -extraRotation;
2607 if (m_xFlipped)
2608 xRotation = 90.0f - labelRotation.z();
2609 }
2610 }
2611 }
2612
2613 if (offsetRotation == 180.0f || offsetRotation == -180.0f)
2614 offsetRotation = 0.0f;
2615
2616 QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: offsetRotation);
2617 QVector3D titleOffsetVector =
2618 offsetRotator.rotatedVector(vector: QVector3D(0.0f, 0.0f, titleOffset));
2619
2620 QQuaternion titleRotation;
2621 if (m_controller->axisX()->isTitleFixed()) {
2622 titleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: zRotation)
2623 * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation)
2624 * QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: xRotation);
2625 } else {
2626 titleRotation = totalRotation
2627 * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: extraRotation);
2628 }
2629
2630 QVector3D titleScale = scale;
2631 titleScale.setX(titleScale.y() * width / height);
2632 m_titleLabelX->setScale(titleScale);
2633 m_titleLabelX->setPosition(labelTrans + titleOffsetVector);
2634 m_titleLabelX->setRotation(titleRotation);
2635 m_titleLabelX->setProperty(name: "labelWidth", value: width);
2636 m_titleLabelX->setProperty(name: "labelHeight", value: height);
2637}
2638
2639void QQuickGraphsItem::updateYTitle(const QVector3D &sideLabelRotation,
2640 const QVector3D &backLabelRotation,
2641 const QVector3D &sideLabelTrans,
2642 const QVector3D &backLabelTrans,
2643 const QQuaternion &totalSideRotation,
2644 const QQuaternion &totalBackRotation,
2645 float labelsMaxWidth,
2646 const QVector3D &scale)
2647{
2648 float pointSize = m_controller->activeTheme()->font().pointSizeF();
2649 float textPadding = pointSize * .5f;
2650 QFontMetrics fm(m_controller->activeTheme()->font());
2651 float height = fm.height() + textPadding;
2652 float width = fm.horizontalAdvance(m_controller->axisY()->title()) + textPadding;
2653
2654 float titleOffset = m_labelMargin + (labelsMaxWidth * scale.x());
2655
2656 QQuaternion zRightAngleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: 90.0f);
2657 float yRotation;
2658 QVector3D titleTrans;
2659 QQuaternion totalRotation;
2660 if (m_xFlipped != m_zFlipped) {
2661 yRotation = backLabelRotation.y();
2662 titleTrans = backLabelTrans;
2663 totalRotation = totalBackRotation;
2664 } else {
2665 yRotation = sideLabelRotation.y();
2666 titleTrans = sideLabelTrans;
2667 totalRotation = totalSideRotation;
2668 }
2669 titleTrans.setY(.0f);
2670
2671 QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation);
2672 QVector3D titleOffsetVector =
2673 offsetRotator.rotatedVector(vector: QVector3D(-titleOffset, 0.0f, 0.0f));
2674
2675 QQuaternion titleRotation;
2676 if (m_controller->axisY()->isTitleFixed()) {
2677 titleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation)
2678 * zRightAngleRotation;
2679 } else {
2680 titleRotation = totalRotation * zRightAngleRotation;
2681 }
2682
2683 QVector3D titleScale = scale;
2684 titleScale.setX(titleScale.y() * width / height);
2685 m_titleLabelY->setScale(titleScale);
2686 m_titleLabelY->setPosition(titleTrans + titleOffsetVector);
2687 m_titleLabelY->setRotation(titleRotation);
2688 m_titleLabelY->setProperty(name: "labelWidth", value: width);
2689 m_titleLabelY->setProperty(name: "labelHeight", value: height);
2690}
2691
2692void QQuickGraphsItem::updateZTitle(const QVector3D &labelRotation, const QVector3D &labelTrans,
2693 const QQuaternion &totalRotation, float labelsMaxWidth,
2694 const QVector3D &scale)
2695{
2696 float pointSize = m_controller->activeTheme()->font().pointSizeF();
2697 float textPadding = pointSize * .5f;
2698 QFontMetrics fm(m_controller->activeTheme()->font());
2699 float height = fm.height() + textPadding;
2700 float width = fm.horizontalAdvance(m_controller->axisZ()->title()) + textPadding;
2701
2702 float titleOffset = m_labelMargin + (labelsMaxWidth * scale.x());
2703
2704 float zRotation = labelRotation.z();
2705 float yRotation = -90.0f;
2706 float xRotation = -90.0f;
2707 float extraRotation = 90.0f;
2708
2709 if (m_yFlipped) {
2710 xRotation = -xRotation;
2711 if (m_zFlipped) {
2712 if (m_xFlipped) {
2713 titleOffset = -titleOffset;
2714 zRotation = -zRotation;
2715 extraRotation = -extraRotation;
2716 } else {
2717 zRotation = -zRotation;
2718 yRotation = -yRotation;
2719 }
2720 } else {
2721 if (m_xFlipped) {
2722 titleOffset = -titleOffset;
2723 } else {
2724 extraRotation = -extraRotation;
2725 yRotation = -yRotation;
2726 }
2727 }
2728 } else {
2729 if (m_zFlipped) {
2730 zRotation = -zRotation;
2731 if (m_xFlipped) {
2732 titleOffset = -titleOffset;
2733 } else {
2734 extraRotation = -extraRotation;
2735 yRotation = -yRotation;
2736 }
2737 } else {
2738 if (m_xFlipped) {
2739 titleOffset = -titleOffset;
2740 extraRotation = -extraRotation;
2741 } else {
2742 yRotation = -yRotation;
2743 }
2744 }
2745 if (m_yFlipped) {
2746 xRotation = -xRotation;
2747 extraRotation = -extraRotation;
2748 }
2749 }
2750
2751 float offsetRotation = zRotation;
2752 if (offsetRotation == 180.0f || offsetRotation == -180.0f)
2753 offsetRotation = 0.0f;
2754
2755 QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: offsetRotation);
2756 QVector3D titleOffsetVector =
2757 offsetRotator.rotatedVector(vector: QVector3D(titleOffset, 0.0f, 0.0f));
2758
2759 QQuaternion titleRotation;
2760 if (m_controller->axisZ()->isTitleFixed()) {
2761 titleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: zRotation)
2762 * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation)
2763 * QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: xRotation);
2764 } else {
2765 titleRotation = totalRotation
2766 * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: extraRotation);
2767 }
2768
2769 QVector3D titleScale = scale;
2770 titleScale.setX(titleScale.y() * width / height);
2771 m_titleLabelZ->setScale(titleScale);
2772 m_titleLabelZ->setPosition(labelTrans + titleOffsetVector);
2773 m_titleLabelZ->setRotation(titleRotation);
2774 m_titleLabelZ->setProperty(name: "labelWidth", value: width);
2775 m_titleLabelZ->setProperty(name: "labelHeight", value: height);
2776}
2777
2778void QQuickGraphsItem::updateCamera()
2779{
2780 QVector3D lookingPosition = m_controller->scene()->activeCamera()->target();
2781 float zoomLevel = m_controller->scene()->activeCamera()->zoomLevel();
2782
2783 const float scale = qMin(a: width(), b: height() * 1.6f);
2784 const float magnificationScaleFactor = 1.0f / 640.0f;
2785 const float magnification = scale * magnificationScaleFactor;
2786
2787 auto useOrtho = m_controller->isOrthoProjection();
2788 if (useOrtho) {
2789 if (m_sliceView && m_sliceView->isVisible()) {
2790 m_oCamera->setVerticalMagnification(zoomLevel * .4f);
2791 m_oCamera->setHorizontalMagnification(zoomLevel * .4f);
2792 } else {
2793 m_oCamera->setVerticalMagnification(zoomLevel * magnification);
2794 m_oCamera->setHorizontalMagnification(zoomLevel * magnification);
2795 }
2796 }
2797 cameraTarget()->setPosition(lookingPosition);
2798 auto rotation = QVector3D(
2799 -m_controller->scene()->activeCamera()->yRotation(),
2800 -m_controller->scene()->activeCamera()->xRotation(),
2801 0);
2802 cameraTarget()->setEulerRotation(rotation);
2803 float zoom = 720.f / zoomLevel;
2804 m_pCamera->setZ(zoom);
2805 updateCustomLabelsRotation();
2806 updateItemLabel(position: m_labelPosition);
2807}
2808
2809void QQuickGraphsItem::handleSegmentLineCountChanged(QAbstract3DAxis *axis, QQuick3DRepeater *repeater)
2810{
2811 int gridLineCount = 0;
2812 if (axis->type() == QAbstract3DAxis::AxisTypeValue) {
2813 auto valueAxis = static_cast<QValue3DAxis *>(axis);
2814 gridLineCount = 2 * valueAxis->gridSize();
2815 } else if (axis->type() == QAbstract3DAxis::AxisTypeCategory) {
2816 gridLineCount = axis->labels().size();
2817 }
2818 repeater->setModel(gridLineCount);
2819 changeGridLineColor(repeater, color: m_controller->activeTheme()->gridLineColor());
2820 m_controller->handleAxisSubSegmentCountChangedBySender(sender: axis);
2821}
2822
2823void QQuickGraphsItem::handleSubSegmentLineCountChanged(QAbstract3DAxis *axis, QQuick3DRepeater *repeater)
2824{
2825 int subGridLineCount = 0;
2826
2827 if (axis->type() == QAbstract3DAxis::AxisTypeValue) {
2828 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
2829 subGridLineCount = 2 * valueAxis->subGridSize();
2830 } else if (axis->type() == QAbstract3DAxis::AxisTypeCategory) {
2831 subGridLineCount = 0;
2832 }
2833
2834 repeater->setModel(subGridLineCount);
2835 changeGridLineColor(repeater, color: m_controller->activeTheme()->gridLineColor());
2836}
2837
2838void QQuickGraphsItem::handleLabelCountChanged(QQuick3DRepeater *repeater)
2839{
2840 Q3DTheme *theme = m_controller->activeTheme();
2841 changeLabelBackgroundColor(repeater, color: theme->labelBackgroundColor());
2842 changeLabelBackgroundEnabled(repeater, enabled: theme->isLabelBackgroundEnabled());
2843 changeLabelBorderEnabled(repeater, enabled: theme->isLabelBorderEnabled());
2844 changeLabelTextColor(repeater, color: theme->labelTextColor());
2845 changeLabelFont(repeater, font: theme->font());
2846
2847 if (m_sliceView) {
2848 changeLabelBackgroundColor(repeater: m_sliceHorizontalLabelRepeater, color: theme->labelBackgroundColor());
2849 changeLabelBackgroundColor(repeater: m_sliceVerticalLabelRepeater, color: theme->labelBackgroundColor());
2850 changeLabelBackgroundEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled: theme->isLabelBackgroundEnabled());
2851 changeLabelBackgroundEnabled(repeater: m_sliceVerticalLabelRepeater, enabled: theme->isLabelBackgroundEnabled());
2852 changeLabelBorderEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled: theme->isLabelBorderEnabled());
2853 changeLabelBorderEnabled(repeater: m_sliceVerticalLabelRepeater, enabled: theme->isLabelBorderEnabled());
2854 changeLabelTextColor(repeater: m_sliceHorizontalLabelRepeater, color: theme->labelTextColor());
2855 changeLabelTextColor(repeater: m_sliceVerticalLabelRepeater, color: theme->labelTextColor());
2856 changeLabelFont(repeater: m_sliceHorizontalLabelRepeater, font: theme->font());
2857 changeLabelFont(repeater: m_sliceVerticalLabelRepeater, font: theme->font());
2858 }
2859}
2860
2861void QQuickGraphsItem::updateCustomData()
2862{
2863 QAbstract3DAxis *axisX = m_controller->axisX();
2864 QAbstract3DAxis *axisY = m_controller->axisY();
2865 QAbstract3DAxis *axisZ = m_controller->axisZ();
2866
2867 int maxX = axisX->max();
2868 int minX = axisX->min();
2869 int maxY = axisY->max();
2870 int minY = axisY->min();
2871 int maxZ = axisZ->max();
2872 int minZ = axisZ->min();
2873 QVector3D adjustment = m_scaleWithBackground * QVector3D(1.0f, 1.0f, -1.0f);
2874 bool isPolar = m_controller->isPolar();
2875
2876 auto labelIterator = m_customLabelList.constBegin();
2877 while (labelIterator != m_customLabelList.constEnd()) {
2878 QCustom3DLabel *label = labelIterator.key();
2879 QQuick3DNode *customLabel = labelIterator.value();
2880
2881 QVector3D pos = label->position();
2882 if (!label->isPositionAbsolute()) {
2883 if (label->position().x() < minX
2884 || label->position().x() > maxX
2885 || label->position().y() < minY
2886 || label->position().y() > maxY
2887 || label->position().z() < minZ
2888 || label->position().z() > maxZ) {
2889 customLabel->setVisible(false);
2890 ++labelIterator;
2891 continue;
2892 }
2893
2894 float xNormalizer = maxX - minX;
2895 float xPos = (pos.x() - minX) / xNormalizer;
2896 float yNormalizer = maxY - minY;
2897 float yPos = (pos.y() - minY) / yNormalizer;
2898 float zNormalizer = maxZ - minZ;
2899 float zPos = (pos.z() - minZ) / zNormalizer;
2900 pos = QVector3D(xPos, yPos, zPos);
2901 if (isPolar) {
2902 float angle = xPos * M_PI * 2.0f;
2903 float radius = zPos;
2904 xPos = radius * qSin(v: angle) * 1.0f;
2905 zPos = -(radius * qCos(v: angle)) * 1.0f;
2906 yPos = yPos * adjustment.y() * 2.0f - adjustment.y();
2907 pos = QVector3D(xPos, yPos, zPos);
2908 } else {
2909 pos = pos * adjustment * 2.0f - adjustment;
2910 }
2911 }
2912
2913 QFontMetrics fm(label->font());
2914 int width = fm.horizontalAdvance(label->text());
2915 int height = fm.height();
2916 customLabel->setProperty(name: "labelWidth", value: width);
2917 customLabel->setProperty(name: "labelHeight", value: height);
2918 customLabel->setPosition(pos);
2919 QQuaternion rotation = label->rotation();
2920 if (label->isFacingCamera()) {
2921 rotation = Utils::calculateRotation(xyzRotations: QVector3D(
2922 -m_controller->scene()->activeCamera()->yRotation(),
2923 -m_controller->scene()->activeCamera()->xRotation(),
2924 0));
2925 }
2926 customLabel->setRotation(rotation);
2927 float pointSize = m_controller->activeTheme()->font().pointSizeF();
2928 float scaleFactor = fontScaleFactor(pointSize) * pointSize;
2929 float fontRatio = float(height) / float(width);
2930 QVector3D fontScaled = QVector3D(scaleFactor / fontRatio, scaleFactor, 0.0f);
2931 customLabel->setScale(fontScaled);
2932 customLabel->setProperty(name: "labelText", value: label->text());
2933 customLabel->setProperty(name: "labelTextColor", value: label->textColor());
2934 customLabel->setProperty(name: "labelFont", value: label->font());
2935 customLabel->setProperty(name: "backgroundEnabled", value: label->isBackgroundEnabled());
2936 customLabel->setProperty(name: "backgroundColor", value: label->backgroundColor());
2937 customLabel->setProperty(name: "borderEnabled", value: label->isBorderEnabled());
2938 customLabel->setVisible(label->isVisible());
2939
2940 ++labelIterator;
2941 }
2942
2943 auto itemIterator = m_customItemList.constBegin();
2944 while (itemIterator != m_customItemList.constEnd()) {
2945 QCustom3DItem *item = itemIterator.key();
2946 QQuick3DModel *model = itemIterator.value();
2947
2948 QVector3D pos = item->position();
2949 if (!item->isPositionAbsolute()) {
2950 if (item->position().x() < minX
2951 || item->position().x() > maxX
2952 || item->position().y() < minY
2953 || item->position().y() > maxY
2954 || item->position().z() < minZ
2955 || item->position().z() > maxZ) {
2956 model->setVisible(false);
2957 ++itemIterator;
2958 continue;
2959 }
2960 float xNormalizer = maxX - minX;
2961 float xPos = (pos.x() - minX) / xNormalizer;
2962 float yNormalizer = maxY - minY;
2963 float yPos = (pos.y() - minY) / yNormalizer;
2964 float zNormalizer = maxZ - minZ;
2965 float zPos = (pos.z() - minZ) / zNormalizer;
2966 pos = QVector3D(xPos, yPos, zPos);
2967 if (isPolar) {
2968 float angle = xPos * M_PI * 2.0f;
2969 float radius = zPos;
2970 xPos = radius * qSin(v: angle) * 1.0f;
2971 zPos = -(radius * qCos(v: angle)) * 1.0f;
2972 yPos = yPos * adjustment.y() * 2.0f - adjustment.y();
2973 pos = QVector3D(xPos, yPos, zPos);
2974 } else {
2975 pos = pos * adjustment * 2.0f - adjustment;
2976 }
2977 }
2978 model->setPosition(pos);
2979
2980 if (auto volume = qobject_cast<QCustom3DVolume *>(object: item)) {
2981 if (!m_customVolumes.contains(key: volume)) {
2982 auto &&volumeItem = m_customVolumes[volume];
2983
2984 volumeItem.model = model;
2985 model->setSource(QUrl(volume->meshFile()));
2986
2987 volumeItem.useHighDefShader = volume->useHighDefShader();
2988 volumeItem.drawSlices = volume->drawSlices();
2989
2990 createVolumeMaterial(volume, volumeItem);
2991
2992 volumeItem.sliceFrameX = createSliceFrame(volumeItem);
2993 volumeItem.sliceFrameY = createSliceFrame(volumeItem);
2994 volumeItem.sliceFrameZ = createSliceFrame(volumeItem);
2995
2996 if (volume->drawSliceFrames()) {
2997 volumeItem.sliceFrameX->setVisible(true);
2998 volumeItem.sliceFrameY->setVisible(true);
2999 volumeItem.sliceFrameZ->setVisible(true);
3000
3001 QVector3D sliceIndices((float(volume->sliceIndexX()) + 0.5f) / float(volume->textureWidth()) * 2.0 - 1.0,
3002 (float(volume->sliceIndexY()) + 0.5f) / float(volume->textureHeight()) * 2.0 - 1.0,
3003 (float(volume->sliceIndexZ()) + 0.5f) / float(volume->textureDepth()) * 2.0 - 1.0);
3004
3005 volumeItem.sliceFrameX->setX(sliceIndices.x());
3006 volumeItem.sliceFrameY->setY(-sliceIndices.y());
3007 volumeItem.sliceFrameZ->setZ(-sliceIndices.z());
3008
3009 volumeItem.sliceFrameX->setRotation(QQuaternion::fromEulerAngles(pitch: 0, yaw: 90, roll: 0));
3010 volumeItem.sliceFrameY->setRotation(QQuaternion::fromEulerAngles(pitch: 90, yaw: 0, roll: 0));
3011
3012 updateSliceFrameMaterials(volume, volumeItem);
3013 } else {
3014 volumeItem.sliceFrameX->setVisible(false);
3015 volumeItem.sliceFrameY->setVisible(false);
3016 volumeItem.sliceFrameZ->setVisible(false);
3017 }
3018 volumeItem.drawSliceFrames = volume->drawSliceFrames();
3019 m_customItemList.insert(key: item, value: model);
3020 }
3021 } else {
3022 model->setSource(QUrl::fromLocalFile(localfile: item->meshFile()));
3023 QQmlListReference materialsRef(model, "materials");
3024 QQuick3DPrincipledMaterial *material = static_cast<QQuick3DPrincipledMaterial *>(materialsRef.at(0));
3025 QQuick3DTexture *texture = material->baseColorMap();
3026 if (!texture) {
3027 texture = new QQuick3DTexture();
3028 texture->setParent(model);
3029 texture->setParentItem(model);
3030 material->setBaseColorMap(texture);
3031 }
3032 if (!item->textureFile().isEmpty()) {
3033 texture->setSource(QUrl::fromLocalFile(localfile: item->textureFile()));
3034 } else {
3035 QImage textureImage = m_controller->customTextureImage(item);
3036 textureImage.convertTo(f: QImage::Format_RGBA32FPx4);
3037 QQuick3DTextureData *textureData = texture->textureData();
3038 if (!textureData) {
3039 textureData = new QQuick3DTextureData();
3040 textureData->setParent(texture);
3041 textureData->setParentItem(texture);
3042 textureData->setFormat(QQuick3DTextureData::RGBA32F);
3043 texture->setTextureData(textureData);
3044 }
3045 textureData->setSize(textureImage.size());
3046 textureData->setTextureData(QByteArray(reinterpret_cast<const char*>(textureImage.bits()),
3047 textureImage.sizeInBytes()));
3048 }
3049 model->setRotation(item->rotation());
3050 model->setScale(item->scaling());
3051 model->setVisible(item->isVisible());
3052 }
3053 ++itemIterator;
3054 }
3055}
3056
3057void QQuickGraphsItem::updateCustomLabelsRotation()
3058{
3059 auto labelIterator = m_customLabelList.constBegin();
3060 while (labelIterator != m_customLabelList.constEnd()) {
3061 QCustom3DLabel *label = labelIterator.key();
3062 QQuick3DNode *customLabel = labelIterator.value();
3063 QQuaternion rotation = label->rotation();
3064 if (label->isFacingCamera()) {
3065 rotation = Utils::calculateRotation(xyzRotations: QVector3D(
3066 -m_controller->scene()->activeCamera()->yRotation(),
3067 -m_controller->scene()->activeCamera()->xRotation(),
3068 0));
3069 }
3070 customLabel->setRotation(rotation);
3071 ++labelIterator;
3072 }
3073}
3074
3075int QQuickGraphsItem::msaaSamples() const
3076{
3077 if (m_renderMode == QAbstract3DGraph::RenderIndirect)
3078 return m_samples;
3079 else
3080 return m_windowSamples;
3081}
3082
3083void QQuickGraphsItem::setMsaaSamples(int samples)
3084{
3085 if (m_renderMode != QAbstract3DGraph::RenderIndirect) {
3086 qWarning(msg: "Multisampling cannot be adjusted in this render mode");
3087 } else if (m_samples != samples) {
3088 m_samples = samples;
3089 setAntialiasing(m_samples > 0);
3090 auto sceneEnv = environment();
3091 sceneEnv->setAntialiasingMode(m_samples > 0
3092 ? QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues::MSAA
3093 : QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues::NoAA);
3094 switch (m_samples) {
3095 case 0:
3096 // no-op
3097 break;
3098 case 2:
3099 sceneEnv->setAntialiasingQuality(
3100 QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::Medium);
3101 break;
3102 case 4:
3103 sceneEnv->setAntialiasingQuality(
3104 QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::High);
3105 break;
3106 case 8:
3107 sceneEnv->setAntialiasingQuality(
3108 QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::VeryHigh);
3109 break;
3110 default:
3111 qWarning(msg: "Invalid multisampling sample number, using 4x instead");
3112 sceneEnv->setAntialiasingQuality(
3113 QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::High);
3114 m_samples = 4;
3115 break;
3116 }
3117 emit msaaSamplesChanged(samples: m_samples);
3118 update();
3119 }
3120}
3121
3122Declarative3DScene *QQuickGraphsItem::scene() const
3123{
3124 return static_cast<Declarative3DScene *>(m_controller->scene());
3125}
3126
3127void QQuickGraphsItem::handleWindowChanged(/*QQuickWindow *window*/)
3128{
3129 auto window = QQuick3DObjectPrivate::get(item: rootNode())->sceneManager->window();
3130 checkWindowList(window);
3131 if (!window)
3132 return;
3133
3134#if defined(Q_OS_MACOS)
3135 bool previousVisibility = window->isVisible();
3136 // Enable touch events for Mac touchpads
3137 window->setVisible(true);
3138 typedef void * (*EnableTouch)(QWindow*, bool);
3139 EnableTouch enableTouch =
3140 (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow");
3141 if (enableTouch)
3142 enableTouch(window, true);
3143 window->setVisible(previousVisibility);
3144#endif
3145
3146 connect(sender: window, signal: &QObject::destroyed, context: this, slot: &QQuickGraphsItem::windowDestroyed);
3147
3148 int oldWindowSamples = m_windowSamples;
3149 m_windowSamples = window->format().samples();
3150 if (m_windowSamples < 0)
3151 m_windowSamples = 0;
3152
3153 connect(sender: window, signal: &QQuickWindow::beforeSynchronizing,
3154 context: this, slot: &QQuickGraphsItem::synchData);
3155
3156 if (m_renderMode == QAbstract3DGraph::RenderDirectToBackground) {
3157 setAntialiasing(m_windowSamples > 0);
3158 if (m_windowSamples != oldWindowSamples)
3159 emit msaaSamplesChanged(samples: m_windowSamples);
3160 }
3161
3162 connect(sender: m_controller.data(), signal: &Abstract3DController::needRender, context: window, slot: &QQuickWindow::update);
3163 // Force camera update before rendering the first frame
3164 // to workaround a Quick3D device pixel ratio bug
3165 connect(sender: window, signal: &QQuickWindow::beforeRendering, context: this, slot: [this, window]() {
3166 m_oCamera->setClipNear(0.001f);
3167 disconnect(sender: window, signal: &QQuickWindow::beforeRendering, receiver: this, zero: nullptr);
3168 });
3169 updateWindowParameters();
3170
3171#if defined(Q_OS_IOS)
3172 // Scenegraph render cycle in iOS sometimes misses update after beforeSynchronizing signal.
3173 // This ensures we don't end up displaying the graph without any data, in case update is
3174 // skipped after synchDataToRenderer.
3175 QTimer::singleShot(0, window, SLOT(update()));
3176#endif
3177}
3178
3179void QQuickGraphsItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
3180{
3181 QQuickItem::geometryChange(newGeometry, oldGeometry);
3182
3183 m_cachedGeometry = newGeometry;
3184 updateWindowParameters();
3185}
3186
3187void QQuickGraphsItem::itemChange(ItemChange change, const ItemChangeData &value)
3188{
3189 QQuick3DViewport::itemChange(change, value);
3190 updateWindowParameters();
3191}
3192
3193void QQuickGraphsItem::updateWindowParameters()
3194{
3195 const QMutexLocker locker(&m_mutex);
3196 // Update the device pixel ratio, window size and bounding box
3197 QQuickWindow *win = window();
3198 if (win && !m_controller.isNull()) {
3199 Q3DScene *scene = m_controller->scene();
3200 if (win->devicePixelRatio() != scene->devicePixelRatio()) {
3201 scene->setDevicePixelRatio(win->devicePixelRatio());
3202 win->update();
3203 }
3204
3205 bool directRender = m_renderMode == QAbstract3DGraph::RenderDirectToBackground;
3206 QSize windowSize;
3207
3208 if (directRender)
3209 windowSize = win->size();
3210 else
3211 windowSize = m_cachedGeometry.size().toSize();
3212
3213 if (windowSize != scene->d_func()->windowSize()) {
3214 scene->d_func()->setWindowSize(windowSize);
3215 win->update();
3216 }
3217
3218 if (directRender) {
3219 // Origin mapping is needed when rendering directly to background
3220 QPointF point = QQuickItem::mapToScene(point: QPointF(0.0, 0.0));
3221 scene->d_func()->setViewport(QRect(point.x() + 0.5f, point.y() + 0.5f,
3222 m_cachedGeometry.width() + 0.5f,
3223 m_cachedGeometry.height() + 0.5f));
3224 } else {
3225 // No translation needed when rendering to FBO
3226 scene->d_func()->setViewport(QRect(0.0, 0.0, m_cachedGeometry.width() + 0.5f,
3227 m_cachedGeometry.height() + 0.5f));
3228 }
3229 }
3230
3231 if (m_sliceView && m_sliceView->isVisible()) {
3232 const float scale = qMin(a: m_sliceView->width(), b: m_sliceView->height());
3233 QQuick3DOrthographicCamera *camera = static_cast<QQuick3DOrthographicCamera *>(m_sliceView->camera());
3234 const float magnificationScaleFactor = .16f; // this controls the size of the slice view
3235 const float magnification = scale * magnificationScaleFactor;
3236 camera->setHorizontalMagnification(magnification);
3237 camera->setVerticalMagnification(magnification);
3238 }
3239}
3240
3241void QQuickGraphsItem::handleSelectionModeChange(QAbstract3DGraph::SelectionFlags mode)
3242{
3243 emit selectionModeChanged(mode);
3244}
3245
3246void QQuickGraphsItem::handleShadowQualityChange(QAbstract3DGraph::ShadowQuality quality)
3247{
3248 emit shadowQualityChanged(quality);
3249}
3250
3251void QQuickGraphsItem::handleSelectedElementChange(QAbstract3DGraph::ElementType type)
3252{
3253 m_controller->m_clickedType = type;
3254 emit selectedElementChanged(type);
3255}
3256
3257void QQuickGraphsItem::handleOptimizationHintChange(QAbstract3DGraph::OptimizationHints hints)
3258{
3259 emit optimizationHintsChanged(hints);
3260}
3261
3262QAbstract3DInputHandler *QQuickGraphsItem::inputHandler() const
3263{
3264 return m_activeInputHandler;
3265}
3266
3267void QQuickGraphsItem::setInputHandler(QAbstract3DInputHandler *inputHandler)
3268{
3269 setActiveInputHandler(inputHandler);
3270}
3271
3272void QQuickGraphsItem::mouseDoubleClickEvent(QMouseEvent *event)
3273{
3274 if (m_activeInputHandler)
3275 m_activeInputHandler->mouseDoubleClickEvent(event);
3276}
3277
3278void QQuickGraphsItem::touchEvent(QTouchEvent *event)
3279{
3280 if (m_activeInputHandler)
3281 m_activeInputHandler->touchEvent(event);
3282 handleTouchEvent(event);
3283 window()->update();
3284}
3285
3286void QQuickGraphsItem::mousePressEvent(QMouseEvent *event)
3287{
3288 QPoint mousePos = event->pos();
3289 handleMousePressedEvent(event);
3290 if (m_activeInputHandler)
3291 m_activeInputHandler->mousePressEvent(event, mousePos);
3292}
3293
3294void QQuickGraphsItem::mouseReleaseEvent(QMouseEvent *event)
3295{
3296 QPoint mousePos = event->pos();
3297 if (m_activeInputHandler)
3298 m_activeInputHandler->mouseReleaseEvent(event, mousePos);
3299}
3300
3301void QQuickGraphsItem::mouseMoveEvent(QMouseEvent *event)
3302{
3303 QPoint mousePos = event->pos();
3304 if (m_activeInputHandler)
3305 m_activeInputHandler->mouseMoveEvent(event, mousePos);
3306}
3307
3308#if QT_CONFIG(wheelevent)
3309void QQuickGraphsItem::wheelEvent(QWheelEvent *event)
3310{
3311 if (m_activeInputHandler)
3312 m_activeInputHandler->wheelEvent(event);
3313}
3314#endif
3315
3316void QQuickGraphsItem::checkWindowList(QQuickWindow *window)
3317{
3318 QQuickWindow *oldWindow = m_graphWindowList.value(key: this);
3319 m_graphWindowList[this] = window;
3320
3321 if (oldWindow != window && oldWindow) {
3322 QObject::disconnect(sender: oldWindow, signal: &QObject::destroyed, receiver: this,
3323 slot: &QQuickGraphsItem::windowDestroyed);
3324 QObject::disconnect(sender: oldWindow, signal: &QQuickWindow::beforeSynchronizing, receiver: this,
3325 slot: &QQuickGraphsItem::synchData);
3326 if (!m_controller.isNull()) {
3327 QObject::disconnect(sender: m_controller.data(), signal: &Abstract3DController::needRender,
3328 receiver: oldWindow, slot: &QQuickWindow::update);
3329 }
3330 }
3331
3332 QList<QQuickWindow *> windowList;
3333
3334 foreach (QQuickGraphsItem *graph, m_graphWindowList.keys()) {
3335 if (graph->m_renderMode == QAbstract3DGraph::RenderDirectToBackground)
3336 windowList.append(t: m_graphWindowList.value(key: graph));
3337 }
3338
3339 if (!window) {
3340 m_graphWindowList.remove(key: this);
3341 return;
3342 }
3343}
3344
3345void QQuickGraphsItem::setMeasureFps(bool enable)
3346{
3347 if (m_measureFps != enable) {
3348 m_measureFps = enable;
3349 if (enable) {
3350 QObject::connect(sender: renderStats(), signal: &QQuick3DRenderStats::fpsChanged,
3351 context: this, slot: &QQuickGraphsItem::handleFpsChanged);
3352 } else {
3353 QObject::disconnect(sender: renderStats(), signal: 0, receiver: this, member: 0);
3354 }
3355 }
3356}
3357
3358bool QQuickGraphsItem::measureFps() const
3359{
3360 return m_measureFps;
3361}
3362
3363int QQuickGraphsItem::currentFps() const
3364{
3365 return m_currentFps;
3366}
3367
3368// TODO: Check if it would make sense to remove these from Abstract3DController - QTBUG-113812
3369// begin..
3370void QQuickGraphsItem::setOrthoProjection(bool enable)
3371{
3372 m_controller->setOrthoProjection(enable);
3373}
3374
3375bool QQuickGraphsItem::isOrthoProjection() const
3376{
3377 return m_controller->isOrthoProjection();
3378}
3379
3380QAbstract3DGraph::ElementType QQuickGraphsItem::selectedElement() const
3381{
3382 return m_controller->selectedElement();
3383}
3384
3385void QQuickGraphsItem::setAspectRatio(qreal ratio)
3386{
3387 m_controller->setAspectRatio(ratio);
3388}
3389
3390qreal QQuickGraphsItem::aspectRatio() const
3391{
3392 return m_controller->aspectRatio();
3393}
3394
3395void QQuickGraphsItem::setOptimizationHints(QAbstract3DGraph::OptimizationHints hints)
3396{
3397 int intmode = int(hints);
3398 m_controller->setOptimizationHints(QAbstract3DGraph::OptimizationHints(intmode));
3399}
3400
3401QAbstract3DGraph::OptimizationHints QQuickGraphsItem::optimizationHints() const
3402{
3403 return m_controller->optimizationHints();
3404}
3405
3406void QQuickGraphsItem::setPolar(bool enable)
3407{
3408 m_controller->setPolar(enable);
3409}
3410
3411bool QQuickGraphsItem::isPolar() const
3412{
3413 return m_controller->isPolar();
3414}
3415
3416void QQuickGraphsItem::setRadialLabelOffset(float offset)
3417{
3418 m_controller->setRadialLabelOffset(offset);
3419}
3420
3421float QQuickGraphsItem::radialLabelOffset() const
3422{
3423 return m_controller->radialLabelOffset();
3424}
3425
3426void QQuickGraphsItem::setHorizontalAspectRatio(qreal ratio)
3427{
3428 m_controller->setHorizontalAspectRatio(ratio);
3429}
3430
3431qreal QQuickGraphsItem::horizontalAspectRatio() const
3432{
3433 return m_controller->horizontalAspectRatio();
3434}
3435
3436void QQuickGraphsItem::setReflection(bool enable)
3437{
3438 m_controller->setReflection(enable);
3439}
3440
3441bool QQuickGraphsItem::isReflection() const
3442{
3443 return m_controller->reflection();
3444}
3445
3446void QQuickGraphsItem::setReflectivity(qreal reflectivity)
3447{
3448 m_controller->setReflectivity(reflectivity);
3449}
3450
3451qreal QQuickGraphsItem::reflectivity() const
3452{
3453 return m_controller->reflectivity();
3454}
3455
3456void QQuickGraphsItem::setLocale(const QLocale &locale)
3457{
3458 m_controller->setLocale(locale);
3459}
3460
3461QLocale QQuickGraphsItem::locale() const
3462{
3463 return m_controller->locale();
3464}
3465
3466QVector3D QQuickGraphsItem::queriedGraphPosition() const
3467{
3468 return m_controller->queriedGraphPosition();
3469}
3470
3471void QQuickGraphsItem::setMargin(qreal margin)
3472{
3473 m_controller->setMargin(margin);
3474}
3475
3476qreal QQuickGraphsItem::margin() const
3477{
3478 return m_controller->margin();
3479}
3480// ..end
3481// TODO: Check if it would make sense to remove these from Abstract3DController - QTBUG-113812
3482
3483QQuick3DNode *QQuickGraphsItem::rootNode() const
3484{
3485 return QQuick3DViewport::scene();
3486}
3487
3488void QQuickGraphsItem::changeLabelBackgroundColor(QQuick3DRepeater *repeater, const QColor &color)
3489{
3490 int count = repeater->count();
3491 for (int i = 0; i < count; i++) {
3492 auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i));
3493 label->setProperty(name: "backgroundColor", value: color);
3494 }
3495}
3496
3497void QQuickGraphsItem::changeLabelBackgroundEnabled(QQuick3DRepeater *repeater, const bool &enabled)
3498{
3499 int count = repeater->count();
3500 for (int i = 0; i < count; i++) {
3501 auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i));
3502 label->setProperty(name: "backgroundEnabled", value: enabled);
3503 }
3504}
3505
3506void QQuickGraphsItem::changeLabelBorderEnabled(QQuick3DRepeater *repeater, const bool &enabled)
3507{
3508 int count = repeater->count();
3509 for (int i = 0; i < count; i++) {
3510 auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i));
3511 label->setProperty(name: "borderEnabled", value: enabled);
3512 }
3513}
3514
3515void QQuickGraphsItem::changeLabelTextColor(QQuick3DRepeater *repeater, const QColor &color)
3516{
3517 int count = repeater->count();
3518 for (int i = 0; i < count; i++) {
3519 auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i));
3520 label->setProperty(name: "labelTextColor", value: color);
3521 }
3522}
3523
3524void QQuickGraphsItem::changeLabelFont(QQuick3DRepeater *repeater, const QFont &font)
3525{
3526 int count = repeater->count();
3527 for (int i = 0; i < count; i++) {
3528 auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i));
3529 label->setProperty(name: "labelFont", value: font);
3530 }
3531}
3532
3533void QQuickGraphsItem::changeLabelsEnabled(QQuick3DRepeater *repeater, const bool &enabled)
3534{
3535 int count = repeater->count();
3536 for (int i = 0; i < count; i++) {
3537 auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i));
3538 label->setProperty(name: "visible", value: enabled);
3539 }
3540}
3541
3542void QQuickGraphsItem::changeGridLineColor(QQuick3DRepeater *repeater, const QColor &color)
3543{
3544 for (int i = 0; i < repeater->count(); i++) {
3545 auto lineNode = static_cast<QQuick3DNode *>(repeater->objectAt(index: i));
3546 lineNode->setProperty(name: "lineColor", value: color);
3547 }
3548}
3549
3550void QQuickGraphsItem::updateTitleLabels()
3551{
3552 if (m_controller->m_changeTracker.axisXTitleVisibilityChanged) {
3553 m_titleLabelX->setVisible(m_controller->axisX()->isTitleVisible());
3554 m_controller->m_changeTracker.axisXTitleVisibilityChanged = false;
3555 }
3556
3557 if (m_controller->m_changeTracker.axisYTitleVisibilityChanged) {
3558 m_titleLabelY->setVisible(m_controller->axisY()->isTitleVisible());
3559 m_controller->m_changeTracker.axisYTitleVisibilityChanged = false;
3560 }
3561
3562 if (m_controller->m_changeTracker.axisZTitleVisibilityChanged) {
3563 m_titleLabelZ->setVisible(m_controller->axisZ()->isTitleVisible());
3564 m_controller->m_changeTracker.axisZTitleVisibilityChanged = false;
3565 }
3566
3567 if (m_controller->m_changeTracker.axisXTitleChanged) {
3568 m_titleLabelX->setProperty(name: "labelText", value: m_controller->axisX()->title());
3569 m_controller->m_changeTracker.axisXTitleChanged = false;
3570 }
3571
3572 if (m_controller->m_changeTracker.axisYTitleChanged) {
3573 m_titleLabelY->setProperty(name: "labelText", value: m_controller->axisY()->title());
3574 m_controller->m_changeTracker.axisYTitleChanged = false;
3575 }
3576
3577 if (m_controller->m_changeTracker.axisZTitleChanged) {
3578 m_titleLabelZ->setProperty(name: "labelText", value: m_controller->axisZ()->title());
3579 m_controller->m_changeTracker.axisZTitleChanged = false;
3580 }
3581}
3582
3583void QQuickGraphsItem::updateSelectionMode(QAbstract3DGraph::SelectionFlags newMode)
3584{
3585 Q_UNUSED(newMode);
3586
3587 if (m_sliceView && m_sliceView->isVisible())
3588 updateSliceGraph();
3589}
3590
3591bool QQuickGraphsItem::doPicking(const QPointF &point)
3592{
3593 if (m_activeInputHandler->d_func()->m_inputState
3594 == QAbstract3DInputHandlerPrivate::InputStateSelecting) {
3595 QList<QQuick3DPickResult> results = pickAll(x: point.x(), y: point.y());
3596 if (!m_customItemList.isEmpty()) {
3597 // Try to pick custom item only
3598 for (const auto &result : results) {
3599 QCustom3DItem *customItem = m_customItemList.key(value: result.objectHit(), defaultKey: nullptr);
3600
3601 if (customItem) {
3602 int selectedIndex = m_controller->m_customItems.indexOf(t: customItem);
3603 m_controller->m_selectedCustomItemIndex = selectedIndex;
3604 handleSelectedElementChange(type: QAbstract3DGraph::ElementCustomItem);
3605 // Don't allow picking in subclasses if custom item is picked
3606 return false;
3607 }
3608 }
3609 }
3610
3611 for (const auto &result : results) {
3612 QString objName = result.objectHit()->objectName();
3613 if (objName.contains(QStringLiteral("ElementAxisXLabel")))
3614 handleSelectedElementChange(type: QAbstract3DGraph::ElementAxisXLabel);
3615 else if (objName.contains(QStringLiteral("ElementAxisYLabel")))
3616 handleSelectedElementChange(type: QAbstract3DGraph::ElementAxisYLabel);
3617 else if (objName.contains(QStringLiteral("ElementAxisZLabel")))
3618 handleSelectedElementChange(type: QAbstract3DGraph::ElementAxisZLabel);
3619 else
3620 continue;
3621 }
3622 return true;
3623 }
3624
3625 return false;
3626}
3627
3628void QQuickGraphsItem::minimizeMainGraph()
3629{
3630 QQuickItem *anchor = QQuickItemPrivate::get(item: this)->anchors()->fill();
3631 if (anchor)
3632 QQuickItemPrivate::get(item: this)->anchors()->resetFill();
3633
3634 const float minimizedSize = .2f;
3635 setWidth(parentItem()->width() * minimizedSize);
3636 setHeight(parentItem()->height() * minimizedSize);
3637}
3638
3639void QQuickGraphsItem::updateSliceGraph()
3640{
3641 if (!m_sliceView || !m_sliceActivatedChanged)
3642 return;
3643
3644 if (m_sliceView->isVisible()) {
3645 setWidth(parentItem()->width());
3646 setHeight(parentItem()->height());
3647
3648 m_sliceView->setVisible(false);
3649 m_controller->setSlicingActive(false);
3650 } else {
3651 minimizeMainGraph();
3652 m_sliceView->setVisible(true);
3653 updateSliceGrid();
3654 updateSliceLabels();
3655 m_controller->setSlicingActive(true);
3656 }
3657
3658 m_sliceActivatedChanged = false;
3659}
3660
3661void QQuickGraphsItem::addInputHandler(QAbstract3DInputHandler *inputHandler)
3662{
3663 Q_ASSERT(inputHandler);
3664 QQuickGraphsItem *owner = qobject_cast<QQuickGraphsItem *>(object: inputHandler->parent());
3665 if (owner != this) {
3666 Q_ASSERT_X(!owner, "addInputHandler",
3667 "Input handler already attached to another component.");
3668 inputHandler->setParent(this);
3669 }
3670
3671 if (!m_inputHandlers.contains(t: inputHandler))
3672 m_inputHandlers.append(t: inputHandler);
3673}
3674
3675void QQuickGraphsItem::releaseInputHandler(QAbstract3DInputHandler *inputHandler)
3676{
3677 if (inputHandler && m_inputHandlers.contains(t: inputHandler)) {
3678 // Clear the default status from released default input handler
3679 if (inputHandler->d_func()->m_isDefaultHandler)
3680 inputHandler->d_func()->m_isDefaultHandler = false;
3681
3682 // If the input handler is in use, remove it
3683 if (m_activeInputHandler == inputHandler)
3684 setActiveInputHandler(nullptr);
3685
3686 m_inputHandlers.removeAll(t: inputHandler);
3687 inputHandler->setParent(nullptr);
3688 }
3689}
3690
3691void QQuickGraphsItem::setActiveInputHandler(QAbstract3DInputHandler *inputHandler)
3692{
3693 if (inputHandler == m_activeInputHandler)
3694 return;
3695
3696 // If existing input handler is the default input handler, delete it
3697 if (m_activeInputHandler) {
3698 if (m_activeInputHandler->d_func()->m_isDefaultHandler) {
3699 m_inputHandlers.removeAll(t: m_activeInputHandler);
3700 delete m_activeInputHandler;
3701 } else {
3702 // Disconnect the old input handler
3703 m_activeInputHandler->setScene(nullptr);
3704 QObject::disconnect(sender: m_activeInputHandler, signal: nullptr, receiver: m_controller, member: nullptr);
3705 QObject::disconnect(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::positionChanged,
3706 receiver: this, slot: &QQuickGraphsItem::doPicking);
3707 }
3708 }
3709
3710 // Assume ownership and connect to this controller's scene
3711 if (inputHandler)
3712 addInputHandler(inputHandler);
3713
3714 m_activeInputHandler = inputHandler;
3715
3716 if (m_activeInputHandler) {
3717 m_activeInputHandler->setScene(scene());
3718
3719 // Connect the input handler
3720 QObject::connect(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::inputViewChanged,
3721 context: m_controller, slot: &Abstract3DController::handleInputViewChanged);
3722 QObject::connect(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::positionChanged,
3723 context: m_controller, slot: &Abstract3DController::handleInputPositionChanged);
3724 QObject::connect(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::positionChanged,
3725 context: this, slot: &QQuickGraphsItem::doPicking);
3726 }
3727
3728 // Notify change of input handler
3729 emit inputHandlerChanged(inputHandler: m_activeInputHandler);
3730}
3731
3732void QQuickGraphsItem::windowDestroyed(QObject *obj)
3733{
3734 // Remove destroyed window from window lists
3735 QQuickWindow *win = static_cast<QQuickWindow *>(obj);
3736 QQuickWindow *oldWindow = m_graphWindowList.value(key: this);
3737
3738 if (win == oldWindow)
3739 m_graphWindowList.remove(key: this);
3740}
3741
3742QQmlComponent *QQuickGraphsItem::createRepeaterDelegateComponent(const QString &fileName)
3743{
3744 QQmlComponent component(qmlEngine(this), fileName);
3745 return qobject_cast<QQmlComponent *>(object: component.create());
3746}
3747
3748QQuick3DRepeater *QQuickGraphsItem::createRepeater(QQuick3DNode *parent)
3749{
3750 auto engine = qmlEngine(this);
3751 QQmlComponent repeaterComponent(engine);
3752 repeaterComponent.setData("import QtQuick3D; Repeater3D{}", baseUrl: QUrl());
3753 auto repeater = qobject_cast<QQuick3DRepeater *>(object: repeaterComponent.create());
3754 repeater->setParent(parent ? parent : graphNode());
3755 repeater->setParentItem(parent ? parent : graphNode());
3756 return repeater;
3757}
3758
3759QQuick3DNode *QQuickGraphsItem::createTitleLabel(QQuick3DNode *parent)
3760{
3761 auto engine = qmlEngine(this);
3762 QQmlComponent comp(engine, QStringLiteral(":/axis/TitleLabel"));
3763 auto titleLabel = qobject_cast<QQuick3DNode *>(object: comp.create());
3764 titleLabel->setParent(parent ? parent : graphNode());
3765 titleLabel->setParentItem(parent ? parent : graphNode());
3766 titleLabel->setVisible(false);
3767 titleLabel->setScale(m_labelScale);
3768 return titleLabel;
3769}
3770
3771void QQuickGraphsItem::createItemLabel()
3772{
3773 auto engine = qmlEngine(this);
3774 QQmlComponent comp(engine, QStringLiteral(":/axis/ItemLabel"));
3775 m_itemLabel = qobject_cast<QQuickItem *>(o: comp.create());
3776 m_itemLabel->setParent(this);
3777 m_itemLabel->setParentItem(this);
3778 m_itemLabel->setVisible(false);
3779}
3780
3781QQuick3DCustomMaterial *QQuickGraphsItem::createQmlCustomMaterial(const QString &fileName)
3782{
3783 QQmlComponent component(qmlEngine(this), fileName);
3784 QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(object: component.create());
3785 return material;
3786}
3787
3788QQuick3DPrincipledMaterial *QQuickGraphsItem::createPrincipledMaterial()
3789{
3790 QQmlComponent component(qmlEngine(this));
3791 component.setData("import QtQuick3D; PrincipledMaterial{}", baseUrl: QUrl());
3792 return qobject_cast<QQuick3DPrincipledMaterial *>(object: component.create());
3793}
3794
3795bool QQuickGraphsItem::event(QEvent *event)
3796{
3797 return QQuickItem::event(event);
3798}
3799
3800void QQuickGraphsItem::createSliceView()
3801{
3802 if (m_sliceView)
3803 return;
3804
3805 connect(sender: parentItem(), signal: &QQuickItem::widthChanged , context: this, slot: &QQuickGraphsItem::handleParentWidthChange);
3806 connect(sender: parentItem(), signal: &QQuickItem::heightChanged , context: this, slot: &QQuickGraphsItem::handleParentHeightChange);
3807
3808 m_sliceView = new QQuick3DViewport();
3809 m_sliceView->setParent(parent());
3810 m_sliceView->setParentItem(parentItem());
3811 m_sliceView->setVisible(false);
3812
3813 m_sliceView->bindableHeight().setBinding(f: [&] { return parentItem()->height(); });
3814 m_sliceView->bindableWidth().setBinding(f: [&] { return parentItem()->width(); });
3815
3816 auto scene = m_sliceView->scene();
3817
3818 auto camera = new QQuick3DOrthographicCamera(scene);
3819 camera->setPosition(QVector3D(.0f, .0f, 20.0f));
3820 const float scale = qMin(a: m_sliceView->width(), b: m_sliceView->height());
3821 const float magnificationScaleFactor = 2 * window()->devicePixelRatio() * .08f; // this controls the size of the slice view
3822 const float magnification = scale * magnificationScaleFactor;
3823 camera->setHorizontalMagnification(magnification);
3824 camera->setVerticalMagnification(magnification);
3825 m_sliceView->setCamera(camera);
3826
3827 auto light = new QQuick3DDirectionalLight(scene);
3828 light->setParent(camera);
3829 light->setParentItem(camera);
3830
3831 auto gridDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
3832 auto labelDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel"));
3833
3834 m_sliceHorizontalGridRepeater = createRepeater(parent: scene);
3835 m_sliceHorizontalGridRepeater->setDelegate(gridDelegate);
3836
3837 m_sliceVerticalGridRepeater = createRepeater(parent: scene);
3838 m_sliceVerticalGridRepeater->setDelegate(gridDelegate);
3839
3840 m_sliceHorizontalLabelRepeater = createRepeater(parent: scene);
3841 m_sliceHorizontalLabelRepeater->setDelegate(labelDelegate);
3842
3843 m_sliceVerticalLabelRepeater = createRepeater(parent: scene);
3844 m_sliceVerticalLabelRepeater->setDelegate(labelDelegate);
3845
3846 m_sliceHorizontalTitleLabel = createTitleLabel(parent: scene);
3847 m_sliceHorizontalTitleLabel->setVisible(true);
3848
3849 m_sliceVerticalTitleLabel = createTitleLabel(parent: scene);
3850 m_sliceVerticalTitleLabel->setVisible(true);
3851
3852 m_sliceItemLabel = createTitleLabel(parent: scene);
3853 m_sliceItemLabel->setVisible(false);
3854}
3855
3856void QQuickGraphsItem::updateSliceGrid()
3857{
3858 QAbstract3DAxis *horizontalAxis = nullptr;
3859 QAbstract3DAxis *verticalAxis = m_controller->axisY();
3860 auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin;
3861 float scale;
3862 float translate;
3863
3864 QVector3D horizontalScale = QVector3D(.0f, .0f, .0f);
3865 QVector3D verticalScale = QVector3D(lineWidthScaleFactor(),
3866 backgroundScale.y() * lineLengthScaleFactor(),
3867 lineWidthScaleFactor());
3868 auto selectionMode = m_controller->selectionMode();
3869 if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow)) {
3870 horizontalAxis = m_controller->axisX();
3871 horizontalScale = QVector3D(backgroundScale.x() * lineLengthScaleFactor(),
3872 lineWidthScaleFactor(),
3873 lineWidthScaleFactor());
3874 scale = m_scaleWithBackground.x();
3875 translate = m_scaleWithBackground.x();
3876 } else if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)) {
3877 horizontalAxis = m_controller->axisZ();
3878 horizontalScale = QVector3D(backgroundScale.z() * lineLengthScaleFactor(),
3879 lineWidthScaleFactor(),
3880 lineWidthScaleFactor());
3881 scale = m_scaleWithBackground.z();
3882 translate = m_scaleWithBackground.z();
3883 }
3884
3885 if (horizontalAxis == nullptr) {
3886 qWarning(msg: "Invalid axis type");
3887 return;
3888 }
3889
3890 if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeValue) {
3891 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(horizontalAxis);
3892 m_sliceVerticalGridRepeater->model().clear();
3893 m_sliceVerticalGridRepeater->setModel(valueAxis->gridSize()
3894 + valueAxis->subGridSize());
3895 } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) {
3896 m_sliceVerticalGridRepeater->model().clear();
3897 m_sliceVerticalGridRepeater->setModel(horizontalAxis->labels().size());
3898 }
3899
3900 if (verticalAxis->type() & QAbstract3DAxis::AxisTypeValue) {
3901 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(verticalAxis);
3902 m_sliceHorizontalGridRepeater->model().clear();
3903 m_sliceHorizontalGridRepeater->setModel(valueAxis->gridSize()
3904 + valueAxis->subGridSize());
3905 } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) {
3906 m_sliceHorizontalGridRepeater->model().clear();
3907 m_sliceHorizontalGridRepeater->setModel(verticalAxis->labels().size());
3908 }
3909
3910 float linePosX = .0f;
3911 float linePosY = .0f;
3912 float linePosZ = -1.f; // Draw grid lines behind slice (especially for surface)
3913
3914 if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeCategory) {
3915 m_sliceVerticalGridRepeater->setVisible(false);
3916 } else if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeValue) {
3917 for (int i = 0; i < m_sliceVerticalGridRepeater->count(); i++) {
3918 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(m_sliceVerticalGridRepeater->objectAt(index: i));
3919 auto axis = static_cast<QValue3DAxis *>(horizontalAxis);
3920 if (i < axis->gridSize())
3921 linePosX = axis->gridPositionAt(gridLine: i) * scale * 2.0f - translate;
3922 else
3923 linePosX = axis->subGridPositionAt(gridLine: i - axis->gridSize()) * scale * 2.0f - translate;
3924 lineNode->setProperty(name: "lineColor", value: QColor(0, 0, 0));
3925 positionAndScaleLine(lineNode, scale: verticalScale, position: QVector3D(linePosX, linePosY, linePosZ));
3926 }
3927 }
3928
3929 linePosX = 0;
3930 scale = m_scaleWithBackground.y();
3931 translate = m_scaleWithBackground.y();
3932
3933 for (int i = 0; i < m_sliceHorizontalGridRepeater->count(); i++) {
3934 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(m_sliceHorizontalGridRepeater->objectAt(index: i));
3935 if (verticalAxis->type() == QAbstract3DAxis::AxisTypeValue) {
3936 auto axis = static_cast<QValue3DAxis *>(verticalAxis);
3937 if (i < axis->gridSize())
3938 linePosY = axis->gridPositionAt(gridLine: i) * scale * 2.0f - translate;
3939 else
3940 linePosY = axis->subGridPositionAt(gridLine: i - axis->gridSize()) * scale * 2.0f - translate;
3941 } else if (verticalAxis->type() == QAbstract3DAxis::AxisTypeCategory) {
3942 linePosY = calculateCategoryGridLinePosition(axis: verticalAxis, index: i);
3943 }
3944 lineNode->setProperty(name: "lineColor", value: QColor(0, 0, 0));
3945 positionAndScaleLine(lineNode, scale: horizontalScale, position: QVector3D(linePosX, linePosY, linePosZ));
3946 }
3947}
3948
3949void QQuickGraphsItem::updateSliceLabels()
3950{
3951 QAbstract3DAxis *horizontalAxis = nullptr;
3952 QAbstract3DAxis *verticalAxis = m_controller->axisY();
3953 auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin;
3954 float scale;
3955 float translate;
3956 auto selectionMode = m_controller->selectionMode();
3957
3958 if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow))
3959 horizontalAxis = m_controller->axisX();
3960 else if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn))
3961 horizontalAxis = m_controller->axisZ();
3962
3963 scale = backgroundScale.x() - m_backgroundScaleMargin.x();
3964 translate = backgroundScale.x() - m_backgroundScaleMargin.x();
3965
3966 if (horizontalAxis == nullptr) {
3967 qWarning(msg: "Invalid selection mode");
3968 return;
3969 }
3970
3971 if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeValue) {
3972 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(horizontalAxis);
3973 m_sliceHorizontalLabelRepeater->model().clear();
3974 m_sliceHorizontalLabelRepeater->setModel(valueAxis->labels().size());
3975 } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) {
3976 m_sliceHorizontalLabelRepeater->model().clear();
3977 m_sliceHorizontalLabelRepeater->setModel(horizontalAxis->labels().size());
3978 }
3979
3980 if (verticalAxis->type() & QAbstract3DAxis::AxisTypeValue) {
3981 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(verticalAxis);
3982 m_sliceVerticalLabelRepeater->model().clear();
3983 m_sliceVerticalLabelRepeater->setModel(valueAxis->labels().size());
3984 } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) {
3985 m_sliceVerticalLabelRepeater->model().clear();
3986 m_sliceVerticalLabelRepeater->setModel(verticalAxis->labels().size());
3987 }
3988
3989 float textPadding = 12.0f;
3990
3991 float labelsMaxWidth = float(findLabelsMaxWidth(labels: horizontalAxis->labels())) + textPadding;
3992 QFontMetrics fm(m_controller->activeTheme()->font());
3993 float labelHeight = fm.height() + textPadding;
3994
3995 float pointSize = m_controller->activeTheme()->font().pointSizeF();
3996 float scaleFactor = fontScaleFactor(pointSize) * pointSize;
3997 float fontRatio = labelsMaxWidth / labelHeight;
3998 QVector3D fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f);
3999
4000 float adjustment = labelsMaxWidth * scaleFactor;
4001 float yPos = backgroundScale.y() + adjustment;
4002
4003 QVector3D labelTrans = QVector3D(0.0f, -yPos, 0.0f);
4004 QStringList labels = horizontalAxis->labels();
4005 Q3DTheme *theme = m_controller->activeTheme();
4006 QFont font = theme->font();
4007 bool borderEnabled = theme->isLabelBorderEnabled();
4008 QColor labelTextColor = theme->labelTextColor();
4009 bool backgroundEnabled = theme->isLabelBackgroundEnabled();
4010 QColor backgroundColor = theme->labelBackgroundColor();
4011
4012 if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeValue) {
4013 auto valueAxis = static_cast<QValue3DAxis *>(horizontalAxis);
4014 for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) {
4015 auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(index: i));
4016 labelTrans.setX(valueAxis->labelPositionAt(index: i) * scale * 2.0f - translate);
4017 obj->setScale(fontScaled);
4018 obj->setPosition(labelTrans);
4019 obj->setProperty(name: "labelText", value: labels[i]);
4020 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
4021 obj->setProperty(name: "labelHeight", value: labelHeight);
4022 obj->setProperty(name: "labelFont", value: font);
4023 obj->setProperty(name: "borderEnabled", value: borderEnabled);
4024 obj->setProperty(name: "labelTextColor", value: labelTextColor);
4025 obj->setProperty(name: "backgroundEnabled", value: backgroundEnabled);
4026 obj->setProperty(name: "backgroundColor", value: backgroundColor);
4027 obj->setEulerRotation(QVector3D(.0f, .0f, -45.0f));
4028 }
4029 } else if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeCategory) {
4030 for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) {
4031 labelTrans = calculateCategoryLabelPosition(axis: horizontalAxis, labelPosition: labelTrans, index: i);
4032 labelTrans.setY(labelTrans.y() - (adjustment / 1.5f));
4033 if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionColumn))
4034 labelTrans.setX(labelTrans.z());
4035 labelTrans.setZ(1.0f); // Bring the labels on top of bars and grid
4036 auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(index: i));
4037 obj->setScale(fontScaled);
4038 obj->setPosition(labelTrans);
4039 obj->setProperty(name: "labelText", value: labels[i]);
4040 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
4041 obj->setProperty(name: "labelHeight", value: labelHeight);
4042 obj->setProperty(name: "labelFont", value: font);
4043 obj->setProperty(name: "borderEnabled", value: borderEnabled);
4044 obj->setProperty(name: "labelTextColor", value: labelTextColor);
4045 obj->setProperty(name: "backgroundEnabled", value: backgroundEnabled);
4046 obj->setProperty(name: "backgroundColor", value: backgroundColor);
4047 obj->setEulerRotation(QVector3D(0.0f, 0.0f, -60.0f));
4048 }
4049 }
4050
4051 scale = backgroundScale.y() - m_backgroundScaleMargin.y();
4052 translate = backgroundScale.y() - m_backgroundScaleMargin.y();
4053 labels = verticalAxis->labels();
4054 labelsMaxWidth = float(findLabelsMaxWidth(labels)) + textPadding;
4055 adjustment = labelsMaxWidth * scaleFactor;
4056 float xPos = 0.0f;
4057 if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionRow))
4058 xPos = backgroundScale.x() + adjustment;
4059 else if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionColumn))
4060 xPos = backgroundScale.z() + adjustment;
4061 labelTrans = QVector3D(xPos, 0.0f, 0.0f);
4062
4063 if (verticalAxis->type() == QAbstract3DAxis::AxisTypeValue) {
4064 auto valueAxis = static_cast<QValue3DAxis *>(verticalAxis);
4065 for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) {
4066 auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(index: i));
4067 labelTrans.setY(valueAxis->labelPositionAt(index: i) * scale * 2.0f - translate);
4068 obj->setScale(fontScaled);
4069 obj->setPosition(labelTrans);
4070 obj->setProperty(name: "labelText", value: labels[i]);
4071 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
4072 obj->setProperty(name: "labelHeight", value: labelHeight);
4073 obj->setProperty(name: "labelFont", value: font);
4074 obj->setProperty(name: "borderEnabled", value: borderEnabled);
4075 obj->setProperty(name: "labelTextColor", value: labelTextColor);
4076 obj->setProperty(name: "backgroundEnabled", value: backgroundEnabled);
4077 obj->setProperty(name: "backgroundColor", value: backgroundColor);
4078 }
4079 } else if (verticalAxis->type() == QAbstract3DAxis::AxisTypeCategory) {
4080 for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) {
4081 labelTrans = calculateCategoryLabelPosition(axis: verticalAxis, labelPosition: labelTrans, index: i);
4082 auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(index: i));
4083 obj->setScale(fontScaled);
4084 obj->setPosition(labelTrans);
4085 obj->setProperty(name: "labelText", value: labels[i]);
4086 obj->setProperty(name: "labelWidth", value: labelsMaxWidth);
4087 obj->setProperty(name: "labelHeight", value: labelHeight);
4088 obj->setProperty(name: "labelFont", value: font);
4089 obj->setProperty(name: "borderEnabled", value: borderEnabled);
4090 obj->setProperty(name: "labelTextColor", value: labelTextColor);
4091 obj->setProperty(name: "backgroundEnabled", value: backgroundEnabled);
4092 obj->setProperty(name: "backgroundColor", value: backgroundColor);
4093 }
4094 }
4095
4096 labelHeight = fm.height() + textPadding;
4097 float labelWidth = fm.horizontalAdvance(verticalAxis->title()) + textPadding;
4098 QVector3D vTitleScale = fontScaled;
4099 vTitleScale.setX(fontScaled.y() * labelWidth / labelHeight);
4100 adjustment = labelHeight * scaleFactor;
4101 if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionRow))
4102 xPos = backgroundScale.x() + adjustment;
4103 else if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionColumn))
4104 xPos = backgroundScale.z() + adjustment;
4105 labelTrans = QVector3D(-xPos, 0.0f, 0.0f);
4106
4107 if (!verticalAxis->title().isEmpty()) {
4108 m_sliceVerticalTitleLabel->setScale(vTitleScale);
4109 m_sliceVerticalTitleLabel->setPosition(labelTrans);
4110 m_sliceVerticalTitleLabel->setProperty(name: "labelWidth", value: labelWidth);
4111 m_sliceVerticalTitleLabel->setProperty(name: "labelHeight", value: labelHeight);
4112 m_sliceVerticalTitleLabel->setProperty(name: "labelText", value: verticalAxis->title());
4113 m_sliceVerticalTitleLabel->setProperty(name: "labelFont", value: font);
4114 m_sliceVerticalTitleLabel->setProperty(name: "borderEnabled", value: borderEnabled);
4115 m_sliceVerticalTitleLabel->setProperty(name: "labelTextColor", value: labelTextColor);
4116 m_sliceVerticalTitleLabel->setProperty(name: "backgroundEnabled", value: backgroundEnabled);
4117 m_sliceVerticalTitleLabel->setProperty(name: "backgroundColor", value: backgroundColor);
4118 m_sliceVerticalTitleLabel->setEulerRotation(QVector3D(.0f, .0f, 90.0f));
4119 } else {
4120 m_sliceVerticalTitleLabel->setVisible(false);
4121 }
4122
4123 labelHeight = fm.height() + textPadding;
4124 labelWidth = fm.horizontalAdvance(horizontalAxis->title()) + textPadding;
4125 QVector3D hTitleScale = fontScaled;
4126 hTitleScale.setX(fontScaled.y() * labelWidth / labelHeight);
4127 adjustment = labelHeight * scaleFactor;
4128 yPos = backgroundScale.y() * 1.5f + adjustment;
4129 labelTrans = QVector3D(0.0f, -yPos, 0.0f);
4130
4131 if (!horizontalAxis->title().isEmpty()) {
4132 m_sliceHorizontalTitleLabel->setScale(hTitleScale);
4133 m_sliceHorizontalTitleLabel->setPosition(labelTrans);
4134 m_sliceHorizontalTitleLabel->setProperty(name: "labelWidth", value: labelWidth);
4135 m_sliceHorizontalTitleLabel->setProperty(name: "labelHeight", value: labelHeight);
4136 m_sliceHorizontalTitleLabel->setProperty(name: "labelText", value: horizontalAxis->title());
4137 m_sliceHorizontalTitleLabel->setProperty(name: "labelFont", value: font);
4138 m_sliceHorizontalTitleLabel->setProperty(name: "borderEnabled", value: borderEnabled);
4139 m_sliceHorizontalTitleLabel->setProperty(name: "labelTextColor", value: labelTextColor);
4140 m_sliceHorizontalTitleLabel->setProperty(name: "backgroundEnabled", value: backgroundEnabled);
4141 m_sliceHorizontalTitleLabel->setProperty(name: "backgroundColor", value: backgroundColor);
4142 } else {
4143 m_sliceHorizontalTitleLabel->setVisible(false);
4144 }
4145
4146 m_sliceItemLabel->setScale(fontScaled);
4147 m_sliceItemLabel->setProperty(name: "labelFont", value: font);
4148 m_sliceItemLabel->setProperty(name: "borderEnabled", value: borderEnabled);
4149 m_sliceItemLabel->setProperty(name: "labelTextColor", value: labelTextColor);
4150 m_sliceItemLabel->setProperty(name: "backgroundEnabled", value: backgroundEnabled);
4151 m_sliceItemLabel->setProperty(name: "backgroundColor", value: backgroundColor);
4152}
4153
4154void QQuickGraphsItem::setUpCamera()
4155{
4156 m_pCamera = new QQuick3DPerspectiveCamera(rootNode());
4157 m_pCamera->setClipNear(0.001f);
4158 m_pCamera->setFieldOfView(45.0f);
4159 m_pCamera->setPosition(QVector3D(.0f, .0f, 5.f));
4160
4161 auto cameraTarget = new QQuick3DNode(rootNode());
4162 cameraTarget->setParentItem(rootNode());
4163
4164 setCameraTarget(cameraTarget);
4165 cameraTarget->setPosition(QVector3D(0, 0, 0));
4166 QQuick3DObjectPrivate::get(item: cameraTarget)->refSceneManager(
4167 *QQuick3DObjectPrivate::get(item: rootNode())->sceneManager);
4168
4169 m_pCamera->lookAt(node: cameraTarget);
4170 m_pCamera->setParent(cameraTarget);
4171 m_pCamera->setParentItem(cameraTarget);
4172
4173 m_oCamera = new QQuick3DOrthographicCamera(rootNode());
4174 // Set clip near 0.0001f so that it can be set correct value to workaround
4175 // a Quick3D device pixel ratio bug
4176 m_oCamera->setClipNear(0.0001f);
4177 m_oCamera->setPosition(QVector3D(0.f, 0.f, 5.f));
4178 m_oCamera->setParent(cameraTarget);
4179 m_oCamera->setParentItem(cameraTarget);
4180 m_oCamera->lookAt(node: cameraTarget);
4181
4182 auto useOrtho = m_controller->isOrthoProjection();
4183 if (useOrtho)
4184 setCamera(m_oCamera);
4185 else
4186 setCamera(m_pCamera);
4187}
4188
4189void QQuickGraphsItem::setUpLight()
4190{
4191 auto light = new QQuick3DDirectionalLight(rootNode());
4192 QQuick3DObjectPrivate::get(item: light)->refSceneManager(
4193 *QQuick3DObjectPrivate::get(item: rootNode())->sceneManager);
4194 light->setParent(camera());
4195 light->setParentItem(camera());
4196 m_light = light;
4197}
4198
4199QT_END_NAMESPACE
4200

source code of qtgraphs/src/graphs/qml/qquickgraphsitem.cpp