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 "qscatterdataproxy_p.h"
31#include "qscatter3dseries_p.h"
32#include "qabstract3daxis_p.h"
33
34QT_BEGIN_NAMESPACE_DATAVISUALIZATION
35
36/*!
37 * \class QScatterDataProxy
38 * \inmodule QtDataVisualization
39 * \brief The QScatterDataProxy class is the data proxy for 3D scatter graphs.
40 * \since QtDataVisualization 1.0
41 *
42 * A scatter data proxy handles adding, inserting, changing, and removing data
43 * items.
44 *
45 * QScatterDataProxy takes ownership of all
46 * QtDataVisualization::QScatterDataArray and QScatterDataItem objects passed to
47 * it.
48 *
49 * \sa {Qt Data Visualization Data Handling}
50 */
51
52/*!
53 * \typedef QScatterDataArray
54 * \relates QScatterDataProxy
55 *
56 * A vector of \l {QScatterDataItem} objects.
57 */
58
59/*!
60 * \qmltype ScatterDataProxy
61 * \inqmlmodule QtDataVisualization
62 * \since QtDataVisualization 1.0
63 * \ingroup datavisualization_qml
64 * \instantiates QScatterDataProxy
65 * \inherits AbstractDataProxy
66 * \brief The data proxy for 3D scatter graphs.
67 *
68 * This type handles adding, inserting, changing, and removing data items.
69 *
70 * This type is uncreatable, but contains properties that are exposed via subtypes.
71 *
72 * \sa ItemModelScatterDataProxy, {Qt Data Visualization Data Handling}
73 */
74
75/*!
76 * \qmlproperty int ScatterDataProxy::itemCount
77 * The number of items in the array.
78 */
79
80/*!
81 * \qmlproperty Scatter3DSeries ScatterDataProxy::series
82 *
83 * The series this proxy is attached to.
84 */
85
86/*!
87 * Constructs QScatterDataProxy with the given \a parent.
88 */
89QScatterDataProxy::QScatterDataProxy(QObject *parent) :
90 QAbstractDataProxy(new QScatterDataProxyPrivate(this), parent)
91{
92}
93
94/*!
95 * \internal
96 */
97QScatterDataProxy::QScatterDataProxy(QScatterDataProxyPrivate *d, QObject *parent) :
98 QAbstractDataProxy(d, parent)
99{
100}
101
102/*!
103 * Deletes the scatter data proxy.
104 */
105QScatterDataProxy::~QScatterDataProxy()
106{
107}
108
109/*!
110 * \property QScatterDataProxy::series
111 *
112 * \brief The series this proxy is attached to.
113 */
114QScatter3DSeries *QScatterDataProxy::series() const
115{
116 return static_cast<QScatter3DSeries *>(d_ptr->series());
117}
118
119/*!
120 * Takes ownership of the array \a newArray. Clears the existing array if the
121 * new array differs from it. If the arrays are the same, this function
122 * just triggers the arrayReset() signal.
123 *
124 * Passing a null array deletes the old array and creates a new empty array.
125 */
126void QScatterDataProxy::resetArray(QScatterDataArray *newArray)
127{
128 if (dptr()->m_dataArray != newArray)
129 dptr()->resetArray(newArray);
130
131 emit arrayReset();
132 emit itemCountChanged(count: itemCount());
133}
134
135/*!
136 * Replaces the item at the position \a index with the item \a item.
137 */
138void QScatterDataProxy::setItem(int index, const QScatterDataItem &item)
139{
140 dptr()->setItem(index, item);
141 emit itemsChanged(startIndex: index, count: 1);
142}
143
144/*!
145 * Replaces the items starting from the position \a index with the items
146 * specified by \a items.
147 */
148void QScatterDataProxy::setItems(int index, const QScatterDataArray &items)
149{
150 dptr()->setItems(index, items);
151 emit itemsChanged(startIndex: index, count: items.size());
152}
153
154/*!
155 * Adds the item \a item to the end of the array.
156 *
157 * Returns the index of the added item.
158 */
159int QScatterDataProxy::addItem(const QScatterDataItem &item)
160{
161 int addIndex = dptr()->addItem(item);
162 emit itemsAdded(startIndex: addIndex, count: 1);
163 emit itemCountChanged(count: itemCount());
164 return addIndex;
165}
166
167/*!
168 * Adds the items specified by \a items to the end of the array.
169 *
170 * Returns the index of the first added item.
171 */
172int QScatterDataProxy::addItems(const QScatterDataArray &items)
173{
174 int addIndex = dptr()->addItems(items);
175 emit itemsAdded(startIndex: addIndex, count: items.size());
176 emit itemCountChanged(count: itemCount());
177 return addIndex;
178}
179
180/*!
181 * Inserts the item \a item to the position \a index. If the index is equal to
182 * the data array size, the item is added to the array.
183 */
184void QScatterDataProxy::insertItem(int index, const QScatterDataItem &item)
185{
186 dptr()->insertItem(index, item);
187 emit itemsInserted(startIndex: index, count: 1);
188 emit itemCountChanged(count: itemCount());
189}
190
191/*!
192 * Inserts the items specified by \a items to the position \a index. If the
193 * index is equal to data array size, the items are added to the array.
194 */
195void QScatterDataProxy::insertItems(int index, const QScatterDataArray &items)
196{
197 dptr()->insertItems(index, items);
198 emit itemsInserted(startIndex: index, count: items.size());
199 emit itemCountChanged(count: itemCount());
200}
201
202/*!
203 * Removes the number of items specified by \a removeCount starting at the
204 * position \a index. Attempting to remove items past the end of
205 * the array does nothing.
206 */
207void QScatterDataProxy::removeItems(int index, int removeCount)
208{
209 if (index >= dptr()->m_dataArray->size())
210 return;
211
212 dptr()->removeItems(index, removeCount);
213 emit itemsRemoved(startIndex: index, count: removeCount);
214 emit itemCountChanged(count: itemCount());
215}
216
217/*!
218 * \property QScatterDataProxy::itemCount
219 *
220 * \brief The number of items in the array.
221 */
222int QScatterDataProxy::itemCount() const
223{
224 return dptrc()->m_dataArray->size();
225}
226
227/*!
228 * Returns the pointer to the data array.
229 */
230const QScatterDataArray *QScatterDataProxy::array() const
231{
232 return dptrc()->m_dataArray;
233}
234
235/*!
236 * Returns the pointer to the item at the index \a index. It is guaranteed to be
237 * valid only until the next call that modifies data.
238 */
239const QScatterDataItem *QScatterDataProxy::itemAt(int index) const
240{
241 return &dptrc()->m_dataArray->at(i: index);
242}
243
244/*!
245 * \internal
246 */
247QScatterDataProxyPrivate *QScatterDataProxy::dptr()
248{
249 return static_cast<QScatterDataProxyPrivate *>(d_ptr.data());
250}
251
252/*!
253 * \internal
254 */
255const QScatterDataProxyPrivate *QScatterDataProxy::dptrc() const
256{
257 return static_cast<const QScatterDataProxyPrivate *>(d_ptr.data());
258}
259
260/*!
261 * \fn void QScatterDataProxy::arrayReset()
262 *
263 * This signal is emitted when the data array is reset.
264 * If the contents of the whole array are changed without calling resetArray(),
265 * this signal needs to be emitted to update the graph.
266 */
267
268/*!
269 * \fn void QScatterDataProxy::itemsAdded(int startIndex, int count)
270 *
271 * This signal is emitted when the number of items specified by \a count is
272 * added starting at the position \a startIndex.
273 * If items are added to the array without calling addItem() or addItems(),
274 * this signal needs to be emitted to update the graph.
275 */
276
277/*!
278 * \fn void QScatterDataProxy::itemsChanged(int startIndex, int count)
279 *
280 * This signal is emitted when the number of items specified by \a count is
281 * changed starting at the position \a startIndex.
282 * If items are changed in the array without calling setItem() or setItems(),
283 * this signal needs to be emitted to update the graph.
284 */
285
286/*!
287 * \fn void QScatterDataProxy::itemsRemoved(int startIndex, int count)
288 *
289 * This signal is emitted when the number of rows specified by \a count is
290 * removed starting at the position \a startIndex.
291 * The index may be larger than the current array size if items are removed from
292 * the end. If items are removed from the array without calling removeItems(),
293 * this signal needs to be emitted to update the graph.
294 */
295
296/*!
297 * \fn void QScatterDataProxy::itemsInserted(int startIndex, int count)
298 *
299 * This signal is emitted when the number of items specified by \a count is
300 * inserted starting at the position \a startIndex.
301 * If items are inserted into the array without calling insertItem() or
302 * insertItems(), this signal needs to be emitted to update the graph.
303 */
304
305// QScatterDataProxyPrivate
306
307QScatterDataProxyPrivate::QScatterDataProxyPrivate(QScatterDataProxy *q)
308 : QAbstractDataProxyPrivate(q, QAbstractDataProxy::DataTypeScatter),
309 m_dataArray(new QScatterDataArray)
310{
311}
312
313QScatterDataProxyPrivate::~QScatterDataProxyPrivate()
314{
315 m_dataArray->clear();
316 delete m_dataArray;
317}
318
319void QScatterDataProxyPrivate::resetArray(QScatterDataArray *newArray)
320{
321 if (!newArray)
322 newArray = new QScatterDataArray;
323
324 if (newArray != m_dataArray) {
325 m_dataArray->clear();
326 delete m_dataArray;
327 m_dataArray = newArray;
328 }
329}
330
331void QScatterDataProxyPrivate::setItem(int index, const QScatterDataItem &item)
332{
333 Q_ASSERT(index >= 0 && index < m_dataArray->size());
334 (*m_dataArray)[index] = item;
335}
336
337void QScatterDataProxyPrivate::setItems(int index, const QScatterDataArray &items)
338{
339 Q_ASSERT(index >= 0 && (index + items.size()) <= m_dataArray->size());
340 for (int i = 0; i < items.size(); i++)
341 (*m_dataArray)[index++] = items[i];
342}
343
344int QScatterDataProxyPrivate::addItem(const QScatterDataItem &item)
345{
346 int currentSize = m_dataArray->size();
347 m_dataArray->append(t: item);
348 return currentSize;
349}
350
351int QScatterDataProxyPrivate::addItems(const QScatterDataArray &items)
352{
353 int currentSize = m_dataArray->size();
354 (*m_dataArray) += items;
355 return currentSize;
356}
357
358void QScatterDataProxyPrivate::insertItem(int index, const QScatterDataItem &item)
359{
360 Q_ASSERT(index >= 0 && index <= m_dataArray->size());
361 m_dataArray->insert(i: index, t: item);
362}
363
364void QScatterDataProxyPrivate::insertItems(int index, const QScatterDataArray &items)
365{
366 Q_ASSERT(index >= 0 && index <= m_dataArray->size());
367 for (int i = 0; i < items.size(); i++)
368 m_dataArray->insert(i: index++, t: items.at(i));
369}
370
371void QScatterDataProxyPrivate::removeItems(int index, int removeCount)
372{
373 Q_ASSERT(index >= 0);
374 int maxRemoveCount = m_dataArray->size() - index;
375 removeCount = qMin(a: removeCount, b: maxRemoveCount);
376 m_dataArray->remove(i: index, n: removeCount);
377}
378
379void QScatterDataProxyPrivate::limitValues(QVector3D &minValues, QVector3D &maxValues,
380 QAbstract3DAxis *axisX, QAbstract3DAxis *axisY,
381 QAbstract3DAxis *axisZ) const
382{
383 if (m_dataArray->isEmpty())
384 return;
385
386 const QVector3D &firstPos = m_dataArray->at(i: 0).position();
387
388 float minX = firstPos.x();
389 float maxX = minX;
390 float minY = firstPos.y();
391 float maxY = minY;
392 float minZ = firstPos.z();
393 float maxZ = minZ;
394
395 if (m_dataArray->size() > 1) {
396 for (int i = 1; i < m_dataArray->size(); i++) {
397 const QVector3D &pos = m_dataArray->at(i).position();
398
399 float value = pos.x();
400 if (qIsNaN(f: value) || qIsInf(f: value))
401 continue;
402 if (isValidValue(axisValue: minX, value, axis: axisX))
403 minX = value;
404 if (maxX < value)
405 maxX = value;
406
407 value = pos.y();
408 if (qIsNaN(f: value) || qIsInf(f: value))
409 continue;
410 if (isValidValue(axisValue: minY, value, axis: axisY))
411 minY = value;
412 if (maxY < value)
413 maxY = value;
414
415 value = pos.z();
416 if (qIsNaN(f: value) || qIsInf(f: value))
417 continue;
418 if (isValidValue(axisValue: minZ, value, axis: axisZ))
419 minZ = value;
420 if (maxZ < value)
421 maxZ = value;
422 }
423 }
424
425 minValues.setX(minX);
426 minValues.setY(minY);
427 minValues.setZ(minZ);
428
429 maxValues.setX(maxX);
430 maxValues.setY(maxY);
431 maxValues.setZ(maxZ);
432}
433
434bool QScatterDataProxyPrivate::isValidValue(float axisValue, float value,
435 QAbstract3DAxis *axis) const
436{
437 return (axisValue > value && (value > 0.0f
438 || (value == 0.0f && axis->d_ptr->allowZero())
439 || (value < 0.0f && axis->d_ptr->allowNegatives())));
440}
441
442void QScatterDataProxyPrivate::setSeries(QAbstract3DSeries *series)
443{
444 QAbstractDataProxyPrivate::setSeries(series);
445 QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(series);
446 emit qptr()->seriesChanged(series: scatterSeries);
447}
448
449QScatterDataProxy *QScatterDataProxyPrivate::qptr()
450{
451 return static_cast<QScatterDataProxy *>(q_ptr);
452}
453
454QT_END_NAMESPACE_DATAVISUALIZATION
455

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