summaryrefslogtreecommitdiffstats
path: root/src/graphs/qml/qquickgraphsitem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphs/qml/qquickgraphsitem.cpp')
-rw-r--r--src/graphs/qml/qquickgraphsitem.cpp2689
1 files changed, 2689 insertions, 0 deletions
diff --git a/src/graphs/qml/qquickgraphsitem.cpp b/src/graphs/qml/qquickgraphsitem.cpp
new file mode 100644
index 00000000..0517d0e1
--- /dev/null
+++ b/src/graphs/qml/qquickgraphsitem.cpp
@@ -0,0 +1,2689 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qquickgraphsitem_p.h"
+#include "declarativetheme_p.h"
+#include "declarativescene_p.h"
+#include "qvalue3daxis_p.h"
+#include "qcategory3daxis_p.h"
+#include "q3dscene_p.h"
+#include "abstract3dcontroller_p.h"
+#include "utils_p.h"
+
+#include <QtGui/QGuiApplication>
+
+#include <QtQuick3D/private/qquick3dperspectivecamera_p.h>
+#include <QtQuick3D/private/qquick3dorthographiccamera_p.h>
+#include <QtQuick3D/private/qquick3dcustommaterial_p.h>
+#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
+#include <QtQuick3D/private/qquick3ddirectionallight_p.h>
+#include <QtQuick3D/private/qquick3drepeater_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+
+#if defined(Q_OS_IOS)
+#include <QtCore/QTimer>
+#endif
+
+#if defined(Q_OS_MACOS)
+#include <qpa/qplatformnativeinterface.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QQuickGraphsItem::QQuickGraphsItem(QQuickItem *parent) :
+ QQuick3DViewport(parent),
+ m_controller(0),
+ m_renderMode(RenderIndirect),
+ m_samples(0),
+ m_windowSamples(0),
+ m_initialisedSize(0, 0)
+{
+ m_nodeMutex = QSharedPointer<QMutex>::create();
+
+ auto sceneManager = QQuick3DObjectPrivate::get(rootNode())->sceneManager;
+ connect(sceneManager.data(), &QQuick3DSceneManager::windowChanged, this, &QQuickGraphsItem::handleWindowChanged);
+ // Set contents to false in case we are in qml designer to make component look nice
+ m_runningInDesigner = QGuiApplication::applicationDisplayName() == QLatin1String("Qml2Puppet");
+ setFlag(ItemHasContents/*, !m_runningInDesigner*/); // Is this relevant anymore?
+
+ // Accept touchevents
+ setAcceptTouchEvents(true);
+}
+
+QQuickGraphsItem::~QQuickGraphsItem()
+{
+ disconnect(this, 0, this, 0);
+ checkWindowList(0);
+
+ // Make sure not deleting locked mutex
+ QMutexLocker locker(&m_mutex);
+ locker.unlock();
+
+ m_nodeMutex.clear();
+}
+
+void QQuickGraphsItem::setRenderingMode(QQuickGraphsItem::RenderingMode mode)
+{
+ if (mode == m_renderMode)
+ return;
+
+ RenderingMode previousMode = m_renderMode;
+
+ m_renderMode = mode;
+
+ m_initialisedSize = QSize(0, 0);
+ setFlag(ItemHasContents/*, !m_runningInDesigner*/);
+
+ // TODO - Need to check if the mode is set properly
+ switch (mode) {
+ case RenderDirectToBackground:
+ // Intentional flowthrough
+ case RenderDirectToBackground_NoClear:
+ update();
+ setRenderMode(QQuick3DViewport::Underlay);
+ if (previousMode == RenderIndirect) {
+ checkWindowList(window());
+ setAntialiasing(m_windowSamples > 0);
+ if (m_windowSamples != m_samples)
+ emit msaaSamplesChanged(m_windowSamples);
+ }
+ break;
+ case RenderIndirect:
+ update();
+ setRenderMode(QQuick3DViewport::Offscreen);
+ break;
+ }
+
+ updateWindowParameters();
+
+ emit renderingModeChanged(mode);
+}
+
+QQuickGraphsItem::RenderingMode QQuickGraphsItem::renderingMode() const
+{
+ return m_renderMode;
+}
+
+void QQuickGraphsItem::keyPressEvent(QKeyEvent *ev)
+{
+ ev->ignore();
+ setFlag(ItemHasContents);
+ update();
+}
+
+bool QQuickGraphsItem::handleMousePressedEvent(QMouseEvent *event)
+{
+ if (Qt::LeftButton == event->button()) {
+ if (m_sliceEnabled && m_controller->isSlicingActive()) {
+ m_sliceEnabled = false;
+ m_sliceActivatedChanged = true;
+ return false;
+ }
+ auto selectionMode = m_controller->selectionMode();
+ if (selectionMode.testFlag(QAbstract3DGraph::SelectionSlice)
+ && (selectionMode.testFlag(QAbstract3DGraph::SelectionColumn)
+ != selectionMode.testFlag(QAbstract3DGraph::SelectionRow))) {
+ m_sliceEnabled = true;
+ }
+ }
+ return true;
+}
+
+void QQuickGraphsItem::handleThemeTypeChange()
+{
+}
+
+void QQuickGraphsItem::handleFpsChanged()
+{
+ int fps = renderStats()->fps();
+ emit currentFpsChanged(fps);
+}
+
+void QQuickGraphsItem::componentComplete()
+{
+ QQuick3DViewport::componentComplete();
+
+ auto url = QUrl(QStringLiteral("defaultMeshes/backgroundMesh"));
+ m_background = new QQuick3DModel();
+ m_backgroundScale = new QQuick3DNode();
+ m_backgroundRotation = new QQuick3DNode();
+
+ m_backgroundScale->setParent(rootNode());
+ m_backgroundScale->setParentItem(rootNode());
+
+ m_backgroundRotation->setParent(m_backgroundScale);
+ m_backgroundRotation->setParentItem(m_backgroundScale);
+
+ m_background->setObjectName("Background");
+ m_background->setParent(m_backgroundRotation);
+ m_background->setParentItem(m_backgroundRotation);
+
+ m_background->setSource(url);
+
+ m_backgroundBB = new QQuick3DModel();
+ m_backgroundBB->setObjectName("BackgroundBB");
+ m_backgroundBB->setParent(m_background);
+ m_backgroundBB->setParentItem(m_background);
+ m_backgroundBB->setSource(QUrl(QStringLiteral("defaultMeshes/barMeshFull")));
+ m_backgroundBB->setPickable(true);
+
+ setUpCamera();
+ setUpLight();
+
+ // Create repeaters for each axis X, Y, Z
+ m_repeaterX = createRepeater();
+ m_repeaterY = createRepeater();
+ m_repeaterZ = createRepeater();
+
+ auto delegateModelX = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel"));
+ auto delegateModelY = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel"));
+ auto delegateModelZ = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel"));
+
+ m_repeaterX->setDelegate(delegateModelX);
+ m_repeaterY->setDelegate(delegateModelY);
+ m_repeaterZ->setDelegate(delegateModelZ);
+
+ // title labels for axes
+ m_titleLabelX = createTitleLabel();
+ m_titleLabelY = createTitleLabel();
+ m_titleLabelZ = createTitleLabel();
+
+ // Testing gridline
+
+ // X lines
+ m_segmentLineRepeaterX = createRepeater();
+
+ auto segmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
+ m_segmentLineRepeaterX->setDelegate(segmentLineDelegate);
+
+ m_subsegmentLineRepeaterX = createRepeater();
+ auto subsegmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
+ m_subsegmentLineRepeaterX->setDelegate(subsegmentLineDelegate);
+
+ // Y lines
+ m_segmentLineRepeaterY = createRepeater();
+ segmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
+ m_segmentLineRepeaterY->setDelegate(segmentLineDelegate);
+
+ m_subsegmentLineRepeaterY = createRepeater();
+ subsegmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
+ m_subsegmentLineRepeaterY->setDelegate(subsegmentLineDelegate);
+
+ // Z lines
+ m_segmentLineRepeaterZ = createRepeater();
+
+ segmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
+ m_segmentLineRepeaterZ->setDelegate(segmentLineDelegate);
+
+ m_subsegmentLineRepeaterZ = createRepeater();
+ subsegmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
+ m_subsegmentLineRepeaterZ->setDelegate(subsegmentLineDelegate);
+
+ m_itemLabel = createTitleLabel();
+
+ auto axis = m_controller->axisX();
+ int segmentCount = 0;
+ int subSegmentCount = 0;
+ int gridLineCount = 0;
+ int subGridLineCount = 0;
+ if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
+ segmentCount = valueAxis->segmentCount();
+ subSegmentCount = valueAxis->subSegmentCount();
+ gridLineCount = 2 * (segmentCount + 1);
+ subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1));
+ } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) {
+ gridLineCount = axis->labels().size();
+ }
+ m_segmentLineRepeaterX->setModel(gridLineCount);
+ m_subsegmentLineRepeaterX->setModel(subGridLineCount);
+ m_repeaterX->setModel(axis->labels().size());
+ m_controller->handleAxisLabelsChangedBySender(m_controller->axisX());
+
+ axis = m_controller->axisY();
+ if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
+ segmentCount = valueAxis->segmentCount();
+ subSegmentCount = valueAxis->subSegmentCount();
+ gridLineCount = 2 * (segmentCount + 1);
+ subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1));
+ } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) {
+ gridLineCount = axis->labels().size();
+ }
+ m_segmentLineRepeaterY->setModel(gridLineCount);
+ m_subsegmentLineRepeaterY->setModel(subGridLineCount);
+ m_repeaterY->setModel(2 * axis->labels().size());
+ m_controller->handleAxisLabelsChangedBySender(m_controller->axisY());
+
+ axis = m_controller->axisZ();
+ if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
+ segmentCount = valueAxis->segmentCount();
+ subSegmentCount = valueAxis->subSegmentCount();
+ gridLineCount = 2 * (segmentCount + 1);
+ subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1));
+ } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) {
+ gridLineCount = axis->labels().size();
+ }
+ m_segmentLineRepeaterZ->setModel(gridLineCount);
+ m_subsegmentLineRepeaterZ->setModel(subGridLineCount);
+ m_repeaterZ->setModel(axis->labels().size());
+ m_controller->handleAxisLabelsChangedBySender(m_controller->axisZ());
+}
+
+QQuick3DDirectionalLight *QQuickGraphsItem::light() const
+{
+ return m_light;
+}
+
+void QQuickGraphsItem::setTheme(Q3DTheme *theme)
+{
+ m_controller->setActiveTheme(theme, isComponentComplete());
+}
+
+Q3DTheme *QQuickGraphsItem::theme() const
+{
+ return m_controller->activeTheme();
+}
+
+void QQuickGraphsItem::clearSelection()
+{
+ m_controller->clearSelection();
+}
+
+bool QQuickGraphsItem::hasSeries(QAbstract3DSeries *series)
+{
+ return m_controller->hasSeries(series);
+}
+
+void QQuickGraphsItem::setSelectionMode(SelectionFlags mode)
+{
+ int intmode = int(mode);
+ m_controller->setSelectionMode(QAbstract3DGraph::SelectionFlags(intmode));
+}
+
+QQuickGraphsItem::SelectionFlags QQuickGraphsItem::selectionMode() const
+{
+ int intmode = int(m_controller->selectionMode());
+ return SelectionFlags(intmode);
+}
+
+void QQuickGraphsItem::setShadowQuality(ShadowQuality quality)
+{
+ m_controller->setShadowQuality(QAbstract3DGraph::ShadowQuality(quality));
+}
+
+QQuickGraphsItem::ShadowQuality QQuickGraphsItem::shadowQuality() const
+{
+ return ShadowQuality(m_controller->shadowQuality());
+}
+
+bool QQuickGraphsItem::shadowsSupported() const
+{
+ return m_controller->shadowsSupported();
+}
+
+int QQuickGraphsItem::addCustomItem(QCustom3DItem *item)
+{
+ return m_controller->addCustomItem(item);
+}
+
+void QQuickGraphsItem::removeCustomItems()
+{
+ m_controller->deleteCustomItems();
+}
+
+void QQuickGraphsItem::removeCustomItem(QCustom3DItem *item)
+{
+ m_controller->deleteCustomItem(item);
+}
+
+void QQuickGraphsItem::removeCustomItemAt(const QVector3D &position)
+{
+ m_controller->deleteCustomItem(position);
+}
+
+void QQuickGraphsItem::releaseCustomItem(QCustom3DItem *item)
+{
+ m_controller->releaseCustomItem(item);
+}
+
+int QQuickGraphsItem::selectedLabelIndex() const
+{
+ return m_controller->selectedLabelIndex();
+}
+
+QAbstract3DAxis *QQuickGraphsItem::selectedAxis() const
+{
+ return m_controller->selectedAxis();
+}
+
+int QQuickGraphsItem::selectedCustomItemIndex() const
+{
+ return m_controller->selectedCustomItemIndex();
+}
+
+QCustom3DItem *QQuickGraphsItem::selectedCustomItem() const
+{
+ return m_controller->selectedCustomItem();
+}
+
+QQmlListProperty<QCustom3DItem> QQuickGraphsItem::customItemList()
+{
+ return QQmlListProperty<QCustom3DItem>(this, this,
+ &QQuickGraphsItem::appendCustomItemFunc,
+ &QQuickGraphsItem::countCustomItemFunc,
+ &QQuickGraphsItem::atCustomItemFunc,
+ &QQuickGraphsItem::clearCustomItemFunc);
+}
+
+void QQuickGraphsItem::appendCustomItemFunc(QQmlListProperty<QCustom3DItem> *list,
+ QCustom3DItem *item)
+{
+ QQuickGraphsItem *decl = reinterpret_cast<QQuickGraphsItem *>(list->data);
+ decl->addCustomItem(item);
+}
+
+qsizetype QQuickGraphsItem::countCustomItemFunc(QQmlListProperty<QCustom3DItem> *list)
+{
+ Q_UNUSED(list);
+ return 0;
+// return reinterpret_cast<QQuickGraphsItem *>(list->data)->m_controller->m_customItems.size();
+}
+
+QCustom3DItem *QQuickGraphsItem::atCustomItemFunc(QQmlListProperty<QCustom3DItem> *list,
+ qsizetype index)
+{
+ Q_UNUSED(list);
+ Q_UNUSED(index);
+ return new QCustom3DItem();
+// return reinterpret_cast<QQuickGraphsItem *>(list->data)->m_controller->m_customItems.at(index);
+}
+
+void QQuickGraphsItem::clearCustomItemFunc(QQmlListProperty<QCustom3DItem> *list)
+{
+ QQuickGraphsItem *decl = reinterpret_cast<QQuickGraphsItem *>(list->data);
+ decl->removeCustomItems();
+}
+
+void QQuickGraphsItem::setSharedController(Abstract3DController *controller)
+{
+ Q_ASSERT(controller);
+ m_controller = controller;
+ m_controller->m_qml = this;
+
+ if (!m_controller->isOpenGLES())
+ setMsaaSamples(4);
+
+ // Reset default theme, as the default C++ theme is Q3DTheme, not DeclarativeTheme3D.
+ DeclarativeTheme3D *defaultTheme = new DeclarativeTheme3D;
+ defaultTheme->d_ptr->setDefaultTheme(true);
+ defaultTheme->setType(Q3DTheme::ThemeQt);
+ m_controller->setActiveTheme(defaultTheme);
+
+ QObject::connect(m_controller.data(), &Abstract3DController::shadowQualityChanged, this,
+ &QQuickGraphsItem::handleShadowQualityChange);
+ QObject::connect(m_controller.data(), &Abstract3DController::activeInputHandlerChanged, this,
+ &QQuickGraphsItem::inputHandlerChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::activeThemeChanged, this,
+ &QQuickGraphsItem::themeChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::themeTypeChanged, this,
+ &QQuickGraphsItem::handleThemeTypeChange);
+ QObject::connect(m_controller.data(), &Abstract3DController::selectionModeChanged, this,
+ &QQuickGraphsItem::handleSelectionModeChange);
+ QObject::connect(m_controller.data(), &Abstract3DController::elementSelected, this,
+ &QQuickGraphsItem::handleSelectedElementChange);
+
+ QObject::connect(m_controller.data(), &Abstract3DController::axisXChanged, this,
+ &QQuickGraphsItem::handleAxisXChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::axisYChanged, this,
+ &QQuickGraphsItem::handleAxisYChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::axisZChanged, this,
+ &QQuickGraphsItem::handleAxisZChanged);
+
+ QObject::connect(m_controller.data(), &Abstract3DController::measureFpsChanged, this,
+ &QQuickGraphsItem::measureFpsChanged);
+// QObject::connect(m_controller.data(), &Abstract3DController::currentFpsChanged, this,
+// &QQuickGraphsItem::currentFpsChanged);
+
+ QObject::connect(m_controller.data(), &Abstract3DController::orthoProjectionChanged, this,
+ &QQuickGraphsItem::orthoProjectionChanged);
+
+ QObject::connect(m_controller.data(), &Abstract3DController::aspectRatioChanged, this,
+ &QQuickGraphsItem::aspectRatioChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::optimizationHintsChanged, this,
+ &QQuickGraphsItem::handleOptimizationHintChange);
+ QObject::connect(m_controller.data(), &Abstract3DController::polarChanged, this,
+ &QQuickGraphsItem::polarChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::radialLabelOffsetChanged, this,
+ &QQuickGraphsItem::radialLabelOffsetChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::horizontalAspectRatioChanged, this,
+ &QQuickGraphsItem::horizontalAspectRatioChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::reflectionChanged, this,
+ &QQuickGraphsItem::reflectionChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::reflectivityChanged, this,
+ &QQuickGraphsItem::reflectivityChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::localeChanged, this,
+ &QQuickGraphsItem::localeChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::queriedGraphPositionChanged, this,
+ &QQuickGraphsItem::queriedGraphPositionChanged);
+ QObject::connect(m_controller.data(), &Abstract3DController::marginChanged, this,
+ &QQuickGraphsItem::marginChanged);
+}
+
+void QQuickGraphsItem::synchData()
+{
+ m_controller->m_renderPending = false;
+
+ bool axisFormatterChanged = false;
+ if (m_controller->m_changeTracker.axisXFormatterChanged) {
+ m_controller->m_changeTracker.axisXFormatterChanged = false;
+ QAbstract3DAxis *axisX = m_controller->axisX();
+ if (axisX->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX);
+ valueAxisX->recalculate();
+ }
+ axisFormatterChanged = true;
+ }
+
+ if (m_controller->m_changeTracker.axisYFormatterChanged) {
+ m_controller->m_changeTracker.axisYFormatterChanged = false;
+ QAbstract3DAxis *axisY = m_controller->axisY();
+ if (axisY->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY);
+ valueAxisY->recalculate();
+ }
+ axisFormatterChanged = true;
+ }
+
+ if (m_controller->m_changeTracker.axisZFormatterChanged) {
+ m_controller->m_changeTracker.axisZFormatterChanged = false;
+ QAbstract3DAxis *axisZ = m_controller->axisZ();
+ if (axisZ->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ);
+ valueAxisZ->recalculate();
+ }
+ axisFormatterChanged = true;
+ }
+
+ if (m_controller->m_changeTracker.shadowQualityChanged) {
+ updateShadowQuality(shadowQuality());
+ m_controller->m_changeTracker.shadowQualityChanged = false;
+ }
+
+ if (m_controller->m_changeTracker.axisYRangeChanged) {
+ updateAxisRange(m_controller->m_axisY->min(), m_controller->m_axisY->max());
+ m_controller->m_changeTracker.axisYRangeChanged = false;
+ }
+
+ if (m_controller->m_changeTracker.axisYReversedChanged) {
+ m_controller->m_changeTracker.axisYReversedChanged = false;
+ if (m_controller->m_axisY->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_controller->m_axisY);
+ updateAxisReversed(valueAxisY->reversed());
+ }
+ }
+
+ QVector3D forward = camera()->forward();
+ auto targetRotation = cameraTarget()->rotation();
+ bool viewFlipped = false;
+ if (m_yFlipped != (targetRotation.x() > 0)) {
+ m_yFlipped = (targetRotation.x() > 0);
+ viewFlipped = true;
+ }
+ if (m_xFlipped != (forward.x() > 0)) {
+ m_xFlipped = (forward.x() > 0);
+ viewFlipped = true;
+ }
+ if (m_zFlipped != (forward.z() >= 0)) {
+ m_zFlipped = (forward.z() >= 0);
+ viewFlipped = true;
+ }
+
+ if (axisFormatterChanged || viewFlipped) {
+ updateGrid();
+ updateLabels();
+ }
+
+ QMatrix4x4 modelMatrix;
+ m_backgroundScale->setScale(m_scaleWithBackground + m_backgroundScaleMargin);
+
+ QVector3D rotVec;
+ if (!m_yFlipped) {
+ rotVec = QVector3D(0, 270, 0);
+ if (m_xFlipped && m_zFlipped)
+ rotVec.setY(90);
+ else if (!m_xFlipped && m_zFlipped)
+ rotVec.setY(0);
+ else if (m_xFlipped && !m_zFlipped)
+ rotVec.setY(180);
+ } else {
+ rotVec = QVector3D(0, 180, 180);
+ if (m_xFlipped && m_zFlipped)
+ rotVec.setY(0);
+ else if (!m_xFlipped && m_zFlipped)
+ rotVec.setY(270);
+ else if (m_xFlipped && !m_zFlipped)
+ rotVec.setY(90);
+ }
+
+ auto rotation = Utils::calculateRotation(rotVec);
+ if (m_yFlipped) {
+ m_backgroundRotation->setRotation(rotation);
+ } else {
+ modelMatrix.rotate(rotation);
+ m_backgroundRotation->setRotation(rotation);
+ }
+
+ if (m_controller->graphPositionQueryPending())
+ graphPositionAt(m_controller->scene()->graphPositionQuery());
+
+ updateCamera();
+
+ Q3DTheme *theme = m_controller->activeTheme();
+
+ if (m_controller->m_changeTracker.themeChanged) {
+ environment()->setClearColor(theme->windowColor());
+
+ m_controller->m_changeTracker.themeChanged = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.lightStrengthDirty) {
+ light()->setBrightness(theme->lightStrength() * .2f);
+ theme->d_ptr->m_dirtyBits.lightStrengthDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.ambientLightStrengthDirty) {
+ float ambientStrength = theme->ambientLightStrength();
+ QColor ambientColor = QColor::fromRgbF(ambientStrength, ambientStrength, ambientStrength);
+ light()->setAmbientColor(ambientColor);
+ theme->d_ptr->m_dirtyBits.ambientLightStrengthDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.lightColorDirty) {
+ light()->setColor(theme->lightColor());
+ theme->d_ptr->m_dirtyBits.lightColorDirty = false;
+ }
+
+ // label Adjustments
+ if (m_repeaterX->count() && m_repeaterZ->count()) {
+ if (theme->d_ptr->m_dirtyBits.labelBackgroundColorDirty) {
+ QColor labelBackgroundColor = theme->labelBackgroundColor();
+ changeLabelBackgroundColor(m_repeaterX, labelBackgroundColor);
+ changeLabelBackgroundColor(m_repeaterY, labelBackgroundColor);
+ changeLabelBackgroundColor(m_repeaterZ, labelBackgroundColor);
+ m_titleLabelX->setProperty("backgroundColor", labelBackgroundColor);
+ m_titleLabelY->setProperty("backgroundColor", labelBackgroundColor);
+ m_titleLabelZ->setProperty("backgroundColor", labelBackgroundColor);
+ m_itemLabel->setProperty("backgroundColor", labelBackgroundColor);
+
+ if (m_sliceView) {
+ changeLabelBackgroundColor(m_sliceHorizontalLabelRepeater, labelBackgroundColor);
+ changeLabelBackgroundColor(m_sliceVerticalLabelRepeater, labelBackgroundColor);
+ m_sliceItemLabel->setProperty("backgroundColor", labelBackgroundColor);
+ m_sliceHorizontalTitleLabel->setProperty("backgroundColor", labelBackgroundColor);
+ m_sliceVerticalTitleLabel->setProperty("backgroundColor", labelBackgroundColor);
+ }
+ theme->d_ptr->m_dirtyBits.labelBackgroundColorDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.labelBackgroundEnabledDirty) {
+ bool enabled = theme->isLabelBackgroundEnabled();
+ changeLabelBackgroundEnabled(m_repeaterX, enabled);
+ changeLabelBackgroundEnabled(m_repeaterY, enabled);
+ changeLabelBackgroundEnabled(m_repeaterZ, enabled);
+ m_titleLabelX->setProperty("backgroundEnabled", enabled);
+ m_titleLabelY->setProperty("backgroundEnabled", enabled);
+ m_titleLabelZ->setProperty("backgroundEnabled", enabled);
+ m_itemLabel->setProperty("backgroundEnabled", enabled);
+
+ if (m_sliceView) {
+ changeLabelBackgroundEnabled(m_sliceHorizontalLabelRepeater, enabled);
+ changeLabelBackgroundEnabled(m_sliceVerticalLabelRepeater, enabled);
+ m_sliceItemLabel->setProperty("backgroundEnabled", enabled);
+ m_sliceHorizontalTitleLabel->setProperty("backgroundEnabled", enabled);
+ m_sliceVerticalTitleLabel->setProperty("backgroundEnabled", enabled);
+ }
+ theme->d_ptr->m_dirtyBits.labelBackgroundEnabledDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.labelBorderEnabledDirty) {
+ bool enabled = theme->isLabelBorderEnabled();
+ changeLabelBorderEnabled(m_repeaterX, enabled);
+ changeLabelBorderEnabled(m_repeaterY, enabled);
+ changeLabelBorderEnabled(m_repeaterZ, enabled);
+ m_titleLabelX->setProperty("borderEnabled", enabled);
+ m_titleLabelY->setProperty("borderEnabled", enabled);
+ m_titleLabelZ->setProperty("borderEnabled", enabled);
+ m_itemLabel->setProperty("borderEnabled", enabled);
+
+ if (m_sliceView) {
+ changeLabelBorderEnabled(m_sliceHorizontalLabelRepeater, enabled);
+ changeLabelBorderEnabled(m_sliceVerticalLabelRepeater, enabled);
+ m_sliceItemLabel->setProperty("borderEnabled", enabled);
+ m_sliceHorizontalTitleLabel->setProperty("borderEnabled", enabled);
+ m_sliceVerticalTitleLabel->setProperty("borderEnabled", enabled);
+ }
+ theme->d_ptr->m_dirtyBits.labelBorderEnabledDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.labelTextColorDirty) {
+ QColor labelTextColor = theme->labelTextColor();
+ changeLabelTextColor(m_repeaterX, labelTextColor);
+ changeLabelTextColor(m_repeaterY, labelTextColor);
+ changeLabelTextColor(m_repeaterZ, labelTextColor);
+ m_titleLabelX->setProperty("labelTextColor", labelTextColor);
+ m_titleLabelY->setProperty("labelTextColor", labelTextColor);
+ m_titleLabelZ->setProperty("labelTextColor", labelTextColor);
+ m_itemLabel->setProperty("labelTextColor", labelTextColor);
+
+ if (m_sliceView) {
+ changeLabelTextColor(m_sliceHorizontalLabelRepeater, labelTextColor);
+ changeLabelTextColor(m_sliceVerticalLabelRepeater, labelTextColor);
+ m_sliceItemLabel->setProperty("labelTextColor", labelTextColor);
+ m_sliceHorizontalTitleLabel->setProperty("labelTextColor", labelTextColor);
+ m_sliceVerticalTitleLabel->setProperty("labelTextColor", labelTextColor);
+ }
+ theme->d_ptr->m_dirtyBits.labelTextColorDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.fontDirty) {
+ auto font = theme->font();
+ changeLabelFont(m_repeaterX, font);
+ changeLabelFont(m_repeaterY, font);
+ changeLabelFont(m_repeaterZ, font);
+ m_titleLabelX->setProperty("labelFont", font);
+ m_titleLabelY->setProperty("labelFont", font);
+ m_titleLabelZ->setProperty("labelFont", font);
+ m_itemLabel->setProperty("labelFont", font);
+ updateLabels();
+
+ if (m_sliceView) {
+ changeLabelFont(m_sliceHorizontalLabelRepeater, font);
+ changeLabelFont(m_sliceVerticalLabelRepeater, font);
+ m_sliceItemLabel->setProperty("labelFont", font);
+ m_sliceHorizontalTitleLabel->setProperty("labelFont", font);
+ m_sliceVerticalTitleLabel->setProperty("labelFont", font);
+ updateSliceLabels();
+ }
+ theme->d_ptr->m_dirtyBits.fontDirty = false;
+ }
+
+ // Grid and background adjustments
+ if (theme->d_ptr->m_dirtyBits.backgroundColorDirty) {
+ QQmlListReference materialsRef(m_background, "materials");
+ QQuick3DPrincipledMaterial *bgMat;
+ if (!materialsRef.size()) {
+ bgMat = new QQuick3DPrincipledMaterial();
+ bgMat->setParent(this);
+ bgMat->setRoughness(.3f);
+ bgMat->setEmissiveFactor(QVector3D(.075f, .075f, .075f));
+ materialsRef.append(bgMat);
+ } else {
+ bgMat = static_cast<QQuick3DPrincipledMaterial *>(materialsRef.at(0));
+ }
+ bgMat->setBaseColor(theme->backgroundColor());
+ theme->d_ptr->m_dirtyBits.backgroundColorDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.backgroundEnabledDirty) {
+ m_background->setVisible(theme->isBackgroundEnabled());
+ theme->d_ptr->m_dirtyBits.backgroundEnabledDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.gridEnabledDirty) {
+ bool enabled = theme->isGridEnabled();
+ m_segmentLineRepeaterX->setVisible(enabled);
+ m_segmentLineRepeaterY->setVisible(enabled);
+ m_segmentLineRepeaterZ->setVisible(enabled);
+
+ m_subsegmentLineRepeaterX->setVisible(enabled);
+ m_subsegmentLineRepeaterY->setVisible(enabled);
+ m_subsegmentLineRepeaterZ->setVisible(enabled);
+
+ if (m_sliceEnabled) {
+ m_sliceHorizontalGridRepeater->setVisible(enabled);
+ m_sliceVerticalGridRepeater->setVisible(enabled);
+ }
+ theme->d_ptr->m_dirtyBits.gridEnabledDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.gridLineColorDirty) {
+ QColor gridLineColor = theme->gridLineColor();
+ changeGridLineColor(m_segmentLineRepeaterX, gridLineColor);
+ changeGridLineColor(m_subsegmentLineRepeaterX, gridLineColor);
+ changeGridLineColor(m_segmentLineRepeaterY, gridLineColor);
+ changeGridLineColor(m_subsegmentLineRepeaterY, gridLineColor);
+ changeGridLineColor(m_segmentLineRepeaterZ, gridLineColor);
+ changeGridLineColor(m_subsegmentLineRepeaterZ, gridLineColor);
+ theme->d_ptr->m_dirtyBits.gridLineColorDirty = false;
+ }
+
+ if (theme->d_ptr->m_dirtyBits.singleHighlightColorDirty) {
+ updateSingleHighlightColor();
+ theme->d_ptr->m_dirtyBits.singleHighlightColorDirty = false;
+ }
+ }
+
+ // Other adjustments
+ if (theme->d_ptr->m_dirtyBits.windowColorDirty) {
+ environment()->setClearColor(theme->windowColor());
+ theme->d_ptr->m_dirtyBits.windowColorDirty = false;
+ }
+
+ if (m_controller->m_changedSeriesList.size()) {
+ updateGraph();
+ m_controller->m_changedSeriesList.clear();
+ }
+
+ if (m_controller->m_isSeriesVisualsDirty) {
+ updateGraph();
+ m_controller->m_isSeriesVisualsDirty = false;
+ }
+
+ if (m_controller->m_isDataDirty) {
+ updateGraph();
+ m_controller->m_isDataDirty = false;
+ }
+
+ if (m_sliceActivatedChanged) {
+ updateSliceGraph();
+ m_sliceActivatedChanged = false;
+ }
+}
+
+void QQuickGraphsItem::updateGrid()
+{
+ int gridLineCountX = segmentLineRepeaterX()->count();
+ int subGridLineCountX = subsegmentLineRepeaterX()->count();
+ int gridLineCountY = segmentLineRepeaterY()->count() / 2;
+ int subGridLineCountY = subsegmentLineRepeaterY()->count() / 2;
+ int gridLineCountZ = segmentLineRepeaterZ()->count();
+ int subGridLineCountZ = subsegmentLineRepeaterZ()->count();
+
+ if (!m_isFloorGridInRange) {
+ gridLineCountX /= 2;
+ subGridLineCountX /= 2;
+ gridLineCountZ /= 2;
+ subGridLineCountZ /= 2;
+ }
+
+ auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin;
+ QVector3D scaleX(backgroundScale.x() * lineLengthScaleFactor(), lineWidthScaleFactor(), lineWidthScaleFactor());
+ QVector3D scaleY(lineWidthScaleFactor(), backgroundScale.y() * lineLengthScaleFactor(), lineWidthScaleFactor());
+ QVector3D scaleZ(backgroundScale.z() * lineLengthScaleFactor(), lineWidthScaleFactor(), lineWidthScaleFactor());
+
+ auto axisX = m_controller->axisX();
+ auto axisY = m_controller->axisY();
+ auto axisZ = m_controller->axisZ();
+
+ const bool xFlipped = isXFlipped();
+ const bool yFlipped = isYFlipped();
+ const bool zFlipped = isZFlipped();
+
+ QQuaternion lineRotation(.0f, .0f, .0f, .0f);
+ QVector3D rotation(90.0f, 0.0f, 0.0f);
+
+ // Floor horizontal line
+ float linePosX = 0.0f;
+ float linePosY = backgroundScale.y();
+ float linePosZ = 0.0f;
+ float scale = m_scaleWithBackground.z();
+ float translate = m_scaleWithBackground.z();
+
+ if (!yFlipped) {
+ linePosY *= -1.0f;
+ rotation.setZ(180.0f);
+ }
+ lineRotation = Utils::calculateRotation(rotation);
+ for (int i = 0; i < subGridLineCountZ; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterZ()->objectAt(i));
+ if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
+ linePosZ = static_cast<QValue3DAxis *>(axisZ)->subGridPositionAt(i) * -scale * 2.0f + translate;
+ } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
+ linePosZ = calculateCategoryGridLinePosition(axisZ, i);
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ }
+ positionAndScaleLine(lineNode, scaleX, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+
+ for (int i = 0; i < gridLineCountZ; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterZ()->objectAt(i));
+ if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
+ linePosZ = static_cast<QValue3DAxis *>(axisZ)->gridPositionAt(i) * -scale * 2.0f + translate;
+ } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
+ linePosZ = calculateCategoryGridLinePosition(axisZ, i);
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ }
+ positionAndScaleLine(lineNode, scaleX, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+
+ // Side vertical line
+ linePosX = -backgroundScale.x();
+ linePosY = 0.0f;
+ rotation = QVector3D(0.0f, 90.0f, 0.0f);
+ if (xFlipped) {
+ linePosX *= -1.0f;
+ rotation.setY(-90.0f);
+ }
+ lineRotation = Utils::calculateRotation(rotation);
+ if (m_hasVerticalSegmentLine) {
+ for (int i = 0; i < subGridLineCountZ; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterZ()->objectAt(i + subGridLineCountZ));
+ if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
+ linePosZ = static_cast<QValue3DAxis *>(axisZ)->subGridPositionAt(i) * scale * 2.0f - translate;
+ } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
+ linePosX = calculateCategoryGridLinePosition(axisZ, i);
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ }
+ positionAndScaleLine(lineNode, scaleY, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+ for (int i = 0; i < gridLineCountZ; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterZ()->objectAt(i + gridLineCountZ));
+ if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
+ linePosZ = static_cast<QValue3DAxis *>(axisZ)->gridPositionAt(i) * scale * 2.0f - translate;
+ } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
+ linePosX = calculateCategoryGridLinePosition(axisZ, i);
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ }
+ positionAndScaleLine(lineNode, scaleY, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+ }
+
+ // Side horizontal line
+ linePosZ = 0.0f;
+ scale = translate = m_scaleWithBackground.y();
+ rotation = QVector3D(180.0f, -90.0f, 0.0f);
+ if (xFlipped)
+ rotation.setY(90.0f);
+ lineRotation = Utils::calculateRotation(rotation);
+ for (int i = 0; i < gridLineCountY; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterY()->objectAt(i));
+ if (axisY->type() == QAbstract3DAxis::AxisTypeValue)
+ linePosY = static_cast<QValue3DAxis *>(axisY)->gridPositionAt(i) * scale * 2.0f - translate;
+ else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory)
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ positionAndScaleLine(lineNode, scaleZ, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+
+ for (int i = 0; i < subGridLineCountY; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterY()->objectAt(i));
+ if (axisY->type() == QAbstract3DAxis::AxisTypeValue)
+ linePosY = static_cast<QValue3DAxis *>(axisY)->subGridPositionAt(i) * scale * 2.0f - translate;
+ else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory)
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ positionAndScaleLine(lineNode, scaleZ, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+
+ // Floor vertical line
+ linePosZ = 0.0f;
+ linePosY = -backgroundScale.y();
+ rotation = QVector3D(-90.0f, 90.0f, 0.0f);
+ if (yFlipped) {
+ linePosY *= -1.0f;
+ rotation.setZ(180.0f);
+ }
+ scale = translate = m_scaleWithBackground.x();
+ lineRotation = Utils::calculateRotation(rotation);
+ for (int i = 0; i < subGridLineCountX; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterX()->objectAt(i));
+ if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
+ linePosX = static_cast<QValue3DAxis *>(axisX)->subGridPositionAt(i) * scale * 2.0f - translate;
+ } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
+ linePosX = calculateCategoryGridLinePosition(axisX, i);
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ }
+ positionAndScaleLine(lineNode, scaleZ, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+ for (int i = 0; i < gridLineCountX; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterX()->objectAt(i));
+ if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
+ linePosX = static_cast<QValue3DAxis *>(axisX)->gridPositionAt(i) * scale * 2.0f - translate;
+ } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
+ linePosX = calculateCategoryGridLinePosition(axisX, i);
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ }
+ positionAndScaleLine(lineNode, scaleZ, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+
+ // Back horizontal line
+ linePosX = 0.0f;
+ linePosZ = -backgroundScale.z();
+ rotation = QVector3D(0.0f, 0.0f, 0.0f);
+ if (zFlipped) {
+ linePosZ *= -1.0f;
+ rotation.setX(180.0f);
+ }
+ lineRotation = Utils::calculateRotation(rotation);
+ scale = translate = m_scaleWithBackground.y();
+ for (int i = 0; i < subGridLineCountY; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterY()->objectAt(i + subGridLineCountY));
+ if (axisY->type() == QAbstract3DAxis::AxisTypeValue)
+ linePosY = static_cast<QValue3DAxis *>(axisY)->subGridPositionAt(i) * scale * 2.0f - translate;
+ else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory)
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ positionAndScaleLine(lineNode, scaleX, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+
+ for (int i = 0; i < gridLineCountY; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterY()->objectAt(i + gridLineCountY));
+ if (axisY->type() == QAbstract3DAxis::AxisTypeValue)
+ linePosY = static_cast<QValue3DAxis *>(axisY)->gridPositionAt(i) * scale * 2.0f - translate;
+ else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory)
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ positionAndScaleLine(lineNode, scaleX, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+
+ // Back vertical line
+ linePosY = 0.0f;
+ scale = translate = m_scaleWithBackground.x();
+ rotation = QVector3D(0.0f, 0.0f, 0.0f);
+ if (zFlipped)
+ rotation.setY(180.0f);
+ lineRotation = Utils::calculateRotation(rotation);
+ if (m_hasVerticalSegmentLine) {
+ for (int i = 0; i < gridLineCountX; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterX()->objectAt(i + gridLineCountX));
+ if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
+ linePosX = static_cast<QValue3DAxis *>(axisX)->gridPositionAt(i) * scale * 2.0f - translate;
+ } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
+ linePosX = calculateCategoryGridLinePosition(axisX, i);
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ }
+ positionAndScaleLine(lineNode, scaleY, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+
+ for (int i = 0; i < subGridLineCountX; i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterX()->objectAt(i + subGridLineCountX));
+ if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
+ linePosX = static_cast<QValue3DAxis *>(axisX)->subGridPositionAt(i) * scale * 2.0f - translate;
+ } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
+ linePosX = calculateCategoryGridLinePosition(axisX, i);
+ linePosY = calculateCategoryGridLinePosition(axisY, i);
+ }
+ positionAndScaleLine(lineNode, scaleY, QVector3D(linePosX, linePosY, linePosZ));
+ lineNode->setRotation(lineRotation);
+ }
+ }
+}
+
+void QQuickGraphsItem::updateLabels()
+{
+ auto axisX = m_controller->axisX();
+
+ auto labels = axisX->labels();
+ float labelAutoAngle = axisX->labelAutoRotation();
+ float labelAngleFraction = labelAutoAngle / 90.0f;
+ float fractionCamX = m_controller->scene()->activeCamera()->xRotation() * labelAngleFraction;
+ float fractionCamY = m_controller->scene()->activeCamera()->yRotation() * labelAngleFraction;
+
+ QVector3D labelRotation = QVector3D(0.0f, 0.0f, 0.0f);
+
+ float xPos = 0.0f;
+ float yPos = 0.0f;
+ float zPos = 0.0f;
+
+ const bool xFlipped = isXFlipped();
+ const bool yFlipped = isYFlipped();
+ const bool zFlipped = isZFlipped();
+
+ auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin;
+
+ if (labelAutoAngle == 0.0f) {
+ labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
+ if (xFlipped)
+ labelRotation.setY(-90.0f);
+ if (yFlipped) {
+ if (xFlipped)
+ labelRotation.setY(-90.0f);
+ else
+ labelRotation.setY(90.0f);
+ labelRotation.setX(90.0f);
+ }
+ } else {
+ if (xFlipped)
+ labelRotation.setY(-90.0f);
+ else
+ labelRotation.setY(90.0f);
+ if (yFlipped) {
+ if (zFlipped) {
+ if (xFlipped) {
+ labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ }
+ } else {
+ if (xFlipped) {
+ labelRotation.setX(90.0f + fractionCamX
+ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - fractionCamX
+ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ }
+ }
+ } else {
+ if (zFlipped) {
+ if (xFlipped) {
+ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ }
+ } else {
+ if (xFlipped) {
+ labelRotation.setX(-90.0f - fractionCamX
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + fractionCamX
+ * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ }
+ }
+ }
+ }
+ auto totalRotation = Utils::calculateRotation(labelRotation);
+
+ float scale = backgroundScale.x() - m_backgroundScaleMargin.x();;
+ float translate = backgroundScale.x() - m_backgroundScaleMargin.x();
+ float textPadding = 12.0f;
+
+ auto pointSize = m_controller->activeTheme()->font().pointSizeF();
+ auto scaleFactor = m_labelScale.x() * m_labelFontScaleFactor / pointSize
+ + m_labelScale.x() * m_fontScaleFactor;
+ QVector3D fontScaled = QVector3D(scaleFactor, scaleFactor, 0.0f);
+
+ m_itemLabel->setScale(fontScaled);
+
+ float labelsMaxWidth = 0.0f;
+ labelsMaxWidth = qMax(labelsMaxWidth, float(findLabelsMaxWidth(axisX->labels()))) + textPadding;
+ QFontMetrics fm(m_controller->activeTheme()->font());
+ float labelHeight = fm.height() + textPadding;
+
+ auto adjustment = labelsMaxWidth * scaleFactor * .5f;
+ zPos = backgroundScale.z() + adjustment + m_labelMargin;
+ adjustment *= qAbs(qSin(qDegreesToRadians(labelRotation.z())));
+ yPos = backgroundScale.y() + adjustment;
+
+ if (!yFlipped)
+ yPos *= -1.0f;
+
+ if (zFlipped)
+ zPos *= -1.0f;
+
+ auto labelTrans = QVector3D(0.0f, yPos, zPos);
+
+ if (axisX->type() == QAbstract3DAxis::AxisTypeValue) {
+ auto valueAxisX = static_cast<QValue3DAxis *>(axisX);
+ for (int i = 0; i < repeaterX()->count(); i++) {
+ auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(i));
+ labelTrans.setX(valueAxisX->labelPositionAt(i) * scale * 2.0f - translate);
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setRotation(totalRotation);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ }
+ } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) {
+ for (int i = 0; i < repeaterX()->count(); i++) {
+ labelTrans = calculateCategoryLabelPosition(axisX, labelTrans, i);
+ auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(i));
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setRotation(totalRotation);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ }
+ }
+
+ if (titleLabelX()->visible()) {
+ float x = labelTrans.x();
+ labelTrans.setX(0.0f);
+ updateXTitle(labelRotation, labelTrans, totalRotation, labelsMaxWidth, labelHeight, fontScaled);
+ labelTrans.setX(x);
+ }
+
+ auto axisY = m_controller->axisY();
+ labels = axisY->labels();
+ labelAutoAngle = axisY->labelAutoRotation();
+ labelAngleFraction = labelAutoAngle / 90.0f;
+ fractionCamX = m_controller->scene()->activeCamera()->xRotation() * labelAngleFraction;
+ fractionCamY = m_controller->scene()->activeCamera()->yRotation() * labelAngleFraction;
+
+ QVector3D sideLabelRotation(0.0f, -90.0f, 0.0f);
+ QVector3D backLabelRotation(0.0f, 0.0f, 0.0f);
+
+ if (labelAutoAngle == 0.0f) {
+ if (!xFlipped)
+ sideLabelRotation.setY(90.0f);
+ if (zFlipped)
+ backLabelRotation.setY(180.f);
+ } else {
+ // Orient side labels somewhat towards the camera
+ if (xFlipped) {
+ if (zFlipped)
+ backLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
+ else
+ backLabelRotation.setY(-fractionCamX);
+ sideLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
+ } else {
+ if (zFlipped)
+ backLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
+ else
+ backLabelRotation.setY(-fractionCamX);
+ sideLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
+ }
+ }
+
+ backLabelRotation.setX(-fractionCamY);
+ sideLabelRotation.setX(-fractionCamY);
+
+ totalRotation = Utils::calculateRotation(sideLabelRotation);
+ scale = translate = backgroundScale.y() - m_backgroundScaleMargin.y();
+
+ labelsMaxWidth = 0.0f;
+ labelsMaxWidth = qMax(labelsMaxWidth, float(findLabelsMaxWidth(axisY->labels()))) + textPadding;
+
+ adjustment = labelsMaxWidth * scaleFactor * .5f + m_labelMargin;
+ xPos = backgroundScale.x();
+ if (!xFlipped)
+ xPos *= -1.0f;
+ labelTrans.setX(xPos);
+ zPos = backgroundScale.z() + adjustment;
+ if (zFlipped)
+ zPos *= -1.0f;
+ labelTrans.setZ(zPos);
+
+ for (int i = 0; i < repeaterY()->count() / 2; i++) {
+ auto obj = static_cast<QQuick3DNode *>(repeaterY()->objectAt(i));
+ labelTrans.setY(static_cast<QValue3DAxis *>(axisY)->labelPositionAt(i) * scale * 2.0f - translate);
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setRotation(totalRotation);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ }
+
+ auto sideLabelTrans = labelTrans;
+ auto totalSideLabelRotation = totalRotation;
+
+ auto axisZ = m_controller->axisZ();
+ labels = axisZ->labels();
+ labelAutoAngle = axisZ->labelAutoRotation();
+ labelAngleFraction = labelAutoAngle / 90.0f;
+ fractionCamX = m_controller->scene()->activeCamera()->xRotation() * labelAngleFraction;
+ fractionCamY = m_controller->scene()->activeCamera()->yRotation() * labelAngleFraction;
+
+ if (labelAutoAngle == 0.0f) {
+ labelRotation = QVector3D(90.0f, 0.0f, 0.0f);
+ if (zFlipped)
+ labelRotation.setY(180.0f);
+ if (yFlipped) {
+ if (zFlipped)
+ labelRotation.setY(180.0f);
+ else
+ labelRotation.setY(0.0f);
+ labelRotation.setX(90.0f);
+ } else {
+ labelRotation.setX(-90.0f);
+ }
+ } else {
+ if (zFlipped)
+ labelRotation.setY(180.0f);
+ else
+ labelRotation.setY(0.0f);
+ if (yFlipped) {
+ if (zFlipped) {
+ if (xFlipped) {
+ labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ }
+ } else {
+ if (xFlipped) {
+ labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
+ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle + fractionCamY);
+ }
+ }
+ } else {
+ if (zFlipped) {
+ if (xFlipped) {
+ labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ }
+ } else {
+ if (xFlipped) {
+ labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
+ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(labelAutoAngle - fractionCamY);
+ } else {
+ labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
+ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
+ labelRotation.setZ(-labelAutoAngle + fractionCamY);
+ }
+ }
+ }
+ }
+
+ totalRotation = Utils::calculateRotation(labelRotation);
+
+ scale = translate = backgroundScale.z() - m_backgroundScaleMargin.z();
+ labelsMaxWidth = 0.0f;
+ labelsMaxWidth = qMax(labelsMaxWidth, float(findLabelsMaxWidth(axisZ->labels()))) + textPadding ;
+ adjustment = labelsMaxWidth * scaleFactor * .5f;
+
+ xPos = backgroundScale.x() + adjustment + m_labelMargin;
+ if (xFlipped)
+ xPos *= -1.0f;
+
+ adjustment *= qAbs(qSin(qDegreesToRadians(labelRotation.z())));
+ yPos = backgroundScale.y() + adjustment;
+ if (!yFlipped)
+ yPos *= -1.0f;
+
+ labelTrans = QVector3D(xPos, yPos, 0.0f);
+
+ if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) {
+ auto valueAxisZ = static_cast<QValue3DAxis *>(axisZ);
+ for (int i = 0; i < repeaterZ()->count(); i++) {
+ auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(i));
+ labelTrans.setZ(valueAxisZ->labelPositionAt(i) * scale * -2.0f + translate);
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setRotation(totalRotation);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ }
+ } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) {
+ for (int i = 0; i < repeaterZ()->count(); i++) {
+ labelTrans = calculateCategoryLabelPosition(axisZ, labelTrans, i);
+ auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(i));
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setRotation(totalRotation);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ }
+ }
+
+ if (titleLabelZ()->visible()) {
+ float z = labelTrans.z();
+ labelTrans.setZ(0.0f);
+ updateZTitle(labelRotation, labelTrans, totalRotation, labelsMaxWidth, labelHeight, fontScaled);
+ labelTrans.setZ(z);
+ }
+
+ labels = axisY->labels();
+ totalRotation = Utils::calculateRotation(backLabelRotation);
+ scale = translate = backgroundScale.y() - m_backgroundScaleMargin.y();
+ labelsMaxWidth = 0.0f;
+ labelsMaxWidth = qMax(labelsMaxWidth, float(findLabelsMaxWidth(axisY->labels()))) + textPadding;
+ adjustment = labelsMaxWidth * scaleFactor * .5f + m_labelMargin;
+
+ xPos = backgroundScale.x() + adjustment;
+ if (xFlipped)
+ xPos *= -1.0f;
+ labelTrans.setX(xPos);
+
+ zPos = -backgroundScale.z();
+ if (zFlipped)
+ zPos *= -1.0f;
+ labelTrans.setZ(zPos);
+
+ for (int i = 0; i < repeaterY()->count() / 2; i++) {
+ auto obj = static_cast<QQuick3DNode *>(repeaterY()->objectAt(i + (repeaterY()->count() / 2)));
+ labelTrans.setY(static_cast<QValue3DAxis *>(axisY)->labelPositionAt(i) * scale * 2.0f - translate);
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setRotation(totalRotation);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ }
+
+ auto backLabelTrans = labelTrans;
+ auto totalBackLabelRotation = totalRotation;
+ if (titleLabelY()->visible()) {
+ updateYTitle(sideLabelRotation, backLabelRotation,
+ sideLabelTrans, backLabelTrans,
+ totalSideLabelRotation, totalBackLabelRotation, labelsMaxWidth, labelHeight, fontScaled);
+ }
+
+}
+
+void QQuickGraphsItem::positionAndScaleLine(QQuick3DNode *lineNode, QVector3D scale, QVector3D position)
+{
+ lineNode->setScale(scale);
+ lineNode->setPosition(position);
+}
+
+void QQuickGraphsItem::graphPositionAt(const QPoint &point)
+{
+ bool isHitted = false;
+ auto results = pickAll(point.x(), point.y());
+ for (auto &result : results) {
+ if (auto hit = result.objectHit()) {
+ isHitted = true;
+ m_controller->setQueriedGraphPosition(QVector3D(
+ result.scenePosition().x(),
+ result.scenePosition().y(),
+ result.scenePosition().z()
+ ));
+ if (m_backgroundBB != hit) {
+ m_controller->setQueriedGraphPosition(hit->position());
+ break;
+ }
+ }
+ }
+
+ if (!isHitted)
+ m_controller->setQueriedGraphPosition(QVector3D(0,0,0));
+
+ emit queriedGraphPositionChanged(m_controller->queriedGraphPosition());
+ emit m_controller->queriedGraphPositionChanged(m_controller->queriedGraphPosition());
+ m_controller->setGraphPositionQueryPending(false);
+ scene()->setGraphPositionQuery(Q3DScene::invalidSelectionPoint());
+}
+
+void QQuickGraphsItem::updateShadowQuality(ShadowQuality quality)
+{
+ if (quality != QQuickGraphsItem::ShadowQualityNone) {
+ light()->setCastsShadow(true);
+
+ QQuick3DAbstractLight::QSSGShadowMapQuality shadowMapQuality;
+ switch (quality) {
+ case QQuickGraphsItem::ShadowQualityLow:
+ case QQuickGraphsItem::ShadowQualitySoftLow:
+ shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityLow;
+ break;
+ case QQuickGraphsItem::ShadowQualityMedium:
+ case QQuickGraphsItem::ShadowQualitySoftMedium:
+ shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityMedium;
+ break;
+ case QQuickGraphsItem::ShadowQualityHigh:
+ case QQuickGraphsItem::ShadowQualitySoftHigh:
+ shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityHigh;
+ break;
+ default:
+ shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityMedium;
+ break;
+ }
+ light()->setShadowMapQuality(shadowMapQuality);
+ } else {
+ light()->setCastsShadow(false);
+ }
+}
+
+void QQuickGraphsItem::updateAxisRange(float min, float max)
+{
+ Q_UNUSED(min);
+ Q_UNUSED(max);
+}
+
+void QQuickGraphsItem::updateAxisReversed(bool enable)
+{
+ Q_UNUSED(enable);
+}
+
+int QQuickGraphsItem::findLabelsMaxWidth(const QStringList &labels)
+{
+ int labelWidth = 0;
+ QFontMetrics labelFM(m_controller->activeTheme()->font());
+
+ for (const auto &label : std::as_const(labels)) {
+ auto width = labelFM.horizontalAdvance(label);
+ if (labelWidth < width)
+ labelWidth = width;
+ }
+ return labelWidth;
+}
+
+QVector3D QQuickGraphsItem::calculateCategoryLabelPosition(QAbstract3DAxis *axis, QVector3D labelPosition, int index)
+{
+ Q_UNUSED(axis);
+ Q_UNUSED(index);
+ return labelPosition;
+}
+
+float QQuickGraphsItem::calculateCategoryGridLinePosition(QAbstract3DAxis *axis, int index)
+{
+ Q_UNUSED(axis);
+ Q_UNUSED(index);
+ return 0.0f;
+}
+
+void QQuickGraphsItem::updateXTitle(const QVector3D &labelRotation, const QVector3D &labelTrans,
+ const QQuaternion &totalRotation, float labelsMaxWidth,
+ float labelHeight, const QVector3D &scale)
+{
+ float scaledFontSize = (0.05 + m_controller->activeTheme()->font().pointSizeF()) / 500.0f;
+ float scaleFactor = scaledFontSize / 115.0f;
+ float titleOffset;
+
+ bool radial = false;
+ if (radial)
+ titleOffset = -2.0f * (m_labelMargin + scaledFontSize);
+ else
+ titleOffset = 2.0f * (m_labelMargin + (labelsMaxWidth * scaleFactor));
+
+ float zRotation = 0.0f;
+ float yRotation = 0.0f;
+ float xRotation = -90.0f + labelRotation.z();
+ float offsetRotation = labelRotation.z();
+ float extraRotation = -90.0f;
+ if (m_yFlipped) {
+ zRotation = 180.0f;
+ if (m_zFlipped) {
+ titleOffset = -titleOffset;
+ if (m_xFlipped) {
+ offsetRotation = -offsetRotation;
+ extraRotation = -extraRotation;
+ } else {
+ xRotation = -90.0f - labelRotation.z();
+ }
+ } else {
+ yRotation = 180.0f;
+ if (m_xFlipped) {
+ offsetRotation = -offsetRotation;
+ xRotation = -90.0f - labelRotation.z();
+ } else {
+ extraRotation = -extraRotation;
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ titleOffset = -titleOffset;
+ if (m_xFlipped) {
+ yRotation = 180.0f;
+ offsetRotation = -offsetRotation;
+ } else {
+ yRotation = 180.0f;
+ xRotation = -90.0f - labelRotation.z();
+ extraRotation = -extraRotation;
+ }
+ if (m_yFlipped) {
+ extraRotation = -extraRotation;
+ if (m_xFlipped)
+ xRotation = 90.0f + labelRotation.z();
+ else
+ xRotation = 90.0f - labelRotation.z();
+ }
+ } else {
+ if (m_xFlipped) {
+ offsetRotation = -offsetRotation;
+ xRotation = -90.0f - labelRotation.z();
+ extraRotation = -extraRotation;
+ }
+ if (m_yFlipped) {
+ xRotation = 90.0f + labelRotation.z();
+ extraRotation = -extraRotation;
+ if (m_xFlipped)
+ xRotation = 90.0f - labelRotation.z();
+ }
+ }
+ }
+
+ if (offsetRotation == 180.0f || offsetRotation == -180.0f)
+ offsetRotation = 0.0f;
+
+ QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, offsetRotation);
+ QVector3D titleOffsetVector =
+ offsetRotator.rotatedVector(QVector3D(0.0f, 0.0f, titleOffset));
+
+ QQuaternion titleRotation;
+ if (m_controller->axisX()->isTitleFixed()) {
+ titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, zRotation)
+ * QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
+ * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xRotation);
+ } else {
+ titleRotation = totalRotation
+ * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, extraRotation);
+ }
+
+ m_titleLabelX->setScale(scale);
+ m_titleLabelX->setPosition(labelTrans + titleOffsetVector);
+ m_titleLabelX->setRotation(titleRotation);
+ m_titleLabelX->setProperty("labelWidth", labelsMaxWidth);
+ m_titleLabelX->setProperty("LabelHeight", labelHeight);
+}
+
+void QQuickGraphsItem::updateYTitle(const QVector3D &sideLabelRotation,
+ const QVector3D &backLabelRotation,
+ const QVector3D &sideLabelTrans,
+ const QVector3D &backLabelTrans,
+ const QQuaternion &totalSideRotation,
+ const QQuaternion &totalBackRotation,
+ float labelsMaxWidth,
+ float labelHeight,
+ const QVector3D &scale)
+{
+ float scaledFontSize = (0.05 + m_controller->activeTheme()->font().pointSizeF()) / 500.0f;
+ float scaleFactor = scaledFontSize / 115.0f;
+ float titleOffset = 2.0f * (m_labelMargin + (labelsMaxWidth * scaleFactor));
+
+ QQuaternion zRightAngleRotation = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f);
+ float yRotation;
+ QVector3D titleTrans;
+ QQuaternion totalRotation;
+ if (m_xFlipped == m_zFlipped) {
+ yRotation = backLabelRotation.y();
+ titleTrans = backLabelTrans;
+ totalRotation = totalBackRotation;
+ } else {
+ yRotation = sideLabelRotation.y();
+ titleTrans = sideLabelTrans;
+ totalRotation = totalSideRotation;
+ }
+
+ QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation);
+ QVector3D titleOffsetVector =
+ offsetRotator.rotatedVector(QVector3D(-titleOffset, 0.0f, 0.0f));
+
+ QQuaternion titleRotation;
+ if (m_controller->axisY()->isTitleFixed()) {
+ titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
+ * zRightAngleRotation;
+ } else {
+ titleRotation = totalRotation * zRightAngleRotation;
+ }
+
+ m_titleLabelY->setScale(scale);
+ m_titleLabelY->setPosition(titleTrans + titleOffsetVector);
+ m_titleLabelY->setRotation(titleRotation);
+ m_titleLabelY->setProperty("labelWidth", labelsMaxWidth);
+ m_titleLabelY->setProperty("LabelHeight", labelHeight);
+}
+
+void QQuickGraphsItem::updateZTitle(const QVector3D &labelRotation, const QVector3D &labelTrans,
+ const QQuaternion &totalRotation, float labelsMaxWidth,
+ float labelHeight, const QVector3D &scale)
+{
+ float scaledFontSize = (0.05 + m_controller->activeTheme()->font().pointSizeF()) / 500.0f;
+ float scaleFactor = scaledFontSize / 115.0f;
+ float titleOffset = 2.0f * (m_labelMargin + (labelsMaxWidth * scaleFactor));
+ float zRotation = labelRotation.z();
+ float yRotation = -90.0f;
+ float xRotation = -90.0f;
+ float extraRotation = 90.0f;
+
+ if (m_yFlipped) {
+
+ xRotation = -xRotation;
+ if (m_zFlipped) {
+ if (m_xFlipped) {
+ titleOffset = -titleOffset;
+ zRotation = -zRotation;
+ extraRotation = -extraRotation;
+ } else {
+ zRotation = -zRotation;
+ yRotation = -yRotation;
+ }
+ } else {
+ if (m_xFlipped) {
+ titleOffset = -titleOffset;
+ } else {
+ extraRotation = -extraRotation;
+ yRotation = -yRotation;
+ }
+ }
+ } else {
+ if (m_zFlipped) {
+ zRotation = -zRotation;
+ if (m_xFlipped) {
+ titleOffset = -titleOffset;
+ } else {
+ extraRotation = -extraRotation;
+ yRotation = -yRotation;
+ }
+ } else {
+ if (m_xFlipped) {
+ titleOffset = -titleOffset;
+ extraRotation = -extraRotation;
+ } else {
+ yRotation = -yRotation;
+ }
+ }
+ if (m_yFlipped) {
+ xRotation = -xRotation;
+ extraRotation = -extraRotation;
+ }
+ }
+
+ float offsetRotation = zRotation;
+ if (offsetRotation == 180.0f || offsetRotation == -180.0f)
+ offsetRotation = 0.0f;
+
+ QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, offsetRotation);
+ QVector3D titleOffsetVector =
+ offsetRotator.rotatedVector(QVector3D(titleOffset, 0.0f, 0.0f));
+
+ QQuaternion titleRotation;
+ if (m_controller->axisZ()->isTitleFixed()) {
+ titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, zRotation)
+ * QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
+ * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xRotation);
+ } else {
+ titleRotation = totalRotation
+ * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, extraRotation);
+ }
+
+ m_titleLabelZ->setScale(scale);
+ m_titleLabelZ->setPosition(labelTrans + titleOffsetVector);
+ m_titleLabelZ->setRotation(titleRotation);
+ m_titleLabelZ->setProperty("labelWidth", labelsMaxWidth);
+ m_titleLabelZ->setProperty("LabelHeight", labelHeight);
+}
+
+void QQuickGraphsItem::updateCamera()
+{
+ float zoom = 720.f / m_controller->scene()->activeCamera()->zoomLevel();
+ camera()->setZ(zoom);
+ cameraTarget()->setPosition(m_controller->scene()->activeCamera()->target());
+ auto rotation = QVector3D(
+ -m_controller->scene()->activeCamera()->yRotation(),
+ -m_controller->scene()->activeCamera()->xRotation(),
+ 0);
+ cameraTarget()->setEulerRotation(rotation);
+
+ if (m_itemLabel->visible())
+ m_itemLabel->setEulerRotation(rotation);
+}
+
+int QQuickGraphsItem::msaaSamples() const
+{
+ if (m_renderMode == RenderIndirect)
+ return m_samples;
+ else
+ return m_windowSamples;
+}
+
+void QQuickGraphsItem::setMsaaSamples(int samples)
+{
+ if (m_renderMode != RenderIndirect) {
+ qWarning("Multisampling cannot be adjusted in this render mode");
+ } else {
+ if (m_controller->isOpenGLES()) {
+ if (samples > 0)
+ qWarning("Multisampling is not supported in OpenGL ES2");
+ } else if (m_samples != samples) {
+ m_samples = samples;
+ setAntialiasing(m_samples > 0);
+ auto sceneEnv = environment();
+ sceneEnv->setAntialiasingMode(m_samples > 0
+ ? QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues::MSAA
+ : QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues::NoAA);
+ switch (m_samples) {
+ case 0:
+ // no-op
+ break;
+ case 2:
+ sceneEnv->setAntialiasingQuality(
+ QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::Medium);
+ break;
+ case 4:
+ sceneEnv->setAntialiasingQuality(
+ QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::High);
+ break;
+ case 8:
+ sceneEnv->setAntialiasingQuality(
+ QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::VeryHigh);
+ break;
+ default:
+ qWarning("Invalid multisampling sample number, using 4x instead");
+ sceneEnv->setAntialiasingQuality(
+ QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::High);
+ m_samples = 4;
+ break;
+ }
+ emit msaaSamplesChanged(m_samples);
+ update();
+ }
+ }
+}
+
+Declarative3DScene *QQuickGraphsItem::scene() const
+{
+ return static_cast<Declarative3DScene *>(m_controller->scene());
+}
+
+void QQuickGraphsItem::handleWindowChanged(/*QQuickWindow *window*/)
+{
+ auto window = QQuick3DObjectPrivate::get(rootNode())->sceneManager->window();
+ checkWindowList(window);
+ if (!window)
+ return;
+
+#if defined(Q_OS_MACOS)
+ bool previousVisibility = window->isVisible();
+ // Enable touch events for Mac touchpads
+ window->setVisible(true);
+ typedef void * (*EnableTouch)(QWindow*, bool);
+ EnableTouch enableTouch =
+ (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow");
+ if (enableTouch)
+ enableTouch(window, true);
+ window->setVisible(previousVisibility);
+#endif
+
+ connect(window, &QObject::destroyed, this, &QQuickGraphsItem::windowDestroyed);
+
+ int oldWindowSamples = m_windowSamples;
+ m_windowSamples = window->format().samples();
+ if (m_windowSamples < 0)
+ m_windowSamples = 0;
+
+ connect(window, &QQuickWindow::beforeSynchronizing,
+ this, &QQuickGraphsItem::synchData);
+
+ if (m_renderMode == RenderDirectToBackground_NoClear
+ || m_renderMode == RenderDirectToBackground) {
+ setAntialiasing(m_windowSamples > 0);
+ if (m_windowSamples != oldWindowSamples)
+ emit msaaSamplesChanged(m_windowSamples);
+ }
+
+ connect(m_controller.data(), &Abstract3DController::needRender, window, &QQuickWindow::update);
+ updateWindowParameters();
+
+ if (sliceView()) {
+ float pixelRatio = window->devicePixelRatio();
+ float magnification = 100.0f * pixelRatio + 50.0f;
+ QQuick3DOrthographicCamera *camera = static_cast<QQuick3DOrthographicCamera *>(sliceView()->camera());
+ camera->setHorizontalMagnification(magnification);
+ camera->setVerticalMagnification(magnification);
+ }
+
+#if defined(Q_OS_IOS)
+ // Scenegraph render cycle in iOS sometimes misses update after beforeSynchronizing signal.
+ // This ensures we don't end up displaying the graph without any data, in case update is
+ // skipped after synchDataToRenderer.
+ QTimer::singleShot(0, window, SLOT(update()));
+#endif
+}
+
+void QQuickGraphsItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+
+ m_cachedGeometry = newGeometry;
+
+ updateWindowParameters();
+}
+
+void QQuickGraphsItem::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ QQuick3DViewport::itemChange(change, value);
+ updateWindowParameters();
+}
+
+void QQuickGraphsItem::updateWindowParameters()
+{
+ const QMutexLocker locker(&m_mutex);
+
+ // Update the device pixel ratio, window size and bounding box
+ QQuickWindow *win = window();
+ if (win && !m_controller.isNull()) {
+ Q3DScene *scene = m_controller->scene();
+ if (win->devicePixelRatio() != scene->devicePixelRatio()) {
+ scene->setDevicePixelRatio(win->devicePixelRatio());
+ win->update();
+ }
+
+ bool directRender = m_renderMode == RenderDirectToBackground
+ || m_renderMode == RenderDirectToBackground_NoClear;
+ QSize windowSize;
+
+ if (directRender)
+ windowSize = win->size();
+ else
+ windowSize = m_cachedGeometry.size().toSize();
+
+ if (windowSize != scene->d_ptr->windowSize()) {
+ scene->d_ptr->setWindowSize(windowSize);
+ win->update();
+ }
+
+ if (directRender) {
+ // Origin mapping is needed when rendering directly to background
+ QPointF point = QQuickItem::mapToScene(QPointF(0.0, 0.0));
+ scene->d_ptr->setViewport(QRect(point.x() + 0.5f, point.y() + 0.5f,
+ m_cachedGeometry.width() + 0.5f,
+ m_cachedGeometry.height() + 0.5f));
+ } else {
+ // No translation needed when rendering to FBO
+ scene->d_ptr->setViewport(QRect(0.0, 0.0, m_cachedGeometry.width() + 0.5f,
+ m_cachedGeometry.height() + 0.5f));
+ }
+ }
+}
+
+void QQuickGraphsItem::handleSelectionModeChange(QAbstract3DGraph::SelectionFlags mode)
+{
+ int intmode = int(mode);
+ emit selectionModeChanged(SelectionFlags(intmode));
+}
+
+void QQuickGraphsItem::handleShadowQualityChange(QAbstract3DGraph::ShadowQuality quality)
+{
+ emit shadowQualityChanged(ShadowQuality(quality));
+}
+
+void QQuickGraphsItem::handleSelectedElementChange(QAbstract3DGraph::ElementType type)
+{
+ emit selectedElementChanged(ElementType(type));
+}
+
+void QQuickGraphsItem::handleOptimizationHintChange(QAbstract3DGraph::OptimizationHints hints)
+{
+ int intHints = int(hints);
+ emit optimizationHintsChanged(OptimizationHints(intHints));
+}
+
+QAbstract3DInputHandler *QQuickGraphsItem::inputHandler() const
+{
+ return m_controller->activeInputHandler();
+}
+
+void QQuickGraphsItem::setInputHandler(QAbstract3DInputHandler *inputHandler)
+{
+ m_controller->setActiveInputHandler(inputHandler);
+}
+
+void QQuickGraphsItem::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ m_controller->mouseDoubleClickEvent(event);
+}
+
+void QQuickGraphsItem::touchEvent(QTouchEvent *event)
+{
+ m_controller->touchEvent(event);
+ window()->update();
+}
+
+void QQuickGraphsItem::mousePressEvent(QMouseEvent *event)
+{
+ QPoint mousePos = event->pos();
+ handleMousePressedEvent(event);
+ m_controller->mousePressEvent(event, mousePos);
+}
+
+void QQuickGraphsItem::mouseReleaseEvent(QMouseEvent *event)
+{
+ QPoint mousePos = event->pos();
+ m_controller->mouseReleaseEvent(event, mousePos);
+}
+
+void QQuickGraphsItem::mouseMoveEvent(QMouseEvent *event)
+{
+ QPoint mousePos = event->pos();
+ m_controller->mouseMoveEvent(event, mousePos);
+}
+
+#if QT_CONFIG(wheelevent)
+void QQuickGraphsItem::wheelEvent(QWheelEvent *event)
+{
+ m_controller->wheelEvent(event);
+}
+#endif
+
+void QQuickGraphsItem::checkWindowList(QQuickWindow *window)
+{
+ QQuickWindow *oldWindow = m_graphWindowList.value(this);
+ m_graphWindowList[this] = window;
+
+ if (oldWindow != window && oldWindow) {
+ QObject::disconnect(oldWindow, &QObject::destroyed, this,
+ &QQuickGraphsItem::windowDestroyed);
+ QObject::disconnect(oldWindow, &QQuickWindow::beforeSynchronizing, this,
+ &QQuickGraphsItem::synchData);
+ if (!m_controller.isNull()) {
+ QObject::disconnect(m_controller.data(), &Abstract3DController::needRender,
+ oldWindow, &QQuickWindow::update);
+ }
+ }
+
+ QList<QQuickWindow *> windowList;
+
+ foreach (QQuickGraphsItem *graph, m_graphWindowList.keys()) {
+ if (graph->m_renderMode == RenderDirectToBackground
+ || graph->m_renderMode == RenderDirectToBackground_NoClear) {
+ windowList.append(m_graphWindowList.value(graph));
+ }
+ }
+
+ if (!window) {
+ m_graphWindowList.remove(this);
+ return;
+ }
+}
+
+void QQuickGraphsItem::setMeasureFps(bool enable)
+{
+ m_controller->setMeasureFps(enable);
+ if (enable)
+ QObject::connect(renderStats(), &QQuick3DRenderStats::fpsChanged, this, &QQuickGraphsItem::handleFpsChanged);
+ else
+ QObject::disconnect(renderStats(), 0, this, 0);
+
+}
+
+bool QQuickGraphsItem::measureFps() const
+{
+ return m_controller->measureFps();
+}
+
+int QQuickGraphsItem::currentFps() const
+{
+ return m_controller->currentFps();
+}
+
+void QQuickGraphsItem::setOrthoProjection(bool enable)
+{
+ m_controller->setOrthoProjection(enable);
+}
+
+bool QQuickGraphsItem::isOrthoProjection() const
+{
+ return m_controller->isOrthoProjection();
+}
+
+QQuickGraphsItem::ElementType QQuickGraphsItem::selectedElement() const
+{
+ return ElementType(m_controller->selectedElement());
+}
+
+void QQuickGraphsItem::setAspectRatio(qreal ratio)
+{
+ m_controller->setAspectRatio(ratio);
+}
+
+qreal QQuickGraphsItem::aspectRatio() const
+{
+ return m_controller->aspectRatio();
+}
+
+void QQuickGraphsItem::setOptimizationHints(OptimizationHints hints)
+{
+ int intmode = int(hints);
+ m_controller->setOptimizationHints(QAbstract3DGraph::OptimizationHints(intmode));
+}
+
+QQuickGraphsItem::OptimizationHints QQuickGraphsItem::optimizationHints() const
+{
+ int intmode = int(m_controller->optimizationHints());
+ return OptimizationHints(intmode);
+}
+
+void QQuickGraphsItem::setPolar(bool enable)
+{
+ m_controller->setPolar(enable);
+}
+
+bool QQuickGraphsItem::isPolar() const
+{
+ return m_controller->isPolar();
+}
+
+void QQuickGraphsItem::setRadialLabelOffset(float offset)
+{
+ m_controller->setRadialLabelOffset(offset);
+}
+
+float QQuickGraphsItem::radialLabelOffset() const
+{
+ return m_controller->radialLabelOffset();
+}
+
+void QQuickGraphsItem::setHorizontalAspectRatio(qreal ratio)
+{
+ m_controller->setHorizontalAspectRatio(ratio);
+}
+
+qreal QQuickGraphsItem::horizontalAspectRatio() const
+{
+ return m_controller->horizontalAspectRatio();
+}
+
+void QQuickGraphsItem::setReflection(bool enable)
+{
+ m_controller->setReflection(enable);
+}
+
+bool QQuickGraphsItem::isReflection() const
+{
+ return m_controller->reflection();
+}
+
+void QQuickGraphsItem::setReflectivity(qreal reflectivity)
+{
+ m_controller->setReflectivity(reflectivity);
+}
+
+qreal QQuickGraphsItem::reflectivity() const
+{
+ return m_controller->reflectivity();
+}
+
+void QQuickGraphsItem::setLocale(const QLocale &locale)
+{
+ m_controller->setLocale(locale);
+}
+
+QLocale QQuickGraphsItem::locale() const
+{
+ return m_controller->locale();
+}
+
+QVector3D QQuickGraphsItem::queriedGraphPosition() const
+{
+ return m_controller->queriedGraphPosition();
+}
+
+void QQuickGraphsItem::setMargin(qreal margin)
+{
+ m_controller->setMargin(margin);
+}
+
+qreal QQuickGraphsItem::margin() const
+{
+ return m_controller->margin();
+}
+
+QQuick3DNode *QQuickGraphsItem::rootNode() const
+{
+ return QQuick3DViewport::scene();
+}
+
+void QQuickGraphsItem::changeLabelBackgroundColor(QQuick3DRepeater *repeater, const QColor &color)
+{
+ int count = repeater->count();
+ for (int i = 0; i < count; i++) {
+ auto label = static_cast<QQuick3DNode *>(repeater->objectAt(i));
+ label->setProperty("backgroundColor", color);
+ }
+}
+
+void QQuickGraphsItem::changeLabelBackgroundEnabled(QQuick3DRepeater *repeater, const bool &enabled)
+{
+ int count = repeater->count();
+ for (int i = 0; i < count; i++) {
+ auto label = static_cast<QQuick3DNode *>(repeater->objectAt(i));
+ label->setProperty("backgroundEnabled", enabled);
+ }
+}
+
+void QQuickGraphsItem::changeLabelBorderEnabled(QQuick3DRepeater *repeater, const bool &enabled)
+{
+ int count = repeater->count();
+ for (int i = 0; i < count; i++) {
+ auto label = static_cast<QQuick3DNode *>(repeater->objectAt(i));
+ label->setProperty("borderEnabled", enabled);
+ }
+}
+
+void QQuickGraphsItem::changeLabelTextColor(QQuick3DRepeater *repeater, const QColor &color)
+{
+ int count = repeater->count();
+ for (int i = 0; i < count; i++) {
+ auto label = static_cast<QQuick3DNode *>(repeater->objectAt(i));
+ label->setProperty("labelTextColor", color);
+ }
+}
+
+void QQuickGraphsItem::changeLabelFont(QQuick3DRepeater *repeater, const QFont &font)
+{
+ int count = repeater->count();
+ for (int i = 0; i < count; i++) {
+ auto label = static_cast<QQuick3DNode *>(repeater->objectAt(i));
+ label->setProperty("labelFont", font);
+ }
+}
+
+void QQuickGraphsItem::changeGridLineColor(QQuick3DRepeater *repeater, const QColor &color)
+{
+ for (int i = 0; i < repeater->count(); i++) {
+ auto lineNode = static_cast<QQuick3DNode *>(repeater->objectAt(i));
+ lineNode->setProperty("lineColor", color);
+ }
+}
+
+void QQuickGraphsItem::updateTitleLabels()
+{
+ if (m_controller->m_changeTracker.axisXTitleVisibilityChanged) {
+ m_titleLabelX->setVisible(m_controller->axisX()->isTitleVisible());
+ m_controller->m_changeTracker.axisXTitleVisibilityChanged = false;
+ }
+
+ if (m_controller->m_changeTracker.axisYTitleVisibilityChanged) {
+ m_titleLabelY->setVisible(m_controller->axisY()->isTitleVisible());
+ m_controller->m_changeTracker.axisYTitleVisibilityChanged = false;
+ }
+
+ if (m_controller->m_changeTracker.axisZTitleVisibilityChanged) {
+ m_titleLabelZ->setVisible(m_controller->axisZ()->isTitleVisible());
+ m_controller->m_changeTracker.axisZTitleVisibilityChanged = false;
+ }
+
+ if (m_controller->m_changeTracker.axisXTitleChanged) {
+ m_titleLabelX->setProperty("labelText", m_controller->axisX()->title());
+ m_controller->m_changeTracker.axisXTitleChanged = false;
+ }
+
+ if (m_controller->m_changeTracker.axisYTitleChanged) {
+ m_titleLabelY->setProperty("labelText", m_controller->axisY()->title());
+ m_controller->m_changeTracker.axisYTitleChanged = false;
+ }
+
+ if (m_controller->m_changeTracker.axisZTitleChanged) {
+ m_titleLabelZ->setProperty("labelText", m_controller->axisZ()->title());
+ m_controller->m_changeTracker.axisZTitleChanged = false;
+ }
+}
+
+void QQuickGraphsItem::updateSliceGraph()
+{
+ if (!m_sliceEnabled) {
+ m_controller->setSlicingActive(false);
+ setWidth(width() * 5.f);
+ setHeight(height() * 5.f);
+ m_sliceView->setVisible(false);
+ } else {
+ float pixelRatio = QQuick3DObjectPrivate::get(rootNode())->sceneManager->window()->devicePixelRatio();
+ float magnification = 100.0f * pixelRatio + 50.0f;
+ QQuick3DOrthographicCamera *camera = static_cast<QQuick3DOrthographicCamera *>(sliceView()->camera());
+ camera->setHorizontalMagnification(magnification);
+ camera->setVerticalMagnification(magnification);
+ QQuickItem *anchor = QQuickItemPrivate::get(this)->anchors()->fill();
+ if (anchor)
+ QQuickItemPrivate::get(this)->anchors()->resetFill();
+ setWidth(width() * .2f);
+ setHeight(height() * .2f);
+ m_sliceView->setVisible(true);
+ updateSliceGrid();
+ updateSliceLabels();
+ }
+}
+
+void QQuickGraphsItem::windowDestroyed(QObject *obj)
+{
+ // Remove destroyed window from window lists
+ QQuickWindow *win = static_cast<QQuickWindow *>(obj);
+ QQuickWindow *oldWindow = m_graphWindowList.value(this);
+
+ if (win == oldWindow)
+ m_graphWindowList.remove(this);
+}
+
+QQmlComponent *QQuickGraphsItem::createRepeaterDelegateComponent(const QString &fileName)
+{
+ QQmlComponent component(qmlEngine(this), fileName);
+ return qobject_cast<QQmlComponent *>(component.create());
+}
+
+QQuick3DRepeater *QQuickGraphsItem::createRepeater()
+{
+ auto engine = qmlEngine(this);
+ QQmlComponent repeaterComponent(engine);
+ repeaterComponent.setData("import QtQuick3D; Repeater3D{}",QUrl());
+ auto repeater = qobject_cast<QQuick3DRepeater *>(repeaterComponent.create());
+ repeater->setParent(rootNode());
+ repeater->setParentItem(rootNode());
+ return repeater;
+}
+
+QQuick3DNode *QQuickGraphsItem::createTitleLabel()
+{
+ QQmlComponent comp(qmlEngine(this), QStringLiteral(":/axis/ItemLabel"));
+ auto titleLabel = qobject_cast<QQuick3DNode *>(comp.create());
+ titleLabel->setParent(rootNode());
+ titleLabel->setParentItem(rootNode());
+ titleLabel->setVisible(false);
+ titleLabel->setScale(m_labelScale);
+ return titleLabel;
+}
+
+QQuick3DCustomMaterial *QQuickGraphsItem::createQmlCustomMaterial(const QString &fileName)
+{
+ QQmlComponent component(qmlEngine(this), fileName);
+ QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(component.create());
+ return material;
+}
+
+QQuick3DPrincipledMaterial *QQuickGraphsItem::createPrincipledMaterial()
+{
+ QQmlComponent component(qmlEngine(this));
+ component.setData("import QtQuick3D; PrincipledMaterial{}", QUrl());
+ return qobject_cast<QQuick3DPrincipledMaterial *>(component.create());
+}
+
+bool QQuickGraphsItem::event(QEvent *event)
+{
+ return QQuickItem::event(event);
+}
+
+void QQuickGraphsItem::createSliceView()
+{
+ if (m_sliceView)
+ return;
+
+ m_sliceView = new QQuick3DViewport();
+ m_sliceView->setParent(parent());
+ m_sliceView->setParentItem(parentItem());
+ m_sliceView->setVisible(false);
+ m_sliceView->setWidth(width());
+ m_sliceView->setHeight(height());
+
+ auto scene = m_sliceView->scene();
+
+ auto camera = new QQuick3DOrthographicCamera(scene);
+ camera->setPosition(QVector3D(.0f, .0f, 20.0f));
+ m_sliceView->setCamera(camera);
+
+ auto light = new QQuick3DDirectionalLight(scene);
+ light->setParent(camera);
+ light->setParentItem(camera);
+
+ m_sliceHorizontalGridRepeater = createRepeater();
+ m_sliceHorizontalGridRepeater->setParent(scene);
+ m_sliceHorizontalGridRepeater->setParentItem(scene);
+ auto gridDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine"));
+ m_sliceHorizontalGridRepeater->setDelegate(gridDelegate);
+
+ m_sliceVerticalGridRepeater = createRepeater();
+ m_sliceVerticalGridRepeater->setParent(scene);
+ m_sliceVerticalGridRepeater->setParentItem(scene);
+ m_sliceVerticalGridRepeater->setDelegate(gridDelegate);
+
+ m_sliceHorizontalLabelRepeater = createRepeater();
+ m_sliceHorizontalLabelRepeater->setParent(scene);
+ m_sliceHorizontalLabelRepeater->setParentItem(scene);
+ auto labelDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel"));
+ m_sliceHorizontalLabelRepeater->setDelegate(labelDelegate);
+
+ m_sliceVerticalLabelRepeater = createRepeater();
+ m_sliceVerticalLabelRepeater->setParent(scene);
+ m_sliceVerticalLabelRepeater->setParentItem(scene);
+ m_sliceVerticalLabelRepeater->setDelegate(labelDelegate);
+
+ m_sliceHorizontalTitleLabel = createTitleLabel();
+ m_sliceHorizontalTitleLabel->setParent(scene);
+ m_sliceHorizontalTitleLabel->setParentItem(scene);
+ m_sliceHorizontalTitleLabel->setVisible(true);
+
+ m_sliceVerticalTitleLabel = createTitleLabel();
+ m_sliceVerticalTitleLabel->setParent(scene);
+ m_sliceVerticalTitleLabel->setParentItem(scene);
+ m_sliceVerticalTitleLabel->setVisible(true);
+
+ m_sliceItemLabel = createTitleLabel();
+ m_sliceItemLabel->setParent(scene);
+ m_sliceItemLabel->setParentItem(scene);
+ m_sliceItemLabel->setVisible(false);
+}
+
+void QQuickGraphsItem::updateSliceGrid()
+{
+ QAbstract3DAxis *horizontalAxis = nullptr;
+ QAbstract3DAxis *verticalAxis = m_controller->axisY();
+ auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin;
+ float scale;
+ float translate;
+
+ QVector3D horizontalScale = QVector3D(.0f, .0f, .0f);
+ QVector3D verticalScale = QVector3D(lineWidthScaleFactor(),
+ backgroundScale.y() * lineLengthScaleFactor(),
+ lineWidthScaleFactor());
+ auto selectionMode = m_controller->selectionMode();
+ if (selectionMode.testFlag(QAbstract3DGraph::SelectionRow)) {
+ horizontalAxis = m_controller->axisX();
+ horizontalScale = QVector3D(backgroundScale.x() * lineLengthScaleFactor(),
+ lineWidthScaleFactor(),
+ lineWidthScaleFactor());
+ scale = m_scaleWithBackground.x();
+ translate = m_scaleWithBackground.x();
+ } else if (selectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
+ horizontalAxis = m_controller->axisZ();
+ horizontalScale = QVector3D(backgroundScale.z() * lineLengthScaleFactor(),
+ lineWidthScaleFactor(),
+ lineWidthScaleFactor());
+ scale = m_scaleWithBackground.z();
+ translate = m_scaleWithBackground.z();
+ }
+
+ if (horizontalAxis == nullptr) {
+ qWarning("Invalid axis type");
+ return;
+ }
+
+ if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(horizontalAxis);
+ m_sliceVerticalGridRepeater->setModel(valueAxis->gridSize()
+ + valueAxis->subGridSize());
+ } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) {
+ m_sliceVerticalGridRepeater->setModel(horizontalAxis->labels().size());
+ }
+
+ if (verticalAxis->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(verticalAxis);
+ m_sliceHorizontalGridRepeater->setModel(valueAxis->gridSize()
+ + valueAxis->subGridSize());
+ } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) {
+ m_sliceHorizontalGridRepeater->setModel(verticalAxis->labels().size());
+ }
+
+ float linePosX = .0f;
+ float linePosY = .0f;
+ float linePosZ = .0f;
+
+ if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeCategory) {
+ m_sliceVerticalGridRepeater->setVisible(false);
+ } else if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeValue) {
+ for (int i = 0; i < m_sliceVerticalGridRepeater->count(); i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(m_sliceVerticalGridRepeater->objectAt(i));
+ auto axis = static_cast<QValue3DAxis *>(horizontalAxis);
+ if (i % 2 == 0)
+ linePosX = axis->gridPositionAt(i / 2) * scale * 2.0f - translate;
+ else
+ linePosX = axis->subGridPositionAt(i / 2) * scale * 2.0f - translate;
+ lineNode->setProperty("lineColor", QColor(0, 0, 0));
+ positionAndScaleLine(lineNode, verticalScale, QVector3D(linePosX, linePosY, linePosZ));
+ }
+ }
+
+ linePosX = 0;
+ scale = m_scaleWithBackground.y();
+ translate = m_scaleWithBackground.y();
+
+ for (int i = 0; i < m_sliceHorizontalGridRepeater->count(); i++) {
+ QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(m_sliceHorizontalGridRepeater->objectAt(i));
+ if (verticalAxis->type() == QAbstract3DAxis::AxisTypeValue) {
+ auto axis = static_cast<QValue3DAxis *>(verticalAxis);
+ if (axis->subGridSize() > 0) {
+ if (i % 2 == 0)
+ linePosY = axis->gridPositionAt(i / 2) * scale * 2.0f - translate;
+ else
+ linePosY = axis->subGridPositionAt(i / 2) * scale * 2.0f - translate;
+ } else {
+ linePosY = axis->gridPositionAt(i) * scale * 2.0f - translate;
+ }
+ } else if (verticalAxis->type() == QAbstract3DAxis::AxisTypeCategory) {
+ linePosY = calculateCategoryGridLinePosition(verticalAxis, i);
+ }
+ lineNode->setProperty("lineColor", QColor(0, 0, 0));
+ positionAndScaleLine(lineNode, horizontalScale, QVector3D(linePosX, linePosY, linePosZ));
+ }
+}
+
+void QQuickGraphsItem::updateSliceLabels()
+{
+ QAbstract3DAxis *horizontalAxis = nullptr;
+ QAbstract3DAxis *verticalAxis = m_controller->axisY();
+ auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin;
+ float scale;
+ float translate;
+ float scaleFactor;
+ auto pointSize = m_controller->activeTheme()->font().pointSizeF();
+ auto selectionMode = m_controller->selectionMode();
+
+ if (selectionMode.testFlag(QAbstract3DGraph::SelectionRow))
+ horizontalAxis = m_controller->axisX();
+ else if (selectionMode.testFlag(QAbstract3DGraph::SelectionColumn))
+ horizontalAxis = m_controller->axisZ();
+
+ scale = backgroundScale.x() - m_backgroundScaleMargin.x();
+ translate = backgroundScale.x() - m_backgroundScaleMargin.x();
+ scaleFactor = m_labelScale.x() * m_labelFontScaleFactor / pointSize
+ + m_labelScale.x() * m_fontScaleFactor;
+
+ if (horizontalAxis == nullptr) {
+ qWarning("Invalid selection mode");
+ return;
+ }
+
+ if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(horizontalAxis);
+ m_sliceHorizontalLabelRepeater->setModel(valueAxis->labels().size());
+ } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) {
+ m_sliceHorizontalLabelRepeater->setModel(horizontalAxis->labels().size());
+ }
+
+ if (verticalAxis->type() & QAbstract3DAxis::AxisTypeValue) {
+ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(verticalAxis);
+ m_sliceVerticalLabelRepeater->setModel(valueAxis->labels().size());
+ } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) {
+ m_sliceVerticalLabelRepeater->setModel(verticalAxis->labels().size());
+ }
+
+ float textPadding = 12.0f;
+ scaleFactor *= .8f;
+
+ QVector3D fontScaled = QVector3D(scaleFactor, scaleFactor, 0.0f);
+
+ float labelsMaxWidth = 0.0f;
+ labelsMaxWidth = qMax(labelsMaxWidth, float(findLabelsMaxWidth(horizontalAxis->labels()))) + textPadding;
+ QFontMetrics fm(m_controller->activeTheme()->font());
+ float labelHeight = fm.height() + textPadding;
+
+ float adjustment = labelsMaxWidth * scaleFactor;
+ float yPos = backgroundScale.y() + adjustment;
+
+ QVector3D labelTrans = QVector3D(0.0f, -yPos, 0.0f);
+ QStringList labels = horizontalAxis->labels();
+ Q3DTheme *theme = m_controller->activeTheme();
+ QFont font = theme->font();
+ bool borderEnabled = theme->isLabelBorderEnabled();
+ QColor labelTextColor = theme->labelTextColor();
+ bool backgroundEnabled = theme->isLabelBackgroundEnabled();
+ QColor backgroundColor = theme->labelBackgroundColor();
+
+ if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeValue) {
+ auto valueAxis = static_cast<QValue3DAxis *>(horizontalAxis);
+ for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) {
+ auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(i));
+ labelTrans.setX(valueAxis->labelPositionAt(i) * scale * 2.0f - translate);
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ obj->setProperty("labelFont", font);
+ obj->setProperty("borderEnabled", borderEnabled);
+ obj->setProperty("labelTextColor", labelTextColor);
+ obj->setProperty("backgroundEnabled", backgroundEnabled);
+ obj->setProperty("backgroundColor", backgroundColor);
+ obj->setEulerRotation(QVector3D(.0f, .0f, -45.0f));
+ }
+ } else if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeCategory) {
+ for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) {
+ labelTrans = calculateCategoryLabelPosition(horizontalAxis, labelTrans, i);
+ labelTrans.setY(labelTrans.y() - (adjustment / 1.5f));
+ auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(i));
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ obj->setProperty("labelFont", font);
+ obj->setProperty("borderEnabled", borderEnabled);
+ obj->setProperty("labelTextColor", labelTextColor);
+ obj->setProperty("backgroundEnabled", backgroundEnabled);
+ obj->setProperty("backgroundColor", backgroundColor);
+ obj->setEulerRotation(QVector3D(0.0f, 0.0f, -60.0f));
+ }
+ }
+
+ scale = backgroundScale.y() - m_backgroundScaleMargin.y();
+ translate = backgroundScale.y() - m_backgroundScaleMargin.y();
+ labels = verticalAxis->labels();
+ labelsMaxWidth = qMax(labelsMaxWidth, float(findLabelsMaxWidth(labels))) + textPadding;
+ adjustment = labelsMaxWidth * scaleFactor;
+ float xPos = backgroundScale.x() + adjustment;
+ labelTrans = QVector3D(xPos, 0.0f, 0.0f);
+
+ if (verticalAxis->type() == QAbstract3DAxis::AxisTypeValue) {
+ auto valueAxis = static_cast<QValue3DAxis *>(verticalAxis);
+ for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) {
+ auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(i));
+ labelTrans.setY(valueAxis->labelPositionAt(i) * scale * 2.0f - translate);
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ obj->setProperty("labelFont", font);
+ obj->setProperty("borderEnabled", borderEnabled);
+ obj->setProperty("labelTextColor", labelTextColor);
+ obj->setProperty("backgroundEnabled", backgroundEnabled);
+ obj->setProperty("backgroundColor", backgroundColor);
+ }
+ } else if (verticalAxis->type() == QAbstract3DAxis::AxisTypeCategory) {
+ for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) {
+ labelTrans = calculateCategoryLabelPosition(verticalAxis, labelTrans, i);
+ auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(i));
+ obj->setScale(fontScaled);
+ obj->setPosition(labelTrans);
+ obj->setProperty("labelText", labels[i]);
+ obj->setProperty("labelWidth", labelsMaxWidth);
+ obj->setProperty("labelHeight", labelHeight);
+ obj->setProperty("labelFont", font);
+ obj->setProperty("borderEnabled", borderEnabled);
+ obj->setProperty("labelTextColor", labelTextColor);
+ obj->setProperty("backgroundEnabled", backgroundEnabled);
+ obj->setProperty("backgroundColor", backgroundColor);
+ }
+ }
+
+ labelHeight = fm.height() + textPadding;
+ float labelWidth = fm.horizontalAdvance(verticalAxis->title()) + textPadding;
+ adjustment = labelHeight * scaleFactor;
+ xPos = backgroundScale.x() + adjustment;
+ labelTrans = QVector3D(-xPos, 0.0f, 0.0f);
+
+ if (!verticalAxis->title().isEmpty()) {
+ m_sliceVerticalTitleLabel->setScale(fontScaled);
+ m_sliceVerticalTitleLabel->setPosition(labelTrans);
+ m_sliceVerticalTitleLabel->setProperty("labelWidth", labelWidth);
+ m_sliceVerticalTitleLabel->setProperty("labelHeight", labelHeight);
+ m_sliceVerticalTitleLabel->setProperty("labelText", verticalAxis->title());
+ m_sliceVerticalTitleLabel->setProperty("labelFont", font);
+ m_sliceVerticalTitleLabel->setProperty("borderEnabled", borderEnabled);
+ m_sliceVerticalTitleLabel->setProperty("labelTextColor", labelTextColor);
+ m_sliceVerticalTitleLabel->setProperty("backgroundEnabled", backgroundEnabled);
+ m_sliceVerticalTitleLabel->setProperty("backgroundColor", backgroundColor);
+ m_sliceVerticalTitleLabel->setEulerRotation(QVector3D(.0f, .0f, 90.0f));
+ } else {
+ m_sliceVerticalTitleLabel->setVisible(false);
+ }
+
+ labelHeight = fm.height() + textPadding;
+ labelWidth = fm.horizontalAdvance(horizontalAxis->title()) + textPadding;
+ adjustment = labelHeight * scaleFactor;
+ yPos = backgroundScale.y() * 1.5f + adjustment;
+ labelTrans = QVector3D(0.0f, -yPos, 0.0f);
+
+ if (!horizontalAxis->title().isEmpty()) {
+ m_sliceHorizontalTitleLabel->setScale(fontScaled);
+ m_sliceHorizontalTitleLabel->setPosition(labelTrans);
+ m_sliceHorizontalTitleLabel->setProperty("labelWidth", labelWidth);
+ m_sliceHorizontalTitleLabel->setProperty("labelHeight", labelHeight);
+ m_sliceHorizontalTitleLabel->setProperty("labelText", horizontalAxis->title());
+ m_sliceHorizontalTitleLabel->setProperty("labelFont", font);
+ m_sliceHorizontalTitleLabel->setProperty("borderEnabled", borderEnabled);
+ m_sliceHorizontalTitleLabel->setProperty("labelTextColor", labelTextColor);
+ m_sliceHorizontalTitleLabel->setProperty("backgroundEnabled", backgroundEnabled);
+ m_sliceHorizontalTitleLabel->setProperty("backgroundColor", backgroundColor);
+ } else {
+ m_sliceHorizontalTitleLabel->setVisible(false);
+ }
+
+ m_sliceItemLabel->setScale(fontScaled);
+ m_sliceItemLabel->setProperty("labelWidth", labelWidth);
+ m_sliceItemLabel->setProperty("labelHeight", labelHeight);
+ m_sliceItemLabel->setProperty("labelFont", font);
+ m_sliceItemLabel->setProperty("borderEnabled", borderEnabled);
+ m_sliceItemLabel->setProperty("labelTextColor", labelTextColor);
+ m_sliceItemLabel->setProperty("backgroundEnabled", backgroundEnabled);
+ m_sliceItemLabel->setProperty("backgroundColor", backgroundColor);
+}
+
+void QQuickGraphsItem::setUpCamera()
+{
+ auto useOrtho = m_controller->isOrthoProjection();
+ QQuick3DCamera *camera;
+ if (!useOrtho) {
+ auto persCamera = new QQuick3DPerspectiveCamera(rootNode());
+ persCamera->setClipNear(0.001f);
+ persCamera->setFieldOfView(45.0f);
+ camera = persCamera;
+ } else {
+ auto orthCamera = new QQuick3DOrthographicCamera(rootNode());
+ camera = orthCamera;
+ }
+
+ QQuick3DObjectPrivate::get(camera)->refSceneManager(
+ *QQuick3DObjectPrivate::get(rootNode())->sceneManager);
+ auto cameraTarget = new QQuick3DNode(rootNode());
+
+ setCameraTarget(cameraTarget);
+ cameraTarget->setPosition(QVector3D(0, 0, 0));
+ QQuick3DObjectPrivate::get(cameraTarget)->refSceneManager(
+ *QQuick3DObjectPrivate::get(rootNode())->sceneManager);
+
+ camera->setParent(cameraTarget);
+ camera->setParentItem(cameraTarget);
+
+ camera->setPosition(QVector3D(0, 0, 5));
+ camera->lookAt(cameraTarget);
+ setCamera(camera);
+}
+
+void QQuickGraphsItem::setUpLight()
+{
+ auto light = new QQuick3DDirectionalLight(rootNode());
+ QQuick3DObjectPrivate::get(light)->refSceneManager(
+ *QQuick3DObjectPrivate::get(rootNode())->sceneManager);
+ light->setParent(camera());
+ light->setParentItem(camera());
+ m_light = light;
+}
+
+QT_END_NAMESPACE