1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qscatter3dseries_p.h"
5#include "scatter3dcontroller_p.h"
6#include "qvalue3daxis.h"
7#include "qcategory3daxis.h"
8
9QT_BEGIN_NAMESPACE
10
11/*!
12 * \class QScatter3DSeries
13 * \inmodule QtGraphs
14 * \brief The QScatter3DSeries class represents a data series in a 3D scatter
15 * graph.
16 *
17 * This class manages the series specific visual elements, as well as the series
18 * data (via a data proxy).
19 *
20 * If no data proxy is set explicitly for the series, the series creates a default
21 * proxy. Setting another proxy will destroy the existing proxy and all data added to it.
22 *
23 * QScatter3DSeries supports the following format tags for QAbstract3DSeries::setItemLabelFormat():
24 * \table
25 * \row
26 * \li @xTitle \li Title from x-axis
27 * \row
28 * \li @yTitle \li Title from y-axis
29 * \row
30 * \li @zTitle \li Title from z-axis
31 * \row
32 * \li @xLabel \li Item value formatted using the format of the x-axis.
33 * For more information, see
34 * \l{QValue3DAxis::setLabelFormat()}.
35 * \row
36 * \li @yLabel \li Item value formatted using the format of the y-axis.
37 * For more information, see
38 * \l{QValue3DAxis::setLabelFormat()}.
39 * \row
40 * \li @zLabel \li Item value formatted using the format of the z-axis.
41 * For more information, see
42 * \l{QValue3DAxis::setLabelFormat()}.
43 * \row
44 * \li @seriesName \li Name of the series
45 * \endtable
46 *
47 * For example:
48 * \snippet doc_src_qtgraphs.cpp 1
49 *
50 * \sa {Qt Graphs Data Handling}
51 */
52
53/*!
54 * \qmltype Scatter3DSeries
55 * \inqmlmodule QtGraphs
56 * \ingroup graphs_qml
57 * \instantiates QScatter3DSeries
58 * \inherits Abstract3DSeries
59 * \brief Represents a data series in a 3D scatter graph.
60 *
61 * This type manages the series specific visual elements, as well as the series
62 * data (via a data proxy).
63 *
64 * For a more complete description, see QScatter3DSeries.
65 *
66 * \sa {Qt Graphs Data Handling}
67 */
68
69/*!
70 * \qmlproperty ScatterDataProxy Scatter3DSeries::dataProxy
71 *
72 * Sets the active data proxy. The series assumes ownership of any proxy set to
73 * it and deletes any previously set proxy when a new one is added. The proxy
74 * cannot be null or set to another series.
75 */
76
77/*!
78 * \qmlproperty int Scatter3DSeries::selectedItem
79 *
80 * The item that is selected at the index in the data array of the series.
81 * Only one item can be selected at a time.
82 * To clear selection from this series, invalidSelectionIndex is set as the index.
83 * If this series is added to a graph, the graph can adjust the selection according to user
84 * interaction or if it becomes invalid. Selecting an item on another added series will also
85 * clear the selection.
86 * Removing items from or inserting items to the series before the selected item
87 * will adjust the selection so that the same item will stay selected.
88 *
89 * \sa AbstractGraph3D::clearSelection()
90 */
91
92/*!
93 * \qmlproperty float Scatter3DSeries::itemSize
94 *
95 * Sets the item size for the series. The size must be between \c 0.0 and
96 * \c 1.0. Setting the size to \c 0.0 causes the item size to be automatically
97 * scaled based on the total number of items in all the series for the graph.
98 * The preset default is \c 0.0.
99 */
100
101/*!
102 * \qmlproperty int Scatter3DSeries::invalidSelectionIndex
103 * A constant property providing an invalid index for selection. This index is
104 * set to the selectedItem property to clear the selection from this series.
105 *
106 * \sa AbstractGraph3D::clearSelection()
107 */
108
109/*!
110 * Constructs a scatter 3D series with the parent \a parent.
111 */
112QScatter3DSeries::QScatter3DSeries(QObject *parent) :
113 QAbstract3DSeries(new QScatter3DSeriesPrivate(this), parent)
114{
115 Q_D(QScatter3DSeries);
116 // Default proxy
117 d->setDataProxy(new QScatterDataProxy);
118}
119
120/*!
121 * Constructs a scatter 3D series with the data proxy \a dataProxy and the
122 * parent \a parent.
123 */
124QScatter3DSeries::QScatter3DSeries(QScatterDataProxy *dataProxy, QObject *parent) :
125 QAbstract3DSeries(new QScatter3DSeriesPrivate(this), parent)
126{
127 Q_D(QScatter3DSeries);
128 d->setDataProxy(dataProxy);
129}
130
131/*!
132 * \internal
133 */
134QScatter3DSeries::QScatter3DSeries(QScatter3DSeriesPrivate *d, QObject *parent) :
135 QAbstract3DSeries(d, parent)
136{
137}
138
139/*!
140 * Deletes the scatter 3D series.
141 */
142QScatter3DSeries::~QScatter3DSeries()
143{
144}
145
146/*!
147 * \property QScatter3DSeries::dataProxy
148 *
149 * \brief The active data proxy.
150 */
151
152/*!
153 * Sets the active data proxy for the series to \a proxy. The series assumes
154 * ownership of any proxy set to it and deletes any previously set proxy when
155 * a new one is added. The \a proxy argument cannot be null or set to another
156 * series.
157 */
158void QScatter3DSeries::setDataProxy(QScatterDataProxy *proxy)
159{
160 Q_D(QScatter3DSeries);
161 d->setDataProxy(proxy);
162}
163
164QScatterDataProxy *QScatter3DSeries::dataProxy() const
165{
166 const Q_D(QScatter3DSeries);
167 return static_cast<QScatterDataProxy *>(d->dataProxy());
168}
169
170/*!
171 * \property QScatter3DSeries::selectedItem
172 *
173 * \brief The item that is selected in the series.
174 */
175
176/*!
177 * Selects the item at the index \a index in the data array of the series.
178 * Only one item can be selected at a time.
179 *
180 * To clear selection from this series, invalidSelectionIndex() is set as \a index.
181 * If this series is added to a graph, the graph can adjust the selection according to user
182 * interaction or if it becomes invalid. Selecting an item on another added series will also
183 * clear the selection.
184 *
185 * Removing items from or inserting items to the series before the selected item
186 * will adjust the selection so that the same item will stay selected.
187 *
188 * \sa QAbstract3DGraph::clearSelection()
189 */
190void QScatter3DSeries::setSelectedItem(int index)
191{
192 Q_D(QScatter3DSeries);
193 // Don't do this in private to avoid loops, as that is used for callback from controller.
194 if (d->m_controller)
195 static_cast<Scatter3DController *>(d->m_controller)->setSelectedItem(index, series: this);
196 else
197 d->setSelectedItem(index);
198}
199
200int QScatter3DSeries::selectedItem() const
201{
202 const Q_D(QScatter3DSeries);
203 return d->m_selectedItem;
204}
205
206/*!
207 * \property QScatter3DSeries::itemSize
208 *
209 * \brief Item size for the series.
210 *
211 * The size must be between \c 0.0f and \c 1.0f. Setting the size to \c 0.0f
212 * causes the item size to be automatically scaled based on the total number of
213 * items in all the series for the graph.
214 *
215 * The preset default is \c 0.0f.
216 */
217void QScatter3DSeries::setItemSize(float size)
218{
219 Q_D(QScatter3DSeries);
220 if (size < 0.0f || size > 1.0f) {
221 qWarning(msg: "Invalid size. Valid range for itemSize is 0.0f...1.0f");
222 } else if (size != d->m_itemSize) {
223 d->setItemSize(size);
224 emit itemSizeChanged(size);
225 }
226}
227
228float QScatter3DSeries::itemSize() const
229{
230 const Q_D(QScatter3DSeries);
231 return d->m_itemSize;
232}
233
234/*!
235 * Returns an invalid index for selection. This index is set to the selectedItem
236 * property to clear the selection from this series.
237 *
238 * \sa QAbstract3DGraph::clearSelection()
239 */
240int QScatter3DSeries::invalidSelectionIndex()
241{
242 return Scatter3DController::invalidSelectionIndex();
243}
244
245// QScatter3DSeriesPrivate
246
247QScatter3DSeriesPrivate::QScatter3DSeriesPrivate(QScatter3DSeries *q)
248 : QAbstract3DSeriesPrivate(q, QAbstract3DSeries::SeriesTypeScatter),
249 m_selectedItem(Scatter3DController::invalidSelectionIndex()),
250 m_itemSize(0.0f)
251{
252 m_itemLabelFormat = QStringLiteral("@xLabel, @yLabel, @zLabel");
253 m_mesh = QAbstract3DSeries::MeshSphere;
254}
255
256QScatter3DSeriesPrivate::~QScatter3DSeriesPrivate()
257{
258}
259
260void QScatter3DSeriesPrivate::setDataProxy(QAbstractDataProxy *proxy)
261{
262 Q_Q(QScatter3DSeries);
263 Q_ASSERT(proxy->type() == QAbstractDataProxy::DataTypeScatter);
264
265 QAbstract3DSeriesPrivate::setDataProxy(proxy);
266
267 emit q->dataProxyChanged(proxy: static_cast<QScatterDataProxy *>(proxy));
268}
269
270void QScatter3DSeriesPrivate::connectControllerAndProxy(Abstract3DController *newController)
271{
272 Q_Q(QScatter3DSeries);
273 QScatterDataProxy *scatterDataProxy = static_cast<QScatterDataProxy *>(m_dataProxy);
274
275 if (m_controller && scatterDataProxy) {
276 //Disconnect old controller/old proxy
277 QObject::disconnect(sender: scatterDataProxy, signal: 0, receiver: m_controller, member: 0);
278 QObject::disconnect(sender: q_ptr, signal: 0, receiver: m_controller, member: 0);
279 }
280
281 if (newController && scatterDataProxy) {
282 Scatter3DController *controller = static_cast<Scatter3DController *>(newController);
283 QObject::connect(sender: scatterDataProxy, signal: &QScatterDataProxy::arrayReset,
284 context: controller, slot: &Scatter3DController::handleArrayReset);
285 QObject::connect(sender: scatterDataProxy, signal: &QScatterDataProxy::itemsAdded,
286 context: controller, slot: &Scatter3DController::handleItemsAdded);
287 QObject::connect(sender: scatterDataProxy, signal: &QScatterDataProxy::itemsChanged,
288 context: controller, slot: &Scatter3DController::handleItemsChanged);
289 QObject::connect(sender: scatterDataProxy, signal: &QScatterDataProxy::itemsRemoved,
290 context: controller, slot: &Scatter3DController::handleItemsRemoved);
291 QObject::connect(sender: scatterDataProxy, signal: &QScatterDataProxy::itemsInserted,
292 context: controller, slot: &Scatter3DController::handleItemsInserted);
293 QObject::connect(sender: q, signal: &QScatter3DSeries::dataProxyChanged,
294 context: controller, slot: &Scatter3DController::handleArrayReset);
295 }
296}
297
298void QScatter3DSeriesPrivate::createItemLabel()
299{
300 Q_Q(QScatter3DSeries);
301 static const QString xTitleTag(QStringLiteral("@xTitle"));
302 static const QString yTitleTag(QStringLiteral("@yTitle"));
303 static const QString zTitleTag(QStringLiteral("@zTitle"));
304 static const QString xLabelTag(QStringLiteral("@xLabel"));
305 static const QString yLabelTag(QStringLiteral("@yLabel"));
306 static const QString zLabelTag(QStringLiteral("@zLabel"));
307 static const QString seriesNameTag(QStringLiteral("@seriesName"));
308
309 if (m_selectedItem == QScatter3DSeries::invalidSelectionIndex()) {
310 m_itemLabel = QString();
311 return;
312 }
313
314 QValue3DAxis *axisX = static_cast<QValue3DAxis *>(m_controller->axisX());
315 QValue3DAxis *axisY = static_cast<QValue3DAxis *>(m_controller->axisY());
316 QValue3DAxis *axisZ = static_cast<QValue3DAxis *>(m_controller->axisZ());
317 QVector3D selectedPosition = q->dataProxy()->itemAt(index: m_selectedItem)->position();
318
319 m_itemLabel = m_itemLabelFormat;
320
321 m_itemLabel.replace(before: xTitleTag, after: axisX->title());
322 m_itemLabel.replace(before: yTitleTag, after: axisY->title());
323 m_itemLabel.replace(before: zTitleTag, after: axisZ->title());
324
325 if (m_itemLabel.contains(s: xLabelTag)) {
326 QString valueLabelText = axisX->formatter()->stringForValue(
327 value: qreal(selectedPosition.x()), format: axisX->labelFormat());
328 m_itemLabel.replace(before: xLabelTag, after: valueLabelText);
329 }
330 if (m_itemLabel.contains(s: yLabelTag)) {
331 QString valueLabelText = axisY->formatter()->stringForValue(
332 value: qreal(selectedPosition.y()), format: axisY->labelFormat());
333 m_itemLabel.replace(before: yLabelTag, after: valueLabelText);
334 }
335 if (m_itemLabel.contains(s: zLabelTag)) {
336 QString valueLabelText = axisZ->formatter()->stringForValue(
337 value: qreal(selectedPosition.z()), format: axisZ->labelFormat());
338 m_itemLabel.replace(before: zLabelTag, after: valueLabelText);
339 }
340 m_itemLabel.replace(before: seriesNameTag, after: m_name);
341}
342
343void QScatter3DSeriesPrivate::setSelectedItem(int index)
344{
345 Q_Q(QScatter3DSeries);
346 if (index != m_selectedItem) {
347 markItemLabelDirty();
348 m_selectedItem = index;
349 emit q->selectedItemChanged(index: m_selectedItem);
350 }
351}
352
353void QScatter3DSeriesPrivate::setItemSize(float size)
354{
355 m_itemSize = size;
356 if (m_controller)
357 m_controller->markSeriesVisualsDirty();
358}
359
360QT_END_NAMESPACE
361

source code of qtgraphs/src/graphs/data/qscatter3dseries.cpp