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

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