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

source code of qtdatavis3d/src/datavisualization/data/qscatter3dseries.cpp