| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Data Visualization module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "abstract3dcontroller_p.h" |
| #include "qabstract3daxis_p.h" |
| #include "qvalue3daxis_p.h" |
| #include "abstract3drenderer_p.h" |
| #include "qabstract3dinputhandler_p.h" |
| #include "qtouch3dinputhandler.h" |
| #include "thememanager_p.h" |
| #include "q3dtheme_p.h" |
| #include "qcustom3ditem_p.h" |
| #include "utils_p.h" |
| #include <QtCore/QThread> |
| #include <QtGui/QOpenGLFramebufferObject> |
| #include <QtCore/QMutexLocker> |
| |
| QT_BEGIN_NAMESPACE_DATAVISUALIZATION |
| |
| Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scene, |
| QObject *parent) : |
| QObject(parent), |
| m_themeManager(new ThemeManager(this)), |
| m_selectionMode(QAbstract3DGraph::SelectionItem), |
| m_shadowQuality(QAbstract3DGraph::ShadowQualityMedium), |
| m_useOrthoProjection(false), |
| m_aspectRatio(2.0), |
| m_horizontalAspectRatio(0.0), |
| m_optimizationHints(QAbstract3DGraph::OptimizationDefault), |
| m_reflectionEnabled(false), |
| m_reflectivity(0.5), |
| m_locale(QLocale::c()), |
| m_scene(scene), |
| m_activeInputHandler(0), |
| m_axisX(0), |
| m_axisY(0), |
| m_axisZ(0), |
| m_renderer(0), |
| m_isDataDirty(true), |
| m_isCustomDataDirty(true), |
| m_isCustomItemDirty(true), |
| m_isSeriesVisualsDirty(true), |
| m_renderPending(false), |
| m_isPolar(false), |
| m_radialLabelOffset(1.0f), |
| m_measureFps(false), |
| m_numFrames(0), |
| m_currentFps(0.0), |
| m_clickedType(QAbstract3DGraph::ElementNone), |
| m_selectedLabelIndex(-1), |
| m_selectedCustomItemIndex(-1), |
| m_margin(-1.0) |
| { |
| if (!m_scene) |
| m_scene = new Q3DScene; |
| m_scene->setParent(this); |
| |
| // Set initial theme |
| Q3DTheme *defaultTheme = new Q3DTheme(Q3DTheme::ThemeQt); |
| defaultTheme->d_ptr->setDefaultTheme(true); |
| setActiveTheme(defaultTheme); |
| |
| m_scene->d_ptr->setViewport(initialViewport); |
| m_scene->activeLight()->setAutoPosition(true); |
| |
| // Create initial default input handler |
| QAbstract3DInputHandler *inputHandler; |
| inputHandler = new QTouch3DInputHandler(); |
| inputHandler->d_ptr->m_isDefaultHandler = true; |
| setActiveInputHandler(inputHandler); |
| connect(m_scene->d_ptr.data(), &Q3DScenePrivate::needRender, this, |
| &Abstract3DController::emitNeedRender); |
| } |
| |
| Abstract3DController::~Abstract3DController() |
| { |
| destroyRenderer(); |
| delete m_scene; |
| delete m_themeManager; |
| foreach (QCustom3DItem *item, m_customItems) |
| delete item; |
| m_customItems.clear(); |
| } |
| |
| void Abstract3DController::destroyRenderer() |
| { |
| QMutexLocker mutexLocker(&m_renderMutex); |
| // Renderer can be in another thread, don't delete it directly in that case |
| if (m_renderer && m_renderer->thread() && m_renderer->thread() != this->thread()) |
| m_renderer->deleteLater(); |
| else |
| delete m_renderer; |
| m_renderer = 0; |
| } |
| |
| /** |
| * @brief setRenderer Sets the renderer to be used. isInitialized returns true from this point onwards. |
| * @param renderer Renderer to be used. |
| */ |
| void Abstract3DController::setRenderer(Abstract3DRenderer *renderer) |
| { |
| // Note: This function must be called within render mutex |
| m_renderer = renderer; |
| |
| // If renderer is created in different thread than controller, make sure renderer gets |
| // destroyed before the render thread finishes. |
| if (renderer->thread() != this->thread()) { |
| QObject::connect(renderer->thread(), &QThread::finished, this, |
| &Abstract3DController::destroyRenderer, Qt::DirectConnection); |
| } |
| } |
| |
| void Abstract3DController::addSeries(QAbstract3DSeries *series) |
| { |
| insertSeries(m_seriesList.size(), series); |
| } |
| |
| void Abstract3DController::insertSeries(int index, QAbstract3DSeries *series) |
| { |
| if (series) { |
| if (m_seriesList.contains(series)) { |
| int oldIndex = m_seriesList.indexOf(series); |
| if (index != oldIndex) { |
| m_seriesList.removeOne(series); |
| if (oldIndex < index) |
| index--; |
| m_seriesList.insert(index, series); |
| } |
| } else { |
| int oldSize = m_seriesList.size(); |
| m_seriesList.insert(index, series); |
| series->d_ptr->setController(this); |
| QObject::connect(series, &QAbstract3DSeries::visibilityChanged, |
| this, &Abstract3DController::handleSeriesVisibilityChanged); |
| series->d_ptr->resetToTheme(*m_themeManager->activeTheme(), oldSize, false); |
| } |
| if (series->isVisible()) |
| handleSeriesVisibilityChangedBySender(series); |
| } |
| } |
| |
| void Abstract3DController::removeSeries(QAbstract3DSeries *series) |
| { |
| if (series && series->d_ptr->m_controller == this) { |
| m_seriesList.removeAll(series); |
| QObject::disconnect(series, &QAbstract3DSeries::visibilityChanged, |
| this, &Abstract3DController::handleSeriesVisibilityChanged); |
| series->d_ptr->setController(0); |
| m_isDataDirty = true; |
| m_isSeriesVisualsDirty = true; |
| emitNeedRender(); |
| } |
| } |
| |
| QList<QAbstract3DSeries *> Abstract3DController::seriesList() |
| { |
| return m_seriesList; |
| } |
| |
| /** |
| * @brief synchDataToRenderer Called on the render thread while main GUI thread is blocked before rendering. |
| */ |
| void Abstract3DController::synchDataToRenderer() |
| { |
| // Subclass implementations check for renderer validity already, so no need to check here. |
| |
| m_renderPending = false; |
| |
| // If there are pending queries, handle those first |
| if (m_renderer->isGraphPositionQueryResolved()) |
| handlePendingGraphPositionQuery(); |
| |
| if (m_renderer->isClickQueryResolved()) |
| handlePendingClick(); |
| |
| startRecordingRemovesAndInserts(); |
| |
| if (m_scene->d_ptr->m_sceneDirty) |
| m_renderer->updateScene(m_scene); |
| |
| m_renderer->updateTheme(m_themeManager->activeTheme()); |
| |
| if (m_changeTracker.polarChanged) { |
| m_renderer->updatePolar(m_isPolar); |
| m_changeTracker.polarChanged = false; |
| } |
| |
| if (m_changeTracker.radialLabelOffsetChanged) { |
| m_renderer->updateRadialLabelOffset(m_radialLabelOffset); |
| m_changeTracker.radialLabelOffsetChanged = false; |
| } |
| |
| if (m_changeTracker.shadowQualityChanged) { |
| m_renderer->updateShadowQuality(m_shadowQuality); |
| m_changeTracker.shadowQualityChanged = false; |
| } |
| |
| if (m_changeTracker.selectionModeChanged) { |
| m_renderer->updateSelectionMode(m_selectionMode); |
| m_changeTracker.selectionModeChanged = false; |
| } |
| |
| if (m_changeTracker.projectionChanged) { |
| m_renderer->m_useOrthoProjection = m_useOrthoProjection; |
| m_changeTracker.projectionChanged = false; |
| } |
| |
| if (m_changeTracker.aspectRatioChanged) { |
| m_renderer->updateAspectRatio(float(m_aspectRatio)); |
| m_changeTracker.aspectRatioChanged = false; |
| } |
| |
| if (m_changeTracker.horizontalAspectRatioChanged) { |
| m_renderer->updateHorizontalAspectRatio(float(m_horizontalAspectRatio)); |
| m_changeTracker.horizontalAspectRatioChanged = false; |
| } |
| |
| if (m_changeTracker.optimizationHintChanged) { |
| m_renderer->updateOptimizationHint(m_optimizationHints); |
| m_changeTracker.optimizationHintChanged = false; |
| } |
| |
| if (m_changeTracker.reflectionChanged) { |
| m_renderer->m_reflectionEnabled = m_reflectionEnabled; |
| m_changeTracker.reflectionChanged = false; |
| } |
| |
| if (m_changeTracker.reflectivityChanged) { |
| // Invert value to match functionality to the property description |
| m_renderer->m_reflectivity = -(m_reflectivity - 1.0); |
| m_changeTracker.reflectivityChanged = false; |
| } |
| |
| if (m_changeTracker.axisXFormatterChanged) { |
| m_changeTracker.axisXFormatterChanged = false; |
| if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); |
| m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationX, |
| valueAxisX->formatter()); |
| } |
| } |
| if (m_changeTracker.axisYFormatterChanged) { |
| m_changeTracker.axisYFormatterChanged = false; |
| if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); |
| m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationY, |
| valueAxisY->formatter()); |
| } |
| } |
| if (m_changeTracker.axisZFormatterChanged) { |
| m_changeTracker.axisZFormatterChanged = false; |
| if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ); |
| m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationZ, |
| valueAxisZ->formatter()); |
| } |
| } |
| |
| if (m_changeTracker.axisXTypeChanged) { |
| m_renderer->updateAxisType(QAbstract3DAxis::AxisOrientationX, m_axisX->type()); |
| m_changeTracker.axisXTypeChanged = false; |
| } |
| |
| if (m_changeTracker.axisYTypeChanged) { |
| m_renderer->updateAxisType(QAbstract3DAxis::AxisOrientationY, m_axisY->type()); |
| m_changeTracker.axisYTypeChanged = false; |
| } |
| |
| if (m_changeTracker.axisZTypeChanged) { |
| m_renderer->updateAxisType(QAbstract3DAxis::AxisOrientationZ, m_axisZ->type()); |
| m_changeTracker.axisZTypeChanged = false; |
| } |
| |
| if (m_changeTracker.axisXTitleChanged) { |
| m_renderer->updateAxisTitle(QAbstract3DAxis::AxisOrientationX, m_axisX->title()); |
| m_changeTracker.axisXTitleChanged = false; |
| } |
| |
| if (m_changeTracker.axisYTitleChanged) { |
| m_renderer->updateAxisTitle(QAbstract3DAxis::AxisOrientationY, m_axisY->title()); |
| m_changeTracker.axisYTitleChanged = false; |
| } |
| |
| if (m_changeTracker.axisZTitleChanged) { |
| m_renderer->updateAxisTitle(QAbstract3DAxis::AxisOrientationZ, m_axisZ->title()); |
| m_changeTracker.axisZTitleChanged = false; |
| } |
| |
| if (m_changeTracker.axisXLabelsChanged) { |
| m_renderer->updateAxisLabels(QAbstract3DAxis::AxisOrientationX, m_axisX->labels()); |
| m_changeTracker.axisXLabelsChanged = false; |
| } |
| |
| if (m_changeTracker.axisYLabelsChanged) { |
| m_renderer->updateAxisLabels(QAbstract3DAxis::AxisOrientationY, m_axisY->labels()); |
| m_changeTracker.axisYLabelsChanged = false; |
| } |
| if (m_changeTracker.axisZLabelsChanged) { |
| m_renderer->updateAxisLabels(QAbstract3DAxis::AxisOrientationZ, m_axisZ->labels()); |
| m_changeTracker.axisZLabelsChanged = false; |
| } |
| |
| if (m_changeTracker.axisXRangeChanged) { |
| m_renderer->updateAxisRange(QAbstract3DAxis::AxisOrientationX, m_axisX->min(), |
| m_axisX->max()); |
| m_changeTracker.axisXRangeChanged = false; |
| } |
| |
| if (m_changeTracker.axisYRangeChanged) { |
| m_renderer->updateAxisRange(QAbstract3DAxis::AxisOrientationY, m_axisY->min(), |
| m_axisY->max()); |
| m_changeTracker.axisYRangeChanged = false; |
| } |
| |
| if (m_changeTracker.axisZRangeChanged) { |
| m_renderer->updateAxisRange(QAbstract3DAxis::AxisOrientationZ, m_axisZ->min(), |
| m_axisZ->max()); |
| m_changeTracker.axisZRangeChanged = false; |
| } |
| |
| if (m_changeTracker.axisXSegmentCountChanged) { |
| m_changeTracker.axisXSegmentCountChanged = false; |
| if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); |
| m_renderer->updateAxisSegmentCount(QAbstract3DAxis::AxisOrientationX, |
| valueAxisX->segmentCount()); |
| } |
| } |
| |
| if (m_changeTracker.axisYSegmentCountChanged) { |
| m_changeTracker.axisYSegmentCountChanged = false; |
| if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); |
| m_renderer->updateAxisSegmentCount(QAbstract3DAxis::AxisOrientationY, |
| valueAxisY->segmentCount()); |
| } |
| } |
| |
| if (m_changeTracker.axisZSegmentCountChanged) { |
| m_changeTracker.axisZSegmentCountChanged = false; |
| if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ); |
| m_renderer->updateAxisSegmentCount(QAbstract3DAxis::AxisOrientationZ, |
| valueAxisZ->segmentCount()); |
| } |
| } |
| |
| if (m_changeTracker.axisXSubSegmentCountChanged) { |
| m_changeTracker.axisXSubSegmentCountChanged = false; |
| if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); |
| m_renderer->updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientationX, |
| valueAxisX->subSegmentCount()); |
| } |
| } |
| |
| if (m_changeTracker.axisYSubSegmentCountChanged) { |
| m_changeTracker.axisYSubSegmentCountChanged = false; |
| if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); |
| m_renderer->updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientationY, |
| valueAxisY->subSegmentCount()); |
| } |
| } |
| |
| if (m_changeTracker.axisZSubSegmentCountChanged) { |
| m_changeTracker.axisZSubSegmentCountChanged = false; |
| if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ); |
| m_renderer->updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientationZ, |
| valueAxisZ->subSegmentCount()); |
| } |
| } |
| |
| if (m_changeTracker.axisXLabelFormatChanged) { |
| m_changeTracker.axisXLabelFormatChanged = false; |
| if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); |
| m_renderer->updateAxisLabelFormat(QAbstract3DAxis::AxisOrientationX, |
| valueAxisX->labelFormat()); |
| } |
| } |
| |
| if (m_changeTracker.axisYLabelFormatChanged) { |
| m_changeTracker.axisYLabelFormatChanged = false; |
| if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); |
| m_renderer->updateAxisLabelFormat(QAbstract3DAxis::AxisOrientationY, |
| valueAxisY->labelFormat()); |
| } |
| } |
| |
| if (m_changeTracker.axisZLabelFormatChanged) { |
| m_changeTracker.axisZLabelFormatChanged = false; |
| if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ); |
| m_renderer->updateAxisLabelFormat(QAbstract3DAxis::AxisOrientationZ, |
| valueAxisZ->labelFormat()); |
| } |
| } |
| |
| if (m_changeTracker.axisXReversedChanged) { |
| m_changeTracker.axisXReversedChanged = false; |
| if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); |
| m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationX, |
| valueAxisX->reversed()); |
| } |
| } |
| |
| if (m_changeTracker.axisYReversedChanged) { |
| m_changeTracker.axisYReversedChanged = false; |
| if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); |
| m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationY, |
| valueAxisY->reversed()); |
| } |
| } |
| |
| if (m_changeTracker.axisZReversedChanged) { |
| m_changeTracker.axisZReversedChanged = false; |
| if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ); |
| m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationZ, |
| valueAxisZ->reversed()); |
| } |
| } |
| |
| if (m_changeTracker.axisXLabelAutoRotationChanged) { |
| m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationX, |
| m_axisX->labelAutoRotation()); |
| m_changeTracker.axisXLabelAutoRotationChanged = false; |
| } |
| |
| if (m_changeTracker.axisYLabelAutoRotationChanged) { |
| m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationY, |
| m_axisY->labelAutoRotation()); |
| m_changeTracker.axisYLabelAutoRotationChanged = false; |
| } |
| |
| if (m_changeTracker.axisZLabelAutoRotationChanged) { |
| m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationZ, |
| m_axisZ->labelAutoRotation()); |
| m_changeTracker.axisZLabelAutoRotationChanged = false; |
| } |
| |
| if (m_changeTracker.axisXTitleVisibilityChanged) { |
| m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationX, |
| m_axisX->isTitleVisible()); |
| m_changeTracker.axisXTitleVisibilityChanged = false; |
| } |
| if (m_changeTracker.axisYTitleVisibilityChanged) { |
| m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationY, |
| m_axisY->isTitleVisible()); |
| m_changeTracker.axisYTitleVisibilityChanged = false; |
| } |
| if (m_changeTracker.axisZTitleVisibilityChanged) { |
| m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationZ, |
| m_axisZ->isTitleVisible()); |
| m_changeTracker.axisZTitleVisibilityChanged = false; |
| } |
| if (m_changeTracker.axisXTitleFixedChanged) { |
| m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationX, |
| m_axisX->isTitleFixed()); |
| m_changeTracker.axisXTitleFixedChanged = false; |
| } |
| if (m_changeTracker.axisYTitleFixedChanged) { |
| m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationY, |
| m_axisY->isTitleFixed()); |
| m_changeTracker.axisYTitleFixedChanged = false; |
| } |
| if (m_changeTracker.axisZTitleFixedChanged) { |
| m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationZ, |
| m_axisZ->isTitleFixed()); |
| m_changeTracker.axisZTitleFixedChanged = false; |
| } |
| |
| if (m_changeTracker.marginChanged) { |
| m_renderer->updateMargin(float(m_margin)); |
| m_changeTracker.marginChanged = false; |
| } |
| |
| if (m_changedSeriesList.size()) { |
| m_renderer->modifiedSeriesList(m_changedSeriesList); |
| m_changedSeriesList.clear(); |
| } |
| |
| if (m_isSeriesVisualsDirty) { |
| m_renderer->updateSeries(m_seriesList); |
| m_isSeriesVisualsDirty = false; |
| } |
| |
| if (m_isDataDirty) { |
| // Series list supplied above in updateSeries() is used to access the data, |
| // so no data needs to be passed in updateData() |
| m_renderer->updateData(); |
| m_isDataDirty = false; |
| } |
| |
| if (m_isCustomDataDirty) { |
| m_renderer->updateCustomData(m_customItems); |
| m_isCustomDataDirty = false; |
| } |
| |
| if (m_isCustomItemDirty) { |
| m_renderer->updateCustomItems(); |
| m_isCustomItemDirty = false; |
| } |
| } |
| |
| void Abstract3DController::render(const GLuint defaultFboHandle) |
| { |
| QMutexLocker mutexLocker(&m_renderMutex); |
| |
| // If not initialized, do nothing. |
| if (!m_renderer) |
| return; |
| |
| if (m_measureFps) { |
| // Measure speed (as milliseconds per frame) |
| m_numFrames++; |
| int elapsed = m_frameTimer.elapsed(); |
| if (elapsed >= 1000) { |
| m_currentFps = qreal(m_numFrames) * 1000.0 / qreal(elapsed); |
| emit currentFpsChanged(m_currentFps); |
| m_numFrames = 0; |
| m_frameTimer.restart(); |
| } |
| // To get meaningful framerate, don't just do render on demand. |
| emitNeedRender(); |
| } |
| |
| m_renderer->render(defaultFboHandle); |
| } |
| |
| void Abstract3DController::mouseDoubleClickEvent(QMouseEvent *event) |
| { |
| if (m_activeInputHandler) |
| m_activeInputHandler->mouseDoubleClickEvent(event); |
| } |
| |
| void Abstract3DController::touchEvent(QTouchEvent *event) |
| { |
| if (m_activeInputHandler) |
| m_activeInputHandler->touchEvent(event); |
| } |
| |
| void Abstract3DController::mousePressEvent(QMouseEvent *event, const QPoint &mousePos) |
| { |
| if (m_activeInputHandler) |
| m_activeInputHandler->mousePressEvent(event, mousePos); |
| } |
| |
| void Abstract3DController::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos) |
| { |
| if (m_activeInputHandler) |
| m_activeInputHandler->mouseReleaseEvent(event, mousePos); |
| } |
| |
| void Abstract3DController::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos) |
| { |
| if (m_activeInputHandler) |
| m_activeInputHandler->mouseMoveEvent(event, mousePos); |
| } |
| |
| #if QT_CONFIG(wheelevent) |
| void Abstract3DController::wheelEvent(QWheelEvent *event) |
| { |
| if (m_activeInputHandler) |
| m_activeInputHandler->wheelEvent(event); |
| } |
| #endif |
| |
| void Abstract3DController::handleThemeColorStyleChanged(Q3DTheme::ColorStyle style) |
| { |
| // Set value for series that have not explicitly set this value |
| foreach (QAbstract3DSeries *series, m_seriesList) { |
| if (!series->d_ptr->m_themeTracker.colorStyleOverride) { |
| series->setColorStyle(style); |
| series->d_ptr->m_themeTracker.colorStyleOverride = false; |
| } |
| } |
| markSeriesVisualsDirty(); |
| } |
| |
| void Abstract3DController::handleThemeBaseColorsChanged(const QList<QColor> &colors) |
| { |
| int colorIdx = 0; |
| // Set value for series that have not explicitly set this value |
| foreach (QAbstract3DSeries *series, m_seriesList) { |
| if (!series->d_ptr->m_themeTracker.baseColorOverride) { |
| series->setBaseColor(colors.at(colorIdx)); |
| series->d_ptr->m_themeTracker.baseColorOverride = false; |
| } |
| if (++colorIdx >= colors.size()) |
| colorIdx = 0; |
| } |
| markSeriesVisualsDirty(); |
| } |
| |
| void Abstract3DController::handleThemeBaseGradientsChanged(const QList<QLinearGradient> &gradients) |
| { |
| int gradientIdx = 0; |
| // Set value for series that have not explicitly set this value |
| foreach (QAbstract3DSeries *series, m_seriesList) { |
| if (!series->d_ptr->m_themeTracker.baseGradientOverride) { |
| series->setBaseGradient(gradients.at(gradientIdx)); |
| series->d_ptr->m_themeTracker.baseGradientOverride = false; |
| } |
| if (++gradientIdx >= gradients.size()) |
| gradientIdx = 0; |
| } |
| markSeriesVisualsDirty(); |
| } |
| |
| void Abstract3DController::handleThemeSingleHighlightColorChanged(const QColor &color) |
| { |
| // Set value for series that have not explicitly set this value |
| foreach (QAbstract3DSeries *series, m_seriesList) { |
| if (!series->d_ptr->m_themeTracker.singleHighlightColorOverride) { |
| series->setSingleHighlightColor(color); |
| series->d_ptr->m_themeTracker.singleHighlightColorOverride = false; |
| } |
| } |
| markSeriesVisualsDirty(); |
| } |
| |
| void Abstract3DController::handleThemeSingleHighlightGradientChanged( |
| const QLinearGradient &gradient) |
| { |
| // Set value for series that have not explicitly set this value |
| foreach (QAbstract3DSeries *series, m_seriesList) { |
| if (!series->d_ptr->m_themeTracker.singleHighlightGradientOverride) { |
| series->setSingleHighlightGradient(gradient); |
| series->d_ptr->m_themeTracker.singleHighlightGradientOverride = false; |
| } |
| } |
| markSeriesVisualsDirty(); |
| } |
| |
| void Abstract3DController::handleThemeMultiHighlightColorChanged(const QColor &color) |
| { |
| // Set value for series that have not explicitly set this value |
| foreach (QAbstract3DSeries *series, m_seriesList) { |
| if (!series->d_ptr->m_themeTracker.multiHighlightColorOverride) { |
| series->setMultiHighlightColor(color); |
| series->d_ptr->m_themeTracker.multiHighlightColorOverride = false; |
| } |
| } |
| markSeriesVisualsDirty(); |
| } |
| |
| void Abstract3DController::handleThemeMultiHighlightGradientChanged(const QLinearGradient &gradient) |
| { |
| // Set value for series that have not explicitly set this value |
| foreach (QAbstract3DSeries *series, m_seriesList) { |
| if (!series->d_ptr->m_themeTracker.multiHighlightGradientOverride) { |
| series->setMultiHighlightGradient(gradient); |
| series->d_ptr->m_themeTracker.multiHighlightGradientOverride = false; |
| } |
| } |
| markSeriesVisualsDirty(); |
| } |
| |
| void Abstract3DController::handleThemeTypeChanged(Q3DTheme::Theme theme) |
| { |
| Q_UNUSED(theme) |
| |
| // Changing theme type is logically equivalent of changing the entire theme |
| // object, so reset all attached series to the new theme. |
| |
| Q3DTheme *activeTheme = m_themeManager->activeTheme(); |
| for (int i = 0; i < m_seriesList.size(); i++) |
| m_seriesList.at(i)->d_ptr->resetToTheme(*activeTheme, i, true); |
| markSeriesVisualsDirty(); |
| } |
| |
| void Abstract3DController::setAxisX(QAbstract3DAxis *axis) |
| { |
| // Setting null axis will always create new default axis |
| if (!axis || axis != m_axisX) { |
| setAxisHelper(QAbstract3DAxis::AxisOrientationX, axis, &m_axisX); |
| emit axisXChanged(m_axisX); |
| } |
| } |
| |
| QAbstract3DAxis *Abstract3DController::axisX() const |
| { |
| return m_axisX; |
| } |
| |
| void Abstract3DController::setAxisY(QAbstract3DAxis *axis) |
| { |
| // Setting null axis will always create new default axis |
| if (!axis || axis != m_axisY) { |
| setAxisHelper(QAbstract3DAxis::AxisOrientationY, axis, &m_axisY); |
| emit axisYChanged(m_axisY); |
| } |
| } |
| |
| QAbstract3DAxis *Abstract3DController::axisY() const |
| { |
| return m_axisY; |
| } |
| |
| void Abstract3DController::setAxisZ(QAbstract3DAxis *axis) |
| { |
| // Setting null axis will always create new default axis |
| if (!axis || axis != m_axisZ) { |
| setAxisHelper(QAbstract3DAxis::AxisOrientationZ, axis, &m_axisZ); |
| emit axisZChanged(m_axisZ); |
| } |
| } |
| |
| QAbstract3DAxis *Abstract3DController::axisZ() const |
| { |
| return m_axisZ; |
| } |
| |
| void Abstract3DController::addAxis(QAbstract3DAxis *axis) |
| { |
| Q_ASSERT(axis); |
| Abstract3DController *owner = qobject_cast<Abstract3DController *>(axis->parent()); |
| if (owner != this) { |
| Q_ASSERT_X(!owner, "addAxis", "Axis already attached to a graph."); |
| axis->setParent(this); |
| } |
| if (!m_axes.contains(axis)) |
| m_axes.append(axis); |
| } |
| |
| void Abstract3DController::releaseAxis(QAbstract3DAxis *axis) |
| { |
| if (axis && m_axes.contains(axis)) { |
| // Clear the default status from released default axes |
| if (axis->d_ptr->isDefaultAxis()) |
| axis->d_ptr->setDefaultAxis(false); |
| |
| // If the axis is in use, replace it with a temporary one |
| switch (axis->orientation()) { |
| case QAbstract3DAxis::AxisOrientationX: |
| setAxisX(0); |
| break; |
| case QAbstract3DAxis::AxisOrientationY: |
| setAxisY(0); |
| break; |
| case QAbstract3DAxis::AxisOrientationZ: |
| setAxisZ(0); |
| break; |
| default: |
| break; |
| } |
| |
| m_axes.removeAll(axis); |
| axis->setParent(0); |
| } |
| } |
| |
| QList<QAbstract3DAxis *> Abstract3DController::axes() const |
| { |
| return m_axes; |
| } |
| |
| void Abstract3DController::addInputHandler(QAbstract3DInputHandler *inputHandler) |
| { |
| Q_ASSERT(inputHandler); |
| Abstract3DController *owner = qobject_cast<Abstract3DController *>(inputHandler->parent()); |
| if (owner != this) { |
| Q_ASSERT_X(!owner, "addInputHandler", |
| "Input handler already attached to another component."); |
| inputHandler->setParent(this); |
| } |
| |
| if (!m_inputHandlers.contains(inputHandler)) |
| m_inputHandlers.append(inputHandler); |
| } |
| |
| void Abstract3DController::releaseInputHandler(QAbstract3DInputHandler *inputHandler) |
| { |
| if (inputHandler && m_inputHandlers.contains(inputHandler)) { |
| // Clear the default status from released default input handler |
| if (inputHandler->d_ptr->m_isDefaultHandler) |
| inputHandler->d_ptr->m_isDefaultHandler = false; |
| |
| // If the input handler is in use, remove it |
| if (m_activeInputHandler == inputHandler) |
| setActiveInputHandler(0); |
| |
| m_inputHandlers.removeAll(inputHandler); |
| inputHandler->setParent(0); |
| } |
| } |
| |
| void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputHandler) |
| { |
| if (inputHandler == m_activeInputHandler) |
| return; |
| |
| // If existing input handler is the default input handler, delete it |
| if (m_activeInputHandler) { |
| if (m_activeInputHandler->d_ptr->m_isDefaultHandler) { |
| m_inputHandlers.removeAll(m_activeInputHandler); |
| delete m_activeInputHandler; |
| } else { |
| // Disconnect the old input handler |
| m_activeInputHandler->setScene(0); |
| QObject::disconnect(m_activeInputHandler, 0, this, 0); |
| } |
| } |
| |
| // Assume ownership and connect to this controller's scene |
| if (inputHandler) |
| addInputHandler(inputHandler); |
| |
| m_activeInputHandler = inputHandler; |
| if (m_activeInputHandler) { |
| m_activeInputHandler->setScene(m_scene); |
| |
| // Connect the input handler |
| QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this, |
| &Abstract3DController::handleInputViewChanged); |
| QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this, |
| &Abstract3DController::handleInputPositionChanged); |
| } |
| |
| // Notify change of input handler |
| emit activeInputHandlerChanged(m_activeInputHandler); |
| } |
| |
| QAbstract3DInputHandler* Abstract3DController::activeInputHandler() |
| { |
| return m_activeInputHandler; |
| } |
| |
| QList<QAbstract3DInputHandler *> Abstract3DController::inputHandlers() const |
| { |
| return m_inputHandlers; |
| } |
| |
| void Abstract3DController::addTheme(Q3DTheme *theme) |
| { |
| m_themeManager->addTheme(theme); |
| } |
| |
| void Abstract3DController::releaseTheme(Q3DTheme *theme) |
| { |
| Q3DTheme *oldTheme = m_themeManager->activeTheme(); |
| |
| m_themeManager->releaseTheme(theme); |
| |
| if (oldTheme != m_themeManager->activeTheme()) |
| emit activeThemeChanged(m_themeManager->activeTheme()); |
| } |
| |
| QList<Q3DTheme *> Abstract3DController::themes() const |
| { |
| return m_themeManager->themes(); |
| } |
| |
| void Abstract3DController::setActiveTheme(Q3DTheme *theme, bool force) |
| { |
| if (theme != m_themeManager->activeTheme()) { |
| m_themeManager->setActiveTheme(theme); |
| m_changeTracker.themeChanged = true; |
| // Default theme can be created by theme manager, so ensure we have correct theme |
| Q3DTheme *newActiveTheme = m_themeManager->activeTheme(); |
| // Reset all attached series to the new theme |
| for (int i = 0; i < m_seriesList.size(); i++) |
| m_seriesList.at(i)->d_ptr->resetToTheme(*newActiveTheme, i, force); |
| markSeriesVisualsDirty(); |
| emit activeThemeChanged(newActiveTheme); |
| } |
| } |
| |
| Q3DTheme *Abstract3DController::activeTheme() const |
| { |
| return m_themeManager->activeTheme(); |
| } |
| |
| void Abstract3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode) |
| { |
| if (mode != m_selectionMode) { |
| m_selectionMode = mode; |
| m_changeTracker.selectionModeChanged = true; |
| emit selectionModeChanged(mode); |
| emitNeedRender(); |
| } |
| } |
| |
| QAbstract3DGraph::SelectionFlags Abstract3DController::selectionMode() const |
| { |
| return m_selectionMode; |
| } |
| |
| void Abstract3DController::setShadowQuality(QAbstract3DGraph::ShadowQuality quality) |
| { |
| if (!m_useOrthoProjection) |
| doSetShadowQuality(quality); |
| } |
| |
| void Abstract3DController::doSetShadowQuality(QAbstract3DGraph::ShadowQuality quality) |
| { |
| if (quality != m_shadowQuality) { |
| m_shadowQuality = quality; |
| m_changeTracker.shadowQualityChanged = true; |
| emit shadowQualityChanged(m_shadowQuality); |
| emitNeedRender(); |
| } |
| } |
| |
| QAbstract3DGraph::ShadowQuality Abstract3DController::shadowQuality() const |
| { |
| return m_shadowQuality; |
| } |
| |
| void Abstract3DController::setOptimizationHints(QAbstract3DGraph::OptimizationHints hints) |
| { |
| if (hints != m_optimizationHints) { |
| m_optimizationHints = hints; |
| m_changeTracker.optimizationHintChanged = true; |
| m_isDataDirty = true; |
| emit optimizationHintsChanged(hints); |
| emitNeedRender(); |
| } |
| } |
| |
| QAbstract3DGraph::OptimizationHints Abstract3DController::optimizationHints() const |
| { |
| return m_optimizationHints; |
| } |
| |
| bool Abstract3DController::shadowsSupported() const |
| { |
| return !isOpenGLES(); |
| } |
| |
| bool Abstract3DController::isSlicingActive() const |
| { |
| return m_scene->isSlicingActive(); |
| } |
| |
| void Abstract3DController::setSlicingActive(bool isSlicing) |
| { |
| m_scene->setSlicingActive(isSlicing); |
| } |
| |
| Q3DScene *Abstract3DController::scene() |
| { |
| return m_scene; |
| } |
| |
| void Abstract3DController::markDataDirty() |
| { |
| m_isDataDirty = true; |
| |
| markSeriesItemLabelsDirty(); |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::markSeriesVisualsDirty() |
| { |
| m_isSeriesVisualsDirty = true; |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::requestRender(QOpenGLFramebufferObject *fbo) |
| { |
| QMutexLocker mutexLocker(&m_renderMutex); |
| m_renderer->render(fbo->handle()); |
| } |
| |
| int Abstract3DController::addCustomItem(QCustom3DItem *item) |
| { |
| if (!item) |
| return -1; |
| |
| int index = m_customItems.indexOf(item); |
| |
| if (index != -1) |
| return index; |
| |
| item->setParent(this); |
| connect(item->d_ptr.data(), &QCustom3DItemPrivate::needUpdate, |
| this, &Abstract3DController::updateCustomItem); |
| m_customItems.append(item); |
| item->d_ptr->resetDirtyBits(); |
| m_isCustomDataDirty = true; |
| emitNeedRender(); |
| return m_customItems.count() - 1; |
| } |
| |
| void Abstract3DController::deleteCustomItems() |
| { |
| foreach (QCustom3DItem *item, m_customItems) |
| delete item; |
| m_customItems.clear(); |
| m_isCustomDataDirty = true; |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::deleteCustomItem(QCustom3DItem *item) |
| { |
| if (!item) |
| return; |
| |
| m_customItems.removeOne(item); |
| delete item; |
| item = 0; |
| m_isCustomDataDirty = true; |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::deleteCustomItem(const QVector3D &position) |
| { |
| // Get the item for the position |
| foreach (QCustom3DItem *item, m_customItems) { |
| if (item->position() == position) |
| deleteCustomItem(item); |
| } |
| } |
| |
| void Abstract3DController::releaseCustomItem(QCustom3DItem *item) |
| { |
| if (item && m_customItems.contains(item)) { |
| disconnect(item->d_ptr.data(), &QCustom3DItemPrivate::needUpdate, |
| this, &Abstract3DController::updateCustomItem); |
| m_customItems.removeOne(item); |
| item->setParent(0); |
| m_isCustomDataDirty = true; |
| emitNeedRender(); |
| } |
| } |
| |
| QList<QCustom3DItem *> Abstract3DController::customItems() const |
| { |
| return m_customItems; |
| } |
| |
| void Abstract3DController::updateCustomItem() |
| { |
| m_isCustomItemDirty = true; |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisTitleChanged(const QString &title) |
| { |
| Q_UNUSED(title) |
| handleAxisTitleChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisTitleChangedBySender(QObject *sender) |
| { |
| if (sender == m_axisX) |
| m_changeTracker.axisXTitleChanged = true; |
| else if (sender == m_axisY) |
| m_changeTracker.axisYTitleChanged = true; |
| else if (sender == m_axisZ) |
| m_changeTracker.axisZTitleChanged = true; |
| else |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| |
| markSeriesItemLabelsDirty(); |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisLabelsChanged() |
| { |
| handleAxisLabelsChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisLabelsChangedBySender(QObject *sender) |
| { |
| if (sender == m_axisX) |
| m_changeTracker.axisXLabelsChanged = true; |
| else if (sender == m_axisY) |
| m_changeTracker.axisYLabelsChanged = true; |
| else if (sender == m_axisZ) |
| m_changeTracker.axisZLabelsChanged = true; |
| else |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| |
| markSeriesItemLabelsDirty(); |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisRangeChanged(float min, float max) |
| { |
| Q_UNUSED(min) |
| Q_UNUSED(max) |
| handleAxisRangeChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisRangeChangedBySender(QObject *sender) |
| { |
| if (sender == m_axisX) { |
| m_isDataDirty = true; |
| m_changeTracker.axisXRangeChanged = true; |
| } else if (sender == m_axisY) { |
| m_isDataDirty = true; |
| m_changeTracker.axisYRangeChanged = true; |
| } else if (sender == m_axisZ) { |
| m_isDataDirty = true; |
| m_changeTracker.axisZRangeChanged = true; |
| } else { |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| } |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisSegmentCountChanged(int count) |
| { |
| Q_UNUSED(count) |
| handleAxisSegmentCountChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisSegmentCountChangedBySender(QObject *sender) |
| { |
| if (sender == m_axisX) |
| m_changeTracker.axisXSegmentCountChanged = true; |
| else if (sender == m_axisY) |
| m_changeTracker.axisYSegmentCountChanged = true; |
| else if (sender == m_axisZ) |
| m_changeTracker.axisZSegmentCountChanged = true; |
| else |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisSubSegmentCountChanged(int count) |
| { |
| Q_UNUSED(count) |
| handleAxisSubSegmentCountChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisSubSegmentCountChangedBySender(QObject *sender) |
| { |
| if (sender == m_axisX) |
| m_changeTracker.axisXSubSegmentCountChanged = true; |
| else if (sender == m_axisY) |
| m_changeTracker.axisYSubSegmentCountChanged = true; |
| else if (sender == m_axisZ) |
| m_changeTracker.axisZSubSegmentCountChanged = true; |
| else |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisAutoAdjustRangeChanged(bool autoAdjust) |
| { |
| QObject *sender = QObject::sender(); |
| if (sender != m_axisX && sender != m_axisY && sender != m_axisZ) |
| return; |
| |
| QAbstract3DAxis *axis = static_cast<QAbstract3DAxis*>(sender); |
| handleAxisAutoAdjustRangeChangedInOrientation(axis->orientation(), autoAdjust); |
| } |
| |
| void Abstract3DController::handleAxisLabelFormatChanged(const QString &format) |
| { |
| Q_UNUSED(format) |
| handleAxisLabelFormatChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisReversedChanged(bool enable) |
| { |
| Q_UNUSED(enable) |
| handleAxisReversedChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisFormatterDirty() |
| { |
| handleAxisFormatterDirtyBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisLabelAutoRotationChanged(float angle) |
| { |
| Q_UNUSED(angle) |
| handleAxisLabelAutoRotationChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisTitleVisibilityChanged(bool visible) |
| { |
| Q_UNUSED(visible) |
| handleAxisTitleVisibilityChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleAxisTitleFixedChanged(bool fixed) |
| { |
| Q_UNUSED(fixed) |
| handleAxisTitleFixedChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleInputViewChanged(QAbstract3DInputHandler::InputView view) |
| { |
| // When in automatic slicing mode, input view change to primary disables slice mode |
| if (m_selectionMode.testFlag(QAbstract3DGraph::SelectionSlice) |
| && view == QAbstract3DInputHandler::InputViewOnPrimary) { |
| setSlicingActive(false); |
| } |
| |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleInputPositionChanged(const QPoint &position) |
| { |
| Q_UNUSED(position) |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleSeriesVisibilityChanged(bool visible) |
| { |
| Q_UNUSED(visible) |
| |
| handleSeriesVisibilityChangedBySender(sender()); |
| } |
| |
| void Abstract3DController::handleRequestShadowQuality(QAbstract3DGraph::ShadowQuality quality) |
| { |
| setShadowQuality(quality); |
| } |
| |
| void Abstract3DController::setMeasureFps(bool enable) |
| { |
| if (m_measureFps != enable) { |
| m_measureFps = enable; |
| m_currentFps = 0.0; |
| |
| if (enable) { |
| m_frameTimer.start(); |
| m_numFrames = -1; |
| emitNeedRender(); |
| } |
| emit measureFpsChanged(enable); |
| } |
| } |
| |
| void Abstract3DController::handleAxisLabelFormatChangedBySender(QObject *sender) |
| { |
| // Label format changing needs to dirty the data so that labels are reset. |
| if (sender == m_axisX) { |
| m_isDataDirty = true; |
| m_changeTracker.axisXLabelFormatChanged = true; |
| } else if (sender == m_axisY) { |
| m_isDataDirty = true; |
| m_changeTracker.axisYLabelFormatChanged = true; |
| } else if (sender == m_axisZ) { |
| m_isDataDirty = true; |
| m_changeTracker.axisZLabelFormatChanged = true; |
| } else { |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| } |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisReversedChangedBySender(QObject *sender) |
| { |
| // Reversing change needs to dirty the data so item positions are recalculated |
| if (sender == m_axisX) { |
| m_isDataDirty = true; |
| m_changeTracker.axisXReversedChanged = true; |
| } else if (sender == m_axisY) { |
| m_isDataDirty = true; |
| m_changeTracker.axisYReversedChanged = true; |
| } else if (sender == m_axisZ) { |
| m_isDataDirty = true; |
| m_changeTracker.axisZReversedChanged = true; |
| } else { |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| } |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisFormatterDirtyBySender(QObject *sender) |
| { |
| // Sender is QValue3DAxisPrivate |
| QValue3DAxis *valueAxis = static_cast<QValue3DAxisPrivate *>(sender)->qptr(); |
| if (valueAxis == m_axisX) { |
| m_isDataDirty = true; |
| m_changeTracker.axisXFormatterChanged = true; |
| } else if (valueAxis == m_axisY) { |
| m_isDataDirty = true; |
| m_changeTracker.axisYFormatterChanged = true; |
| } else if (valueAxis == m_axisZ) { |
| m_isDataDirty = true; |
| m_changeTracker.axisZFormatterChanged = true; |
| } else { |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| } |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisLabelAutoRotationChangedBySender(QObject *sender) |
| { |
| if (sender == m_axisX) |
| m_changeTracker.axisXLabelAutoRotationChanged = true; |
| else if (sender == m_axisY) |
| m_changeTracker.axisYLabelAutoRotationChanged = true; |
| else if (sender == m_axisZ) |
| m_changeTracker.axisZLabelAutoRotationChanged = true; |
| else |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisTitleVisibilityChangedBySender(QObject *sender) |
| { |
| if (sender == m_axisX) |
| m_changeTracker.axisXTitleVisibilityChanged = true; |
| else if (sender == m_axisY) |
| m_changeTracker.axisYTitleVisibilityChanged = true; |
| else if (sender == m_axisZ) |
| m_changeTracker.axisZTitleVisibilityChanged = true; |
| else |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleAxisTitleFixedChangedBySender(QObject *sender) |
| { |
| if (sender == m_axisX) |
| m_changeTracker.axisXTitleFixedChanged = true; |
| else if (sender == m_axisY) |
| m_changeTracker.axisYTitleFixedChanged = true; |
| else if (sender == m_axisZ) |
| m_changeTracker.axisZTitleFixedChanged = true; |
| else |
| qWarning() << __FUNCTION__ << "invoked for invalid axis"; |
| |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::handleSeriesVisibilityChangedBySender(QObject *sender) |
| { |
| QAbstract3DSeries *series = static_cast<QAbstract3DSeries *>(sender); |
| series->d_ptr->m_changeTracker.visibilityChanged = true; |
| |
| m_isDataDirty = true; |
| m_isSeriesVisualsDirty = true; |
| |
| adjustAxisRanges(); |
| |
| emitNeedRender(); |
| } |
| |
| void Abstract3DController::markSeriesItemLabelsDirty() |
| { |
| for (int i = 0; i < m_seriesList.size(); i++) |
| m_seriesList.at(i)->d_ptr->markItemLabelDirty(); |
| } |
| |
| bool Abstract3DController::isOpenGLES() const |
| { |
| return Utils::isOpenGLES(); |
| } |
| |
| void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orientation, |
| QAbstract3DAxis *axis, QAbstract3DAxis **axisPtr) |
| { |
| // Setting null axis indicates using default axis |
| if (!axis) |
| axis = createDefaultAxis(orientation); |
| |
| // If old axis is default axis, delete it |
| QAbstract3DAxis *oldAxis = *axisPtr; |
| if (oldAxis) { |
| if (oldAxis->d_ptr->isDefaultAxis()) { |
| m_axes.removeAll(oldAxis); |
| delete oldAxis; |
| oldAxis = 0; |
| } else { |
| // Disconnect the old axis from use |
| QObject::disconnect(oldAxis, 0, this, 0); |
| oldAxis->d_ptr->setOrientation(QAbstract3DAxis::AxisOrientationNone); |
| } |
| } |
| |
| // Assume ownership |
| addAxis(axis); |
| |
| // Connect the new axis |
| *axisPtr = axis; |
| |
| axis->d_ptr->setOrientation(orientation); |
| |
| QObject::connect(axis, &QAbstract3DAxis::titleChanged, |
| this, &Abstract3DController::handleAxisTitleChanged); |
| QObject::connect(axis, &QAbstract3DAxis::labelsChanged, |
| this, &Abstract3DController::handleAxisLabelsChanged); |
| QObject::connect(axis, &QAbstract3DAxis::rangeChanged, |
| this, &Abstract3DController::handleAxisRangeChanged); |
| QObject::connect(axis, &QAbstract3DAxis::autoAdjustRangeChanged, |
| this, &Abstract3DController::handleAxisAutoAdjustRangeChanged); |
| QObject::connect(axis, &QAbstract3DAxis::labelAutoRotationChanged, |
| this, &Abstract3DController::handleAxisLabelAutoRotationChanged); |
| QObject::connect(axis, &QAbstract3DAxis::titleVisibilityChanged, |
| this, &Abstract3DController::handleAxisTitleVisibilityChanged); |
| QObject::connect(axis, &QAbstract3DAxis::titleFixedChanged, |
| this, &Abstract3DController::handleAxisTitleFixedChanged); |
| |
| if (orientation == QAbstract3DAxis::AxisOrientationX) |
| m_changeTracker.axisXTypeChanged = true; |
| else if (orientation == QAbstract3DAxis::AxisOrientationY) |
| m_changeTracker.axisYTypeChanged = true; |
| else if (orientation == QAbstract3DAxis::AxisOrientationZ) |
| m_changeTracker.axisZTypeChanged = true; |
| |
| handleAxisTitleChangedBySender(axis); |
| handleAxisLabelsChangedBySender(axis); |
| handleAxisRangeChangedBySender(axis); |
| handleAxisAutoAdjustRangeChangedInOrientation(axis->orientation(), |
| axis->isAutoAdjustRange()); |
| handleAxisLabelAutoRotationChangedBySender(axis); |
| handleAxisTitleVisibilityChangedBySender(axis); |
| handleAxisTitleFixedChangedBySender(axis); |
| |
| if (axis->type() & QAbstract3DAxis::AxisTypeValue) { |
| QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis); |
| QObject::connect(valueAxis, &QValue3DAxis::segmentCountChanged, |
| this, &Abstract3DController::handleAxisSegmentCountChanged); |
| QObject::connect(valueAxis, &QValue3DAxis::subSegmentCountChanged, |
| this, &Abstract3DController::handleAxisSubSegmentCountChanged); |
| QObject::connect(valueAxis, &QValue3DAxis::labelFormatChanged, |
| this, &Abstract3DController::handleAxisLabelFormatChanged); |
| QObject::connect(valueAxis, &QValue3DAxis::reversedChanged, |
| this, &Abstract3DController::handleAxisReversedChanged); |
| QObject::connect(valueAxis->dptr(), &QValue3DAxisPrivate::formatterDirty, |
| this, &Abstract3DController::handleAxisFormatterDirty); |
| |
| handleAxisSegmentCountChangedBySender(valueAxis); |
| handleAxisSubSegmentCountChangedBySender(valueAxis); |
| handleAxisLabelFormatChangedBySender(valueAxis); |
| handleAxisReversedChangedBySender(valueAxis); |
| handleAxisFormatterDirtyBySender(valueAxis->dptr()); |
| |
| valueAxis->formatter()->setLocale(m_locale); |
| } |
| } |
| |
| QAbstract3DAxis *Abstract3DController::createDefaultAxis( |
| QAbstract3DAxis::AxisOrientation orientation) |
| { |
| Q_UNUSED(orientation) |
| |
| // The default default axis is a value axis. If the graph type has a different default axis |
| // for some orientation, this function needs to be overridden. |
| QAbstract3DAxis *defaultAxis = createDefaultValueAxis(); |
| return defaultAxis; |
| } |
| |
| QValue3DAxis *Abstract3DController::createDefaultValueAxis() |
| { |
| // Default value axis has single segment, empty label format, and auto scaling |
| QValue3DAxis *defaultAxis = new QValue3DAxis; |
| defaultAxis->d_ptr->setDefaultAxis(true); |
| |
| return defaultAxis; |
| } |
| |
| QCategory3DAxis *Abstract3DController::createDefaultCategoryAxis() |
| { |
| // Default category axis has no labels |
| QCategory3DAxis *defaultAxis = new QCategory3DAxis; |
| defaultAxis->d_ptr->setDefaultAxis(true); |
| return defaultAxis; |
| } |
| |
| void Abstract3DController::startRecordingRemovesAndInserts() |
| { |
| // Default implementation does nothing |
| } |
| |
| void Abstract3DController::emitNeedRender() |
| { |
| if (!m_renderPending) { |
| emit needRender(); |
| m_renderPending = true; |
| } |
| } |
| |
| void Abstract3DController::handlePendingClick() |
| { |
| m_clickedType = m_renderer->clickedType(); |
| m_selectedLabelIndex = m_renderer->m_selectedLabelIndex; |
| m_selectedCustomItemIndex = m_renderer->m_selectedCustomItemIndex; |
| |
| // Invalidate query position to indicate the query has been handled, unless another |
| // point has been queried. |
| if (m_renderer->cachedClickQuery() == m_scene->selectionQueryPosition()) |
| m_scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint()); |
| |
| m_renderer->clearClickQueryResolved(); |
| |
| emit elementSelected(m_clickedType); |
| } |
| |
| void Abstract3DController::handlePendingGraphPositionQuery() |
| { |
| m_queriedGraphPosition = m_renderer->queriedGraphPosition(); |
| |
| // Invalidate query position to indicate the query has been handled, unless another |
| // point has been queried. |
| if (m_renderer->cachedGraphPositionQuery() == m_scene->graphPositionQuery()) |
| m_scene->setGraphPositionQuery(Q3DScene::invalidSelectionPoint()); |
| |
| m_renderer->clearGraphPositionQueryResolved(); |
| |
| emit queriedGraphPositionChanged(m_queriedGraphPosition); |
| } |
| |
| int Abstract3DController::selectedLabelIndex() const |
| { |
| int index = m_selectedLabelIndex; |
| QAbstract3DAxis *axis = selectedAxis(); |
| if (axis && axis->labels().count() <= index) |
| index = -1; |
| return index; |
| } |
| |
| QAbstract3DAxis *Abstract3DController::selectedAxis() const |
| { |
| QAbstract3DAxis *axis = 0; |
| QAbstract3DGraph::ElementType type = m_clickedType; |
| switch (type) { |
| case QAbstract3DGraph::ElementAxisXLabel: |
| axis = axisX(); |
| break; |
| case QAbstract3DGraph::ElementAxisYLabel: |
| axis = axisY(); |
| break; |
| case QAbstract3DGraph::ElementAxisZLabel: |
| axis = axisZ(); |
| break; |
| default: |
| axis = 0; |
| break; |
| } |
| |
| return axis; |
| } |
| |
| int Abstract3DController::selectedCustomItemIndex() const |
| { |
| int index = m_selectedCustomItemIndex; |
| if (m_customItems.count() <= index) |
| index = -1; |
| return index; |
| } |
| |
| QCustom3DItem *Abstract3DController::selectedCustomItem() const |
| { |
| QCustom3DItem *item = 0; |
| int index = selectedCustomItemIndex(); |
| if (index >= 0) |
| item = m_customItems[index]; |
| return item; |
| } |
| |
| QAbstract3DGraph::ElementType Abstract3DController::selectedElement() const |
| { |
| return m_clickedType; |
| } |
| |
| void Abstract3DController::setOrthoProjection(bool enable) |
| { |
| if (enable != m_useOrthoProjection) { |
| m_useOrthoProjection = enable; |
| m_changeTracker.projectionChanged = true; |
| emit orthoProjectionChanged(m_useOrthoProjection); |
| // If changed to ortho, disable shadows |
| if (m_useOrthoProjection) |
| doSetShadowQuality(QAbstract3DGraph::ShadowQualityNone); |
| emitNeedRender(); |
| } |
| } |
| |
| bool Abstract3DController::isOrthoProjection() const |
| { |
| return m_useOrthoProjection; |
| } |
| |
| void Abstract3DController::setAspectRatio(qreal ratio) |
| { |
| if (m_aspectRatio != ratio) { |
| m_aspectRatio = ratio; |
| m_changeTracker.aspectRatioChanged = true; |
| emit aspectRatioChanged(m_aspectRatio); |
| m_isDataDirty = true; |
| emitNeedRender(); |
| } |
| } |
| |
| qreal Abstract3DController::aspectRatio() |
| { |
| return m_aspectRatio; |
| } |
| |
| void Abstract3DController::setHorizontalAspectRatio(qreal ratio) |
| { |
| if (m_horizontalAspectRatio != ratio) { |
| m_horizontalAspectRatio = ratio; |
| m_changeTracker.horizontalAspectRatioChanged = true; |
| emit horizontalAspectRatioChanged(m_horizontalAspectRatio); |
| m_isDataDirty = true; |
| emitNeedRender(); |
| } |
| } |
| |
| qreal Abstract3DController::horizontalAspectRatio() const |
| { |
| return m_horizontalAspectRatio; |
| } |
| |
| void Abstract3DController::setReflection(bool enable) |
| { |
| if (m_reflectionEnabled != enable) { |
| m_reflectionEnabled = enable; |
| m_changeTracker.reflectionChanged = true; |
| emit reflectionChanged(m_reflectionEnabled); |
| emitNeedRender(); |
| } |
| } |
| |
| bool Abstract3DController::reflection() const |
| { |
| return m_reflectionEnabled; |
| } |
| |
| void Abstract3DController::setReflectivity(qreal reflectivity) |
| { |
| if (m_reflectivity != reflectivity) { |
| m_reflectivity = reflectivity; |
| m_changeTracker.reflectivityChanged = true; |
| emit reflectivityChanged(m_reflectivity); |
| emitNeedRender(); |
| } |
| } |
| |
| qreal Abstract3DController::reflectivity() const |
| { |
| return m_reflectivity; |
| } |
| |
| void Abstract3DController::setPolar(bool enable) |
| { |
| if (enable != m_isPolar) { |
| m_isPolar = enable; |
| m_changeTracker.polarChanged = true; |
| m_isDataDirty = true; |
| emit polarChanged(m_isPolar); |
| emitNeedRender(); |
| } |
| } |
| |
| bool Abstract3DController::isPolar() const |
| { |
| return m_isPolar; |
| } |
| |
| void Abstract3DController::setRadialLabelOffset(float offset) |
| { |
| if (m_radialLabelOffset != offset) { |
| m_radialLabelOffset = offset; |
| m_changeTracker.radialLabelOffsetChanged = true; |
| emit radialLabelOffsetChanged(m_radialLabelOffset); |
| emitNeedRender(); |
| } |
| } |
| |
| float Abstract3DController::radialLabelOffset() const |
| { |
| return m_radialLabelOffset; |
| } |
| |
| void Abstract3DController::setLocale(const QLocale &locale) |
| { |
| if (m_locale != locale) { |
| m_locale = locale; |
| |
| // Value axis formatters need to be updated |
| QValue3DAxis *axis = qobject_cast<QValue3DAxis *>(m_axisX); |
| if (axis) |
| axis->formatter()->setLocale(m_locale); |
| axis = qobject_cast<QValue3DAxis *>(m_axisY); |
| if (axis) |
| axis->formatter()->setLocale(m_locale); |
| axis = qobject_cast<QValue3DAxis *>(m_axisZ); |
| if (axis) |
| axis->formatter()->setLocale(m_locale); |
| emit localeChanged(m_locale); |
| } |
| } |
| |
| QLocale Abstract3DController::locale() const |
| { |
| return m_locale; |
| } |
| |
| QVector3D Abstract3DController::queriedGraphPosition() const |
| { |
| return m_queriedGraphPosition; |
| } |
| |
| void Abstract3DController::setMargin(qreal margin) |
| { |
| if (m_margin != margin) { |
| m_margin = margin; |
| m_changeTracker.marginChanged = true; |
| emit marginChanged(margin); |
| emitNeedRender(); |
| } |
| } |
| |
| qreal Abstract3DController::margin() const |
| { |
| return m_margin; |
| } |
| |
| |
| QT_END_NAMESPACE_DATAVISUALIZATION |