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

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