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

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