1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3#include "qquickgraphssurfacenode_p.h"
4
5QT_BEGIN_NAMESPACE
6
7/*!
8 * \qmltype Surface3DNode
9 * \inherits GraphsNode
10 * \inqmlmodule QtGraphs
11 * \ingroup graphs_qml_3D
12 * \brief Describes the usage of the 3D surface graph node.
13 *
14 * This type enables developers to render a surface graph node in 3D with Qt Quick.
15 *
16 * You will need to import the Qt Graphs module to use this type:
17 *
18 * \snippet doc_src_qmlnodes.qml 0
19 *
20 * After that you can use Surface3DNode in your qml files:
21 *
22 * \snippet doc_src_qmlnodes.qml 3
23 *
24 *
25 * \sa Surface3DSeries, ItemModelSurfaceDataProxy, Bars3DNode, Scatter3DNode,
26 * {Qt Graphs C++ Classes for 3D}
27 */
28
29/*!
30 * \qmlproperty Value3DAxis Surface3DNode::axisX
31 * The active x-axis.
32 *
33 * If an axis is not given, a temporary default axis with no labels and an
34 * automatically adjusting range is created.
35 * This temporary axis is destroyed if another axis is explicitly set to the
36 * same orientation.
37 */
38
39/*!
40 * \qmlproperty Value3DAxis Surface3DNode::axisY
41 * The active y-axis.
42 *
43 * If an axis is not given, a temporary default axis with no labels and an
44 * automatically adjusting range is created.
45 * This temporary axis is destroyed if another axis is explicitly set to the
46 * same orientation.
47 */
48
49/*!
50 * \qmlproperty Value3DAxis Surface3DNode::axisZ
51 * The active z-axis.
52 *
53 * If an axis is not given, a temporary default axis with no labels and an
54 * automatically adjusting range is created.
55 * This temporary axis is destroyed if another axis is explicitly set to the
56 * same orientation.
57 */
58
59/*!
60 * \qmlproperty Surface3DSeries Surface3DNode::selectedSeries
61 * \readonly
62 *
63 * The selected series or null. If \l {GraphsNode::selectionMode}{selectionMode}
64 * has the \c MultiSeries flag set, this property holds the series
65 * which owns the selected point.
66 */
67
68/*!
69 * \qmlproperty list<Surface3DSeries> Surface3DNode::seriesList
70 * \qmldefault
71 * This property holds the series of the graph.
72 * By default, this property contains an empty list.
73 * To set the series, either use the addSeries() function or define them as
74 * children of the graph.
75 */
76
77/*!
78 * \qmlproperty bool Surface3DNode::flipHorizontalGrid
79 *
80 * In some use cases the horizontal axis grid is mostly covered by the surface,
81 * so it can be more useful to display the horizontal axis grid on top of the
82 * graph rather than on the bottom. A typical use case for this is showing 2D
83 * spectrograms using orthoGraphic projection with a top-down viewpoint.
84 *
85 * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal
86 * background of the graph.
87 * If \c{true}, the horizontal axis grid and labels are drawn on the opposite
88 * side of the graph from the horizontal background.
89 * Defaults to \c{false}.
90 */
91
92/*!
93 * \qmlmethod void Surface3DNode::addSeries(Surface3DSeries series)
94 * Adds the \a series to the graph.
95 * \sa GraphsNode::hasSeries()
96 */
97
98/*!
99 * \qmlmethod void Surface3DNode::removeSeries(Surface3DSeries series)
100 * Removes the \a series from the graph.
101 * \sa GraphsNode::hasSeries()
102 */
103
104/*!
105 * \qmlsignal Surface3DNode::axisXChanged(ValueAxis3D axis)
106 *
107 * This signal is emitted when axisX changes to \a axis.
108 */
109
110/*!
111 * \qmlsignal Surface3DNode::axisYChanged(ValueAxis3D axis)
112 *
113 * This signal is emitted when axisY changes to \a axis.
114 */
115
116/*!
117 * \qmlsignal Surface3DNode::axisZChanged(ValueAxis3D axis)
118 *
119 * This signal is emitted when axisZ changes to \a axis.
120 */
121
122/*!
123 * \qmlsignal Surface3DNode::selectedSeriesChanged(Surface3DSeries series)
124 *
125 * This signal is emitted when selectedSeries changes to \a series.
126 */
127
128/*!
129 * \qmlsignal Surface3DNode::flipHorizontalGridChanged(bool flip)
130 *
131 * This signal is emitted when flipHorizontalGrid changes to \a flip.
132 */
133
134QQuickGraphsSurfaceNode::QQuickGraphsSurfaceNode(QQuick3DNode *parent)
135 : QQuickGraphsNode(parent)
136 , m_flipHorizontalGrid(false)
137{}
138
139QQuickGraphsSurfaceNode::~QQuickGraphsSurfaceNode() {}
140
141void QQuickGraphsSurfaceNode::componentComplete()
142{
143 const QString qmlData = QLatin1StringView(R"QML(
144 import QtQuick;
145 import QtGraphs;
146
147 Surface3D{}
148 )QML");
149
150 QQmlComponent *component = new QQmlComponent(qmlEngine(this), this);
151 component->setData(qmlData.toUtf8(), baseUrl: QUrl());
152 m_graph.reset(p: qobject_cast<QQuickGraphsItem *>(object: component->create()));
153 setGraphParent();
154 QQuickGraphsNode::componentComplete();
155
156 //initialize components
157
158 if (m_axisX)
159 graphSurface()->setAxisX(static_cast<QValue3DAxis *>(m_axisX));
160 if (m_axisY)
161 graphSurface()->setAxisY(static_cast<QValue3DAxis *>(m_axisY));
162 if (m_axisZ)
163 graphSurface()->setAxisZ(static_cast<QValue3DAxis *>(m_axisZ));
164
165 graphSurface()->setSelectionMode(m_selectionMode);
166 graphSurface()->setFlipHorizontalGrid(m_flipHorizontalGrid);
167
168 for (auto series : std::as_const(t&: m_seriesList))
169 graphSurface()->addSeries(series: static_cast<QSurface3DSeries *>(series));
170
171 //connect signals
172 connect(sender: graphSurface(),
173 signal: &QQuickGraphsSurface::axisXChanged,
174 context: this,
175 slot: &QQuickGraphsSurfaceNode::axisXChanged);
176 connect(sender: graphSurface(),
177 signal: &QQuickGraphsSurface::axisYChanged,
178 context: this,
179 slot: &QQuickGraphsSurfaceNode::axisYChanged);
180 connect(sender: graphSurface(),
181 signal: &QQuickGraphsSurface::axisZChanged,
182 context: this,
183 slot: &QQuickGraphsSurfaceNode::axisZChanged);
184 connect(sender: graphSurface(),
185 signal: &QQuickGraphsSurface::flipHorizontalGridChanged,
186 context: this,
187 slot: &QQuickGraphsSurfaceNode::flipHorizontalGridChanged);
188}
189
190QValue3DAxis *QQuickGraphsSurfaceNode::axisX() const
191{
192 if (graphSurface())
193 return graphSurface()->axisX();
194 else
195 return static_cast<QValue3DAxis *>(m_axisX);
196}
197
198void QQuickGraphsSurfaceNode::setAxisX(QValue3DAxis *axis)
199{
200 if (m_axisX == axis)
201 return;
202 m_axisX = axis;
203 if (graphSurface())
204 graphSurface()->setAxisX(axis);
205}
206
207QValue3DAxis *QQuickGraphsSurfaceNode::axisY() const
208{
209 if (graphSurface())
210 return graphSurface()->axisY();
211 else
212 return static_cast<QValue3DAxis *>(m_axisY);
213}
214void QQuickGraphsSurfaceNode::setAxisY(QValue3DAxis *axis)
215{
216 if (m_axisY == axis)
217 return;
218 m_axisY = axis;
219 if (graphSurface())
220 graphSurface()->setAxisY(axis);
221}
222
223QValue3DAxis *QQuickGraphsSurfaceNode::axisZ() const
224{
225 if (graphSurface())
226 return graphSurface()->axisZ();
227 else
228 return static_cast<QValue3DAxis *>(m_axisZ);
229}
230void QQuickGraphsSurfaceNode::setAxisZ(QValue3DAxis *axis)
231{
232 if (m_axisZ == axis)
233 return;
234 m_axisZ = axis;
235 if (graphSurface())
236 graphSurface()->setAxisZ(axis);
237}
238
239QSurface3DSeries *QQuickGraphsSurfaceNode::selectedSeries() const
240{
241 if (graphSurface())
242 return graphSurface()->selectedSeries();
243 else
244 return nullptr;
245}
246
247void QQuickGraphsSurfaceNode::setSelectionMode(QtGraphs3D::SelectionFlags mode)
248{
249 if (graphSurface())
250 graphSurface()->setSelectionMode(mode);
251 else
252 setSelectionMode(mode);
253}
254
255bool QQuickGraphsSurfaceNode::flipHorizontalGrid() const
256{
257 if (graphSurface())
258 return graphSurface()->flipHorizontalGrid();
259 else
260 return m_flipHorizontalGrid;
261}
262void QQuickGraphsSurfaceNode::setFlipHorizontalGrid(bool flip)
263{
264 if (m_flipHorizontalGrid == flip)
265 return;
266 m_flipHorizontalGrid = flip;
267 if (graphSurface())
268 graphSurface()->setFlipHorizontalGrid(flip);
269}
270
271QList<QSurface3DSeries *> QQuickGraphsSurfaceNode::surfaceSeriesList()
272{
273 if (graphSurface()) {
274 return graphSurface()->surfaceSeriesList();
275 } else {
276 QList<QSurface3DSeries *> surfaceSeriesList;
277 for (QAbstract3DSeries *abstractSeries : std::as_const(t&: m_seriesList)) {
278 QSurface3DSeries *surfaceSeries = qobject_cast<QSurface3DSeries *>(object: abstractSeries);
279 if (surfaceSeries)
280 surfaceSeriesList.append(t: surfaceSeries);
281 }
282 return surfaceSeriesList;
283 }
284}
285
286QQmlListProperty<QSurface3DSeries> QQuickGraphsSurfaceNode::seriesList()
287{
288 if (graphSurface()) {
289 return graphSurface()->seriesList();
290 } else {
291 return QQmlListProperty<QSurface3DSeries>(this,
292 this,
293 &QQuickGraphsSurfaceNode::appendSeriesFunc,
294 &QQuickGraphsSurfaceNode::countSeriesFunc,
295 &QQuickGraphsSurfaceNode::atSeriesFunc,
296 &QQuickGraphsSurfaceNode::clearSeriesFunc);
297 }
298}
299
300void QQuickGraphsSurfaceNode::appendSeriesFunc(QQmlListProperty<QSurface3DSeries> *list,
301 QSurface3DSeries *series)
302{
303 reinterpret_cast<QQuickGraphsSurfaceNode *>(list->data)->addSeries(series);
304}
305
306qsizetype QQuickGraphsSurfaceNode::countSeriesFunc(QQmlListProperty<QSurface3DSeries> *list)
307{
308 return reinterpret_cast<QQuickGraphsSurfaceNode *>(list->data)->surfaceSeriesList().size();
309}
310
311QSurface3DSeries *QQuickGraphsSurfaceNode::atSeriesFunc(QQmlListProperty<QSurface3DSeries> *list,
312 qsizetype index)
313{
314 return reinterpret_cast<QQuickGraphsSurfaceNode *>(list->data)->surfaceSeriesList().at(i: index);
315}
316
317void QQuickGraphsSurfaceNode::clearSeriesFunc(QQmlListProperty<QSurface3DSeries> *list)
318{
319 QQuickGraphsSurfaceNode *declSurfaceNode = reinterpret_cast<QQuickGraphsSurfaceNode *>(
320 list->data);
321 QList<QSurface3DSeries *> realList = declSurfaceNode->surfaceSeriesList();
322 qsizetype count = realList.size();
323 for (qsizetype i = 0; i < count; i++)
324 declSurfaceNode->removeSeries(series: realList.at(i));
325}
326
327void QQuickGraphsSurfaceNode::addSeries(QSurface3DSeries *series)
328{
329 Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesType::Surface);
330
331 if (graphSurface())
332 graphSurface()->addSeries(series);
333
334 QQuickGraphsNode::addSeriesInternal(series);
335}
336
337void QQuickGraphsSurfaceNode::removeSeries(QSurface3DSeries *series)
338{
339 if (graphSurface())
340 graphSurface()->removeSeries(series);
341 QQuickGraphsNode::removeSeriesInternal(series);
342}
343
344void QQuickGraphsSurfaceNode::clearSelection()
345{
346 if (graphSurface())
347 graphSurface()->clearSelection();
348}
349
350bool QQuickGraphsSurfaceNode::doPicking(QPointF point)
351{
352 if (graphSurface())
353 return graphSurface()->doPicking(position: point);
354 else
355 return false;
356}
357
358bool QQuickGraphsSurfaceNode::doRayPicking(const QVector3D &origin, const QVector3D &direction)
359{
360 if (graphSurface())
361 return graphSurface()->doRayPicking(origin, direction);
362 else
363 return false;
364}
365/*!
366 * \internal
367 */
368QQuickGraphsSurface *QQuickGraphsSurfaceNode::graphSurface()
369{
370 return static_cast<QQuickGraphsSurface *>(m_graph.get());
371}
372
373/*!
374 * \internal
375 */
376const QQuickGraphsSurface *QQuickGraphsSurfaceNode::graphSurface() const
377{
378 return static_cast<QQuickGraphsSurface *>(m_graph.get());
379}
380
381QT_END_NAMESPACE
382

source code of qtgraphs/src/graphs3d/qml/qquickgraphssurfacenode.cpp