Revision control

++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qgraphs3dlogging_p.h"
++ #include "qquickgraphssurfacenode_p.h"
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ * \qmltype Surface3DNode
++ * \inherits GraphsNode
++ * \inqmlmodule QtGraphs
++ * \ingroup graphs_qml_3D
++ * \brief Describes the usage of the 3D surface graph node.
++ *
++ * This type enables developers to render a surface graph node in 3D with Qt Quick.
++ *
++ * You will need to import the Qt Graphs module to use this type:
++ *
++ * \snippet doc_src_qmlnodes.qml 0
++ *
++ * After that you can use Surface3DNode in your qml files:
++ *
++ * \snippet doc_src_qmlnodes.qml 3
++ *
++ *
++ * \sa Surface3DSeries, ItemModelSurfaceDataProxy, Bars3DNode, Scatter3DNode,
++ * {Qt Graphs C++ Classes for 3D}
++ */
++
++ /*!
++ * \qmlproperty Value3DAxis Surface3DNode::axisX
++ * The active x-axis.
++ *
++ * If an axis is not given, a temporary default axis with no labels and an
++ * automatically adjusting range is created.
++ * This temporary axis is destroyed if another axis is explicitly set to the
++ * same orientation.
++ */
++
++ /*!
++ * \qmlproperty Value3DAxis Surface3DNode::axisY
++ * The active y-axis.
++ *
++ * If an axis is not given, a temporary default axis with no labels and an
++ * automatically adjusting range is created.
++ * This temporary axis is destroyed if another axis is explicitly set to the
++ * same orientation.
++ */
++
++ /*!
++ * \qmlproperty Value3DAxis Surface3DNode::axisZ
++ * The active z-axis.
++ *
++ * If an axis is not given, a temporary default axis with no labels and an
++ * automatically adjusting range is created.
++ * This temporary axis is destroyed if another axis is explicitly set to the
++ * same orientation.
++ */
++
++ /*!
++ * \qmlproperty Surface3DSeries Surface3DNode::selectedSeries
++ * \readonly
++ *
++ * The selected series or null. If \l {GraphsNode::selectionMode}{selectionMode}
++ * has the \c MultiSeries flag set, this property holds the series
++ * which owns the selected point.
++ */
++
++ /*!
++ * \qmlproperty list<Surface3DSeries> Surface3DNode::seriesList
++ * \qmldefault
++ * This property holds the series of the graph.
++ * By default, this property contains an empty list.
++ * To set the series, either use the addSeries() function or define them as
++ * children of the graph.
++ */
++
++ /*!
++ * \qmlproperty bool Surface3DNode::flipHorizontalGrid
++ *
++ * In some use cases the horizontal axis grid is mostly covered by the surface,
++ * so it can be more useful to display the horizontal axis grid on top of the
++ * graph rather than on the bottom. A typical use case for this is showing 2D
++ * spectrograms using orthoGraphic projection with a top-down viewpoint.
++ *
++ * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal
++ * background of the graph.
++ * If \c{true}, the horizontal axis grid and labels are drawn on the opposite
++ * side of the graph from the horizontal background.
++ * Defaults to \c{false}.
++ */
++
++ /*!
++ * \qmlmethod void Surface3DNode::addSeries(Surface3DSeries series)
++ * Adds the \a series to the graph.
++ * \sa GraphsNode::hasSeries()
++ */
++
++ /*!
++ * \qmlmethod void Surface3DNode::removeSeries(Surface3DSeries series)
++ * Removes the \a series from the graph.
++ * \sa GraphsNode::hasSeries()
++ */
++
++ /*!
++ * \qmlsignal Surface3DNode::axisXChanged(ValueAxis3D axis)
++ *
++ * This signal is emitted when axisX changes to \a axis.
++ */
++
++ /*!
++ * \qmlsignal Surface3DNode::axisYChanged(ValueAxis3D axis)
++ *
++ * This signal is emitted when axisY changes to \a axis.
++ */
++
++ /*!
++ * \qmlsignal Surface3DNode::axisZChanged(ValueAxis3D axis)
++ *
++ * This signal is emitted when axisZ changes to \a axis.
++ */
++
++ /*!
++ * \qmlsignal Surface3DNode::selectedSeriesChanged(Surface3DSeries series)
++ *
++ * This signal is emitted when selectedSeries changes to \a series.
++ */
++
++ /*!
++ * \qmlsignal Surface3DNode::flipHorizontalGridChanged(bool flip)
++ *
++ * This signal is emitted when flipHorizontalGrid changes to \a flip.
++ */
++
++ QQuickGraphsSurfaceNode::QQuickGraphsSurfaceNode(QQuick3DNode *parent)
++ : QQuickGraphsNode(parent)
++ , m_flipHorizontalGrid(false)
++ {}
++
++ QQuickGraphsSurfaceNode::~QQuickGraphsSurfaceNode() {}
++
++ void QQuickGraphsSurfaceNode::componentComplete()
++ {
++ const QString qmlData = QLatin1StringView(R"QML(
++ import QtQuick;
++ import QtGraphs;
++
++ Surface3D{}
++ )QML");
++
++ QQmlComponent *component = new QQmlComponent(qmlEngine(this), this);
++ component->setData(qmlData.toUtf8(), QUrl());
++ m_graph.reset(qobject_cast<QQuickGraphsItem *>(component->create()));
++ setGraphParent();
++ QQuickGraphsNode::componentComplete();
++
++ //initialize components
++
++ if (m_axisX)
++ graphSurface()->setAxisX(static_cast<QValue3DAxis *>(m_axisX));
++ if (m_axisY)
++ graphSurface()->setAxisY(static_cast<QValue3DAxis *>(m_axisY));
++ if (m_axisZ)
++ graphSurface()->setAxisZ(static_cast<QValue3DAxis *>(m_axisZ));
++
++ graphSurface()->setSelectionMode(m_selectionMode);
++ graphSurface()->setFlipHorizontalGrid(m_flipHorizontalGrid);
++
++ for (auto series : std::as_const(m_seriesList))
++ graphSurface()->addSeries(static_cast<QSurface3DSeries *>(series));
++
++ //connect signals
++ connect(graphSurface(),
++ &QQuickGraphsSurface::axisXChanged,
++ this,
++ &QQuickGraphsSurfaceNode::axisXChanged);
++ connect(graphSurface(),
++ &QQuickGraphsSurface::axisYChanged,
++ this,
++ &QQuickGraphsSurfaceNode::axisYChanged);
++ connect(graphSurface(),
++ &QQuickGraphsSurface::axisZChanged,
++ this,
++ &QQuickGraphsSurfaceNode::axisZChanged);
++ connect(graphSurface(),
++ &QQuickGraphsSurface::flipHorizontalGridChanged,
++ this,
++ &QQuickGraphsSurfaceNode::flipHorizontalGridChanged);
++ }
++
++ QValue3DAxis *QQuickGraphsSurfaceNode::axisX() const
++ {
++ if (graphSurface())
++ return graphSurface()->axisX();
++ else
++ return static_cast<QValue3DAxis *>(m_axisX);
++ }
++
++ void QQuickGraphsSurfaceNode::setAxisX(QValue3DAxis *axis)
++ {
++ if (m_axisX == axis) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << axis;
++ return;
++ }
++ m_axisX = axis;
++ if (graphSurface())
++ graphSurface()->setAxisX(axis);
++ }
++
++ QValue3DAxis *QQuickGraphsSurfaceNode::axisY() const
++ {
++ if (graphSurface())
++ return graphSurface()->axisY();
++ else
++ return static_cast<QValue3DAxis *>(m_axisY);
++ }
++ void QQuickGraphsSurfaceNode::setAxisY(QValue3DAxis *axis)
++ {
++ if (m_axisY == axis) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << axis;
++ return;
++ }
++ m_axisY = axis;
++ if (graphSurface())
++ graphSurface()->setAxisY(axis);
++ }
++
++ QValue3DAxis *QQuickGraphsSurfaceNode::axisZ() const
++ {
++ if (graphSurface())
++ return graphSurface()->axisZ();
++ else
++ return static_cast<QValue3DAxis *>(m_axisZ);
++ }
++ void QQuickGraphsSurfaceNode::setAxisZ(QValue3DAxis *axis)
++ {
++ if (m_axisZ == axis) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << axis;
++ return;
++ }
++ m_axisZ = axis;
++ if (graphSurface())
++ graphSurface()->setAxisZ(axis);
++ }
++
++ QSurface3DSeries *QQuickGraphsSurfaceNode::selectedSeries() const
++ {
++ if (graphSurface())
++ return graphSurface()->selectedSeries();
++ else
++ return nullptr;
++ }
++
++ void QQuickGraphsSurfaceNode::setSelectionMode(QtGraphs3D::SelectionFlags mode)
++ {
++ if (graphSurface())
++ graphSurface()->setSelectionMode(mode);
++ else
++ setSelectionMode(mode);
++ }
++
++ bool QQuickGraphsSurfaceNode::flipHorizontalGrid() const
++ {
++ if (graphSurface())
++ return graphSurface()->flipHorizontalGrid();
++ else
++ return m_flipHorizontalGrid;
++ }
++ void QQuickGraphsSurfaceNode::setFlipHorizontalGrid(bool flip)
++ {
++ if (m_flipHorizontalGrid == flip) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << flip;
++ return;
++ }
++ m_flipHorizontalGrid = flip;
++ if (graphSurface())
++ graphSurface()->setFlipHorizontalGrid(flip);
++ }
++
++ QList<QSurface3DSeries *> QQuickGraphsSurfaceNode::surfaceSeriesList()
++ {
++ if (graphSurface()) {
++ return graphSurface()->surfaceSeriesList();
++ } else {
++ QList<QSurface3DSeries *> surfaceSeriesList;
++ for (QAbstract3DSeries *abstractSeries : std::as_const(m_seriesList)) {
++ QSurface3DSeries *surfaceSeries = qobject_cast<QSurface3DSeries *>(abstractSeries);
++ if (surfaceSeries)
++ surfaceSeriesList.append(surfaceSeries);
++ }
++ return surfaceSeriesList;
++ }
++ }
++
++ QQmlListProperty<QSurface3DSeries> QQuickGraphsSurfaceNode::seriesList()
++ {
++ if (graphSurface()) {
++ return graphSurface()->seriesList();
++ } else {
++ return QQmlListProperty<QSurface3DSeries>(this,
++ this,
++ &QQuickGraphsSurfaceNode::appendSeriesFunc,
++ &QQuickGraphsSurfaceNode::countSeriesFunc,
++ &QQuickGraphsSurfaceNode::atSeriesFunc,
++ &QQuickGraphsSurfaceNode::clearSeriesFunc);
++ }
++ }
++
++ void QQuickGraphsSurfaceNode::appendSeriesFunc(QQmlListProperty<QSurface3DSeries> *list,
++ QSurface3DSeries *series)
++ {
++ reinterpret_cast<QQuickGraphsSurfaceNode *>(list->data)->addSeries(series);
++ }
++
++ qsizetype QQuickGraphsSurfaceNode::countSeriesFunc(QQmlListProperty<QSurface3DSeries> *list)
++ {
++ return reinterpret_cast<QQuickGraphsSurfaceNode *>(list->data)->surfaceSeriesList().size();
++ }
++
++ QSurface3DSeries *QQuickGraphsSurfaceNode::atSeriesFunc(QQmlListProperty<QSurface3DSeries> *list,
++ qsizetype index)
++ {
++ return reinterpret_cast<QQuickGraphsSurfaceNode *>(list->data)->surfaceSeriesList().at(index);
++ }
++
++ void QQuickGraphsSurfaceNode::clearSeriesFunc(QQmlListProperty<QSurface3DSeries> *list)
++ {
++ QQuickGraphsSurfaceNode *declSurfaceNode = reinterpret_cast<QQuickGraphsSurfaceNode *>(
++ list->data);
++ QList<QSurface3DSeries *> realList = declSurfaceNode->surfaceSeriesList();
++ qsizetype count = realList.size();
++ for (qsizetype i = 0; i < count; i++)
++ declSurfaceNode->removeSeries(realList.at(i));
++ }
++
++ void QQuickGraphsSurfaceNode::addSeries(QSurface3DSeries *series)
++ {
++ Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesType::Surface);
++
++ if (graphSurface())
++ graphSurface()->addSeries(series);
++
++ QQuickGraphsNode::addSeriesInternal(series);
++ }
++
++ void QQuickGraphsSurfaceNode::removeSeries(QSurface3DSeries *series)
++ {
++ if (graphSurface())
++ graphSurface()->removeSeries(series);
++ QQuickGraphsNode::removeSeriesInternal(series);
++ }
++
++ void QQuickGraphsSurfaceNode::clearSelection()
++ {
++ if (graphSurface())
++ graphSurface()->clearSelection();
++ }
++
++ bool QQuickGraphsSurfaceNode::doPicking(QPointF point)
++ {
++ if (graphSurface())
++ return graphSurface()->doPicking(point);
++ else
++ return false;
++ }
++
++ bool QQuickGraphsSurfaceNode::doRayPicking(const QVector3D &origin, const QVector3D &direction)
++ {
++ if (graphSurface())
++ return graphSurface()->doRayPicking(origin, direction);
++ else
++ return false;
++ }
++ /*!
++ * \internal
++ */
++ QQuickGraphsSurface *QQuickGraphsSurfaceNode::graphSurface()
++ {
++ return static_cast<QQuickGraphsSurface *>(m_graph.get());
++ }
++
++ /*!
++ * \internal
++ */
++ const QQuickGraphsSurface *QQuickGraphsSurfaceNode::graphSurface() const
++ {
++ return static_cast<QQuickGraphsSurface *>(m_graph.get());
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qquickgraphssurfacenode_p.cpp"